diff --git a/.axe-version b/.axe-version index 6085e946..88c5fb89 100644 --- a/.axe-version +++ b/.axe-version @@ -1 +1 @@ -1.2.1 +1.4.0 diff --git a/.claude/agents/xcodebuild-mcp-qa-tester.md b/.claude/agents/xcodebuild-mcp-qa-tester.md deleted file mode 100644 index a055b237..00000000 --- a/.claude/agents/xcodebuild-mcp-qa-tester.md +++ /dev/null @@ -1,220 +0,0 @@ ---- -name: xcodebuild-mcp-qa-tester -description: Use this agent when you need comprehensive black box testing of the XcodeBuildMCP server using Reloaderoo. This agent should be used after code changes, before releases, or when validating tool functionality. Examples:\n\n- \n Context: The user has made changes to XcodeBuildMCP tools and wants to validate everything works correctly.\n user: "I've updated the simulator tools and need to make sure they all work properly"\n assistant: "I'll use the xcodebuild-mcp-qa-tester agent to perform comprehensive black box testing of all simulator tools using Reloaderoo"\n \n Since the user needs thorough testing of XcodeBuildMCP functionality, use the xcodebuild-mcp-qa-tester agent to systematically validate all tools and resources.\n \n\n\n- \n Context: The user is preparing for a release and needs full QA validation.\n user: "We're about to release version 2.1.0 and need complete testing coverage"\n assistant: "I'll launch the xcodebuild-mcp-qa-tester agent to perform thorough black box testing of all XcodeBuildMCP tools and resources following the manual testing procedures"\n \n For release validation, the QA tester agent should perform comprehensive testing to ensure all functionality works as expected.\n \n -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, NotebookRead, WebFetch, TodoWrite, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool -color: purple ---- - -You are a senior quality assurance software engineer specializing in black box testing of the XcodeBuildMCP server. Your expertise lies in systematic, thorough testing using the Reloaderoo MCP package to validate all tools and resources exposed by the MCP server. - -## Your Core Responsibilities - -1. **Follow Manual Testing Procedures**: Strictly adhere to the instructions in @docs/MANUAL_TESTING.md for systematic test execution -2. **Use Reloaderoo Exclusively**: Utilize the Reloaderoo CLI inspection tools as documented in @docs/RELOADEROO.md for all testing activities -3. **Comprehensive Coverage**: Test ALL tools and resources - never skip or assume functionality works -4. **Black Box Approach**: Test from the user perspective without knowledge of internal implementation details -5. **Live Documentation**: Create and continuously update a markdown test report showing real-time progress -6. **MANDATORY COMPLETION**: Continue testing until EVERY SINGLE tool and resource has been tested - DO NOT STOP until 100% completion is achieved - -## MANDATORY Test Report Creation and Updates - -### Step 1: Create Initial Test Report (IMMEDIATELY) -**BEFORE TESTING BEGINS**, you MUST: - -1. **Create Test Report File**: Generate a markdown file in the workspace root named `TESTING_REPORT__.md` -2. **Include Report Header**: Date, time, environment information, and testing scope -3. **Discovery Phase**: Run `list-tools` and `list-resources` to get complete inventory -4. **Create Checkbox Lists**: Add unchecked markdown checkboxes for every single tool and resource discovered - -### Test Report Initial Structure -```markdown -# XcodeBuildMCP Testing Report -**Date:** YYYY-MM-DD HH:MM:SS -**Environment:** [System details] -**Testing Scope:** Comprehensive black box testing of all tools and resources - -## Test Summary -- **Total Tools:** [X] -- **Total Resources:** [Y] -- **Tests Completed:** 0/[X+Y] -- **Tests Passed:** 0 -- **Tests Failed:** 0 - -## Tools Testing Checklist -- [ ] Tool: tool_name_1 - Test with valid parameters -- [ ] Tool: tool_name_2 - Test with valid parameters -[... all tools discovered ...] - -## Resources Testing Checklist -- [ ] Resource: resource_uri_1 - Validate content and accessibility -- [ ] Resource: resource_uri_2 - Validate content and accessibility -[... all resources discovered ...] - -## Detailed Test Results -[Updated as tests are completed] - -## Failed Tests -[Updated if any failures occur] -``` - -### Step 2: Continuous Updates (AFTER EACH TEST) -**IMMEDIATELY after completing each test**, you MUST update the test report with: - -1. **Check the box**: Change `- [ ]` to `- [x]` for the completed test -2. **Update test summary counts**: Increment completed/passed/failed counters -3. **Add detailed result**: Append to "Detailed Test Results" section with: - - Test command used - - Verification method - - Validation summary - - Pass/fail status - -### Live Update Example -After testing `list_sims` tool, update the report: -```markdown -- [x] Tool: list_sims - Test with valid parameters ✅ PASSED - -## Detailed Test Results - -### Tool: list_sims ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool list_sims --params '{}' -- node build/index.js` -**Verification:** Command returned JSON array with 6 simulator objects -**Validation Summary:** Successfully discovered 6 available simulators with UUIDs, names, and boot status -**Timestamp:** 2025-01-29 14:30:15 -``` - -## Testing Methodology - -### Pre-Testing Setup -- Always start by building the project: `npm run build` -- Verify Reloaderoo is available: `npx reloaderoo@latest --help` -- Check server connectivity: `npx reloaderoo@latest inspect ping -- node build/index.js` -- Get server information: `npx reloaderoo@latest inspect server-info -- node build/index.js` - -### Systematic Testing Workflow -1. **Create Initial Report**: Generate test report with all checkboxes unchecked -2. **Individual Testing**: Test each tool/resource systematically -3. **Live Updates**: Update report immediately after each test completion -4. **Continuous Tracking**: Report serves as real-time progress tracker -5. **CONTINUOUS EXECUTION**: Never stop until ALL tools and resources are tested (100% completion) -6. **Progress Monitoring**: Check total tested vs total available - continue if any remain untested -7. **Final Review**: Ensure all checkboxes are marked and results documented - -### CRITICAL: NO EARLY TERMINATION -- **NEVER STOP** testing until every single tool and resource has been tested -- If you have tested X out of Y items, IMMEDIATELY continue testing the remaining Y-X items -- The only acceptable completion state is 100% coverage (all checkboxes checked) -- Do not summarize or conclude until literally every tool and resource has been individually tested -- Use the test report checkbox count as your progress indicator - if any boxes remain unchecked, CONTINUE TESTING - -### Tool Testing Process -For each tool: -1. Execute test with `npx reloaderoo@latest inspect call-tool --params '' -- node build/index.js` -2. Verify response format and content -3. **IMMEDIATELY** update test report with result -4. Check the box and add detailed verification summary -5. Move to next tool - -### Resource Testing Process -For each resource: -1. Execute test with `npx reloaderoo@latest inspect read-resource "" -- node build/index.js` -2. Verify resource accessibility and content format -3. **IMMEDIATELY** update test report with result -4. Check the box and add detailed verification summary -5. Move to next resource - -## Quality Standards - -### Thoroughness Over Speed -- **NEVER rush testing** - take time to be comprehensive -- Test every single tool and resource without exception -- Update the test report after every single test - no batching -- The markdown report is the single source of truth for progress - -### Test Documentation Requirements -- Record the exact command used for each test -- Document expected vs actual results -- Note any warnings, errors, or unexpected behavior -- Include full JSON responses for failed tests -- Categorize issues by severity (critical, major, minor) -- **MANDATORY**: Update test report immediately after each test completion - -### Validation Criteria -- All tools must respond without errors for valid inputs -- Error messages must be clear and actionable for invalid inputs -- JSON responses must be properly formatted -- Resource URIs must be accessible and return valid data -- Tool descriptions must accurately reflect functionality - -## Testing Environment Considerations - -### Prerequisites Validation -- Verify Xcode is installed and accessible -- Check for required simulators and devices -- Validate development environment setup -- Ensure all dependencies are available - -### Platform-Specific Testing -- Test iOS simulator tools with actual simulators -- Validate device tools (when devices are available) -- Test macOS-specific functionality -- Verify Swift Package Manager integration - -## Test Report Management - -### File Naming Convention -- Format: `TESTING_REPORT__.md` -- Location: Workspace root directory -- Example: `TESTING_REPORT_2025-01-29_14-30.md` - -### Update Requirements -- **Real-time updates**: Update after every single test completion -- **No batching**: Never wait to update multiple tests at once -- **Checkbox tracking**: Visual progress through checked/unchecked boxes -- **Detailed results**: Each test gets a dedicated result section -- **Summary statistics**: Keep running totals updated - -### Verification Summary Requirements -Every test result MUST answer: "How did you know this test passed?" - -Examples of strong verification summaries: -- `Successfully discovered 84 tools in server response` -- `Returned valid app bundle path: /path/to/MyApp.app` -- `Listed 6 simulators with expected UUID format and boot status` -- `Resource returned JSON array with 4 device objects containing UDID and name fields` -- `Tool correctly rejected invalid parameters with clear error message` - -## Error Investigation Protocol - -1. **Reproduce Consistently**: Ensure errors can be reproduced reliably -2. **Isolate Variables**: Test with minimal parameters to isolate issues -3. **Check Prerequisites**: Verify all required tools and environments are available -4. **Document Context**: Include system information, versions, and environment details -5. **Update Report**: Document failures immediately in the test report - -## Critical Success Criteria - -- ✅ Test report created BEFORE any testing begins with all checkboxes unchecked -- ✅ Every single tool has its own checkbox and detailed result section -- ✅ Every single resource has its own checkbox and detailed result section -- ✅ Report updated IMMEDIATELY after each individual test completion -- ✅ No tool or resource is skipped or grouped together -- ✅ Each verification summary clearly explains how success was determined -- ✅ Real-time progress tracking through checkbox completion -- ✅ Test report serves as the single source of truth for all testing progress -- ✅ **100% COMPLETION MANDATORY**: All checkboxes must be checked before considering testing complete - -## ABSOLUTE COMPLETION REQUIREMENT - -**YOU MUST NOT STOP TESTING UNTIL:** -- Every single tool discovered by `list-tools` has been individually tested -- Every single resource discovered by `list-resources` has been individually tested -- All checkboxes in your test report are marked as complete -- The test summary shows X/X completion (100%) - -**IF TESTING IS NOT 100% COMPLETE:** -- Immediately identify which tools/resources remain untested -- Continue systematic testing of the remaining items -- Update the test report after each additional test -- Do not provide final summaries or conclusions until literally everything is tested - -Remember: Your role is to be the final quality gate before release. The test report you create and continuously update is the definitive record of testing progress and results. Be meticulous, be thorough, and update the report after every single test completion - never batch updates or wait until the end. **NEVER CONCLUDE TESTING UNTIL 100% COMPLETION IS ACHIEVED.** diff --git a/.cursorrules b/.cursorrules deleted file mode 120000 index 47dc3e3d..00000000 --- a/.cursorrules +++ /dev/null @@ -1 +0,0 @@ -AGENTS.md \ No newline at end of file diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 00000000..c62f90aa --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,37 @@ +#!/bin/sh +set -eu + +echo "Running pre-commit checks..." + +RED='\033[0;31m' +NC='\033[0m' + +echo "Checking formatting..." +npm run format:check +if [ $? -ne 0 ]; then + echo "${RED}Formatting check failed. Aborting commit.${NC}" + exit 1 +fi + +echo "Running linter..." +npm run lint +if [ $? -ne 0 ]; then + echo "${RED}Linting failed. Aborting commit.${NC}" + exit 1 +fi + +echo "Building project..." +npm run build +if [ $? -ne 0 ]; then + echo "${RED}Build failed. Aborting commit.${NC}" + exit 1 +fi + +echo "Validating CLI command references in consumer docs..." +npm run docs:check +if [ $? -ne 0 ]; then + echo "${RED}Docs command validation failed. Aborting commit.${NC}" + exit 1 +fi + +echo "Pre-commit checks passed." diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 2ca5bffe..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms -github: cameroncooke -buy_me_a_coffee: cameroncooke diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 2a55b6c7..33f3670f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,7 @@ name: Bug Report description: Report a bug or issue with XcodeBuildMCP -title: "[Bug]: " -labels: ["bug"] +title: '[Bug]: ' +labels: ['bug'] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index ec4bb386..3ba13e0c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1 +1 @@ -blank_issues_enabled: false \ No newline at end of file +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 109bf712..5f4d148d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,13 +1,13 @@ name: Feature Request description: Suggest a new feature for XcodeBuildMCP -title: "[Feature]: " -labels: ["enhancement"] +title: '[Feature]: ' +labels: ['enhancement'] body: - type: markdown attributes: value: | Thanks for suggesting a new feature for XcodeBuildMCP! - + - type: textarea id: feature-description attributes: @@ -16,7 +16,7 @@ body: placeholder: I would like the AI assistant to be able to... validations: required: true - + - type: textarea id: use-cases attributes: @@ -28,7 +28,7 @@ body: - Automating complex Xcode workflows validations: required: false - + - type: textarea id: example-interactions attributes: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c68d165..9a5bfe31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: build-and-test: @@ -15,33 +15,36 @@ jobs: node-version: [24.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' - - name: Install dependencies - run: npm ci + - name: Install dependencies + run: npm ci - - name: Build (tsup) - run: npm run build:tsup + - name: Bundle AXe artifacts + run: npm run bundle:axe - - name: Build (Smithery) - run: npm run build + - name: Build + run: npm run build - - name: Lint - run: npm run lint + - name: Validate docs CLI command references + run: npm run docs:check - - name: Check formatting - run: npm run format:check + - name: Lint + run: npm run lint - - name: Type check - run: npm run typecheck + - name: Check formatting + run: npm run format:check - - name: Run tests - run: npm test + - name: Type check + run: npm run typecheck - - run: npx pkg-pr-new publish + - name: Run tests + run: npm test + + - run: npx pkg-pr-new publish diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5095a376..4f59b7b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,8 @@ jobs: release: runs-on: macos-latest environment: production + outputs: + version: ${{ steps.get_version.outputs.VERSION }} steps: - name: Checkout code @@ -48,10 +50,7 @@ jobs: - name: Bundle AXe artifacts run: npm run bundle:axe - - name: Build TypeScript (tsup) - run: npm run build:tsup - - - name: Build Smithery bundle + - name: Build package run: npm run build - name: Run tests @@ -75,6 +74,29 @@ jobs: # For tag-based releases, package.json was already updated by release script fi + - name: Resolve npm tag from version + id: resolve_npm_tag + run: | + VERSION="${{ steps.get_version.outputs.VERSION }}" + if [[ "$VERSION" == *"-beta"* ]]; then + NPM_TAG="beta" + elif [[ "$VERSION" == *"-alpha"* ]]; then + NPM_TAG="alpha" + elif [[ "$VERSION" == *"-rc"* ]]; then + NPM_TAG="rc" + else + NPM_TAG="latest" + fi + echo "NPM_TAG=$NPM_TAG" >> "$GITHUB_OUTPUT" + echo "Resolved npm tag: $NPM_TAG" + + - name: Generate GitHub release notes (production releases only) + if: github.event_name == 'push' + run: | + node scripts/generate-github-release-notes.mjs \ + --version "${{ steps.get_version.outputs.VERSION }}" \ + --out github-release-body.md + - name: Create package run: npm pack @@ -82,7 +104,7 @@ jobs: if: github.event_name == 'workflow_dispatch' run: | echo "🧪 Testing package creation (dry run)" - npm publish --dry-run --access public + npm publish --dry-run --access public --tag "${{ steps.resolve_npm_tag.outputs.NPM_TAG }}" - name: Publish to NPM (production releases only) if: github.event_name == 'push' @@ -93,17 +115,7 @@ jobs: echo "✅ xcodebuildmcp@$VERSION already on NPM. Skipping publish." exit 0 fi - # Determine the appropriate npm tag based on version - if [[ "$VERSION" == *"-beta"* ]]; then - NPM_TAG="beta" - elif [[ "$VERSION" == *"-alpha"* ]]; then - NPM_TAG="alpha" - elif [[ "$VERSION" == *"-rc"* ]]; then - NPM_TAG="rc" - else - # For stable releases, explicitly use latest tag - NPM_TAG="latest" - fi + NPM_TAG="${{ steps.resolve_npm_tag.outputs.NPM_TAG }}" echo "📦 Publishing to NPM with tag: $NPM_TAG" npm publish --access public --tag "$NPM_TAG" @@ -113,20 +125,7 @@ jobs: with: tag_name: v${{ steps.get_version.outputs.VERSION }} name: Release v${{ steps.get_version.outputs.VERSION }} - body: | - ## Release v${{ steps.get_version.outputs.VERSION }} - - ### Installation - ```bash - npm install -g xcodebuildmcp@${{ steps.get_version.outputs.VERSION }} - ``` - - Or use with npx: - ```bash - npx xcodebuildmcp@${{ steps.get_version.outputs.VERSION }} - ``` - - 📦 **NPM Package**: https://www.npmjs.com/package/xcodebuildmcp/v/${{ steps.get_version.outputs.VERSION }} + body_path: github-release-body.md files: | xcodebuildmcp-${{ steps.get_version.outputs.VERSION }}.tgz draft: false @@ -211,3 +210,218 @@ jobs: delay=$((delay*2)) done echo "✅ MCP Registry publish succeeded." + + build_and_package_macos: + needs: release + strategy: + fail-fast: false + matrix: + include: + - arch: arm64 + runner: macos-14 + - arch: x64 + runner: macos-14 + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: Package portable artifact + run: | + npm run package:macos -- --arch "${{ matrix.arch }}" --version "${{ needs.release.outputs.version }}" + + - name: Verify portable artifact + run: | + npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz" + + - name: Upload arch artifact + uses: actions/upload-artifact@v4 + with: + name: portable-${{ matrix.arch }} + path: | + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }} + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz.sha256 + + build_universal_and_verify: + needs: [release, build_and_package_macos] + runs-on: macos-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: Download arm64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-arm64 + path: dist/portable/arm64 + + - name: Download x64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-x64 + path: dist/portable/x64 + + - name: Expand per-arch archives + id: expand_archives + run: | + VERSION="${{ needs.release.outputs.version }}" + ARM64_TGZ="dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz" + X64_TGZ="dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz" + ARM64_ROOT="dist/portable/unpacked/arm64/xcodebuildmcp-${VERSION}-darwin-arm64" + X64_ROOT="dist/portable/unpacked/x64/xcodebuildmcp-${VERSION}-darwin-x64" + mkdir -p dist/portable/unpacked/arm64 dist/portable/unpacked/x64 + tar -xzf "$ARM64_TGZ" -C dist/portable/unpacked/arm64 + tar -xzf "$X64_TGZ" -C dist/portable/unpacked/x64 + echo "ARM64_ROOT=$ARM64_ROOT" >> "$GITHUB_OUTPUT" + echo "X64_ROOT=$X64_ROOT" >> "$GITHUB_OUTPUT" + + - name: Build universal portable artifact + run: | + npm run package:macos:universal -- \ + --version "${{ needs.release.outputs.version }}" \ + --arm64-root "${{ steps.expand_archives.outputs.ARM64_ROOT }}" \ + --x64-root "${{ steps.expand_archives.outputs.X64_ROOT }}" + + - name: Verify universal portable artifact + run: | + npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz" + + - name: Upload universal artifact + uses: actions/upload-artifact@v4 + with: + name: portable-universal + path: | + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256 + + publish_portable_assets: + if: github.event_name == 'push' + needs: [release, build_and_package_macos, build_universal_and_verify] + runs-on: ubuntu-latest + steps: + - name: Download arm64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-arm64 + path: dist/portable/arm64 + + - name: Download x64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-x64 + path: dist/portable/x64 + + - name: Download universal artifact + uses: actions/download-artifact@v4 + with: + name: portable-universal + path: dist/portable/universal + + - name: Upload portable assets to GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload "v${{ needs.release.outputs.version }}" \ + dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz \ + dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz.sha256 \ + dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz \ + dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz.sha256 \ + dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz \ + dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256 \ + --clobber \ + --repo "${{ github.repository }}" + + update_homebrew_tap: + if: github.event_name == 'push' + needs: [release, build_and_package_macos, publish_portable_assets] + runs-on: ubuntu-latest + env: + HOMEBREW_TAP_DEPLOY_KEY: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Download arm64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-arm64 + path: dist/portable/arm64 + + - name: Download x64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-x64 + path: dist/portable/x64 + + - name: Skip when deploy key is unavailable + if: env.HOMEBREW_TAP_DEPLOY_KEY == '' + run: echo "HOMEBREW_TAP_DEPLOY_KEY is not set; skipping Homebrew tap update." + + - name: Generate formula + if: env.HOMEBREW_TAP_DEPLOY_KEY != '' + run: | + VERSION="${{ needs.release.outputs.version }}" + FORMULA_BASE_URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}" + ARM64_SHA="$(awk '{print $1}' dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256)" + X64_SHA="$(awk '{print $1}' dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256)" + npm run homebrew:formula -- \ + --version "$VERSION" \ + --arm64-sha "$ARM64_SHA" \ + --x64-sha "$X64_SHA" \ + --base-url "$FORMULA_BASE_URL" \ + --out dist/homebrew/Formula/xcodebuildmcp.rb + + - name: Configure SSH for tap repository + if: env.HOMEBREW_TAP_DEPLOY_KEY != '' + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }} + + - name: Update tap repository + if: env.HOMEBREW_TAP_DEPLOY_KEY != '' + run: | + VERSION="${{ needs.release.outputs.version }}" + git clone git@github.com:${{ github.repository_owner }}/homebrew-xcodebuildmcp.git tap-repo + mkdir -p tap-repo/Formula + cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb + cd tap-repo + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + DEFAULT_BRANCH="$(git symbolic-ref refs/remotes/origin/HEAD | sed 's|refs/remotes/origin/||')" + if ! git rev-parse --verify HEAD >/dev/null 2>&1; then + echo "Tap repo has no commits; bootstrapping ${DEFAULT_BRANCH}." + git checkout -b "$DEFAULT_BRANCH" + else + git checkout "$DEFAULT_BRANCH" + git pull --ff-only origin "$DEFAULT_BRANCH" + fi + git add Formula/xcodebuildmcp.rb + if git diff --cached --quiet; then + echo "Formula already up to date; nothing to push." + exit 0 + fi + git commit -m "xcodebuildmcp ${VERSION}" + git push origin "$DEFAULT_BRANCH" diff --git a/.github/workflows/sentry.yml b/.github/workflows/sentry.yml index d88f57ed..cd14ac86 100644 --- a/.github/workflows/sentry.yml +++ b/.github/workflows/sentry.yml @@ -32,5 +32,5 @@ jobs: SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} with: environment: production - sourcemaps: "./build" - version: ${{ steps.get_version.outputs.MCP_VERSION }} \ No newline at end of file + sourcemaps: './build' + version: ${{ steps.get_version.outputs.MCP_VERSION }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index c2646032..f0864d37 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,8 +1,8 @@ -name: "Stale Issues and PRs" +name: 'Stale Issues and PRs' on: schedule: - - cron: "30 3 * * *" + - cron: '30 3 * * *' workflow_dispatch: {} permissions: @@ -21,12 +21,12 @@ jobs: days-before-issue-close: 7 days-before-pr-stale: 21 days-before-pr-close: 7 - stale-issue-label: "stale" - stale-pr-label: "stale-pr" + stale-issue-label: 'stale' + stale-pr-label: 'stale-pr' exempt-issue-assignees: true exempt-pr-assignees: true - exempt-issue-labels: "no-stale,security,pinned" - exempt-pr-labels: "no-stale,security,pinned" + exempt-issue-labels: 'no-stale,security,pinned' + exempt-pr-labels: 'no-stale,security,pinned' stale-issue-message: > This issue has been inactive for 30 days. It will be closed in 7 days if no further activity occurs. Add a comment to keep it open, or apply diff --git a/.gitignore b/.gitignore index 5a29692b..b99c5936 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ dist/ *.tsbuildinfo # Auto-generated files +src/version.ts # IDE and editor files .idea/ @@ -92,6 +93,7 @@ xcuserdata/ .sentryclirc # Claude Config File +.claude/ **/.claude/settings.local.json # incremental builds @@ -107,3 +109,4 @@ bundled/ .mcpli .factory DerivedData +.derivedData diff --git a/.smithery/index.cjs b/.smithery/index.cjs deleted file mode 100644 index d8e78b44..00000000 --- a/.smithery/index.cjs +++ /dev/null @@ -1,786 +0,0 @@ -var nKe=Object.create;var iw=Object.defineProperty;var oKe=Object.getOwnPropertyDescriptor;var iKe=Object.getOwnPropertyNames;var sKe=Object.getPrototypeOf,aKe=Object.prototype.hasOwnProperty;var O=(t,e)=>()=>(t&&(e=t(t=0)),e);var S=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),Y=(t,e)=>{for(var r in e)iw(t,r,{get:e[r],enumerable:!0})},lX=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of iKe(e))!aKe.call(t,o)&&o!==r&&iw(t,o,{get:()=>e[o],enumerable:!(n=oKe(e,o))||n.enumerable});return t};var W=(t,e,r)=>(r=t!=null?nKe(sKe(t)):{},lX(e||!t||!t.__esModule?iw(r,"default",{value:t,enumerable:!0}):r,t)),se=t=>lX(iw({},"__esModule",{value:!0}),t);function D(t,e,r){function n(a,c){if(a._zod||Object.defineProperty(a,"_zod",{value:{def:c,constr:s,traits:new Set},enumerable:!1}),a._zod.traits.has(t))return;a._zod.traits.add(t),e(a,c);let u=s.prototype,p=Object.keys(u);for(let f=0;fr?.Parent&&a instanceof r.Parent?!0:a?._zod?.traits?.has(t)}),Object.defineProperty(s,"name",{value:t}),s}function cn(t){return t&&Object.assign(oy,t),oy}var mh,iy,Ga,tp,oy,hh=O(()=>{mh=Object.freeze({status:"aborted"});iy=Symbol("zod_brand"),Ga=class extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}},tp=class extends Error{constructor(e){super(`Encountered unidirectional transform during encode: ${e}`),this.name="ZodEncodeError"}},oy={}});var te={};Y(te,{BIGINT_FORMAT_RANGES:()=>nj,Class:()=>ZU,NUMBER_FORMAT_RANGES:()=>rj,aborted:()=>ip,allowsEval:()=>XU,assert:()=>dKe,assertEqual:()=>cKe,assertIs:()=>lKe,assertNever:()=>pKe,assertNotEqual:()=>uKe,assignProp:()=>np,base64ToUint8Array:()=>dX,base64urlToUint8Array:()=>IKe,cached:()=>vh,captureStackTrace:()=>aw,cleanEnum:()=>PKe,cleanRegex:()=>ay,clone:()=>oo,cloneDef:()=>mKe,createTransparentProxy:()=>yKe,defineLazy:()=>Rt,esc:()=>sw,escapeRegex:()=>Vs,extend:()=>bKe,finalizeIssue:()=>Ui,floatSafeRemainder:()=>YU,getElementAtPath:()=>hKe,getEnumValues:()=>gh,getLengthableOrigin:()=>ly,getParsedType:()=>SKe,getSizableOrigin:()=>uy,hexToUint8Array:()=>NKe,isObject:()=>sf,isPlainObject:()=>op,issue:()=>Sh,joinValues:()=>q,jsonStringifyReplacer:()=>_h,merge:()=>AKe,mergeDefs:()=>Wu,normalizeParams:()=>ae,nullish:()=>rp,numKeys:()=>vKe,objectClone:()=>fKe,omit:()=>TKe,optionalKeys:()=>tj,parsedType:()=>oe,partial:()=>wKe,pick:()=>EKe,prefixIssues:()=>ds,primitiveTypes:()=>ej,promiseAllObject:()=>gKe,propertyKeyTypes:()=>cy,randomString:()=>_Ke,required:()=>RKe,safeExtend:()=>xKe,shallowClone:()=>QU,slugify:()=>JU,stringifyPrimitive:()=>re,uint8ArrayToBase64:()=>fX,uint8ArrayToBase64url:()=>OKe,uint8ArrayToHex:()=>CKe,unwrapMessage:()=>sy});function cKe(t){return t}function uKe(t){return t}function lKe(t){}function pKe(t){throw new Error("Unexpected value in exhaustive check")}function dKe(t){}function gh(t){let e=Object.values(t).filter(n=>typeof n=="number");return Object.entries(t).filter(([n,o])=>e.indexOf(+n)===-1).map(([n,o])=>o)}function q(t,e="|"){return t.map(r=>re(r)).join(e)}function _h(t,e){return typeof e=="bigint"?e.toString():e}function vh(t){return{get value(){{let r=t();return Object.defineProperty(this,"value",{value:r}),r}throw new Error("cached value already set")}}}function rp(t){return t==null}function ay(t){let e=t.startsWith("^")?1:0,r=t.endsWith("$")?t.length-1:t.length;return t.slice(e,r)}function YU(t,e){let r=(t.toString().split(".")[1]||"").length,n=e.toString(),o=(n.split(".")[1]||"").length;if(o===0&&/\d?e-\d?/.test(n)){let c=n.match(/\d?e-(\d?)/);c?.[1]&&(o=Number.parseInt(c[1]))}let i=r>o?r:o,s=Number.parseInt(t.toFixed(i).replace(".","")),a=Number.parseInt(e.toFixed(i).replace(".",""));return s%a/10**i}function Rt(t,e,r){let n;Object.defineProperty(t,e,{get(){if(n!==pX)return n===void 0&&(n=pX,n=r()),n},set(o){Object.defineProperty(t,e,{value:o})},configurable:!0})}function fKe(t){return Object.create(Object.getPrototypeOf(t),Object.getOwnPropertyDescriptors(t))}function np(t,e,r){Object.defineProperty(t,e,{value:r,writable:!0,enumerable:!0,configurable:!0})}function Wu(...t){let e={};for(let r of t){let n=Object.getOwnPropertyDescriptors(r);Object.assign(e,n)}return Object.defineProperties({},e)}function mKe(t){return Wu(t._zod.def)}function hKe(t,e){return e?e.reduce((r,n)=>r?.[n],t):t}function gKe(t){let e=Object.keys(t),r=e.map(n=>t[n]);return Promise.all(r).then(n=>{let o={};for(let i=0;ie};if(e?.message!==void 0){if(e?.error!==void 0)throw new Error("Cannot specify both `message` and `error` params");e.error=e.message}return delete e.message,typeof e.error=="string"?{...e,error:()=>e.error}:e}function yKe(t){let e;return new Proxy({},{get(r,n,o){return e??(e=t()),Reflect.get(e,n,o)},set(r,n,o,i){return e??(e=t()),Reflect.set(e,n,o,i)},has(r,n){return e??(e=t()),Reflect.has(e,n)},deleteProperty(r,n){return e??(e=t()),Reflect.deleteProperty(e,n)},ownKeys(r){return e??(e=t()),Reflect.ownKeys(e)},getOwnPropertyDescriptor(r,n){return e??(e=t()),Reflect.getOwnPropertyDescriptor(e,n)},defineProperty(r,n,o){return e??(e=t()),Reflect.defineProperty(e,n,o)}})}function re(t){return typeof t=="bigint"?t.toString()+"n":typeof t=="string"?`"${t}"`:`${t}`}function tj(t){return Object.keys(t).filter(e=>t[e]._zod.optin==="optional"&&t[e]._zod.optout==="optional")}function EKe(t,e){let r=t._zod.def,n=r.checks;if(n&&n.length>0)throw new Error(".pick() cannot be used on object schemas containing refinements");let i=Wu(t._zod.def,{get shape(){let s={};for(let a in e){if(!(a in r.shape))throw new Error(`Unrecognized key: "${a}"`);e[a]&&(s[a]=r.shape[a])}return np(this,"shape",s),s},checks:[]});return oo(t,i)}function TKe(t,e){let r=t._zod.def,n=r.checks;if(n&&n.length>0)throw new Error(".omit() cannot be used on object schemas containing refinements");let i=Wu(t._zod.def,{get shape(){let s={...t._zod.def.shape};for(let a in e){if(!(a in r.shape))throw new Error(`Unrecognized key: "${a}"`);e[a]&&delete s[a]}return np(this,"shape",s),s},checks:[]});return oo(t,i)}function bKe(t,e){if(!op(e))throw new Error("Invalid input to extend: expected a plain object");let r=t._zod.def.checks;if(r&&r.length>0){let i=t._zod.def.shape;for(let s in e)if(Object.getOwnPropertyDescriptor(i,s)!==void 0)throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}let o=Wu(t._zod.def,{get shape(){let i={...t._zod.def.shape,...e};return np(this,"shape",i),i}});return oo(t,o)}function xKe(t,e){if(!op(e))throw new Error("Invalid input to safeExtend: expected a plain object");let r=Wu(t._zod.def,{get shape(){let n={...t._zod.def.shape,...e};return np(this,"shape",n),n}});return oo(t,r)}function AKe(t,e){let r=Wu(t._zod.def,{get shape(){let n={...t._zod.def.shape,...e._zod.def.shape};return np(this,"shape",n),n},get catchall(){return e._zod.def.catchall},checks:[]});return oo(t,r)}function wKe(t,e,r){let o=e._zod.def.checks;if(o&&o.length>0)throw new Error(".partial() cannot be used on object schemas containing refinements");let s=Wu(e._zod.def,{get shape(){let a=e._zod.def.shape,c={...a};if(r)for(let u in r){if(!(u in a))throw new Error(`Unrecognized key: "${u}"`);r[u]&&(c[u]=t?new t({type:"optional",innerType:a[u]}):a[u])}else for(let u in a)c[u]=t?new t({type:"optional",innerType:a[u]}):a[u];return np(this,"shape",c),c},checks:[]});return oo(e,s)}function RKe(t,e,r){let n=Wu(e._zod.def,{get shape(){let o=e._zod.def.shape,i={...o};if(r)for(let s in r){if(!(s in i))throw new Error(`Unrecognized key: "${s}"`);r[s]&&(i[s]=new t({type:"nonoptional",innerType:o[s]}))}else for(let s in o)i[s]=new t({type:"nonoptional",innerType:o[s]});return np(this,"shape",i),i}});return oo(e,n)}function ip(t,e=0){if(t.aborted===!0)return!0;for(let r=e;r{var n;return(n=r).path??(n.path=[]),r.path.unshift(t),r})}function sy(t){return typeof t=="string"?t:t?.message}function Ui(t,e,r){let n={...t,path:t.path??[]};if(!t.message){let o=sy(t.inst?._zod.def?.error?.(t))??sy(e?.error?.(t))??sy(r.customError?.(t))??sy(r.localeError?.(t))??"Invalid input";n.message=o}return delete n.inst,delete n.continue,e?.reportInput||delete n.input,n}function uy(t){return t instanceof Set?"set":t instanceof Map?"map":t instanceof File?"file":"unknown"}function ly(t){return Array.isArray(t)?"array":typeof t=="string"?"string":"unknown"}function oe(t){let e=typeof t;switch(e){case"number":return Number.isNaN(t)?"nan":"number";case"object":{if(t===null)return"null";if(Array.isArray(t))return"array";let r=t;if(r&&Object.getPrototypeOf(r)!==Object.prototype&&"constructor"in r&&r.constructor)return r.constructor.name}}return e}function Sh(...t){let[e,r,n]=t;return typeof e=="string"?{message:e,code:"custom",input:r,inst:n}:{...e}}function PKe(t){return Object.entries(t).filter(([e,r])=>Number.isNaN(Number.parseInt(e,10))).map(e=>e[1])}function dX(t){let e=atob(t),r=new Uint8Array(e.length);for(let n=0;ne.toString(16).padStart(2,"0")).join("")}var pX,aw,XU,SKe,cy,ej,rj,nj,ZU,je=O(()=>{pX=Symbol("evaluating");aw="captureStackTrace"in Error?Error.captureStackTrace:(...t)=>{};XU=vh(()=>{if(typeof navigator<"u"&&navigator?.userAgent?.includes("Cloudflare"))return!1;try{let t=Function;return new t(""),!0}catch{return!1}});SKe=t=>{let e=typeof t;switch(e){case"undefined":return"undefined";case"string":return"string";case"number":return Number.isNaN(t)?"nan":"number";case"boolean":return"boolean";case"function":return"function";case"bigint":return"bigint";case"symbol":return"symbol";case"object":return Array.isArray(t)?"array":t===null?"null":t.then&&typeof t.then=="function"&&t.catch&&typeof t.catch=="function"?"promise":typeof Map<"u"&&t instanceof Map?"map":typeof Set<"u"&&t instanceof Set?"set":typeof Date<"u"&&t instanceof Date?"date":typeof File<"u"&&t instanceof File?"file":"object";default:throw new Error(`Unknown data type: ${e}`)}},cy=new Set(["string","number","symbol"]),ej=new Set(["string","number","bigint","boolean","symbol","undefined"]);rj={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]},nj={int64:[BigInt("-9223372036854775808"),BigInt("9223372036854775807")],uint64:[BigInt(0),BigInt("18446744073709551615")]};ZU=class{constructor(...e){}}});function yh(t,e=r=>r.message){let r={},n=[];for(let o of t.issues)o.path.length>0?(r[o.path[0]]=r[o.path[0]]||[],r[o.path[0]].push(e(o))):n.push(e(o));return{formErrors:n,fieldErrors:r}}function Eh(t,e=r=>r.message){let r={_errors:[]},n=o=>{for(let i of o.issues)if(i.code==="invalid_union"&&i.errors.length)i.errors.map(s=>n({issues:s}));else if(i.code==="invalid_key")n({issues:i.issues});else if(i.code==="invalid_element")n({issues:i.issues});else if(i.path.length===0)r._errors.push(e(i));else{let s=r,a=0;for(;ar.message){let r={errors:[]},n=(o,i=[])=>{var s,a;for(let c of o.issues)if(c.code==="invalid_union"&&c.errors.length)c.errors.map(u=>n({issues:u},c.path));else if(c.code==="invalid_key")n({issues:c.issues},c.path);else if(c.code==="invalid_element")n({issues:c.issues},c.path);else{let u=[...i,...c.path];if(u.length===0){r.errors.push(e(c));continue}let p=r,f=0;for(;ftypeof n=="object"?n.key:n);for(let n of r)typeof n=="number"?e.push(`[${n}]`):typeof n=="symbol"?e.push(`[${JSON.stringify(String(n))}]`):/[^\w$]/.test(n)?e.push(`[${JSON.stringify(n)}]`):(e.length&&e.push("."),e.push(n));return e.join("")}function uw(t){let e=[],r=[...t.issues].sort((n,o)=>(n.path??[]).length-(o.path??[]).length);for(let n of r)e.push(`\u2716 ${n.message}`),n.path?.length&&e.push(` \u2192 at ${hX(n.path)}`);return e.join(` -`)}var mX,py,ji,oj=O(()=>{hh();je();mX=(t,e)=>{t.name="$ZodError",Object.defineProperty(t,"_zod",{value:t._zod,enumerable:!1}),Object.defineProperty(t,"issues",{value:e,enumerable:!1}),t.message=JSON.stringify(e,_h,2),Object.defineProperty(t,"toString",{value:()=>t.message,enumerable:!1})},py=D("$ZodError",mX),ji=D("$ZodError",mX,{Parent:Error})});var Th,af,bh,cf,xh,sp,Ah,ap,lw,gX,pw,_X,dw,vX,fw,SX,mw,yX,hw,EX,gw,TX,_w,bX,ij=O(()=>{hh();oj();je();Th=t=>(e,r,n,o)=>{let i=n?Object.assign(n,{async:!1}):{async:!1},s=e._zod.run({value:r,issues:[]},i);if(s instanceof Promise)throw new Ga;if(s.issues.length){let a=new(o?.Err??t)(s.issues.map(c=>Ui(c,i,cn())));throw aw(a,o?.callee),a}return s.value},af=Th(ji),bh=t=>async(e,r,n,o)=>{let i=n?Object.assign(n,{async:!0}):{async:!0},s=e._zod.run({value:r,issues:[]},i);if(s instanceof Promise&&(s=await s),s.issues.length){let a=new(o?.Err??t)(s.issues.map(c=>Ui(c,i,cn())));throw aw(a,o?.callee),a}return s.value},cf=bh(ji),xh=t=>(e,r,n)=>{let o=n?{...n,async:!1}:{async:!1},i=e._zod.run({value:r,issues:[]},o);if(i instanceof Promise)throw new Ga;return i.issues.length?{success:!1,error:new(t??py)(i.issues.map(s=>Ui(s,o,cn())))}:{success:!0,data:i.value}},sp=xh(ji),Ah=t=>async(e,r,n)=>{let o=n?Object.assign(n,{async:!0}):{async:!0},i=e._zod.run({value:r,issues:[]},o);return i instanceof Promise&&(i=await i),i.issues.length?{success:!1,error:new t(i.issues.map(s=>Ui(s,o,cn())))}:{success:!0,data:i.value}},ap=Ah(ji),lw=t=>(e,r,n)=>{let o=n?Object.assign(n,{direction:"backward"}):{direction:"backward"};return Th(t)(e,r,o)},gX=lw(ji),pw=t=>(e,r,n)=>Th(t)(e,r,n),_X=pw(ji),dw=t=>async(e,r,n)=>{let o=n?Object.assign(n,{direction:"backward"}):{direction:"backward"};return bh(t)(e,r,o)},vX=dw(ji),fw=t=>async(e,r,n)=>bh(t)(e,r,n),SX=fw(ji),mw=t=>(e,r,n)=>{let o=n?Object.assign(n,{direction:"backward"}):{direction:"backward"};return xh(t)(e,r,o)},yX=mw(ji),hw=t=>(e,r,n)=>xh(t)(e,r,n),EX=hw(ji),gw=t=>async(e,r,n)=>{let o=n?Object.assign(n,{direction:"backward"}):{direction:"backward"};return Ah(t)(e,r,o)},TX=gw(ji),_w=t=>async(e,r,n)=>Ah(t)(e,r,n),bX=_w(ji)});var zi={};Y(zi,{base64:()=>Ej,base64url:()=>vw,bigint:()=>Rj,boolean:()=>Ij,browserEmail:()=>FKe,cidrv4:()=>Sj,cidrv6:()=>yj,cuid:()=>sj,cuid2:()=>aj,date:()=>bj,datetime:()=>Aj,domain:()=>GKe,duration:()=>dj,e164:()=>Tj,email:()=>mj,emoji:()=>hj,extendedDuration:()=>kKe,guid:()=>fj,hex:()=>VKe,hostname:()=>BKe,html5Email:()=>UKe,idnEmail:()=>zKe,integer:()=>Pj,ipv4:()=>gj,ipv6:()=>_j,ksuid:()=>lj,lowercase:()=>Cj,mac:()=>vj,md5_base64:()=>WKe,md5_base64url:()=>KKe,md5_hex:()=>HKe,nanoid:()=>pj,null:()=>Oj,number:()=>Sw,rfc5322Email:()=>jKe,sha1_base64:()=>YKe,sha1_base64url:()=>JKe,sha1_hex:()=>ZKe,sha256_base64:()=>QKe,sha256_base64url:()=>eZe,sha256_hex:()=>XKe,sha384_base64:()=>rZe,sha384_base64url:()=>nZe,sha384_hex:()=>tZe,sha512_base64:()=>iZe,sha512_base64url:()=>sZe,sha512_hex:()=>oZe,string:()=>wj,time:()=>xj,ulid:()=>cj,undefined:()=>Nj,unicodeEmail:()=>xX,uppercase:()=>$j,uuid:()=>uf,uuid4:()=>MKe,uuid6:()=>DKe,uuid7:()=>LKe,xid:()=>uj});function hj(){return new RegExp(qKe,"u")}function wX(t){let e="(?:[01]\\d|2[0-3]):[0-5]\\d";return typeof t.precision=="number"?t.precision===-1?`${e}`:t.precision===0?`${e}:[0-5]\\d`:`${e}:[0-5]\\d\\.\\d{${t.precision}}`:`${e}(?::[0-5]\\d(?:\\.\\d+)?)?`}function xj(t){return new RegExp(`^${wX(t)}$`)}function Aj(t){let e=wX({precision:t.precision}),r=["Z"];t.local&&r.push(""),t.offset&&r.push("([+-](?:[01]\\d|2[0-3]):[0-5]\\d)");let n=`${e}(?:${r.join("|")})`;return new RegExp(`^${AX}T(?:${n})$`)}function dy(t,e){return new RegExp(`^[A-Za-z0-9+/]{${t}}${e}$`)}function fy(t){return new RegExp(`^[A-Za-z0-9_-]{${t}}$`)}var sj,aj,cj,uj,lj,pj,dj,kKe,fj,uf,MKe,DKe,LKe,mj,UKe,jKe,xX,zKe,FKe,qKe,gj,_j,vj,Sj,yj,Ej,vw,BKe,GKe,Tj,AX,bj,wj,Rj,Pj,Sw,Ij,Oj,Nj,Cj,$j,VKe,HKe,WKe,KKe,ZKe,YKe,JKe,XKe,QKe,eZe,tZe,rZe,nZe,oZe,iZe,sZe,yw=O(()=>{je();sj=/^[cC][^\s-]{8,}$/,aj=/^[0-9a-z]+$/,cj=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,uj=/^[0-9a-vA-V]{20}$/,lj=/^[A-Za-z0-9]{27}$/,pj=/^[a-zA-Z0-9_-]{21}$/,dj=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,kKe=/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/,fj=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,uf=t=>t?new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${t}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,MKe=uf(4),DKe=uf(6),LKe=uf(7),mj=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,UKe=/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,jKe=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,xX=/^[^\s@"]{1,64}@[^\s@]{1,255}$/u,zKe=xX,FKe=/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,qKe="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";gj=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,_j=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,vj=t=>{let e=Vs(t??":");return new RegExp(`^(?:[0-9A-F]{2}${e}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${e}){5}[0-9a-f]{2}$`)},Sj=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,yj=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,Ej=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,vw=/^[A-Za-z0-9_-]*$/,BKe=/^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/,GKe=/^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/,Tj=/^\+[1-9]\d{6,14}$/,AX="(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))",bj=new RegExp(`^${AX}$`);wj=t=>{let e=t?`[\\s\\S]{${t?.minimum??0},${t?.maximum??""}}`:"[\\s\\S]*";return new RegExp(`^${e}$`)},Rj=/^-?\d+n?$/,Pj=/^-?\d+$/,Sw=/^-?\d+(?:\.\d+)?$/,Ij=/^(?:true|false)$/i,Oj=/^null$/i,Nj=/^undefined$/i,Cj=/^[^A-Z]*$/,$j=/^[^a-z]*$/,VKe=/^[0-9a-fA-F]*$/;HKe=/^[0-9a-fA-F]{32}$/,WKe=dy(22,"=="),KKe=fy(22),ZKe=/^[0-9a-fA-F]{40}$/,YKe=dy(27,"="),JKe=fy(27),XKe=/^[0-9a-fA-F]{64}$/,QKe=dy(43,"="),eZe=fy(43),tZe=/^[0-9a-fA-F]{96}$/,rZe=dy(64,""),nZe=fy(64),oZe=/^[0-9a-fA-F]{128}$/,iZe=dy(86,"=="),sZe=fy(86)});function RX(t,e,r){t.issues.length&&e.issues.push(...ds(r,t.issues))}var wr,PX,Ew,Tw,kj,Mj,Dj,Lj,Uj,jj,zj,Fj,qj,wh,Bj,Gj,Vj,Hj,Wj,Kj,Zj,Yj,Jj,bw=O(()=>{hh();yw();je();wr=D("$ZodCheck",(t,e)=>{var r;t._zod??(t._zod={}),t._zod.def=e,(r=t._zod).onattach??(r.onattach=[])}),PX={number:"number",bigint:"bigint",object:"date"},Ew=D("$ZodCheckLessThan",(t,e)=>{wr.init(t,e);let r=PX[typeof e.value];t._zod.onattach.push(n=>{let o=n._zod.bag,i=(e.inclusive?o.maximum:o.exclusiveMaximum)??Number.POSITIVE_INFINITY;e.value{(e.inclusive?n.value<=e.value:n.value{wr.init(t,e);let r=PX[typeof e.value];t._zod.onattach.push(n=>{let o=n._zod.bag,i=(e.inclusive?o.minimum:o.exclusiveMinimum)??Number.NEGATIVE_INFINITY;e.value>i&&(e.inclusive?o.minimum=e.value:o.exclusiveMinimum=e.value)}),t._zod.check=n=>{(e.inclusive?n.value>=e.value:n.value>e.value)||n.issues.push({origin:r,code:"too_small",minimum:typeof e.value=="object"?e.value.getTime():e.value,input:n.value,inclusive:e.inclusive,inst:t,continue:!e.abort})}}),kj=D("$ZodCheckMultipleOf",(t,e)=>{wr.init(t,e),t._zod.onattach.push(r=>{var n;(n=r._zod.bag).multipleOf??(n.multipleOf=e.value)}),t._zod.check=r=>{if(typeof r.value!=typeof e.value)throw new Error("Cannot mix number and bigint in multiple_of check.");(typeof r.value=="bigint"?r.value%e.value===BigInt(0):YU(r.value,e.value)===0)||r.issues.push({origin:typeof r.value,code:"not_multiple_of",divisor:e.value,input:r.value,inst:t,continue:!e.abort})}}),Mj=D("$ZodCheckNumberFormat",(t,e)=>{wr.init(t,e),e.format=e.format||"float64";let r=e.format?.includes("int"),n=r?"int":"number",[o,i]=rj[e.format];t._zod.onattach.push(s=>{let a=s._zod.bag;a.format=e.format,a.minimum=o,a.maximum=i,r&&(a.pattern=Pj)}),t._zod.check=s=>{let a=s.value;if(r){if(!Number.isInteger(a)){s.issues.push({expected:n,format:e.format,code:"invalid_type",continue:!1,input:a,inst:t});return}if(!Number.isSafeInteger(a)){a>0?s.issues.push({input:a,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:t,origin:n,inclusive:!0,continue:!e.abort}):s.issues.push({input:a,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:t,origin:n,inclusive:!0,continue:!e.abort});return}}ai&&s.issues.push({origin:"number",input:a,code:"too_big",maximum:i,inclusive:!0,inst:t,continue:!e.abort})}}),Dj=D("$ZodCheckBigIntFormat",(t,e)=>{wr.init(t,e);let[r,n]=nj[e.format];t._zod.onattach.push(o=>{let i=o._zod.bag;i.format=e.format,i.minimum=r,i.maximum=n}),t._zod.check=o=>{let i=o.value;in&&o.issues.push({origin:"bigint",input:i,code:"too_big",maximum:n,inclusive:!0,inst:t,continue:!e.abort})}}),Lj=D("$ZodCheckMaxSize",(t,e)=>{var r;wr.init(t,e),(r=t._zod.def).when??(r.when=n=>{let o=n.value;return!rp(o)&&o.size!==void 0}),t._zod.onattach.push(n=>{let o=n._zod.bag.maximum??Number.POSITIVE_INFINITY;e.maximum{let o=n.value;o.size<=e.maximum||n.issues.push({origin:uy(o),code:"too_big",maximum:e.maximum,inclusive:!0,input:o,inst:t,continue:!e.abort})}}),Uj=D("$ZodCheckMinSize",(t,e)=>{var r;wr.init(t,e),(r=t._zod.def).when??(r.when=n=>{let o=n.value;return!rp(o)&&o.size!==void 0}),t._zod.onattach.push(n=>{let o=n._zod.bag.minimum??Number.NEGATIVE_INFINITY;e.minimum>o&&(n._zod.bag.minimum=e.minimum)}),t._zod.check=n=>{let o=n.value;o.size>=e.minimum||n.issues.push({origin:uy(o),code:"too_small",minimum:e.minimum,inclusive:!0,input:o,inst:t,continue:!e.abort})}}),jj=D("$ZodCheckSizeEquals",(t,e)=>{var r;wr.init(t,e),(r=t._zod.def).when??(r.when=n=>{let o=n.value;return!rp(o)&&o.size!==void 0}),t._zod.onattach.push(n=>{let o=n._zod.bag;o.minimum=e.size,o.maximum=e.size,o.size=e.size}),t._zod.check=n=>{let o=n.value,i=o.size;if(i===e.size)return;let s=i>e.size;n.issues.push({origin:uy(o),...s?{code:"too_big",maximum:e.size}:{code:"too_small",minimum:e.size},inclusive:!0,exact:!0,input:n.value,inst:t,continue:!e.abort})}}),zj=D("$ZodCheckMaxLength",(t,e)=>{var r;wr.init(t,e),(r=t._zod.def).when??(r.when=n=>{let o=n.value;return!rp(o)&&o.length!==void 0}),t._zod.onattach.push(n=>{let o=n._zod.bag.maximum??Number.POSITIVE_INFINITY;e.maximum{let o=n.value;if(o.length<=e.maximum)return;let s=ly(o);n.issues.push({origin:s,code:"too_big",maximum:e.maximum,inclusive:!0,input:o,inst:t,continue:!e.abort})}}),Fj=D("$ZodCheckMinLength",(t,e)=>{var r;wr.init(t,e),(r=t._zod.def).when??(r.when=n=>{let o=n.value;return!rp(o)&&o.length!==void 0}),t._zod.onattach.push(n=>{let o=n._zod.bag.minimum??Number.NEGATIVE_INFINITY;e.minimum>o&&(n._zod.bag.minimum=e.minimum)}),t._zod.check=n=>{let o=n.value;if(o.length>=e.minimum)return;let s=ly(o);n.issues.push({origin:s,code:"too_small",minimum:e.minimum,inclusive:!0,input:o,inst:t,continue:!e.abort})}}),qj=D("$ZodCheckLengthEquals",(t,e)=>{var r;wr.init(t,e),(r=t._zod.def).when??(r.when=n=>{let o=n.value;return!rp(o)&&o.length!==void 0}),t._zod.onattach.push(n=>{let o=n._zod.bag;o.minimum=e.length,o.maximum=e.length,o.length=e.length}),t._zod.check=n=>{let o=n.value,i=o.length;if(i===e.length)return;let s=ly(o),a=i>e.length;n.issues.push({origin:s,...a?{code:"too_big",maximum:e.length}:{code:"too_small",minimum:e.length},inclusive:!0,exact:!0,input:n.value,inst:t,continue:!e.abort})}}),wh=D("$ZodCheckStringFormat",(t,e)=>{var r,n;wr.init(t,e),t._zod.onattach.push(o=>{let i=o._zod.bag;i.format=e.format,e.pattern&&(i.patterns??(i.patterns=new Set),i.patterns.add(e.pattern))}),e.pattern?(r=t._zod).check??(r.check=o=>{e.pattern.lastIndex=0,!e.pattern.test(o.value)&&o.issues.push({origin:"string",code:"invalid_format",format:e.format,input:o.value,...e.pattern?{pattern:e.pattern.toString()}:{},inst:t,continue:!e.abort})}):(n=t._zod).check??(n.check=()=>{})}),Bj=D("$ZodCheckRegex",(t,e)=>{wh.init(t,e),t._zod.check=r=>{e.pattern.lastIndex=0,!e.pattern.test(r.value)&&r.issues.push({origin:"string",code:"invalid_format",format:"regex",input:r.value,pattern:e.pattern.toString(),inst:t,continue:!e.abort})}}),Gj=D("$ZodCheckLowerCase",(t,e)=>{e.pattern??(e.pattern=Cj),wh.init(t,e)}),Vj=D("$ZodCheckUpperCase",(t,e)=>{e.pattern??(e.pattern=$j),wh.init(t,e)}),Hj=D("$ZodCheckIncludes",(t,e)=>{wr.init(t,e);let r=Vs(e.includes),n=new RegExp(typeof e.position=="number"?`^.{${e.position}}${r}`:r);e.pattern=n,t._zod.onattach.push(o=>{let i=o._zod.bag;i.patterns??(i.patterns=new Set),i.patterns.add(n)}),t._zod.check=o=>{o.value.includes(e.includes,e.position)||o.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:e.includes,input:o.value,inst:t,continue:!e.abort})}}),Wj=D("$ZodCheckStartsWith",(t,e)=>{wr.init(t,e);let r=new RegExp(`^${Vs(e.prefix)}.*`);e.pattern??(e.pattern=r),t._zod.onattach.push(n=>{let o=n._zod.bag;o.patterns??(o.patterns=new Set),o.patterns.add(r)}),t._zod.check=n=>{n.value.startsWith(e.prefix)||n.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:e.prefix,input:n.value,inst:t,continue:!e.abort})}}),Kj=D("$ZodCheckEndsWith",(t,e)=>{wr.init(t,e);let r=new RegExp(`.*${Vs(e.suffix)}$`);e.pattern??(e.pattern=r),t._zod.onattach.push(n=>{let o=n._zod.bag;o.patterns??(o.patterns=new Set),o.patterns.add(r)}),t._zod.check=n=>{n.value.endsWith(e.suffix)||n.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:e.suffix,input:n.value,inst:t,continue:!e.abort})}});Zj=D("$ZodCheckProperty",(t,e)=>{wr.init(t,e),t._zod.check=r=>{let n=e.schema._zod.run({value:r.value[e.property],issues:[]},{});if(n instanceof Promise)return n.then(o=>RX(o,r,e.property));RX(n,r,e.property)}}),Yj=D("$ZodCheckMimeType",(t,e)=>{wr.init(t,e);let r=new Set(e.mime);t._zod.onattach.push(n=>{n._zod.bag.mime=e.mime}),t._zod.check=n=>{r.has(n.value.type)||n.issues.push({code:"invalid_value",values:e.mime,input:n.value.type,inst:t,continue:!e.abort})}}),Jj=D("$ZodCheckOverwrite",(t,e)=>{wr.init(t,e),t._zod.check=r=>{r.value=e.tx(r.value)}})});var my,Xj=O(()=>{my=class{constructor(e=[]){this.content=[],this.indent=0,this&&(this.args=e)}indented(e){this.indent+=1,e(this),this.indent-=1}write(e){if(typeof e=="function"){e(this,{execution:"sync"}),e(this,{execution:"async"});return}let n=e.split(` -`).filter(s=>s),o=Math.min(...n.map(s=>s.length-s.trimStart().length)),i=n.map(s=>s.slice(o)).map(s=>" ".repeat(this.indent*2)+s);for(let s of i)this.content.push(s)}compile(){let e=Function,r=this?.args,o=[...(this?.content??[""]).map(i=>` ${i}`)];return new e(...r,o.join(` -`))}}});var Qj,ez=O(()=>{Qj={major:4,minor:3,patch:2}});function rz(t){if(t==="")return!0;if(t.length%4!==0)return!1;try{return atob(t),!0}catch{return!1}}function FX(t){if(!vw.test(t))return!1;let e=t.replace(/[-_]/g,n=>n==="-"?"+":"/"),r=e.padEnd(Math.ceil(e.length/4)*4,"=");return rz(r)}function qX(t,e=null){try{let r=t.split(".");if(r.length!==3)return!1;let[n]=r;if(!n)return!1;let o=JSON.parse(atob(n));return!("typ"in o&&o?.typ!=="JWT"||!o.alg||e&&(!("alg"in o)||o.alg!==e))}catch{return!1}}function OX(t,e,r){t.issues.length&&e.issues.push(...ds(r,t.issues)),e.value[r]=t.value}function Pw(t,e,r,n,o){if(t.issues.length){if(o&&!(r in n))return;e.issues.push(...ds(r,t.issues))}t.value===void 0?r in n&&(e.value[r]=void 0):e.value[r]=t.value}function BX(t){let e=Object.keys(t.shape);for(let n of e)if(!t.shape?.[n]?._zod?.traits?.has("$ZodType"))throw new Error(`Invalid element at key "${n}": expected a Zod schema`);let r=tj(t.shape);return{...t,keys:e,keySet:new Set(e),numKeys:e.length,optionalKeys:new Set(r)}}function GX(t,e,r,n,o,i){let s=[],a=o.keySet,c=o.catchall._zod,u=c.def.type,p=c.optout==="optional";for(let f in e){if(a.has(f))continue;if(u==="never"){s.push(f);continue}let m=c.run({value:e[f],issues:[]},n);m instanceof Promise?t.push(m.then(h=>Pw(h,r,f,e,p))):Pw(m,r,f,e,p)}return s.length&&r.issues.push({code:"unrecognized_keys",keys:s,input:e,inst:i}),t.length?Promise.all(t).then(()=>r):r}function NX(t,e,r,n){for(let i of t)if(i.issues.length===0)return e.value=i.value,e;let o=t.filter(i=>!ip(i));return o.length===1?(e.value=o[0].value,o[0]):(e.issues.push({code:"invalid_union",input:e.value,inst:r,errors:t.map(i=>i.issues.map(s=>Ui(s,n,cn())))}),e)}function CX(t,e,r,n){let o=t.filter(i=>i.issues.length===0);return o.length===1?(e.value=o[0].value,e):(o.length===0?e.issues.push({code:"invalid_union",input:e.value,inst:r,errors:t.map(i=>i.issues.map(s=>Ui(s,n,cn())))}):e.issues.push({code:"invalid_union",input:e.value,inst:r,errors:[],inclusive:!1}),e)}function tz(t,e){if(t===e)return{valid:!0,data:t};if(t instanceof Date&&e instanceof Date&&+t==+e)return{valid:!0,data:t};if(op(t)&&op(e)){let r=Object.keys(e),n=Object.keys(t).filter(i=>r.indexOf(i)!==-1),o={...t,...e};for(let i of n){let s=tz(t[i],e[i]);if(!s.valid)return{valid:!1,mergeErrorPath:[i,...s.mergeErrorPath]};o[i]=s.data}return{valid:!0,data:o}}if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return{valid:!1,mergeErrorPath:[]};let r=[];for(let n=0;na.l&&a.r).map(([a])=>a);if(i.length&&o&&t.issues.push({...o,keys:i}),ip(t))return t;let s=tz(e.value,r.value);if(!s.valid)throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(s.mergeErrorPath)}`);return t.value=s.data,t}function xw(t,e,r){t.issues.length&&e.issues.push(...ds(r,t.issues)),e.value[r]=t.value}function kX(t,e,r,n,o,i,s){t.issues.length&&(cy.has(typeof n)?r.issues.push(...ds(n,t.issues)):r.issues.push({code:"invalid_key",origin:"map",input:o,inst:i,issues:t.issues.map(a=>Ui(a,s,cn()))})),e.issues.length&&(cy.has(typeof n)?r.issues.push(...ds(n,e.issues)):r.issues.push({origin:"map",code:"invalid_element",input:o,inst:i,key:n,issues:e.issues.map(a=>Ui(a,s,cn()))})),r.value.set(t.value,e.value)}function MX(t,e){t.issues.length&&e.issues.push(...t.issues),e.value.add(t.value)}function DX(t,e){return t.issues.length&&e===void 0?{issues:[],value:void 0}:t}function LX(t,e){return t.value===void 0&&(t.value=e.defaultValue),t}function UX(t,e){return!t.issues.length&&t.value===void 0&&t.issues.push({code:"invalid_type",expected:"nonoptional",input:t.value,inst:e}),t}function Aw(t,e,r){return t.issues.length?(t.aborted=!0,t):e._zod.run({value:t.value,issues:t.issues},r)}function ww(t,e,r){if(t.issues.length)return t.aborted=!0,t;if((r.direction||"forward")==="forward"){let o=e.transform(t.value,t);return o instanceof Promise?o.then(i=>Rw(t,i,e.out,r)):Rw(t,o,e.out,r)}else{let o=e.reverseTransform(t.value,t);return o instanceof Promise?o.then(i=>Rw(t,i,e.in,r)):Rw(t,o,e.in,r)}}function Rw(t,e,r,n){return t.issues.length?(t.aborted=!0,t):r._zod.run({value:e,issues:t.issues},n)}function jX(t){return t.value=Object.freeze(t.value),t}function zX(t,e,r,n){if(!t){let o={code:"custom",input:r,inst:n,path:[...n._zod.def.path??[]],continue:!n._zod.def.abort};n._zod.def.params&&(o.params=n._zod.def.params),e.issues.push(Sh(o))}}var nt,cp,Er,Iw,Ow,Nw,Cw,$w,kw,Mw,Dw,Lw,Uw,jw,zw,Fw,qw,Bw,Gw,Vw,Hw,Ww,Kw,Zw,Yw,Jw,Xw,Qw,hy,eR,Rh,gy,tR,rR,nR,oR,iR,sR,aR,cR,uR,lR,pR,nz,Ph,dR,fR,mR,_y,hR,gR,_R,vR,SR,yR,ER,vy,TR,bR,xR,AR,wR,RR,PR,IR,OR,Ih,NR,CR,$R,kR,MR,Sy,oz=O(()=>{bw();hh();Xj();ij();yw();je();ez();je();nt=D("$ZodType",(t,e)=>{var r;t??(t={}),t._zod.def=e,t._zod.bag=t._zod.bag||{},t._zod.version=Qj;let n=[...t._zod.def.checks??[]];t._zod.traits.has("$ZodCheck")&&n.unshift(t);for(let o of n)for(let i of o._zod.onattach)i(t);if(n.length===0)(r=t._zod).deferred??(r.deferred=[]),t._zod.deferred?.push(()=>{t._zod.run=t._zod.parse});else{let o=(s,a,c)=>{let u=ip(s),p;for(let f of a){if(f._zod.def.when){if(!f._zod.def.when(s))continue}else if(u)continue;let m=s.issues.length,h=f._zod.check(s);if(h instanceof Promise&&c?.async===!1)throw new Ga;if(p||h instanceof Promise)p=(p??Promise.resolve()).then(async()=>{await h,s.issues.length!==m&&(u||(u=ip(s,m)))});else{if(s.issues.length===m)continue;u||(u=ip(s,m))}}return p?p.then(()=>s):s},i=(s,a,c)=>{if(ip(s))return s.aborted=!0,s;let u=o(a,n,c);if(u instanceof Promise){if(c.async===!1)throw new Ga;return u.then(p=>t._zod.parse(p,c))}return t._zod.parse(u,c)};t._zod.run=(s,a)=>{if(a.skipChecks)return t._zod.parse(s,a);if(a.direction==="backward"){let u=t._zod.parse({value:s.value,issues:[]},{...a,skipChecks:!0});return u instanceof Promise?u.then(p=>i(p,s,a)):i(u,s,a)}let c=t._zod.parse(s,a);if(c instanceof Promise){if(a.async===!1)throw new Ga;return c.then(u=>o(u,n,a))}return o(c,n,a)}}Rt(t,"~standard",()=>({validate:o=>{try{let i=sp(t,o);return i.success?{value:i.data}:{issues:i.error?.issues}}catch{return ap(t,o).then(s=>s.success?{value:s.data}:{issues:s.error?.issues})}},vendor:"zod",version:1}))}),cp=D("$ZodString",(t,e)=>{nt.init(t,e),t._zod.pattern=[...t?._zod.bag?.patterns??[]].pop()??wj(t._zod.bag),t._zod.parse=(r,n)=>{if(e.coerce)try{r.value=String(r.value)}catch{}return typeof r.value=="string"||r.issues.push({expected:"string",code:"invalid_type",input:r.value,inst:t}),r}}),Er=D("$ZodStringFormat",(t,e)=>{wh.init(t,e),cp.init(t,e)}),Iw=D("$ZodGUID",(t,e)=>{e.pattern??(e.pattern=fj),Er.init(t,e)}),Ow=D("$ZodUUID",(t,e)=>{if(e.version){let n={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8}[e.version];if(n===void 0)throw new Error(`Invalid UUID version: "${e.version}"`);e.pattern??(e.pattern=uf(n))}else e.pattern??(e.pattern=uf());Er.init(t,e)}),Nw=D("$ZodEmail",(t,e)=>{e.pattern??(e.pattern=mj),Er.init(t,e)}),Cw=D("$ZodURL",(t,e)=>{Er.init(t,e),t._zod.check=r=>{try{let n=r.value.trim(),o=new URL(n);e.hostname&&(e.hostname.lastIndex=0,e.hostname.test(o.hostname)||r.issues.push({code:"invalid_format",format:"url",note:"Invalid hostname",pattern:e.hostname.source,input:r.value,inst:t,continue:!e.abort})),e.protocol&&(e.protocol.lastIndex=0,e.protocol.test(o.protocol.endsWith(":")?o.protocol.slice(0,-1):o.protocol)||r.issues.push({code:"invalid_format",format:"url",note:"Invalid protocol",pattern:e.protocol.source,input:r.value,inst:t,continue:!e.abort})),e.normalize?r.value=o.href:r.value=n;return}catch{r.issues.push({code:"invalid_format",format:"url",input:r.value,inst:t,continue:!e.abort})}}}),$w=D("$ZodEmoji",(t,e)=>{e.pattern??(e.pattern=hj()),Er.init(t,e)}),kw=D("$ZodNanoID",(t,e)=>{e.pattern??(e.pattern=pj),Er.init(t,e)}),Mw=D("$ZodCUID",(t,e)=>{e.pattern??(e.pattern=sj),Er.init(t,e)}),Dw=D("$ZodCUID2",(t,e)=>{e.pattern??(e.pattern=aj),Er.init(t,e)}),Lw=D("$ZodULID",(t,e)=>{e.pattern??(e.pattern=cj),Er.init(t,e)}),Uw=D("$ZodXID",(t,e)=>{e.pattern??(e.pattern=uj),Er.init(t,e)}),jw=D("$ZodKSUID",(t,e)=>{e.pattern??(e.pattern=lj),Er.init(t,e)}),zw=D("$ZodISODateTime",(t,e)=>{e.pattern??(e.pattern=Aj(e)),Er.init(t,e)}),Fw=D("$ZodISODate",(t,e)=>{e.pattern??(e.pattern=bj),Er.init(t,e)}),qw=D("$ZodISOTime",(t,e)=>{e.pattern??(e.pattern=xj(e)),Er.init(t,e)}),Bw=D("$ZodISODuration",(t,e)=>{e.pattern??(e.pattern=dj),Er.init(t,e)}),Gw=D("$ZodIPv4",(t,e)=>{e.pattern??(e.pattern=gj),Er.init(t,e),t._zod.bag.format="ipv4"}),Vw=D("$ZodIPv6",(t,e)=>{e.pattern??(e.pattern=_j),Er.init(t,e),t._zod.bag.format="ipv6",t._zod.check=r=>{try{new URL(`http://[${r.value}]`)}catch{r.issues.push({code:"invalid_format",format:"ipv6",input:r.value,inst:t,continue:!e.abort})}}}),Hw=D("$ZodMAC",(t,e)=>{e.pattern??(e.pattern=vj(e.delimiter)),Er.init(t,e),t._zod.bag.format="mac"}),Ww=D("$ZodCIDRv4",(t,e)=>{e.pattern??(e.pattern=Sj),Er.init(t,e)}),Kw=D("$ZodCIDRv6",(t,e)=>{e.pattern??(e.pattern=yj),Er.init(t,e),t._zod.check=r=>{let n=r.value.split("/");try{if(n.length!==2)throw new Error;let[o,i]=n;if(!i)throw new Error;let s=Number(i);if(`${s}`!==i)throw new Error;if(s<0||s>128)throw new Error;new URL(`http://[${o}]`)}catch{r.issues.push({code:"invalid_format",format:"cidrv6",input:r.value,inst:t,continue:!e.abort})}}});Zw=D("$ZodBase64",(t,e)=>{e.pattern??(e.pattern=Ej),Er.init(t,e),t._zod.bag.contentEncoding="base64",t._zod.check=r=>{rz(r.value)||r.issues.push({code:"invalid_format",format:"base64",input:r.value,inst:t,continue:!e.abort})}});Yw=D("$ZodBase64URL",(t,e)=>{e.pattern??(e.pattern=vw),Er.init(t,e),t._zod.bag.contentEncoding="base64url",t._zod.check=r=>{FX(r.value)||r.issues.push({code:"invalid_format",format:"base64url",input:r.value,inst:t,continue:!e.abort})}}),Jw=D("$ZodE164",(t,e)=>{e.pattern??(e.pattern=Tj),Er.init(t,e)});Xw=D("$ZodJWT",(t,e)=>{Er.init(t,e),t._zod.check=r=>{qX(r.value,e.alg)||r.issues.push({code:"invalid_format",format:"jwt",input:r.value,inst:t,continue:!e.abort})}}),Qw=D("$ZodCustomStringFormat",(t,e)=>{Er.init(t,e),t._zod.check=r=>{e.fn(r.value)||r.issues.push({code:"invalid_format",format:e.format,input:r.value,inst:t,continue:!e.abort})}}),hy=D("$ZodNumber",(t,e)=>{nt.init(t,e),t._zod.pattern=t._zod.bag.pattern??Sw,t._zod.parse=(r,n)=>{if(e.coerce)try{r.value=Number(r.value)}catch{}let o=r.value;if(typeof o=="number"&&!Number.isNaN(o)&&Number.isFinite(o))return r;let i=typeof o=="number"?Number.isNaN(o)?"NaN":Number.isFinite(o)?void 0:"Infinity":void 0;return r.issues.push({expected:"number",code:"invalid_type",input:o,inst:t,...i?{received:i}:{}}),r}}),eR=D("$ZodNumberFormat",(t,e)=>{Mj.init(t,e),hy.init(t,e)}),Rh=D("$ZodBoolean",(t,e)=>{nt.init(t,e),t._zod.pattern=Ij,t._zod.parse=(r,n)=>{if(e.coerce)try{r.value=!!r.value}catch{}let o=r.value;return typeof o=="boolean"||r.issues.push({expected:"boolean",code:"invalid_type",input:o,inst:t}),r}}),gy=D("$ZodBigInt",(t,e)=>{nt.init(t,e),t._zod.pattern=Rj,t._zod.parse=(r,n)=>{if(e.coerce)try{r.value=BigInt(r.value)}catch{}return typeof r.value=="bigint"||r.issues.push({expected:"bigint",code:"invalid_type",input:r.value,inst:t}),r}}),tR=D("$ZodBigIntFormat",(t,e)=>{Dj.init(t,e),gy.init(t,e)}),rR=D("$ZodSymbol",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value;return typeof o=="symbol"||r.issues.push({expected:"symbol",code:"invalid_type",input:o,inst:t}),r}}),nR=D("$ZodUndefined",(t,e)=>{nt.init(t,e),t._zod.pattern=Nj,t._zod.values=new Set([void 0]),t._zod.optin="optional",t._zod.optout="optional",t._zod.parse=(r,n)=>{let o=r.value;return typeof o>"u"||r.issues.push({expected:"undefined",code:"invalid_type",input:o,inst:t}),r}}),oR=D("$ZodNull",(t,e)=>{nt.init(t,e),t._zod.pattern=Oj,t._zod.values=new Set([null]),t._zod.parse=(r,n)=>{let o=r.value;return o===null||r.issues.push({expected:"null",code:"invalid_type",input:o,inst:t}),r}}),iR=D("$ZodAny",(t,e)=>{nt.init(t,e),t._zod.parse=r=>r}),sR=D("$ZodUnknown",(t,e)=>{nt.init(t,e),t._zod.parse=r=>r}),aR=D("$ZodNever",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>(r.issues.push({expected:"never",code:"invalid_type",input:r.value,inst:t}),r)}),cR=D("$ZodVoid",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value;return typeof o>"u"||r.issues.push({expected:"void",code:"invalid_type",input:o,inst:t}),r}}),uR=D("$ZodDate",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{if(e.coerce)try{r.value=new Date(r.value)}catch{}let o=r.value,i=o instanceof Date;return i&&!Number.isNaN(o.getTime())||r.issues.push({expected:"date",code:"invalid_type",input:o,...i?{received:"Invalid Date"}:{},inst:t}),r}});lR=D("$ZodArray",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value;if(!Array.isArray(o))return r.issues.push({expected:"array",code:"invalid_type",input:o,inst:t}),r;r.value=Array(o.length);let i=[];for(let s=0;sOX(u,r,s))):OX(c,r,s)}return i.length?Promise.all(i).then(()=>r):r}});pR=D("$ZodObject",(t,e)=>{if(nt.init(t,e),!Object.getOwnPropertyDescriptor(e,"shape")?.get){let a=e.shape;Object.defineProperty(e,"shape",{get:()=>{let c={...a};return Object.defineProperty(e,"shape",{value:c}),c}})}let n=vh(()=>BX(e));Rt(t._zod,"propValues",()=>{let a=e.shape,c={};for(let u in a){let p=a[u]._zod;if(p.values){c[u]??(c[u]=new Set);for(let f of p.values)c[u].add(f)}}return c});let o=sf,i=e.catchall,s;t._zod.parse=(a,c)=>{s??(s=n.value);let u=a.value;if(!o(u))return a.issues.push({expected:"object",code:"invalid_type",input:u,inst:t}),a;a.value={};let p=[],f=s.shape;for(let m of s.keys){let h=f[m],_=h._zod.optout==="optional",v=h._zod.run({value:u[m],issues:[]},c);v instanceof Promise?p.push(v.then(E=>Pw(E,a,m,u,_))):Pw(v,a,m,u,_)}return i?GX(p,u,a,c,n.value,t):p.length?Promise.all(p).then(()=>a):a}}),nz=D("$ZodObjectJIT",(t,e)=>{pR.init(t,e);let r=t._zod.parse,n=vh(()=>BX(e)),o=m=>{let h=new my(["shape","payload","ctx"]),_=n.value,v=I=>{let N=sw(I);return`shape[${N}]._zod.run({ value: input[${N}], issues: [] }, ctx)`};h.write("const input = payload.value;");let E=Object.create(null),x=0;for(let I of _.keys)E[I]=`key_${x++}`;h.write("const newResult = {};");for(let I of _.keys){let N=E[I],$=sw(I),G=m[I]?._zod?.optout==="optional";h.write(`const ${N} = ${v(I)};`),G?h.write(` - if (${N}.issues.length) { - if (${$} in input) { - payload.issues = payload.issues.concat(${N}.issues.map(iss => ({ - ...iss, - path: iss.path ? [${$}, ...iss.path] : [${$}] - }))); - } - } - - if (${N}.value === undefined) { - if (${$} in input) { - newResult[${$}] = undefined; - } - } else { - newResult[${$}] = ${N}.value; - } - - `):h.write(` - if (${N}.issues.length) { - payload.issues = payload.issues.concat(${N}.issues.map(iss => ({ - ...iss, - path: iss.path ? [${$}, ...iss.path] : [${$}] - }))); - } - - if (${N}.value === undefined) { - if (${$} in input) { - newResult[${$}] = undefined; - } - } else { - newResult[${$}] = ${N}.value; - } - - `)}h.write("payload.value = newResult;"),h.write("return payload;");let w=h.compile();return(I,N)=>w(m,I,N)},i,s=sf,a=!oy.jitless,u=a&&XU.value,p=e.catchall,f;t._zod.parse=(m,h)=>{f??(f=n.value);let _=m.value;return s(_)?a&&u&&h?.async===!1&&h.jitless!==!0?(i||(i=o(e.shape)),m=i(m,h),p?GX([],_,m,h,f,t):m):r(m,h):(m.issues.push({expected:"object",code:"invalid_type",input:_,inst:t}),m)}});Ph=D("$ZodUnion",(t,e)=>{nt.init(t,e),Rt(t._zod,"optin",()=>e.options.some(o=>o._zod.optin==="optional")?"optional":void 0),Rt(t._zod,"optout",()=>e.options.some(o=>o._zod.optout==="optional")?"optional":void 0),Rt(t._zod,"values",()=>{if(e.options.every(o=>o._zod.values))return new Set(e.options.flatMap(o=>Array.from(o._zod.values)))}),Rt(t._zod,"pattern",()=>{if(e.options.every(o=>o._zod.pattern)){let o=e.options.map(i=>i._zod.pattern);return new RegExp(`^(${o.map(i=>ay(i.source)).join("|")})$`)}});let r=e.options.length===1,n=e.options[0]._zod.run;t._zod.parse=(o,i)=>{if(r)return n(o,i);let s=!1,a=[];for(let c of e.options){let u=c._zod.run({value:o.value,issues:[]},i);if(u instanceof Promise)a.push(u),s=!0;else{if(u.issues.length===0)return u;a.push(u)}}return s?Promise.all(a).then(c=>NX(c,o,t,i)):NX(a,o,t,i)}});dR=D("$ZodXor",(t,e)=>{Ph.init(t,e),e.inclusive=!1;let r=e.options.length===1,n=e.options[0]._zod.run;t._zod.parse=(o,i)=>{if(r)return n(o,i);let s=!1,a=[];for(let c of e.options){let u=c._zod.run({value:o.value,issues:[]},i);u instanceof Promise?(a.push(u),s=!0):a.push(u)}return s?Promise.all(a).then(c=>CX(c,o,t,i)):CX(a,o,t,i)}}),fR=D("$ZodDiscriminatedUnion",(t,e)=>{e.inclusive=!1,Ph.init(t,e);let r=t._zod.parse;Rt(t._zod,"propValues",()=>{let o={};for(let i of e.options){let s=i._zod.propValues;if(!s||Object.keys(s).length===0)throw new Error(`Invalid discriminated union option at index "${e.options.indexOf(i)}"`);for(let[a,c]of Object.entries(s)){o[a]||(o[a]=new Set);for(let u of c)o[a].add(u)}}return o});let n=vh(()=>{let o=e.options,i=new Map;for(let s of o){let a=s._zod.propValues?.[e.discriminator];if(!a||a.size===0)throw new Error(`Invalid discriminated union option at index "${e.options.indexOf(s)}"`);for(let c of a){if(i.has(c))throw new Error(`Duplicate discriminator value "${String(c)}"`);i.set(c,s)}}return i});t._zod.parse=(o,i)=>{let s=o.value;if(!sf(s))return o.issues.push({code:"invalid_type",expected:"object",input:s,inst:t}),o;let a=n.value.get(s?.[e.discriminator]);return a?a._zod.run(o,i):e.unionFallback?r(o,i):(o.issues.push({code:"invalid_union",errors:[],note:"No matching discriminator",discriminator:e.discriminator,input:s,path:[e.discriminator],inst:t}),o)}}),mR=D("$ZodIntersection",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value,i=e.left._zod.run({value:o,issues:[]},n),s=e.right._zod.run({value:o,issues:[]},n);return i instanceof Promise||s instanceof Promise?Promise.all([i,s]).then(([c,u])=>$X(r,c,u)):$X(r,i,s)}});_y=D("$ZodTuple",(t,e)=>{nt.init(t,e);let r=e.items;t._zod.parse=(n,o)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({input:i,inst:t,expected:"tuple",code:"invalid_type"}),n;n.value=[];let s=[],a=[...r].reverse().findIndex(p=>p._zod.optin!=="optional"),c=a===-1?0:r.length-a;if(!e.rest){let p=i.length>r.length,f=i.length=i.length&&u>=c)continue;let f=p._zod.run({value:i[u],issues:[]},o);f instanceof Promise?s.push(f.then(m=>xw(m,n,u))):xw(f,n,u)}if(e.rest){let p=i.slice(r.length);for(let f of p){u++;let m=e.rest._zod.run({value:f,issues:[]},o);m instanceof Promise?s.push(m.then(h=>xw(h,n,u))):xw(m,n,u)}}return s.length?Promise.all(s).then(()=>n):n}});hR=D("$ZodRecord",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value;if(!op(o))return r.issues.push({expected:"record",code:"invalid_type",input:o,inst:t}),r;let i=[],s=e.keyType._zod.values;if(s){r.value={};let a=new Set;for(let u of s)if(typeof u=="string"||typeof u=="number"||typeof u=="symbol"){a.add(typeof u=="number"?u.toString():u);let p=e.valueType._zod.run({value:o[u],issues:[]},n);p instanceof Promise?i.push(p.then(f=>{f.issues.length&&r.issues.push(...ds(u,f.issues)),r.value[u]=f.value})):(p.issues.length&&r.issues.push(...ds(u,p.issues)),r.value[u]=p.value)}let c;for(let u in o)a.has(u)||(c=c??[],c.push(u));c&&c.length>0&&r.issues.push({code:"unrecognized_keys",input:o,inst:t,keys:c})}else{r.value={};for(let a of Reflect.ownKeys(o)){if(a==="__proto__")continue;let c=e.keyType._zod.run({value:a,issues:[]},n);if(c instanceof Promise)throw new Error("Async schemas not supported in object keys currently");if(typeof a=="string"&&Sw.test(a)&&c.issues.length&&c.issues.some(f=>f.code==="invalid_type"&&f.expected==="number")){let f=e.keyType._zod.run({value:Number(a),issues:[]},n);if(f instanceof Promise)throw new Error("Async schemas not supported in object keys currently");f.issues.length===0&&(c=f)}if(c.issues.length){e.mode==="loose"?r.value[a]=o[a]:r.issues.push({code:"invalid_key",origin:"record",issues:c.issues.map(f=>Ui(f,n,cn())),input:a,path:[a],inst:t});continue}let p=e.valueType._zod.run({value:o[a],issues:[]},n);p instanceof Promise?i.push(p.then(f=>{f.issues.length&&r.issues.push(...ds(a,f.issues)),r.value[c.value]=f.value})):(p.issues.length&&r.issues.push(...ds(a,p.issues)),r.value[c.value]=p.value)}}return i.length?Promise.all(i).then(()=>r):r}}),gR=D("$ZodMap",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value;if(!(o instanceof Map))return r.issues.push({expected:"map",code:"invalid_type",input:o,inst:t}),r;let i=[];r.value=new Map;for(let[s,a]of o){let c=e.keyType._zod.run({value:s,issues:[]},n),u=e.valueType._zod.run({value:a,issues:[]},n);c instanceof Promise||u instanceof Promise?i.push(Promise.all([c,u]).then(([p,f])=>{kX(p,f,r,s,o,t,n)})):kX(c,u,r,s,o,t,n)}return i.length?Promise.all(i).then(()=>r):r}});_R=D("$ZodSet",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value;if(!(o instanceof Set))return r.issues.push({input:o,inst:t,expected:"set",code:"invalid_type"}),r;let i=[];r.value=new Set;for(let s of o){let a=e.valueType._zod.run({value:s,issues:[]},n);a instanceof Promise?i.push(a.then(c=>MX(c,r))):MX(a,r)}return i.length?Promise.all(i).then(()=>r):r}});vR=D("$ZodEnum",(t,e)=>{nt.init(t,e);let r=gh(e.entries),n=new Set(r);t._zod.values=n,t._zod.pattern=new RegExp(`^(${r.filter(o=>cy.has(typeof o)).map(o=>typeof o=="string"?Vs(o):o.toString()).join("|")})$`),t._zod.parse=(o,i)=>{let s=o.value;return n.has(s)||o.issues.push({code:"invalid_value",values:r,input:s,inst:t}),o}}),SR=D("$ZodLiteral",(t,e)=>{if(nt.init(t,e),e.values.length===0)throw new Error("Cannot create literal schema with no valid values");let r=new Set(e.values);t._zod.values=r,t._zod.pattern=new RegExp(`^(${e.values.map(n=>typeof n=="string"?Vs(n):n?Vs(n.toString()):String(n)).join("|")})$`),t._zod.parse=(n,o)=>{let i=n.value;return r.has(i)||n.issues.push({code:"invalid_value",values:e.values,input:i,inst:t}),n}}),yR=D("$ZodFile",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{let o=r.value;return o instanceof File||r.issues.push({expected:"file",code:"invalid_type",input:o,inst:t}),r}}),ER=D("$ZodTransform",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{if(n.direction==="backward")throw new tp(t.constructor.name);let o=e.transform(r.value,r);if(n.async)return(o instanceof Promise?o:Promise.resolve(o)).then(s=>(r.value=s,r));if(o instanceof Promise)throw new Ga;return r.value=o,r}});vy=D("$ZodOptional",(t,e)=>{nt.init(t,e),t._zod.optin="optional",t._zod.optout="optional",Rt(t._zod,"values",()=>e.innerType._zod.values?new Set([...e.innerType._zod.values,void 0]):void 0),Rt(t._zod,"pattern",()=>{let r=e.innerType._zod.pattern;return r?new RegExp(`^(${ay(r.source)})?$`):void 0}),t._zod.parse=(r,n)=>{if(e.innerType._zod.optin==="optional"){let o=e.innerType._zod.run(r,n);return o instanceof Promise?o.then(i=>DX(i,r.value)):DX(o,r.value)}return r.value===void 0?r:e.innerType._zod.run(r,n)}}),TR=D("$ZodExactOptional",(t,e)=>{vy.init(t,e),Rt(t._zod,"values",()=>e.innerType._zod.values),Rt(t._zod,"pattern",()=>e.innerType._zod.pattern),t._zod.parse=(r,n)=>e.innerType._zod.run(r,n)}),bR=D("$ZodNullable",(t,e)=>{nt.init(t,e),Rt(t._zod,"optin",()=>e.innerType._zod.optin),Rt(t._zod,"optout",()=>e.innerType._zod.optout),Rt(t._zod,"pattern",()=>{let r=e.innerType._zod.pattern;return r?new RegExp(`^(${ay(r.source)}|null)$`):void 0}),Rt(t._zod,"values",()=>e.innerType._zod.values?new Set([...e.innerType._zod.values,null]):void 0),t._zod.parse=(r,n)=>r.value===null?r:e.innerType._zod.run(r,n)}),xR=D("$ZodDefault",(t,e)=>{nt.init(t,e),t._zod.optin="optional",Rt(t._zod,"values",()=>e.innerType._zod.values),t._zod.parse=(r,n)=>{if(n.direction==="backward")return e.innerType._zod.run(r,n);if(r.value===void 0)return r.value=e.defaultValue,r;let o=e.innerType._zod.run(r,n);return o instanceof Promise?o.then(i=>LX(i,e)):LX(o,e)}});AR=D("$ZodPrefault",(t,e)=>{nt.init(t,e),t._zod.optin="optional",Rt(t._zod,"values",()=>e.innerType._zod.values),t._zod.parse=(r,n)=>(n.direction==="backward"||r.value===void 0&&(r.value=e.defaultValue),e.innerType._zod.run(r,n))}),wR=D("$ZodNonOptional",(t,e)=>{nt.init(t,e),Rt(t._zod,"values",()=>{let r=e.innerType._zod.values;return r?new Set([...r].filter(n=>n!==void 0)):void 0}),t._zod.parse=(r,n)=>{let o=e.innerType._zod.run(r,n);return o instanceof Promise?o.then(i=>UX(i,t)):UX(o,t)}});RR=D("$ZodSuccess",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>{if(n.direction==="backward")throw new tp("ZodSuccess");let o=e.innerType._zod.run(r,n);return o instanceof Promise?o.then(i=>(r.value=i.issues.length===0,r)):(r.value=o.issues.length===0,r)}}),PR=D("$ZodCatch",(t,e)=>{nt.init(t,e),Rt(t._zod,"optin",()=>e.innerType._zod.optin),Rt(t._zod,"optout",()=>e.innerType._zod.optout),Rt(t._zod,"values",()=>e.innerType._zod.values),t._zod.parse=(r,n)=>{if(n.direction==="backward")return e.innerType._zod.run(r,n);let o=e.innerType._zod.run(r,n);return o instanceof Promise?o.then(i=>(r.value=i.value,i.issues.length&&(r.value=e.catchValue({...r,error:{issues:i.issues.map(s=>Ui(s,n,cn()))},input:r.value}),r.issues=[]),r)):(r.value=o.value,o.issues.length&&(r.value=e.catchValue({...r,error:{issues:o.issues.map(i=>Ui(i,n,cn()))},input:r.value}),r.issues=[]),r)}}),IR=D("$ZodNaN",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>((typeof r.value!="number"||!Number.isNaN(r.value))&&r.issues.push({input:r.value,inst:t,expected:"nan",code:"invalid_type"}),r)}),OR=D("$ZodPipe",(t,e)=>{nt.init(t,e),Rt(t._zod,"values",()=>e.in._zod.values),Rt(t._zod,"optin",()=>e.in._zod.optin),Rt(t._zod,"optout",()=>e.out._zod.optout),Rt(t._zod,"propValues",()=>e.in._zod.propValues),t._zod.parse=(r,n)=>{if(n.direction==="backward"){let i=e.out._zod.run(r,n);return i instanceof Promise?i.then(s=>Aw(s,e.in,n)):Aw(i,e.in,n)}let o=e.in._zod.run(r,n);return o instanceof Promise?o.then(i=>Aw(i,e.out,n)):Aw(o,e.out,n)}});Ih=D("$ZodCodec",(t,e)=>{nt.init(t,e),Rt(t._zod,"values",()=>e.in._zod.values),Rt(t._zod,"optin",()=>e.in._zod.optin),Rt(t._zod,"optout",()=>e.out._zod.optout),Rt(t._zod,"propValues",()=>e.in._zod.propValues),t._zod.parse=(r,n)=>{if((n.direction||"forward")==="forward"){let i=e.in._zod.run(r,n);return i instanceof Promise?i.then(s=>ww(s,e,n)):ww(i,e,n)}else{let i=e.out._zod.run(r,n);return i instanceof Promise?i.then(s=>ww(s,e,n)):ww(i,e,n)}}});NR=D("$ZodReadonly",(t,e)=>{nt.init(t,e),Rt(t._zod,"propValues",()=>e.innerType._zod.propValues),Rt(t._zod,"values",()=>e.innerType._zod.values),Rt(t._zod,"optin",()=>e.innerType?._zod?.optin),Rt(t._zod,"optout",()=>e.innerType?._zod?.optout),t._zod.parse=(r,n)=>{if(n.direction==="backward")return e.innerType._zod.run(r,n);let o=e.innerType._zod.run(r,n);return o instanceof Promise?o.then(jX):jX(o)}});CR=D("$ZodTemplateLiteral",(t,e)=>{nt.init(t,e);let r=[];for(let n of e.parts)if(typeof n=="object"&&n!==null){if(!n._zod.pattern)throw new Error(`Invalid template literal part, no pattern found: ${[...n._zod.traits].shift()}`);let o=n._zod.pattern instanceof RegExp?n._zod.pattern.source:n._zod.pattern;if(!o)throw new Error(`Invalid template literal part: ${n._zod.traits}`);let i=o.startsWith("^")?1:0,s=o.endsWith("$")?o.length-1:o.length;r.push(o.slice(i,s))}else if(n===null||ej.has(typeof n))r.push(Vs(`${n}`));else throw new Error(`Invalid template literal part: ${n}`);t._zod.pattern=new RegExp(`^${r.join("")}$`),t._zod.parse=(n,o)=>typeof n.value!="string"?(n.issues.push({input:n.value,inst:t,expected:"string",code:"invalid_type"}),n):(t._zod.pattern.lastIndex=0,t._zod.pattern.test(n.value)||n.issues.push({input:n.value,inst:t,code:"invalid_format",format:e.format??"template_literal",pattern:t._zod.pattern.source}),n)}),$R=D("$ZodFunction",(t,e)=>(nt.init(t,e),t._def=e,t._zod.def=e,t.implement=r=>{if(typeof r!="function")throw new Error("implement() must be called with a function");return function(...n){let o=t._def.input?af(t._def.input,n):n,i=Reflect.apply(r,this,o);return t._def.output?af(t._def.output,i):i}},t.implementAsync=r=>{if(typeof r!="function")throw new Error("implementAsync() must be called with a function");return async function(...n){let o=t._def.input?await cf(t._def.input,n):n,i=await Reflect.apply(r,this,o);return t._def.output?await cf(t._def.output,i):i}},t._zod.parse=(r,n)=>typeof r.value!="function"?(r.issues.push({code:"invalid_type",expected:"function",input:r.value,inst:t}),r):(t._def.output&&t._def.output._zod.def.type==="promise"?r.value=t.implementAsync(r.value):r.value=t.implement(r.value),r),t.input=(...r)=>{let n=t.constructor;return Array.isArray(r[0])?new n({type:"function",input:new _y({type:"tuple",items:r[0],rest:r[1]}),output:t._def.output}):new n({type:"function",input:r[0],output:t._def.output})},t.output=r=>{let n=t.constructor;return new n({type:"function",input:t._def.input,output:r})},t)),kR=D("$ZodPromise",(t,e)=>{nt.init(t,e),t._zod.parse=(r,n)=>Promise.resolve(r.value).then(o=>e.innerType._zod.run({value:o,issues:[]},n))}),MR=D("$ZodLazy",(t,e)=>{nt.init(t,e),Rt(t._zod,"innerType",()=>e.getter()),Rt(t._zod,"pattern",()=>t._zod.innerType?._zod?.pattern),Rt(t._zod,"propValues",()=>t._zod.innerType?._zod?.propValues),Rt(t._zod,"optin",()=>t._zod.innerType?._zod?.optin??void 0),Rt(t._zod,"optout",()=>t._zod.innerType?._zod?.optout??void 0),t._zod.parse=(r,n)=>t._zod.innerType._zod.run(r,n)}),Sy=D("$ZodCustom",(t,e)=>{wr.init(t,e),nt.init(t,e),t._zod.parse=(r,n)=>r,t._zod.check=r=>{let n=r.value,o=e.fn(n);if(o instanceof Promise)return o.then(i=>zX(i,r,n,t));zX(o,r,n,t)}})});function VX(){return{localeError:cZe()}}var cZe,HX=O(()=>{je();cZe=()=>{let t={string:{unit:"\u062D\u0631\u0641",verb:"\u0623\u0646 \u064A\u062D\u0648\u064A"},file:{unit:"\u0628\u0627\u064A\u062A",verb:"\u0623\u0646 \u064A\u062D\u0648\u064A"},array:{unit:"\u0639\u0646\u0635\u0631",verb:"\u0623\u0646 \u064A\u062D\u0648\u064A"},set:{unit:"\u0639\u0646\u0635\u0631",verb:"\u0623\u0646 \u064A\u062D\u0648\u064A"}};function e(o){return t[o]??null}let r={regex:"\u0645\u062F\u062E\u0644",email:"\u0628\u0631\u064A\u062F \u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A",url:"\u0631\u0627\u0628\u0637",emoji:"\u0625\u064A\u0645\u0648\u062C\u064A",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\u062A\u0627\u0631\u064A\u062E \u0648\u0648\u0642\u062A \u0628\u0645\u0639\u064A\u0627\u0631 ISO",date:"\u062A\u0627\u0631\u064A\u062E \u0628\u0645\u0639\u064A\u0627\u0631 ISO",time:"\u0648\u0642\u062A \u0628\u0645\u0639\u064A\u0627\u0631 ISO",duration:"\u0645\u062F\u0629 \u0628\u0645\u0639\u064A\u0627\u0631 ISO",ipv4:"\u0639\u0646\u0648\u0627\u0646 IPv4",ipv6:"\u0639\u0646\u0648\u0627\u0646 IPv6",cidrv4:"\u0645\u062F\u0649 \u0639\u0646\u0627\u0648\u064A\u0646 \u0628\u0635\u064A\u063A\u0629 IPv4",cidrv6:"\u0645\u062F\u0649 \u0639\u0646\u0627\u0648\u064A\u0646 \u0628\u0635\u064A\u063A\u0629 IPv6",base64:"\u0646\u064E\u0635 \u0628\u062A\u0631\u0645\u064A\u0632 base64-encoded",base64url:"\u0646\u064E\u0635 \u0628\u062A\u0631\u0645\u064A\u0632 base64url-encoded",json_string:"\u0646\u064E\u0635 \u0639\u0644\u0649 \u0647\u064A\u0626\u0629 JSON",e164:"\u0631\u0642\u0645 \u0647\u0627\u062A\u0641 \u0628\u0645\u0639\u064A\u0627\u0631 E.164",jwt:"JWT",template_literal:"\u0645\u062F\u062E\u0644"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u0645\u062F\u062E\u0644\u0627\u062A \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644\u0629: \u064A\u0641\u062A\u0631\u0636 \u0625\u062F\u062E\u0627\u0644 instanceof ${o.expected}\u060C \u0648\u0644\u0643\u0646 \u062A\u0645 \u0625\u062F\u062E\u0627\u0644 ${a}`:`\u0645\u062F\u062E\u0644\u0627\u062A \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644\u0629: \u064A\u0641\u062A\u0631\u0636 \u0625\u062F\u062E\u0627\u0644 ${i}\u060C \u0648\u0644\u0643\u0646 \u062A\u0645 \u0625\u062F\u062E\u0627\u0644 ${a}`}case"invalid_value":return o.values.length===1?`\u0645\u062F\u062E\u0644\u0627\u062A \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644\u0629: \u064A\u0641\u062A\u0631\u0636 \u0625\u062F\u062E\u0627\u0644 ${re(o.values[0])}`:`\u0627\u062E\u062A\u064A\u0627\u0631 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644: \u064A\u062A\u0648\u0642\u0639 \u0627\u0646\u062A\u0642\u0627\u0621 \u0623\u062D\u062F \u0647\u0630\u0647 \u0627\u0644\u062E\u064A\u0627\u0631\u0627\u062A: ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?` \u0623\u0643\u0628\u0631 \u0645\u0646 \u0627\u0644\u0644\u0627\u0632\u0645: \u064A\u0641\u062A\u0631\u0636 \u0623\u0646 \u062A\u0643\u0648\u0646 ${o.origin??"\u0627\u0644\u0642\u064A\u0645\u0629"} ${i} ${o.maximum.toString()} ${s.unit??"\u0639\u0646\u0635\u0631"}`:`\u0623\u0643\u0628\u0631 \u0645\u0646 \u0627\u0644\u0644\u0627\u0632\u0645: \u064A\u0641\u062A\u0631\u0636 \u0623\u0646 \u062A\u0643\u0648\u0646 ${o.origin??"\u0627\u0644\u0642\u064A\u0645\u0629"} ${i} ${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u0623\u0635\u063A\u0631 \u0645\u0646 \u0627\u0644\u0644\u0627\u0632\u0645: \u064A\u0641\u062A\u0631\u0636 \u0644\u0640 ${o.origin} \u0623\u0646 \u064A\u0643\u0648\u0646 ${i} ${o.minimum.toString()} ${s.unit}`:`\u0623\u0635\u063A\u0631 \u0645\u0646 \u0627\u0644\u0644\u0627\u0632\u0645: \u064A\u0641\u062A\u0631\u0636 \u0644\u0640 ${o.origin} \u0623\u0646 \u064A\u0643\u0648\u0646 ${i} ${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u0646\u064E\u0635 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644: \u064A\u062C\u0628 \u0623\u0646 \u064A\u0628\u062F\u0623 \u0628\u0640 "${o.prefix}"`:i.format==="ends_with"?`\u0646\u064E\u0635 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644: \u064A\u062C\u0628 \u0623\u0646 \u064A\u0646\u062A\u0647\u064A \u0628\u0640 "${i.suffix}"`:i.format==="includes"?`\u0646\u064E\u0635 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644: \u064A\u062C\u0628 \u0623\u0646 \u064A\u062A\u0636\u0645\u0651\u064E\u0646 "${i.includes}"`:i.format==="regex"?`\u0646\u064E\u0635 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644: \u064A\u062C\u0628 \u0623\u0646 \u064A\u0637\u0627\u0628\u0642 \u0627\u0644\u0646\u0645\u0637 ${i.pattern}`:`${r[i.format]??o.format} \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644`}case"not_multiple_of":return`\u0631\u0642\u0645 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644: \u064A\u062C\u0628 \u0623\u0646 \u064A\u0643\u0648\u0646 \u0645\u0646 \u0645\u0636\u0627\u0639\u0641\u0627\u062A ${o.divisor}`;case"unrecognized_keys":return`\u0645\u0639\u0631\u0641${o.keys.length>1?"\u0627\u062A":""} \u063A\u0631\u064A\u0628${o.keys.length>1?"\u0629":""}: ${q(o.keys,"\u060C ")}`;case"invalid_key":return`\u0645\u0639\u0631\u0641 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644 \u0641\u064A ${o.origin}`;case"invalid_union":return"\u0645\u062F\u062E\u0644 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644";case"invalid_element":return`\u0645\u062F\u062E\u0644 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644 \u0641\u064A ${o.origin}`;default:return"\u0645\u062F\u062E\u0644 \u063A\u064A\u0631 \u0645\u0642\u0628\u0648\u0644"}}}});function WX(){return{localeError:uZe()}}var uZe,KX=O(()=>{je();uZe=()=>{let t={string:{unit:"simvol",verb:"olmal\u0131d\u0131r"},file:{unit:"bayt",verb:"olmal\u0131d\u0131r"},array:{unit:"element",verb:"olmal\u0131d\u0131r"},set:{unit:"element",verb:"olmal\u0131d\u0131r"}};function e(o){return t[o]??null}let r={regex:"input",email:"email address",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO datetime",date:"ISO date",time:"ISO time",duration:"ISO duration",ipv4:"IPv4 address",ipv6:"IPv6 address",cidrv4:"IPv4 range",cidrv6:"IPv6 range",base64:"base64-encoded string",base64url:"base64url-encoded string",json_string:"JSON string",e164:"E.164 number",jwt:"JWT",template_literal:"input"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Yanl\u0131\u015F d\u0259y\u0259r: g\xF6zl\u0259nil\u0259n instanceof ${o.expected}, daxil olan ${a}`:`Yanl\u0131\u015F d\u0259y\u0259r: g\xF6zl\u0259nil\u0259n ${i}, daxil olan ${a}`}case"invalid_value":return o.values.length===1?`Yanl\u0131\u015F d\u0259y\u0259r: g\xF6zl\u0259nil\u0259n ${re(o.values[0])}`:`Yanl\u0131\u015F se\xE7im: a\u015Fa\u011F\u0131dak\u0131lardan biri olmal\u0131d\u0131r: ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\xC7ox b\xF6y\xFCk: g\xF6zl\u0259nil\u0259n ${o.origin??"d\u0259y\u0259r"} ${i}${o.maximum.toString()} ${s.unit??"element"}`:`\xC7ox b\xF6y\xFCk: g\xF6zl\u0259nil\u0259n ${o.origin??"d\u0259y\u0259r"} ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\xC7ox ki\xE7ik: g\xF6zl\u0259nil\u0259n ${o.origin} ${i}${o.minimum.toString()} ${s.unit}`:`\xC7ox ki\xE7ik: g\xF6zl\u0259nil\u0259n ${o.origin} ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Yanl\u0131\u015F m\u0259tn: "${i.prefix}" il\u0259 ba\u015Flamal\u0131d\u0131r`:i.format==="ends_with"?`Yanl\u0131\u015F m\u0259tn: "${i.suffix}" il\u0259 bitm\u0259lidir`:i.format==="includes"?`Yanl\u0131\u015F m\u0259tn: "${i.includes}" daxil olmal\u0131d\u0131r`:i.format==="regex"?`Yanl\u0131\u015F m\u0259tn: ${i.pattern} \u015Fablonuna uy\u011Fun olmal\u0131d\u0131r`:`Yanl\u0131\u015F ${r[i.format]??o.format}`}case"not_multiple_of":return`Yanl\u0131\u015F \u0259d\u0259d: ${o.divisor} il\u0259 b\xF6l\xFCn\u0259 bil\u0259n olmal\u0131d\u0131r`;case"unrecognized_keys":return`Tan\u0131nmayan a\xE7ar${o.keys.length>1?"lar":""}: ${q(o.keys,", ")}`;case"invalid_key":return`${o.origin} daxilind\u0259 yanl\u0131\u015F a\xE7ar`;case"invalid_union":return"Yanl\u0131\u015F d\u0259y\u0259r";case"invalid_element":return`${o.origin} daxilind\u0259 yanl\u0131\u015F d\u0259y\u0259r`;default:return"Yanl\u0131\u015F d\u0259y\u0259r"}}}});function ZX(t,e,r,n){let o=Math.abs(t),i=o%10,s=o%100;return s>=11&&s<=19?n:i===1?e:i>=2&&i<=4?r:n}function YX(){return{localeError:lZe()}}var lZe,JX=O(()=>{je();lZe=()=>{let t={string:{unit:{one:"\u0441\u0456\u043C\u0432\u0430\u043B",few:"\u0441\u0456\u043C\u0432\u0430\u043B\u044B",many:"\u0441\u0456\u043C\u0432\u0430\u043B\u0430\u045E"},verb:"\u043C\u0435\u0446\u044C"},array:{unit:{one:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442",few:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u044B",many:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430\u045E"},verb:"\u043C\u0435\u0446\u044C"},set:{unit:{one:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442",few:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u044B",many:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430\u045E"},verb:"\u043C\u0435\u0446\u044C"},file:{unit:{one:"\u0431\u0430\u0439\u0442",few:"\u0431\u0430\u0439\u0442\u044B",many:"\u0431\u0430\u0439\u0442\u0430\u045E"},verb:"\u043C\u0435\u0446\u044C"}};function e(o){return t[o]??null}let r={regex:"\u0443\u0432\u043E\u0434",email:"email \u0430\u0434\u0440\u0430\u0441",url:"URL",emoji:"\u044D\u043C\u043E\u0434\u0437\u0456",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \u0434\u0430\u0442\u0430 \u0456 \u0447\u0430\u0441",date:"ISO \u0434\u0430\u0442\u0430",time:"ISO \u0447\u0430\u0441",duration:"ISO \u043F\u0440\u0430\u0446\u044F\u0433\u043B\u0430\u0441\u0446\u044C",ipv4:"IPv4 \u0430\u0434\u0440\u0430\u0441",ipv6:"IPv6 \u0430\u0434\u0440\u0430\u0441",cidrv4:"IPv4 \u0434\u044B\u044F\u043F\u0430\u0437\u043E\u043D",cidrv6:"IPv6 \u0434\u044B\u044F\u043F\u0430\u0437\u043E\u043D",base64:"\u0440\u0430\u0434\u043E\u043A \u0443 \u0444\u0430\u0440\u043C\u0430\u0446\u0435 base64",base64url:"\u0440\u0430\u0434\u043E\u043A \u0443 \u0444\u0430\u0440\u043C\u0430\u0446\u0435 base64url",json_string:"JSON \u0440\u0430\u0434\u043E\u043A",e164:"\u043D\u0443\u043C\u0430\u0440 E.164",jwt:"JWT",template_literal:"\u0443\u0432\u043E\u0434"},n={nan:"NaN",number:"\u043B\u0456\u043A",array:"\u043C\u0430\u0441\u0456\u045E"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u045E\u0432\u043E\u0434: \u0447\u0430\u043A\u0430\u045E\u0441\u044F instanceof ${o.expected}, \u0430\u0442\u0440\u044B\u043C\u0430\u043D\u0430 ${a}`:`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u045E\u0432\u043E\u0434: \u0447\u0430\u043A\u0430\u045E\u0441\u044F ${i}, \u0430\u0442\u0440\u044B\u043C\u0430\u043D\u0430 ${a}`}case"invalid_value":return o.values.length===1?`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u045E\u0432\u043E\u0434: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F ${re(o.values[0])}`:`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u0432\u0430\u0440\u044B\u044F\u043D\u0442: \u0447\u0430\u043A\u0430\u045E\u0441\u044F \u0430\u0434\u0437\u0456\u043D \u0437 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);if(s){let a=Number(o.maximum),c=ZX(a,s.unit.one,s.unit.few,s.unit.many);return`\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${o.origin??"\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${s.verb} ${i}${o.maximum.toString()} ${c}`}return`\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${o.origin??"\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);if(s){let a=Number(o.minimum),c=ZX(a,s.unit.one,s.unit.few,s.unit.many);return`\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${o.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${s.verb} ${i}${o.minimum.toString()} ${c}`}return`\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${o.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u0440\u0430\u0434\u043E\u043A: \u043F\u0430\u0432\u0456\u043D\u0435\u043D \u043F\u0430\u0447\u044B\u043D\u0430\u0446\u0446\u0430 \u0437 "${i.prefix}"`:i.format==="ends_with"?`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u0440\u0430\u0434\u043E\u043A: \u043F\u0430\u0432\u0456\u043D\u0435\u043D \u0437\u0430\u043A\u0430\u043D\u0447\u0432\u0430\u0446\u0446\u0430 \u043D\u0430 "${i.suffix}"`:i.format==="includes"?`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u0440\u0430\u0434\u043E\u043A: \u043F\u0430\u0432\u0456\u043D\u0435\u043D \u0437\u043C\u044F\u0448\u0447\u0430\u0446\u044C "${i.includes}"`:i.format==="regex"?`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u0440\u0430\u0434\u043E\u043A: \u043F\u0430\u0432\u0456\u043D\u0435\u043D \u0430\u0434\u043F\u0430\u0432\u044F\u0434\u0430\u0446\u044C \u0448\u0430\u0431\u043B\u043E\u043D\u0443 ${i.pattern}`:`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B ${r[i.format]??o.format}`}case"not_multiple_of":return`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u043B\u0456\u043A: \u043F\u0430\u0432\u0456\u043D\u0435\u043D \u0431\u044B\u0446\u044C \u043A\u0440\u0430\u0442\u043D\u044B\u043C ${o.divisor}`;case"unrecognized_keys":return`\u041D\u0435\u0440\u0430\u0441\u043F\u0430\u0437\u043D\u0430\u043D\u044B ${o.keys.length>1?"\u043A\u043B\u044E\u0447\u044B":"\u043A\u043B\u044E\u0447"}: ${q(o.keys,", ")}`;case"invalid_key":return`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u043A\u043B\u044E\u0447 \u0443 ${o.origin}`;case"invalid_union":return"\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u045E\u0432\u043E\u0434";case"invalid_element":return`\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u0430\u0435 \u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435 \u045E ${o.origin}`;default:return"\u041D\u044F\u043F\u0440\u0430\u0432\u0456\u043B\u044C\u043D\u044B \u045E\u0432\u043E\u0434"}}}});function XX(){return{localeError:pZe()}}var pZe,QX=O(()=>{je();pZe=()=>{let t={string:{unit:"\u0441\u0438\u043C\u0432\u043E\u043B\u0430",verb:"\u0434\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430"},file:{unit:"\u0431\u0430\u0439\u0442\u0430",verb:"\u0434\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430"},array:{unit:"\u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0430",verb:"\u0434\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430"},set:{unit:"\u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0430",verb:"\u0434\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430"}};function e(o){return t[o]??null}let r={regex:"\u0432\u0445\u043E\u0434",email:"\u0438\u043C\u0435\u0439\u043B \u0430\u0434\u0440\u0435\u0441",url:"URL",emoji:"\u0435\u043C\u043E\u0434\u0436\u0438",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \u0432\u0440\u0435\u043C\u0435",date:"ISO \u0434\u0430\u0442\u0430",time:"ISO \u0432\u0440\u0435\u043C\u0435",duration:"ISO \u043F\u0440\u043E\u0434\u044A\u043B\u0436\u0438\u0442\u0435\u043B\u043D\u043E\u0441\u0442",ipv4:"IPv4 \u0430\u0434\u0440\u0435\u0441",ipv6:"IPv6 \u0430\u0434\u0440\u0435\u0441",cidrv4:"IPv4 \u0434\u0438\u0430\u043F\u0430\u0437\u043E\u043D",cidrv6:"IPv6 \u0434\u0438\u0430\u043F\u0430\u0437\u043E\u043D",base64:"base64-\u043A\u043E\u0434\u0438\u0440\u0430\u043D \u043D\u0438\u0437",base64url:"base64url-\u043A\u043E\u0434\u0438\u0440\u0430\u043D \u043D\u0438\u0437",json_string:"JSON \u043D\u0438\u0437",e164:"E.164 \u043D\u043E\u043C\u0435\u0440",jwt:"JWT",template_literal:"\u0432\u0445\u043E\u0434"},n={nan:"NaN",number:"\u0447\u0438\u0441\u043B\u043E",array:"\u043C\u0430\u0441\u0438\u0432"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u0432\u0445\u043E\u0434: \u043E\u0447\u0430\u043A\u0432\u0430\u043D instanceof ${o.expected}, \u043F\u043E\u043B\u0443\u0447\u0435\u043D ${a}`:`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u0432\u0445\u043E\u0434: \u043E\u0447\u0430\u043A\u0432\u0430\u043D ${i}, \u043F\u043E\u043B\u0443\u0447\u0435\u043D ${a}`}case"invalid_value":return o.values.length===1?`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u0432\u0445\u043E\u0434: \u043E\u0447\u0430\u043A\u0432\u0430\u043D ${re(o.values[0])}`:`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u0430 \u043E\u043F\u0446\u0438\u044F: \u043E\u0447\u0430\u043A\u0432\u0430\u043D\u043E \u0435\u0434\u043D\u043E \u043E\u0442 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u0422\u0432\u044A\u0440\u0434\u0435 \u0433\u043E\u043B\u044F\u043C\u043E: \u043E\u0447\u0430\u043A\u0432\u0430 \u0441\u0435 ${o.origin??"\u0441\u0442\u043E\u0439\u043D\u043E\u0441\u0442"} \u0434\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430 ${i}${o.maximum.toString()} ${s.unit??"\u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0430"}`:`\u0422\u0432\u044A\u0440\u0434\u0435 \u0433\u043E\u043B\u044F\u043C\u043E: \u043E\u0447\u0430\u043A\u0432\u0430 \u0441\u0435 ${o.origin??"\u0441\u0442\u043E\u0439\u043D\u043E\u0441\u0442"} \u0434\u0430 \u0431\u044A\u0434\u0435 ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u0422\u0432\u044A\u0440\u0434\u0435 \u043C\u0430\u043B\u043A\u043E: \u043E\u0447\u0430\u043A\u0432\u0430 \u0441\u0435 ${o.origin} \u0434\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430 ${i}${o.minimum.toString()} ${s.unit}`:`\u0422\u0432\u044A\u0440\u0434\u0435 \u043C\u0430\u043B\u043A\u043E: \u043E\u0447\u0430\u043A\u0432\u0430 \u0441\u0435 ${o.origin} \u0434\u0430 \u0431\u044A\u0434\u0435 ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;if(i.format==="starts_with")return`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u043D\u0438\u0437: \u0442\u0440\u044F\u0431\u0432\u0430 \u0434\u0430 \u0437\u0430\u043F\u043E\u0447\u0432\u0430 \u0441 "${i.prefix}"`;if(i.format==="ends_with")return`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u043D\u0438\u0437: \u0442\u0440\u044F\u0431\u0432\u0430 \u0434\u0430 \u0437\u0430\u0432\u044A\u0440\u0448\u0432\u0430 \u0441 "${i.suffix}"`;if(i.format==="includes")return`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u043D\u0438\u0437: \u0442\u0440\u044F\u0431\u0432\u0430 \u0434\u0430 \u0432\u043A\u043B\u044E\u0447\u0432\u0430 "${i.includes}"`;if(i.format==="regex")return`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u043D\u0438\u0437: \u0442\u0440\u044F\u0431\u0432\u0430 \u0434\u0430 \u0441\u044A\u0432\u043F\u0430\u0434\u0430 \u0441 ${i.pattern}`;let s="\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D";return i.format==="emoji"&&(s="\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u043E"),i.format==="datetime"&&(s="\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u043E"),i.format==="date"&&(s="\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u0430"),i.format==="time"&&(s="\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u043E"),i.format==="duration"&&(s="\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u0430"),`${s} ${r[i.format]??o.format}`}case"not_multiple_of":return`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u043E \u0447\u0438\u0441\u043B\u043E: \u0442\u0440\u044F\u0431\u0432\u0430 \u0434\u0430 \u0431\u044A\u0434\u0435 \u043A\u0440\u0430\u0442\u043D\u043E \u043D\u0430 ${o.divisor}`;case"unrecognized_keys":return`\u041D\u0435\u0440\u0430\u0437\u043F\u043E\u0437\u043D\u0430\u0442${o.keys.length>1?"\u0438":""} \u043A\u043B\u044E\u0447${o.keys.length>1?"\u043E\u0432\u0435":""}: ${q(o.keys,", ")}`;case"invalid_key":return`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u043A\u043B\u044E\u0447 \u0432 ${o.origin}`;case"invalid_union":return"\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u0432\u0445\u043E\u0434";case"invalid_element":return`\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u0430 \u0441\u0442\u043E\u0439\u043D\u043E\u0441\u0442 \u0432 ${o.origin}`;default:return"\u041D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D \u0432\u0445\u043E\u0434"}}}});function eQ(){return{localeError:dZe()}}var dZe,tQ=O(()=>{je();dZe=()=>{let t={string:{unit:"car\xE0cters",verb:"contenir"},file:{unit:"bytes",verb:"contenir"},array:{unit:"elements",verb:"contenir"},set:{unit:"elements",verb:"contenir"}};function e(o){return t[o]??null}let r={regex:"entrada",email:"adre\xE7a electr\xF2nica",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"data i hora ISO",date:"data ISO",time:"hora ISO",duration:"durada ISO",ipv4:"adre\xE7a IPv4",ipv6:"adre\xE7a IPv6",cidrv4:"rang IPv4",cidrv6:"rang IPv6",base64:"cadena codificada en base64",base64url:"cadena codificada en base64url",json_string:"cadena JSON",e164:"n\xFAmero E.164",jwt:"JWT",template_literal:"entrada"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Tipus inv\xE0lid: s'esperava instanceof ${o.expected}, s'ha rebut ${a}`:`Tipus inv\xE0lid: s'esperava ${i}, s'ha rebut ${a}`}case"invalid_value":return o.values.length===1?`Valor inv\xE0lid: s'esperava ${re(o.values[0])}`:`Opci\xF3 inv\xE0lida: s'esperava una de ${q(o.values," o ")}`;case"too_big":{let i=o.inclusive?"com a m\xE0xim":"menys de",s=e(o.origin);return s?`Massa gran: s'esperava que ${o.origin??"el valor"} contingu\xE9s ${i} ${o.maximum.toString()} ${s.unit??"elements"}`:`Massa gran: s'esperava que ${o.origin??"el valor"} fos ${i} ${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?"com a m\xEDnim":"m\xE9s de",s=e(o.origin);return s?`Massa petit: s'esperava que ${o.origin} contingu\xE9s ${i} ${o.minimum.toString()} ${s.unit}`:`Massa petit: s'esperava que ${o.origin} fos ${i} ${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Format inv\xE0lid: ha de comen\xE7ar amb "${i.prefix}"`:i.format==="ends_with"?`Format inv\xE0lid: ha d'acabar amb "${i.suffix}"`:i.format==="includes"?`Format inv\xE0lid: ha d'incloure "${i.includes}"`:i.format==="regex"?`Format inv\xE0lid: ha de coincidir amb el patr\xF3 ${i.pattern}`:`Format inv\xE0lid per a ${r[i.format]??o.format}`}case"not_multiple_of":return`N\xFAmero inv\xE0lid: ha de ser m\xFAltiple de ${o.divisor}`;case"unrecognized_keys":return`Clau${o.keys.length>1?"s":""} no reconeguda${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Clau inv\xE0lida a ${o.origin}`;case"invalid_union":return"Entrada inv\xE0lida";case"invalid_element":return`Element inv\xE0lid a ${o.origin}`;default:return"Entrada inv\xE0lida"}}}});function rQ(){return{localeError:fZe()}}var fZe,nQ=O(()=>{je();fZe=()=>{let t={string:{unit:"znak\u016F",verb:"m\xEDt"},file:{unit:"bajt\u016F",verb:"m\xEDt"},array:{unit:"prvk\u016F",verb:"m\xEDt"},set:{unit:"prvk\u016F",verb:"m\xEDt"}};function e(o){return t[o]??null}let r={regex:"regul\xE1rn\xED v\xFDraz",email:"e-mailov\xE1 adresa",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"datum a \u010Das ve form\xE1tu ISO",date:"datum ve form\xE1tu ISO",time:"\u010Das ve form\xE1tu ISO",duration:"doba trv\xE1n\xED ISO",ipv4:"IPv4 adresa",ipv6:"IPv6 adresa",cidrv4:"rozsah IPv4",cidrv6:"rozsah IPv6",base64:"\u0159et\u011Bzec zak\xF3dovan\xFD ve form\xE1tu base64",base64url:"\u0159et\u011Bzec zak\xF3dovan\xFD ve form\xE1tu base64url",json_string:"\u0159et\u011Bzec ve form\xE1tu JSON",e164:"\u010D\xEDslo E.164",jwt:"JWT",template_literal:"vstup"},n={nan:"NaN",number:"\u010D\xEDslo",string:"\u0159et\u011Bzec",function:"funkce",array:"pole"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Neplatn\xFD vstup: o\u010Dek\xE1v\xE1no instanceof ${o.expected}, obdr\u017Eeno ${a}`:`Neplatn\xFD vstup: o\u010Dek\xE1v\xE1no ${i}, obdr\u017Eeno ${a}`}case"invalid_value":return o.values.length===1?`Neplatn\xFD vstup: o\u010Dek\xE1v\xE1no ${re(o.values[0])}`:`Neplatn\xE1 mo\u017Enost: o\u010Dek\xE1v\xE1na jedna z hodnot ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Hodnota je p\u0159\xEDli\u0161 velk\xE1: ${o.origin??"hodnota"} mus\xED m\xEDt ${i}${o.maximum.toString()} ${s.unit??"prvk\u016F"}`:`Hodnota je p\u0159\xEDli\u0161 velk\xE1: ${o.origin??"hodnota"} mus\xED b\xFDt ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Hodnota je p\u0159\xEDli\u0161 mal\xE1: ${o.origin??"hodnota"} mus\xED m\xEDt ${i}${o.minimum.toString()} ${s.unit??"prvk\u016F"}`:`Hodnota je p\u0159\xEDli\u0161 mal\xE1: ${o.origin??"hodnota"} mus\xED b\xFDt ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Neplatn\xFD \u0159et\u011Bzec: mus\xED za\u010D\xEDnat na "${i.prefix}"`:i.format==="ends_with"?`Neplatn\xFD \u0159et\u011Bzec: mus\xED kon\u010Dit na "${i.suffix}"`:i.format==="includes"?`Neplatn\xFD \u0159et\u011Bzec: mus\xED obsahovat "${i.includes}"`:i.format==="regex"?`Neplatn\xFD \u0159et\u011Bzec: mus\xED odpov\xEDdat vzoru ${i.pattern}`:`Neplatn\xFD form\xE1t ${r[i.format]??o.format}`}case"not_multiple_of":return`Neplatn\xE9 \u010D\xEDslo: mus\xED b\xFDt n\xE1sobkem ${o.divisor}`;case"unrecognized_keys":return`Nezn\xE1m\xE9 kl\xED\u010De: ${q(o.keys,", ")}`;case"invalid_key":return`Neplatn\xFD kl\xED\u010D v ${o.origin}`;case"invalid_union":return"Neplatn\xFD vstup";case"invalid_element":return`Neplatn\xE1 hodnota v ${o.origin}`;default:return"Neplatn\xFD vstup"}}}});function oQ(){return{localeError:mZe()}}var mZe,iQ=O(()=>{je();mZe=()=>{let t={string:{unit:"tegn",verb:"havde"},file:{unit:"bytes",verb:"havde"},array:{unit:"elementer",verb:"indeholdt"},set:{unit:"elementer",verb:"indeholdt"}};function e(o){return t[o]??null}let r={regex:"input",email:"e-mailadresse",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO dato- og klokkesl\xE6t",date:"ISO-dato",time:"ISO-klokkesl\xE6t",duration:"ISO-varighed",ipv4:"IPv4-omr\xE5de",ipv6:"IPv6-omr\xE5de",cidrv4:"IPv4-spektrum",cidrv6:"IPv6-spektrum",base64:"base64-kodet streng",base64url:"base64url-kodet streng",json_string:"JSON-streng",e164:"E.164-nummer",jwt:"JWT",template_literal:"input"},n={nan:"NaN",string:"streng",number:"tal",boolean:"boolean",array:"liste",object:"objekt",set:"s\xE6t",file:"fil"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Ugyldigt input: forventede instanceof ${o.expected}, fik ${a}`:`Ugyldigt input: forventede ${i}, fik ${a}`}case"invalid_value":return o.values.length===1?`Ugyldig v\xE6rdi: forventede ${re(o.values[0])}`:`Ugyldigt valg: forventede en af f\xF8lgende ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin),a=n[o.origin]??o.origin;return s?`For stor: forventede ${a??"value"} ${s.verb} ${i} ${o.maximum.toString()} ${s.unit??"elementer"}`:`For stor: forventede ${a??"value"} havde ${i} ${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin),a=n[o.origin]??o.origin;return s?`For lille: forventede ${a} ${s.verb} ${i} ${o.minimum.toString()} ${s.unit}`:`For lille: forventede ${a} havde ${i} ${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Ugyldig streng: skal starte med "${i.prefix}"`:i.format==="ends_with"?`Ugyldig streng: skal ende med "${i.suffix}"`:i.format==="includes"?`Ugyldig streng: skal indeholde "${i.includes}"`:i.format==="regex"?`Ugyldig streng: skal matche m\xF8nsteret ${i.pattern}`:`Ugyldig ${r[i.format]??o.format}`}case"not_multiple_of":return`Ugyldigt tal: skal v\xE6re deleligt med ${o.divisor}`;case"unrecognized_keys":return`${o.keys.length>1?"Ukendte n\xF8gler":"Ukendt n\xF8gle"}: ${q(o.keys,", ")}`;case"invalid_key":return`Ugyldig n\xF8gle i ${o.origin}`;case"invalid_union":return"Ugyldigt input: matcher ingen af de tilladte typer";case"invalid_element":return`Ugyldig v\xE6rdi i ${o.origin}`;default:return"Ugyldigt input"}}}});function sQ(){return{localeError:hZe()}}var hZe,aQ=O(()=>{je();hZe=()=>{let t={string:{unit:"Zeichen",verb:"zu haben"},file:{unit:"Bytes",verb:"zu haben"},array:{unit:"Elemente",verb:"zu haben"},set:{unit:"Elemente",verb:"zu haben"}};function e(o){return t[o]??null}let r={regex:"Eingabe",email:"E-Mail-Adresse",url:"URL",emoji:"Emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO-Datum und -Uhrzeit",date:"ISO-Datum",time:"ISO-Uhrzeit",duration:"ISO-Dauer",ipv4:"IPv4-Adresse",ipv6:"IPv6-Adresse",cidrv4:"IPv4-Bereich",cidrv6:"IPv6-Bereich",base64:"Base64-codierter String",base64url:"Base64-URL-codierter String",json_string:"JSON-String",e164:"E.164-Nummer",jwt:"JWT",template_literal:"Eingabe"},n={nan:"NaN",number:"Zahl",array:"Array"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Ung\xFCltige Eingabe: erwartet instanceof ${o.expected}, erhalten ${a}`:`Ung\xFCltige Eingabe: erwartet ${i}, erhalten ${a}`}case"invalid_value":return o.values.length===1?`Ung\xFCltige Eingabe: erwartet ${re(o.values[0])}`:`Ung\xFCltige Option: erwartet eine von ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Zu gro\xDF: erwartet, dass ${o.origin??"Wert"} ${i}${o.maximum.toString()} ${s.unit??"Elemente"} hat`:`Zu gro\xDF: erwartet, dass ${o.origin??"Wert"} ${i}${o.maximum.toString()} ist`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Zu klein: erwartet, dass ${o.origin} ${i}${o.minimum.toString()} ${s.unit} hat`:`Zu klein: erwartet, dass ${o.origin} ${i}${o.minimum.toString()} ist`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Ung\xFCltiger String: muss mit "${i.prefix}" beginnen`:i.format==="ends_with"?`Ung\xFCltiger String: muss mit "${i.suffix}" enden`:i.format==="includes"?`Ung\xFCltiger String: muss "${i.includes}" enthalten`:i.format==="regex"?`Ung\xFCltiger String: muss dem Muster ${i.pattern} entsprechen`:`Ung\xFCltig: ${r[i.format]??o.format}`}case"not_multiple_of":return`Ung\xFCltige Zahl: muss ein Vielfaches von ${o.divisor} sein`;case"unrecognized_keys":return`${o.keys.length>1?"Unbekannte Schl\xFCssel":"Unbekannter Schl\xFCssel"}: ${q(o.keys,", ")}`;case"invalid_key":return`Ung\xFCltiger Schl\xFCssel in ${o.origin}`;case"invalid_union":return"Ung\xFCltige Eingabe";case"invalid_element":return`Ung\xFCltiger Wert in ${o.origin}`;default:return"Ung\xFCltige Eingabe"}}}});function DR(){return{localeError:gZe()}}var gZe,iz=O(()=>{je();gZe=()=>{let t={string:{unit:"characters",verb:"to have"},file:{unit:"bytes",verb:"to have"},array:{unit:"items",verb:"to have"},set:{unit:"items",verb:"to have"},map:{unit:"entries",verb:"to have"}};function e(o){return t[o]??null}let r={regex:"input",email:"email address",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO datetime",date:"ISO date",time:"ISO time",duration:"ISO duration",ipv4:"IPv4 address",ipv6:"IPv6 address",mac:"MAC address",cidrv4:"IPv4 range",cidrv6:"IPv6 range",base64:"base64-encoded string",base64url:"base64url-encoded string",json_string:"JSON string",e164:"E.164 number",jwt:"JWT",template_literal:"input"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return`Invalid input: expected ${i}, received ${a}`}case"invalid_value":return o.values.length===1?`Invalid input: expected ${re(o.values[0])}`:`Invalid option: expected one of ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Too big: expected ${o.origin??"value"} to have ${i}${o.maximum.toString()} ${s.unit??"elements"}`:`Too big: expected ${o.origin??"value"} to be ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Too small: expected ${o.origin} to have ${i}${o.minimum.toString()} ${s.unit}`:`Too small: expected ${o.origin} to be ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Invalid string: must start with "${i.prefix}"`:i.format==="ends_with"?`Invalid string: must end with "${i.suffix}"`:i.format==="includes"?`Invalid string: must include "${i.includes}"`:i.format==="regex"?`Invalid string: must match pattern ${i.pattern}`:`Invalid ${r[i.format]??o.format}`}case"not_multiple_of":return`Invalid number: must be a multiple of ${o.divisor}`;case"unrecognized_keys":return`Unrecognized key${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Invalid key in ${o.origin}`;case"invalid_union":return"Invalid input";case"invalid_element":return`Invalid value in ${o.origin}`;default:return"Invalid input"}}}});function cQ(){return{localeError:_Ze()}}var _Ze,uQ=O(()=>{je();_Ze=()=>{let t={string:{unit:"karaktrojn",verb:"havi"},file:{unit:"bajtojn",verb:"havi"},array:{unit:"elementojn",verb:"havi"},set:{unit:"elementojn",verb:"havi"}};function e(o){return t[o]??null}let r={regex:"enigo",email:"retadreso",url:"URL",emoji:"emo\u011Dio",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO-datotempo",date:"ISO-dato",time:"ISO-tempo",duration:"ISO-da\u016Dro",ipv4:"IPv4-adreso",ipv6:"IPv6-adreso",cidrv4:"IPv4-rango",cidrv6:"IPv6-rango",base64:"64-ume kodita karaktraro",base64url:"URL-64-ume kodita karaktraro",json_string:"JSON-karaktraro",e164:"E.164-nombro",jwt:"JWT",template_literal:"enigo"},n={nan:"NaN",number:"nombro",array:"tabelo",null:"senvalora"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Nevalida enigo: atendi\u011Dis instanceof ${o.expected}, ricevi\u011Dis ${a}`:`Nevalida enigo: atendi\u011Dis ${i}, ricevi\u011Dis ${a}`}case"invalid_value":return o.values.length===1?`Nevalida enigo: atendi\u011Dis ${re(o.values[0])}`:`Nevalida opcio: atendi\u011Dis unu el ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Tro granda: atendi\u011Dis ke ${o.origin??"valoro"} havu ${i}${o.maximum.toString()} ${s.unit??"elementojn"}`:`Tro granda: atendi\u011Dis ke ${o.origin??"valoro"} havu ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Tro malgranda: atendi\u011Dis ke ${o.origin} havu ${i}${o.minimum.toString()} ${s.unit}`:`Tro malgranda: atendi\u011Dis ke ${o.origin} estu ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Nevalida karaktraro: devas komenci\u011Di per "${i.prefix}"`:i.format==="ends_with"?`Nevalida karaktraro: devas fini\u011Di per "${i.suffix}"`:i.format==="includes"?`Nevalida karaktraro: devas inkluzivi "${i.includes}"`:i.format==="regex"?`Nevalida karaktraro: devas kongrui kun la modelo ${i.pattern}`:`Nevalida ${r[i.format]??o.format}`}case"not_multiple_of":return`Nevalida nombro: devas esti oblo de ${o.divisor}`;case"unrecognized_keys":return`Nekonata${o.keys.length>1?"j":""} \u015Dlosilo${o.keys.length>1?"j":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Nevalida \u015Dlosilo en ${o.origin}`;case"invalid_union":return"Nevalida enigo";case"invalid_element":return`Nevalida valoro en ${o.origin}`;default:return"Nevalida enigo"}}}});function lQ(){return{localeError:vZe()}}var vZe,pQ=O(()=>{je();vZe=()=>{let t={string:{unit:"caracteres",verb:"tener"},file:{unit:"bytes",verb:"tener"},array:{unit:"elementos",verb:"tener"},set:{unit:"elementos",verb:"tener"}};function e(o){return t[o]??null}let r={regex:"entrada",email:"direcci\xF3n de correo electr\xF3nico",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"fecha y hora ISO",date:"fecha ISO",time:"hora ISO",duration:"duraci\xF3n ISO",ipv4:"direcci\xF3n IPv4",ipv6:"direcci\xF3n IPv6",cidrv4:"rango IPv4",cidrv6:"rango IPv6",base64:"cadena codificada en base64",base64url:"URL codificada en base64",json_string:"cadena JSON",e164:"n\xFAmero E.164",jwt:"JWT",template_literal:"entrada"},n={nan:"NaN",string:"texto",number:"n\xFAmero",boolean:"booleano",array:"arreglo",object:"objeto",set:"conjunto",file:"archivo",date:"fecha",bigint:"n\xFAmero grande",symbol:"s\xEDmbolo",undefined:"indefinido",null:"nulo",function:"funci\xF3n",map:"mapa",record:"registro",tuple:"tupla",enum:"enumeraci\xF3n",union:"uni\xF3n",literal:"literal",promise:"promesa",void:"vac\xEDo",never:"nunca",unknown:"desconocido",any:"cualquiera"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Entrada inv\xE1lida: se esperaba instanceof ${o.expected}, recibido ${a}`:`Entrada inv\xE1lida: se esperaba ${i}, recibido ${a}`}case"invalid_value":return o.values.length===1?`Entrada inv\xE1lida: se esperaba ${re(o.values[0])}`:`Opci\xF3n inv\xE1lida: se esperaba una de ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin),a=n[o.origin]??o.origin;return s?`Demasiado grande: se esperaba que ${a??"valor"} tuviera ${i}${o.maximum.toString()} ${s.unit??"elementos"}`:`Demasiado grande: se esperaba que ${a??"valor"} fuera ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin),a=n[o.origin]??o.origin;return s?`Demasiado peque\xF1o: se esperaba que ${a} tuviera ${i}${o.minimum.toString()} ${s.unit}`:`Demasiado peque\xF1o: se esperaba que ${a} fuera ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Cadena inv\xE1lida: debe comenzar con "${i.prefix}"`:i.format==="ends_with"?`Cadena inv\xE1lida: debe terminar en "${i.suffix}"`:i.format==="includes"?`Cadena inv\xE1lida: debe incluir "${i.includes}"`:i.format==="regex"?`Cadena inv\xE1lida: debe coincidir con el patr\xF3n ${i.pattern}`:`Inv\xE1lido ${r[i.format]??o.format}`}case"not_multiple_of":return`N\xFAmero inv\xE1lido: debe ser m\xFAltiplo de ${o.divisor}`;case"unrecognized_keys":return`Llave${o.keys.length>1?"s":""} desconocida${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Llave inv\xE1lida en ${n[o.origin]??o.origin}`;case"invalid_union":return"Entrada inv\xE1lida";case"invalid_element":return`Valor inv\xE1lido en ${n[o.origin]??o.origin}`;default:return"Entrada inv\xE1lida"}}}});function dQ(){return{localeError:SZe()}}var SZe,fQ=O(()=>{je();SZe=()=>{let t={string:{unit:"\u06A9\u0627\u0631\u0627\u06A9\u062A\u0631",verb:"\u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F"},file:{unit:"\u0628\u0627\u06CC\u062A",verb:"\u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F"},array:{unit:"\u0622\u06CC\u062A\u0645",verb:"\u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F"},set:{unit:"\u0622\u06CC\u062A\u0645",verb:"\u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F"}};function e(o){return t[o]??null}let r={regex:"\u0648\u0631\u0648\u062F\u06CC",email:"\u0622\u062F\u0631\u0633 \u0627\u06CC\u0645\u06CC\u0644",url:"URL",emoji:"\u0627\u06CC\u0645\u0648\u062C\u06CC",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\u062A\u0627\u0631\u06CC\u062E \u0648 \u0632\u0645\u0627\u0646 \u0627\u06CC\u0632\u0648",date:"\u062A\u0627\u0631\u06CC\u062E \u0627\u06CC\u0632\u0648",time:"\u0632\u0645\u0627\u0646 \u0627\u06CC\u0632\u0648",duration:"\u0645\u062F\u062A \u0632\u0645\u0627\u0646 \u0627\u06CC\u0632\u0648",ipv4:"IPv4 \u0622\u062F\u0631\u0633",ipv6:"IPv6 \u0622\u062F\u0631\u0633",cidrv4:"IPv4 \u062F\u0627\u0645\u0646\u0647",cidrv6:"IPv6 \u062F\u0627\u0645\u0646\u0647",base64:"base64-encoded \u0631\u0634\u062A\u0647",base64url:"base64url-encoded \u0631\u0634\u062A\u0647",json_string:"JSON \u0631\u0634\u062A\u0647",e164:"E.164 \u0639\u062F\u062F",jwt:"JWT",template_literal:"\u0648\u0631\u0648\u062F\u06CC"},n={nan:"NaN",number:"\u0639\u062F\u062F",array:"\u0622\u0631\u0627\u06CC\u0647"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u0648\u0631\u0648\u062F\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0645\u06CC\u200C\u0628\u0627\u06CC\u0633\u062A instanceof ${o.expected} \u0645\u06CC\u200C\u0628\u0648\u062F\u060C ${a} \u062F\u0631\u06CC\u0627\u0641\u062A \u0634\u062F`:`\u0648\u0631\u0648\u062F\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0645\u06CC\u200C\u0628\u0627\u06CC\u0633\u062A ${i} \u0645\u06CC\u200C\u0628\u0648\u062F\u060C ${a} \u062F\u0631\u06CC\u0627\u0641\u062A \u0634\u062F`}case"invalid_value":return o.values.length===1?`\u0648\u0631\u0648\u062F\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0645\u06CC\u200C\u0628\u0627\u06CC\u0633\u062A ${re(o.values[0])} \u0645\u06CC\u200C\u0628\u0648\u062F`:`\u06AF\u0632\u06CC\u0646\u0647 \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0645\u06CC\u200C\u0628\u0627\u06CC\u0633\u062A \u06CC\u06A9\u06CC \u0627\u0632 ${q(o.values,"|")} \u0645\u06CC\u200C\u0628\u0648\u062F`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u062E\u06CC\u0644\u06CC \u0628\u0632\u0631\u06AF: ${o.origin??"\u0645\u0642\u062F\u0627\u0631"} \u0628\u0627\u06CC\u062F ${i}${o.maximum.toString()} ${s.unit??"\u0639\u0646\u0635\u0631"} \u0628\u0627\u0634\u062F`:`\u062E\u06CC\u0644\u06CC \u0628\u0632\u0631\u06AF: ${o.origin??"\u0645\u0642\u062F\u0627\u0631"} \u0628\u0627\u06CC\u062F ${i}${o.maximum.toString()} \u0628\u0627\u0634\u062F`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u062E\u06CC\u0644\u06CC \u06A9\u0648\u0686\u06A9: ${o.origin} \u0628\u0627\u06CC\u062F ${i}${o.minimum.toString()} ${s.unit} \u0628\u0627\u0634\u062F`:`\u062E\u06CC\u0644\u06CC \u06A9\u0648\u0686\u06A9: ${o.origin} \u0628\u0627\u06CC\u062F ${i}${o.minimum.toString()} \u0628\u0627\u0634\u062F`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u0631\u0634\u062A\u0647 \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0628\u0627\u06CC\u062F \u0628\u0627 "${i.prefix}" \u0634\u0631\u0648\u0639 \u0634\u0648\u062F`:i.format==="ends_with"?`\u0631\u0634\u062A\u0647 \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0628\u0627\u06CC\u062F \u0628\u0627 "${i.suffix}" \u062A\u0645\u0627\u0645 \u0634\u0648\u062F`:i.format==="includes"?`\u0631\u0634\u062A\u0647 \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0628\u0627\u06CC\u062F \u0634\u0627\u0645\u0644 "${i.includes}" \u0628\u0627\u0634\u062F`:i.format==="regex"?`\u0631\u0634\u062A\u0647 \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0628\u0627\u06CC\u062F \u0628\u0627 \u0627\u0644\u06AF\u0648\u06CC ${i.pattern} \u0645\u0637\u0627\u0628\u0642\u062A \u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F`:`${r[i.format]??o.format} \u0646\u0627\u0645\u0639\u062A\u0628\u0631`}case"not_multiple_of":return`\u0639\u062F\u062F \u0646\u0627\u0645\u0639\u062A\u0628\u0631: \u0628\u0627\u06CC\u062F \u0645\u0636\u0631\u0628 ${o.divisor} \u0628\u0627\u0634\u062F`;case"unrecognized_keys":return`\u06A9\u0644\u06CC\u062F${o.keys.length>1?"\u0647\u0627\u06CC":""} \u0646\u0627\u0634\u0646\u0627\u0633: ${q(o.keys,", ")}`;case"invalid_key":return`\u06A9\u0644\u06CC\u062F \u0646\u0627\u0634\u0646\u0627\u0633 \u062F\u0631 ${o.origin}`;case"invalid_union":return"\u0648\u0631\u0648\u062F\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631";case"invalid_element":return`\u0645\u0642\u062F\u0627\u0631 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u062F\u0631 ${o.origin}`;default:return"\u0648\u0631\u0648\u062F\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631"}}}});function mQ(){return{localeError:yZe()}}var yZe,hQ=O(()=>{je();yZe=()=>{let t={string:{unit:"merkki\xE4",subject:"merkkijonon"},file:{unit:"tavua",subject:"tiedoston"},array:{unit:"alkiota",subject:"listan"},set:{unit:"alkiota",subject:"joukon"},number:{unit:"",subject:"luvun"},bigint:{unit:"",subject:"suuren kokonaisluvun"},int:{unit:"",subject:"kokonaisluvun"},date:{unit:"",subject:"p\xE4iv\xE4m\xE4\xE4r\xE4n"}};function e(o){return t[o]??null}let r={regex:"s\xE4\xE4nn\xF6llinen lauseke",email:"s\xE4hk\xF6postiosoite",url:"URL-osoite",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO-aikaleima",date:"ISO-p\xE4iv\xE4m\xE4\xE4r\xE4",time:"ISO-aika",duration:"ISO-kesto",ipv4:"IPv4-osoite",ipv6:"IPv6-osoite",cidrv4:"IPv4-alue",cidrv6:"IPv6-alue",base64:"base64-koodattu merkkijono",base64url:"base64url-koodattu merkkijono",json_string:"JSON-merkkijono",e164:"E.164-luku",jwt:"JWT",template_literal:"templaattimerkkijono"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Virheellinen tyyppi: odotettiin instanceof ${o.expected}, oli ${a}`:`Virheellinen tyyppi: odotettiin ${i}, oli ${a}`}case"invalid_value":return o.values.length===1?`Virheellinen sy\xF6te: t\xE4ytyy olla ${re(o.values[0])}`:`Virheellinen valinta: t\xE4ytyy olla yksi seuraavista: ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Liian suuri: ${s.subject} t\xE4ytyy olla ${i}${o.maximum.toString()} ${s.unit}`.trim():`Liian suuri: arvon t\xE4ytyy olla ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Liian pieni: ${s.subject} t\xE4ytyy olla ${i}${o.minimum.toString()} ${s.unit}`.trim():`Liian pieni: arvon t\xE4ytyy olla ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Virheellinen sy\xF6te: t\xE4ytyy alkaa "${i.prefix}"`:i.format==="ends_with"?`Virheellinen sy\xF6te: t\xE4ytyy loppua "${i.suffix}"`:i.format==="includes"?`Virheellinen sy\xF6te: t\xE4ytyy sis\xE4lt\xE4\xE4 "${i.includes}"`:i.format==="regex"?`Virheellinen sy\xF6te: t\xE4ytyy vastata s\xE4\xE4nn\xF6llist\xE4 lauseketta ${i.pattern}`:`Virheellinen ${r[i.format]??o.format}`}case"not_multiple_of":return`Virheellinen luku: t\xE4ytyy olla luvun ${o.divisor} monikerta`;case"unrecognized_keys":return`${o.keys.length>1?"Tuntemattomat avaimet":"Tuntematon avain"}: ${q(o.keys,", ")}`;case"invalid_key":return"Virheellinen avain tietueessa";case"invalid_union":return"Virheellinen unioni";case"invalid_element":return"Virheellinen arvo joukossa";default:return"Virheellinen sy\xF6te"}}}});function gQ(){return{localeError:EZe()}}var EZe,_Q=O(()=>{je();EZe=()=>{let t={string:{unit:"caract\xE8res",verb:"avoir"},file:{unit:"octets",verb:"avoir"},array:{unit:"\xE9l\xE9ments",verb:"avoir"},set:{unit:"\xE9l\xE9ments",verb:"avoir"}};function e(o){return t[o]??null}let r={regex:"entr\xE9e",email:"adresse e-mail",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"date et heure ISO",date:"date ISO",time:"heure ISO",duration:"dur\xE9e ISO",ipv4:"adresse IPv4",ipv6:"adresse IPv6",cidrv4:"plage IPv4",cidrv6:"plage IPv6",base64:"cha\xEEne encod\xE9e en base64",base64url:"cha\xEEne encod\xE9e en base64url",json_string:"cha\xEEne JSON",e164:"num\xE9ro E.164",jwt:"JWT",template_literal:"entr\xE9e"},n={nan:"NaN",number:"nombre",array:"tableau"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Entr\xE9e invalide : instanceof ${o.expected} attendu, ${a} re\xE7u`:`Entr\xE9e invalide : ${i} attendu, ${a} re\xE7u`}case"invalid_value":return o.values.length===1?`Entr\xE9e invalide : ${re(o.values[0])} attendu`:`Option invalide : une valeur parmi ${q(o.values,"|")} attendue`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Trop grand : ${o.origin??"valeur"} doit ${s.verb} ${i}${o.maximum.toString()} ${s.unit??"\xE9l\xE9ment(s)"}`:`Trop grand : ${o.origin??"valeur"} doit \xEAtre ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Trop petit : ${o.origin} doit ${s.verb} ${i}${o.minimum.toString()} ${s.unit}`:`Trop petit : ${o.origin} doit \xEAtre ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Cha\xEEne invalide : doit commencer par "${i.prefix}"`:i.format==="ends_with"?`Cha\xEEne invalide : doit se terminer par "${i.suffix}"`:i.format==="includes"?`Cha\xEEne invalide : doit inclure "${i.includes}"`:i.format==="regex"?`Cha\xEEne invalide : doit correspondre au mod\xE8le ${i.pattern}`:`${r[i.format]??o.format} invalide`}case"not_multiple_of":return`Nombre invalide : doit \xEAtre un multiple de ${o.divisor}`;case"unrecognized_keys":return`Cl\xE9${o.keys.length>1?"s":""} non reconnue${o.keys.length>1?"s":""} : ${q(o.keys,", ")}`;case"invalid_key":return`Cl\xE9 invalide dans ${o.origin}`;case"invalid_union":return"Entr\xE9e invalide";case"invalid_element":return`Valeur invalide dans ${o.origin}`;default:return"Entr\xE9e invalide"}}}});function vQ(){return{localeError:TZe()}}var TZe,SQ=O(()=>{je();TZe=()=>{let t={string:{unit:"caract\xE8res",verb:"avoir"},file:{unit:"octets",verb:"avoir"},array:{unit:"\xE9l\xE9ments",verb:"avoir"},set:{unit:"\xE9l\xE9ments",verb:"avoir"}};function e(o){return t[o]??null}let r={regex:"entr\xE9e",email:"adresse courriel",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"date-heure ISO",date:"date ISO",time:"heure ISO",duration:"dur\xE9e ISO",ipv4:"adresse IPv4",ipv6:"adresse IPv6",cidrv4:"plage IPv4",cidrv6:"plage IPv6",base64:"cha\xEEne encod\xE9e en base64",base64url:"cha\xEEne encod\xE9e en base64url",json_string:"cha\xEEne JSON",e164:"num\xE9ro E.164",jwt:"JWT",template_literal:"entr\xE9e"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Entr\xE9e invalide : attendu instanceof ${o.expected}, re\xE7u ${a}`:`Entr\xE9e invalide : attendu ${i}, re\xE7u ${a}`}case"invalid_value":return o.values.length===1?`Entr\xE9e invalide : attendu ${re(o.values[0])}`:`Option invalide : attendu l'une des valeurs suivantes ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"\u2264":"<",s=e(o.origin);return s?`Trop grand : attendu que ${o.origin??"la valeur"} ait ${i}${o.maximum.toString()} ${s.unit}`:`Trop grand : attendu que ${o.origin??"la valeur"} soit ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?"\u2265":">",s=e(o.origin);return s?`Trop petit : attendu que ${o.origin} ait ${i}${o.minimum.toString()} ${s.unit}`:`Trop petit : attendu que ${o.origin} soit ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Cha\xEEne invalide : doit commencer par "${i.prefix}"`:i.format==="ends_with"?`Cha\xEEne invalide : doit se terminer par "${i.suffix}"`:i.format==="includes"?`Cha\xEEne invalide : doit inclure "${i.includes}"`:i.format==="regex"?`Cha\xEEne invalide : doit correspondre au motif ${i.pattern}`:`${r[i.format]??o.format} invalide`}case"not_multiple_of":return`Nombre invalide : doit \xEAtre un multiple de ${o.divisor}`;case"unrecognized_keys":return`Cl\xE9${o.keys.length>1?"s":""} non reconnue${o.keys.length>1?"s":""} : ${q(o.keys,", ")}`;case"invalid_key":return`Cl\xE9 invalide dans ${o.origin}`;case"invalid_union":return"Entr\xE9e invalide";case"invalid_element":return`Valeur invalide dans ${o.origin}`;default:return"Entr\xE9e invalide"}}}});function yQ(){return{localeError:bZe()}}var bZe,EQ=O(()=>{je();bZe=()=>{let t={string:{label:"\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA",gender:"f"},number:{label:"\u05DE\u05E1\u05E4\u05E8",gender:"m"},boolean:{label:"\u05E2\u05E8\u05DA \u05D1\u05D5\u05DC\u05D9\u05D0\u05E0\u05D9",gender:"m"},bigint:{label:"BigInt",gender:"m"},date:{label:"\u05EA\u05D0\u05E8\u05D9\u05DA",gender:"m"},array:{label:"\u05DE\u05E2\u05E8\u05DA",gender:"m"},object:{label:"\u05D0\u05D5\u05D1\u05D9\u05D9\u05E7\u05D8",gender:"m"},null:{label:"\u05E2\u05E8\u05DA \u05E8\u05D9\u05E7 (null)",gender:"m"},undefined:{label:"\u05E2\u05E8\u05DA \u05DC\u05D0 \u05DE\u05D5\u05D2\u05D3\u05E8 (undefined)",gender:"m"},symbol:{label:"\u05E1\u05D9\u05DE\u05D1\u05D5\u05DC (Symbol)",gender:"m"},function:{label:"\u05E4\u05D5\u05E0\u05E7\u05E6\u05D9\u05D4",gender:"f"},map:{label:"\u05DE\u05E4\u05D4 (Map)",gender:"f"},set:{label:"\u05E7\u05D1\u05D5\u05E6\u05D4 (Set)",gender:"f"},file:{label:"\u05E7\u05D5\u05D1\u05E5",gender:"m"},promise:{label:"Promise",gender:"m"},NaN:{label:"NaN",gender:"m"},unknown:{label:"\u05E2\u05E8\u05DA \u05DC\u05D0 \u05D9\u05D3\u05D5\u05E2",gender:"m"},value:{label:"\u05E2\u05E8\u05DA",gender:"m"}},e={string:{unit:"\u05EA\u05D5\u05D5\u05D9\u05DD",shortLabel:"\u05E7\u05E6\u05E8",longLabel:"\u05D0\u05E8\u05D5\u05DA"},file:{unit:"\u05D1\u05D9\u05D9\u05D8\u05D9\u05DD",shortLabel:"\u05E7\u05D8\u05DF",longLabel:"\u05D2\u05D3\u05D5\u05DC"},array:{unit:"\u05E4\u05E8\u05D9\u05D8\u05D9\u05DD",shortLabel:"\u05E7\u05D8\u05DF",longLabel:"\u05D2\u05D3\u05D5\u05DC"},set:{unit:"\u05E4\u05E8\u05D9\u05D8\u05D9\u05DD",shortLabel:"\u05E7\u05D8\u05DF",longLabel:"\u05D2\u05D3\u05D5\u05DC"},number:{unit:"",shortLabel:"\u05E7\u05D8\u05DF",longLabel:"\u05D2\u05D3\u05D5\u05DC"}},r=u=>u?t[u]:void 0,n=u=>{let p=r(u);return p?p.label:u??t.unknown.label},o=u=>`\u05D4${n(u)}`,i=u=>(r(u)?.gender??"m")==="f"?"\u05E6\u05E8\u05D9\u05DB\u05D4 \u05DC\u05D4\u05D9\u05D5\u05EA":"\u05E6\u05E8\u05D9\u05DA \u05DC\u05D4\u05D9\u05D5\u05EA",s=u=>u?e[u]??null:null,a={regex:{label:"\u05E7\u05DC\u05D8",gender:"m"},email:{label:"\u05DB\u05EA\u05D5\u05D1\u05EA \u05D0\u05D9\u05DE\u05D9\u05D9\u05DC",gender:"f"},url:{label:"\u05DB\u05EA\u05D5\u05D1\u05EA \u05E8\u05E9\u05EA",gender:"f"},emoji:{label:"\u05D0\u05D9\u05DE\u05D5\u05D2'\u05D9",gender:"m"},uuid:{label:"UUID",gender:"m"},nanoid:{label:"nanoid",gender:"m"},guid:{label:"GUID",gender:"m"},cuid:{label:"cuid",gender:"m"},cuid2:{label:"cuid2",gender:"m"},ulid:{label:"ULID",gender:"m"},xid:{label:"XID",gender:"m"},ksuid:{label:"KSUID",gender:"m"},datetime:{label:"\u05EA\u05D0\u05E8\u05D9\u05DA \u05D5\u05D6\u05DE\u05DF ISO",gender:"m"},date:{label:"\u05EA\u05D0\u05E8\u05D9\u05DA ISO",gender:"m"},time:{label:"\u05D6\u05DE\u05DF ISO",gender:"m"},duration:{label:"\u05DE\u05E9\u05DA \u05D6\u05DE\u05DF ISO",gender:"m"},ipv4:{label:"\u05DB\u05EA\u05D5\u05D1\u05EA IPv4",gender:"f"},ipv6:{label:"\u05DB\u05EA\u05D5\u05D1\u05EA IPv6",gender:"f"},cidrv4:{label:"\u05D8\u05D5\u05D5\u05D7 IPv4",gender:"m"},cidrv6:{label:"\u05D8\u05D5\u05D5\u05D7 IPv6",gender:"m"},base64:{label:"\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA \u05D1\u05D1\u05E1\u05D9\u05E1 64",gender:"f"},base64url:{label:"\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA \u05D1\u05D1\u05E1\u05D9\u05E1 64 \u05DC\u05DB\u05EA\u05D5\u05D1\u05D5\u05EA \u05E8\u05E9\u05EA",gender:"f"},json_string:{label:"\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA JSON",gender:"f"},e164:{label:"\u05DE\u05E1\u05E4\u05E8 E.164",gender:"m"},jwt:{label:"JWT",gender:"m"},ends_with:{label:"\u05E7\u05DC\u05D8",gender:"m"},includes:{label:"\u05E7\u05DC\u05D8",gender:"m"},lowercase:{label:"\u05E7\u05DC\u05D8",gender:"m"},starts_with:{label:"\u05E7\u05DC\u05D8",gender:"m"},uppercase:{label:"\u05E7\u05DC\u05D8",gender:"m"}},c={nan:"NaN"};return u=>{switch(u.code){case"invalid_type":{let p=u.expected,f=c[p??""]??n(p),m=oe(u.input),h=c[m]??t[m]?.label??m;return/^[A-Z]/.test(u.expected)?`\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05E6\u05E8\u05D9\u05DA \u05DC\u05D4\u05D9\u05D5\u05EA instanceof ${u.expected}, \u05D4\u05EA\u05E7\u05D1\u05DC ${h}`:`\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05E6\u05E8\u05D9\u05DA \u05DC\u05D4\u05D9\u05D5\u05EA ${f}, \u05D4\u05EA\u05E7\u05D1\u05DC ${h}`}case"invalid_value":{if(u.values.length===1)return`\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05E2\u05E8\u05DA \u05D7\u05D9\u05D9\u05D1 \u05DC\u05D4\u05D9\u05D5\u05EA ${re(u.values[0])}`;let p=u.values.map(h=>re(h));if(u.values.length===2)return`\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA \u05D4\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05D4\u05DF ${p[0]} \u05D0\u05D5 ${p[1]}`;let f=p[p.length-1];return`\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA \u05D4\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05D4\u05DF ${p.slice(0,-1).join(", ")} \u05D0\u05D5 ${f}`}case"too_big":{let p=s(u.origin),f=o(u.origin??"value");if(u.origin==="string")return`${p?.longLabel??"\u05D0\u05E8\u05D5\u05DA"} \u05DE\u05D3\u05D9: ${f} \u05E6\u05E8\u05D9\u05DB\u05D4 \u05DC\u05D4\u05DB\u05D9\u05DC ${u.maximum.toString()} ${p?.unit??""} ${u.inclusive?"\u05D0\u05D5 \u05E4\u05D7\u05D5\u05EA":"\u05DC\u05DB\u05DC \u05D4\u05D9\u05D5\u05EA\u05E8"}`.trim();if(u.origin==="number"){let _=u.inclusive?`\u05E7\u05D8\u05DF \u05D0\u05D5 \u05E9\u05D5\u05D5\u05D4 \u05DC-${u.maximum}`:`\u05E7\u05D8\u05DF \u05DE-${u.maximum}`;return`\u05D2\u05D3\u05D5\u05DC \u05DE\u05D3\u05D9: ${f} \u05E6\u05E8\u05D9\u05DA \u05DC\u05D4\u05D9\u05D5\u05EA ${_}`}if(u.origin==="array"||u.origin==="set"){let _=u.origin==="set"?"\u05E6\u05E8\u05D9\u05DB\u05D4":"\u05E6\u05E8\u05D9\u05DA",v=u.inclusive?`${u.maximum} ${p?.unit??""} \u05D0\u05D5 \u05E4\u05D7\u05D5\u05EA`:`\u05E4\u05D7\u05D5\u05EA \u05DE-${u.maximum} ${p?.unit??""}`;return`\u05D2\u05D3\u05D5\u05DC \u05DE\u05D3\u05D9: ${f} ${_} \u05DC\u05D4\u05DB\u05D9\u05DC ${v}`.trim()}let m=u.inclusive?"<=":"<",h=i(u.origin??"value");return p?.unit?`${p.longLabel} \u05DE\u05D3\u05D9: ${f} ${h} ${m}${u.maximum.toString()} ${p.unit}`:`${p?.longLabel??"\u05D2\u05D3\u05D5\u05DC"} \u05DE\u05D3\u05D9: ${f} ${h} ${m}${u.maximum.toString()}`}case"too_small":{let p=s(u.origin),f=o(u.origin??"value");if(u.origin==="string")return`${p?.shortLabel??"\u05E7\u05E6\u05E8"} \u05DE\u05D3\u05D9: ${f} \u05E6\u05E8\u05D9\u05DB\u05D4 \u05DC\u05D4\u05DB\u05D9\u05DC ${u.minimum.toString()} ${p?.unit??""} ${u.inclusive?"\u05D0\u05D5 \u05D9\u05D5\u05EA\u05E8":"\u05DC\u05E4\u05D7\u05D5\u05EA"}`.trim();if(u.origin==="number"){let _=u.inclusive?`\u05D2\u05D3\u05D5\u05DC \u05D0\u05D5 \u05E9\u05D5\u05D5\u05D4 \u05DC-${u.minimum}`:`\u05D2\u05D3\u05D5\u05DC \u05DE-${u.minimum}`;return`\u05E7\u05D8\u05DF \u05DE\u05D3\u05D9: ${f} \u05E6\u05E8\u05D9\u05DA \u05DC\u05D4\u05D9\u05D5\u05EA ${_}`}if(u.origin==="array"||u.origin==="set"){let _=u.origin==="set"?"\u05E6\u05E8\u05D9\u05DB\u05D4":"\u05E6\u05E8\u05D9\u05DA";if(u.minimum===1&&u.inclusive){let E=(u.origin==="set","\u05DC\u05E4\u05D7\u05D5\u05EA \u05E4\u05E8\u05D9\u05D8 \u05D0\u05D7\u05D3");return`\u05E7\u05D8\u05DF \u05DE\u05D3\u05D9: ${f} ${_} \u05DC\u05D4\u05DB\u05D9\u05DC ${E}`}let v=u.inclusive?`${u.minimum} ${p?.unit??""} \u05D0\u05D5 \u05D9\u05D5\u05EA\u05E8`:`\u05D9\u05D5\u05EA\u05E8 \u05DE-${u.minimum} ${p?.unit??""}`;return`\u05E7\u05D8\u05DF \u05DE\u05D3\u05D9: ${f} ${_} \u05DC\u05D4\u05DB\u05D9\u05DC ${v}`.trim()}let m=u.inclusive?">=":">",h=i(u.origin??"value");return p?.unit?`${p.shortLabel} \u05DE\u05D3\u05D9: ${f} ${h} ${m}${u.minimum.toString()} ${p.unit}`:`${p?.shortLabel??"\u05E7\u05D8\u05DF"} \u05DE\u05D3\u05D9: ${f} ${h} ${m}${u.minimum.toString()}`}case"invalid_format":{let p=u;if(p.format==="starts_with")return`\u05D4\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA \u05D7\u05D9\u05D9\u05D1\u05EA \u05DC\u05D4\u05EA\u05D7\u05D9\u05DC \u05D1 "${p.prefix}"`;if(p.format==="ends_with")return`\u05D4\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA \u05D7\u05D9\u05D9\u05D1\u05EA \u05DC\u05D4\u05E1\u05EA\u05D9\u05D9\u05DD \u05D1 "${p.suffix}"`;if(p.format==="includes")return`\u05D4\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA \u05D7\u05D9\u05D9\u05D1\u05EA \u05DC\u05DB\u05DC\u05D5\u05DC "${p.includes}"`;if(p.format==="regex")return`\u05D4\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA \u05D7\u05D9\u05D9\u05D1\u05EA \u05DC\u05D4\u05EA\u05D0\u05D9\u05DD \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA ${p.pattern}`;let f=a[p.format],m=f?.label??p.format,_=(f?.gender??"m")==="f"?"\u05EA\u05E7\u05D9\u05E0\u05D4":"\u05EA\u05E7\u05D9\u05DF";return`${m} \u05DC\u05D0 ${_}`}case"not_multiple_of":return`\u05DE\u05E1\u05E4\u05E8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D7\u05D9\u05D9\u05D1 \u05DC\u05D4\u05D9\u05D5\u05EA \u05DE\u05DB\u05E4\u05DC\u05D4 \u05E9\u05DC ${u.divisor}`;case"unrecognized_keys":return`\u05DE\u05E4\u05EA\u05D7${u.keys.length>1?"\u05D5\u05EA":""} \u05DC\u05D0 \u05DE\u05D6\u05D5\u05D4${u.keys.length>1?"\u05D9\u05DD":"\u05D4"}: ${q(u.keys,", ")}`;case"invalid_key":return"\u05E9\u05D3\u05D4 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1\u05D0\u05D5\u05D1\u05D9\u05D9\u05E7\u05D8";case"invalid_union":return"\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF";case"invalid_element":return`\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1${o(u.origin??"array")}`;default:return"\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF"}}}});function TQ(){return{localeError:xZe()}}var xZe,bQ=O(()=>{je();xZe=()=>{let t={string:{unit:"karakter",verb:"legyen"},file:{unit:"byte",verb:"legyen"},array:{unit:"elem",verb:"legyen"},set:{unit:"elem",verb:"legyen"}};function e(o){return t[o]??null}let r={regex:"bemenet",email:"email c\xEDm",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO id\u0151b\xE9lyeg",date:"ISO d\xE1tum",time:"ISO id\u0151",duration:"ISO id\u0151intervallum",ipv4:"IPv4 c\xEDm",ipv6:"IPv6 c\xEDm",cidrv4:"IPv4 tartom\xE1ny",cidrv6:"IPv6 tartom\xE1ny",base64:"base64-k\xF3dolt string",base64url:"base64url-k\xF3dolt string",json_string:"JSON string",e164:"E.164 sz\xE1m",jwt:"JWT",template_literal:"bemenet"},n={nan:"NaN",number:"sz\xE1m",array:"t\xF6mb"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\xC9rv\xE9nytelen bemenet: a v\xE1rt \xE9rt\xE9k instanceof ${o.expected}, a kapott \xE9rt\xE9k ${a}`:`\xC9rv\xE9nytelen bemenet: a v\xE1rt \xE9rt\xE9k ${i}, a kapott \xE9rt\xE9k ${a}`}case"invalid_value":return o.values.length===1?`\xC9rv\xE9nytelen bemenet: a v\xE1rt \xE9rt\xE9k ${re(o.values[0])}`:`\xC9rv\xE9nytelen opci\xF3: valamelyik \xE9rt\xE9k v\xE1rt ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`T\xFAl nagy: ${o.origin??"\xE9rt\xE9k"} m\xE9rete t\xFAl nagy ${i}${o.maximum.toString()} ${s.unit??"elem"}`:`T\xFAl nagy: a bemeneti \xE9rt\xE9k ${o.origin??"\xE9rt\xE9k"} t\xFAl nagy: ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`T\xFAl kicsi: a bemeneti \xE9rt\xE9k ${o.origin} m\xE9rete t\xFAl kicsi ${i}${o.minimum.toString()} ${s.unit}`:`T\xFAl kicsi: a bemeneti \xE9rt\xE9k ${o.origin} t\xFAl kicsi ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\xC9rv\xE9nytelen string: "${i.prefix}" \xE9rt\xE9kkel kell kezd\u0151dnie`:i.format==="ends_with"?`\xC9rv\xE9nytelen string: "${i.suffix}" \xE9rt\xE9kkel kell v\xE9gz\u0151dnie`:i.format==="includes"?`\xC9rv\xE9nytelen string: "${i.includes}" \xE9rt\xE9ket kell tartalmaznia`:i.format==="regex"?`\xC9rv\xE9nytelen string: ${i.pattern} mint\xE1nak kell megfelelnie`:`\xC9rv\xE9nytelen ${r[i.format]??o.format}`}case"not_multiple_of":return`\xC9rv\xE9nytelen sz\xE1m: ${o.divisor} t\xF6bbsz\xF6r\xF6s\xE9nek kell lennie`;case"unrecognized_keys":return`Ismeretlen kulcs${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`\xC9rv\xE9nytelen kulcs ${o.origin}`;case"invalid_union":return"\xC9rv\xE9nytelen bemenet";case"invalid_element":return`\xC9rv\xE9nytelen \xE9rt\xE9k: ${o.origin}`;default:return"\xC9rv\xE9nytelen bemenet"}}}});function xQ(t,e,r){return Math.abs(t)===1?e:r}function Oh(t){if(!t)return"";let e=["\u0561","\u0565","\u0568","\u056B","\u0578","\u0578\u0582","\u0585"],r=t[t.length-1];return t+(e.includes(r)?"\u0576":"\u0568")}function AQ(){return{localeError:AZe()}}var AZe,wQ=O(()=>{je();AZe=()=>{let t={string:{unit:{one:"\u0576\u0577\u0561\u0576",many:"\u0576\u0577\u0561\u0576\u0576\u0565\u0580"},verb:"\u0578\u0582\u0576\u0565\u0576\u0561\u056C"},file:{unit:{one:"\u0562\u0561\u0575\u0569",many:"\u0562\u0561\u0575\u0569\u0565\u0580"},verb:"\u0578\u0582\u0576\u0565\u0576\u0561\u056C"},array:{unit:{one:"\u057F\u0561\u0580\u0580",many:"\u057F\u0561\u0580\u0580\u0565\u0580"},verb:"\u0578\u0582\u0576\u0565\u0576\u0561\u056C"},set:{unit:{one:"\u057F\u0561\u0580\u0580",many:"\u057F\u0561\u0580\u0580\u0565\u0580"},verb:"\u0578\u0582\u0576\u0565\u0576\u0561\u056C"}};function e(o){return t[o]??null}let r={regex:"\u0574\u0578\u0582\u057F\u0584",email:"\u0567\u056C. \u0570\u0561\u057D\u0581\u0565",url:"URL",emoji:"\u0567\u0574\u0578\u057B\u056B",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \u0561\u0574\u057D\u0561\u0569\u056B\u057E \u0587 \u056A\u0561\u0574",date:"ISO \u0561\u0574\u057D\u0561\u0569\u056B\u057E",time:"ISO \u056A\u0561\u0574",duration:"ISO \u057F\u0587\u0578\u0572\u0578\u0582\u0569\u0575\u0578\u0582\u0576",ipv4:"IPv4 \u0570\u0561\u057D\u0581\u0565",ipv6:"IPv6 \u0570\u0561\u057D\u0581\u0565",cidrv4:"IPv4 \u0574\u056B\u057B\u0561\u056F\u0561\u0575\u0584",cidrv6:"IPv6 \u0574\u056B\u057B\u0561\u056F\u0561\u0575\u0584",base64:"base64 \u0571\u0587\u0561\u0579\u0561\u0583\u0578\u057E \u057F\u0578\u0572",base64url:"base64url \u0571\u0587\u0561\u0579\u0561\u0583\u0578\u057E \u057F\u0578\u0572",json_string:"JSON \u057F\u0578\u0572",e164:"E.164 \u0570\u0561\u0574\u0561\u0580",jwt:"JWT",template_literal:"\u0574\u0578\u0582\u057F\u0584"},n={nan:"NaN",number:"\u0569\u056B\u057E",array:"\u0566\u0561\u0576\u0563\u057E\u0561\u056E"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u054D\u056D\u0561\u056C \u0574\u0578\u0582\u057F\u0584\u0561\u0563\u0580\u0578\u0582\u0574\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567\u0580 instanceof ${o.expected}, \u057D\u057F\u0561\u0581\u057E\u0565\u056C \u0567 ${a}`:`\u054D\u056D\u0561\u056C \u0574\u0578\u0582\u057F\u0584\u0561\u0563\u0580\u0578\u0582\u0574\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567\u0580 ${i}, \u057D\u057F\u0561\u0581\u057E\u0565\u056C \u0567 ${a}`}case"invalid_value":return o.values.length===1?`\u054D\u056D\u0561\u056C \u0574\u0578\u0582\u057F\u0584\u0561\u0563\u0580\u0578\u0582\u0574\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567\u0580 ${re(o.values[1])}`:`\u054D\u056D\u0561\u056C \u057F\u0561\u0580\u0562\u0565\u0580\u0561\u056F\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567\u0580 \u0570\u0565\u057F\u0587\u0575\u0561\u056C\u0576\u0565\u0580\u056B\u0581 \u0574\u0565\u056F\u0568\u055D ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);if(s){let a=Number(o.maximum),c=xQ(a,s.unit.one,s.unit.many);return`\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${Oh(o.origin??"\u0561\u0580\u056A\u0565\u0584")} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${i}${o.maximum.toString()} ${c}`}return`\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${Oh(o.origin??"\u0561\u0580\u056A\u0565\u0584")} \u056C\u056B\u0576\u056B ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);if(s){let a=Number(o.minimum),c=xQ(a,s.unit.one,s.unit.many);return`\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${Oh(o.origin)} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${i}${o.minimum.toString()} ${c}`}return`\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${Oh(o.origin)} \u056C\u056B\u0576\u056B ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u054D\u056D\u0561\u056C \u057F\u0578\u0572\u2024 \u057A\u0565\u057F\u0584 \u0567 \u057D\u056F\u057D\u057E\u056B "${i.prefix}"-\u0578\u057E`:i.format==="ends_with"?`\u054D\u056D\u0561\u056C \u057F\u0578\u0572\u2024 \u057A\u0565\u057F\u0584 \u0567 \u0561\u057E\u0561\u0580\u057F\u057E\u056B "${i.suffix}"-\u0578\u057E`:i.format==="includes"?`\u054D\u056D\u0561\u056C \u057F\u0578\u0572\u2024 \u057A\u0565\u057F\u0584 \u0567 \u057A\u0561\u0580\u0578\u0582\u0576\u0561\u056F\u056B "${i.includes}"`:i.format==="regex"?`\u054D\u056D\u0561\u056C \u057F\u0578\u0572\u2024 \u057A\u0565\u057F\u0584 \u0567 \u0570\u0561\u0574\u0561\u057A\u0561\u057F\u0561\u057D\u056D\u0561\u0576\u056B ${i.pattern} \u0571\u0587\u0561\u0579\u0561\u0583\u056B\u0576`:`\u054D\u056D\u0561\u056C ${r[i.format]??o.format}`}case"not_multiple_of":return`\u054D\u056D\u0561\u056C \u0569\u056B\u057E\u2024 \u057A\u0565\u057F\u0584 \u0567 \u0562\u0561\u0566\u0574\u0561\u057A\u0561\u057F\u056B\u056F \u056C\u056B\u0576\u056B ${o.divisor}-\u056B`;case"unrecognized_keys":return`\u0549\u0573\u0561\u0576\u0561\u0579\u057E\u0561\u056E \u0562\u0561\u0576\u0561\u056C\u056B${o.keys.length>1?"\u0576\u0565\u0580":""}. ${q(o.keys,", ")}`;case"invalid_key":return`\u054D\u056D\u0561\u056C \u0562\u0561\u0576\u0561\u056C\u056B ${Oh(o.origin)}-\u0578\u0582\u0574`;case"invalid_union":return"\u054D\u056D\u0561\u056C \u0574\u0578\u0582\u057F\u0584\u0561\u0563\u0580\u0578\u0582\u0574";case"invalid_element":return`\u054D\u056D\u0561\u056C \u0561\u0580\u056A\u0565\u0584 ${Oh(o.origin)}-\u0578\u0582\u0574`;default:return"\u054D\u056D\u0561\u056C \u0574\u0578\u0582\u057F\u0584\u0561\u0563\u0580\u0578\u0582\u0574"}}}});function RQ(){return{localeError:wZe()}}var wZe,PQ=O(()=>{je();wZe=()=>{let t={string:{unit:"karakter",verb:"memiliki"},file:{unit:"byte",verb:"memiliki"},array:{unit:"item",verb:"memiliki"},set:{unit:"item",verb:"memiliki"}};function e(o){return t[o]??null}let r={regex:"input",email:"alamat email",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"tanggal dan waktu format ISO",date:"tanggal format ISO",time:"jam format ISO",duration:"durasi format ISO",ipv4:"alamat IPv4",ipv6:"alamat IPv6",cidrv4:"rentang alamat IPv4",cidrv6:"rentang alamat IPv6",base64:"string dengan enkode base64",base64url:"string dengan enkode base64url",json_string:"string JSON",e164:"angka E.164",jwt:"JWT",template_literal:"input"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Input tidak valid: diharapkan instanceof ${o.expected}, diterima ${a}`:`Input tidak valid: diharapkan ${i}, diterima ${a}`}case"invalid_value":return o.values.length===1?`Input tidak valid: diharapkan ${re(o.values[0])}`:`Pilihan tidak valid: diharapkan salah satu dari ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Terlalu besar: diharapkan ${o.origin??"value"} memiliki ${i}${o.maximum.toString()} ${s.unit??"elemen"}`:`Terlalu besar: diharapkan ${o.origin??"value"} menjadi ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Terlalu kecil: diharapkan ${o.origin} memiliki ${i}${o.minimum.toString()} ${s.unit}`:`Terlalu kecil: diharapkan ${o.origin} menjadi ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`String tidak valid: harus dimulai dengan "${i.prefix}"`:i.format==="ends_with"?`String tidak valid: harus berakhir dengan "${i.suffix}"`:i.format==="includes"?`String tidak valid: harus menyertakan "${i.includes}"`:i.format==="regex"?`String tidak valid: harus sesuai pola ${i.pattern}`:`${r[i.format]??o.format} tidak valid`}case"not_multiple_of":return`Angka tidak valid: harus kelipatan dari ${o.divisor}`;case"unrecognized_keys":return`Kunci tidak dikenali ${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Kunci tidak valid di ${o.origin}`;case"invalid_union":return"Input tidak valid";case"invalid_element":return`Nilai tidak valid di ${o.origin}`;default:return"Input tidak valid"}}}});function IQ(){return{localeError:RZe()}}var RZe,OQ=O(()=>{je();RZe=()=>{let t={string:{unit:"stafi",verb:"a\xF0 hafa"},file:{unit:"b\xE6ti",verb:"a\xF0 hafa"},array:{unit:"hluti",verb:"a\xF0 hafa"},set:{unit:"hluti",verb:"a\xF0 hafa"}};function e(o){return t[o]??null}let r={regex:"gildi",email:"netfang",url:"vefsl\xF3\xF0",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO dagsetning og t\xEDmi",date:"ISO dagsetning",time:"ISO t\xEDmi",duration:"ISO t\xEDmalengd",ipv4:"IPv4 address",ipv6:"IPv6 address",cidrv4:"IPv4 range",cidrv6:"IPv6 range",base64:"base64-encoded strengur",base64url:"base64url-encoded strengur",json_string:"JSON strengur",e164:"E.164 t\xF6lugildi",jwt:"JWT",template_literal:"gildi"},n={nan:"NaN",number:"n\xFAmer",array:"fylki"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Rangt gildi: \xDE\xFA sl\xF3st inn ${a} \xFEar sem \xE1 a\xF0 vera instanceof ${o.expected}`:`Rangt gildi: \xDE\xFA sl\xF3st inn ${a} \xFEar sem \xE1 a\xF0 vera ${i}`}case"invalid_value":return o.values.length===1?`Rangt gildi: gert r\xE1\xF0 fyrir ${re(o.values[0])}`:`\xD3gilt val: m\xE1 vera eitt af eftirfarandi ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Of st\xF3rt: gert er r\xE1\xF0 fyrir a\xF0 ${o.origin??"gildi"} hafi ${i}${o.maximum.toString()} ${s.unit??"hluti"}`:`Of st\xF3rt: gert er r\xE1\xF0 fyrir a\xF0 ${o.origin??"gildi"} s\xE9 ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Of l\xEDti\xF0: gert er r\xE1\xF0 fyrir a\xF0 ${o.origin} hafi ${i}${o.minimum.toString()} ${s.unit}`:`Of l\xEDti\xF0: gert er r\xE1\xF0 fyrir a\xF0 ${o.origin} s\xE9 ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\xD3gildur strengur: ver\xF0ur a\xF0 byrja \xE1 "${i.prefix}"`:i.format==="ends_with"?`\xD3gildur strengur: ver\xF0ur a\xF0 enda \xE1 "${i.suffix}"`:i.format==="includes"?`\xD3gildur strengur: ver\xF0ur a\xF0 innihalda "${i.includes}"`:i.format==="regex"?`\xD3gildur strengur: ver\xF0ur a\xF0 fylgja mynstri ${i.pattern}`:`Rangt ${r[i.format]??o.format}`}case"not_multiple_of":return`R\xF6ng tala: ver\xF0ur a\xF0 vera margfeldi af ${o.divisor}`;case"unrecognized_keys":return`\xD3\xFEekkt ${o.keys.length>1?"ir lyklar":"ur lykill"}: ${q(o.keys,", ")}`;case"invalid_key":return`Rangur lykill \xED ${o.origin}`;case"invalid_union":return"Rangt gildi";case"invalid_element":return`Rangt gildi \xED ${o.origin}`;default:return"Rangt gildi"}}}});function NQ(){return{localeError:PZe()}}var PZe,CQ=O(()=>{je();PZe=()=>{let t={string:{unit:"caratteri",verb:"avere"},file:{unit:"byte",verb:"avere"},array:{unit:"elementi",verb:"avere"},set:{unit:"elementi",verb:"avere"}};function e(o){return t[o]??null}let r={regex:"input",email:"indirizzo email",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"data e ora ISO",date:"data ISO",time:"ora ISO",duration:"durata ISO",ipv4:"indirizzo IPv4",ipv6:"indirizzo IPv6",cidrv4:"intervallo IPv4",cidrv6:"intervallo IPv6",base64:"stringa codificata in base64",base64url:"URL codificata in base64",json_string:"stringa JSON",e164:"numero E.164",jwt:"JWT",template_literal:"input"},n={nan:"NaN",number:"numero",array:"vettore"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Input non valido: atteso instanceof ${o.expected}, ricevuto ${a}`:`Input non valido: atteso ${i}, ricevuto ${a}`}case"invalid_value":return o.values.length===1?`Input non valido: atteso ${re(o.values[0])}`:`Opzione non valida: atteso uno tra ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Troppo grande: ${o.origin??"valore"} deve avere ${i}${o.maximum.toString()} ${s.unit??"elementi"}`:`Troppo grande: ${o.origin??"valore"} deve essere ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Troppo piccolo: ${o.origin} deve avere ${i}${o.minimum.toString()} ${s.unit}`:`Troppo piccolo: ${o.origin} deve essere ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Stringa non valida: deve iniziare con "${i.prefix}"`:i.format==="ends_with"?`Stringa non valida: deve terminare con "${i.suffix}"`:i.format==="includes"?`Stringa non valida: deve includere "${i.includes}"`:i.format==="regex"?`Stringa non valida: deve corrispondere al pattern ${i.pattern}`:`Invalid ${r[i.format]??o.format}`}case"not_multiple_of":return`Numero non valido: deve essere un multiplo di ${o.divisor}`;case"unrecognized_keys":return`Chiav${o.keys.length>1?"i":"e"} non riconosciut${o.keys.length>1?"e":"a"}: ${q(o.keys,", ")}`;case"invalid_key":return`Chiave non valida in ${o.origin}`;case"invalid_union":return"Input non valido";case"invalid_element":return`Valore non valido in ${o.origin}`;default:return"Input non valido"}}}});function $Q(){return{localeError:IZe()}}var IZe,kQ=O(()=>{je();IZe=()=>{let t={string:{unit:"\u6587\u5B57",verb:"\u3067\u3042\u308B"},file:{unit:"\u30D0\u30A4\u30C8",verb:"\u3067\u3042\u308B"},array:{unit:"\u8981\u7D20",verb:"\u3067\u3042\u308B"},set:{unit:"\u8981\u7D20",verb:"\u3067\u3042\u308B"}};function e(o){return t[o]??null}let r={regex:"\u5165\u529B\u5024",email:"\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9",url:"URL",emoji:"\u7D75\u6587\u5B57",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO\u65E5\u6642",date:"ISO\u65E5\u4ED8",time:"ISO\u6642\u523B",duration:"ISO\u671F\u9593",ipv4:"IPv4\u30A2\u30C9\u30EC\u30B9",ipv6:"IPv6\u30A2\u30C9\u30EC\u30B9",cidrv4:"IPv4\u7BC4\u56F2",cidrv6:"IPv6\u7BC4\u56F2",base64:"base64\u30A8\u30F3\u30B3\u30FC\u30C9\u6587\u5B57\u5217",base64url:"base64url\u30A8\u30F3\u30B3\u30FC\u30C9\u6587\u5B57\u5217",json_string:"JSON\u6587\u5B57\u5217",e164:"E.164\u756A\u53F7",jwt:"JWT",template_literal:"\u5165\u529B\u5024"},n={nan:"NaN",number:"\u6570\u5024",array:"\u914D\u5217"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u7121\u52B9\u306A\u5165\u529B: instanceof ${o.expected}\u304C\u671F\u5F85\u3055\u308C\u307E\u3057\u305F\u304C\u3001${a}\u304C\u5165\u529B\u3055\u308C\u307E\u3057\u305F`:`\u7121\u52B9\u306A\u5165\u529B: ${i}\u304C\u671F\u5F85\u3055\u308C\u307E\u3057\u305F\u304C\u3001${a}\u304C\u5165\u529B\u3055\u308C\u307E\u3057\u305F`}case"invalid_value":return o.values.length===1?`\u7121\u52B9\u306A\u5165\u529B: ${re(o.values[0])}\u304C\u671F\u5F85\u3055\u308C\u307E\u3057\u305F`:`\u7121\u52B9\u306A\u9078\u629E: ${q(o.values,"\u3001")}\u306E\u3044\u305A\u308C\u304B\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`;case"too_big":{let i=o.inclusive?"\u4EE5\u4E0B\u3067\u3042\u308B":"\u3088\u308A\u5C0F\u3055\u3044",s=e(o.origin);return s?`\u5927\u304D\u3059\u304E\u308B\u5024: ${o.origin??"\u5024"}\u306F${o.maximum.toString()}${s.unit??"\u8981\u7D20"}${i}\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`:`\u5927\u304D\u3059\u304E\u308B\u5024: ${o.origin??"\u5024"}\u306F${o.maximum.toString()}${i}\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`}case"too_small":{let i=o.inclusive?"\u4EE5\u4E0A\u3067\u3042\u308B":"\u3088\u308A\u5927\u304D\u3044",s=e(o.origin);return s?`\u5C0F\u3055\u3059\u304E\u308B\u5024: ${o.origin}\u306F${o.minimum.toString()}${s.unit}${i}\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`:`\u5C0F\u3055\u3059\u304E\u308B\u5024: ${o.origin}\u306F${o.minimum.toString()}${i}\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u7121\u52B9\u306A\u6587\u5B57\u5217: "${i.prefix}"\u3067\u59CB\u307E\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`:i.format==="ends_with"?`\u7121\u52B9\u306A\u6587\u5B57\u5217: "${i.suffix}"\u3067\u7D42\u308F\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`:i.format==="includes"?`\u7121\u52B9\u306A\u6587\u5B57\u5217: "${i.includes}"\u3092\u542B\u3080\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`:i.format==="regex"?`\u7121\u52B9\u306A\u6587\u5B57\u5217: \u30D1\u30BF\u30FC\u30F3${i.pattern}\u306B\u4E00\u81F4\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`:`\u7121\u52B9\u306A${r[i.format]??o.format}`}case"not_multiple_of":return`\u7121\u52B9\u306A\u6570\u5024: ${o.divisor}\u306E\u500D\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`;case"unrecognized_keys":return`\u8A8D\u8B58\u3055\u308C\u3066\u3044\u306A\u3044\u30AD\u30FC${o.keys.length>1?"\u7FA4":""}: ${q(o.keys,"\u3001")}`;case"invalid_key":return`${o.origin}\u5185\u306E\u7121\u52B9\u306A\u30AD\u30FC`;case"invalid_union":return"\u7121\u52B9\u306A\u5165\u529B";case"invalid_element":return`${o.origin}\u5185\u306E\u7121\u52B9\u306A\u5024`;default:return"\u7121\u52B9\u306A\u5165\u529B"}}}});function MQ(){return{localeError:OZe()}}var OZe,DQ=O(()=>{je();OZe=()=>{let t={string:{unit:"\u10E1\u10D8\u10DB\u10D1\u10DD\u10DA\u10DD",verb:"\u10E3\u10DC\u10D3\u10D0 \u10E8\u10D4\u10D8\u10EA\u10D0\u10D5\u10D3\u10D4\u10E1"},file:{unit:"\u10D1\u10D0\u10D8\u10E2\u10D8",verb:"\u10E3\u10DC\u10D3\u10D0 \u10E8\u10D4\u10D8\u10EA\u10D0\u10D5\u10D3\u10D4\u10E1"},array:{unit:"\u10D4\u10DA\u10D4\u10DB\u10D4\u10DC\u10E2\u10D8",verb:"\u10E3\u10DC\u10D3\u10D0 \u10E8\u10D4\u10D8\u10EA\u10D0\u10D5\u10D3\u10D4\u10E1"},set:{unit:"\u10D4\u10DA\u10D4\u10DB\u10D4\u10DC\u10E2\u10D8",verb:"\u10E3\u10DC\u10D3\u10D0 \u10E8\u10D4\u10D8\u10EA\u10D0\u10D5\u10D3\u10D4\u10E1"}};function e(o){return t[o]??null}let r={regex:"\u10E8\u10D4\u10E7\u10D5\u10D0\u10DC\u10D0",email:"\u10D4\u10DA-\u10E4\u10DD\u10E1\u10E2\u10D8\u10E1 \u10DB\u10D8\u10E1\u10D0\u10DB\u10D0\u10E0\u10D7\u10D8",url:"URL",emoji:"\u10D4\u10DB\u10DD\u10EF\u10D8",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\u10D7\u10D0\u10E0\u10D8\u10E6\u10D8-\u10D3\u10E0\u10DD",date:"\u10D7\u10D0\u10E0\u10D8\u10E6\u10D8",time:"\u10D3\u10E0\u10DD",duration:"\u10EE\u10D0\u10DC\u10D2\u10E0\u10EB\u10DA\u10D8\u10D5\u10DD\u10D1\u10D0",ipv4:"IPv4 \u10DB\u10D8\u10E1\u10D0\u10DB\u10D0\u10E0\u10D7\u10D8",ipv6:"IPv6 \u10DB\u10D8\u10E1\u10D0\u10DB\u10D0\u10E0\u10D7\u10D8",cidrv4:"IPv4 \u10D3\u10D8\u10D0\u10DE\u10D0\u10D6\u10DD\u10DC\u10D8",cidrv6:"IPv6 \u10D3\u10D8\u10D0\u10DE\u10D0\u10D6\u10DD\u10DC\u10D8",base64:"base64-\u10D9\u10DD\u10D3\u10D8\u10E0\u10D4\u10D1\u10E3\u10DA\u10D8 \u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8",base64url:"base64url-\u10D9\u10DD\u10D3\u10D8\u10E0\u10D4\u10D1\u10E3\u10DA\u10D8 \u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8",json_string:"JSON \u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8",e164:"E.164 \u10DC\u10DD\u10DB\u10D4\u10E0\u10D8",jwt:"JWT",template_literal:"\u10E8\u10D4\u10E7\u10D5\u10D0\u10DC\u10D0"},n={nan:"NaN",number:"\u10E0\u10D8\u10EA\u10EE\u10D5\u10D8",string:"\u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8",boolean:"\u10D1\u10E3\u10DA\u10D4\u10D0\u10DC\u10D8",function:"\u10E4\u10E3\u10DC\u10E5\u10EA\u10D8\u10D0",array:"\u10DB\u10D0\u10E1\u10D8\u10D5\u10D8"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E8\u10D4\u10E7\u10D5\u10D0\u10DC\u10D0: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8 instanceof ${o.expected}, \u10DB\u10D8\u10E6\u10D4\u10D1\u10E3\u10DA\u10D8 ${a}`:`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E8\u10D4\u10E7\u10D5\u10D0\u10DC\u10D0: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8 ${i}, \u10DB\u10D8\u10E6\u10D4\u10D1\u10E3\u10DA\u10D8 ${a}`}case"invalid_value":return o.values.length===1?`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E8\u10D4\u10E7\u10D5\u10D0\u10DC\u10D0: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8 ${re(o.values[0])}`:`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10D5\u10D0\u10E0\u10D8\u10D0\u10DC\u10E2\u10D8: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8\u10D0 \u10D4\u10E0\u10D7-\u10D4\u10E0\u10D7\u10D8 ${q(o.values,"|")}-\u10D3\u10D0\u10DC`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u10D6\u10D4\u10D3\u10DB\u10D4\u10E2\u10D0\u10D3 \u10D3\u10D8\u10D3\u10D8: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8 ${o.origin??"\u10DB\u10DC\u10D8\u10E8\u10D5\u10DC\u10D4\u10DA\u10DD\u10D1\u10D0"} ${s.verb} ${i}${o.maximum.toString()} ${s.unit}`:`\u10D6\u10D4\u10D3\u10DB\u10D4\u10E2\u10D0\u10D3 \u10D3\u10D8\u10D3\u10D8: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8 ${o.origin??"\u10DB\u10DC\u10D8\u10E8\u10D5\u10DC\u10D4\u10DA\u10DD\u10D1\u10D0"} \u10D8\u10E7\u10DD\u10E1 ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u10D6\u10D4\u10D3\u10DB\u10D4\u10E2\u10D0\u10D3 \u10DE\u10D0\u10E2\u10D0\u10E0\u10D0: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8 ${o.origin} ${s.verb} ${i}${o.minimum.toString()} ${s.unit}`:`\u10D6\u10D4\u10D3\u10DB\u10D4\u10E2\u10D0\u10D3 \u10DE\u10D0\u10E2\u10D0\u10E0\u10D0: \u10DB\u10DD\u10E1\u10D0\u10DA\u10DD\u10D3\u10DC\u10D4\u10DA\u10D8 ${o.origin} \u10D8\u10E7\u10DD\u10E1 ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8: \u10E3\u10DC\u10D3\u10D0 \u10D8\u10EC\u10E7\u10D4\u10D1\u10DD\u10D3\u10D4\u10E1 "${i.prefix}"-\u10D8\u10D7`:i.format==="ends_with"?`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8: \u10E3\u10DC\u10D3\u10D0 \u10DB\u10D7\u10D0\u10D5\u10E0\u10D3\u10D4\u10D1\u10DD\u10D3\u10D4\u10E1 "${i.suffix}"-\u10D8\u10D7`:i.format==="includes"?`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8: \u10E3\u10DC\u10D3\u10D0 \u10E8\u10D4\u10D8\u10EA\u10D0\u10D5\u10D3\u10D4\u10E1 "${i.includes}"-\u10E1`:i.format==="regex"?`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E1\u10E2\u10E0\u10D8\u10DC\u10D2\u10D8: \u10E3\u10DC\u10D3\u10D0 \u10E8\u10D4\u10D4\u10E1\u10D0\u10D1\u10D0\u10DB\u10D4\u10D1\u10DD\u10D3\u10D4\u10E1 \u10E8\u10D0\u10D1\u10DA\u10DD\u10DC\u10E1 ${i.pattern}`:`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 ${r[i.format]??o.format}`}case"not_multiple_of":return`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E0\u10D8\u10EA\u10EE\u10D5\u10D8: \u10E3\u10DC\u10D3\u10D0 \u10D8\u10E7\u10DD\u10E1 ${o.divisor}-\u10D8\u10E1 \u10EF\u10D4\u10E0\u10D0\u10D3\u10D8`;case"unrecognized_keys":return`\u10E3\u10EA\u10DC\u10DD\u10D1\u10D8 \u10D2\u10D0\u10E1\u10D0\u10E6\u10D4\u10D1${o.keys.length>1?"\u10D4\u10D1\u10D8":"\u10D8"}: ${q(o.keys,", ")}`;case"invalid_key":return`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10D2\u10D0\u10E1\u10D0\u10E6\u10D4\u10D1\u10D8 ${o.origin}-\u10E8\u10D8`;case"invalid_union":return"\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E8\u10D4\u10E7\u10D5\u10D0\u10DC\u10D0";case"invalid_element":return`\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10DB\u10DC\u10D8\u10E8\u10D5\u10DC\u10D4\u10DA\u10DD\u10D1\u10D0 ${o.origin}-\u10E8\u10D8`;default:return"\u10D0\u10E0\u10D0\u10E1\u10EC\u10DD\u10E0\u10D8 \u10E8\u10D4\u10E7\u10D5\u10D0\u10DC\u10D0"}}}});function LR(){return{localeError:NZe()}}var NZe,sz=O(()=>{je();NZe=()=>{let t={string:{unit:"\u178F\u17BD\u17A2\u1780\u17D2\u179F\u179A",verb:"\u1782\u17BD\u179A\u1798\u17B6\u1793"},file:{unit:"\u1794\u17C3",verb:"\u1782\u17BD\u179A\u1798\u17B6\u1793"},array:{unit:"\u1792\u17B6\u178F\u17BB",verb:"\u1782\u17BD\u179A\u1798\u17B6\u1793"},set:{unit:"\u1792\u17B6\u178F\u17BB",verb:"\u1782\u17BD\u179A\u1798\u17B6\u1793"}};function e(o){return t[o]??null}let r={regex:"\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1794\u1789\u17D2\u1785\u17BC\u179B",email:"\u17A2\u17B6\u179F\u1799\u178A\u17D2\u178B\u17B6\u1793\u17A2\u17CA\u17B8\u1798\u17C2\u179B",url:"URL",emoji:"\u179F\u1789\u17D2\u1789\u17B6\u17A2\u17B6\u179A\u1798\u17D2\u1798\u178E\u17CD",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\u1780\u17B6\u179B\u1794\u179A\u17B7\u1785\u17D2\u1786\u17C1\u1791 \u1793\u17B7\u1784\u1798\u17C9\u17C4\u1784 ISO",date:"\u1780\u17B6\u179B\u1794\u179A\u17B7\u1785\u17D2\u1786\u17C1\u1791 ISO",time:"\u1798\u17C9\u17C4\u1784 ISO",duration:"\u179A\u1799\u17C8\u1796\u17C1\u179B ISO",ipv4:"\u17A2\u17B6\u179F\u1799\u178A\u17D2\u178B\u17B6\u1793 IPv4",ipv6:"\u17A2\u17B6\u179F\u1799\u178A\u17D2\u178B\u17B6\u1793 IPv6",cidrv4:"\u178A\u17C2\u1793\u17A2\u17B6\u179F\u1799\u178A\u17D2\u178B\u17B6\u1793 IPv4",cidrv6:"\u178A\u17C2\u1793\u17A2\u17B6\u179F\u1799\u178A\u17D2\u178B\u17B6\u1793 IPv6",base64:"\u1781\u17D2\u179F\u17C2\u17A2\u1780\u17D2\u179F\u179A\u17A2\u17CA\u17B7\u1780\u17BC\u178A base64",base64url:"\u1781\u17D2\u179F\u17C2\u17A2\u1780\u17D2\u179F\u179A\u17A2\u17CA\u17B7\u1780\u17BC\u178A base64url",json_string:"\u1781\u17D2\u179F\u17C2\u17A2\u1780\u17D2\u179F\u179A JSON",e164:"\u179B\u17C1\u1781 E.164",jwt:"JWT",template_literal:"\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1794\u1789\u17D2\u1785\u17BC\u179B"},n={nan:"NaN",number:"\u179B\u17C1\u1781",array:"\u17A2\u17B6\u179A\u17C1 (Array)",null:"\u1782\u17D2\u1798\u17B6\u1793\u178F\u1798\u17D2\u179B\u17C3 (null)"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1794\u1789\u17D2\u1785\u17BC\u179B\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1780\u17B6\u179A instanceof ${o.expected} \u1794\u17C9\u17BB\u1793\u17D2\u178F\u17C2\u1791\u1791\u17BD\u179B\u1794\u17B6\u1793 ${a}`:`\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1794\u1789\u17D2\u1785\u17BC\u179B\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1780\u17B6\u179A ${i} \u1794\u17C9\u17BB\u1793\u17D2\u178F\u17C2\u1791\u1791\u17BD\u179B\u1794\u17B6\u1793 ${a}`}case"invalid_value":return o.values.length===1?`\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1794\u1789\u17D2\u1785\u17BC\u179B\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1780\u17B6\u179A ${re(o.values[0])}`:`\u1787\u1798\u17D2\u179A\u17BE\u179F\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1787\u17B6\u1798\u17BD\u1799\u1780\u17D2\u1793\u17BB\u1784\u1785\u17C6\u178E\u17C4\u1798 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u1792\u17C6\u1796\u17C1\u1780\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1780\u17B6\u179A ${o.origin??"\u178F\u1798\u17D2\u179B\u17C3"} ${i} ${o.maximum.toString()} ${s.unit??"\u1792\u17B6\u178F\u17BB"}`:`\u1792\u17C6\u1796\u17C1\u1780\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1780\u17B6\u179A ${o.origin??"\u178F\u1798\u17D2\u179B\u17C3"} ${i} ${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u178F\u17BC\u1785\u1796\u17C1\u1780\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1780\u17B6\u179A ${o.origin} ${i} ${o.minimum.toString()} ${s.unit}`:`\u178F\u17BC\u1785\u1796\u17C1\u1780\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1780\u17B6\u179A ${o.origin} ${i} ${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u1781\u17D2\u179F\u17C2\u17A2\u1780\u17D2\u179F\u179A\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1785\u17B6\u1794\u17CB\u1795\u17D2\u178F\u17BE\u1798\u178A\u17C4\u1799 "${i.prefix}"`:i.format==="ends_with"?`\u1781\u17D2\u179F\u17C2\u17A2\u1780\u17D2\u179F\u179A\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1794\u1789\u17D2\u1785\u1794\u17CB\u178A\u17C4\u1799 "${i.suffix}"`:i.format==="includes"?`\u1781\u17D2\u179F\u17C2\u17A2\u1780\u17D2\u179F\u179A\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u1798\u17B6\u1793 "${i.includes}"`:i.format==="regex"?`\u1781\u17D2\u179F\u17C2\u17A2\u1780\u17D2\u179F\u179A\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u178F\u17C2\u1795\u17D2\u1782\u17BC\u1795\u17D2\u1782\u1784\u1793\u17B9\u1784\u1791\u1798\u17D2\u179A\u1784\u17CB\u178A\u17C2\u179B\u1794\u17B6\u1793\u1780\u17C6\u178E\u178F\u17CB ${i.pattern}`:`\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 ${r[i.format]??o.format}`}case"not_multiple_of":return`\u179B\u17C1\u1781\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u17D6 \u178F\u17D2\u179A\u17BC\u179C\u178F\u17C2\u1787\u17B6\u1796\u17A0\u17BB\u1782\u17BB\u178E\u1793\u17C3 ${o.divisor}`;case"unrecognized_keys":return`\u179A\u1780\u1783\u17BE\u1789\u179F\u17C4\u1798\u17B7\u1793\u179F\u17D2\u1782\u17B6\u179B\u17CB\u17D6 ${q(o.keys,", ")}`;case"invalid_key":return`\u179F\u17C4\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u1793\u17C5\u1780\u17D2\u1793\u17BB\u1784 ${o.origin}`;case"invalid_union":return"\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C";case"invalid_element":return`\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C\u1793\u17C5\u1780\u17D2\u1793\u17BB\u1784 ${o.origin}`;default:return"\u1791\u17B7\u1793\u17D2\u1793\u1793\u17D0\u1799\u1798\u17B7\u1793\u178F\u17D2\u179A\u17B9\u1798\u178F\u17D2\u179A\u17BC\u179C"}}}});function LQ(){return LR()}var UQ=O(()=>{sz()});function jQ(){return{localeError:CZe()}}var CZe,zQ=O(()=>{je();CZe=()=>{let t={string:{unit:"\uBB38\uC790",verb:"to have"},file:{unit:"\uBC14\uC774\uD2B8",verb:"to have"},array:{unit:"\uAC1C",verb:"to have"},set:{unit:"\uAC1C",verb:"to have"}};function e(o){return t[o]??null}let r={regex:"\uC785\uB825",email:"\uC774\uBA54\uC77C \uC8FC\uC18C",url:"URL",emoji:"\uC774\uBAA8\uC9C0",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \uB0A0\uC9DC\uC2DC\uAC04",date:"ISO \uB0A0\uC9DC",time:"ISO \uC2DC\uAC04",duration:"ISO \uAE30\uAC04",ipv4:"IPv4 \uC8FC\uC18C",ipv6:"IPv6 \uC8FC\uC18C",cidrv4:"IPv4 \uBC94\uC704",cidrv6:"IPv6 \uBC94\uC704",base64:"base64 \uC778\uCF54\uB529 \uBB38\uC790\uC5F4",base64url:"base64url \uC778\uCF54\uB529 \uBB38\uC790\uC5F4",json_string:"JSON \uBB38\uC790\uC5F4",e164:"E.164 \uBC88\uD638",jwt:"JWT",template_literal:"\uC785\uB825"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\uC798\uBABB\uB41C \uC785\uB825: \uC608\uC0C1 \uD0C0\uC785\uC740 instanceof ${o.expected}, \uBC1B\uC740 \uD0C0\uC785\uC740 ${a}\uC785\uB2C8\uB2E4`:`\uC798\uBABB\uB41C \uC785\uB825: \uC608\uC0C1 \uD0C0\uC785\uC740 ${i}, \uBC1B\uC740 \uD0C0\uC785\uC740 ${a}\uC785\uB2C8\uB2E4`}case"invalid_value":return o.values.length===1?`\uC798\uBABB\uB41C \uC785\uB825: \uAC12\uC740 ${re(o.values[0])} \uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4`:`\uC798\uBABB\uB41C \uC635\uC158: ${q(o.values,"\uB610\uB294 ")} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4`;case"too_big":{let i=o.inclusive?"\uC774\uD558":"\uBBF8\uB9CC",s=i==="\uBBF8\uB9CC"?"\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4":"\uC5EC\uC57C \uD569\uB2C8\uB2E4",a=e(o.origin),c=a?.unit??"\uC694\uC18C";return a?`${o.origin??"\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${o.maximum.toString()}${c} ${i}${s}`:`${o.origin??"\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${o.maximum.toString()} ${i}${s}`}case"too_small":{let i=o.inclusive?"\uC774\uC0C1":"\uCD08\uACFC",s=i==="\uC774\uC0C1"?"\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4":"\uC5EC\uC57C \uD569\uB2C8\uB2E4",a=e(o.origin),c=a?.unit??"\uC694\uC18C";return a?`${o.origin??"\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${o.minimum.toString()}${c} ${i}${s}`:`${o.origin??"\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${o.minimum.toString()} ${i}${s}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\uC798\uBABB\uB41C \uBB38\uC790\uC5F4: "${i.prefix}"(\uC73C)\uB85C \uC2DC\uC791\uD574\uC57C \uD569\uB2C8\uB2E4`:i.format==="ends_with"?`\uC798\uBABB\uB41C \uBB38\uC790\uC5F4: "${i.suffix}"(\uC73C)\uB85C \uB05D\uB098\uC57C \uD569\uB2C8\uB2E4`:i.format==="includes"?`\uC798\uBABB\uB41C \uBB38\uC790\uC5F4: "${i.includes}"\uC744(\uB97C) \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4`:i.format==="regex"?`\uC798\uBABB\uB41C \uBB38\uC790\uC5F4: \uC815\uADDC\uC2DD ${i.pattern} \uD328\uD134\uACFC \uC77C\uCE58\uD574\uC57C \uD569\uB2C8\uB2E4`:`\uC798\uBABB\uB41C ${r[i.format]??o.format}`}case"not_multiple_of":return`\uC798\uBABB\uB41C \uC22B\uC790: ${o.divisor}\uC758 \uBC30\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4`;case"unrecognized_keys":return`\uC778\uC2DD\uD560 \uC218 \uC5C6\uB294 \uD0A4: ${q(o.keys,", ")}`;case"invalid_key":return`\uC798\uBABB\uB41C \uD0A4: ${o.origin}`;case"invalid_union":return"\uC798\uBABB\uB41C \uC785\uB825";case"invalid_element":return`\uC798\uBABB\uB41C \uAC12: ${o.origin}`;default:return"\uC798\uBABB\uB41C \uC785\uB825"}}}});function FQ(t){let e=Math.abs(t),r=e%10,n=e%100;return n>=11&&n<=19||r===0?"many":r===1?"one":"few"}function qQ(){return{localeError:$Ze()}}var yy,$Ze,BQ=O(()=>{je();yy=t=>t.charAt(0).toUpperCase()+t.slice(1);$Ze=()=>{let t={string:{unit:{one:"simbolis",few:"simboliai",many:"simboli\u0173"},verb:{smaller:{inclusive:"turi b\u016Bti ne ilgesn\u0117 kaip",notInclusive:"turi b\u016Bti trumpesn\u0117 kaip"},bigger:{inclusive:"turi b\u016Bti ne trumpesn\u0117 kaip",notInclusive:"turi b\u016Bti ilgesn\u0117 kaip"}}},file:{unit:{one:"baitas",few:"baitai",many:"bait\u0173"},verb:{smaller:{inclusive:"turi b\u016Bti ne didesnis kaip",notInclusive:"turi b\u016Bti ma\u017Eesnis kaip"},bigger:{inclusive:"turi b\u016Bti ne ma\u017Eesnis kaip",notInclusive:"turi b\u016Bti didesnis kaip"}}},array:{unit:{one:"element\u0105",few:"elementus",many:"element\u0173"},verb:{smaller:{inclusive:"turi tur\u0117ti ne daugiau kaip",notInclusive:"turi tur\u0117ti ma\u017Eiau kaip"},bigger:{inclusive:"turi tur\u0117ti ne ma\u017Eiau kaip",notInclusive:"turi tur\u0117ti daugiau kaip"}}},set:{unit:{one:"element\u0105",few:"elementus",many:"element\u0173"},verb:{smaller:{inclusive:"turi tur\u0117ti ne daugiau kaip",notInclusive:"turi tur\u0117ti ma\u017Eiau kaip"},bigger:{inclusive:"turi tur\u0117ti ne ma\u017Eiau kaip",notInclusive:"turi tur\u0117ti daugiau kaip"}}}};function e(o,i,s,a){let c=t[o]??null;return c===null?c:{unit:c.unit[i],verb:c.verb[a][s?"inclusive":"notInclusive"]}}let r={regex:"\u012Fvestis",email:"el. pa\u0161to adresas",url:"URL",emoji:"jaustukas",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO data ir laikas",date:"ISO data",time:"ISO laikas",duration:"ISO trukm\u0117",ipv4:"IPv4 adresas",ipv6:"IPv6 adresas",cidrv4:"IPv4 tinklo prefiksas (CIDR)",cidrv6:"IPv6 tinklo prefiksas (CIDR)",base64:"base64 u\u017Ekoduota eilut\u0117",base64url:"base64url u\u017Ekoduota eilut\u0117",json_string:"JSON eilut\u0117",e164:"E.164 numeris",jwt:"JWT",template_literal:"\u012Fvestis"},n={nan:"NaN",number:"skai\u010Dius",bigint:"sveikasis skai\u010Dius",string:"eilut\u0117",boolean:"login\u0117 reik\u0161m\u0117",undefined:"neapibr\u0117\u017Eta reik\u0161m\u0117",function:"funkcija",symbol:"simbolis",array:"masyvas",object:"objektas",null:"nulin\u0117 reik\u0161m\u0117"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Gautas tipas ${a}, o tik\u0117tasi - instanceof ${o.expected}`:`Gautas tipas ${a}, o tik\u0117tasi - ${i}`}case"invalid_value":return o.values.length===1?`Privalo b\u016Bti ${re(o.values[0])}`:`Privalo b\u016Bti vienas i\u0161 ${q(o.values,"|")} pasirinkim\u0173`;case"too_big":{let i=n[o.origin]??o.origin,s=e(o.origin,FQ(Number(o.maximum)),o.inclusive??!1,"smaller");if(s?.verb)return`${yy(i??o.origin??"reik\u0161m\u0117")} ${s.verb} ${o.maximum.toString()} ${s.unit??"element\u0173"}`;let a=o.inclusive?"ne didesnis kaip":"ma\u017Eesnis kaip";return`${yy(i??o.origin??"reik\u0161m\u0117")} turi b\u016Bti ${a} ${o.maximum.toString()} ${s?.unit}`}case"too_small":{let i=n[o.origin]??o.origin,s=e(o.origin,FQ(Number(o.minimum)),o.inclusive??!1,"bigger");if(s?.verb)return`${yy(i??o.origin??"reik\u0161m\u0117")} ${s.verb} ${o.minimum.toString()} ${s.unit??"element\u0173"}`;let a=o.inclusive?"ne ma\u017Eesnis kaip":"didesnis kaip";return`${yy(i??o.origin??"reik\u0161m\u0117")} turi b\u016Bti ${a} ${o.minimum.toString()} ${s?.unit}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Eilut\u0117 privalo prasid\u0117ti "${i.prefix}"`:i.format==="ends_with"?`Eilut\u0117 privalo pasibaigti "${i.suffix}"`:i.format==="includes"?`Eilut\u0117 privalo \u012Ftraukti "${i.includes}"`:i.format==="regex"?`Eilut\u0117 privalo atitikti ${i.pattern}`:`Neteisingas ${r[i.format]??o.format}`}case"not_multiple_of":return`Skai\u010Dius privalo b\u016Bti ${o.divisor} kartotinis.`;case"unrecognized_keys":return`Neatpa\u017Eint${o.keys.length>1?"i":"as"} rakt${o.keys.length>1?"ai":"as"}: ${q(o.keys,", ")}`;case"invalid_key":return"Rastas klaidingas raktas";case"invalid_union":return"Klaidinga \u012Fvestis";case"invalid_element":{let i=n[o.origin]??o.origin;return`${yy(i??o.origin??"reik\u0161m\u0117")} turi klaiding\u0105 \u012Fvest\u012F`}default:return"Klaidinga \u012Fvestis"}}}});function GQ(){return{localeError:kZe()}}var kZe,VQ=O(()=>{je();kZe=()=>{let t={string:{unit:"\u0437\u043D\u0430\u0446\u0438",verb:"\u0434\u0430 \u0438\u043C\u0430\u0430\u0442"},file:{unit:"\u0431\u0430\u0458\u0442\u0438",verb:"\u0434\u0430 \u0438\u043C\u0430\u0430\u0442"},array:{unit:"\u0441\u0442\u0430\u0432\u043A\u0438",verb:"\u0434\u0430 \u0438\u043C\u0430\u0430\u0442"},set:{unit:"\u0441\u0442\u0430\u0432\u043A\u0438",verb:"\u0434\u0430 \u0438\u043C\u0430\u0430\u0442"}};function e(o){return t[o]??null}let r={regex:"\u0432\u043D\u0435\u0441",email:"\u0430\u0434\u0440\u0435\u0441\u0430 \u043D\u0430 \u0435-\u043F\u043E\u0448\u0442\u0430",url:"URL",emoji:"\u0435\u043C\u043E\u045F\u0438",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \u0434\u0430\u0442\u0443\u043C \u0438 \u0432\u0440\u0435\u043C\u0435",date:"ISO \u0434\u0430\u0442\u0443\u043C",time:"ISO \u0432\u0440\u0435\u043C\u0435",duration:"ISO \u0432\u0440\u0435\u043C\u0435\u0442\u0440\u0430\u0435\u045A\u0435",ipv4:"IPv4 \u0430\u0434\u0440\u0435\u0441\u0430",ipv6:"IPv6 \u0430\u0434\u0440\u0435\u0441\u0430",cidrv4:"IPv4 \u043E\u043F\u0441\u0435\u0433",cidrv6:"IPv6 \u043E\u043F\u0441\u0435\u0433",base64:"base64-\u0435\u043D\u043A\u043E\u0434\u0438\u0440\u0430\u043D\u0430 \u043D\u0438\u0437\u0430",base64url:"base64url-\u0435\u043D\u043A\u043E\u0434\u0438\u0440\u0430\u043D\u0430 \u043D\u0438\u0437\u0430",json_string:"JSON \u043D\u0438\u0437\u0430",e164:"E.164 \u0431\u0440\u043E\u0458",jwt:"JWT",template_literal:"\u0432\u043D\u0435\u0441"},n={nan:"NaN",number:"\u0431\u0440\u043E\u0458",array:"\u043D\u0438\u0437\u0430"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u0413\u0440\u0435\u0448\u0435\u043D \u0432\u043D\u0435\u0441: \u0441\u0435 \u043E\u0447\u0435\u043A\u0443\u0432\u0430 instanceof ${o.expected}, \u043F\u0440\u0438\u043C\u0435\u043D\u043E ${a}`:`\u0413\u0440\u0435\u0448\u0435\u043D \u0432\u043D\u0435\u0441: \u0441\u0435 \u043E\u0447\u0435\u043A\u0443\u0432\u0430 ${i}, \u043F\u0440\u0438\u043C\u0435\u043D\u043E ${a}`}case"invalid_value":return o.values.length===1?`Invalid input: expected ${re(o.values[0])}`:`\u0413\u0440\u0435\u0448\u0430\u043D\u0430 \u043E\u043F\u0446\u0438\u0458\u0430: \u0441\u0435 \u043E\u0447\u0435\u043A\u0443\u0432\u0430 \u0435\u0434\u043D\u0430 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u041F\u0440\u0435\u043C\u043D\u043E\u0433\u0443 \u0433\u043E\u043B\u0435\u043C: \u0441\u0435 \u043E\u0447\u0435\u043A\u0443\u0432\u0430 ${o.origin??"\u0432\u0440\u0435\u0434\u043D\u043E\u0441\u0442\u0430"} \u0434\u0430 \u0438\u043C\u0430 ${i}${o.maximum.toString()} ${s.unit??"\u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0438"}`:`\u041F\u0440\u0435\u043C\u043D\u043E\u0433\u0443 \u0433\u043E\u043B\u0435\u043C: \u0441\u0435 \u043E\u0447\u0435\u043A\u0443\u0432\u0430 ${o.origin??"\u0432\u0440\u0435\u0434\u043D\u043E\u0441\u0442\u0430"} \u0434\u0430 \u0431\u0438\u0434\u0435 ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u041F\u0440\u0435\u043C\u043D\u043E\u0433\u0443 \u043C\u0430\u043B: \u0441\u0435 \u043E\u0447\u0435\u043A\u0443\u0432\u0430 ${o.origin} \u0434\u0430 \u0438\u043C\u0430 ${i}${o.minimum.toString()} ${s.unit}`:`\u041F\u0440\u0435\u043C\u043D\u043E\u0433\u0443 \u043C\u0430\u043B: \u0441\u0435 \u043E\u0447\u0435\u043A\u0443\u0432\u0430 ${o.origin} \u0434\u0430 \u0431\u0438\u0434\u0435 ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u041D\u0435\u0432\u0430\u0436\u0435\u0447\u043A\u0430 \u043D\u0438\u0437\u0430: \u043C\u043E\u0440\u0430 \u0434\u0430 \u0437\u0430\u043F\u043E\u0447\u043D\u0443\u0432\u0430 \u0441\u043E "${i.prefix}"`:i.format==="ends_with"?`\u041D\u0435\u0432\u0430\u0436\u0435\u0447\u043A\u0430 \u043D\u0438\u0437\u0430: \u043C\u043E\u0440\u0430 \u0434\u0430 \u0437\u0430\u0432\u0440\u0448\u0443\u0432\u0430 \u0441\u043E "${i.suffix}"`:i.format==="includes"?`\u041D\u0435\u0432\u0430\u0436\u0435\u0447\u043A\u0430 \u043D\u0438\u0437\u0430: \u043C\u043E\u0440\u0430 \u0434\u0430 \u0432\u043A\u043B\u0443\u0447\u0443\u0432\u0430 "${i.includes}"`:i.format==="regex"?`\u041D\u0435\u0432\u0430\u0436\u0435\u0447\u043A\u0430 \u043D\u0438\u0437\u0430: \u043C\u043E\u0440\u0430 \u0434\u0430 \u043E\u0434\u0433\u043E\u0430\u0440\u0430 \u043D\u0430 \u043F\u0430\u0442\u0435\u0440\u043D\u043E\u0442 ${i.pattern}`:`Invalid ${r[i.format]??o.format}`}case"not_multiple_of":return`\u0413\u0440\u0435\u0448\u0435\u043D \u0431\u0440\u043E\u0458: \u043C\u043E\u0440\u0430 \u0434\u0430 \u0431\u0438\u0434\u0435 \u0434\u0435\u043B\u0438\u0432 \u0441\u043E ${o.divisor}`;case"unrecognized_keys":return`${o.keys.length>1?"\u041D\u0435\u043F\u0440\u0435\u043F\u043E\u0437\u043D\u0430\u0435\u043D\u0438 \u043A\u043B\u0443\u0447\u0435\u0432\u0438":"\u041D\u0435\u043F\u0440\u0435\u043F\u043E\u0437\u043D\u0430\u0435\u043D \u043A\u043B\u0443\u0447"}: ${q(o.keys,", ")}`;case"invalid_key":return`\u0413\u0440\u0435\u0448\u0435\u043D \u043A\u043B\u0443\u0447 \u0432\u043E ${o.origin}`;case"invalid_union":return"\u0413\u0440\u0435\u0448\u0435\u043D \u0432\u043D\u0435\u0441";case"invalid_element":return`\u0413\u0440\u0435\u0448\u043D\u0430 \u0432\u0440\u0435\u0434\u043D\u043E\u0441\u0442 \u0432\u043E ${o.origin}`;default:return"\u0413\u0440\u0435\u0448\u0435\u043D \u0432\u043D\u0435\u0441"}}}});function HQ(){return{localeError:MZe()}}var MZe,WQ=O(()=>{je();MZe=()=>{let t={string:{unit:"aksara",verb:"mempunyai"},file:{unit:"bait",verb:"mempunyai"},array:{unit:"elemen",verb:"mempunyai"},set:{unit:"elemen",verb:"mempunyai"}};function e(o){return t[o]??null}let r={regex:"input",email:"alamat e-mel",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"tarikh masa ISO",date:"tarikh ISO",time:"masa ISO",duration:"tempoh ISO",ipv4:"alamat IPv4",ipv6:"alamat IPv6",cidrv4:"julat IPv4",cidrv6:"julat IPv6",base64:"string dikodkan base64",base64url:"string dikodkan base64url",json_string:"string JSON",e164:"nombor E.164",jwt:"JWT",template_literal:"input"},n={nan:"NaN",number:"nombor"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Input tidak sah: dijangka instanceof ${o.expected}, diterima ${a}`:`Input tidak sah: dijangka ${i}, diterima ${a}`}case"invalid_value":return o.values.length===1?`Input tidak sah: dijangka ${re(o.values[0])}`:`Pilihan tidak sah: dijangka salah satu daripada ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Terlalu besar: dijangka ${o.origin??"nilai"} ${s.verb} ${i}${o.maximum.toString()} ${s.unit??"elemen"}`:`Terlalu besar: dijangka ${o.origin??"nilai"} adalah ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Terlalu kecil: dijangka ${o.origin} ${s.verb} ${i}${o.minimum.toString()} ${s.unit}`:`Terlalu kecil: dijangka ${o.origin} adalah ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`String tidak sah: mesti bermula dengan "${i.prefix}"`:i.format==="ends_with"?`String tidak sah: mesti berakhir dengan "${i.suffix}"`:i.format==="includes"?`String tidak sah: mesti mengandungi "${i.includes}"`:i.format==="regex"?`String tidak sah: mesti sepadan dengan corak ${i.pattern}`:`${r[i.format]??o.format} tidak sah`}case"not_multiple_of":return`Nombor tidak sah: perlu gandaan ${o.divisor}`;case"unrecognized_keys":return`Kunci tidak dikenali: ${q(o.keys,", ")}`;case"invalid_key":return`Kunci tidak sah dalam ${o.origin}`;case"invalid_union":return"Input tidak sah";case"invalid_element":return`Nilai tidak sah dalam ${o.origin}`;default:return"Input tidak sah"}}}});function KQ(){return{localeError:DZe()}}var DZe,ZQ=O(()=>{je();DZe=()=>{let t={string:{unit:"tekens",verb:"heeft"},file:{unit:"bytes",verb:"heeft"},array:{unit:"elementen",verb:"heeft"},set:{unit:"elementen",verb:"heeft"}};function e(o){return t[o]??null}let r={regex:"invoer",email:"emailadres",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO datum en tijd",date:"ISO datum",time:"ISO tijd",duration:"ISO duur",ipv4:"IPv4-adres",ipv6:"IPv6-adres",cidrv4:"IPv4-bereik",cidrv6:"IPv6-bereik",base64:"base64-gecodeerde tekst",base64url:"base64 URL-gecodeerde tekst",json_string:"JSON string",e164:"E.164-nummer",jwt:"JWT",template_literal:"invoer"},n={nan:"NaN",number:"getal"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Ongeldige invoer: verwacht instanceof ${o.expected}, ontving ${a}`:`Ongeldige invoer: verwacht ${i}, ontving ${a}`}case"invalid_value":return o.values.length===1?`Ongeldige invoer: verwacht ${re(o.values[0])}`:`Ongeldige optie: verwacht \xE9\xE9n van ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin),a=o.origin==="date"?"laat":o.origin==="string"?"lang":"groot";return s?`Te ${a}: verwacht dat ${o.origin??"waarde"} ${i}${o.maximum.toString()} ${s.unit??"elementen"} ${s.verb}`:`Te ${a}: verwacht dat ${o.origin??"waarde"} ${i}${o.maximum.toString()} is`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin),a=o.origin==="date"?"vroeg":o.origin==="string"?"kort":"klein";return s?`Te ${a}: verwacht dat ${o.origin} ${i}${o.minimum.toString()} ${s.unit} ${s.verb}`:`Te ${a}: verwacht dat ${o.origin} ${i}${o.minimum.toString()} is`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Ongeldige tekst: moet met "${i.prefix}" beginnen`:i.format==="ends_with"?`Ongeldige tekst: moet op "${i.suffix}" eindigen`:i.format==="includes"?`Ongeldige tekst: moet "${i.includes}" bevatten`:i.format==="regex"?`Ongeldige tekst: moet overeenkomen met patroon ${i.pattern}`:`Ongeldig: ${r[i.format]??o.format}`}case"not_multiple_of":return`Ongeldig getal: moet een veelvoud van ${o.divisor} zijn`;case"unrecognized_keys":return`Onbekende key${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Ongeldige key in ${o.origin}`;case"invalid_union":return"Ongeldige invoer";case"invalid_element":return`Ongeldige waarde in ${o.origin}`;default:return"Ongeldige invoer"}}}});function YQ(){return{localeError:LZe()}}var LZe,JQ=O(()=>{je();LZe=()=>{let t={string:{unit:"tegn",verb:"\xE5 ha"},file:{unit:"bytes",verb:"\xE5 ha"},array:{unit:"elementer",verb:"\xE5 inneholde"},set:{unit:"elementer",verb:"\xE5 inneholde"}};function e(o){return t[o]??null}let r={regex:"input",email:"e-postadresse",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO dato- og klokkeslett",date:"ISO-dato",time:"ISO-klokkeslett",duration:"ISO-varighet",ipv4:"IPv4-omr\xE5de",ipv6:"IPv6-omr\xE5de",cidrv4:"IPv4-spekter",cidrv6:"IPv6-spekter",base64:"base64-enkodet streng",base64url:"base64url-enkodet streng",json_string:"JSON-streng",e164:"E.164-nummer",jwt:"JWT",template_literal:"input"},n={nan:"NaN",number:"tall",array:"liste"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Ugyldig input: forventet instanceof ${o.expected}, fikk ${a}`:`Ugyldig input: forventet ${i}, fikk ${a}`}case"invalid_value":return o.values.length===1?`Ugyldig verdi: forventet ${re(o.values[0])}`:`Ugyldig valg: forventet en av ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`For stor(t): forventet ${o.origin??"value"} til \xE5 ha ${i}${o.maximum.toString()} ${s.unit??"elementer"}`:`For stor(t): forventet ${o.origin??"value"} til \xE5 ha ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`For lite(n): forventet ${o.origin} til \xE5 ha ${i}${o.minimum.toString()} ${s.unit}`:`For lite(n): forventet ${o.origin} til \xE5 ha ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Ugyldig streng: m\xE5 starte med "${i.prefix}"`:i.format==="ends_with"?`Ugyldig streng: m\xE5 ende med "${i.suffix}"`:i.format==="includes"?`Ugyldig streng: m\xE5 inneholde "${i.includes}"`:i.format==="regex"?`Ugyldig streng: m\xE5 matche m\xF8nsteret ${i.pattern}`:`Ugyldig ${r[i.format]??o.format}`}case"not_multiple_of":return`Ugyldig tall: m\xE5 v\xE6re et multiplum av ${o.divisor}`;case"unrecognized_keys":return`${o.keys.length>1?"Ukjente n\xF8kler":"Ukjent n\xF8kkel"}: ${q(o.keys,", ")}`;case"invalid_key":return`Ugyldig n\xF8kkel i ${o.origin}`;case"invalid_union":return"Ugyldig input";case"invalid_element":return`Ugyldig verdi i ${o.origin}`;default:return"Ugyldig input"}}}});function XQ(){return{localeError:UZe()}}var UZe,QQ=O(()=>{je();UZe=()=>{let t={string:{unit:"harf",verb:"olmal\u0131d\u0131r"},file:{unit:"bayt",verb:"olmal\u0131d\u0131r"},array:{unit:"unsur",verb:"olmal\u0131d\u0131r"},set:{unit:"unsur",verb:"olmal\u0131d\u0131r"}};function e(o){return t[o]??null}let r={regex:"giren",email:"epostag\xE2h",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO heng\xE2m\u0131",date:"ISO tarihi",time:"ISO zaman\u0131",duration:"ISO m\xFCddeti",ipv4:"IPv4 ni\u015F\xE2n\u0131",ipv6:"IPv6 ni\u015F\xE2n\u0131",cidrv4:"IPv4 menzili",cidrv6:"IPv6 menzili",base64:"base64-\u015Fifreli metin",base64url:"base64url-\u015Fifreli metin",json_string:"JSON metin",e164:"E.164 say\u0131s\u0131",jwt:"JWT",template_literal:"giren"},n={nan:"NaN",number:"numara",array:"saf",null:"gayb"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`F\xE2sit giren: umulan instanceof ${o.expected}, al\u0131nan ${a}`:`F\xE2sit giren: umulan ${i}, al\u0131nan ${a}`}case"invalid_value":return o.values.length===1?`F\xE2sit giren: umulan ${re(o.values[0])}`:`F\xE2sit tercih: m\xFBteberler ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Fazla b\xFCy\xFCk: ${o.origin??"value"}, ${i}${o.maximum.toString()} ${s.unit??"elements"} sahip olmal\u0131yd\u0131.`:`Fazla b\xFCy\xFCk: ${o.origin??"value"}, ${i}${o.maximum.toString()} olmal\u0131yd\u0131.`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Fazla k\xFC\xE7\xFCk: ${o.origin}, ${i}${o.minimum.toString()} ${s.unit} sahip olmal\u0131yd\u0131.`:`Fazla k\xFC\xE7\xFCk: ${o.origin}, ${i}${o.minimum.toString()} olmal\u0131yd\u0131.`}case"invalid_format":{let i=o;return i.format==="starts_with"?`F\xE2sit metin: "${i.prefix}" ile ba\u015Flamal\u0131.`:i.format==="ends_with"?`F\xE2sit metin: "${i.suffix}" ile bitmeli.`:i.format==="includes"?`F\xE2sit metin: "${i.includes}" ihtiv\xE2 etmeli.`:i.format==="regex"?`F\xE2sit metin: ${i.pattern} nak\u015F\u0131na uymal\u0131.`:`F\xE2sit ${r[i.format]??o.format}`}case"not_multiple_of":return`F\xE2sit say\u0131: ${o.divisor} kat\u0131 olmal\u0131yd\u0131.`;case"unrecognized_keys":return`Tan\u0131nmayan anahtar ${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`${o.origin} i\xE7in tan\u0131nmayan anahtar var.`;case"invalid_union":return"Giren tan\u0131namad\u0131.";case"invalid_element":return`${o.origin} i\xE7in tan\u0131nmayan k\u0131ymet var.`;default:return"K\u0131ymet tan\u0131namad\u0131."}}}});function eee(){return{localeError:jZe()}}var jZe,tee=O(()=>{je();jZe=()=>{let t={string:{unit:"\u062A\u0648\u06A9\u064A",verb:"\u0648\u0644\u0631\u064A"},file:{unit:"\u0628\u0627\u06CC\u067C\u0633",verb:"\u0648\u0644\u0631\u064A"},array:{unit:"\u062A\u0648\u06A9\u064A",verb:"\u0648\u0644\u0631\u064A"},set:{unit:"\u062A\u0648\u06A9\u064A",verb:"\u0648\u0644\u0631\u064A"}};function e(o){return t[o]??null}let r={regex:"\u0648\u0631\u0648\u062F\u064A",email:"\u0628\u0631\u06CC\u069A\u0646\u0627\u0644\u06CC\u06A9",url:"\u06CC\u0648 \u0622\u0631 \u0627\u0644",emoji:"\u0627\u06CC\u0645\u0648\u062C\u064A",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\u0646\u06CC\u067C\u0647 \u0627\u0648 \u0648\u062E\u062A",date:"\u0646\u06D0\u067C\u0647",time:"\u0648\u062E\u062A",duration:"\u0645\u0648\u062F\u0647",ipv4:"\u062F IPv4 \u067E\u062A\u0647",ipv6:"\u062F IPv6 \u067E\u062A\u0647",cidrv4:"\u062F IPv4 \u0633\u0627\u062D\u0647",cidrv6:"\u062F IPv6 \u0633\u0627\u062D\u0647",base64:"base64-encoded \u0645\u062A\u0646",base64url:"base64url-encoded \u0645\u062A\u0646",json_string:"JSON \u0645\u062A\u0646",e164:"\u062F E.164 \u0634\u0645\u06D0\u0631\u0647",jwt:"JWT",template_literal:"\u0648\u0631\u0648\u062F\u064A"},n={nan:"NaN",number:"\u0639\u062F\u062F",array:"\u0627\u0631\u06D0"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u0646\u0627\u0633\u0645 \u0648\u0631\u0648\u062F\u064A: \u0628\u0627\u06CC\u062F instanceof ${o.expected} \u0648\u0627\u06CC, \u0645\u06AB\u0631 ${a} \u062A\u0631\u0644\u0627\u0633\u0647 \u0634\u0648`:`\u0646\u0627\u0633\u0645 \u0648\u0631\u0648\u062F\u064A: \u0628\u0627\u06CC\u062F ${i} \u0648\u0627\u06CC, \u0645\u06AB\u0631 ${a} \u062A\u0631\u0644\u0627\u0633\u0647 \u0634\u0648`}case"invalid_value":return o.values.length===1?`\u0646\u0627\u0633\u0645 \u0648\u0631\u0648\u062F\u064A: \u0628\u0627\u06CC\u062F ${re(o.values[0])} \u0648\u0627\u06CC`:`\u0646\u0627\u0633\u0645 \u0627\u0646\u062A\u062E\u0627\u0628: \u0628\u0627\u06CC\u062F \u06CC\u0648 \u0644\u0647 ${q(o.values,"|")} \u0685\u062E\u0647 \u0648\u0627\u06CC`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u0689\u06CC\u0631 \u0644\u0648\u06CC: ${o.origin??"\u0627\u0631\u0632\u069A\u062A"} \u0628\u0627\u06CC\u062F ${i}${o.maximum.toString()} ${s.unit??"\u0639\u0646\u0635\u0631\u0648\u0646\u0647"} \u0648\u0644\u0631\u064A`:`\u0689\u06CC\u0631 \u0644\u0648\u06CC: ${o.origin??"\u0627\u0631\u0632\u069A\u062A"} \u0628\u0627\u06CC\u062F ${i}${o.maximum.toString()} \u0648\u064A`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u0689\u06CC\u0631 \u06A9\u0648\u0686\u0646\u06CC: ${o.origin} \u0628\u0627\u06CC\u062F ${i}${o.minimum.toString()} ${s.unit} \u0648\u0644\u0631\u064A`:`\u0689\u06CC\u0631 \u06A9\u0648\u0686\u0646\u06CC: ${o.origin} \u0628\u0627\u06CC\u062F ${i}${o.minimum.toString()} \u0648\u064A`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u0646\u0627\u0633\u0645 \u0645\u062A\u0646: \u0628\u0627\u06CC\u062F \u062F "${i.prefix}" \u0633\u0631\u0647 \u067E\u06CC\u0644 \u0634\u064A`:i.format==="ends_with"?`\u0646\u0627\u0633\u0645 \u0645\u062A\u0646: \u0628\u0627\u06CC\u062F \u062F "${i.suffix}" \u0633\u0631\u0647 \u067E\u0627\u06CC \u062A\u0647 \u0648\u0631\u0633\u064A\u0696\u064A`:i.format==="includes"?`\u0646\u0627\u0633\u0645 \u0645\u062A\u0646: \u0628\u0627\u06CC\u062F "${i.includes}" \u0648\u0644\u0631\u064A`:i.format==="regex"?`\u0646\u0627\u0633\u0645 \u0645\u062A\u0646: \u0628\u0627\u06CC\u062F \u062F ${i.pattern} \u0633\u0631\u0647 \u0645\u0637\u0627\u0628\u0642\u062A \u0648\u0644\u0631\u064A`:`${r[i.format]??o.format} \u0646\u0627\u0633\u0645 \u062F\u06CC`}case"not_multiple_of":return`\u0646\u0627\u0633\u0645 \u0639\u062F\u062F: \u0628\u0627\u06CC\u062F \u062F ${o.divisor} \u0645\u0636\u0631\u0628 \u0648\u064A`;case"unrecognized_keys":return`\u0646\u0627\u0633\u0645 ${o.keys.length>1?"\u06A9\u0644\u06CC\u0689\u0648\u0646\u0647":"\u06A9\u0644\u06CC\u0689"}: ${q(o.keys,", ")}`;case"invalid_key":return`\u0646\u0627\u0633\u0645 \u06A9\u0644\u06CC\u0689 \u067E\u0647 ${o.origin} \u06A9\u06D0`;case"invalid_union":return"\u0646\u0627\u0633\u0645\u0647 \u0648\u0631\u0648\u062F\u064A";case"invalid_element":return`\u0646\u0627\u0633\u0645 \u0639\u0646\u0635\u0631 \u067E\u0647 ${o.origin} \u06A9\u06D0`;default:return"\u0646\u0627\u0633\u0645\u0647 \u0648\u0631\u0648\u062F\u064A"}}}});function ree(){return{localeError:zZe()}}var zZe,nee=O(()=>{je();zZe=()=>{let t={string:{unit:"znak\xF3w",verb:"mie\u0107"},file:{unit:"bajt\xF3w",verb:"mie\u0107"},array:{unit:"element\xF3w",verb:"mie\u0107"},set:{unit:"element\xF3w",verb:"mie\u0107"}};function e(o){return t[o]??null}let r={regex:"wyra\u017Cenie",email:"adres email",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"data i godzina w formacie ISO",date:"data w formacie ISO",time:"godzina w formacie ISO",duration:"czas trwania ISO",ipv4:"adres IPv4",ipv6:"adres IPv6",cidrv4:"zakres IPv4",cidrv6:"zakres IPv6",base64:"ci\u0105g znak\xF3w zakodowany w formacie base64",base64url:"ci\u0105g znak\xF3w zakodowany w formacie base64url",json_string:"ci\u0105g znak\xF3w w formacie JSON",e164:"liczba E.164",jwt:"JWT",template_literal:"wej\u015Bcie"},n={nan:"NaN",number:"liczba",array:"tablica"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Nieprawid\u0142owe dane wej\u015Bciowe: oczekiwano instanceof ${o.expected}, otrzymano ${a}`:`Nieprawid\u0142owe dane wej\u015Bciowe: oczekiwano ${i}, otrzymano ${a}`}case"invalid_value":return o.values.length===1?`Nieprawid\u0142owe dane wej\u015Bciowe: oczekiwano ${re(o.values[0])}`:`Nieprawid\u0142owa opcja: oczekiwano jednej z warto\u015Bci ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Za du\u017Ca warto\u015B\u0107: oczekiwano, \u017Ce ${o.origin??"warto\u015B\u0107"} b\u0119dzie mie\u0107 ${i}${o.maximum.toString()} ${s.unit??"element\xF3w"}`:`Zbyt du\u017C(y/a/e): oczekiwano, \u017Ce ${o.origin??"warto\u015B\u0107"} b\u0119dzie wynosi\u0107 ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Za ma\u0142a warto\u015B\u0107: oczekiwano, \u017Ce ${o.origin??"warto\u015B\u0107"} b\u0119dzie mie\u0107 ${i}${o.minimum.toString()} ${s.unit??"element\xF3w"}`:`Zbyt ma\u0142(y/a/e): oczekiwano, \u017Ce ${o.origin??"warto\u015B\u0107"} b\u0119dzie wynosi\u0107 ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Nieprawid\u0142owy ci\u0105g znak\xF3w: musi zaczyna\u0107 si\u0119 od "${i.prefix}"`:i.format==="ends_with"?`Nieprawid\u0142owy ci\u0105g znak\xF3w: musi ko\u0144czy\u0107 si\u0119 na "${i.suffix}"`:i.format==="includes"?`Nieprawid\u0142owy ci\u0105g znak\xF3w: musi zawiera\u0107 "${i.includes}"`:i.format==="regex"?`Nieprawid\u0142owy ci\u0105g znak\xF3w: musi odpowiada\u0107 wzorcowi ${i.pattern}`:`Nieprawid\u0142ow(y/a/e) ${r[i.format]??o.format}`}case"not_multiple_of":return`Nieprawid\u0142owa liczba: musi by\u0107 wielokrotno\u015Bci\u0105 ${o.divisor}`;case"unrecognized_keys":return`Nierozpoznane klucze${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Nieprawid\u0142owy klucz w ${o.origin}`;case"invalid_union":return"Nieprawid\u0142owe dane wej\u015Bciowe";case"invalid_element":return`Nieprawid\u0142owa warto\u015B\u0107 w ${o.origin}`;default:return"Nieprawid\u0142owe dane wej\u015Bciowe"}}}});function oee(){return{localeError:FZe()}}var FZe,iee=O(()=>{je();FZe=()=>{let t={string:{unit:"caracteres",verb:"ter"},file:{unit:"bytes",verb:"ter"},array:{unit:"itens",verb:"ter"},set:{unit:"itens",verb:"ter"}};function e(o){return t[o]??null}let r={regex:"padr\xE3o",email:"endere\xE7o de e-mail",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"data e hora ISO",date:"data ISO",time:"hora ISO",duration:"dura\xE7\xE3o ISO",ipv4:"endere\xE7o IPv4",ipv6:"endere\xE7o IPv6",cidrv4:"faixa de IPv4",cidrv6:"faixa de IPv6",base64:"texto codificado em base64",base64url:"URL codificada em base64",json_string:"texto JSON",e164:"n\xFAmero E.164",jwt:"JWT",template_literal:"entrada"},n={nan:"NaN",number:"n\xFAmero",null:"nulo"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Tipo inv\xE1lido: esperado instanceof ${o.expected}, recebido ${a}`:`Tipo inv\xE1lido: esperado ${i}, recebido ${a}`}case"invalid_value":return o.values.length===1?`Entrada inv\xE1lida: esperado ${re(o.values[0])}`:`Op\xE7\xE3o inv\xE1lida: esperada uma das ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Muito grande: esperado que ${o.origin??"valor"} tivesse ${i}${o.maximum.toString()} ${s.unit??"elementos"}`:`Muito grande: esperado que ${o.origin??"valor"} fosse ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Muito pequeno: esperado que ${o.origin} tivesse ${i}${o.minimum.toString()} ${s.unit}`:`Muito pequeno: esperado que ${o.origin} fosse ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Texto inv\xE1lido: deve come\xE7ar com "${i.prefix}"`:i.format==="ends_with"?`Texto inv\xE1lido: deve terminar com "${i.suffix}"`:i.format==="includes"?`Texto inv\xE1lido: deve incluir "${i.includes}"`:i.format==="regex"?`Texto inv\xE1lido: deve corresponder ao padr\xE3o ${i.pattern}`:`${r[i.format]??o.format} inv\xE1lido`}case"not_multiple_of":return`N\xFAmero inv\xE1lido: deve ser m\xFAltiplo de ${o.divisor}`;case"unrecognized_keys":return`Chave${o.keys.length>1?"s":""} desconhecida${o.keys.length>1?"s":""}: ${q(o.keys,", ")}`;case"invalid_key":return`Chave inv\xE1lida em ${o.origin}`;case"invalid_union":return"Entrada inv\xE1lida";case"invalid_element":return`Valor inv\xE1lido em ${o.origin}`;default:return"Campo inv\xE1lido"}}}});function see(t,e,r,n){let o=Math.abs(t),i=o%10,s=o%100;return s>=11&&s<=19?n:i===1?e:i>=2&&i<=4?r:n}function aee(){return{localeError:qZe()}}var qZe,cee=O(()=>{je();qZe=()=>{let t={string:{unit:{one:"\u0441\u0438\u043C\u0432\u043E\u043B",few:"\u0441\u0438\u043C\u0432\u043E\u043B\u0430",many:"\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432"},verb:"\u0438\u043C\u0435\u0442\u044C"},file:{unit:{one:"\u0431\u0430\u0439\u0442",few:"\u0431\u0430\u0439\u0442\u0430",many:"\u0431\u0430\u0439\u0442"},verb:"\u0438\u043C\u0435\u0442\u044C"},array:{unit:{one:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442",few:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430",many:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432"},verb:"\u0438\u043C\u0435\u0442\u044C"},set:{unit:{one:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442",few:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430",many:"\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432"},verb:"\u0438\u043C\u0435\u0442\u044C"}};function e(o){return t[o]??null}let r={regex:"\u0432\u0432\u043E\u0434",email:"email \u0430\u0434\u0440\u0435\u0441",url:"URL",emoji:"\u044D\u043C\u043E\u0434\u0437\u0438",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043C\u044F",date:"ISO \u0434\u0430\u0442\u0430",time:"ISO \u0432\u0440\u0435\u043C\u044F",duration:"ISO \u0434\u043B\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C",ipv4:"IPv4 \u0430\u0434\u0440\u0435\u0441",ipv6:"IPv6 \u0430\u0434\u0440\u0435\u0441",cidrv4:"IPv4 \u0434\u0438\u0430\u043F\u0430\u0437\u043E\u043D",cidrv6:"IPv6 \u0434\u0438\u0430\u043F\u0430\u0437\u043E\u043D",base64:"\u0441\u0442\u0440\u043E\u043A\u0430 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 base64",base64url:"\u0441\u0442\u0440\u043E\u043A\u0430 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 base64url",json_string:"JSON \u0441\u0442\u0440\u043E\u043A\u0430",e164:"\u043D\u043E\u043C\u0435\u0440 E.164",jwt:"JWT",template_literal:"\u0432\u0432\u043E\u0434"},n={nan:"NaN",number:"\u0447\u0438\u0441\u043B\u043E",array:"\u043C\u0430\u0441\u0441\u0438\u0432"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0432\u0432\u043E\u0434: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C instanceof ${o.expected}, \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043E ${a}`:`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0432\u0432\u043E\u0434: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C ${i}, \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043E ${a}`}case"invalid_value":return o.values.length===1?`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0432\u0432\u043E\u0434: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C ${re(o.values[0])}`:`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0434\u043D\u043E \u0438\u0437 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);if(s){let a=Number(o.maximum),c=see(a,s.unit.one,s.unit.few,s.unit.many);return`\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${o.origin??"\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${i}${o.maximum.toString()} ${c}`}return`\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${o.origin??"\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);if(s){let a=Number(o.minimum),c=see(a,s.unit.one,s.unit.few,s.unit.many);return`\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${o.origin} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${i}${o.minimum.toString()} ${c}`}return`\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${o.origin} \u0431\u0443\u0434\u0435\u0442 ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u041D\u0435\u0432\u0435\u0440\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430: \u0434\u043E\u043B\u0436\u043D\u0430 \u043D\u0430\u0447\u0438\u043D\u0430\u0442\u044C\u0441\u044F \u0441 "${i.prefix}"`:i.format==="ends_with"?`\u041D\u0435\u0432\u0435\u0440\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430: \u0434\u043E\u043B\u0436\u043D\u0430 \u0437\u0430\u043A\u0430\u043D\u0447\u0438\u0432\u0430\u0442\u044C\u0441\u044F \u043D\u0430 "${i.suffix}"`:i.format==="includes"?`\u041D\u0435\u0432\u0435\u0440\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430: \u0434\u043E\u043B\u0436\u043D\u0430 \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C "${i.includes}"`:i.format==="regex"?`\u041D\u0435\u0432\u0435\u0440\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430: \u0434\u043E\u043B\u0436\u043D\u0430 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C \u0448\u0430\u0431\u043B\u043E\u043D\u0443 ${i.pattern}`:`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 ${r[i.format]??o.format}`}case"not_multiple_of":return`\u041D\u0435\u0432\u0435\u0440\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E: \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043A\u0440\u0430\u0442\u043D\u044B\u043C ${o.divisor}`;case"unrecognized_keys":return`\u041D\u0435\u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u043D\u043D${o.keys.length>1?"\u044B\u0435":"\u044B\u0439"} \u043A\u043B\u044E\u0447${o.keys.length>1?"\u0438":""}: ${q(o.keys,", ")}`;case"invalid_key":return`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043A\u043B\u044E\u0447 \u0432 ${o.origin}`;case"invalid_union":return"\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0435 \u0432\u0445\u043E\u0434\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435";case"invalid_element":return`\u041D\u0435\u0432\u0435\u0440\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0432 ${o.origin}`;default:return"\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0435 \u0432\u0445\u043E\u0434\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435"}}}});function uee(){return{localeError:BZe()}}var BZe,lee=O(()=>{je();BZe=()=>{let t={string:{unit:"znakov",verb:"imeti"},file:{unit:"bajtov",verb:"imeti"},array:{unit:"elementov",verb:"imeti"},set:{unit:"elementov",verb:"imeti"}};function e(o){return t[o]??null}let r={regex:"vnos",email:"e-po\u0161tni naslov",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO datum in \u010Das",date:"ISO datum",time:"ISO \u010Das",duration:"ISO trajanje",ipv4:"IPv4 naslov",ipv6:"IPv6 naslov",cidrv4:"obseg IPv4",cidrv6:"obseg IPv6",base64:"base64 kodiran niz",base64url:"base64url kodiran niz",json_string:"JSON niz",e164:"E.164 \u0161tevilka",jwt:"JWT",template_literal:"vnos"},n={nan:"NaN",number:"\u0161tevilo",array:"tabela"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Neveljaven vnos: pri\u010Dakovano instanceof ${o.expected}, prejeto ${a}`:`Neveljaven vnos: pri\u010Dakovano ${i}, prejeto ${a}`}case"invalid_value":return o.values.length===1?`Neveljaven vnos: pri\u010Dakovano ${re(o.values[0])}`:`Neveljavna mo\u017Enost: pri\u010Dakovano eno izmed ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Preveliko: pri\u010Dakovano, da bo ${o.origin??"vrednost"} imelo ${i}${o.maximum.toString()} ${s.unit??"elementov"}`:`Preveliko: pri\u010Dakovano, da bo ${o.origin??"vrednost"} ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Premajhno: pri\u010Dakovano, da bo ${o.origin} imelo ${i}${o.minimum.toString()} ${s.unit}`:`Premajhno: pri\u010Dakovano, da bo ${o.origin} ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Neveljaven niz: mora se za\u010Deti z "${i.prefix}"`:i.format==="ends_with"?`Neveljaven niz: mora se kon\u010Dati z "${i.suffix}"`:i.format==="includes"?`Neveljaven niz: mora vsebovati "${i.includes}"`:i.format==="regex"?`Neveljaven niz: mora ustrezati vzorcu ${i.pattern}`:`Neveljaven ${r[i.format]??o.format}`}case"not_multiple_of":return`Neveljavno \u0161tevilo: mora biti ve\u010Dkratnik ${o.divisor}`;case"unrecognized_keys":return`Neprepoznan${o.keys.length>1?"i klju\u010Di":" klju\u010D"}: ${q(o.keys,", ")}`;case"invalid_key":return`Neveljaven klju\u010D v ${o.origin}`;case"invalid_union":return"Neveljaven vnos";case"invalid_element":return`Neveljavna vrednost v ${o.origin}`;default:return"Neveljaven vnos"}}}});function pee(){return{localeError:GZe()}}var GZe,dee=O(()=>{je();GZe=()=>{let t={string:{unit:"tecken",verb:"att ha"},file:{unit:"bytes",verb:"att ha"},array:{unit:"objekt",verb:"att inneh\xE5lla"},set:{unit:"objekt",verb:"att inneh\xE5lla"}};function e(o){return t[o]??null}let r={regex:"regulj\xE4rt uttryck",email:"e-postadress",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO-datum och tid",date:"ISO-datum",time:"ISO-tid",duration:"ISO-varaktighet",ipv4:"IPv4-intervall",ipv6:"IPv6-intervall",cidrv4:"IPv4-spektrum",cidrv6:"IPv6-spektrum",base64:"base64-kodad str\xE4ng",base64url:"base64url-kodad str\xE4ng",json_string:"JSON-str\xE4ng",e164:"E.164-nummer",jwt:"JWT",template_literal:"mall-literal"},n={nan:"NaN",number:"antal",array:"lista"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Ogiltig inmatning: f\xF6rv\xE4ntat instanceof ${o.expected}, fick ${a}`:`Ogiltig inmatning: f\xF6rv\xE4ntat ${i}, fick ${a}`}case"invalid_value":return o.values.length===1?`Ogiltig inmatning: f\xF6rv\xE4ntat ${re(o.values[0])}`:`Ogiltigt val: f\xF6rv\xE4ntade en av ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`F\xF6r stor(t): f\xF6rv\xE4ntade ${o.origin??"v\xE4rdet"} att ha ${i}${o.maximum.toString()} ${s.unit??"element"}`:`F\xF6r stor(t): f\xF6rv\xE4ntat ${o.origin??"v\xE4rdet"} att ha ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`F\xF6r lite(t): f\xF6rv\xE4ntade ${o.origin??"v\xE4rdet"} att ha ${i}${o.minimum.toString()} ${s.unit}`:`F\xF6r lite(t): f\xF6rv\xE4ntade ${o.origin??"v\xE4rdet"} att ha ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Ogiltig str\xE4ng: m\xE5ste b\xF6rja med "${i.prefix}"`:i.format==="ends_with"?`Ogiltig str\xE4ng: m\xE5ste sluta med "${i.suffix}"`:i.format==="includes"?`Ogiltig str\xE4ng: m\xE5ste inneh\xE5lla "${i.includes}"`:i.format==="regex"?`Ogiltig str\xE4ng: m\xE5ste matcha m\xF6nstret "${i.pattern}"`:`Ogiltig(t) ${r[i.format]??o.format}`}case"not_multiple_of":return`Ogiltigt tal: m\xE5ste vara en multipel av ${o.divisor}`;case"unrecognized_keys":return`${o.keys.length>1?"Ok\xE4nda nycklar":"Ok\xE4nd nyckel"}: ${q(o.keys,", ")}`;case"invalid_key":return`Ogiltig nyckel i ${o.origin??"v\xE4rdet"}`;case"invalid_union":return"Ogiltig input";case"invalid_element":return`Ogiltigt v\xE4rde i ${o.origin??"v\xE4rdet"}`;default:return"Ogiltig input"}}}});function fee(){return{localeError:VZe()}}var VZe,mee=O(()=>{je();VZe=()=>{let t={string:{unit:"\u0B8E\u0BB4\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1\u0B95\u0BCD\u0B95\u0BB3\u0BCD",verb:"\u0B95\u0BCA\u0BA3\u0BCD\u0B9F\u0BBF\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD"},file:{unit:"\u0BAA\u0BC8\u0B9F\u0BCD\u0B9F\u0BC1\u0B95\u0BB3\u0BCD",verb:"\u0B95\u0BCA\u0BA3\u0BCD\u0B9F\u0BBF\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD"},array:{unit:"\u0B89\u0BB1\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BCD",verb:"\u0B95\u0BCA\u0BA3\u0BCD\u0B9F\u0BBF\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD"},set:{unit:"\u0B89\u0BB1\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BCD",verb:"\u0B95\u0BCA\u0BA3\u0BCD\u0B9F\u0BBF\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD"}};function e(o){return t[o]??null}let r={regex:"\u0B89\u0BB3\u0BCD\u0BB3\u0BC0\u0B9F\u0BC1",email:"\u0BAE\u0BBF\u0BA9\u0BCD\u0BA9\u0B9E\u0BCD\u0B9A\u0BB2\u0BCD \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \u0BA4\u0BC7\u0BA4\u0BBF \u0BA8\u0BC7\u0BB0\u0BAE\u0BCD",date:"ISO \u0BA4\u0BC7\u0BA4\u0BBF",time:"ISO \u0BA8\u0BC7\u0BB0\u0BAE\u0BCD",duration:"ISO \u0B95\u0BBE\u0BB2 \u0B85\u0BB3\u0BB5\u0BC1",ipv4:"IPv4 \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF",ipv6:"IPv6 \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF",cidrv4:"IPv4 \u0BB5\u0BB0\u0BAE\u0BCD\u0BAA\u0BC1",cidrv6:"IPv6 \u0BB5\u0BB0\u0BAE\u0BCD\u0BAA\u0BC1",base64:"base64-encoded \u0B9A\u0BB0\u0BAE\u0BCD",base64url:"base64url-encoded \u0B9A\u0BB0\u0BAE\u0BCD",json_string:"JSON \u0B9A\u0BB0\u0BAE\u0BCD",e164:"E.164 \u0B8E\u0BA3\u0BCD",jwt:"JWT",template_literal:"input"},n={nan:"NaN",number:"\u0B8E\u0BA3\u0BCD",array:"\u0B85\u0BA3\u0BBF",null:"\u0BB5\u0BC6\u0BB1\u0BC1\u0BAE\u0BC8"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B89\u0BB3\u0BCD\u0BB3\u0BC0\u0B9F\u0BC1: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 instanceof ${o.expected}, \u0BAA\u0BC6\u0BB1\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${a}`:`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B89\u0BB3\u0BCD\u0BB3\u0BC0\u0B9F\u0BC1: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${i}, \u0BAA\u0BC6\u0BB1\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${a}`}case"invalid_value":return o.values.length===1?`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B89\u0BB3\u0BCD\u0BB3\u0BC0\u0B9F\u0BC1: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${re(o.values[0])}`:`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0BB5\u0BBF\u0BB0\u0BC1\u0BAA\u0BCD\u0BAA\u0BAE\u0BCD: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${q(o.values,"|")} \u0B87\u0BB2\u0BCD \u0B92\u0BA9\u0BCD\u0BB1\u0BC1`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u0BAE\u0BBF\u0B95 \u0BAA\u0BC6\u0BB0\u0BBF\u0BAF\u0BA4\u0BC1: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${o.origin??"\u0BAE\u0BA4\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1"} ${i}${o.maximum.toString()} ${s.unit??"\u0B89\u0BB1\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BCD"} \u0B86\u0B95 \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`:`\u0BAE\u0BBF\u0B95 \u0BAA\u0BC6\u0BB0\u0BBF\u0BAF\u0BA4\u0BC1: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${o.origin??"\u0BAE\u0BA4\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1"} ${i}${o.maximum.toString()} \u0B86\u0B95 \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u0BAE\u0BBF\u0B95\u0B9A\u0BCD \u0B9A\u0BBF\u0BB1\u0BBF\u0BAF\u0BA4\u0BC1: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${o.origin} ${i}${o.minimum.toString()} ${s.unit} \u0B86\u0B95 \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`:`\u0BAE\u0BBF\u0B95\u0B9A\u0BCD \u0B9A\u0BBF\u0BB1\u0BBF\u0BAF\u0BA4\u0BC1: \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1 ${o.origin} ${i}${o.minimum.toString()} \u0B86\u0B95 \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B9A\u0BB0\u0BAE\u0BCD: "${i.prefix}" \u0B87\u0BB2\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`:i.format==="ends_with"?`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B9A\u0BB0\u0BAE\u0BCD: "${i.suffix}" \u0B87\u0BB2\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0B9F\u0BC8\u0BAF \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`:i.format==="includes"?`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B9A\u0BB0\u0BAE\u0BCD: "${i.includes}" \u0B90 \u0B89\u0BB3\u0BCD\u0BB3\u0B9F\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`:i.format==="regex"?`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B9A\u0BB0\u0BAE\u0BCD: ${i.pattern} \u0BAE\u0BC1\u0BB1\u0BC8\u0BAA\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1\u0B9F\u0BA9\u0BCD \u0BAA\u0BCA\u0BB0\u0BC1\u0BA8\u0BCD\u0BA4 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`:`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 ${r[i.format]??o.format}`}case"not_multiple_of":return`\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B8E\u0BA3\u0BCD: ${o.divisor} \u0B87\u0BA9\u0BCD \u0BAA\u0BB2\u0BAE\u0BBE\u0B95 \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD`;case"unrecognized_keys":return`\u0B85\u0B9F\u0BC8\u0BAF\u0BBE\u0BB3\u0BAE\u0BCD \u0BA4\u0BC6\u0BB0\u0BBF\u0BAF\u0BBE\u0BA4 \u0BB5\u0BBF\u0B9A\u0BC8${o.keys.length>1?"\u0B95\u0BB3\u0BCD":""}: ${q(o.keys,", ")}`;case"invalid_key":return`${o.origin} \u0B87\u0BB2\u0BCD \u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0BB5\u0BBF\u0B9A\u0BC8`;case"invalid_union":return"\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B89\u0BB3\u0BCD\u0BB3\u0BC0\u0B9F\u0BC1";case"invalid_element":return`${o.origin} \u0B87\u0BB2\u0BCD \u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0BAE\u0BA4\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1`;default:return"\u0BA4\u0BB5\u0BB1\u0BBE\u0BA9 \u0B89\u0BB3\u0BCD\u0BB3\u0BC0\u0B9F\u0BC1"}}}});function hee(){return{localeError:HZe()}}var HZe,gee=O(()=>{je();HZe=()=>{let t={string:{unit:"\u0E15\u0E31\u0E27\u0E2D\u0E31\u0E01\u0E29\u0E23",verb:"\u0E04\u0E27\u0E23\u0E21\u0E35"},file:{unit:"\u0E44\u0E1A\u0E15\u0E4C",verb:"\u0E04\u0E27\u0E23\u0E21\u0E35"},array:{unit:"\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23",verb:"\u0E04\u0E27\u0E23\u0E21\u0E35"},set:{unit:"\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23",verb:"\u0E04\u0E27\u0E23\u0E21\u0E35"}};function e(o){return t[o]??null}let r={regex:"\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E17\u0E35\u0E48\u0E1B\u0E49\u0E2D\u0E19",email:"\u0E17\u0E35\u0E48\u0E2D\u0E22\u0E39\u0E48\u0E2D\u0E35\u0E40\u0E21\u0E25",url:"URL",emoji:"\u0E2D\u0E34\u0E42\u0E21\u0E08\u0E34",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\u0E27\u0E31\u0E19\u0E17\u0E35\u0E48\u0E40\u0E27\u0E25\u0E32\u0E41\u0E1A\u0E1A ISO",date:"\u0E27\u0E31\u0E19\u0E17\u0E35\u0E48\u0E41\u0E1A\u0E1A ISO",time:"\u0E40\u0E27\u0E25\u0E32\u0E41\u0E1A\u0E1A ISO",duration:"\u0E0A\u0E48\u0E27\u0E07\u0E40\u0E27\u0E25\u0E32\u0E41\u0E1A\u0E1A ISO",ipv4:"\u0E17\u0E35\u0E48\u0E2D\u0E22\u0E39\u0E48 IPv4",ipv6:"\u0E17\u0E35\u0E48\u0E2D\u0E22\u0E39\u0E48 IPv6",cidrv4:"\u0E0A\u0E48\u0E27\u0E07 IP \u0E41\u0E1A\u0E1A IPv4",cidrv6:"\u0E0A\u0E48\u0E27\u0E07 IP \u0E41\u0E1A\u0E1A IPv6",base64:"\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E41\u0E1A\u0E1A Base64",base64url:"\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E41\u0E1A\u0E1A Base64 \u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A URL",json_string:"\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E41\u0E1A\u0E1A JSON",e164:"\u0E40\u0E1A\u0E2D\u0E23\u0E4C\u0E42\u0E17\u0E23\u0E28\u0E31\u0E1E\u0E17\u0E4C\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07\u0E1B\u0E23\u0E30\u0E40\u0E17\u0E28 (E.164)",jwt:"\u0E42\u0E17\u0E40\u0E04\u0E19 JWT",template_literal:"\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E17\u0E35\u0E48\u0E1B\u0E49\u0E2D\u0E19"},n={nan:"NaN",number:"\u0E15\u0E31\u0E27\u0E40\u0E25\u0E02",array:"\u0E2D\u0E32\u0E23\u0E4C\u0E40\u0E23\u0E22\u0E4C (Array)",null:"\u0E44\u0E21\u0E48\u0E21\u0E35\u0E04\u0E48\u0E32 (null)"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u0E1B\u0E23\u0E30\u0E40\u0E20\u0E17\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E04\u0E27\u0E23\u0E40\u0E1B\u0E47\u0E19 instanceof ${o.expected} \u0E41\u0E15\u0E48\u0E44\u0E14\u0E49\u0E23\u0E31\u0E1A ${a}`:`\u0E1B\u0E23\u0E30\u0E40\u0E20\u0E17\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E04\u0E27\u0E23\u0E40\u0E1B\u0E47\u0E19 ${i} \u0E41\u0E15\u0E48\u0E44\u0E14\u0E49\u0E23\u0E31\u0E1A ${a}`}case"invalid_value":return o.values.length===1?`\u0E04\u0E48\u0E32\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E04\u0E27\u0E23\u0E40\u0E1B\u0E47\u0E19 ${re(o.values[0])}`:`\u0E15\u0E31\u0E27\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E04\u0E27\u0E23\u0E40\u0E1B\u0E47\u0E19\u0E2B\u0E19\u0E36\u0E48\u0E07\u0E43\u0E19 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19":"\u0E19\u0E49\u0E2D\u0E22\u0E01\u0E27\u0E48\u0E32",s=e(o.origin);return s?`\u0E40\u0E01\u0E34\u0E19\u0E01\u0E33\u0E2B\u0E19\u0E14: ${o.origin??"\u0E04\u0E48\u0E32"} \u0E04\u0E27\u0E23\u0E21\u0E35${i} ${o.maximum.toString()} ${s.unit??"\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23"}`:`\u0E40\u0E01\u0E34\u0E19\u0E01\u0E33\u0E2B\u0E19\u0E14: ${o.origin??"\u0E04\u0E48\u0E32"} \u0E04\u0E27\u0E23\u0E21\u0E35${i} ${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?"\u0E2D\u0E22\u0E48\u0E32\u0E07\u0E19\u0E49\u0E2D\u0E22":"\u0E21\u0E32\u0E01\u0E01\u0E27\u0E48\u0E32",s=e(o.origin);return s?`\u0E19\u0E49\u0E2D\u0E22\u0E01\u0E27\u0E48\u0E32\u0E01\u0E33\u0E2B\u0E19\u0E14: ${o.origin} \u0E04\u0E27\u0E23\u0E21\u0E35${i} ${o.minimum.toString()} ${s.unit}`:`\u0E19\u0E49\u0E2D\u0E22\u0E01\u0E27\u0E48\u0E32\u0E01\u0E33\u0E2B\u0E19\u0E14: ${o.origin} \u0E04\u0E27\u0E23\u0E21\u0E35${i} ${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E15\u0E49\u0E2D\u0E07\u0E02\u0E36\u0E49\u0E19\u0E15\u0E49\u0E19\u0E14\u0E49\u0E27\u0E22 "${i.prefix}"`:i.format==="ends_with"?`\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E15\u0E49\u0E2D\u0E07\u0E25\u0E07\u0E17\u0E49\u0E32\u0E22\u0E14\u0E49\u0E27\u0E22 "${i.suffix}"`:i.format==="includes"?`\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E15\u0E49\u0E2D\u0E07\u0E21\u0E35 "${i.includes}" \u0E2D\u0E22\u0E39\u0E48\u0E43\u0E19\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21`:i.format==="regex"?`\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E15\u0E49\u0E2D\u0E07\u0E15\u0E23\u0E07\u0E01\u0E31\u0E1A\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A\u0E17\u0E35\u0E48\u0E01\u0E33\u0E2B\u0E19\u0E14 ${i.pattern}`:`\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: ${r[i.format]??o.format}`}case"not_multiple_of":return`\u0E15\u0E31\u0E27\u0E40\u0E25\u0E02\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E15\u0E49\u0E2D\u0E07\u0E40\u0E1B\u0E47\u0E19\u0E08\u0E33\u0E19\u0E27\u0E19\u0E17\u0E35\u0E48\u0E2B\u0E32\u0E23\u0E14\u0E49\u0E27\u0E22 ${o.divisor} \u0E44\u0E14\u0E49\u0E25\u0E07\u0E15\u0E31\u0E27`;case"unrecognized_keys":return`\u0E1E\u0E1A\u0E04\u0E35\u0E22\u0E4C\u0E17\u0E35\u0E48\u0E44\u0E21\u0E48\u0E23\u0E39\u0E49\u0E08\u0E31\u0E01: ${q(o.keys,", ")}`;case"invalid_key":return`\u0E04\u0E35\u0E22\u0E4C\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07\u0E43\u0E19 ${o.origin}`;case"invalid_union":return"\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07: \u0E44\u0E21\u0E48\u0E15\u0E23\u0E07\u0E01\u0E31\u0E1A\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A\u0E22\u0E39\u0E40\u0E19\u0E35\u0E22\u0E19\u0E17\u0E35\u0E48\u0E01\u0E33\u0E2B\u0E19\u0E14\u0E44\u0E27\u0E49";case"invalid_element":return`\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07\u0E43\u0E19 ${o.origin}`;default:return"\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07"}}}});function _ee(){return{localeError:WZe()}}var WZe,vee=O(()=>{je();WZe=()=>{let t={string:{unit:"karakter",verb:"olmal\u0131"},file:{unit:"bayt",verb:"olmal\u0131"},array:{unit:"\xF6\u011Fe",verb:"olmal\u0131"},set:{unit:"\xF6\u011Fe",verb:"olmal\u0131"}};function e(o){return t[o]??null}let r={regex:"girdi",email:"e-posta adresi",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO tarih ve saat",date:"ISO tarih",time:"ISO saat",duration:"ISO s\xFCre",ipv4:"IPv4 adresi",ipv6:"IPv6 adresi",cidrv4:"IPv4 aral\u0131\u011F\u0131",cidrv6:"IPv6 aral\u0131\u011F\u0131",base64:"base64 ile \u015Fifrelenmi\u015F metin",base64url:"base64url ile \u015Fifrelenmi\u015F metin",json_string:"JSON dizesi",e164:"E.164 say\u0131s\u0131",jwt:"JWT",template_literal:"\u015Eablon dizesi"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Ge\xE7ersiz de\u011Fer: beklenen instanceof ${o.expected}, al\u0131nan ${a}`:`Ge\xE7ersiz de\u011Fer: beklenen ${i}, al\u0131nan ${a}`}case"invalid_value":return o.values.length===1?`Ge\xE7ersiz de\u011Fer: beklenen ${re(o.values[0])}`:`Ge\xE7ersiz se\xE7enek: a\u015Fa\u011F\u0131dakilerden biri olmal\u0131: ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\xC7ok b\xFCy\xFCk: beklenen ${o.origin??"de\u011Fer"} ${i}${o.maximum.toString()} ${s.unit??"\xF6\u011Fe"}`:`\xC7ok b\xFCy\xFCk: beklenen ${o.origin??"de\u011Fer"} ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\xC7ok k\xFC\xE7\xFCk: beklenen ${o.origin} ${i}${o.minimum.toString()} ${s.unit}`:`\xC7ok k\xFC\xE7\xFCk: beklenen ${o.origin} ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Ge\xE7ersiz metin: "${i.prefix}" ile ba\u015Flamal\u0131`:i.format==="ends_with"?`Ge\xE7ersiz metin: "${i.suffix}" ile bitmeli`:i.format==="includes"?`Ge\xE7ersiz metin: "${i.includes}" i\xE7ermeli`:i.format==="regex"?`Ge\xE7ersiz metin: ${i.pattern} desenine uymal\u0131`:`Ge\xE7ersiz ${r[i.format]??o.format}`}case"not_multiple_of":return`Ge\xE7ersiz say\u0131: ${o.divisor} ile tam b\xF6l\xFCnebilmeli`;case"unrecognized_keys":return`Tan\u0131nmayan anahtar${o.keys.length>1?"lar":""}: ${q(o.keys,", ")}`;case"invalid_key":return`${o.origin} i\xE7inde ge\xE7ersiz anahtar`;case"invalid_union":return"Ge\xE7ersiz de\u011Fer";case"invalid_element":return`${o.origin} i\xE7inde ge\xE7ersiz de\u011Fer`;default:return"Ge\xE7ersiz de\u011Fer"}}}});function UR(){return{localeError:KZe()}}var KZe,az=O(()=>{je();KZe=()=>{let t={string:{unit:"\u0441\u0438\u043C\u0432\u043E\u043B\u0456\u0432",verb:"\u043C\u0430\u0442\u0438\u043C\u0435"},file:{unit:"\u0431\u0430\u0439\u0442\u0456\u0432",verb:"\u043C\u0430\u0442\u0438\u043C\u0435"},array:{unit:"\u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0456\u0432",verb:"\u043C\u0430\u0442\u0438\u043C\u0435"},set:{unit:"\u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0456\u0432",verb:"\u043C\u0430\u0442\u0438\u043C\u0435"}};function e(o){return t[o]??null}let r={regex:"\u0432\u0445\u0456\u0434\u043D\u0456 \u0434\u0430\u043D\u0456",email:"\u0430\u0434\u0440\u0435\u0441\u0430 \u0435\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0457 \u043F\u043E\u0448\u0442\u0438",url:"URL",emoji:"\u0435\u043C\u043E\u0434\u0437\u0456",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\u0434\u0430\u0442\u0430 \u0442\u0430 \u0447\u0430\u0441 ISO",date:"\u0434\u0430\u0442\u0430 ISO",time:"\u0447\u0430\u0441 ISO",duration:"\u0442\u0440\u0438\u0432\u0430\u043B\u0456\u0441\u0442\u044C ISO",ipv4:"\u0430\u0434\u0440\u0435\u0441\u0430 IPv4",ipv6:"\u0430\u0434\u0440\u0435\u0441\u0430 IPv6",cidrv4:"\u0434\u0456\u0430\u043F\u0430\u0437\u043E\u043D IPv4",cidrv6:"\u0434\u0456\u0430\u043F\u0430\u0437\u043E\u043D IPv6",base64:"\u0440\u044F\u0434\u043E\u043A \u0443 \u043A\u043E\u0434\u0443\u0432\u0430\u043D\u043D\u0456 base64",base64url:"\u0440\u044F\u0434\u043E\u043A \u0443 \u043A\u043E\u0434\u0443\u0432\u0430\u043D\u043D\u0456 base64url",json_string:"\u0440\u044F\u0434\u043E\u043A JSON",e164:"\u043D\u043E\u043C\u0435\u0440 E.164",jwt:"JWT",template_literal:"\u0432\u0445\u0456\u0434\u043D\u0456 \u0434\u0430\u043D\u0456"},n={nan:"NaN",number:"\u0447\u0438\u0441\u043B\u043E",array:"\u043C\u0430\u0441\u0438\u0432"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0456 \u0432\u0445\u0456\u0434\u043D\u0456 \u0434\u0430\u043D\u0456: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F instanceof ${o.expected}, \u043E\u0442\u0440\u0438\u043C\u0430\u043D\u043E ${a}`:`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0456 \u0432\u0445\u0456\u0434\u043D\u0456 \u0434\u0430\u043D\u0456: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F ${i}, \u043E\u0442\u0440\u0438\u043C\u0430\u043D\u043E ${a}`}case"invalid_value":return o.values.length===1?`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0456 \u0432\u0445\u0456\u0434\u043D\u0456 \u0434\u0430\u043D\u0456: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F ${re(o.values[0])}`:`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0430 \u043E\u043F\u0446\u0456\u044F: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F \u043E\u0434\u043D\u0435 \u0437 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u0417\u0430\u043D\u0430\u0434\u0442\u043E \u0432\u0435\u043B\u0438\u043A\u0435: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F, \u0449\u043E ${o.origin??"\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044F"} ${s.verb} ${i}${o.maximum.toString()} ${s.unit??"\u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0456\u0432"}`:`\u0417\u0430\u043D\u0430\u0434\u0442\u043E \u0432\u0435\u043B\u0438\u043A\u0435: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F, \u0449\u043E ${o.origin??"\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044F"} \u0431\u0443\u0434\u0435 ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u0417\u0430\u043D\u0430\u0434\u0442\u043E \u043C\u0430\u043B\u0435: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F, \u0449\u043E ${o.origin} ${s.verb} ${i}${o.minimum.toString()} ${s.unit}`:`\u0417\u0430\u043D\u0430\u0434\u0442\u043E \u043C\u0430\u043B\u0435: \u043E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F, \u0449\u043E ${o.origin} \u0431\u0443\u0434\u0435 ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0438\u0439 \u0440\u044F\u0434\u043E\u043A: \u043F\u043E\u0432\u0438\u043D\u0435\u043D \u043F\u043E\u0447\u0438\u043D\u0430\u0442\u0438\u0441\u044F \u0437 "${i.prefix}"`:i.format==="ends_with"?`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0438\u0439 \u0440\u044F\u0434\u043E\u043A: \u043F\u043E\u0432\u0438\u043D\u0435\u043D \u0437\u0430\u043A\u0456\u043D\u0447\u0443\u0432\u0430\u0442\u0438\u0441\u044F \u043D\u0430 "${i.suffix}"`:i.format==="includes"?`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0438\u0439 \u0440\u044F\u0434\u043E\u043A: \u043F\u043E\u0432\u0438\u043D\u0435\u043D \u043C\u0456\u0441\u0442\u0438\u0442\u0438 "${i.includes}"`:i.format==="regex"?`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0438\u0439 \u0440\u044F\u0434\u043E\u043A: \u043F\u043E\u0432\u0438\u043D\u0435\u043D \u0432\u0456\u0434\u043F\u043E\u0432\u0456\u0434\u0430\u0442\u0438 \u0448\u0430\u0431\u043B\u043E\u043D\u0443 ${i.pattern}`:`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0438\u0439 ${r[i.format]??o.format}`}case"not_multiple_of":return`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0435 \u0447\u0438\u0441\u043B\u043E: \u043F\u043E\u0432\u0438\u043D\u043D\u043E \u0431\u0443\u0442\u0438 \u043A\u0440\u0430\u0442\u043D\u0438\u043C ${o.divisor}`;case"unrecognized_keys":return`\u041D\u0435\u0440\u043E\u0437\u043F\u0456\u0437\u043D\u0430\u043D\u0438\u0439 \u043A\u043B\u044E\u0447${o.keys.length>1?"\u0456":""}: ${q(o.keys,", ")}`;case"invalid_key":return`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0438\u0439 \u043A\u043B\u044E\u0447 \u0443 ${o.origin}`;case"invalid_union":return"\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0456 \u0432\u0445\u0456\u0434\u043D\u0456 \u0434\u0430\u043D\u0456";case"invalid_element":return`\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044F \u0443 ${o.origin}`;default:return"\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0456 \u0432\u0445\u0456\u0434\u043D\u0456 \u0434\u0430\u043D\u0456"}}}});function See(){return UR()}var yee=O(()=>{az()});function Eee(){return{localeError:ZZe()}}var ZZe,Tee=O(()=>{je();ZZe=()=>{let t={string:{unit:"\u062D\u0631\u0648\u0641",verb:"\u06C1\u0648\u0646\u0627"},file:{unit:"\u0628\u0627\u0626\u0679\u0633",verb:"\u06C1\u0648\u0646\u0627"},array:{unit:"\u0622\u0626\u0679\u0645\u0632",verb:"\u06C1\u0648\u0646\u0627"},set:{unit:"\u0622\u0626\u0679\u0645\u0632",verb:"\u06C1\u0648\u0646\u0627"}};function e(o){return t[o]??null}let r={regex:"\u0627\u0646 \u067E\u0679",email:"\u0627\u06CC \u0645\u06CC\u0644 \u0627\u06CC\u0688\u0631\u06CC\u0633",url:"\u06CC\u0648 \u0622\u0631 \u0627\u06CC\u0644",emoji:"\u0627\u06CC\u0645\u0648\u062C\u06CC",uuid:"\u06CC\u0648 \u06CC\u0648 \u0622\u0626\u06CC \u0688\u06CC",uuidv4:"\u06CC\u0648 \u06CC\u0648 \u0622\u0626\u06CC \u0688\u06CC \u0648\u06CC 4",uuidv6:"\u06CC\u0648 \u06CC\u0648 \u0622\u0626\u06CC \u0688\u06CC \u0648\u06CC 6",nanoid:"\u0646\u06CC\u0646\u0648 \u0622\u0626\u06CC \u0688\u06CC",guid:"\u062C\u06CC \u06CC\u0648 \u0622\u0626\u06CC \u0688\u06CC",cuid:"\u0633\u06CC \u06CC\u0648 \u0622\u0626\u06CC \u0688\u06CC",cuid2:"\u0633\u06CC \u06CC\u0648 \u0622\u0626\u06CC \u0688\u06CC 2",ulid:"\u06CC\u0648 \u0627\u06CC\u0644 \u0622\u0626\u06CC \u0688\u06CC",xid:"\u0627\u06CC\u06A9\u0633 \u0622\u0626\u06CC \u0688\u06CC",ksuid:"\u06A9\u06D2 \u0627\u06CC\u0633 \u06CC\u0648 \u0622\u0626\u06CC \u0688\u06CC",datetime:"\u0622\u0626\u06CC \u0627\u06CC\u0633 \u0627\u0648 \u0688\u06CC\u0679 \u0679\u0627\u0626\u0645",date:"\u0622\u0626\u06CC \u0627\u06CC\u0633 \u0627\u0648 \u062A\u0627\u0631\u06CC\u062E",time:"\u0622\u0626\u06CC \u0627\u06CC\u0633 \u0627\u0648 \u0648\u0642\u062A",duration:"\u0622\u0626\u06CC \u0627\u06CC\u0633 \u0627\u0648 \u0645\u062F\u062A",ipv4:"\u0622\u0626\u06CC \u067E\u06CC \u0648\u06CC 4 \u0627\u06CC\u0688\u0631\u06CC\u0633",ipv6:"\u0622\u0626\u06CC \u067E\u06CC \u0648\u06CC 6 \u0627\u06CC\u0688\u0631\u06CC\u0633",cidrv4:"\u0622\u0626\u06CC \u067E\u06CC \u0648\u06CC 4 \u0631\u06CC\u0646\u062C",cidrv6:"\u0622\u0626\u06CC \u067E\u06CC \u0648\u06CC 6 \u0631\u06CC\u0646\u062C",base64:"\u0628\u06CC\u0633 64 \u0627\u0646 \u06A9\u0648\u0688\u0688 \u0633\u0679\u0631\u0646\u06AF",base64url:"\u0628\u06CC\u0633 64 \u06CC\u0648 \u0622\u0631 \u0627\u06CC\u0644 \u0627\u0646 \u06A9\u0648\u0688\u0688 \u0633\u0679\u0631\u0646\u06AF",json_string:"\u062C\u06D2 \u0627\u06CC\u0633 \u0627\u0648 \u0627\u06CC\u0646 \u0633\u0679\u0631\u0646\u06AF",e164:"\u0627\u06CC 164 \u0646\u0645\u0628\u0631",jwt:"\u062C\u06D2 \u0688\u0628\u0644\u06CC\u0648 \u0679\u06CC",template_literal:"\u0627\u0646 \u067E\u0679"},n={nan:"NaN",number:"\u0646\u0645\u0628\u0631",array:"\u0622\u0631\u06D2",null:"\u0646\u0644"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u063A\u0644\u0637 \u0627\u0646 \u067E\u0679: instanceof ${o.expected} \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u0627\u060C ${a} \u0645\u0648\u0635\u0648\u0644 \u06C1\u0648\u0627`:`\u063A\u0644\u0637 \u0627\u0646 \u067E\u0679: ${i} \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u0627\u060C ${a} \u0645\u0648\u0635\u0648\u0644 \u06C1\u0648\u0627`}case"invalid_value":return o.values.length===1?`\u063A\u0644\u0637 \u0627\u0646 \u067E\u0679: ${re(o.values[0])} \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u0627`:`\u063A\u0644\u0637 \u0622\u067E\u0634\u0646: ${q(o.values,"|")} \u0645\u06CC\u06BA \u0633\u06D2 \u0627\u06CC\u06A9 \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u0627`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u0628\u06C1\u062A \u0628\u0691\u0627: ${o.origin??"\u0648\u06CC\u0644\u06CC\u0648"} \u06A9\u06D2 ${i}${o.maximum.toString()} ${s.unit??"\u0639\u0646\u0627\u0635\u0631"} \u06C1\u0648\u0646\u06D2 \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u06D2`:`\u0628\u06C1\u062A \u0628\u0691\u0627: ${o.origin??"\u0648\u06CC\u0644\u06CC\u0648"} \u06A9\u0627 ${i}${o.maximum.toString()} \u06C1\u0648\u0646\u0627 \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u0627`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u0628\u06C1\u062A \u0686\u06BE\u0648\u0679\u0627: ${o.origin} \u06A9\u06D2 ${i}${o.minimum.toString()} ${s.unit} \u06C1\u0648\u0646\u06D2 \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u06D2`:`\u0628\u06C1\u062A \u0686\u06BE\u0648\u0679\u0627: ${o.origin} \u06A9\u0627 ${i}${o.minimum.toString()} \u06C1\u0648\u0646\u0627 \u0645\u062A\u0648\u0642\u0639 \u062A\u06BE\u0627`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u063A\u0644\u0637 \u0633\u0679\u0631\u0646\u06AF: "${i.prefix}" \u0633\u06D2 \u0634\u0631\u0648\u0639 \u06C1\u0648\u0646\u0627 \u0686\u0627\u06C1\u06CC\u06D2`:i.format==="ends_with"?`\u063A\u0644\u0637 \u0633\u0679\u0631\u0646\u06AF: "${i.suffix}" \u067E\u0631 \u062E\u062A\u0645 \u06C1\u0648\u0646\u0627 \u0686\u0627\u06C1\u06CC\u06D2`:i.format==="includes"?`\u063A\u0644\u0637 \u0633\u0679\u0631\u0646\u06AF: "${i.includes}" \u0634\u0627\u0645\u0644 \u06C1\u0648\u0646\u0627 \u0686\u0627\u06C1\u06CC\u06D2`:i.format==="regex"?`\u063A\u0644\u0637 \u0633\u0679\u0631\u0646\u06AF: \u067E\u06CC\u0679\u0631\u0646 ${i.pattern} \u0633\u06D2 \u0645\u06CC\u0686 \u06C1\u0648\u0646\u0627 \u0686\u0627\u06C1\u06CC\u06D2`:`\u063A\u0644\u0637 ${r[i.format]??o.format}`}case"not_multiple_of":return`\u063A\u0644\u0637 \u0646\u0645\u0628\u0631: ${o.divisor} \u06A9\u0627 \u0645\u0636\u0627\u0639\u0641 \u06C1\u0648\u0646\u0627 \u0686\u0627\u06C1\u06CC\u06D2`;case"unrecognized_keys":return`\u063A\u06CC\u0631 \u062A\u0633\u0644\u06CC\u0645 \u0634\u062F\u06C1 \u06A9\u06CC${o.keys.length>1?"\u0632":""}: ${q(o.keys,"\u060C ")}`;case"invalid_key":return`${o.origin} \u0645\u06CC\u06BA \u063A\u0644\u0637 \u06A9\u06CC`;case"invalid_union":return"\u063A\u0644\u0637 \u0627\u0646 \u067E\u0679";case"invalid_element":return`${o.origin} \u0645\u06CC\u06BA \u063A\u0644\u0637 \u0648\u06CC\u0644\u06CC\u0648`;default:return"\u063A\u0644\u0637 \u0627\u0646 \u067E\u0679"}}}});function bee(){return{localeError:YZe()}}var YZe,xee=O(()=>{je();YZe=()=>{let t={string:{unit:"belgi",verb:"bo\u2018lishi kerak"},file:{unit:"bayt",verb:"bo\u2018lishi kerak"},array:{unit:"element",verb:"bo\u2018lishi kerak"},set:{unit:"element",verb:"bo\u2018lishi kerak"}};function e(o){return t[o]??null}let r={regex:"kirish",email:"elektron pochta manzili",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO sana va vaqti",date:"ISO sana",time:"ISO vaqt",duration:"ISO davomiylik",ipv4:"IPv4 manzil",ipv6:"IPv6 manzil",mac:"MAC manzil",cidrv4:"IPv4 diapazon",cidrv6:"IPv6 diapazon",base64:"base64 kodlangan satr",base64url:"base64url kodlangan satr",json_string:"JSON satr",e164:"E.164 raqam",jwt:"JWT",template_literal:"kirish"},n={nan:"NaN",number:"raqam",array:"massiv"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`Noto\u2018g\u2018ri kirish: kutilgan instanceof ${o.expected}, qabul qilingan ${a}`:`Noto\u2018g\u2018ri kirish: kutilgan ${i}, qabul qilingan ${a}`}case"invalid_value":return o.values.length===1?`Noto\u2018g\u2018ri kirish: kutilgan ${re(o.values[0])}`:`Noto\u2018g\u2018ri variant: quyidagilardan biri kutilgan ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Juda katta: kutilgan ${o.origin??"qiymat"} ${i}${o.maximum.toString()} ${s.unit} ${s.verb}`:`Juda katta: kutilgan ${o.origin??"qiymat"} ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Juda kichik: kutilgan ${o.origin} ${i}${o.minimum.toString()} ${s.unit} ${s.verb}`:`Juda kichik: kutilgan ${o.origin} ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Noto\u2018g\u2018ri satr: "${i.prefix}" bilan boshlanishi kerak`:i.format==="ends_with"?`Noto\u2018g\u2018ri satr: "${i.suffix}" bilan tugashi kerak`:i.format==="includes"?`Noto\u2018g\u2018ri satr: "${i.includes}" ni o\u2018z ichiga olishi kerak`:i.format==="regex"?`Noto\u2018g\u2018ri satr: ${i.pattern} shabloniga mos kelishi kerak`:`Noto\u2018g\u2018ri ${r[i.format]??o.format}`}case"not_multiple_of":return`Noto\u2018g\u2018ri raqam: ${o.divisor} ning karralisi bo\u2018lishi kerak`;case"unrecognized_keys":return`Noma\u2019lum kalit${o.keys.length>1?"lar":""}: ${q(o.keys,", ")}`;case"invalid_key":return`${o.origin} dagi kalit noto\u2018g\u2018ri`;case"invalid_union":return"Noto\u2018g\u2018ri kirish";case"invalid_element":return`${o.origin} da noto\u2018g\u2018ri qiymat`;default:return"Noto\u2018g\u2018ri kirish"}}}});function Aee(){return{localeError:JZe()}}var JZe,wee=O(()=>{je();JZe=()=>{let t={string:{unit:"k\xFD t\u1EF1",verb:"c\xF3"},file:{unit:"byte",verb:"c\xF3"},array:{unit:"ph\u1EA7n t\u1EED",verb:"c\xF3"},set:{unit:"ph\u1EA7n t\u1EED",verb:"c\xF3"}};function e(o){return t[o]??null}let r={regex:"\u0111\u1EA7u v\xE0o",email:"\u0111\u1ECBa ch\u1EC9 email",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ng\xE0y gi\u1EDD ISO",date:"ng\xE0y ISO",time:"gi\u1EDD ISO",duration:"kho\u1EA3ng th\u1EDDi gian ISO",ipv4:"\u0111\u1ECBa ch\u1EC9 IPv4",ipv6:"\u0111\u1ECBa ch\u1EC9 IPv6",cidrv4:"d\u1EA3i IPv4",cidrv6:"d\u1EA3i IPv6",base64:"chu\u1ED7i m\xE3 h\xF3a base64",base64url:"chu\u1ED7i m\xE3 h\xF3a base64url",json_string:"chu\u1ED7i JSON",e164:"s\u1ED1 E.164",jwt:"JWT",template_literal:"\u0111\u1EA7u v\xE0o"},n={nan:"NaN",number:"s\u1ED1",array:"m\u1EA3ng"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u0110\u1EA7u v\xE0o kh\xF4ng h\u1EE3p l\u1EC7: mong \u0111\u1EE3i instanceof ${o.expected}, nh\u1EADn \u0111\u01B0\u1EE3c ${a}`:`\u0110\u1EA7u v\xE0o kh\xF4ng h\u1EE3p l\u1EC7: mong \u0111\u1EE3i ${i}, nh\u1EADn \u0111\u01B0\u1EE3c ${a}`}case"invalid_value":return o.values.length===1?`\u0110\u1EA7u v\xE0o kh\xF4ng h\u1EE3p l\u1EC7: mong \u0111\u1EE3i ${re(o.values[0])}`:`T\xF9y ch\u1ECDn kh\xF4ng h\u1EE3p l\u1EC7: mong \u0111\u1EE3i m\u1ED9t trong c\xE1c gi\xE1 tr\u1ECB ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`Qu\xE1 l\u1EDBn: mong \u0111\u1EE3i ${o.origin??"gi\xE1 tr\u1ECB"} ${s.verb} ${i}${o.maximum.toString()} ${s.unit??"ph\u1EA7n t\u1EED"}`:`Qu\xE1 l\u1EDBn: mong \u0111\u1EE3i ${o.origin??"gi\xE1 tr\u1ECB"} ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`Qu\xE1 nh\u1ECF: mong \u0111\u1EE3i ${o.origin} ${s.verb} ${i}${o.minimum.toString()} ${s.unit}`:`Qu\xE1 nh\u1ECF: mong \u0111\u1EE3i ${o.origin} ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`Chu\u1ED7i kh\xF4ng h\u1EE3p l\u1EC7: ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng "${i.prefix}"`:i.format==="ends_with"?`Chu\u1ED7i kh\xF4ng h\u1EE3p l\u1EC7: ph\u1EA3i k\u1EBFt th\xFAc b\u1EB1ng "${i.suffix}"`:i.format==="includes"?`Chu\u1ED7i kh\xF4ng h\u1EE3p l\u1EC7: ph\u1EA3i bao g\u1ED3m "${i.includes}"`:i.format==="regex"?`Chu\u1ED7i kh\xF4ng h\u1EE3p l\u1EC7: ph\u1EA3i kh\u1EDBp v\u1EDBi m\u1EABu ${i.pattern}`:`${r[i.format]??o.format} kh\xF4ng h\u1EE3p l\u1EC7`}case"not_multiple_of":return`S\u1ED1 kh\xF4ng h\u1EE3p l\u1EC7: ph\u1EA3i l\xE0 b\u1ED9i s\u1ED1 c\u1EE7a ${o.divisor}`;case"unrecognized_keys":return`Kh\xF3a kh\xF4ng \u0111\u01B0\u1EE3c nh\u1EADn d\u1EA1ng: ${q(o.keys,", ")}`;case"invalid_key":return`Kh\xF3a kh\xF4ng h\u1EE3p l\u1EC7 trong ${o.origin}`;case"invalid_union":return"\u0110\u1EA7u v\xE0o kh\xF4ng h\u1EE3p l\u1EC7";case"invalid_element":return`Gi\xE1 tr\u1ECB kh\xF4ng h\u1EE3p l\u1EC7 trong ${o.origin}`;default:return"\u0110\u1EA7u v\xE0o kh\xF4ng h\u1EE3p l\u1EC7"}}}});function Ree(){return{localeError:XZe()}}var XZe,Pee=O(()=>{je();XZe=()=>{let t={string:{unit:"\u5B57\u7B26",verb:"\u5305\u542B"},file:{unit:"\u5B57\u8282",verb:"\u5305\u542B"},array:{unit:"\u9879",verb:"\u5305\u542B"},set:{unit:"\u9879",verb:"\u5305\u542B"}};function e(o){return t[o]??null}let r={regex:"\u8F93\u5165",email:"\u7535\u5B50\u90AE\u4EF6",url:"URL",emoji:"\u8868\u60C5\u7B26\u53F7",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO\u65E5\u671F\u65F6\u95F4",date:"ISO\u65E5\u671F",time:"ISO\u65F6\u95F4",duration:"ISO\u65F6\u957F",ipv4:"IPv4\u5730\u5740",ipv6:"IPv6\u5730\u5740",cidrv4:"IPv4\u7F51\u6BB5",cidrv6:"IPv6\u7F51\u6BB5",base64:"base64\u7F16\u7801\u5B57\u7B26\u4E32",base64url:"base64url\u7F16\u7801\u5B57\u7B26\u4E32",json_string:"JSON\u5B57\u7B26\u4E32",e164:"E.164\u53F7\u7801",jwt:"JWT",template_literal:"\u8F93\u5165"},n={nan:"NaN",number:"\u6570\u5B57",array:"\u6570\u7EC4",null:"\u7A7A\u503C(null)"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u65E0\u6548\u8F93\u5165\uFF1A\u671F\u671B instanceof ${o.expected}\uFF0C\u5B9E\u9645\u63A5\u6536 ${a}`:`\u65E0\u6548\u8F93\u5165\uFF1A\u671F\u671B ${i}\uFF0C\u5B9E\u9645\u63A5\u6536 ${a}`}case"invalid_value":return o.values.length===1?`\u65E0\u6548\u8F93\u5165\uFF1A\u671F\u671B ${re(o.values[0])}`:`\u65E0\u6548\u9009\u9879\uFF1A\u671F\u671B\u4EE5\u4E0B\u4E4B\u4E00 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u6570\u503C\u8FC7\u5927\uFF1A\u671F\u671B ${o.origin??"\u503C"} ${i}${o.maximum.toString()} ${s.unit??"\u4E2A\u5143\u7D20"}`:`\u6570\u503C\u8FC7\u5927\uFF1A\u671F\u671B ${o.origin??"\u503C"} ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u6570\u503C\u8FC7\u5C0F\uFF1A\u671F\u671B ${o.origin} ${i}${o.minimum.toString()} ${s.unit}`:`\u6570\u503C\u8FC7\u5C0F\uFF1A\u671F\u671B ${o.origin} ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u65E0\u6548\u5B57\u7B26\u4E32\uFF1A\u5FC5\u987B\u4EE5 "${i.prefix}" \u5F00\u5934`:i.format==="ends_with"?`\u65E0\u6548\u5B57\u7B26\u4E32\uFF1A\u5FC5\u987B\u4EE5 "${i.suffix}" \u7ED3\u5C3E`:i.format==="includes"?`\u65E0\u6548\u5B57\u7B26\u4E32\uFF1A\u5FC5\u987B\u5305\u542B "${i.includes}"`:i.format==="regex"?`\u65E0\u6548\u5B57\u7B26\u4E32\uFF1A\u5FC5\u987B\u6EE1\u8DB3\u6B63\u5219\u8868\u8FBE\u5F0F ${i.pattern}`:`\u65E0\u6548${r[i.format]??o.format}`}case"not_multiple_of":return`\u65E0\u6548\u6570\u5B57\uFF1A\u5FC5\u987B\u662F ${o.divisor} \u7684\u500D\u6570`;case"unrecognized_keys":return`\u51FA\u73B0\u672A\u77E5\u7684\u952E(key): ${q(o.keys,", ")}`;case"invalid_key":return`${o.origin} \u4E2D\u7684\u952E(key)\u65E0\u6548`;case"invalid_union":return"\u65E0\u6548\u8F93\u5165";case"invalid_element":return`${o.origin} \u4E2D\u5305\u542B\u65E0\u6548\u503C(value)`;default:return"\u65E0\u6548\u8F93\u5165"}}}});function Iee(){return{localeError:QZe()}}var QZe,Oee=O(()=>{je();QZe=()=>{let t={string:{unit:"\u5B57\u5143",verb:"\u64C1\u6709"},file:{unit:"\u4F4D\u5143\u7D44",verb:"\u64C1\u6709"},array:{unit:"\u9805\u76EE",verb:"\u64C1\u6709"},set:{unit:"\u9805\u76EE",verb:"\u64C1\u6709"}};function e(o){return t[o]??null}let r={regex:"\u8F38\u5165",email:"\u90F5\u4EF6\u5730\u5740",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO \u65E5\u671F\u6642\u9593",date:"ISO \u65E5\u671F",time:"ISO \u6642\u9593",duration:"ISO \u671F\u9593",ipv4:"IPv4 \u4F4D\u5740",ipv6:"IPv6 \u4F4D\u5740",cidrv4:"IPv4 \u7BC4\u570D",cidrv6:"IPv6 \u7BC4\u570D",base64:"base64 \u7DE8\u78BC\u5B57\u4E32",base64url:"base64url \u7DE8\u78BC\u5B57\u4E32",json_string:"JSON \u5B57\u4E32",e164:"E.164 \u6578\u503C",jwt:"JWT",template_literal:"\u8F38\u5165"},n={nan:"NaN"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\u7121\u6548\u7684\u8F38\u5165\u503C\uFF1A\u9810\u671F\u70BA instanceof ${o.expected}\uFF0C\u4F46\u6536\u5230 ${a}`:`\u7121\u6548\u7684\u8F38\u5165\u503C\uFF1A\u9810\u671F\u70BA ${i}\uFF0C\u4F46\u6536\u5230 ${a}`}case"invalid_value":return o.values.length===1?`\u7121\u6548\u7684\u8F38\u5165\u503C\uFF1A\u9810\u671F\u70BA ${re(o.values[0])}`:`\u7121\u6548\u7684\u9078\u9805\uFF1A\u9810\u671F\u70BA\u4EE5\u4E0B\u5176\u4E2D\u4E4B\u4E00 ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`\u6578\u503C\u904E\u5927\uFF1A\u9810\u671F ${o.origin??"\u503C"} \u61C9\u70BA ${i}${o.maximum.toString()} ${s.unit??"\u500B\u5143\u7D20"}`:`\u6578\u503C\u904E\u5927\uFF1A\u9810\u671F ${o.origin??"\u503C"} \u61C9\u70BA ${i}${o.maximum.toString()}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`\u6578\u503C\u904E\u5C0F\uFF1A\u9810\u671F ${o.origin} \u61C9\u70BA ${i}${o.minimum.toString()} ${s.unit}`:`\u6578\u503C\u904E\u5C0F\uFF1A\u9810\u671F ${o.origin} \u61C9\u70BA ${i}${o.minimum.toString()}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u7121\u6548\u7684\u5B57\u4E32\uFF1A\u5FC5\u9808\u4EE5 "${i.prefix}" \u958B\u982D`:i.format==="ends_with"?`\u7121\u6548\u7684\u5B57\u4E32\uFF1A\u5FC5\u9808\u4EE5 "${i.suffix}" \u7D50\u5C3E`:i.format==="includes"?`\u7121\u6548\u7684\u5B57\u4E32\uFF1A\u5FC5\u9808\u5305\u542B "${i.includes}"`:i.format==="regex"?`\u7121\u6548\u7684\u5B57\u4E32\uFF1A\u5FC5\u9808\u7B26\u5408\u683C\u5F0F ${i.pattern}`:`\u7121\u6548\u7684 ${r[i.format]??o.format}`}case"not_multiple_of":return`\u7121\u6548\u7684\u6578\u5B57\uFF1A\u5FC5\u9808\u70BA ${o.divisor} \u7684\u500D\u6578`;case"unrecognized_keys":return`\u7121\u6CD5\u8B58\u5225\u7684\u9375\u503C${o.keys.length>1?"\u5011":""}\uFF1A${q(o.keys,"\u3001")}`;case"invalid_key":return`${o.origin} \u4E2D\u6709\u7121\u6548\u7684\u9375\u503C`;case"invalid_union":return"\u7121\u6548\u7684\u8F38\u5165\u503C";case"invalid_element":return`${o.origin} \u4E2D\u6709\u7121\u6548\u7684\u503C`;default:return"\u7121\u6548\u7684\u8F38\u5165\u503C"}}}});function Nee(){return{localeError:eYe()}}var eYe,Cee=O(()=>{je();eYe=()=>{let t={string:{unit:"\xE0mi",verb:"n\xED"},file:{unit:"bytes",verb:"n\xED"},array:{unit:"nkan",verb:"n\xED"},set:{unit:"nkan",verb:"n\xED"}};function e(o){return t[o]??null}let r={regex:"\u1EB9\u0300r\u1ECD \xECb\xE1w\u1ECDl\xE9",email:"\xE0d\xEDr\u1EB9\u0301s\xEC \xECm\u1EB9\u0301l\xEC",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"\xE0k\xF3k\xF2 ISO",date:"\u1ECDj\u1ECD\u0301 ISO",time:"\xE0k\xF3k\xF2 ISO",duration:"\xE0k\xF3k\xF2 t\xF3 p\xE9 ISO",ipv4:"\xE0d\xEDr\u1EB9\u0301s\xEC IPv4",ipv6:"\xE0d\xEDr\u1EB9\u0301s\xEC IPv6",cidrv4:"\xE0gb\xE8gb\xE8 IPv4",cidrv6:"\xE0gb\xE8gb\xE8 IPv6",base64:"\u1ECD\u0300r\u1ECD\u0300 t\xED a k\u1ECD\u0301 n\xED base64",base64url:"\u1ECD\u0300r\u1ECD\u0300 base64url",json_string:"\u1ECD\u0300r\u1ECD\u0300 JSON",e164:"n\u1ECD\u0301mb\xE0 E.164",jwt:"JWT",template_literal:"\u1EB9\u0300r\u1ECD \xECb\xE1w\u1ECDl\xE9"},n={nan:"NaN",number:"n\u1ECD\u0301mb\xE0",array:"akop\u1ECD"};return o=>{switch(o.code){case"invalid_type":{let i=n[o.expected]??o.expected,s=oe(o.input),a=n[s]??s;return/^[A-Z]/.test(o.expected)?`\xCCb\xE1w\u1ECDl\xE9 a\u1E63\xEC\u1E63e: a n\xED l\xE1ti fi instanceof ${o.expected}, \xE0m\u1ECD\u0300 a r\xED ${a}`:`\xCCb\xE1w\u1ECDl\xE9 a\u1E63\xEC\u1E63e: a n\xED l\xE1ti fi ${i}, \xE0m\u1ECD\u0300 a r\xED ${a}`}case"invalid_value":return o.values.length===1?`\xCCb\xE1w\u1ECDl\xE9 a\u1E63\xEC\u1E63e: a n\xED l\xE1ti fi ${re(o.values[0])}`:`\xC0\u1E63\xE0y\xE0n a\u1E63\xEC\u1E63e: yan \u1ECD\u0300kan l\xE1ra ${q(o.values,"|")}`;case"too_big":{let i=o.inclusive?"<=":"<",s=e(o.origin);return s?`T\xF3 p\u1ECD\u0300 j\xF9: a n\xED l\xE1ti j\u1EB9\u0301 p\xE9 ${o.origin??"iye"} ${s.verb} ${i}${o.maximum} ${s.unit}`:`T\xF3 p\u1ECD\u0300 j\xF9: a n\xED l\xE1ti j\u1EB9\u0301 ${i}${o.maximum}`}case"too_small":{let i=o.inclusive?">=":">",s=e(o.origin);return s?`K\xE9r\xE9 ju: a n\xED l\xE1ti j\u1EB9\u0301 p\xE9 ${o.origin} ${s.verb} ${i}${o.minimum} ${s.unit}`:`K\xE9r\xE9 ju: a n\xED l\xE1ti j\u1EB9\u0301 ${i}${o.minimum}`}case"invalid_format":{let i=o;return i.format==="starts_with"?`\u1ECC\u0300r\u1ECD\u0300 a\u1E63\xEC\u1E63e: gb\u1ECD\u0301d\u1ECD\u0300 b\u1EB9\u0300r\u1EB9\u0300 p\u1EB9\u0300l\xFA "${i.prefix}"`:i.format==="ends_with"?`\u1ECC\u0300r\u1ECD\u0300 a\u1E63\xEC\u1E63e: gb\u1ECD\u0301d\u1ECD\u0300 par\xED p\u1EB9\u0300l\xFA "${i.suffix}"`:i.format==="includes"?`\u1ECC\u0300r\u1ECD\u0300 a\u1E63\xEC\u1E63e: gb\u1ECD\u0301d\u1ECD\u0300 n\xED "${i.includes}"`:i.format==="regex"?`\u1ECC\u0300r\u1ECD\u0300 a\u1E63\xEC\u1E63e: gb\u1ECD\u0301d\u1ECD\u0300 b\xE1 \xE0p\u1EB9\u1EB9r\u1EB9 mu ${i.pattern}`:`A\u1E63\xEC\u1E63e: ${r[i.format]??o.format}`}case"not_multiple_of":return`N\u1ECD\u0301mb\xE0 a\u1E63\xEC\u1E63e: gb\u1ECD\u0301d\u1ECD\u0300 j\u1EB9\u0301 \xE8y\xE0 p\xEDp\xEDn ti ${o.divisor}`;case"unrecognized_keys":return`B\u1ECDt\xECn\xEC \xE0\xECm\u1ECD\u0300: ${q(o.keys,", ")}`;case"invalid_key":return`B\u1ECDt\xECn\xEC a\u1E63\xEC\u1E63e n\xEDn\xFA ${o.origin}`;case"invalid_union":return"\xCCb\xE1w\u1ECDl\xE9 a\u1E63\xEC\u1E63e";case"invalid_element":return`Iye a\u1E63\xEC\u1E63e n\xEDn\xFA ${o.origin}`;default:return"\xCCb\xE1w\u1ECDl\xE9 a\u1E63\xEC\u1E63e"}}}});var Nh={};Y(Nh,{ar:()=>VX,az:()=>WX,be:()=>YX,bg:()=>XX,ca:()=>eQ,cs:()=>rQ,da:()=>oQ,de:()=>sQ,en:()=>DR,eo:()=>cQ,es:()=>lQ,fa:()=>dQ,fi:()=>mQ,fr:()=>gQ,frCA:()=>vQ,he:()=>yQ,hu:()=>TQ,hy:()=>AQ,id:()=>RQ,is:()=>IQ,it:()=>NQ,ja:()=>$Q,ka:()=>MQ,kh:()=>LQ,km:()=>LR,ko:()=>jQ,lt:()=>qQ,mk:()=>GQ,ms:()=>HQ,nl:()=>KQ,no:()=>YQ,ota:()=>XQ,pl:()=>ree,ps:()=>eee,pt:()=>oee,ru:()=>aee,sl:()=>uee,sv:()=>pee,ta:()=>fee,th:()=>hee,tr:()=>_ee,ua:()=>See,uk:()=>UR,ur:()=>Eee,uz:()=>bee,vi:()=>Aee,yo:()=>Nee,zhCN:()=>Ree,zhTW:()=>Iee});var jR=O(()=>{HX();KX();JX();QX();tQ();nQ();iQ();aQ();iz();uQ();pQ();fQ();hQ();_Q();SQ();EQ();bQ();wQ();PQ();OQ();CQ();kQ();DQ();UQ();sz();zQ();BQ();VQ();WQ();ZQ();JQ();QQ();tee();nee();iee();cee();lee();dee();mee();gee();vee();yee();az();Tee();xee();wee();Pee();Oee();Cee()});function Ey(){return new zR}var $ee,FR,qR,zR,io,Ty=O(()=>{FR=Symbol("ZodOutput"),qR=Symbol("ZodInput"),zR=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...r){let n=r[0];return this._map.set(e,n),n&&typeof n=="object"&&"id"in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let r=this._map.get(e);return r&&typeof r=="object"&&"id"in r&&this._idmap.delete(r.id),this._map.delete(e),this}get(e){let r=e._zod.parent;if(r){let n={...this.get(r)??{}};delete n.id;let o={...n,...this._map.get(e)};return Object.keys(o).length?o:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};($ee=globalThis).__zod_globalRegistry??($ee.__zod_globalRegistry=Ey());io=globalThis.__zod_globalRegistry});function BR(t,e){return new t({type:"string",...ae(e)})}function GR(t,e){return new t({type:"string",coerce:!0,...ae(e)})}function by(t,e){return new t({type:"string",format:"email",check:"string_format",abort:!1,...ae(e)})}function Ch(t,e){return new t({type:"string",format:"guid",check:"string_format",abort:!1,...ae(e)})}function xy(t,e){return new t({type:"string",format:"uuid",check:"string_format",abort:!1,...ae(e)})}function Ay(t,e){return new t({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v4",...ae(e)})}function wy(t,e){return new t({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v6",...ae(e)})}function Ry(t,e){return new t({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v7",...ae(e)})}function $h(t,e){return new t({type:"string",format:"url",check:"string_format",abort:!1,...ae(e)})}function Py(t,e){return new t({type:"string",format:"emoji",check:"string_format",abort:!1,...ae(e)})}function Iy(t,e){return new t({type:"string",format:"nanoid",check:"string_format",abort:!1,...ae(e)})}function Oy(t,e){return new t({type:"string",format:"cuid",check:"string_format",abort:!1,...ae(e)})}function Ny(t,e){return new t({type:"string",format:"cuid2",check:"string_format",abort:!1,...ae(e)})}function Cy(t,e){return new t({type:"string",format:"ulid",check:"string_format",abort:!1,...ae(e)})}function $y(t,e){return new t({type:"string",format:"xid",check:"string_format",abort:!1,...ae(e)})}function ky(t,e){return new t({type:"string",format:"ksuid",check:"string_format",abort:!1,...ae(e)})}function My(t,e){return new t({type:"string",format:"ipv4",check:"string_format",abort:!1,...ae(e)})}function Dy(t,e){return new t({type:"string",format:"ipv6",check:"string_format",abort:!1,...ae(e)})}function VR(t,e){return new t({type:"string",format:"mac",check:"string_format",abort:!1,...ae(e)})}function Ly(t,e){return new t({type:"string",format:"cidrv4",check:"string_format",abort:!1,...ae(e)})}function Uy(t,e){return new t({type:"string",format:"cidrv6",check:"string_format",abort:!1,...ae(e)})}function jy(t,e){return new t({type:"string",format:"base64",check:"string_format",abort:!1,...ae(e)})}function zy(t,e){return new t({type:"string",format:"base64url",check:"string_format",abort:!1,...ae(e)})}function Fy(t,e){return new t({type:"string",format:"e164",check:"string_format",abort:!1,...ae(e)})}function qy(t,e){return new t({type:"string",format:"jwt",check:"string_format",abort:!1,...ae(e)})}function WR(t,e){return new t({type:"string",format:"datetime",check:"string_format",offset:!1,local:!1,precision:null,...ae(e)})}function KR(t,e){return new t({type:"string",format:"date",check:"string_format",...ae(e)})}function ZR(t,e){return new t({type:"string",format:"time",check:"string_format",precision:null,...ae(e)})}function YR(t,e){return new t({type:"string",format:"duration",check:"string_format",...ae(e)})}function JR(t,e){return new t({type:"number",checks:[],...ae(e)})}function XR(t,e){return new t({type:"number",coerce:!0,checks:[],...ae(e)})}function QR(t,e){return new t({type:"number",check:"number_format",abort:!1,format:"safeint",...ae(e)})}function eP(t,e){return new t({type:"number",check:"number_format",abort:!1,format:"float32",...ae(e)})}function tP(t,e){return new t({type:"number",check:"number_format",abort:!1,format:"float64",...ae(e)})}function rP(t,e){return new t({type:"number",check:"number_format",abort:!1,format:"int32",...ae(e)})}function nP(t,e){return new t({type:"number",check:"number_format",abort:!1,format:"uint32",...ae(e)})}function oP(t,e){return new t({type:"boolean",...ae(e)})}function iP(t,e){return new t({type:"boolean",coerce:!0,...ae(e)})}function sP(t,e){return new t({type:"bigint",...ae(e)})}function aP(t,e){return new t({type:"bigint",coerce:!0,...ae(e)})}function cP(t,e){return new t({type:"bigint",check:"bigint_format",abort:!1,format:"int64",...ae(e)})}function uP(t,e){return new t({type:"bigint",check:"bigint_format",abort:!1,format:"uint64",...ae(e)})}function lP(t,e){return new t({type:"symbol",...ae(e)})}function pP(t,e){return new t({type:"undefined",...ae(e)})}function dP(t,e){return new t({type:"null",...ae(e)})}function fP(t){return new t({type:"any"})}function mP(t){return new t({type:"unknown"})}function hP(t,e){return new t({type:"never",...ae(e)})}function gP(t,e){return new t({type:"void",...ae(e)})}function _P(t,e){return new t({type:"date",...ae(e)})}function vP(t,e){return new t({type:"date",coerce:!0,...ae(e)})}function SP(t,e){return new t({type:"nan",...ae(e)})}function Va(t,e){return new Ew({check:"less_than",...ae(e),value:t,inclusive:!1})}function mi(t,e){return new Ew({check:"less_than",...ae(e),value:t,inclusive:!0})}function Ha(t,e){return new Tw({check:"greater_than",...ae(e),value:t,inclusive:!1})}function To(t,e){return new Tw({check:"greater_than",...ae(e),value:t,inclusive:!0})}function By(t){return Ha(0,t)}function Gy(t){return Va(0,t)}function Vy(t){return mi(0,t)}function Hy(t){return To(0,t)}function Ku(t,e){return new kj({check:"multiple_of",...ae(e),value:t})}function Zu(t,e){return new Lj({check:"max_size",...ae(e),maximum:t})}function Wa(t,e){return new Uj({check:"min_size",...ae(e),minimum:t})}function up(t,e){return new jj({check:"size_equals",...ae(e),size:t})}function lp(t,e){return new zj({check:"max_length",...ae(e),maximum:t})}function Dc(t,e){return new Fj({check:"min_length",...ae(e),minimum:t})}function pp(t,e){return new qj({check:"length_equals",...ae(e),length:t})}function lf(t,e){return new Bj({check:"string_format",format:"regex",...ae(e),pattern:t})}function pf(t){return new Gj({check:"string_format",format:"lowercase",...ae(t)})}function df(t){return new Vj({check:"string_format",format:"uppercase",...ae(t)})}function ff(t,e){return new Hj({check:"string_format",format:"includes",...ae(e),includes:t})}function mf(t,e){return new Wj({check:"string_format",format:"starts_with",...ae(e),prefix:t})}function hf(t,e){return new Kj({check:"string_format",format:"ends_with",...ae(e),suffix:t})}function Wy(t,e,r){return new Zj({check:"property",property:t,schema:e,...ae(r)})}function gf(t,e){return new Yj({check:"mime_type",mime:t,...ae(e)})}function Hs(t){return new Jj({check:"overwrite",tx:t})}function _f(t){return Hs(e=>e.normalize(t))}function vf(){return Hs(t=>t.trim())}function Sf(){return Hs(t=>t.toLowerCase())}function yf(){return Hs(t=>t.toUpperCase())}function kh(){return Hs(t=>JU(t))}function cz(t,e,r){return new t({type:"array",element:e,...ae(r)})}function rYe(t,e,r){return new t({type:"union",options:e,...ae(r)})}function nYe(t,e,r){return new t({type:"union",options:e,inclusive:!1,...ae(r)})}function oYe(t,e,r,n){return new t({type:"union",options:r,discriminator:e,...ae(n)})}function iYe(t,e,r){return new t({type:"intersection",left:e,right:r})}function sYe(t,e,r,n){let o=r instanceof nt,i=o?n:r,s=o?r:null;return new t({type:"tuple",items:e,rest:s,...ae(i)})}function aYe(t,e,r,n){return new t({type:"record",keyType:e,valueType:r,...ae(n)})}function cYe(t,e,r,n){return new t({type:"map",keyType:e,valueType:r,...ae(n)})}function uYe(t,e,r){return new t({type:"set",valueType:e,...ae(r)})}function lYe(t,e,r){let n=Array.isArray(e)?Object.fromEntries(e.map(o=>[o,o])):e;return new t({type:"enum",entries:n,...ae(r)})}function pYe(t,e,r){return new t({type:"enum",entries:e,...ae(r)})}function dYe(t,e,r){return new t({type:"literal",values:Array.isArray(e)?e:[e],...ae(r)})}function yP(t,e){return new t({type:"file",...ae(e)})}function fYe(t,e){return new t({type:"transform",transform:e})}function mYe(t,e){return new t({type:"optional",innerType:e})}function hYe(t,e){return new t({type:"nullable",innerType:e})}function gYe(t,e,r){return new t({type:"default",innerType:e,get defaultValue(){return typeof r=="function"?r():QU(r)}})}function _Ye(t,e,r){return new t({type:"nonoptional",innerType:e,...ae(r)})}function vYe(t,e){return new t({type:"success",innerType:e})}function SYe(t,e,r){return new t({type:"catch",innerType:e,catchValue:typeof r=="function"?r:()=>r})}function yYe(t,e,r){return new t({type:"pipe",in:e,out:r})}function EYe(t,e){return new t({type:"readonly",innerType:e})}function TYe(t,e,r){return new t({type:"template_literal",parts:e,...ae(r)})}function bYe(t,e){return new t({type:"lazy",getter:e})}function xYe(t,e){return new t({type:"promise",innerType:e})}function EP(t,e,r){let n=ae(r);return n.abort??(n.abort=!0),new t({type:"custom",check:"custom",fn:e,...n})}function Ky(t,e,r){return new t({type:"custom",check:"custom",fn:e,...ae(r)})}function TP(t){let e=kee(r=>(r.addIssue=n=>{if(typeof n=="string")r.issues.push(Sh(n,r.value,e._zod.def));else{let o=n;o.fatal&&(o.continue=!1),o.code??(o.code="custom"),o.input??(o.input=r.value),o.inst??(o.inst=e),o.continue??(o.continue=!e._zod.def.abort),r.issues.push(Sh(o))}},t(r.value,r)));return e}function kee(t,e){let r=new wr({check:"custom",...ae(e)});return r._zod.check=t,r}function bP(t){let e=new wr({check:"describe"});return e._zod.onattach=[r=>{let n=io.get(r)??{};io.add(r,{...n,description:t})}],e._zod.check=()=>{},e}function xP(t){let e=new wr({check:"meta"});return e._zod.onattach=[r=>{let n=io.get(r)??{};io.add(r,{...n,...t})}],e._zod.check=()=>{},e}function AP(t,e){let r=ae(e),n=r.truthy??["true","1","yes","on","y","enabled"],o=r.falsy??["false","0","no","off","n","disabled"];r.case!=="sensitive"&&(n=n.map(h=>typeof h=="string"?h.toLowerCase():h),o=o.map(h=>typeof h=="string"?h.toLowerCase():h));let i=new Set(n),s=new Set(o),a=t.Codec??Ih,c=t.Boolean??Rh,u=t.String??cp,p=new u({type:"string",error:r.error}),f=new c({type:"boolean",error:r.error}),m=new a({type:"pipe",in:p,out:f,transform:((h,_)=>{let v=h;return r.case!=="sensitive"&&(v=v.toLowerCase()),i.has(v)?!0:s.has(v)?!1:(_.issues.push({code:"invalid_value",expected:"stringbool",values:[...i,...s],input:_.value,inst:m,continue:!1}),{})}),reverseTransform:((h,_)=>h===!0?n[0]||"true":o[0]||"false"),error:r.error});return m}function Ef(t,e,r,n={}){let o=ae(n),i={...ae(n),check:"string_format",type:"string",format:e,fn:typeof r=="function"?r:a=>r.test(a),...o};return r instanceof RegExp&&(i.pattern=r),new t(i)}var HR,Mee=O(()=>{bw();Ty();oz();je();HR={Any:null,Minute:-1,Second:0,Millisecond:3,Microsecond:6}});function dp(t){let e=t?.target??"draft-2020-12";return e==="draft-4"&&(e="draft-04"),e==="draft-7"&&(e="draft-07"),{processors:t.processors??{},metadataRegistry:t?.metadata??io,target:e,unrepresentable:t?.unrepresentable??"throw",override:t?.override??(()=>{}),io:t?.io??"output",counter:0,seen:new Map,cycles:t?.cycles??"ref",reused:t?.reused??"inline",external:t?.external??void 0}}function Tr(t,e,r={path:[],schemaPath:[]}){var n;let o=t._zod.def,i=e.seen.get(t);if(i)return i.count++,r.schemaPath.includes(t)&&(i.cycle=r.path),i.schema;let s={schema:{},count:1,cycle:void 0,path:r.path};e.seen.set(t,s);let a=t._zod.toJSONSchema?.();if(a)s.schema=a;else{let p={...r,schemaPath:[...r.schemaPath,t],path:r.path};if(t._zod.processJSONSchema)t._zod.processJSONSchema(e,s.schema,p);else{let m=s.schema,h=e.processors[o.type];if(!h)throw new Error(`[toJSONSchema]: Non-representable type encountered: ${o.type}`);h(t,e,m,p)}let f=t._zod.parent;f&&(s.ref||(s.ref=f),Tr(f,e,p),e.seen.get(f).isParent=!0)}let c=e.metadataRegistry.get(t);return c&&Object.assign(s.schema,c),e.io==="input"&&hi(t)&&(delete s.schema.examples,delete s.schema.default),e.io==="input"&&s.schema._prefault&&((n=s.schema).default??(n.default=s.schema._prefault)),delete s.schema._prefault,e.seen.get(t).schema}function fp(t,e){let r=t.seen.get(e);if(!r)throw new Error("Unprocessed schema. This is a bug in Zod.");let n=new Map;for(let s of t.seen.entries()){let a=t.metadataRegistry.get(s[0])?.id;if(a){let c=n.get(a);if(c&&c!==s[0])throw new Error(`Duplicate schema id "${a}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);n.set(a,s[0])}}let o=s=>{let a=t.target==="draft-2020-12"?"$defs":"definitions";if(t.external){let f=t.external.registry.get(s[0])?.id,m=t.external.uri??(_=>_);if(f)return{ref:m(f)};let h=s[1].defId??s[1].schema.id??`schema${t.counter++}`;return s[1].defId=h,{defId:h,ref:`${m("__shared")}#/${a}/${h}`}}if(s[1]===r)return{ref:"#"};let u=`#/${a}/`,p=s[1].schema.id??`__schema${t.counter++}`;return{defId:p,ref:u+p}},i=s=>{if(s[1].schema.$ref)return;let a=s[1],{ref:c,defId:u}=o(s);a.def={...a.schema},u&&(a.defId=u);let p=a.schema;for(let f in p)delete p[f];p.$ref=c};if(t.cycles==="throw")for(let s of t.seen.entries()){let a=s[1];if(a.cycle)throw new Error(`Cycle detected: #/${a.cycle?.join("/")}/ - -Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`)}for(let s of t.seen.entries()){let a=s[1];if(e===s[0]){i(s);continue}if(t.external){let u=t.external.registry.get(s[0])?.id;if(e!==s[0]&&u){i(s);continue}}if(t.metadataRegistry.get(s[0])?.id){i(s);continue}if(a.cycle){i(s);continue}if(a.count>1&&t.reused==="ref"){i(s);continue}}}function mp(t,e){let r=t.seen.get(e);if(!r)throw new Error("Unprocessed schema. This is a bug in Zod.");let n=s=>{let a=t.seen.get(s);if(a.ref===null)return;let c=a.def??a.schema,u={...c},p=a.ref;if(a.ref=null,p){n(p);let m=t.seen.get(p),h=m.schema;if(h.$ref&&(t.target==="draft-07"||t.target==="draft-04"||t.target==="openapi-3.0")?(c.allOf=c.allOf??[],c.allOf.push(h)):Object.assign(c,h),Object.assign(c,u),s._zod.parent===p)for(let v in c)v==="$ref"||v==="allOf"||v in u||delete c[v];if(h.$ref)for(let v in c)v==="$ref"||v==="allOf"||v in m.def&&JSON.stringify(c[v])===JSON.stringify(m.def[v])&&delete c[v]}let f=s._zod.parent;if(f&&f!==p){n(f);let m=t.seen.get(f);if(m?.schema.$ref&&(c.$ref=m.schema.$ref,m.def))for(let h in c)h==="$ref"||h==="allOf"||h in m.def&&JSON.stringify(c[h])===JSON.stringify(m.def[h])&&delete c[h]}t.override({zodSchema:s,jsonSchema:c,path:a.path??[]})};for(let s of[...t.seen.entries()].reverse())n(s[0]);let o={};if(t.target==="draft-2020-12"?o.$schema="https://json-schema.org/draft/2020-12/schema":t.target==="draft-07"?o.$schema="http://json-schema.org/draft-07/schema#":t.target==="draft-04"?o.$schema="http://json-schema.org/draft-04/schema#":t.target,t.external?.uri){let s=t.external.registry.get(e)?.id;if(!s)throw new Error("Schema is missing an `id` property");o.$id=t.external.uri(s)}Object.assign(o,r.def??r.schema);let i=t.external?.defs??{};for(let s of t.seen.entries()){let a=s[1];a.def&&a.defId&&(i[a.defId]=a.def)}t.external||Object.keys(i).length>0&&(t.target==="draft-2020-12"?o.$defs=i:o.definitions=i);try{let s=JSON.parse(JSON.stringify(o));return Object.defineProperty(s,"~standard",{value:{...e["~standard"],jsonSchema:{input:Mh(e,"input",t.processors),output:Mh(e,"output",t.processors)}},enumerable:!1,writable:!1}),s}catch{throw new Error("Error converting schema to JSON.")}}function hi(t,e){let r=e??{seen:new Set};if(r.seen.has(t))return!1;r.seen.add(t);let n=t._zod.def;if(n.type==="transform")return!0;if(n.type==="array")return hi(n.element,r);if(n.type==="set")return hi(n.valueType,r);if(n.type==="lazy")return hi(n.getter(),r);if(n.type==="promise"||n.type==="optional"||n.type==="nonoptional"||n.type==="nullable"||n.type==="readonly"||n.type==="default"||n.type==="prefault")return hi(n.innerType,r);if(n.type==="intersection")return hi(n.left,r)||hi(n.right,r);if(n.type==="record"||n.type==="map")return hi(n.keyType,r)||hi(n.valueType,r);if(n.type==="pipe")return hi(n.in,r)||hi(n.out,r);if(n.type==="object"){for(let o in n.shape)if(hi(n.shape[o],r))return!0;return!1}if(n.type==="union"){for(let o of n.options)if(hi(o,r))return!0;return!1}if(n.type==="tuple"){for(let o of n.items)if(hi(o,r))return!0;return!!(n.rest&&hi(n.rest,r))}return!1}var uz,Mh,Zy=O(()=>{Ty();uz=(t,e={})=>r=>{let n=dp({...r,processors:e});return Tr(t,n),fp(n,t),mp(n,t)},Mh=(t,e,r={})=>n=>{let{libraryOptions:o,target:i}=n??{},s=dp({...o??{},target:i,io:e,processors:r});return Tr(t,s),fp(s,t),mp(s,t)}});function gi(t,e){if("_idmap"in t){let n=t,o=dp({...e,processors:wP}),i={};for(let c of n._idmap.entries()){let[u,p]=c;Tr(p,o)}let s={},a={registry:n,uri:e?.uri,defs:i};o.external=a;for(let c of n._idmap.entries()){let[u,p]=c;fp(o,p),s[u]=mp(o,p)}if(Object.keys(i).length>0){let c=o.target==="draft-2020-12"?"$defs":"definitions";s.__shared={[c]:i}}return{schemas:s}}let r=dp({...e,processors:wP});return Tr(t,r),fp(r,t),mp(r,t)}var AYe,lz,pz,dz,fz,mz,hz,gz,_z,vz,Sz,yz,Ez,Tz,bz,xz,Az,wz,Rz,Pz,Iz,Oz,Nz,Cz,$z,kz,RP,Mz,Dz,Lz,Uz,jz,zz,Fz,qz,Bz,Gz,Vz,PP,Hz,wP,Dh=O(()=>{Zy();je();AYe={guid:"uuid",url:"uri",datetime:"date-time",json_string:"json-string",regex:""},lz=(t,e,r,n)=>{let o=r;o.type="string";let{minimum:i,maximum:s,format:a,patterns:c,contentEncoding:u}=t._zod.bag;if(typeof i=="number"&&(o.minLength=i),typeof s=="number"&&(o.maxLength=s),a&&(o.format=AYe[a]??a,o.format===""&&delete o.format,a==="time"&&delete o.format),u&&(o.contentEncoding=u),c&&c.size>0){let p=[...c];p.length===1?o.pattern=p[0].source:p.length>1&&(o.allOf=[...p.map(f=>({...e.target==="draft-07"||e.target==="draft-04"||e.target==="openapi-3.0"?{type:"string"}:{},pattern:f.source}))])}},pz=(t,e,r,n)=>{let o=r,{minimum:i,maximum:s,format:a,multipleOf:c,exclusiveMaximum:u,exclusiveMinimum:p}=t._zod.bag;typeof a=="string"&&a.includes("int")?o.type="integer":o.type="number",typeof p=="number"&&(e.target==="draft-04"||e.target==="openapi-3.0"?(o.minimum=p,o.exclusiveMinimum=!0):o.exclusiveMinimum=p),typeof i=="number"&&(o.minimum=i,typeof p=="number"&&e.target!=="draft-04"&&(p>=i?delete o.minimum:delete o.exclusiveMinimum)),typeof u=="number"&&(e.target==="draft-04"||e.target==="openapi-3.0"?(o.maximum=u,o.exclusiveMaximum=!0):o.exclusiveMaximum=u),typeof s=="number"&&(o.maximum=s,typeof u=="number"&&e.target!=="draft-04"&&(u<=s?delete o.maximum:delete o.exclusiveMaximum)),typeof c=="number"&&(o.multipleOf=c)},dz=(t,e,r,n)=>{r.type="boolean"},fz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("BigInt cannot be represented in JSON Schema")},mz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Symbols cannot be represented in JSON Schema")},hz=(t,e,r,n)=>{e.target==="openapi-3.0"?(r.type="string",r.nullable=!0,r.enum=[null]):r.type="null"},gz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Undefined cannot be represented in JSON Schema")},_z=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Void cannot be represented in JSON Schema")},vz=(t,e,r,n)=>{r.not={}},Sz=(t,e,r,n)=>{},yz=(t,e,r,n)=>{},Ez=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Date cannot be represented in JSON Schema")},Tz=(t,e,r,n)=>{let o=t._zod.def,i=gh(o.entries);i.every(s=>typeof s=="number")&&(r.type="number"),i.every(s=>typeof s=="string")&&(r.type="string"),r.enum=i},bz=(t,e,r,n)=>{let o=t._zod.def,i=[];for(let s of o.values)if(s===void 0){if(e.unrepresentable==="throw")throw new Error("Literal `undefined` cannot be represented in JSON Schema")}else if(typeof s=="bigint"){if(e.unrepresentable==="throw")throw new Error("BigInt literals cannot be represented in JSON Schema");i.push(Number(s))}else i.push(s);if(i.length!==0)if(i.length===1){let s=i[0];r.type=s===null?"null":typeof s,e.target==="draft-04"||e.target==="openapi-3.0"?r.enum=[s]:r.const=s}else i.every(s=>typeof s=="number")&&(r.type="number"),i.every(s=>typeof s=="string")&&(r.type="string"),i.every(s=>typeof s=="boolean")&&(r.type="boolean"),i.every(s=>s===null)&&(r.type="null"),r.enum=i},xz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("NaN cannot be represented in JSON Schema")},Az=(t,e,r,n)=>{let o=r,i=t._zod.pattern;if(!i)throw new Error("Pattern not found in template literal");o.type="string",o.pattern=i.source},wz=(t,e,r,n)=>{let o=r,i={type:"string",format:"binary",contentEncoding:"binary"},{minimum:s,maximum:a,mime:c}=t._zod.bag;s!==void 0&&(i.minLength=s),a!==void 0&&(i.maxLength=a),c?c.length===1?(i.contentMediaType=c[0],Object.assign(o,i)):(Object.assign(o,i),o.anyOf=c.map(u=>({contentMediaType:u}))):Object.assign(o,i)},Rz=(t,e,r,n)=>{r.type="boolean"},Pz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Custom types cannot be represented in JSON Schema")},Iz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Function types cannot be represented in JSON Schema")},Oz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Transforms cannot be represented in JSON Schema")},Nz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Map cannot be represented in JSON Schema")},Cz=(t,e,r,n)=>{if(e.unrepresentable==="throw")throw new Error("Set cannot be represented in JSON Schema")},$z=(t,e,r,n)=>{let o=r,i=t._zod.def,{minimum:s,maximum:a}=t._zod.bag;typeof s=="number"&&(o.minItems=s),typeof a=="number"&&(o.maxItems=a),o.type="array",o.items=Tr(i.element,e,{...n,path:[...n.path,"items"]})},kz=(t,e,r,n)=>{let o=r,i=t._zod.def;o.type="object",o.properties={};let s=i.shape;for(let u in s)o.properties[u]=Tr(s[u],e,{...n,path:[...n.path,"properties",u]});let a=new Set(Object.keys(s)),c=new Set([...a].filter(u=>{let p=i.shape[u]._zod;return e.io==="input"?p.optin===void 0:p.optout===void 0}));c.size>0&&(o.required=Array.from(c)),i.catchall?._zod.def.type==="never"?o.additionalProperties=!1:i.catchall?i.catchall&&(o.additionalProperties=Tr(i.catchall,e,{...n,path:[...n.path,"additionalProperties"]})):e.io==="output"&&(o.additionalProperties=!1)},RP=(t,e,r,n)=>{let o=t._zod.def,i=o.inclusive===!1,s=o.options.map((a,c)=>Tr(a,e,{...n,path:[...n.path,i?"oneOf":"anyOf",c]}));i?r.oneOf=s:r.anyOf=s},Mz=(t,e,r,n)=>{let o=t._zod.def,i=Tr(o.left,e,{...n,path:[...n.path,"allOf",0]}),s=Tr(o.right,e,{...n,path:[...n.path,"allOf",1]}),a=u=>"allOf"in u&&Object.keys(u).length===1,c=[...a(i)?i.allOf:[i],...a(s)?s.allOf:[s]];r.allOf=c},Dz=(t,e,r,n)=>{let o=r,i=t._zod.def;o.type="array";let s=e.target==="draft-2020-12"?"prefixItems":"items",a=e.target==="draft-2020-12"||e.target==="openapi-3.0"?"items":"additionalItems",c=i.items.map((m,h)=>Tr(m,e,{...n,path:[...n.path,s,h]})),u=i.rest?Tr(i.rest,e,{...n,path:[...n.path,a,...e.target==="openapi-3.0"?[i.items.length]:[]]}):null;e.target==="draft-2020-12"?(o.prefixItems=c,u&&(o.items=u)):e.target==="openapi-3.0"?(o.items={anyOf:c},u&&o.items.anyOf.push(u),o.minItems=c.length,u||(o.maxItems=c.length)):(o.items=c,u&&(o.additionalItems=u));let{minimum:p,maximum:f}=t._zod.bag;typeof p=="number"&&(o.minItems=p),typeof f=="number"&&(o.maxItems=f)},Lz=(t,e,r,n)=>{let o=r,i=t._zod.def;o.type="object",(e.target==="draft-07"||e.target==="draft-2020-12")&&(o.propertyNames=Tr(i.keyType,e,{...n,path:[...n.path,"propertyNames"]})),o.additionalProperties=Tr(i.valueType,e,{...n,path:[...n.path,"additionalProperties"]});let s=i.keyType._zod.def;if(s.type==="enum"){let c=gh(s.entries).filter(u=>typeof u=="string"||typeof u=="number");c.length>0&&(o.required=c)}},Uz=(t,e,r,n)=>{let o=t._zod.def,i=Tr(o.innerType,e,n),s=e.seen.get(t);e.target==="openapi-3.0"?(s.ref=o.innerType,r.nullable=!0):r.anyOf=[i,{type:"null"}]},jz=(t,e,r,n)=>{let o=t._zod.def;Tr(o.innerType,e,n);let i=e.seen.get(t);i.ref=o.innerType},zz=(t,e,r,n)=>{let o=t._zod.def;Tr(o.innerType,e,n);let i=e.seen.get(t);i.ref=o.innerType,r.default=JSON.parse(JSON.stringify(o.defaultValue))},Fz=(t,e,r,n)=>{let o=t._zod.def;Tr(o.innerType,e,n);let i=e.seen.get(t);i.ref=o.innerType,e.io==="input"&&(r._prefault=JSON.parse(JSON.stringify(o.defaultValue)))},qz=(t,e,r,n)=>{let o=t._zod.def;Tr(o.innerType,e,n);let i=e.seen.get(t);i.ref=o.innerType;let s;try{s=o.catchValue(void 0)}catch{throw new Error("Dynamic catch values are not supported in JSON Schema")}r.default=s},Bz=(t,e,r,n)=>{let o=t._zod.def,i=e.io==="input"?o.in._zod.def.type==="transform"?o.out:o.in:o.out;Tr(i,e,n);let s=e.seen.get(t);s.ref=i},Gz=(t,e,r,n)=>{let o=t._zod.def;Tr(o.innerType,e,n);let i=e.seen.get(t);i.ref=o.innerType,r.readOnly=!0},Vz=(t,e,r,n)=>{let o=t._zod.def;Tr(o.innerType,e,n);let i=e.seen.get(t);i.ref=o.innerType},PP=(t,e,r,n)=>{let o=t._zod.def;Tr(o.innerType,e,n);let i=e.seen.get(t);i.ref=o.innerType},Hz=(t,e,r,n)=>{let o=t._zod.innerType;Tr(o,e,n);let i=e.seen.get(t);i.ref=o},wP={string:lz,number:pz,boolean:dz,bigint:fz,symbol:mz,null:hz,undefined:gz,void:_z,never:vz,any:Sz,unknown:yz,date:Ez,enum:Tz,literal:bz,nan:xz,template_literal:Az,file:wz,success:Rz,custom:Pz,function:Iz,transform:Oz,map:Nz,set:Cz,array:$z,object:kz,union:RP,intersection:Mz,tuple:Dz,record:Lz,nullable:Uz,nonoptional:jz,default:zz,prefault:Fz,catch:qz,pipe:Bz,readonly:Gz,promise:Vz,optional:PP,lazy:Hz}});var IP,Dee=O(()=>{Dh();Zy();IP=class{get metadataRegistry(){return this.ctx.metadataRegistry}get target(){return this.ctx.target}get unrepresentable(){return this.ctx.unrepresentable}get override(){return this.ctx.override}get io(){return this.ctx.io}get counter(){return this.ctx.counter}set counter(e){this.ctx.counter=e}get seen(){return this.ctx.seen}constructor(e){let r=e?.target??"draft-2020-12";r==="draft-4"&&(r="draft-04"),r==="draft-7"&&(r="draft-07"),this.ctx=dp({processors:wP,target:r,...e?.metadata&&{metadata:e.metadata},...e?.unrepresentable&&{unrepresentable:e.unrepresentable},...e?.override&&{override:e.override},...e?.io&&{io:e.io}})}process(e,r={path:[],schemaPath:[]}){return Tr(e,this.ctx,r)}emit(e,r){r&&(r.cycles&&(this.ctx.cycles=r.cycles),r.reused&&(this.ctx.reused=r.reused),r.external&&(this.ctx.external=r.external)),fp(this.ctx,e);let n=mp(this.ctx,e),{"~standard":o,...i}=n;return i}}});var Lee={};var Uee=O(()=>{});var fs={};Y(fs,{$ZodAny:()=>iR,$ZodArray:()=>lR,$ZodAsyncError:()=>Ga,$ZodBase64:()=>Zw,$ZodBase64URL:()=>Yw,$ZodBigInt:()=>gy,$ZodBigIntFormat:()=>tR,$ZodBoolean:()=>Rh,$ZodCIDRv4:()=>Ww,$ZodCIDRv6:()=>Kw,$ZodCUID:()=>Mw,$ZodCUID2:()=>Dw,$ZodCatch:()=>PR,$ZodCheck:()=>wr,$ZodCheckBigIntFormat:()=>Dj,$ZodCheckEndsWith:()=>Kj,$ZodCheckGreaterThan:()=>Tw,$ZodCheckIncludes:()=>Hj,$ZodCheckLengthEquals:()=>qj,$ZodCheckLessThan:()=>Ew,$ZodCheckLowerCase:()=>Gj,$ZodCheckMaxLength:()=>zj,$ZodCheckMaxSize:()=>Lj,$ZodCheckMimeType:()=>Yj,$ZodCheckMinLength:()=>Fj,$ZodCheckMinSize:()=>Uj,$ZodCheckMultipleOf:()=>kj,$ZodCheckNumberFormat:()=>Mj,$ZodCheckOverwrite:()=>Jj,$ZodCheckProperty:()=>Zj,$ZodCheckRegex:()=>Bj,$ZodCheckSizeEquals:()=>jj,$ZodCheckStartsWith:()=>Wj,$ZodCheckStringFormat:()=>wh,$ZodCheckUpperCase:()=>Vj,$ZodCodec:()=>Ih,$ZodCustom:()=>Sy,$ZodCustomStringFormat:()=>Qw,$ZodDate:()=>uR,$ZodDefault:()=>xR,$ZodDiscriminatedUnion:()=>fR,$ZodE164:()=>Jw,$ZodEmail:()=>Nw,$ZodEmoji:()=>$w,$ZodEncodeError:()=>tp,$ZodEnum:()=>vR,$ZodError:()=>py,$ZodExactOptional:()=>TR,$ZodFile:()=>yR,$ZodFunction:()=>$R,$ZodGUID:()=>Iw,$ZodIPv4:()=>Gw,$ZodIPv6:()=>Vw,$ZodISODate:()=>Fw,$ZodISODateTime:()=>zw,$ZodISODuration:()=>Bw,$ZodISOTime:()=>qw,$ZodIntersection:()=>mR,$ZodJWT:()=>Xw,$ZodKSUID:()=>jw,$ZodLazy:()=>MR,$ZodLiteral:()=>SR,$ZodMAC:()=>Hw,$ZodMap:()=>gR,$ZodNaN:()=>IR,$ZodNanoID:()=>kw,$ZodNever:()=>aR,$ZodNonOptional:()=>wR,$ZodNull:()=>oR,$ZodNullable:()=>bR,$ZodNumber:()=>hy,$ZodNumberFormat:()=>eR,$ZodObject:()=>pR,$ZodObjectJIT:()=>nz,$ZodOptional:()=>vy,$ZodPipe:()=>OR,$ZodPrefault:()=>AR,$ZodPromise:()=>kR,$ZodReadonly:()=>NR,$ZodRealError:()=>ji,$ZodRecord:()=>hR,$ZodRegistry:()=>zR,$ZodSet:()=>_R,$ZodString:()=>cp,$ZodStringFormat:()=>Er,$ZodSuccess:()=>RR,$ZodSymbol:()=>rR,$ZodTemplateLiteral:()=>CR,$ZodTransform:()=>ER,$ZodTuple:()=>_y,$ZodType:()=>nt,$ZodULID:()=>Lw,$ZodURL:()=>Cw,$ZodUUID:()=>Ow,$ZodUndefined:()=>nR,$ZodUnion:()=>Ph,$ZodUnknown:()=>sR,$ZodVoid:()=>cR,$ZodXID:()=>Uw,$ZodXor:()=>dR,$brand:()=>iy,$constructor:()=>D,$input:()=>qR,$output:()=>FR,Doc:()=>my,JSONSchema:()=>Lee,JSONSchemaGenerator:()=>IP,NEVER:()=>mh,TimePrecision:()=>HR,_any:()=>fP,_array:()=>cz,_base64:()=>jy,_base64url:()=>zy,_bigint:()=>sP,_boolean:()=>oP,_catch:()=>SYe,_check:()=>kee,_cidrv4:()=>Ly,_cidrv6:()=>Uy,_coercedBigint:()=>aP,_coercedBoolean:()=>iP,_coercedDate:()=>vP,_coercedNumber:()=>XR,_coercedString:()=>GR,_cuid:()=>Oy,_cuid2:()=>Ny,_custom:()=>EP,_date:()=>_P,_decode:()=>pw,_decodeAsync:()=>fw,_default:()=>gYe,_discriminatedUnion:()=>oYe,_e164:()=>Fy,_email:()=>by,_emoji:()=>Py,_encode:()=>lw,_encodeAsync:()=>dw,_endsWith:()=>hf,_enum:()=>lYe,_file:()=>yP,_float32:()=>eP,_float64:()=>tP,_gt:()=>Ha,_gte:()=>To,_guid:()=>Ch,_includes:()=>ff,_int:()=>QR,_int32:()=>rP,_int64:()=>cP,_intersection:()=>iYe,_ipv4:()=>My,_ipv6:()=>Dy,_isoDate:()=>KR,_isoDateTime:()=>WR,_isoDuration:()=>YR,_isoTime:()=>ZR,_jwt:()=>qy,_ksuid:()=>ky,_lazy:()=>bYe,_length:()=>pp,_literal:()=>dYe,_lowercase:()=>pf,_lt:()=>Va,_lte:()=>mi,_mac:()=>VR,_map:()=>cYe,_max:()=>mi,_maxLength:()=>lp,_maxSize:()=>Zu,_mime:()=>gf,_min:()=>To,_minLength:()=>Dc,_minSize:()=>Wa,_multipleOf:()=>Ku,_nan:()=>SP,_nanoid:()=>Iy,_nativeEnum:()=>pYe,_negative:()=>Gy,_never:()=>hP,_nonnegative:()=>Hy,_nonoptional:()=>_Ye,_nonpositive:()=>Vy,_normalize:()=>_f,_null:()=>dP,_nullable:()=>hYe,_number:()=>JR,_optional:()=>mYe,_overwrite:()=>Hs,_parse:()=>Th,_parseAsync:()=>bh,_pipe:()=>yYe,_positive:()=>By,_promise:()=>xYe,_property:()=>Wy,_readonly:()=>EYe,_record:()=>aYe,_refine:()=>Ky,_regex:()=>lf,_safeDecode:()=>hw,_safeDecodeAsync:()=>_w,_safeEncode:()=>mw,_safeEncodeAsync:()=>gw,_safeParse:()=>xh,_safeParseAsync:()=>Ah,_set:()=>uYe,_size:()=>up,_slugify:()=>kh,_startsWith:()=>mf,_string:()=>BR,_stringFormat:()=>Ef,_stringbool:()=>AP,_success:()=>vYe,_superRefine:()=>TP,_symbol:()=>lP,_templateLiteral:()=>TYe,_toLowerCase:()=>Sf,_toUpperCase:()=>yf,_transform:()=>fYe,_trim:()=>vf,_tuple:()=>sYe,_uint32:()=>nP,_uint64:()=>uP,_ulid:()=>Cy,_undefined:()=>pP,_union:()=>rYe,_unknown:()=>mP,_uppercase:()=>df,_url:()=>$h,_uuid:()=>xy,_uuidv4:()=>Ay,_uuidv6:()=>wy,_uuidv7:()=>Ry,_void:()=>gP,_xid:()=>$y,_xor:()=>nYe,clone:()=>oo,config:()=>cn,createStandardJSONSchemaMethod:()=>Mh,createToJSONSchemaMethod:()=>uz,decode:()=>_X,decodeAsync:()=>SX,describe:()=>bP,encode:()=>gX,encodeAsync:()=>vX,extractDefs:()=>fp,finalize:()=>mp,flattenError:()=>yh,formatError:()=>Eh,globalConfig:()=>oy,globalRegistry:()=>io,initializeContext:()=>dp,isValidBase64:()=>rz,isValidBase64URL:()=>FX,isValidJWT:()=>qX,locales:()=>Nh,meta:()=>xP,parse:()=>af,parseAsync:()=>cf,prettifyError:()=>uw,process:()=>Tr,regexes:()=>zi,registry:()=>Ey,safeDecode:()=>EX,safeDecodeAsync:()=>bX,safeEncode:()=>yX,safeEncodeAsync:()=>TX,safeParse:()=>sp,safeParseAsync:()=>ap,toDotPath:()=>hX,toJSONSchema:()=>gi,treeifyError:()=>cw,util:()=>te,version:()=>Qj});var mn=O(()=>{hh();ij();oj();oz();bw();ez();je();yw();jR();Ty();Xj();Mee();Zy();Dh();Dee();Uee()});var OP={};Y(OP,{endsWith:()=>hf,gt:()=>Ha,gte:()=>To,includes:()=>ff,length:()=>pp,lowercase:()=>pf,lt:()=>Va,lte:()=>mi,maxLength:()=>lp,maxSize:()=>Zu,mime:()=>gf,minLength:()=>Dc,minSize:()=>Wa,multipleOf:()=>Ku,negative:()=>Gy,nonnegative:()=>Hy,nonpositive:()=>Vy,normalize:()=>_f,overwrite:()=>Hs,positive:()=>By,property:()=>Wy,regex:()=>lf,size:()=>up,slugify:()=>kh,startsWith:()=>mf,toLowerCase:()=>Sf,toUpperCase:()=>yf,trim:()=>vf,uppercase:()=>df});var NP=O(()=>{mn()});var hp={};Y(hp,{ZodISODate:()=>$P,ZodISODateTime:()=>CP,ZodISODuration:()=>MP,ZodISOTime:()=>kP,date:()=>Kz,datetime:()=>Wz,duration:()=>Yz,time:()=>Zz});function Wz(t){return WR(CP,t)}function Kz(t){return KR($P,t)}function Zz(t){return ZR(kP,t)}function Yz(t){return YR(MP,t)}var CP,$P,kP,MP,Yy=O(()=>{mn();Xy();CP=D("ZodISODateTime",(t,e)=>{zw.init(t,e),Rr.init(t,e)});$P=D("ZodISODate",(t,e)=>{Fw.init(t,e),Rr.init(t,e)});kP=D("ZodISOTime",(t,e)=>{qw.init(t,e),Rr.init(t,e)});MP=D("ZodISODuration",(t,e)=>{Bw.init(t,e),Rr.init(t,e)})});var jee,DP,Fi,Jz=O(()=>{mn();mn();je();jee=(t,e)=>{py.init(t,e),t.name="ZodError",Object.defineProperties(t,{format:{value:r=>Eh(t,r)},flatten:{value:r=>yh(t,r)},addIssue:{value:r=>{t.issues.push(r),t.message=JSON.stringify(t.issues,_h,2)}},addIssues:{value:r=>{t.issues.push(...r),t.message=JSON.stringify(t.issues,_h,2)}},isEmpty:{get(){return t.issues.length===0}}})},DP=D("ZodError",jee),Fi=D("ZodError",jee,{Parent:Error})});var Xz,Qz,e4,t4,r4,n4,o4,i4,s4,a4,c4,u4,l4=O(()=>{mn();Jz();Xz=Th(Fi),Qz=bh(Fi),e4=xh(Fi),t4=Ah(Fi),r4=lw(Fi),n4=pw(Fi),o4=dw(Fi),i4=fw(Fi),s4=mw(Fi),a4=hw(Fi),c4=gw(Fi),u4=_w(Fi)});var Jy={};Y(Jy,{ZodAny:()=>h4,ZodArray:()=>S4,ZodBase64:()=>XP,ZodBase64URL:()=>QP,ZodBigInt:()=>qh,ZodBigIntFormat:()=>rI,ZodBoolean:()=>Fh,ZodCIDRv4:()=>YP,ZodCIDRv6:()=>JP,ZodCUID:()=>BP,ZodCUID2:()=>GP,ZodCatch:()=>j4,ZodCodec:()=>cI,ZodCustom:()=>cE,ZodCustomStringFormat:()=>jh,ZodDate:()=>nE,ZodDefault:()=>$4,ZodDiscriminatedUnion:()=>E4,ZodE164:()=>eI,ZodEmail:()=>jP,ZodEmoji:()=>FP,ZodEnum:()=>Lh,ZodExactOptional:()=>O4,ZodFile:()=>P4,ZodFunction:()=>K4,ZodGUID:()=>Qy,ZodIPv4:()=>KP,ZodIPv6:()=>ZP,ZodIntersection:()=>T4,ZodJWT:()=>tI,ZodKSUID:()=>WP,ZodLazy:()=>V4,ZodLiteral:()=>R4,ZodMAC:()=>p4,ZodMap:()=>A4,ZodNaN:()=>F4,ZodNanoID:()=>qP,ZodNever:()=>_4,ZodNonOptional:()=>sI,ZodNull:()=>m4,ZodNullable:()=>C4,ZodNumber:()=>zh,ZodNumberFormat:()=>Tf,ZodObject:()=>oE,ZodOptional:()=>Vh,ZodPipe:()=>aI,ZodPrefault:()=>M4,ZodPromise:()=>W4,ZodReadonly:()=>q4,ZodRecord:()=>aE,ZodSet:()=>w4,ZodString:()=>Uh,ZodStringFormat:()=>Rr,ZodSuccess:()=>U4,ZodSymbol:()=>d4,ZodTemplateLiteral:()=>G4,ZodTransform:()=>I4,ZodTuple:()=>b4,ZodType:()=>vt,ZodULID:()=>VP,ZodURL:()=>rE,ZodUUID:()=>Lc,ZodUndefined:()=>f4,ZodUnion:()=>iE,ZodUnknown:()=>g4,ZodVoid:()=>v4,ZodXID:()=>HP,ZodXor:()=>y4,_ZodString:()=>UP,_default:()=>k4,_function:()=>Lte,any:()=>nI,array:()=>H,base64:()=>ote,base64url:()=>ite,bigint:()=>gte,boolean:()=>ue,catch:()=>z4,check:()=>Ute,cidrv4:()=>rte,cidrv6:()=>nte,codec:()=>kte,cuid:()=>Kee,cuid2:()=>Zee,custom:()=>uI,date:()=>Tte,describe:()=>jte,discriminatedUnion:()=>sE,e164:()=>ste,email:()=>zee,emoji:()=>Hee,enum:()=>Ze,exactOptional:()=>N4,file:()=>Ote,float32:()=>dte,float64:()=>fte,function:()=>Lte,guid:()=>Fee,hash:()=>pte,hex:()=>lte,hostname:()=>ute,httpUrl:()=>Vee,instanceof:()=>Fte,int:()=>LP,int32:()=>mte,int64:()=>_te,intersection:()=>Gh,ipv4:()=>Qee,ipv6:()=>tte,json:()=>Bte,jwt:()=>ate,keyof:()=>bte,ksuid:()=>Xee,lazy:()=>H4,literal:()=>Ie,looseObject:()=>wn,looseRecord:()=>wte,mac:()=>ete,map:()=>Rte,meta:()=>zte,nan:()=>$te,nanoid:()=>Wee,nativeEnum:()=>Ite,never:()=>oI,nonoptional:()=>L4,null:()=>Bh,nullable:()=>eE,nullish:()=>Nte,number:()=>ce,object:()=>k,optional:()=>qr,partialRecord:()=>Ate,pipe:()=>tE,prefault:()=>D4,preprocess:()=>Bt,promise:()=>Dte,readonly:()=>B4,record:()=>ir,refine:()=>Z4,set:()=>Pte,strictObject:()=>st,string:()=>T,stringFormat:()=>cte,stringbool:()=>qte,success:()=>Cte,superRefine:()=>Y4,symbol:()=>Ste,templateLiteral:()=>Mte,transform:()=>iI,tuple:()=>x4,uint32:()=>hte,uint64:()=>vte,ulid:()=>Yee,undefined:()=>yte,union:()=>br,unknown:()=>Pr,url:()=>zP,uuid:()=>Dt,uuidv4:()=>qee,uuidv6:()=>Bee,uuidv7:()=>Gee,void:()=>Ete,xid:()=>Jee,xor:()=>xte});function T(t){return BR(Uh,t)}function zee(t){return by(jP,t)}function Fee(t){return Ch(Qy,t)}function Dt(t){return xy(Lc,t)}function qee(t){return Ay(Lc,t)}function Bee(t){return wy(Lc,t)}function Gee(t){return Ry(Lc,t)}function zP(t){return $h(rE,t)}function Vee(t){return $h(rE,{protocol:/^https?$/,hostname:zi.domain,...te.normalizeParams(t)})}function Hee(t){return Py(FP,t)}function Wee(t){return Iy(qP,t)}function Kee(t){return Oy(BP,t)}function Zee(t){return Ny(GP,t)}function Yee(t){return Cy(VP,t)}function Jee(t){return $y(HP,t)}function Xee(t){return ky(WP,t)}function Qee(t){return My(KP,t)}function ete(t){return VR(p4,t)}function tte(t){return Dy(ZP,t)}function rte(t){return Ly(YP,t)}function nte(t){return Uy(JP,t)}function ote(t){return jy(XP,t)}function ite(t){return zy(QP,t)}function ste(t){return Fy(eI,t)}function ate(t){return qy(tI,t)}function cte(t,e,r={}){return Ef(jh,t,e,r)}function ute(t){return Ef(jh,"hostname",zi.hostname,t)}function lte(t){return Ef(jh,"hex",zi.hex,t)}function pte(t,e){let r=e?.enc??"hex",n=`${t}_${r}`,o=zi[n];if(!o)throw new Error(`Unrecognized hash format: ${n}`);return Ef(jh,n,o,e)}function ce(t){return JR(zh,t)}function LP(t){return QR(Tf,t)}function dte(t){return eP(Tf,t)}function fte(t){return tP(Tf,t)}function mte(t){return rP(Tf,t)}function hte(t){return nP(Tf,t)}function ue(t){return oP(Fh,t)}function gte(t){return sP(qh,t)}function _te(t){return cP(rI,t)}function vte(t){return uP(rI,t)}function Ste(t){return lP(d4,t)}function yte(t){return pP(f4,t)}function Bh(t){return dP(m4,t)}function nI(){return fP(h4)}function Pr(){return mP(g4)}function oI(t){return hP(_4,t)}function Ete(t){return gP(v4,t)}function Tte(t){return _P(nE,t)}function H(t,e){return cz(S4,t,e)}function bte(t){let e=t._zod.def.shape;return Ze(Object.keys(e))}function k(t,e){let r={type:"object",shape:t??{},...te.normalizeParams(e)};return new oE(r)}function st(t,e){return new oE({type:"object",shape:t,catchall:oI(),...te.normalizeParams(e)})}function wn(t,e){return new oE({type:"object",shape:t,catchall:Pr(),...te.normalizeParams(e)})}function br(t,e){return new iE({type:"union",options:t,...te.normalizeParams(e)})}function xte(t,e){return new y4({type:"union",options:t,inclusive:!1,...te.normalizeParams(e)})}function sE(t,e,r){return new E4({type:"union",options:e,discriminator:t,...te.normalizeParams(r)})}function Gh(t,e){return new T4({type:"intersection",left:t,right:e})}function x4(t,e,r){let n=e instanceof nt,o=n?r:e,i=n?e:null;return new b4({type:"tuple",items:t,rest:i,...te.normalizeParams(o)})}function ir(t,e,r){return new aE({type:"record",keyType:t,valueType:e,...te.normalizeParams(r)})}function Ate(t,e,r){let n=oo(t);return n._zod.values=void 0,new aE({type:"record",keyType:n,valueType:e,...te.normalizeParams(r)})}function wte(t,e,r){return new aE({type:"record",keyType:t,valueType:e,mode:"loose",...te.normalizeParams(r)})}function Rte(t,e,r){return new A4({type:"map",keyType:t,valueType:e,...te.normalizeParams(r)})}function Pte(t,e){return new w4({type:"set",valueType:t,...te.normalizeParams(e)})}function Ze(t,e){let r=Array.isArray(t)?Object.fromEntries(t.map(n=>[n,n])):t;return new Lh({type:"enum",entries:r,...te.normalizeParams(e)})}function Ite(t,e){return new Lh({type:"enum",entries:t,...te.normalizeParams(e)})}function Ie(t,e){return new R4({type:"literal",values:Array.isArray(t)?t:[t],...te.normalizeParams(e)})}function Ote(t){return yP(P4,t)}function iI(t){return new I4({type:"transform",transform:t})}function qr(t){return new Vh({type:"optional",innerType:t})}function N4(t){return new O4({type:"optional",innerType:t})}function eE(t){return new C4({type:"nullable",innerType:t})}function Nte(t){return qr(eE(t))}function k4(t,e){return new $4({type:"default",innerType:t,get defaultValue(){return typeof e=="function"?e():te.shallowClone(e)}})}function D4(t,e){return new M4({type:"prefault",innerType:t,get defaultValue(){return typeof e=="function"?e():te.shallowClone(e)}})}function L4(t,e){return new sI({type:"nonoptional",innerType:t,...te.normalizeParams(e)})}function Cte(t){return new U4({type:"success",innerType:t})}function z4(t,e){return new j4({type:"catch",innerType:t,catchValue:typeof e=="function"?e:()=>e})}function $te(t){return SP(F4,t)}function tE(t,e){return new aI({type:"pipe",in:t,out:e})}function kte(t,e,r){return new cI({type:"pipe",in:t,out:e,transform:r.decode,reverseTransform:r.encode})}function B4(t){return new q4({type:"readonly",innerType:t})}function Mte(t,e){return new G4({type:"template_literal",parts:t,...te.normalizeParams(e)})}function H4(t){return new V4({type:"lazy",getter:t})}function Dte(t){return new W4({type:"promise",innerType:t})}function Lte(t){return new K4({type:"function",input:Array.isArray(t?.input)?x4(t?.input):t?.input??H(Pr()),output:t?.output??Pr()})}function Ute(t){let e=new wr({check:"custom"});return e._zod.check=t,e}function uI(t,e){return EP(cE,t??(()=>!0),e)}function Z4(t,e={}){return Ky(cE,t,e)}function Y4(t){return TP(t)}function Fte(t,e={}){let r=new cE({type:"custom",check:"custom",fn:n=>n instanceof t,abort:!0,...te.normalizeParams(e)});return r._zod.bag.Class=t,r._zod.check=n=>{n.value instanceof t||n.issues.push({code:"invalid_type",expected:t.name,input:n.value,inst:r,path:[...r._zod.def.path??[]]})},r}function Bte(t){let e=H4(()=>br([T(t),ce(),ue(),Bh(),H(e),ir(T(),e)]));return e}function Bt(t,e){return tE(iI(t),e)}var vt,UP,Uh,Rr,jP,Qy,Lc,rE,FP,qP,BP,GP,VP,HP,WP,KP,p4,ZP,YP,JP,XP,QP,eI,tI,jh,zh,Tf,Fh,qh,rI,d4,f4,m4,h4,g4,_4,v4,nE,S4,oE,iE,y4,E4,T4,b4,aE,A4,w4,Lh,R4,P4,I4,Vh,O4,C4,$4,M4,sI,U4,j4,F4,aI,cI,q4,G4,V4,W4,K4,cE,jte,zte,qte,Xy=O(()=>{mn();mn();Dh();Zy();NP();Yy();l4();vt=D("ZodType",(t,e)=>(nt.init(t,e),Object.assign(t["~standard"],{jsonSchema:{input:Mh(t,"input"),output:Mh(t,"output")}}),t.toJSONSchema=uz(t,{}),t.def=e,t.type=e.type,Object.defineProperty(t,"_def",{value:e}),t.check=(...r)=>t.clone(te.mergeDefs(e,{checks:[...e.checks??[],...r.map(n=>typeof n=="function"?{_zod:{check:n,def:{check:"custom"},onattach:[]}}:n)]}),{parent:!0}),t.with=t.check,t.clone=(r,n)=>oo(t,r,n),t.brand=()=>t,t.register=((r,n)=>(r.add(t,n),t)),t.parse=(r,n)=>Xz(t,r,n,{callee:t.parse}),t.safeParse=(r,n)=>e4(t,r,n),t.parseAsync=async(r,n)=>Qz(t,r,n,{callee:t.parseAsync}),t.safeParseAsync=async(r,n)=>t4(t,r,n),t.spa=t.safeParseAsync,t.encode=(r,n)=>r4(t,r,n),t.decode=(r,n)=>n4(t,r,n),t.encodeAsync=async(r,n)=>o4(t,r,n),t.decodeAsync=async(r,n)=>i4(t,r,n),t.safeEncode=(r,n)=>s4(t,r,n),t.safeDecode=(r,n)=>a4(t,r,n),t.safeEncodeAsync=async(r,n)=>c4(t,r,n),t.safeDecodeAsync=async(r,n)=>u4(t,r,n),t.refine=(r,n)=>t.check(Z4(r,n)),t.superRefine=r=>t.check(Y4(r)),t.overwrite=r=>t.check(Hs(r)),t.optional=()=>qr(t),t.exactOptional=()=>N4(t),t.nullable=()=>eE(t),t.nullish=()=>qr(eE(t)),t.nonoptional=r=>L4(t,r),t.array=()=>H(t),t.or=r=>br([t,r]),t.and=r=>Gh(t,r),t.transform=r=>tE(t,iI(r)),t.default=r=>k4(t,r),t.prefault=r=>D4(t,r),t.catch=r=>z4(t,r),t.pipe=r=>tE(t,r),t.readonly=()=>B4(t),t.describe=r=>{let n=t.clone();return io.add(n,{description:r}),n},Object.defineProperty(t,"description",{get(){return io.get(t)?.description},configurable:!0}),t.meta=(...r)=>{if(r.length===0)return io.get(t);let n=t.clone();return io.add(n,r[0]),n},t.isOptional=()=>t.safeParse(void 0).success,t.isNullable=()=>t.safeParse(null).success,t.apply=r=>r(t),t)),UP=D("_ZodString",(t,e)=>{cp.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(n,o,i)=>lz(t,n,o,i);let r=t._zod.bag;t.format=r.format??null,t.minLength=r.minimum??null,t.maxLength=r.maximum??null,t.regex=(...n)=>t.check(lf(...n)),t.includes=(...n)=>t.check(ff(...n)),t.startsWith=(...n)=>t.check(mf(...n)),t.endsWith=(...n)=>t.check(hf(...n)),t.min=(...n)=>t.check(Dc(...n)),t.max=(...n)=>t.check(lp(...n)),t.length=(...n)=>t.check(pp(...n)),t.nonempty=(...n)=>t.check(Dc(1,...n)),t.lowercase=n=>t.check(pf(n)),t.uppercase=n=>t.check(df(n)),t.trim=()=>t.check(vf()),t.normalize=(...n)=>t.check(_f(...n)),t.toLowerCase=()=>t.check(Sf()),t.toUpperCase=()=>t.check(yf()),t.slugify=()=>t.check(kh())}),Uh=D("ZodString",(t,e)=>{cp.init(t,e),UP.init(t,e),t.email=r=>t.check(by(jP,r)),t.url=r=>t.check($h(rE,r)),t.jwt=r=>t.check(qy(tI,r)),t.emoji=r=>t.check(Py(FP,r)),t.guid=r=>t.check(Ch(Qy,r)),t.uuid=r=>t.check(xy(Lc,r)),t.uuidv4=r=>t.check(Ay(Lc,r)),t.uuidv6=r=>t.check(wy(Lc,r)),t.uuidv7=r=>t.check(Ry(Lc,r)),t.nanoid=r=>t.check(Iy(qP,r)),t.guid=r=>t.check(Ch(Qy,r)),t.cuid=r=>t.check(Oy(BP,r)),t.cuid2=r=>t.check(Ny(GP,r)),t.ulid=r=>t.check(Cy(VP,r)),t.base64=r=>t.check(jy(XP,r)),t.base64url=r=>t.check(zy(QP,r)),t.xid=r=>t.check($y(HP,r)),t.ksuid=r=>t.check(ky(WP,r)),t.ipv4=r=>t.check(My(KP,r)),t.ipv6=r=>t.check(Dy(ZP,r)),t.cidrv4=r=>t.check(Ly(YP,r)),t.cidrv6=r=>t.check(Uy(JP,r)),t.e164=r=>t.check(Fy(eI,r)),t.datetime=r=>t.check(Wz(r)),t.date=r=>t.check(Kz(r)),t.time=r=>t.check(Zz(r)),t.duration=r=>t.check(Yz(r))});Rr=D("ZodStringFormat",(t,e)=>{Er.init(t,e),UP.init(t,e)}),jP=D("ZodEmail",(t,e)=>{Nw.init(t,e),Rr.init(t,e)});Qy=D("ZodGUID",(t,e)=>{Iw.init(t,e),Rr.init(t,e)});Lc=D("ZodUUID",(t,e)=>{Ow.init(t,e),Rr.init(t,e)});rE=D("ZodURL",(t,e)=>{Cw.init(t,e),Rr.init(t,e)});FP=D("ZodEmoji",(t,e)=>{$w.init(t,e),Rr.init(t,e)});qP=D("ZodNanoID",(t,e)=>{kw.init(t,e),Rr.init(t,e)});BP=D("ZodCUID",(t,e)=>{Mw.init(t,e),Rr.init(t,e)});GP=D("ZodCUID2",(t,e)=>{Dw.init(t,e),Rr.init(t,e)});VP=D("ZodULID",(t,e)=>{Lw.init(t,e),Rr.init(t,e)});HP=D("ZodXID",(t,e)=>{Uw.init(t,e),Rr.init(t,e)});WP=D("ZodKSUID",(t,e)=>{jw.init(t,e),Rr.init(t,e)});KP=D("ZodIPv4",(t,e)=>{Gw.init(t,e),Rr.init(t,e)});p4=D("ZodMAC",(t,e)=>{Hw.init(t,e),Rr.init(t,e)});ZP=D("ZodIPv6",(t,e)=>{Vw.init(t,e),Rr.init(t,e)});YP=D("ZodCIDRv4",(t,e)=>{Ww.init(t,e),Rr.init(t,e)});JP=D("ZodCIDRv6",(t,e)=>{Kw.init(t,e),Rr.init(t,e)});XP=D("ZodBase64",(t,e)=>{Zw.init(t,e),Rr.init(t,e)});QP=D("ZodBase64URL",(t,e)=>{Yw.init(t,e),Rr.init(t,e)});eI=D("ZodE164",(t,e)=>{Jw.init(t,e),Rr.init(t,e)});tI=D("ZodJWT",(t,e)=>{Xw.init(t,e),Rr.init(t,e)});jh=D("ZodCustomStringFormat",(t,e)=>{Qw.init(t,e),Rr.init(t,e)});zh=D("ZodNumber",(t,e)=>{hy.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(n,o,i)=>pz(t,n,o,i),t.gt=(n,o)=>t.check(Ha(n,o)),t.gte=(n,o)=>t.check(To(n,o)),t.min=(n,o)=>t.check(To(n,o)),t.lt=(n,o)=>t.check(Va(n,o)),t.lte=(n,o)=>t.check(mi(n,o)),t.max=(n,o)=>t.check(mi(n,o)),t.int=n=>t.check(LP(n)),t.safe=n=>t.check(LP(n)),t.positive=n=>t.check(Ha(0,n)),t.nonnegative=n=>t.check(To(0,n)),t.negative=n=>t.check(Va(0,n)),t.nonpositive=n=>t.check(mi(0,n)),t.multipleOf=(n,o)=>t.check(Ku(n,o)),t.step=(n,o)=>t.check(Ku(n,o)),t.finite=()=>t;let r=t._zod.bag;t.minValue=Math.max(r.minimum??Number.NEGATIVE_INFINITY,r.exclusiveMinimum??Number.NEGATIVE_INFINITY)??null,t.maxValue=Math.min(r.maximum??Number.POSITIVE_INFINITY,r.exclusiveMaximum??Number.POSITIVE_INFINITY)??null,t.isInt=(r.format??"").includes("int")||Number.isSafeInteger(r.multipleOf??.5),t.isFinite=!0,t.format=r.format??null});Tf=D("ZodNumberFormat",(t,e)=>{eR.init(t,e),zh.init(t,e)});Fh=D("ZodBoolean",(t,e)=>{Rh.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>dz(t,r,n,o)});qh=D("ZodBigInt",(t,e)=>{gy.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(n,o,i)=>fz(t,n,o,i),t.gte=(n,o)=>t.check(To(n,o)),t.min=(n,o)=>t.check(To(n,o)),t.gt=(n,o)=>t.check(Ha(n,o)),t.gte=(n,o)=>t.check(To(n,o)),t.min=(n,o)=>t.check(To(n,o)),t.lt=(n,o)=>t.check(Va(n,o)),t.lte=(n,o)=>t.check(mi(n,o)),t.max=(n,o)=>t.check(mi(n,o)),t.positive=n=>t.check(Ha(BigInt(0),n)),t.negative=n=>t.check(Va(BigInt(0),n)),t.nonpositive=n=>t.check(mi(BigInt(0),n)),t.nonnegative=n=>t.check(To(BigInt(0),n)),t.multipleOf=(n,o)=>t.check(Ku(n,o));let r=t._zod.bag;t.minValue=r.minimum??null,t.maxValue=r.maximum??null,t.format=r.format??null});rI=D("ZodBigIntFormat",(t,e)=>{tR.init(t,e),qh.init(t,e)});d4=D("ZodSymbol",(t,e)=>{rR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>mz(t,r,n,o)});f4=D("ZodUndefined",(t,e)=>{nR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>gz(t,r,n,o)});m4=D("ZodNull",(t,e)=>{oR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>hz(t,r,n,o)});h4=D("ZodAny",(t,e)=>{iR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Sz(t,r,n,o)});g4=D("ZodUnknown",(t,e)=>{sR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>yz(t,r,n,o)});_4=D("ZodNever",(t,e)=>{aR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>vz(t,r,n,o)});v4=D("ZodVoid",(t,e)=>{cR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>_z(t,r,n,o)});nE=D("ZodDate",(t,e)=>{uR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(n,o,i)=>Ez(t,n,o,i),t.min=(n,o)=>t.check(To(n,o)),t.max=(n,o)=>t.check(mi(n,o));let r=t._zod.bag;t.minDate=r.minimum?new Date(r.minimum):null,t.maxDate=r.maximum?new Date(r.maximum):null});S4=D("ZodArray",(t,e)=>{lR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>$z(t,r,n,o),t.element=e.element,t.min=(r,n)=>t.check(Dc(r,n)),t.nonempty=r=>t.check(Dc(1,r)),t.max=(r,n)=>t.check(lp(r,n)),t.length=(r,n)=>t.check(pp(r,n)),t.unwrap=()=>t.element});oE=D("ZodObject",(t,e)=>{nz.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>kz(t,r,n,o),te.defineLazy(t,"shape",()=>e.shape),t.keyof=()=>Ze(Object.keys(t._zod.def.shape)),t.catchall=r=>t.clone({...t._zod.def,catchall:r}),t.passthrough=()=>t.clone({...t._zod.def,catchall:Pr()}),t.loose=()=>t.clone({...t._zod.def,catchall:Pr()}),t.strict=()=>t.clone({...t._zod.def,catchall:oI()}),t.strip=()=>t.clone({...t._zod.def,catchall:void 0}),t.extend=r=>te.extend(t,r),t.safeExtend=r=>te.safeExtend(t,r),t.merge=r=>te.merge(t,r),t.pick=r=>te.pick(t,r),t.omit=r=>te.omit(t,r),t.partial=(...r)=>te.partial(Vh,t,r[0]),t.required=(...r)=>te.required(sI,t,r[0])});iE=D("ZodUnion",(t,e)=>{Ph.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>RP(t,r,n,o),t.options=e.options});y4=D("ZodXor",(t,e)=>{iE.init(t,e),dR.init(t,e),t._zod.processJSONSchema=(r,n,o)=>RP(t,r,n,o),t.options=e.options});E4=D("ZodDiscriminatedUnion",(t,e)=>{iE.init(t,e),fR.init(t,e)});T4=D("ZodIntersection",(t,e)=>{mR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Mz(t,r,n,o)});b4=D("ZodTuple",(t,e)=>{_y.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Dz(t,r,n,o),t.rest=r=>t.clone({...t._zod.def,rest:r})});aE=D("ZodRecord",(t,e)=>{hR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Lz(t,r,n,o),t.keyType=e.keyType,t.valueType=e.valueType});A4=D("ZodMap",(t,e)=>{gR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Nz(t,r,n,o),t.keyType=e.keyType,t.valueType=e.valueType,t.min=(...r)=>t.check(Wa(...r)),t.nonempty=r=>t.check(Wa(1,r)),t.max=(...r)=>t.check(Zu(...r)),t.size=(...r)=>t.check(up(...r))});w4=D("ZodSet",(t,e)=>{_R.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Cz(t,r,n,o),t.min=(...r)=>t.check(Wa(...r)),t.nonempty=r=>t.check(Wa(1,r)),t.max=(...r)=>t.check(Zu(...r)),t.size=(...r)=>t.check(up(...r))});Lh=D("ZodEnum",(t,e)=>{vR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(n,o,i)=>Tz(t,n,o,i),t.enum=e.entries,t.options=Object.values(e.entries);let r=new Set(Object.keys(e.entries));t.extract=(n,o)=>{let i={};for(let s of n)if(r.has(s))i[s]=e.entries[s];else throw new Error(`Key ${s} not found in enum`);return new Lh({...e,checks:[],...te.normalizeParams(o),entries:i})},t.exclude=(n,o)=>{let i={...e.entries};for(let s of n)if(r.has(s))delete i[s];else throw new Error(`Key ${s} not found in enum`);return new Lh({...e,checks:[],...te.normalizeParams(o),entries:i})}});R4=D("ZodLiteral",(t,e)=>{SR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>bz(t,r,n,o),t.values=new Set(e.values),Object.defineProperty(t,"value",{get(){if(e.values.length>1)throw new Error("This schema contains multiple valid literal values. Use `.values` instead.");return e.values[0]}})});P4=D("ZodFile",(t,e)=>{yR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>wz(t,r,n,o),t.min=(r,n)=>t.check(Wa(r,n)),t.max=(r,n)=>t.check(Zu(r,n)),t.mime=(r,n)=>t.check(gf(Array.isArray(r)?r:[r],n))});I4=D("ZodTransform",(t,e)=>{ER.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Oz(t,r,n,o),t._zod.parse=(r,n)=>{if(n.direction==="backward")throw new tp(t.constructor.name);r.addIssue=i=>{if(typeof i=="string")r.issues.push(te.issue(i,r.value,e));else{let s=i;s.fatal&&(s.continue=!1),s.code??(s.code="custom"),s.input??(s.input=r.value),s.inst??(s.inst=t),r.issues.push(te.issue(s))}};let o=e.transform(r.value,r);return o instanceof Promise?o.then(i=>(r.value=i,r)):(r.value=o,r)}});Vh=D("ZodOptional",(t,e)=>{vy.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>PP(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});O4=D("ZodExactOptional",(t,e)=>{TR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>PP(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});C4=D("ZodNullable",(t,e)=>{bR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Uz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});$4=D("ZodDefault",(t,e)=>{xR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>zz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType,t.removeDefault=t.unwrap});M4=D("ZodPrefault",(t,e)=>{AR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Fz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});sI=D("ZodNonOptional",(t,e)=>{wR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>jz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});U4=D("ZodSuccess",(t,e)=>{RR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Rz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});j4=D("ZodCatch",(t,e)=>{PR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>qz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType,t.removeCatch=t.unwrap});F4=D("ZodNaN",(t,e)=>{IR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>xz(t,r,n,o)});aI=D("ZodPipe",(t,e)=>{OR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Bz(t,r,n,o),t.in=e.in,t.out=e.out});cI=D("ZodCodec",(t,e)=>{aI.init(t,e),Ih.init(t,e)});q4=D("ZodReadonly",(t,e)=>{NR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Gz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});G4=D("ZodTemplateLiteral",(t,e)=>{CR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Az(t,r,n,o)});V4=D("ZodLazy",(t,e)=>{MR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Hz(t,r,n,o),t.unwrap=()=>t._zod.def.getter()});W4=D("ZodPromise",(t,e)=>{kR.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Vz(t,r,n,o),t.unwrap=()=>t._zod.def.innerType});K4=D("ZodFunction",(t,e)=>{$R.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Iz(t,r,n,o)});cE=D("ZodCustom",(t,e)=>{Sy.init(t,e),vt.init(t,e),t._zod.processJSONSchema=(r,n,o)=>Pz(t,r,n,o)});jte=bP,zte=xP;qte=(...t)=>AP({Codec:cI,Boolean:Fh,String:Uh},...t)});function PYe(t){cn({customError:t})}function IYe(){return cn().customError}var gp,J4,Gte=O(()=>{mn();mn();gp={invalid_type:"invalid_type",too_big:"too_big",too_small:"too_small",invalid_format:"invalid_format",not_multiple_of:"not_multiple_of",unrecognized_keys:"unrecognized_keys",invalid_union:"invalid_union",invalid_key:"invalid_key",invalid_element:"invalid_element",invalid_value:"invalid_value",custom:"custom"};J4||(J4={})});function NYe(t,e){let r=t.$schema;return r==="https://json-schema.org/draft/2020-12/schema"?"draft-2020-12":r==="http://json-schema.org/draft-07/schema#"?"draft-7":r==="http://json-schema.org/draft-04/schema#"?"draft-4":e??"draft-2020-12"}function CYe(t,e){if(!t.startsWith("#"))throw new Error("External $ref is not supported, only local refs (#/...) are allowed");let r=t.slice(1).split("/").filter(Boolean);if(r.length===0)return e.rootSchema;let n=e.version==="draft-2020-12"?"$defs":"definitions";if(r[0]===n){let o=r[1];if(!o||!e.defs[o])throw new Error(`Reference not found: ${t}`);return e.defs[o]}throw new Error(`Reference not found: ${t}`)}function Vte(t,e){if(t.not!==void 0){if(typeof t.not=="object"&&Object.keys(t.not).length===0)return ve.never();throw new Error("not is not supported in Zod (except { not: {} } for never)")}if(t.unevaluatedItems!==void 0)throw new Error("unevaluatedItems is not supported");if(t.unevaluatedProperties!==void 0)throw new Error("unevaluatedProperties is not supported");if(t.if!==void 0||t.then!==void 0||t.else!==void 0)throw new Error("Conditional schemas (if/then/else) are not supported");if(t.dependentSchemas!==void 0||t.dependentRequired!==void 0)throw new Error("dependentSchemas and dependentRequired are not supported");if(t.$ref){let o=t.$ref;if(e.refs.has(o))return e.refs.get(o);if(e.processing.has(o))return ve.lazy(()=>{if(!e.refs.has(o))throw new Error(`Circular reference not resolved: ${o}`);return e.refs.get(o)});e.processing.add(o);let i=CYe(o,e),s=Bo(i,e);return e.refs.set(o,s),e.processing.delete(o),s}if(t.enum!==void 0){let o=t.enum;if(e.version==="openapi-3.0"&&t.nullable===!0&&o.length===1&&o[0]===null)return ve.null();if(o.length===0)return ve.never();if(o.length===1)return ve.literal(o[0]);if(o.every(s=>typeof s=="string"))return ve.enum(o);let i=o.map(s=>ve.literal(s));return i.length<2?i[0]:ve.union([i[0],i[1],...i.slice(2)])}if(t.const!==void 0)return ve.literal(t.const);let r=t.type;if(Array.isArray(r)){let o=r.map(i=>{let s={...t,type:i};return Vte(s,e)});return o.length===0?ve.never():o.length===1?o[0]:ve.union(o)}if(!r)return ve.any();let n;switch(r){case"string":{let o=ve.string();if(t.format){let i=t.format;i==="email"?o=o.check(ve.email()):i==="uri"||i==="uri-reference"?o=o.check(ve.url()):i==="uuid"||i==="guid"?o=o.check(ve.uuid()):i==="date-time"?o=o.check(ve.iso.datetime()):i==="date"?o=o.check(ve.iso.date()):i==="time"?o=o.check(ve.iso.time()):i==="duration"?o=o.check(ve.iso.duration()):i==="ipv4"?o=o.check(ve.ipv4()):i==="ipv6"?o=o.check(ve.ipv6()):i==="mac"?o=o.check(ve.mac()):i==="cidr"?o=o.check(ve.cidrv4()):i==="cidr-v6"?o=o.check(ve.cidrv6()):i==="base64"?o=o.check(ve.base64()):i==="base64url"?o=o.check(ve.base64url()):i==="e164"?o=o.check(ve.e164()):i==="jwt"?o=o.check(ve.jwt()):i==="emoji"?o=o.check(ve.emoji()):i==="nanoid"?o=o.check(ve.nanoid()):i==="cuid"?o=o.check(ve.cuid()):i==="cuid2"?o=o.check(ve.cuid2()):i==="ulid"?o=o.check(ve.ulid()):i==="xid"?o=o.check(ve.xid()):i==="ksuid"&&(o=o.check(ve.ksuid()))}typeof t.minLength=="number"&&(o=o.min(t.minLength)),typeof t.maxLength=="number"&&(o=o.max(t.maxLength)),t.pattern&&(o=o.regex(new RegExp(t.pattern))),n=o;break}case"number":case"integer":{let o=r==="integer"?ve.number().int():ve.number();typeof t.minimum=="number"&&(o=o.min(t.minimum)),typeof t.maximum=="number"&&(o=o.max(t.maximum)),typeof t.exclusiveMinimum=="number"?o=o.gt(t.exclusiveMinimum):t.exclusiveMinimum===!0&&typeof t.minimum=="number"&&(o=o.gt(t.minimum)),typeof t.exclusiveMaximum=="number"?o=o.lt(t.exclusiveMaximum):t.exclusiveMaximum===!0&&typeof t.maximum=="number"&&(o=o.lt(t.maximum)),typeof t.multipleOf=="number"&&(o=o.multipleOf(t.multipleOf)),n=o;break}case"boolean":{n=ve.boolean();break}case"null":{n=ve.null();break}case"object":{let o={},i=t.properties||{},s=new Set(t.required||[]);for(let[c,u]of Object.entries(i)){let p=Bo(u,e);o[c]=s.has(c)?p:p.optional()}if(t.propertyNames){let c=Bo(t.propertyNames,e),u=t.additionalProperties&&typeof t.additionalProperties=="object"?Bo(t.additionalProperties,e):ve.any();if(Object.keys(o).length===0){n=ve.record(c,u);break}let p=ve.object(o).passthrough(),f=ve.looseRecord(c,u);n=ve.intersection(p,f);break}if(t.patternProperties){let c=t.patternProperties,u=Object.keys(c),p=[];for(let m of u){let h=Bo(c[m],e),_=ve.string().regex(new RegExp(m));p.push(ve.looseRecord(_,h))}let f=[];if(Object.keys(o).length>0&&f.push(ve.object(o).passthrough()),f.push(...p),f.length===0)n=ve.object({}).passthrough();else if(f.length===1)n=f[0];else{let m=ve.intersection(f[0],f[1]);for(let h=2;hBo(c,e)),a=i&&typeof i=="object"&&!Array.isArray(i)?Bo(i,e):void 0;a?n=ve.tuple(s).rest(a):n=ve.tuple(s),typeof t.minItems=="number"&&(n=n.check(ve.minLength(t.minItems))),typeof t.maxItems=="number"&&(n=n.check(ve.maxLength(t.maxItems)))}else if(Array.isArray(i)){let s=i.map(c=>Bo(c,e)),a=t.additionalItems&&typeof t.additionalItems=="object"?Bo(t.additionalItems,e):void 0;a?n=ve.tuple(s).rest(a):n=ve.tuple(s),typeof t.minItems=="number"&&(n=n.check(ve.minLength(t.minItems))),typeof t.maxItems=="number"&&(n=n.check(ve.maxLength(t.maxItems)))}else if(i!==void 0){let s=Bo(i,e),a=ve.array(s);typeof t.minItems=="number"&&(a=a.min(t.minItems)),typeof t.maxItems=="number"&&(a=a.max(t.maxItems)),n=a}else n=ve.array(ve.any());break}default:throw new Error(`Unsupported type: ${r}`)}return t.description&&(n=n.describe(t.description)),t.default!==void 0&&(n=n.default(t.default)),n}function Bo(t,e){if(typeof t=="boolean")return t?ve.any():ve.never();let r=Vte(t,e),n=t.type||t.enum!==void 0||t.const!==void 0;if(t.anyOf&&Array.isArray(t.anyOf)){let a=t.anyOf.map(u=>Bo(u,e)),c=ve.union(a);r=n?ve.intersection(r,c):c}if(t.oneOf&&Array.isArray(t.oneOf)){let a=t.oneOf.map(u=>Bo(u,e)),c=ve.xor(a);r=n?ve.intersection(r,c):c}if(t.allOf&&Array.isArray(t.allOf))if(t.allOf.length===0)r=n?r:ve.any();else{let a=n?r:Bo(t.allOf[0],e),c=n?0:1;for(let u=c;u0&&e.registry.add(r,o),r}function Hte(t,e){if(typeof t=="boolean")return t?ve.any():ve.never();let r=NYe(t,e?.defaultTarget),n=t.$defs||t.definitions||{},o={version:r,defs:n,refs:new Map,processing:new Set,rootSchema:t,registry:e?.registry??io};return Bo(t,o)}var ve,OYe,Wte=O(()=>{Ty();NP();Yy();Xy();ve={...Jy,...OP,iso:hp},OYe=new Set(["$schema","$ref","$defs","definitions","$id","id","$comment","$anchor","$vocabulary","$dynamicRef","$dynamicAnchor","type","enum","const","anyOf","oneOf","allOf","not","properties","required","additionalProperties","patternProperties","propertyNames","minProperties","maxProperties","items","prefixItems","additionalItems","minItems","maxItems","uniqueItems","contains","minContains","maxContains","minLength","maxLength","pattern","format","minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf","description","default","contentEncoding","contentMediaType","contentSchema","unevaluatedItems","unevaluatedProperties","if","then","else","dependentSchemas","dependentRequired","nullable","readOnly"])});var uE={};Y(uE,{bigint:()=>DYe,boolean:()=>MYe,date:()=>LYe,number:()=>kYe,string:()=>$Ye});function $Ye(t){return GR(Uh,t)}function kYe(t){return XR(zh,t)}function MYe(t){return iP(Fh,t)}function DYe(t){return aP(qh,t)}function LYe(t){return vP(nE,t)}var Kte=O(()=>{mn();Xy()});var Yu={};Y(Yu,{$brand:()=>iy,$input:()=>qR,$output:()=>FR,NEVER:()=>mh,TimePrecision:()=>HR,ZodAny:()=>h4,ZodArray:()=>S4,ZodBase64:()=>XP,ZodBase64URL:()=>QP,ZodBigInt:()=>qh,ZodBigIntFormat:()=>rI,ZodBoolean:()=>Fh,ZodCIDRv4:()=>YP,ZodCIDRv6:()=>JP,ZodCUID:()=>BP,ZodCUID2:()=>GP,ZodCatch:()=>j4,ZodCodec:()=>cI,ZodCustom:()=>cE,ZodCustomStringFormat:()=>jh,ZodDate:()=>nE,ZodDefault:()=>$4,ZodDiscriminatedUnion:()=>E4,ZodE164:()=>eI,ZodEmail:()=>jP,ZodEmoji:()=>FP,ZodEnum:()=>Lh,ZodError:()=>DP,ZodExactOptional:()=>O4,ZodFile:()=>P4,ZodFirstPartyTypeKind:()=>J4,ZodFunction:()=>K4,ZodGUID:()=>Qy,ZodIPv4:()=>KP,ZodIPv6:()=>ZP,ZodISODate:()=>$P,ZodISODateTime:()=>CP,ZodISODuration:()=>MP,ZodISOTime:()=>kP,ZodIntersection:()=>T4,ZodIssueCode:()=>gp,ZodJWT:()=>tI,ZodKSUID:()=>WP,ZodLazy:()=>V4,ZodLiteral:()=>R4,ZodMAC:()=>p4,ZodMap:()=>A4,ZodNaN:()=>F4,ZodNanoID:()=>qP,ZodNever:()=>_4,ZodNonOptional:()=>sI,ZodNull:()=>m4,ZodNullable:()=>C4,ZodNumber:()=>zh,ZodNumberFormat:()=>Tf,ZodObject:()=>oE,ZodOptional:()=>Vh,ZodPipe:()=>aI,ZodPrefault:()=>M4,ZodPromise:()=>W4,ZodReadonly:()=>q4,ZodRealError:()=>Fi,ZodRecord:()=>aE,ZodSet:()=>w4,ZodString:()=>Uh,ZodStringFormat:()=>Rr,ZodSuccess:()=>U4,ZodSymbol:()=>d4,ZodTemplateLiteral:()=>G4,ZodTransform:()=>I4,ZodTuple:()=>b4,ZodType:()=>vt,ZodULID:()=>VP,ZodURL:()=>rE,ZodUUID:()=>Lc,ZodUndefined:()=>f4,ZodUnion:()=>iE,ZodUnknown:()=>g4,ZodVoid:()=>v4,ZodXID:()=>HP,ZodXor:()=>y4,_ZodString:()=>UP,_default:()=>k4,_function:()=>Lte,any:()=>nI,array:()=>H,base64:()=>ote,base64url:()=>ite,bigint:()=>gte,boolean:()=>ue,catch:()=>z4,check:()=>Ute,cidrv4:()=>rte,cidrv6:()=>nte,clone:()=>oo,codec:()=>kte,coerce:()=>uE,config:()=>cn,core:()=>fs,cuid:()=>Kee,cuid2:()=>Zee,custom:()=>uI,date:()=>Tte,decode:()=>n4,decodeAsync:()=>i4,describe:()=>jte,discriminatedUnion:()=>sE,e164:()=>ste,email:()=>zee,emoji:()=>Hee,encode:()=>r4,encodeAsync:()=>o4,endsWith:()=>hf,enum:()=>Ze,exactOptional:()=>N4,file:()=>Ote,flattenError:()=>yh,float32:()=>dte,float64:()=>fte,formatError:()=>Eh,fromJSONSchema:()=>Hte,function:()=>Lte,getErrorMap:()=>IYe,globalRegistry:()=>io,gt:()=>Ha,gte:()=>To,guid:()=>Fee,hash:()=>pte,hex:()=>lte,hostname:()=>ute,httpUrl:()=>Vee,includes:()=>ff,instanceof:()=>Fte,int:()=>LP,int32:()=>mte,int64:()=>_te,intersection:()=>Gh,ipv4:()=>Qee,ipv6:()=>tte,iso:()=>hp,json:()=>Bte,jwt:()=>ate,keyof:()=>bte,ksuid:()=>Xee,lazy:()=>H4,length:()=>pp,literal:()=>Ie,locales:()=>Nh,looseObject:()=>wn,looseRecord:()=>wte,lowercase:()=>pf,lt:()=>Va,lte:()=>mi,mac:()=>ete,map:()=>Rte,maxLength:()=>lp,maxSize:()=>Zu,meta:()=>zte,mime:()=>gf,minLength:()=>Dc,minSize:()=>Wa,multipleOf:()=>Ku,nan:()=>$te,nanoid:()=>Wee,nativeEnum:()=>Ite,negative:()=>Gy,never:()=>oI,nonnegative:()=>Hy,nonoptional:()=>L4,nonpositive:()=>Vy,normalize:()=>_f,null:()=>Bh,nullable:()=>eE,nullish:()=>Nte,number:()=>ce,object:()=>k,optional:()=>qr,overwrite:()=>Hs,parse:()=>Xz,parseAsync:()=>Qz,partialRecord:()=>Ate,pipe:()=>tE,positive:()=>By,prefault:()=>D4,preprocess:()=>Bt,prettifyError:()=>uw,promise:()=>Dte,property:()=>Wy,readonly:()=>B4,record:()=>ir,refine:()=>Z4,regex:()=>lf,regexes:()=>zi,registry:()=>Ey,safeDecode:()=>a4,safeDecodeAsync:()=>u4,safeEncode:()=>s4,safeEncodeAsync:()=>c4,safeParse:()=>e4,safeParseAsync:()=>t4,set:()=>Pte,setErrorMap:()=>PYe,size:()=>up,slugify:()=>kh,startsWith:()=>mf,strictObject:()=>st,string:()=>T,stringFormat:()=>cte,stringbool:()=>qte,success:()=>Cte,superRefine:()=>Y4,symbol:()=>Ste,templateLiteral:()=>Mte,toJSONSchema:()=>gi,toLowerCase:()=>Sf,toUpperCase:()=>yf,transform:()=>iI,treeifyError:()=>cw,trim:()=>vf,tuple:()=>x4,uint32:()=>hte,uint64:()=>vte,ulid:()=>Yee,undefined:()=>yte,union:()=>br,unknown:()=>Pr,uppercase:()=>df,url:()=>zP,util:()=>te,uuid:()=>Dt,uuidv4:()=>qee,uuidv6:()=>Bee,uuidv7:()=>Gee,void:()=>Ete,xid:()=>Jee,xor:()=>xte});var lE=O(()=>{mn();Xy();NP();Jz();l4();Gte();mn();iz();mn();Dh();Wte();jR();Yy();Yy();Kte();cn(DR())});var Te=O(()=>{lE();lE()});function fre(){return process.env.VITEST==="true"||!1||process.env.XCODEBUILDMCP_SILENCE_LOGS==="true"}function pJe(){if(!h2||fre())return null;if(kI)return kI;try{return kI=lJe("@sentry/node"),kI}catch{return null}}function dJe(t){let e=pJe();if(e)try{t(e)}catch{}}function mre(t){m2=t,A("debug",`Log level set to: ${t}`)}function fJe(t){if(fre())return!1;if(m2===null)return!0;let e=t.toLowerCase();return e in f2?f2[e]<=f2[m2]:!0}function A(t,e,r){if(!fJe(t))return;let o=`[${new Date().toISOString()}] [${t.toUpperCase()}] ${e}`;h2&&(r?.sentry??t==="error")&&dJe(s=>s.captureMessage(o)),console.error(o)}var pre,dre,h2,f2,m2,lJe,kI,Go=O(()=>{"use strict";pre=require("node:module"),dre=require("node:path"),h2=process.env.SENTRY_DISABLED!=="true"&&process.env.XCODEBUILDMCP_SENTRY_DISABLED!=="true",f2={emergency:0,alert:1,critical:2,error:3,warning:4,notice:5,info:6,debug:7},m2=null;lJe=(0,pre.createRequire)(typeof __filename=="string"?__filename:(0,dre.resolve)(process.cwd(),"package.json")),kI=null;h2||(process.env.SENTRY_DISABLED==="true"?A("info","Sentry disabled due to SENTRY_DISABLED environment variable"):process.env.XCODEBUILDMCP_SENTRY_DISABLED==="true"&&A("info","Sentry disabled due to XCODEBUILDMCP_SENTRY_DISABLED environment variable"))});var De=O(()=>{"use strict";Go()});async function mJe(t,e,r=!0,n,o=!1){let i=t;r&&(i=["sh","-c",t.map(c=>/[\s,"'=$`;&|<>(){}[\]\\*?~]/.test(c)&&!/^".*"$/.test(c)?`"${c.replace(/(["\\])/g,"\\$1")}"`:c).join(" ")]);let s=r&&i.length===3?i[2]:i.join(" ");return A("info",`Executing ${e??""} command: ${s}`),new Promise((a,c)=>{let u=i[0],p=i.slice(1),f={stdio:["ignore","pipe","pipe"],env:{...process.env,...n?.env??{}},cwd:n?.cwd},m=(0,hre.spawn)(u,p,f),h="",_="";if(m.stdout?.on("data",v=>{h+=v.toString()}),m.stderr?.on("data",v=>{_+=v.toString()}),o){let v=!1;m.on("error",E=>{v||(v=!0,c(E))}),setTimeout(()=>{v||(v=!0,m.pid?a({success:!0,output:"",process:m}):a({success:!1,output:"",error:"Failed to start detached process",process:m}))},100)}else m.on("close",v=>{let E=v===0;a({success:E,output:h,error:E?void 0:_,process:m,exitCode:v??void 0})}),m.on("error",v=>{c(v)})})}function z(){if(process.env.VITEST==="true")throw new Error(`\u{1F6A8} REAL SYSTEM EXECUTOR DETECTED IN TEST! \u{1F6A8} -This test is trying to use the default command executor instead of a mock. -Fix: Pass createMockExecutor() as the commandExecutor parameter in your test. -Example: await plugin.handler(args, createMockExecutor({success: true}), mockFileSystem) -See docs/TESTING.md for proper testing patterns.`);return mJe}function Ir(){if(process.env.VITEST==="true")throw new Error(`\u{1F6A8} REAL FILESYSTEM EXECUTOR DETECTED IN TEST! \u{1F6A8} -This test is trying to use the default filesystem executor instead of a mock. -Fix: Pass createMockFileSystemExecutor() as the fileSystemExecutor parameter in your test. -Example: await plugin.handler(args, mockCmd, createMockFileSystemExecutor()) -See docs/TESTING.md for proper testing patterns.`);return hJe}var hre,gre,_re,hJe,hs=O(()=>{"use strict";hre=require("child_process"),gre=require("fs"),_re=require("os");Go();hJe={async mkdir(t,e){await(await import("fs/promises")).mkdir(t,e)},async readFile(t,e="utf8"){return await(await import("fs/promises")).readFile(t,e)},async writeFile(t,e,r="utf8"){await(await import("fs/promises")).writeFile(t,e,r)},async cp(t,e,r){await(await import("fs/promises")).cp(t,e,r)},async readdir(t,e){return await(await import("fs/promises")).readdir(t,e)},async rm(t,e){await(await import("fs/promises")).rm(t,e)},existsSync(t){return(0,gre.existsSync)(t)},async stat(t){return await(await import("fs/promises")).stat(t)},async mkdtemp(t){return await(await import("fs/promises")).mkdtemp(t)},tmpdir(){return(0,_re.tmpdir)()}}});var ze=O(()=>{"use strict";hs()});function Lt(t){return{type:"text",text:t}}function g2(t,e){return{type:"image",data:t,mimeType:e}}var Dn=O(()=>{"use strict"});function Sre(){return gJe}function MI(){let t=process.env.XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS;if(!t)return!1;let e=t.trim().toLowerCase();return["1","true","yes","on"].includes(e)}function Jh(t){let e={};for(let[r,n]of Object.entries(t??{})){if(n==null)continue;let o=r.startsWith("TEST_RUNNER_")?r:`TEST_RUNNER_${r}`;e[o]=n}return e}var vre,_2,gJe,Xh=O(()=>{"use strict";vre=require("child_process");Go();_2=class{isRunningUnderClaudeCode(){if(process.env.VITEST==="true")return!1;if(process.env.CLAUDECODE==="1"||process.env.CLAUDE_CODE_ENTRYPOINT==="cli")return!0;try{let e=process.ppid;if(e&&(0,vre.execSync)(`ps -o command= -p ${e}`,{encoding:"utf8",timeout:1e3}).trim().includes("claude"))return!0}catch(e){A("debug",`Failed to detect parent process: ${e}`)}return!1}},gJe=new _2});function le(t,e=!1){return{content:[{type:"text",text:t}],isError:e}}function DI(t,e){return(e?e.existsSync(t):yre.existsSync(t))?{isValid:!0}:{isValid:!1,errorResponse:le(`File not found: '${t}'. Please check the path and try again.`,!0)}}function Sp(t){if(!Sre().isRunningUnderClaudeCode()||!t.content||t.content.length<=1)return t;let r=[];if(t.content.forEach((o,i)=>{o.type==="text"&&(i>0&&r.length>0&&r.push(` ---- -`),r.push(o.text))}),r.length===0)return t;let n=r.join("");return{...t,content:[{type:"text",text:n}]}}var yre,Qh=O(()=>{"use strict";yre=W(require("fs"),1);Go();Dn();Xh()});function ge(t,e){let r=e?` -Details: ${e}`:"";return{content:[{type:"text",text:`Error: ${t}${r}`}],isError:!0}}var eg,Ka,at,v2,gt,sr,yp=O(()=>{"use strict";eg=class t extends Error{constructor(e){super(e),this.name="XcodeBuildMCPError",Object.setPrototypeOf(this,t.prototype)}},Ka=class t extends eg{constructor(r,n){super(r);this.paramName=n;this.name="ValidationError",Object.setPrototypeOf(this,t.prototype)}},at=class t extends eg{constructor(r,n){super(r);this.originalError=n;this.name="SystemError",Object.setPrototypeOf(this,t.prototype)}},v2=class t extends eg{constructor(e){super(e),this.name="ConfigurationError",Object.setPrototypeOf(this,t.prototype)}},gt=class t extends eg{constructor(r,n,o,i){super(r);this.command=n;this.axeOutput=o;this.simulatorId=i;this.name="AxeError",Object.setPrototypeOf(this,t.prototype)}};sr=class t extends v2{constructor(r,n){super(r);this.details=n;this.name="DependencyError",Object.setPrototypeOf(this,t.prototype)}}});var hr=O(()=>{"use strict";Qh();yp()});var S2,gs,tg=O(()=>{"use strict";Go();S2=class{defaults={};setDefaults(e){this.defaults={...this.defaults,...e},A("info",`[Session] Defaults updated: ${Object.keys(e).join(", ")}`)}clear(e){if(e==null){this.defaults={},A("info","[Session] All defaults cleared");return}if(e.length===0){A("info","[Session] No keys provided to clear; no changes made");return}for(let r of e)delete this.defaults[r];A("info",`[Session] Defaults cleared: ${e.join(", ")}`)}get(e){return this.defaults[e]}getAll(){return{...this.defaults}}},gs=new S2});function Ht(t,e,r){return async n=>{try{let o=t.parse(n);return await e(o,r())}catch(o){if(o instanceof DP){let i=`Invalid parameters: -${Tre(o)}`;return ge("Parameter validation failed",i)}throw o}}}function _Je(t,e){return t.filter(r=>e[r]==null)}function Ere(t){let e=t.optOutEnabled?"Missing required parameters":"Missing required session defaults",r=t.optOutEnabled?t.message:[t.message,t.setHint].filter(Boolean).join(` -`);return{title:e,body:r}}function de(t){return MI()?t.legacy.shape:t.sessionAware.shape}function fe(t){let{internalSchema:e,logicFunction:r,getExecutor:n,requirements:o=[],exclusivePairs:i=[]}=t;return async s=>{try{let a={};for(let[p,f]of Object.entries(s))f!=null&&(typeof f=="string"&&f.trim()===""||(a[p]=f));for(let p of i){let f=p.filter(m=>Object.prototype.hasOwnProperty.call(a,m));if(f.length>=2)return ge("Parameter validation failed",`Invalid parameters: -Mutually exclusive parameters provided: ${f.join(", ")}. Provide only one.`)}let c={...gs.getAll(),...a};for(let p of i)if(p.some(m=>Object.prototype.hasOwnProperty.call(a,m)))for(let m of p)!Object.prototype.hasOwnProperty.call(a,m)&&m in c&&delete c[m];for(let p of o)if("allOf"in p){let f=_Je(p.allOf,c);if(f.length>0){let m=`Set with: session-set-defaults { ${f.map(v=>`"${v}": "..."`).join(", ")} }`,{title:h,body:_}=Ere({message:p.message??`Required: ${p.allOf.join(", ")}`,setHint:m,optOutEnabled:MI()});return ge(h,_)}}else if("oneOf"in p&&!p.oneOf.some(m=>c[m]!=null)){let m=p.oneOf.join(", "),h=p.oneOf.map(E=>`session-set-defaults { "${E}": "..." }`).join(" OR "),{title:_,body:v}=Ere({message:p.message??`Provide one of: ${m}`,setHint:`Set with: ${h}`,optOutEnabled:MI()});return ge(_,v)}let u=e.parse(c);return await r(u,n())}catch(a){if(a instanceof DP){let c=`Invalid parameters: -${Tre(a)}`;return ge("Parameter validation failed",c)}throw a}}}function Tre(t){return t.issues.map(e=>`${e.path.length>0?e.path.map(String).join("."):"root"}: ${e.message}`).join(` -`)}var Le=O(()=>{"use strict";Te();hr();tg();Xh()});var wre={};Y(wre,{default:()=>vJe,list_devicesLogic:()=>LI});async function LI(t,e,r,n){A("info","Starting device discovery");try{let o=r?.tmpdir?r.tmpdir():(0,xre.tmpdir)(),i=r?.join?"123":Date.now(),s=r?.join?r.join(o,`devicectl-${i}.json`):(0,Are.join)(o,`devicectl-${i}.json`),a=[],c=!1;try{if((await e(["xcrun","devicectl","list","devices","--json-output",s],"List Devices (devicectl with JSON)",!0,void 0)).success){c=!0;let h=n?.readFile?await n.readFile(s,"utf8"):await y2.promises.readFile(s,"utf8"),_=JSON.parse(h);if((E=>typeof E=="object"&&E!==null&&"result"in E&&typeof E.result=="object"&&E.result!==null&&"devices"in E.result&&Array.isArray(E.result.devices))(_)&&_.result?.devices)for(let E of _.result.devices){if(!(Q=>{if(typeof Q!="object"||Q===null)return!1;let me=Q;if(typeof me.identifier!="string"&&me.identifier!==void 0||me.visibilityClass!==void 0&&typeof me.visibilityClass!="string")return!1;if(me.connectionProperties!==void 0){if(typeof me.connectionProperties!="object"||me.connectionProperties===null)return!1;let J=me.connectionProperties;if(J.pairingState!==void 0&&typeof J.pairingState!="string"||J.tunnelState!==void 0&&typeof J.tunnelState!="string"||J.transportType!==void 0&&typeof J.transportType!="string")return!1}if(me.deviceProperties!==void 0){if(typeof me.deviceProperties!="object"||me.deviceProperties===null)return!1;let J=me.deviceProperties;if(J.platformIdentifier!==void 0&&typeof J.platformIdentifier!="string"||J.name!==void 0&&typeof J.name!="string"||J.osVersionNumber!==void 0&&typeof J.osVersionNumber!="string"||J.developerModeStatus!==void 0&&typeof J.developerModeStatus!="string"||J.marketingName!==void 0&&typeof J.marketingName!="string")return!1}if(me.hardwareProperties!==void 0){if(typeof me.hardwareProperties!="object"||me.hardwareProperties===null)return!1;let J=me.hardwareProperties;if(J.productType!==void 0&&typeof J.productType!="string")return!1;if(J.cpuType!==void 0){if(typeof J.cpuType!="object"||J.cpuType===null)return!1;let Oe=J.cpuType;if(Oe.name!==void 0&&typeof Oe.name!="string")return!1}}return!0})(E))continue;let w=E;if(w.visibilityClass==="Simulator"||!w.connectionProperties?.pairingState)continue;let I="Unknown",N=w.deviceProperties?.platformIdentifier?.toLowerCase()??"";typeof N=="string"&&(N.includes("ios")||N.includes("iphone")?I="iOS":N.includes("ipad")?I="iPadOS":N.includes("watch")?I="watchOS":N.includes("tv")||N.includes("apple tv")?I="tvOS":N.includes("vision")&&(I="visionOS"));let $=w.connectionProperties?.pairingState??"",B=w.connectionProperties?.tunnelState??"",G=w.connectionProperties?.transportType??"",he="Unknown";$==="paired"?B==="connected"?he="Available":he="Available (WiFi)":he="Unpaired",a.push({name:w.deviceProperties?.name??"Unknown Device",identifier:w.identifier??"Unknown",platform:I,model:w.deviceProperties?.marketingName??w.hardwareProperties?.productType,osVersion:w.deviceProperties?.osVersionNumber,state:he,connectionType:G,trustState:$,developerModeStatus:w.deviceProperties?.developerModeStatus,productType:w.hardwareProperties?.productType,cpuArchitecture:w.hardwareProperties?.cpuType?.name})}}}catch{A("info","devicectl with JSON failed, trying xctrace fallback")}finally{try{n?.unlink?await n.unlink(s):await y2.promises.unlink(s)}catch{}}if(!c||a.length===0){let m=await e(["xcrun","xctrace","list","devices"],"List Devices (xctrace)",!0,void 0);return m.success?{content:[{type:"text",text:`Device listing (xctrace output): - -${m.output} - -Note: For better device information, please upgrade to Xcode 15 or later which supports the modern devicectl command.`}]}:{content:[{type:"text",text:`Failed to list devices: ${m.error} - -Make sure Xcode is installed and devices are connected and trusted.`}],isError:!0}}let u=`Connected Devices: - -`,p=a.filter((m,h,_)=>h===_.findIndex(v=>v.identifier===m.identifier));if(p.length===0)u+=`No physical Apple devices found. - -`,u+=`Make sure: -`,u+=`1. Devices are connected via USB or WiFi -`,u+=`2. Devices are unlocked and trusted -`,u+=`3. "Trust this computer" has been accepted on the device -`,u+=`4. Developer mode is enabled on the device (iOS 16+) -`,u+=`5. Xcode is properly installed - -`,u+=`For simulators, use the list_sims tool instead. -`;else{let m=p.filter(v=>v.state==="Available"||v.state==="Available (WiFi)"||v.state==="Connected"),h=p.filter(v=>v.state==="Paired (not connected)"),_=p.filter(v=>v.state==="Unpaired");if(m.length>0){u+=`\u2705 Available Devices: -`;for(let v of m)u+=` -\u{1F4F1} ${v.name} -`,u+=` UDID: ${v.identifier} -`,u+=` Model: ${v.model??"Unknown"} -`,v.productType&&(u+=` Product Type: ${v.productType} -`),u+=` Platform: ${v.platform} ${v.osVersion??""} -`,v.cpuArchitecture&&(u+=` CPU Architecture: ${v.cpuArchitecture} -`),u+=` Connection: ${v.connectionType??"Unknown"} -`,v.developerModeStatus&&(u+=` Developer Mode: ${v.developerModeStatus} -`);u+=` -`}if(h.length>0){u+=`\u{1F517} Paired but Not Connected: -`;for(let v of h)u+=` -\u{1F4F1} ${v.name} -`,u+=` UDID: ${v.identifier} -`,u+=` Model: ${v.model??"Unknown"} -`,u+=` Platform: ${v.platform} ${v.osVersion??""} -`;u+=` -`}if(_.length>0){u+=`\u274C Unpaired Devices: -`;for(let v of _)u+=`- ${v.name} (${v.identifier}) -`;u+=` -`}}return p.some(m=>m.state==="Available"||m.state==="Available (WiFi)"||m.state==="Connected")?(u+=`Next Steps: -`,u+=`1. Build for device: build_device({ scheme: 'SCHEME', deviceId: 'DEVICE_UDID' }) -`,u+=`2. Run tests: test_device({ scheme: 'SCHEME', deviceId: 'DEVICE_UDID' }) -`,u+=`3. Get app path: get_device_app_path({ scheme: 'SCHEME' }) - -`,u+=`Note: Use the device ID/UDID from above when required by other tools. -`):p.length>0&&(u+=`Note: No devices are currently available for testing. Make sure devices are: -`,u+=`- Connected via USB -`,u+=`- Unlocked and trusted -`,u+=`- Have developer mode enabled (iOS 16+) -`),{content:[{type:"text",text:u}]}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error listing devices: ${i}`),{content:[{type:"text",text:`Failed to list devices: ${i}`}],isError:!0}}}var y2,xre,Are,bre,vJe,E2=O(()=>{"use strict";Te();De();ze();Le();y2=require("fs"),xre=require("os"),Are=require("path"),bre=k({});vJe={name:"list_devices",description:"Lists connected physical Apple devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) with their UUIDs, names, and connection status. Use this to discover physical devices for testing.",schema:bre.shape,annotations:{title:"List Devices",readOnlyHint:!0},handler:Ht(bre,LI,z)}});var Pre={};Y(Pre,{default:()=>SJe,devicesResourceLogic:()=>Rre});async function Rre(t=z()){try{A("info","Processing devices resource request");let e=await LI({},t);if(e.isError){let r=e.content[0]?.text;throw new Error(typeof r=="string"?r:"Failed to retrieve device data")}return{contents:[{text:typeof e.content[0]?.text=="string"?e.content[0].text:"No device data available"}]}}catch(e){let r=e instanceof Error?e.message:String(e);return A("error",`Error in devices resource handler: ${r}`),{contents:[{text:`Error retrieving device data: ${r}`}]}}}var SJe,Ire=O(()=>{"use strict";De();ze();E2();SJe={uri:"xcodebuildmcp://devices",name:"devices",description:"Connected physical Apple devices with their UUIDs, names, and connection status",mimeType:"text/plain",async handler(){return Rre()}}});var Ep,Ore,Nre,bE=O(()=>{"use strict";Ep="1.15.1",Ore="v1.0.8",Nre="v1.0.5"});var Cre=O(()=>{"use strict";bE()});var $re={};Y($re,{workflow:()=>yJe});var yJe,kre=O(()=>{"use strict";yJe={name:"iOS Device Development",description:"Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Build, test, deploy, and debug apps on real hardware."}});function UI(t,e,r,n=!0,o){let i=["iOS Simulator","watchOS Simulator","tvOS Simulator","visionOS Simulator"].includes(t);if(i&&r)return`platform=${t},id=${r}`;if(i&&e)return`platform=${t},name=${e}${n?",OS=latest":""}`;if(i&&!r&&!e)throw A("warning",`Constructing generic destination for ${t} without name or ID. This might not be specific enough.`),new Error(`Simulator name or ID is required for specific ${t} operations`);switch(t){case"macOS":return o?`platform=macOS,arch=${o}`:"platform=macOS";case"iOS":return"generic/platform=iOS";case"watchOS":return"generic/platform=watchOS";case"tvOS":return"generic/platform=tvOS";case"visionOS":return"generic/platform=visionOS"}return A("error",`Reached unexpected point in constructDestinationString for platform: ${t}`),`platform=${t}`}var Mre=O(()=>{"use strict";Go();Dn()});function ng(){let t=process.env[EJe];return t==="1"||t==="true"||t==="yes"}function TJe(){return xE??"xcodemake"}function bJe(t){xE=t,A("info",`Using overridden xcodemake path: ${t}`)}async function xJe(){let t=Dre.tmpdir(),e=T2.join(t,"xcodebuildmcp"),r=T2.join(e,"xcodemake");A("info",`Attempting to install xcodemake to ${r}`);try{await rg.mkdir(e,{recursive:!0}),A("info","Downloading xcodemake from GitHub...");let n=await fetch("https://raw.githubusercontent.com/cameroncooke/xcodemake/main/xcodemake");if(!n.ok)throw new Error(`Failed to download xcodemake: ${n.status} ${n.statusText}`);let o=await n.text();return await rg.writeFile(r,o,"utf8"),await rg.chmod(r,493),A("info","Made xcodemake executable"),bJe(r),!0}catch(n){return A("error",`Error installing xcodemake: ${n instanceof Error?n.message:String(n)}`),!1}}async function wE(){if(!ng())return A("debug","xcodemake is not enabled, skipping availability check"),!1;try{return xE&&(0,AE.existsSync)(xE)?(A("debug",`xcodemake found at overridden path: ${xE}`),!0):(await z()(["which","xcodemake"])).success?(A("debug","xcodemake found in PATH"),!0):(A("info","xcodemake not found in PATH, attempting to download..."),await xJe()?(A("info","xcodemake installed successfully"),!0):(A("warn","xcodemake installation failed"),!1))}catch(t){return A("error",`Error checking for xcodemake: ${t instanceof Error?t.message:String(t)}`),!1}}function RE(t){return(0,AE.existsSync)(`${t}/Makefile`)}function Lre(t,e){let r=process.cwd();try{process.chdir(t);let s=`${["xcodemake",...e.slice(1)].map(u=>{let p=t+"/";return u.startsWith(p)?u.substring(p.length):u}).join(" ")}.log`;A("debug",`Checking for Makefile log: ${s} in directory: ${process.cwd()}`);let c=(0,AE.readdirSync)(".").includes(s);return A("debug",`Makefile log ${c?"exists":"does not exist"}: ${s}`),c}catch(n){return A("error",`Error checking for Makefile log: ${n instanceof Error?n.message:String(n)}`),!1}finally{process.chdir(r)}}async function Ure(t,e,r){process.chdir(t);let o=[TJe(),...e].map(i=>i.replace(t+"/",""));return z()(o,r)}async function jre(t,e){let r=["cd",t,"&&","make"];return z()(r,e)}var AE,T2,Dre,rg,EJe,xE,b2=O(()=>{"use strict";Go();hs();AE=require("fs"),T2=W(require("path"),1),Dre=W(require("os"),1),rg=W(require("fs/promises"),1),EJe="INCREMENTAL_BUILDS_ENABLED",xE=null});async function Ln(t,e,r=!1,n="build",o,i){let s=[];function a(p){return p.split(` -`).map(f=>/warning:/i.test(f)?{type:"warning",content:f}:/error:/i.test(f)?{type:"error",content:f}:null).filter(Boolean)}A("info",`Starting ${e.logPrefix} ${n} for scheme ${t.scheme}`);let c=ng(),u=!1;c&&n==="build"&&(u=await wE(),u&&r?(A("info","xcodemake is enabled but preferXcodebuild is set to true. Falling back to xcodebuild."),s.push({type:"text",text:"\u26A0\uFE0F incremental build support is enabled but preferXcodebuild is set to true. Falling back to xcodebuild."})):u?(A("info","xcodemake is enabled and available, using it for incremental builds."),s.push({type:"text",text:"\u2139\uFE0F xcodemake is enabled and available, using it for incremental builds."})):(s.push({type:"text",text:"\u26A0\uFE0F xcodemake is enabled but not available. Falling back to xcodebuild."}),A("info","xcodemake is enabled but not available. Falling back to xcodebuild.")));try{let p=["xcodebuild"],f="";t.workspacePath?(f=x2.default.dirname(t.workspacePath),p.push("-workspace",t.workspacePath)):t.projectPath&&(f=x2.default.dirname(t.projectPath),p.push("-project",t.projectPath)),p.push("-scheme",t.scheme),p.push("-configuration",t.configuration),p.push("-skipMacroValidation");let m,h=["iOS Simulator","watchOS Simulator","tvOS Simulator","visionOS Simulator"].includes(e.platform);if(h)if(e.simulatorId)m=UI(e.platform,void 0,e.simulatorId);else if(e.simulatorName)m=UI(e.platform,e.simulatorName,void 0,e.useLatestOS);else return le(`For ${e.platform} platform, either simulatorId or simulatorName must be provided`,!0);else if(e.platform==="macOS")m=UI(e.platform,void 0,void 0,!1,e.arch);else if(e.platform==="iOS")e.deviceId?m=`platform=iOS,id=${e.deviceId}`:m="generic/platform=iOS";else if(e.platform==="watchOS")e.deviceId?m=`platform=watchOS,id=${e.deviceId}`:m="generic/platform=watchOS";else if(e.platform==="tvOS")e.deviceId?m=`platform=tvOS,id=${e.deviceId}`:m="generic/platform=tvOS";else if(e.platform==="visionOS")e.deviceId?m=`platform=visionOS,id=${e.deviceId}`:m="generic/platform=visionOS";else return le(`Unsupported platform: ${e.platform}`,!0);p.push("-destination",m),t.derivedDataPath&&p.push("-derivedDataPath",t.derivedDataPath),t.extraArgs&&t.extraArgs.length>0&&p.push(...t.extraArgs),p.push(n);let _;if(c&&u&&n==="build"&&!r){let I=RE(f);A("debug","Makefile exists: "+I);let N=Lre(f,p);A("debug","Makefile log exists: "+N),I&&N?(s.push({type:"text",text:"\u2139\uFE0F Using make for incremental build"}),_=await jre(f,e.logPrefix)):(s.push({type:"text",text:"\u2139\uFE0F Generating Makefile with xcodemake (first build may take longer)"}),_=await Ure(f,p.slice(1),e.logPrefix))}else _=await o(p,e.logPrefix,!0,i);let v=a(_.output),E=gs.get("suppressWarnings");if(v.forEach(({type:I,content:N})=>{I==="warning"&&E||s.push({type:"text",text:I==="warning"?`\u26A0\uFE0F Warning: ${N}`:`\u274C Error: ${N}`})}),_.error&&_.error.split(` -`).forEach(I=>{I.trim()&&s.push({type:"text",text:`\u274C [stderr] ${I}`})}),!_.success){let I=_.exitCode===64;A(I?"error":"warning",`${e.logPrefix} ${n} failed: ${_.error}`,{sentry:I});let N=le(`\u274C ${e.logPrefix} ${n} failed for scheme ${t.scheme}.`,!0);return s.length>0&&N.content&&N.content.unshift(...s),v.length==0&&c&&u&&n==="build"&&!r&&N.content.push({type:"text",text:"\u{1F4A1} Incremental build using xcodemake failed, suggest using preferXcodebuild option to try build again using slower xcodebuild command."}),Sp(N)}A("info",`\u2705 ${e.logPrefix} ${n} succeeded.`);let x="";if(c&&u&&n==="build"&&!r&&(x+=`xcodemake: Using faster incremental builds with xcodemake. -Future builds will use the generated Makefile for improved performance. - -`),n==="build"){if(e.platform==="macOS")x=`Next Steps: -1. Get app path: get_mac_app_path({ scheme: '${t.scheme}' }) -2. Get bundle ID: get_mac_bundle_id({ appPath: 'PATH_FROM_STEP_1' }) -3. Launch: launch_mac_app({ appPath: 'PATH_FROM_STEP_1' })`;else if(e.platform==="iOS")x=`Next Steps: -1. Get app path: get_device_app_path({ scheme: '${t.scheme}' }) -2. Get bundle ID: get_app_bundle_id({ appPath: 'PATH_FROM_STEP_1' }) -3. Launch: launch_app_device({ bundleId: 'BUNDLE_ID_FROM_STEP_2' })`;else if(h){let I=e.simulatorId?"simulatorId":"simulatorName",N=e.simulatorId??e.simulatorName;x=`Next Steps: -1. Get app path: get_sim_app_path({ ${I}: '${N}', scheme: '${t.scheme}', platform: 'iOS Simulator' }) -2. Get bundle ID: get_app_bundle_id({ appPath: 'PATH_FROM_STEP_1' }) -3. Launch: launch_app_sim({ ${I}: '${N}', bundleId: 'BUNDLE_ID_FROM_STEP_2' }) - Or with logs: launch_app_logs_sim({ ${I}: '${N}', bundleId: 'BUNDLE_ID_FROM_STEP_2' })`}}let w={content:[...s,{type:"text",text:`\u2705 ${e.logPrefix} ${n} succeeded for scheme ${t.scheme}.`}]};return x&&w.content.push({type:"text",text:x}),Sp(w)}catch(p){let f=p instanceof Error?p.message:String(p),m=p instanceof Error&&"code"in p&&["ENOENT","EACCES","EPERM"].includes(p.code??"");return A("error",`Error during ${e.logPrefix} ${n}: ${f}`,{sentry:!m}),Sp(le(`Error during ${e.logPrefix} ${n}: ${f}`,!0))}}var x2,zre=O(()=>{"use strict";Go();Mre();Qh();b2();tg();x2=W(require("path"),1)});var Uc=O(()=>{"use strict";zre()});function pr(t){if(t&&typeof t=="object"&&!Array.isArray(t)){let e={...t};for(let r of Object.keys(e)){let n=e[r];typeof n=="string"&&n.trim()===""&&(e[r]=void 0)}return e}return t}var wo=O(()=>{"use strict"});var qre={};Y(qre,{buildDeviceLogic:()=>Fre,default:()=>RJe});async function Fre(t,e){let r={...t,configuration:t.configuration??"Debug"};return Ln(r,{platform:"iOS",logPrefix:"iOS Device Build"},t.preferXcodebuild??!1,"build",e)}var A2,AJe,wJe,RJe,Bre=O(()=>{"use strict";Te();Dn();Uc();ze();Le();wo();A2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),scheme:T().describe("The scheme to build"),configuration:T().optional().describe("Build configuration (Debug, Release)"),derivedDataPath:T().optional().describe("Path to derived data directory"),extraArgs:H(T()).optional().describe("Additional arguments to pass to xcodebuild"),preferXcodebuild:ue().optional().describe("Prefer xcodebuild over faster alternatives")}),AJe=Bt(pr,A2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."})),wJe=A2.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0});RJe={name:"build_device",description:"Builds an app for a connected device.",schema:de({sessionAware:wJe,legacy:A2}),annotations:{title:"Build Device",destructiveHint:!0},handler:fe({internalSchema:AJe,logicFunction:Fre,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var Vre={};Y(Vre,{cleanLogic:()=>Gre,default:()=>bf});async function Gre(t,e){if(t.workspacePath&&!t.scheme)return ge("Parameter validation failed",`Invalid parameters: -scheme: scheme is required when workspacePath is provided.`);let r=t.platform??"iOS",o={macOS:"macOS",iOS:"iOS","iOS Simulator":"iOS Simulator",watchOS:"watchOS","watchOS Simulator":"watchOS Simulator",tvOS:"tvOS","tvOS Simulator":"tvOS Simulator",visionOS:"visionOS","visionOS Simulator":"visionOS Simulator"}[r];if(!o)return ge("Parameter validation failed",`Invalid parameters: -platform: unsupported value "${r}".`);let s={...typeof t.projectPath=="string"?{projectPath:t.projectPath}:{workspacePath:t.workspacePath},scheme:t.scheme??"",configuration:t.configuration??"Debug",derivedDataPath:t.derivedDataPath,extraArgs:t.extraArgs},c={"iOS Simulator":"iOS","watchOS Simulator":"watchOS","tvOS Simulator":"tvOS","visionOS Simulator":"visionOS"}[o]??o;return Ln(s,{platform:c,logPrefix:"Clean"},!1,"clean",e)}var PJe,w2,IJe,OJe,bf,PE=O(()=>{"use strict";Te();Le();ze();Uc();Dn();hr();wo();PJe={scheme:T().optional().describe("Optional: The scheme to clean"),configuration:T().optional().describe("Optional: Build configuration to clean (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Optional: Path where derived data might be located"),extraArgs:H(T()).optional().describe("Additional xcodebuild arguments"),preferXcodebuild:ue().optional().describe("If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails."),platform:Ze(["macOS","iOS","iOS Simulator","watchOS","watchOS Simulator","tvOS","tvOS Simulator","visionOS","visionOS Simulator"]).optional().describe("Optional: Platform to clean for (defaults to iOS). Choose from macOS, iOS, iOS Simulator, watchOS, watchOS Simulator, tvOS, tvOS Simulator, visionOS, visionOS Simulator")},w2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),...PJe}),IJe=Bt(pr,w2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}).refine(t=>!(t.workspacePath&&!t.scheme),{message:"scheme is required when workspacePath is provided.",path:["scheme"]}));OJe=w2.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0}),bf={name:"clean",description:"Cleans build products with xcodebuild.",schema:de({sessionAware:OJe,legacy:w2}),annotations:{title:"Clean",destructiveHint:!0},handler:fe({internalSchema:IJe,logicFunction:Gre,getExecutor:z,requirements:[{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var Hre={};Y(Hre,{default:()=>bf});var Wre=O(()=>{"use strict";PE()});var Xre={};Y(Xre,{default:()=>xf,discover_projsLogic:()=>Jre});async function Yre(t,e,r,n,o,i=Ir()){if(r>=n){A("debug",`Max depth ${n} reached at ${t}, stopping recursion.`);return}A("debug",`Scanning directory: ${t} at depth ${r}`);let s=Ya.normalize(e);try{let a=await i.readdir(t,{withFileTypes:!0});for(let c of a){let u=c,p=Ya.join(t,u.name),f=Ya.relative(e,p);if(u.isSymbolicLink()){A("debug",`Skipping symbolic link: ${f}`);continue}if(u.isDirectory()&&NJe.has(u.name)){A("debug",`Skipping standard directory: ${f}`);continue}if(!Ya.normalize(p).startsWith(s)){A("warn",`Skipping entry outside workspace root: ${p} (Workspace: ${e})`);continue}if(u.isDirectory()){let m=!1;u.name.endsWith(".xcodeproj")?(o.projects.push(p),A("debug",`Found project: ${p}`),m=!0):u.name.endsWith(".xcworkspace")&&(o.workspaces.push(p),A("debug",`Found workspace: ${p}`),m=!0),m||await Yre(p,e,r+1,n,o,i)}}}catch(a){let c,u="Unknown error";a instanceof Error?(u=a.message,"code"in a&&(c=a.code)):typeof a=="object"&&a!==null?("message"in a&&typeof a.message=="string"&&(u=a.message),"code"in a&&typeof a.code=="string"&&(c=a.code)):u=String(a),c==="EPERM"||c==="EACCES"?A("debug",`Permission denied scanning directory: ${t}`):A("warning",`Error scanning directory ${t}: ${u} (Code: ${c??"N/A"})`)}}async function Jre(t,e){let r=t.scanPath??".",n=t.maxDepth??Zre,o=t.workspaceRoot,i=r,a=Ya.resolve(o,i??"."),c=Ya.normalize(o);Ya.normalize(a).startsWith(c)||(A("warn",`Requested scan path '${i}' resolved outside workspace root '${o}'. Defaulting scan to workspace root.`),a=c);let u={projects:[],workspaces:[]};A("info",`Starting project discovery request: path=${a}, maxDepth=${n}, workspace=${o}`);try{if(!(await e.stat(a)).isDirectory()){let m=`Scan path is not a directory: ${a}`;return A("error",m),{content:[Lt(m)],isError:!0}}}catch(f){let m,h="Unknown error accessing scan path";f instanceof Error?(h=f.message,"code"in f&&(m=f.code)):typeof f=="object"&&f!==null?("message"in f&&typeof f.message=="string"&&(h=f.message),"code"in f&&typeof f.code=="string"&&(m=f.code)):h=String(f);let _=`Failed to access scan path: ${a}. Error: ${h}`;return A("error",`${_} - Code: ${m??"N/A"}`),{content:[Lt(_)],isError:!0}}await Yre(a,o,0,n,u,e),A("info",`Discovery finished. Found ${u.projects.length} projects and ${u.workspaces.length} workspaces.`);let p=[Lt(`Discovery finished. Found ${u.projects.length} projects and ${u.workspaces.length} workspaces.`)];return u.projects.sort(),u.workspaces.sort(),u.projects.length>0&&p.push(Lt(`Projects found: - - ${u.projects.join(` - - `)}`)),u.workspaces.length>0&&p.push(Lt(`Workspaces found: - - ${u.workspaces.join(` - - `)}`)),{content:p,isError:!1}}var Ya,Zre,NJe,Kre,xf,IE=O(()=>{"use strict";Te();Ya=W(require("node:path"),1);De();Dn();hs();Le();Zre=5,NJe=new Set(["build","DerivedData","Pods",".git","node_modules"]);Kre=k({workspaceRoot:T().describe("The absolute path of the workspace root to scan within."),scanPath:T().optional().describe("Optional: Path relative to workspace root to scan. Defaults to workspace root."),maxDepth:ce().int().nonnegative().optional().describe(`Optional: Maximum directory depth to scan. Defaults to ${Zre}.`)});xf={name:"discover_projs",description:"Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files.",schema:Kre.shape,annotations:{title:"Discover Projects",readOnlyHint:!0},handler:Ht(Kre,t=>Jre(t,Ir()),z)}});var Qre={};Y(Qre,{default:()=>xf});var ene=O(()=>{"use strict";IE()});var one={};Y(one,{default:()=>OE,get_app_bundle_idLogic:()=>nne});async function rne(t,e){let r=await e(["/bin/sh","-c",t],"Bundle ID Extraction");if(!r.success)throw new Error(r.error??"Command failed");return r.output||""}async function nne(t,e,r){let n=t.appPath;if(!r.existsSync(n))return{content:[{type:"text",text:`File not found: '${n}'. Please check the path and try again.`}],isError:!0};A("info",`Starting bundle ID extraction for app: ${n}`);try{let o;try{o=await rne(`defaults read "${n}/Info" CFBundleIdentifier`,e)}catch{try{o=await rne(`/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${n}/Info.plist"`,e)}catch(i){throw new Error(`Could not extract bundle ID from Info.plist: ${i instanceof Error?i.message:String(i)}`)}}return A("info",`Extracted app bundle ID: ${o}`),{content:[{type:"text",text:`\u2705 Bundle ID: ${o}`},{type:"text",text:`Next Steps: -- Simulator: install_app_sim + launch_app_sim -- Device: install_app_device + launch_app_device`}],isError:!1}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error extracting app bundle ID: ${i}`),{content:[{type:"text",text:`Error extracting app bundle ID: ${i}`},{type:"text",text:"Make sure the path points to a valid app bundle (.app directory)."}],isError:!0}}}var tne,OE,jI=O(()=>{"use strict";Te();De();hs();Le();tne=k({appPath:T().describe("Path to the .app bundle to extract bundle ID from (full path to the .app directory)")});OE={name:"get_app_bundle_id",description:"Extracts the bundle identifier from an app bundle (.app) for any Apple platform (iOS, iPadOS, watchOS, tvOS, visionOS). IMPORTANT: You MUST provide the appPath parameter. Example: get_app_bundle_id({ appPath: '/path/to/your/app.app' })",schema:tne.shape,annotations:{title:"Get App Bundle ID",readOnlyHint:!0},handler:Ht(tne,t=>nne(t,z(),Ir()),z)}});var ine={};Y(ine,{default:()=>OE});var sne=O(()=>{"use strict";jI()});var cne={};Y(cne,{default:()=>MJe,get_device_app_pathLogic:()=>ane});async function ane(t,e){let n={iOS:"iOS",watchOS:"watchOS",tvOS:"tvOS",visionOS:"visionOS"}[t.platform??"iOS"],o=t.configuration??"Debug";A("info",`Getting app path for scheme ${t.scheme} on platform ${n}`);try{let i=["xcodebuild","-showBuildSettings"];if(t.projectPath)i.push("-project",t.projectPath);else if(t.workspacePath)i.push("-workspace",t.workspacePath);else throw new Error("Either projectPath or workspacePath is required.");i.push("-scheme",t.scheme),i.push("-configuration",o);let s="";if(n==="iOS")s="generic/platform=iOS";else if(n==="watchOS")s="generic/platform=watchOS";else if(n==="tvOS")s="generic/platform=tvOS";else if(n==="visionOS")s="generic/platform=visionOS";else return le(`Unsupported platform: ${n}`,!0);i.push("-destination",s);let a=await e(i,"Get App Path",!0);if(!a.success)return le(`Failed to get app path: ${a.error}`,!0);if(!a.output)return le("Failed to extract build settings output from the result.",!0);let c=a.output,u=c.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/m),p=c.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/m);if(!u||!p)return le("Failed to extract app path from build settings. Make sure the app has been built first.",!0);let f=u[1].trim(),m=p[1].trim(),h=`${f}/${m}`,_=`Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${h}" }) -2. Install app on device: install_app_device({ deviceId: "DEVICE_UDID", appPath: "${h}" }) -3. Launch app on device: launch_app_device({ deviceId: "DEVICE_UDID", bundleId: "BUNDLE_ID" })`;return{content:[{type:"text",text:`\u2705 App path retrieved successfully: ${h}`},{type:"text",text:_}]}}catch(i){let s=i instanceof Error?i.message:String(i);return A("error",`Error retrieving app path: ${s}`),le(`Error retrieving app path: ${s}`,!0)}}var CJe,R2,$Je,kJe,MJe,une=O(()=>{"use strict";Te();Dn();De();hr();ze();Le();wo();CJe={scheme:T().describe("The scheme to use"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),platform:Ze(["iOS","watchOS","tvOS","visionOS"]).optional().describe("Target platform (defaults to iOS)")},R2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),...CJe}),$Je=Bt(pr,R2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."})),kJe=R2.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0});MJe={name:"get_device_app_path",description:"Retrieves the built app path for a connected device.",schema:de({sessionAware:kJe,legacy:R2}),annotations:{title:"Get Device App Path",readOnlyHint:!0},handler:fe({internalSchema:$Je,logicFunction:ane,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var pne={};Y(pne,{default:()=>LJe,install_app_deviceLogic:()=>lne});async function lne(t,e){let{deviceId:r,appPath:n}=t;A("info",`Installing app on device ${r}`);try{let o=await e(["xcrun","devicectl","device","install","app","--device",r,n],"Install app on device",!0,void 0);return o.success?{content:[{type:"text",text:`\u2705 App installed successfully on device ${r} - -${o.output}`}]}:{content:[{type:"text",text:`Failed to install app: ${o.error}`}],isError:!0}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error installing app on device: ${i}`),{content:[{type:"text",text:`Failed to install app on device: ${i}`}],isError:!0}}}var P2,DJe,LJe,dne=O(()=>{"use strict";Te();De();ze();Le();P2=k({deviceId:T().min(1,{message:"Device ID cannot be empty"}).describe("UDID of the device (obtained from list_devices)"),appPath:T().describe("Path to the .app bundle to install (full path to the .app directory)")}),DJe=P2.omit({deviceId:!0});LJe={name:"install_app_device",description:"Installs an app on a connected device.",schema:de({sessionAware:DJe,legacy:P2}),annotations:{title:"Install App Device",destructiveHint:!0},handler:fe({internalSchema:P2,logicFunction:lne,getExecutor:z,requirements:[{allOf:["deviceId"],message:"deviceId is required"}]})}});var gne={};Y(gne,{default:()=>jJe,launch_app_deviceLogic:()=>hne});async function hne(t,e){let{deviceId:r,bundleId:n}=t;A("info",`Launching app ${n} on device ${r}`);try{let o=(0,mne.join)((0,fne.tmpdir)(),`launch-${Date.now()}.json`),i=await e(["xcrun","devicectl","device","process","launch","--device",r,"--json-output",o,"--terminate-existing",n],"Launch app on device",!0,void 0);if(!i.success)return{content:[{type:"text",text:`Failed to launch app: ${i.error}`}],isError:!0};let s;try{let c=await I2.promises.readFile(o,"utf8"),u=JSON.parse(c);u&&typeof u=="object"&&"result"in u&&u.result&&typeof u.result=="object"&&"process"in u.result&&u.result.process&&typeof u.result.process=="object"&&"processIdentifier"in u.result.process&&typeof u.result.process.processIdentifier=="number"&&(s=u.result?.process?.processIdentifier),await I2.promises.unlink(o).catch(()=>{})}catch(c){A("warn",`Failed to parse launch JSON output: ${c}`)}let a=`\u2705 App launched successfully - -${i.output}`;return s&&(a+=` - -Process ID: ${s}`,a+=` - -Next Steps:`,a+=` -1. Interact with your app on the device`,a+=` -2. Stop the app: stop_app_device({ deviceId: "${r}", processId: ${s} })`),{content:[{type:"text",text:a}]}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error launching app on device: ${i}`),{content:[{type:"text",text:`Failed to launch app on device: ${i}`}],isError:!0}}}var I2,fne,mne,O2,UJe,jJe,_ne=O(()=>{"use strict";Te();De();ze();Le();I2=require("fs"),fne=require("os"),mne=require("path"),O2=k({deviceId:T().describe("UDID of the device (obtained from list_devices)"),bundleId:T().describe('Bundle identifier of the app to launch (e.g., "com.example.MyApp")')}),UJe=O2.omit({deviceId:!0});jJe={name:"launch_app_device",description:"Launches an app on a connected device.",schema:de({sessionAware:UJe,legacy:O2}),annotations:{title:"Launch App Device",destructiveHint:!0},handler:fe({internalSchema:O2,logicFunction:hne,getExecutor:z,requirements:[{allOf:["deviceId"],message:"deviceId is required"}]})}});var Sne={};Y(Sne,{default:()=>Af,listSchemesLogic:()=>vne});async function vne(t,e){A("info","Listing schemes");try{let r=["xcodebuild","-list"],n=typeof t.projectPath=="string",o=n?"project":"workspace",i=n?t.projectPath:t.workspacePath;n?r.push("-project",t.projectPath):r.push("-workspace",t.workspacePath);let s=await e(r,"List Schemes",!0);if(!s.success)return le(`Failed to list schemes: ${s.error}`,!0);let a=s.output.match(/Schemes:([\s\S]*?)(?=\n\n|$)/);if(!a)return le("No schemes found in the output",!0);let u=a[1].trim().split(` -`).map(f=>f.trim()).filter(f=>f),p="";if(u.length>0){let f=u[0];p=`Next Steps: -1. Build the app: build_macos({ ${o}Path: "${i}", scheme: "${f}" }) - or for iOS: build_sim({ ${o}Path: "${i}", scheme: "${f}", simulatorName: "iPhone 16" }) -2. Show build settings: show_build_settings({ ${o}Path: "${i}", scheme: "${f}" })`}return{content:[{type:"text",text:"\u2705 Available schemes:"},{type:"text",text:u.join(` -`)},{type:"text",text:p}],isError:!1}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error listing schemes: ${n}`),le(`Error listing schemes: ${n}`,!0)}}var N2,zJe,FJe,Af,NE=O(()=>{"use strict";Te();De();ze();hr();Le();wo();N2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file")}),zJe=Bt(pr,N2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}));FJe=N2.omit({projectPath:!0,workspacePath:!0}),Af={name:"list_schemes",description:"Lists schemes for a project or workspace.",schema:de({sessionAware:FJe,legacy:N2}),annotations:{title:"List Schemes",readOnlyHint:!0},handler:fe({internalSchema:zJe,logicFunction:vne,getExecutor:z,requirements:[{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var yne={};Y(yne,{default:()=>Af});var Ene=O(()=>{"use strict";NE()});var bne={};Y(bne,{default:()=>wf,showBuildSettingsLogic:()=>Tne});async function Tne(t,e){A("info",`Showing build settings for scheme ${t.scheme}`);try{let r=["xcodebuild","-showBuildSettings"],n=typeof t.projectPath=="string",o=n?t.projectPath:t.workspacePath;n?r.push("-project",t.projectPath):r.push("-workspace",t.workspacePath),r.push("-scheme",t.scheme);let i=await e(r,"Show Build Settings",!0);if(!i.success)return le(`Failed to show build settings: ${i.error}`,!0);let s=[{type:"text",text:n?`\u2705 Build settings for scheme ${t.scheme}:`:"\u2705 Build settings retrieved successfully"},{type:"text",text:i.output||"Build settings retrieved successfully."}];return!n&&o&&s.push({type:"text",text:`Next Steps: -- Build the workspace: build_macos({ workspacePath: "${o}", scheme: "${t.scheme}" }) -- For iOS: build_sim({ workspacePath: "${o}", scheme: "${t.scheme}", simulatorName: "iPhone 16" }) -- List schemes: list_schemes({ workspacePath: "${o}" })`}),{content:s,isError:!1}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error showing build settings: ${n}`),le(`Error showing build settings: ${n}`,!0)}}var C2,qJe,BJe,wf,CE=O(()=>{"use strict";Te();De();ze();hr();Le();wo();C2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),scheme:T().describe("Scheme name to show build settings for (Required)")}),qJe=Bt(pr,C2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}));BJe=C2.omit({projectPath:!0,workspacePath:!0,scheme:!0}),wf={name:"show_build_settings",description:"Shows xcodebuild build settings.",schema:de({sessionAware:BJe,legacy:C2}),annotations:{title:"Show Build Settings",readOnlyHint:!0},handler:fe({internalSchema:qJe,logicFunction:Tne,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var xne={};Y(xne,{default:()=>wf});var Ane=O(()=>{"use strict";CE()});function wne(t,e=0){return(Ro[t[e+0]]+Ro[t[e+1]]+Ro[t[e+2]]+Ro[t[e+3]]+"-"+Ro[t[e+4]]+Ro[t[e+5]]+"-"+Ro[t[e+6]]+Ro[t[e+7]]+"-"+Ro[t[e+8]]+Ro[t[e+9]]+"-"+Ro[t[e+10]]+Ro[t[e+11]]+Ro[t[e+12]]+Ro[t[e+13]]+Ro[t[e+14]]+Ro[t[e+15]]).toLowerCase()}var Ro,Rne=O(()=>{Ro=[];for(let t=0;t<256;++t)Ro.push((t+256).toString(16).slice(1))});function $2(){return zI>FI.length-16&&((0,Pne.randomFillSync)(FI),zI=0),FI.slice(zI,zI+=16)}var Pne,FI,zI,Ine=O(()=>{Pne=require("crypto"),FI=new Uint8Array(256),zI=FI.length});var One,k2,Nne=O(()=>{One=require("crypto"),k2={randomUUID:One.randomUUID}});function GJe(t,e,r){if(k2.randomUUID&&!e&&!t)return k2.randomUUID();t=t||{};let n=t.random??t.rng?.()??$2();if(n.length<16)throw new Error("Random bytes length must be >= 16");if(n[6]=n[6]&15|64,n[8]=n[8]&63|128,e){if(r=r||0,r<0||r+16>e.length)throw new RangeError(`UUID byte range ${r}:${r+15} is out of buffer bounds`);for(let o=0;o<16;++o)e[r+o]=n[o];return e}return wne(n)}var Rf,Cne=O(()=>{Nne();Ine();Rne();Rf=GJe});var qI=O(()=>{Cne()});var Une={};Y(Une,{activeDeviceLogSessions:()=>kE,default:()=>L2,startDeviceLogCapture:()=>Dne,start_device_log_capLogic:()=>Lne});function KJe(){let t=process.env.XBMCP_LAUNCH_JSON_WAIT_MS;if(t===void 0)return kne;let e=Number(t);return!Number.isFinite(e)||e<0?kne:e}function ZJe(t){try{let e=JSON.parse(t);return!e||typeof e!="object"?null:e}catch{return null}}function YJe(t){if(!t)return null;let r=t.result?.process?.processIdentifier;if(typeof r=="number"&&Number.isFinite(r))return{pid:r};let n=t.error;if(!n)return null;let o=[];typeof n.localizedDescription=="string"&&n.localizedDescription.length>0&&o.push(n.localizedDescription);let i=n.userInfo??{},s=i?.NSLocalizedRecoverySuggestion,a=i?.NSLocalizedFailureReason,c=i?.BundleIdentifier;typeof a=="string"&&a.length>0&&o.push(a),typeof s=="string"&&s.length>0&&o.push(s),typeof c=="string"&&c.length>0&&o.push(`BundleIdentifier = ${c}`);let u=n.domain,p=n.code,f=typeof u=="string"&&u.length>0?u:void 0,m=typeof p=="number"&&Number.isFinite(p)?p:void 0;return(f||m!==void 0)&&o.push(`(${f??"UnknownDomain"} code ${m??"unknown"})`),o.length===0?{errorMessage:"Launch failed"}:{errorMessage:o.join(` -`)}}async function BI(t,e){try{if(e){e.existsSync(t)&&await e.rm(t,{force:!0});return}_s.existsSync(t)&&await _s.promises.rm(t,{force:!0})}catch{}}async function JJe(t,e,r){let n=Date.now(),o=async()=>{try{if(!(e?.existsSync(t)??_s.existsSync(t)))return null;let c=e?await e.readFile(t,"utf8"):await _s.promises.readFile(t,"utf8"),u=YJe(ZJe(c));if(u)return await BI(t,e),u}catch{}return null},i=await o();if(i)return i;if(r<=0)return null;let s=Math.min(100,Math.max(10,Math.floor(r/4)||10));for(;Date.now()-nsetTimeout(c,s));let a=await o();if(a)return a;s=Math.min(400,s+50)}return null}async function Dne(t,e=z(),r){await QJe();let{deviceUuid:n,bundleId:o}=t,i=Rf(),s=`${Mne}${i}.log`,a=r?r.tmpdir():D2.tmpdir(),c=GI.join(a,s),u=GI.join(a,`devicectl-launch-${i}.json`),p;try{r?(await r.mkdir(a,{recursive:!0}),await r.writeFile(c,"")):(await _s.promises.mkdir(a,{recursive:!0}),await _s.promises.writeFile(c,"")),p=_s.createWriteStream(c,{flags:"a"}),p.write(` ---- Device log capture for bundle ID: ${o} on device: ${n} --- -`);let f=await e(["xcrun","devicectl","device","process","launch","--console","--terminate-existing","--device",n,"--json-output",u,o],"Device Log Capture",!0,void 0,!0);if(!f.success)return A("error",`Device log capture process reported failure: ${f.error??"unknown error"}`),p&&!p.destroyed&&(p.write(` ---- Device log capture failed to start --- -${f.error??"Unknown error"} -`),p.end()),{sessionId:"",error:f.error??"Failed to start device log capture"};let m=f.process;if(!m)throw new Error("Device log capture process handle was not returned");let h={process:m,logFilePath:c,deviceUuid:n,bundleId:o,logStream:p,hasEnded:!1},_="",v=$=>{_+=$,_.length>$ne&&(_=_.slice(_.length-$ne))},E,x=$=>{if(!p||p.destroyed)return;let B=typeof $=="string"?$:$ instanceof Buffer?$.toString("utf8"):String($??"");if(B.length>0){v(B);let G=$E(_);G&&E?.(G),p.write(B)}};m.stdout?.setEncoding?.("utf8"),m.stdout?.on?.("data",x),m.stderr?.setEncoding?.("utf8"),m.stderr?.on?.("data",x);let w=()=>{m.stdout?.off?.("data",x),m.stderr?.off?.("data",x)},I=await XJe(m,HJe,()=>_,$=>{E=$});if(I){w(),h.hasEnded=!0;let $=I.errorMessage&&I.errorMessage.length>0?I.errorMessage:`Device log capture process exited immediately (exit code: ${I.exitCode??"unknown"})`;if(A("error",`Device log capture failed to start: ${$}`),p&&!p.destroyed){try{p.write(` ---- Device log capture failed to start --- -${$} -`)}catch{}p.end()}return await BI(u,r),m.kill?.("SIGTERM"),{sessionId:"",error:$}}let N=await JJe(u,r,KJe());if(N?.errorMessage){w(),h.hasEnded=!0;let $=N.errorMessage;if(A("error",`Device log capture failed to start (JSON): ${$}`),p&&!p.destroyed){try{p.write(` ---- Device log capture failed to start --- -${$} -`)}catch{}p.end()}return m.kill?.("SIGTERM"),{sessionId:"",error:$}}if(N?.pid&&p&&!p.destroyed)try{p.write(`Process ID: ${N.pid} -`)}catch{}return m.once?.("error",$=>{A("error",`Device log capture process error (session ${i}): ${$ instanceof Error?$.message:String($)}`)}),m.once?.("close",$=>{w(),h.hasEnded=!0,p&&!p.destroyed&&!p.closed&&(p.write(` ---- Device log capture ended (exit code: ${$??"unknown"}) --- -`),p.end()),BI(u,r)}),kE.set(i,h),A("info",`Device log capture started with session ID: ${i}`),{sessionId:i}}catch(f){let m=f instanceof Error?f.message:String(f);if(A("error",`Failed to start device log capture: ${m}`),p&&!p.destroyed&&!p.closed){try{p.write(` ---- Device log capture failed: ${m} --- -`)}catch{}p.end()}return await BI(u,r),{sessionId:"",error:m}}}function XJe(t,e,r,n){if(t.exitCode!=null){if(t.exitCode===0){let i=$E(r?.());return Promise.resolve(i?{exitCode:t.exitCode,errorMessage:i}:null)}let o=$E(r?.());return Promise.resolve({exitCode:t.exitCode,errorMessage:o})}return new Promise(o=>{let i=!1,s=p=>{i||(i=!0,t.removeListener("close",a),t.removeListener("error",c),clearTimeout(u),o(p))};n?.(p=>{s({exitCode:t.exitCode??null,errorMessage:p})});let a=p=>{let f=$E(r?.());if(p===0&&f){s({exitCode:p??null,errorMessage:f});return}s(p===0?null:{exitCode:p??null,errorMessage:f})},c=p=>{s({exitCode:null,errorMessage:p.message})},u=setTimeout(()=>{let p=$E(r?.());if(p){t.kill?.("SIGTERM"),s({exitCode:t.exitCode??null,errorMessage:p});return}s(null)},e);t.once("close",a),t.once("error",c)})}function $E(t){if(!t)return;let r=t.replace(/\r/g,"").split(` -`).map(o=>o.trim()).filter(Boolean),n=o=>o?o.startsWith("NS")||o.startsWith("BundleIdentifier")||o.startsWith("Provide ")||o.startsWith("The application")||o.startsWith("ERROR:"):!1;for(let o of WJe){let i=r.findIndex(p=>o.test(p));if(i===-1)continue;let s=[r[i]],a=r[i+1],c=r[i+2];n(a)&&s.push(a),n(c)&&s.push(c);let u=s.join(` -`).trim();return u.length>0?u:r[i]}}async function QJe(){let t=D2.tmpdir(),e;try{e=await _s.promises.readdir(t)}catch(o){A("warn",`Could not read temp dir for device log cleanup: ${o instanceof Error?o.message:String(o)}`);return}let r=Date.now(),n=VJe*24*60*60*1e3;await Promise.all(e.filter(o=>o.startsWith(Mne)&&o.endsWith(".log")).map(async o=>{let i=GI.join(t,o);try{let s=await _s.promises.stat(i);r-s.mtimeMs>n&&(await _s.promises.unlink(i),A("info",`Deleted old device log file: ${i}`))}catch(s){A("warn",`Error during device log cleanup for ${i}: ${s instanceof Error?s.message:String(s)}`)}}))}async function Lne(t,e,r){let{deviceId:n,bundleId:o}=t,{sessionId:i,error:s}=await Dne({deviceUuid:n,bundleId:o},e,r);return s?{content:[{type:"text",text:`Failed to start device log capture: ${s}`}],isError:!0}:{content:[{type:"text",text:`\u2705 Device log capture started successfully - -Session ID: ${i} - -Note: The app has been launched on the device with console output capture enabled. - -Next Steps: -1. Interact with your app on the device -2. Use stop_device_log_cap({ logSessionId: '${i}' }) to stop capture and retrieve logs`}]}}var _s,GI,D2,VJe,Mne,kE,HJe,$ne,kne,WJe,M2,eXe,L2,VI=O(()=>{"use strict";_s=W(require("fs"),1),GI=W(require("path"),1),D2=W(require("os"),1);qI();Te();De();ze();Le();VJe=3,Mne="xcodemcp_device_log_",kE=new Map,HJe=5e3,$ne=8192,kne=8e3,WJe=[/The application failed to launch/i,/Provide a valid bundle identifier/i,/The requested application .* is not installed/i,/NSOSStatusErrorDomain/i,/NSLocalizedFailureReason/i,/ERROR:/i];M2=k({deviceId:T().describe("UDID of the device (obtained from list_devices)"),bundleId:T().describe("Bundle identifier of the app to launch and capture logs for.")}),eXe=M2.omit({deviceId:!0});L2={name:"start_device_log_cap",description:"Starts log capture on a connected device.",schema:de({sessionAware:eXe,legacy:M2}),annotations:{title:"Start Device Log Capture",destructiveHint:!0},handler:fe({internalSchema:M2,logicFunction:Lne,getExecutor:z,requirements:[{allOf:["deviceId"],message:"deviceId is required"}]})}});var jne={};Y(jne,{default:()=>L2});var zne=O(()=>{"use strict";VI()});var qne={};Y(qne,{default:()=>rXe,stop_app_deviceLogic:()=>Fne});async function Fne(t,e){let{deviceId:r,processId:n}=t;A("info",`Stopping app with PID ${n} on device ${r}`);try{let o=await e(["xcrun","devicectl","device","process","terminate","--device",r,"--pid",n.toString()],"Stop app on device",!0,void 0);return o.success?{content:[{type:"text",text:`\u2705 App stopped successfully - -${o.output}`}]}:{content:[{type:"text",text:`Failed to stop app: ${o.error}`}],isError:!0}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error stopping app on device: ${i}`),{content:[{type:"text",text:`Failed to stop app on device: ${i}`}],isError:!0}}}var U2,tXe,rXe,Bne=O(()=>{"use strict";Te();De();ze();Le();U2=k({deviceId:T().describe("UDID of the device (obtained from list_devices)"),processId:ce().describe("Process ID (PID) of the app to stop")}),tXe=U2.omit({deviceId:!0});rXe={name:"stop_app_device",description:"Stops a running app on a connected device.",schema:de({sessionAware:tXe,legacy:U2}),annotations:{title:"Stop App Device",destructiveHint:!0},handler:fe({internalSchema:U2,logicFunction:Fne,getExecutor:z,requirements:[{allOf:["deviceId"],message:"deviceId is required"}]})}});var Vne={};Y(Vne,{default:()=>z2,stopDeviceLogCapture:()=>sXe,stop_device_log_capLogic:()=>j2});async function j2(t,e){let{logSessionId:r}=t,n=kE.get(r);if(!n)return A("warning",`Device log session not found: ${r}`),{content:[{type:"text",text:`Failed to stop device log capture session ${r}: Device log capture session not found: ${r}`}],isError:!0};try{A("info",`Attempting to stop device log capture session: ${r}`),!(n.hasEnded??!1)&&n.process.killed!==!0&&n.process.exitCode==null&&n.process.kill?.("SIGTERM"),await oXe(n),n.logStream&&await nXe(n.logStream);let i=n.logFilePath;if(kE.delete(r),!e.existsSync(i))throw new Error(`Log file not found: ${i}`);let s=await e.readFile(i,"utf-8");return A("info",`Successfully read device log content from ${i}`),A("info",`Device log capture session ${r} stopped. Log file retained at: ${i}`),{content:[{type:"text",text:`\u2705 Device log capture session stopped successfully - -Session ID: ${r} - ---- Captured Logs --- -${s}`}]}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Failed to stop device log capture session ${r}: ${i}`),{content:[{type:"text",text:`Failed to stop device log capture session ${r}: ${i}`}],isError:!0}}}async function nXe(t){let e=t;e.destroyed||e.closed||await new Promise(r=>{let n=()=>r();e.once("close",n),e.end()}).catch(()=>{})}async function oXe(t){if(!t.hasEnded){if(t.process.exitCode!=null){t.hasEnded=!0;return}if(typeof t.process.once=="function"){await new Promise(e=>{let r=()=>{clearTimeout(n),t.hasEnded=!0,e()},n=setTimeout(()=>{t.process.removeListener?.("close",r),t.hasEnded=!0,e()},1e3);t.process.once("close",r),(t.hasEnded||t.process.exitCode!=null)&&(t.process.removeListener?.("close",r),r())});return}for(let e=0;e<20;e+=1){if(t.hasEnded||t.process.exitCode!=null){t.hasEnded=!0;break}await new Promise(r=>setTimeout(r,50))}}}function Tp(t){return typeof t=="object"&&t!==null&&"promises"in t}function iXe(t){return typeof t=="object"&&t!==null&&"existsSync"in t}async function sXe(t,e){let r=e??vs,o=await j2({logSessionId:t},{async mkdir(c,u){Tp(r)?await r.promises.mkdir(c,u):await vs.promises.mkdir(c,u)},async readFile(c,u="utf8"){if(Tp(r)){let p=await r.promises.readFile(c,u);return typeof p=="string"?p:p.toString()}else{let p=await vs.promises.readFile(c,u);return typeof p=="string"?p:p.toString()}},async writeFile(c,u,p="utf8"){Tp(r)?await r.promises.writeFile(c,u,p):await vs.promises.writeFile(c,u,p)},async cp(c,u,p){Tp(r)?await r.promises.cp(c,u,p):await vs.promises.cp(c,u,p)},async readdir(c,u){if(Tp(r))if(u?.withFileTypes===!0){let p=await r.promises.readdir(c,{withFileTypes:!0});return Array.isArray(p)?p:[]}else{let p=await r.promises.readdir(c);return Array.isArray(p)?p:[]}else if(u?.withFileTypes===!0){let p=await vs.promises.readdir(c,{withFileTypes:!0});return Array.isArray(p)?p:[]}else{let p=await vs.promises.readdir(c);return Array.isArray(p)?p:[]}},async rm(c,u){Tp(r)?await r.promises.rm(c,u):await vs.promises.rm(c,u)},existsSync(c){return iXe(r)?r.existsSync(c):vs.existsSync(c)},async stat(c){return Tp(r)?await r.promises.stat(c):await vs.promises.stat(c)},async mkdtemp(c){return Tp(r)?await r.promises.mkdtemp(c):await vs.promises.mkdtemp(c)},tmpdir(){return"/tmp"}});if(o.isError){let c=o.content[0]?.text;return{logContent:"",error:typeof c=="string"?c.replace(`Failed to stop device log capture session ${t}: `,""):"Unknown error occurred"}}let i=o.content[0]?.text;return typeof i!="string"?{logContent:"",error:"Invalid response format: expected text content"}:{logContent:i.match(/--- Captured Logs ---\n([\s\S]*)$/)?.[1]??""}}var vs,Gne,z2,F2=O(()=>{"use strict";vs=W(require("fs"),1);Te();De();VI();hs();Le();Gne=k({logSessionId:T().describe("The session ID returned by start_device_log_cap.")});z2={name:"stop_device_log_cap",description:"Stops an active Apple device log capture session and returns the captured logs.",schema:Gne.shape,annotations:{title:"Stop Device Log Capture",destructiveHint:!0},handler:Ht(Gne,t=>j2(t,Ir()),z)}});var Hne={};Y(Hne,{default:()=>z2});var Wne=O(()=>{"use strict";F2()});var Zne={};Y(Zne,{default:()=>pXe,testDeviceLogic:()=>Kne});async function uXe(t,e=z()){try{let r=await e(["xcrun","xcresulttool","get","test-results","summary","--path",t],"Parse xcresult bundle");if(!r.success)throw new Error(r.error??"Failed to execute xcresulttool");if(!r.output||r.output.trim().length===0)throw new Error("xcresulttool returned no output");let n=JSON.parse(r.output);return lXe(n)}catch(r){let n=r instanceof Error?r.message:String(r);throw A("error",`Error parsing xcresult bundle: ${n}`),r}}function lXe(t){let e=[];if(e.push(`Test Summary: ${t.title??"Unknown"}`),e.push(`Overall Result: ${t.result??"Unknown"}`),e.push(""),e.push("Test Counts:"),e.push(` Total: ${t.totalTestCount??0}`),e.push(` Passed: ${t.passedTests??0}`),e.push(` Failed: ${t.failedTests??0}`),e.push(` Skipped: ${t.skippedTests??0}`),e.push(` Expected Failures: ${t.expectedFailures??0}`),e.push(""),t.environmentDescription&&(e.push(`Environment: ${t.environmentDescription}`),e.push("")),t.devicesAndConfigurations&&Array.isArray(t.devicesAndConfigurations)&&t.devicesAndConfigurations.length>0){let n=t.devicesAndConfigurations[0].device;n&&(e.push(`Device: ${n.deviceName??"Unknown"} (${n.platform??"Unknown"} ${n.osVersion??"Unknown"})`),e.push(""))}return t.testFailures&&Array.isArray(t.testFailures)&&t.testFailures.length>0&&(e.push("Test Failures:"),t.testFailures.forEach((r,n)=>{let o=r;e.push(` ${n+1}. ${o.testName??"Unknown Test"} (${o.targetName??"Unknown Target"})`),o.failureText&&e.push(` ${o.failureText}`)}),e.push("")),t.topInsights&&Array.isArray(t.topInsights)&&t.topInsights.length>0&&(e.push("Insights:"),t.topInsights.forEach((r,n)=>{let o=r;e.push(` ${n+1}. [${o.impact??"Unknown"}] ${o.text??"No description"}`)})),e.join(` -`)}async function Kne(t,e=z(),r=Ir()){A("info",`Starting test run for scheme ${t.scheme} on platform ${t.platform??"iOS"} (internal)`);let n,o=async()=>{if(n)try{await r.rm(n,{recursive:!0,force:!0})}catch(i){A("warn",`Failed to clean up temporary directory: ${i}`)}};try{n=await r.mkdtemp((0,q2.join)(r.tmpdir(),"xcodebuild-test-"));let i=(0,q2.join)(n,"TestResults.xcresult"),s=[...t.extraArgs??[],"-resultBundlePath",i],a=t.testRunnerEnv?{env:Jh(t.testRunnerEnv)}:void 0,c=await Ln({projectPath:t.projectPath,workspacePath:t.workspacePath,scheme:t.scheme,configuration:t.configuration??"Debug",derivedDataPath:t.derivedDataPath,extraArgs:s},{platform:t.platform||"iOS",simulatorName:void 0,simulatorId:void 0,deviceId:t.deviceId,useLatestOS:!1,logPrefix:"Test Run"},t.preferXcodebuild,"test",e,a);try{A("info",`Attempting to parse xcresult bundle at: ${i}`);try{await r.stat(i),A("info",`xcresult bundle exists at: ${i}`)}catch{throw A("warn",`xcresult bundle does not exist at: ${i}`),new Error(`xcresult bundle not found at ${i}`)}let u=await uXe(i,e);return A("info","Successfully parsed xcresult bundle"),await o(),{content:[...c.content||[],{type:"text",text:` -Test Results Summary: -`+u}],isError:c.isError}}catch(u){return A("warn",`Failed to parse xcresult bundle: ${u}`),await o(),c}}catch(i){let s=i instanceof Error?i.message:String(i);return A("error",`Error during test run: ${s}`),le(`Error during test run: ${s}`,!0)}finally{await o()}}var q2,B2,aXe,cXe,pXe,Yne=O(()=>{"use strict";Te();q2=require("path");Dn();De();Uc();hr();Xh();ze();Le();wo();B2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),scheme:T().describe("The scheme to test"),deviceId:T().describe("UDID of the device (obtained from list_devices)"),configuration:T().optional().describe("Build configuration (Debug, Release)"),derivedDataPath:T().optional().describe("Path to derived data directory"),extraArgs:H(T()).optional().describe("Additional arguments to pass to xcodebuild"),preferXcodebuild:ue().optional().describe("Prefer xcodebuild over faster alternatives"),platform:Ze(["iOS","watchOS","tvOS","visionOS"]).optional().describe("Target platform (defaults to iOS)"),testRunnerEnv:ir(T(),T()).optional().describe("Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)")}),aXe=Bt(pr,B2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."})),cXe=B2.omit({projectPath:!0,workspacePath:!0,scheme:!0,deviceId:!0,configuration:!0});pXe={name:"test_device",description:"Runs tests on a physical Apple device.",schema:de({sessionAware:cXe,legacy:B2}),annotations:{title:"Test Device",destructiveHint:!0},handler:fe({internalSchema:aXe,logicFunction:(t,e)=>Kne({...t,platform:t.platform??"iOS"},e,Ir()),getExecutor:z,requirements:[{allOf:["scheme","deviceId"],message:"Provide scheme and deviceId"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var Jne={};Y(Jne,{workflow:()=>dXe});var dXe,Xne=O(()=>{"use strict";dXe={name:"System Doctor",description:"Debug tools and system doctor for troubleshooting XcodeBuildMCP server, development environment, and tool availability."}});var Qne={};Y(Qne,{workflow:()=>fXe});var fXe,eoe=O(()=>{"use strict";fXe={name:"Log Capture & Management",description:"Log capture and management tools for iOS simulators and physical devices. Start, stop, and analyze application and system logs during development and testing."}});async function ME(t,e=z()){await hXe();let{simulatorUuid:r,bundleId:n,captureConsole:o=!1,args:i=[]}=t,s=Rf(),a=`${toe}${s}.log`,c=V2.join(HI.tmpdir(),a);try{await Zs.promises.mkdir(HI.tmpdir(),{recursive:!0}),await Zs.promises.writeFile(c,"");let u=Zs.createWriteStream(c,{flags:"a"}),p=[];if(u.write(` ---- Log capture for bundle ID: `+n+` --- -`),o){let m=["xcrun","simctl","launch","--console-pty","--terminate-running-process",r,n];i.length>0&&m.push(...i);let h=await e(m,"Console Log Capture",!0,void 0,!0);if(!h.success)return{sessionId:"",logFilePath:"",processes:[],error:h.error??"Failed to start console log capture"};h.process.stdout?.pipe(u),h.process.stderr?.pipe(u),p.push(h.process)}let f=await e(["xcrun","simctl","spawn",r,"log","stream","--level=debug","--predicate",`subsystem == "${n}"`],"OS Log Capture",!0,void 0,!0);if(!f.success)return{sessionId:"",logFilePath:"",processes:[],error:f.error??"Failed to start OS log capture"};f.process.stdout?.pipe(u),f.process.stderr?.pipe(u),p.push(f.process);for(let m of p)m.on("close",h=>{A("info",`A log capture process for session ${s} exited with code ${h}.`)});return G2.set(s,{processes:p,logFilePath:c,simulatorUuid:r,bundleId:n}),A("info",`Log capture started with session ID: ${s}`),{sessionId:s,logFilePath:c,processes:p}}catch(u){let p=u instanceof Error?u.message:String(u);return A("error",`Failed to start log capture: ${p}`),{sessionId:"",logFilePath:"",processes:[],error:p}}}async function H2(t){let e=G2.get(t);if(!e)return A("warning",`Log session not found: ${t}`),{logContent:"",error:`Log capture session not found: ${t}`};try{A("info",`Attempting to stop log capture session: ${t}`);let r=e.logFilePath;for(let o of e.processes)!o.killed&&o.exitCode===null&&o.kill("SIGTERM");G2.delete(t),A("info",`Log capture session ${t} stopped. Log file retained at: ${r}`),await Zs.promises.access(r,Zs.constants.R_OK);let n=await Zs.promises.readFile(r,"utf-8");return A("info",`Successfully read log content from ${r}`),{logContent:n}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Failed to stop log capture session ${t}: ${n}`),{logContent:"",error:n}}}async function hXe(){let t=HI.tmpdir(),e;try{e=await Zs.promises.readdir(t)}catch(o){A("warn",`Could not read temp dir for log cleanup: ${o instanceof Error?o.message:String(o)}`);return}let r=Date.now(),n=mXe*24*60*60*1e3;await Promise.all(e.filter(o=>o.startsWith(toe)&&o.endsWith(".log")).map(async o=>{let i=V2.join(t,o);try{let s=await Zs.promises.stat(i);r-s.mtimeMs>n&&(await Zs.promises.unlink(i),A("info",`Deleted old log file: ${i}`))}catch(s){A("warn",`Error during log cleanup for ${i}: ${s instanceof Error?s.message:String(s)}`)}}))}var Zs,V2,HI,mXe,toe,G2,roe=O(()=>{"use strict";Zs=W(require("fs"),1),V2=W(require("path"),1),HI=W(require("os"),1);qI();Go();hs();mXe=3,toe="xcodemcp_sim_log_",G2=new Map});var WI=O(()=>{"use strict";roe()});var ooe={};Y(ooe,{default:()=>_Xe,start_sim_log_capLogic:()=>noe});async function noe(t,e=z(),r=ME){let n=t.captureConsole??!1,{sessionId:o,error:i}=await r({simulatorUuid:t.simulatorId,bundleId:t.bundleId,captureConsole:n},e);return i?{content:[Lt(`Error starting log capture: ${i}`)],isError:!0}:{content:[Lt(`Log capture started successfully. Session ID: ${o}. - -${n?"Note: Your app was relaunched to capture console output.":"Note: Only structured logs are being captured."} - -Next Steps: -1. Interact with your simulator and app. -2. Use 'stop_sim_log_cap' with session ID '${o}' to stop capture and retrieve logs.`)]}}var W2,gXe,_Xe,ioe=O(()=>{"use strict";Te();WI();hs();Dn();Le();W2=k({simulatorId:Dt().describe("UUID of the simulator to capture logs from (obtained from list_simulators)."),bundleId:T().describe("Bundle identifier of the app to capture logs for."),captureConsole:ue().optional().describe("Whether to capture console output (requires app relaunch).")});gXe=st(W2.omit({simulatorId:!0}).shape),_Xe={name:"start_sim_log_cap",description:"Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs.",schema:de({sessionAware:gXe,legacy:W2}),annotations:{title:"Start Simulator Log Capture",destructiveHint:!0},handler:fe({internalSchema:W2,logicFunction:noe,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var coe={};Y(coe,{default:()=>vXe,stop_sim_log_capLogic:()=>aoe});async function aoe(t){let{logContent:e,error:r}=await H2(t.logSessionId);return r?{content:[Lt(`Error stopping log capture session ${t.logSessionId}: ${r}`)],isError:!0}:{content:[Lt(`Log capture session ${t.logSessionId} stopped successfully. Log content follows: - -${e}`)]}}var soe,vXe,uoe=O(()=>{"use strict";Te();WI();Dn();Le();hs();soe=k({logSessionId:T().describe("The session ID returned by start_sim_log_cap.")});vXe={name:"stop_sim_log_cap",description:"Stops an active simulator log capture session and returns the captured logs.",schema:soe.shape,annotations:{title:"Stop Simulator Log Capture",destructiveHint:!0},handler:Ht(soe,aoe,z)}});var loe={};Y(loe,{workflow:()=>SXe});var SXe,poe=O(()=>{"use strict";SXe={name:"macOS Development",description:"Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications."}});var foe={};Y(foe,{buildMacOSLogic:()=>doe,default:()=>bXe});async function doe(t,e,r=yXe){A("info",`Starting macOS build for scheme ${t.scheme} (internal)`);let n={...t,configuration:t.configuration??"Debug",preferXcodebuild:t.preferXcodebuild??!1};return r.executeXcodeBuildCommand(n,{platform:"macOS",arch:t.arch,logPrefix:"macOS Build"},n.preferXcodebuild??!1,"build",e)}var yXe,K2,EXe,TXe,bXe,moe=O(()=>{"use strict";Te();De();Uc();Dn();ze();Le();wo();yXe={executeXcodeBuildCommand:Ln},K2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),scheme:T().describe("The scheme to use"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Path where build products and other derived data will go"),arch:Ze(["arm64","x86_64"]).optional().describe("Architecture to build for (arm64 or x86_64). For macOS only."),extraArgs:H(T()).optional().describe("Additional xcodebuild arguments"),preferXcodebuild:ue().optional().describe("If true, prefers xcodebuild over the experimental incremental build system")}),EXe=K2.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0,arch:!0}),TXe=Bt(pr,K2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}));bXe={name:"build_macos",description:"Builds a macOS app.",schema:de({sessionAware:EXe,legacy:K2}),annotations:{title:"Build macOS",destructiveHint:!0},handler:fe({internalSchema:TXe,logicFunction:doe,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var goe={};Y(goe,{buildRunMacOSLogic:()=>hoe,default:()=>PXe});async function wXe(t,e){return A("info",`Starting macOS build for scheme ${t.scheme} (internal)`),Ln({...t,configuration:t.configuration??"Debug"},{platform:"macOS",arch:t.arch,logPrefix:"macOS Build"},t.preferXcodebuild??!1,"build",e)}async function RXe(t,e){try{let r=["xcodebuild","-showBuildSettings"];t.projectPath?r.push("-project",t.projectPath):t.workspacePath&&r.push("-workspace",t.workspacePath),r.push("-scheme",t.scheme),r.push("-configuration",t.configuration??"Debug"),t.derivedDataPath&&r.push("-derivedDataPath",t.derivedDataPath),t.extraArgs&&t.extraArgs.length>0&&r.push(...t.extraArgs);let n=await e(r,"Get Build Settings for Launch",!0,void 0);if(!n.success)return{success:!1,error:n.error??"Failed to get build settings"};let o=n.output,i=o.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/m),s=o.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/m);return!i||!s?{success:!1,error:"Could not extract app path from build settings"}:{success:!0,appPath:`${i[1].trim()}/${s[1].trim()}`}}catch(r){return{success:!1,error:r instanceof Error?r.message:String(r)}}}async function hoe(t,e){A("info","Handling macOS build & run logic...");try{let r=await wXe(t,e);if(r.isError)return r;let n=r.content?.filter(c=>c.type==="text")??[],o=await RXe(t,e);if(!o.success){A("error","Build succeeded, but failed to get app path to launch.");let c=le(`\u2705 Build succeeded, but failed to get app path to launch: ${o.error}`,!1);return c.content&&c.content.unshift(...n),c}let i=o.appPath;A("info",`App path determined as: ${i}`);let s=await e(["open",i],"Launch macOS App",!0);if(!s.success){A("error",`Build succeeded, but failed to launch app ${i}: ${s.error}`);let c=le(`\u2705 Build succeeded, but failed to launch app ${i}. Error: ${s.error}`,!1);return c.content&&c.content.unshift(...n),c}return A("info",`\u2705 macOS app launched successfully: ${i}`),{content:[...n,{type:"text",text:`\u2705 macOS build and run succeeded for scheme ${t.scheme}. App launched: ${i}`}],isError:!1}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error during macOS build & run logic: ${n}`),le(`Error during macOS build and run: ${n}`,!0)}}var Z2,xXe,AXe,PXe,_oe=O(()=>{"use strict";Te();De();hr();Uc();Dn();ze();Le();wo();Z2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),scheme:T().describe("The scheme to use"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Path where build products and other derived data will go"),arch:Ze(["arm64","x86_64"]).optional().describe("Architecture to build for (arm64 or x86_64). For macOS only."),extraArgs:H(T()).optional().describe("Additional xcodebuild arguments"),preferXcodebuild:ue().optional().describe("If true, prefers xcodebuild over the experimental incremental build system")}),xXe=Z2.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0,arch:!0}),AXe=Bt(pr,Z2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}));PXe={name:"build_run_macos",description:"Builds and runs a macOS app.",schema:de({sessionAware:xXe,legacy:Z2}),annotations:{title:"Build Run macOS",destructiveHint:!0},handler:fe({internalSchema:AXe,logicFunction:hoe,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var voe={};Y(voe,{default:()=>bf});var Soe=O(()=>{"use strict";PE()});var yoe={};Y(yoe,{default:()=>xf});var Eoe=O(()=>{"use strict";IE()});var boe={};Y(boe,{default:()=>$Xe,get_mac_app_pathLogic:()=>Toe});async function Toe(t,e){let r=t.configuration??"Debug";A("info",`Getting app path for scheme ${t.scheme} on platform ${CXe.macOS}`);try{let n=["xcodebuild","-showBuildSettings"];if(t.projectPath)n.push("-project",t.projectPath);else if(t.workspacePath)n.push("-workspace",t.workspacePath);else throw new Error("Either projectPath or workspacePath is required.");if(n.push("-scheme",t.scheme),n.push("-configuration",r),t.derivedDataPath&&n.push("-derivedDataPath",t.derivedDataPath),t.arch){let m=`platform=macOS,arch=${t.arch}`;n.push("-destination",m)}t.extraArgs&&Array.isArray(t.extraArgs)&&n.push(...t.extraArgs);let o=await e(n,"Get App Path",!0,void 0);if(!o.success)return{content:[{type:"text",text:`Error: Failed to get macOS app path -Details: ${o.error}`}],isError:!0};if(!o.output)return{content:[{type:"text",text:`Error: Failed to get macOS app path -Details: Failed to extract build settings output from the result`}],isError:!0};let i=o.output,s=i.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/m),a=i.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/m);if(!s||!a)return{content:[{type:"text",text:`Error: Failed to get macOS app path -Details: Could not extract app path from build settings`}],isError:!0};let c=s[1].trim(),u=a[1].trim(),p=`${c}/${u}`,f=`Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${p}" }) -2. Launch app: launch_mac_app({ appPath: "${p}" })`;return{content:[{type:"text",text:`\u2705 App path retrieved successfully: ${p}`},{type:"text",text:f}]}}catch(n){let o=n instanceof Error?n.message:String(n);return A("error",`Error retrieving app path: ${o}`),{content:[{type:"text",text:`Error: Failed to get macOS app path -Details: ${o}`}],isError:!0}}}var IXe,Y2,OXe,NXe,CXe,$Xe,xoe=O(()=>{"use strict";Te();De();ze();Le();wo();IXe={scheme:T().describe("The scheme to use"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Path to derived data directory"),extraArgs:H(T()).optional().describe("Additional arguments to pass to xcodebuild"),arch:Ze(["arm64","x86_64"]).optional().describe("Architecture to build for (arm64 or x86_64). For macOS only.")},Y2=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),...IXe}),OXe=Y2.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0,arch:!0}),NXe=Bt(pr,Y2.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."})),CXe={iOS:"iOS",watchOS:"watchOS",tvOS:"tvOS",visionOS:"visionOS",iOSSimulator:"iOS Simulator",watchOSSimulator:"watchOS Simulator",tvOSSimulator:"tvOS Simulator",visionOSSimulator:"visionOS Simulator",macOS:"macOS"};$Xe={name:"get_mac_app_path",description:"Retrieves the built macOS app bundle path.",schema:de({sessionAware:OXe,legacy:Y2}),annotations:{title:"Get macOS App Path",readOnlyHint:!0},handler:fe({internalSchema:NXe,logicFunction:Toe,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var Poe={};Y(Poe,{default:()=>J2,get_mac_bundle_idLogic:()=>Roe});async function Aoe(t,e){let r=await e(["/bin/sh","-c",t],"macOS Bundle ID Extraction");if(!r.success)throw new Error(r.error??"Command failed");return r.output||""}async function Roe(t,e,r){let n=t.appPath;if(!r.existsSync(n))return{content:[{type:"text",text:`File not found: '${n}'. Please check the path and try again.`}],isError:!0};A("info",`Starting bundle ID extraction for macOS app: ${n}`);try{let o;try{o=await Aoe(`defaults read "${n}/Contents/Info" CFBundleIdentifier`,e)}catch{try{o=await Aoe(`/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${n}/Contents/Info.plist"`,e)}catch(i){throw new Error(`Could not extract bundle ID from Info.plist: ${i instanceof Error?i.message:String(i)}`)}}return A("info",`Extracted macOS bundle ID: ${o}`),{content:[{type:"text",text:`\u2705 Bundle ID: ${o}`},{type:"text",text:`Next Steps: -- Launch: launch_mac_app({ appPath: "${n}" }) -- Build again: build_macos({ scheme: "SCHEME_NAME" })`}],isError:!1}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error extracting macOS bundle ID: ${i}`),{content:[{type:"text",text:`Error extracting macOS bundle ID: ${i}`},{type:"text",text:"Make sure the path points to a valid macOS app bundle (.app directory)."}],isError:!0}}}var woe,J2,X2=O(()=>{"use strict";Te();De();hs();Le();woe=k({appPath:T().describe("Path to the macOS .app bundle to extract bundle ID from (full path to the .app directory)")});J2={name:"get_mac_bundle_id",description:"Extracts the bundle identifier from a macOS app bundle (.app). IMPORTANT: You MUST provide the appPath parameter. Example: get_mac_bundle_id({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_get_macos_bundle_id.",schema:woe.shape,annotations:{title:"Get Mac Bundle ID",readOnlyHint:!0},handler:Ht(woe,t=>Roe(t,z(),Ir()),z)}});var Ioe={};Y(Ioe,{default:()=>J2});var Ooe=O(()=>{"use strict";X2()});var Q2=O(()=>{"use strict";Qh()});var $oe={};Y($oe,{default:()=>kXe,launch_mac_appLogic:()=>Coe});async function Coe(t,e,r){let n=DI(t.appPath,r);if(!n.isValid)return n.errorResponse;A("info",`Starting launch macOS app request for ${t.appPath}`);try{let o=["open",t.appPath];return t.args&&Array.isArray(t.args)&&t.args.length>0&&o.push("--args",...t.args),await e(o,"Launch macOS App"),{content:[{type:"text",text:`\u2705 macOS app launched successfully: ${t.appPath}`}]}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error during launch macOS app operation: ${i}`),{content:[{type:"text",text:`\u274C Launch macOS app operation failed: ${i}`}],isError:!0}}}var Noe,kXe,koe=O(()=>{"use strict";Te();De();Q2();ze();Le();Noe=k({appPath:T().describe("Path to the macOS .app bundle to launch (full path to the .app directory)"),args:H(T()).optional().describe("Additional arguments to pass to the app")});kXe={name:"launch_mac_app",description:"Launches a macOS application. IMPORTANT: You MUST provide the appPath parameter. Example: launch_mac_app({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_launch_macos_app.",schema:Noe.shape,annotations:{title:"Launch macOS App",destructiveHint:!0},handler:Ht(Noe,Coe,z)}});var Moe={};Y(Moe,{default:()=>Af});var Doe=O(()=>{"use strict";NE()});var Loe={};Y(Loe,{default:()=>wf});var Uoe=O(()=>{"use strict";CE()});var Foe={};Y(Foe,{default:()=>MXe,stop_mac_appLogic:()=>zoe});async function zoe(t,e){if(!t.appName&&!t.processId)return{content:[{type:"text",text:"Either appName or processId must be provided."}],isError:!0};A("info",`Stopping macOS app: ${t.processId?`PID ${t.processId}`:t.appName}`);try{let r;return t.processId?r=["kill",String(t.processId)]:r=["sh","-c",`pkill -f "${t.appName}" || osascript -e 'tell application "${t.appName}" to quit'`],await e(r,"Stop macOS App"),{content:[{type:"text",text:`\u2705 macOS app stopped successfully: ${t.processId?`PID ${t.processId}`:t.appName}`}]}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error stopping macOS app: ${n}`),{content:[{type:"text",text:`\u274C Stop macOS app operation failed: ${n}`}],isError:!0}}}var joe,MXe,qoe=O(()=>{"use strict";Te();De();ze();Le();joe=k({appName:T().optional().describe('Name of the application to stop (e.g., "Calculator" or "MyApp")'),processId:ce().optional().describe("Process ID (PID) of the application to stop")});MXe={name:"stop_mac_app",description:"Stops a running macOS application. Can stop by app name or process ID.",schema:joe.shape,annotations:{title:"Stop macOS App",destructiveHint:!0},handler:Ht(joe,zoe,z)}});var Goe={};Y(Goe,{default:()=>zXe,testMacosLogic:()=>Boe});async function UXe(t,e=z()){try{let r=await e(["xcrun","xcresulttool","get","test-results","summary","--path",t],"Parse xcresult bundle",!0);if(!r.success)throw new Error(r.error??"Failed to parse xcresult bundle");let n;try{n=JSON.parse(r.output||"{}")}catch(o){throw new Error(`Failed to parse JSON output: ${o}`)}if(typeof n!="object"||n===null)throw new Error("Invalid JSON output: expected object");return jXe(n)}catch(r){let n=r instanceof Error?r.message:String(r);throw A("error",`Error parsing xcresult bundle: ${n}`),r}}function jXe(t){let e=[];if(e.push(`Test Summary: ${t.title??"Unknown"}`),e.push(`Overall Result: ${t.result??"Unknown"}`),e.push(""),e.push("Test Counts:"),e.push(` Total: ${t.totalTestCount??0}`),e.push(` Passed: ${t.passedTests??0}`),e.push(` Failed: ${t.failedTests??0}`),e.push(` Skipped: ${t.skippedTests??0}`),e.push(` Expected Failures: ${t.expectedFailures??0}`),e.push(""),t.environmentDescription&&(e.push(`Environment: ${t.environmentDescription}`),e.push("")),t.devicesAndConfigurations&&Array.isArray(t.devicesAndConfigurations)&&t.devicesAndConfigurations.length>0){let r=t.devicesAndConfigurations[0];if(typeof r=="object"&&r!==null&&"device"in r){let n=r.device;if(typeof n=="object"&&n!==null){let o=n,i="deviceName"in o&&typeof o.deviceName=="string"?o.deviceName:"Unknown",s="platform"in o&&typeof o.platform=="string"?o.platform:"Unknown",a="osVersion"in o&&typeof o.osVersion=="string"?o.osVersion:"Unknown";e.push(`Device: ${i} (${s} ${a})`),e.push("")}}}return t.testFailures&&Array.isArray(t.testFailures)&&t.testFailures.length>0&&(e.push("Test Failures:"),t.testFailures.forEach((r,n)=>{if(typeof r=="object"&&r!==null){let o=r,i="testName"in o&&typeof o.testName=="string"?o.testName:"Unknown Test",s="targetName"in o&&typeof o.targetName=="string"?o.targetName:"Unknown Target";e.push(` ${n+1}. ${i} (${s})`),"failureText"in o&&typeof o.failureText=="string"&&e.push(` ${o.failureText}`)}}),e.push("")),t.topInsights&&Array.isArray(t.topInsights)&&t.topInsights.length>0&&(e.push("Insights:"),t.topInsights.forEach((r,n)=>{if(typeof r=="object"&&r!==null){let o=r,i="impact"in o&&typeof o.impact=="string"?o.impact:"Unknown",s="text"in o&&typeof o.text=="string"?o.text:"No description";e.push(` ${n+1}. [${i}] ${s}`)}})),e.join(` -`)}async function Boe(t,e=z(),r=Ir()){A("info",`Starting test run for scheme ${t.scheme} on platform macOS (internal)`);try{let n=await r.mkdtemp((0,eF.join)(r.tmpdir(),"xcodebuild-test-")),o=(0,eF.join)(n,"TestResults.xcresult"),i=[...t.extraArgs??[],"-resultBundlePath",o],s=t.testRunnerEnv?{env:Jh(t.testRunnerEnv)}:void 0,a=await Ln({projectPath:t.projectPath,workspacePath:t.workspacePath,scheme:t.scheme,configuration:t.configuration??"Debug",derivedDataPath:t.derivedDataPath,extraArgs:i},{platform:"macOS",logPrefix:"Test Run"},t.preferXcodebuild??!1,"test",e,s);try{A("info",`Attempting to parse xcresult bundle at: ${o}`);try{await r.stat(o),A("info",`xcresult bundle exists at: ${o}`)}catch{throw A("warn",`xcresult bundle does not exist at: ${o}`),new Error(`xcresult bundle not found at ${o}`)}let c=await UXe(o,e);return A("info","Successfully parsed xcresult bundle"),await r.rm(n,{recursive:!0,force:!0}),{content:[...a.content??[],{type:"text",text:` -Test Results Summary: -`+c}],isError:a.isError}}catch(c){A("warn",`Failed to parse xcresult bundle: ${c}`);try{await r.rm(n,{recursive:!0,force:!0})}catch(u){A("warn",`Failed to clean up temporary directory: ${u}`)}return a}}catch(n){let o=n instanceof Error?n.message:String(n);return A("error",`Error during test run: ${o}`),le(`Error during test run: ${o}`,!0)}}var eF,tF,DXe,LXe,zXe,Voe=O(()=>{"use strict";Te();eF=require("path");Dn();De();Uc();hr();Xh();ze();Le();wo();tF=k({projectPath:T().optional().describe("Path to the .xcodeproj file"),workspacePath:T().optional().describe("Path to the .xcworkspace file"),scheme:T().describe("The scheme to use"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Path where build products and other derived data will go"),extraArgs:H(T()).optional().describe("Additional xcodebuild arguments"),preferXcodebuild:ue().optional().describe("If true, prefers xcodebuild over the experimental incremental build system"),testRunnerEnv:ir(T(),T()).optional().describe("Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)")}),DXe=tF.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0}),LXe=Bt(pr,tF.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}));zXe={name:"test_macos",description:"Runs tests for a macOS target.",schema:de({sessionAware:DXe,legacy:tF}),annotations:{title:"Test macOS",destructiveHint:!0},handler:fe({internalSchema:LXe,logicFunction:(t,e)=>Boe(t,e,Ir()),getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"}],exclusivePairs:[["projectPath","workspacePath"]]})}});var Hoe={};Y(Hoe,{workflow:()=>FXe});var FXe,Woe=O(()=>{"use strict";FXe={name:"Project Discovery",description:"Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information."}});var Koe={};Y(Koe,{workflow:()=>qXe});var qXe,Zoe=O(()=>{"use strict";qXe={name:"Project Scaffolding",description:"Tools for creating new iOS and macOS projects from templates. Bootstrap new applications with best practices, standard configurations, and modern project structures."}});var DE,rF,Yoe,Xu,Joe=O(()=>{"use strict";DE=require("path"),rF=require("os"),Yoe=require("crypto");Go();bE();Xu=class{static GITHUB_ORG="cameroncooke";static IOS_TEMPLATE_REPO="XcodeBuildMCP-iOS-Template";static MACOS_TEMPLATE_REPO="XcodeBuildMCP-macOS-Template";static async getTemplatePath(e,r,n){let o=e==="iOS"?"XCODEBUILDMCP_IOS_TEMPLATE_PATH":"XCODEBUILDMCP_MACOS_TEMPLATE_PATH",i=process.env[o];if(A("debug",`[TemplateManager] Checking env var '${o}'. Value: '${i}'`),i){let s=n.existsSync(i);if(A("debug",`[TemplateManager] Env var set. Path '${i}' exists? ${s}`),s){let a=(0,DE.join)(i,"template"),c=n.existsSync(a);if(A("debug",`[TemplateManager] Checking for subdir '${a}'. Exists? ${c}`),c)return A("info",`Using local ${e} template from: ${a}`),a;A("info",`Template directory not found in ${i}, using GitHub release`)}}return A("debug","[TemplateManager] Env var not set or path invalid, proceeding to download."),await this.downloadTemplate(e,r,n)}static async downloadTemplate(e,r,n){let o=e==="iOS"?this.IOS_TEMPLATE_REPO:this.MACOS_TEMPLATE_REPO,i=e==="iOS"?Ore:Nre,s=e==="iOS"?"XCODEBUILD_MCP_IOS_TEMPLATE_VERSION":"XCODEBUILD_MCP_MACOS_TEMPLATE_VERSION",a=String(process.env[s]??process.env.XCODEBUILD_MCP_TEMPLATE_VERSION??i),c=(0,DE.join)((0,rF.tmpdir)(),`xcodebuild-mcp-template-${(0,Yoe.randomUUID)()}`);await n.mkdir(c,{recursive:!0});try{let u=`https://github.com/${this.GITHUB_ORG}/${o}/releases/download/${a}/${o}-${a.substring(1)}.zip`,p=(0,DE.join)(c,"template.zip");A("info",`Downloading ${e} template ${a} from GitHub...`),A("info",`Download URL: ${u}`);let f=await r(["curl","-L","-f","-o",p,u],"Download Template",!0,void 0);if(!f.success)throw new Error(`Failed to download template: ${f.error}`);let m=process.cwd();try{process.chdir(c);let _=await r(["unzip","-q",p],"Extract Template",!0,void 0);if(!_.success)throw new Error(`Failed to extract template: ${_.error}`)}finally{process.chdir(m)}let h=(0,DE.join)(c,`${o}-${a.substring(1)}`);if(!n.existsSync(h))throw new Error(`Expected template directory not found: ${h}`);return A("info",`Successfully downloaded ${e} template ${a}`),h}catch(u){throw A("error",`Failed to download ${e} template ${a}: ${u}`),await this.cleanup(c,n),u}}static async cleanup(e,r){e.startsWith((0,rF.tmpdir)())&&await r.rm(e,{recursive:!0,force:!0})}}});var nF=O(()=>{"use strict";Joe()});var rie={};Y(rie,{default:()=>YXe,scaffold_ios_projectLogic:()=>tie});function Qoe(t){switch(t){case"Portrait":return"UIInterfaceOrientationPortrait";case"PortraitUpsideDown":return"UIInterfaceOrientationPortraitUpsideDown";case"LandscapeLeft":return"UIInterfaceOrientationLandscapeLeft";case"LandscapeRight":return"UIInterfaceOrientationLandscapeRight";default:return t}}function GXe(t){switch(t){case"iPhone":return"1";case"iPad":return"2";case"iPhone+iPad":return"1,2";default:return"1,2"}}function VXe(t,e){let r=t,n=e.projectName,o=e.platform,i=e.deploymentTarget,s=`${n}Feature`,a=`${n}FeatureTests`;if(r=r.replace(/MyProjectFeatureTests/g,a),r=r.replace(/MyProjectFeature/g,s),o==="iOS"&&i){let c=i.split(".")[0];r=r.replace(/\.iOS\(\.v\d+\)/,`.iOS(.v${c})`)}return r}function HXe(t,e){let r=t,n=e.projectName,o=e.displayName,i=e.bundleIdentifier,s=e.marketingVersion,a=e.currentProjectVersion,c=e.platform,u=e.deploymentTarget,p=e.targetedDeviceFamily,f=e.supportedOrientations,m=e.supportedOrientationsIpad;if(r=r.replace(/PRODUCT_NAME = .+/g,`PRODUCT_NAME = ${n}`),r=r.replace(/PRODUCT_DISPLAY_NAME = .+/g,`PRODUCT_DISPLAY_NAME = ${o??n}`),r=r.replace(/PRODUCT_BUNDLE_IDENTIFIER = .+/g,`PRODUCT_BUNDLE_IDENTIFIER = ${i??`com.example.${n.toLowerCase().replace(/[^a-z0-9]/g,"")}`}`),r=r.replace(/MARKETING_VERSION = .+/g,`MARKETING_VERSION = ${s??"1.0"}`),r=r.replace(/CURRENT_PROJECT_VERSION = .+/g,`CURRENT_PROJECT_VERSION = ${a??"1"}`),c==="iOS"){if(u&&(r=r.replace(/IPHONEOS_DEPLOYMENT_TARGET = .+/g,`IPHONEOS_DEPLOYMENT_TARGET = ${u}`)),p){let h=GXe(p);r=r.replace(/TARGETED_DEVICE_FAMILY = .+/g,`TARGETED_DEVICE_FAMILY = ${h}`)}if(f&&f.length>0){let h=f.filter(_=>_&&_.trim()!=="");if(h.length>0){let _=h.map(Qoe).join(" ");r=r.replace(/INFOPLIST_KEY_UISupportedInterfaceOrientations = .+/g,`INFOPLIST_KEY_UISupportedInterfaceOrientations = ${_}`)}}if(m&&m.length>0){let h=m.filter(_=>_&&_.trim()!=="");if(h.length>0){let _=h.map(Qoe).join(" ");r=r.replace(/INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = .+/g,`INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = ${_}`)}}r=r.replace(/CODE_SIGN_ENTITLEMENTS = .+/g,`CODE_SIGN_ENTITLEMENTS = Config/${n}.entitlements`)}return r=r.replace(/TEST_TARGET_NAME = .+/g,`TEST_TARGET_NAME = ${n}`),r=r.replace(/Config\/MyProject\.entitlements/g,`Config/${n}.entitlements`),r}function WXe(t,e,r){let n=t;return n=n.replace(/MyProject/g,e),r&&(n=n.replace(/com\.example\.MyProject/g,r),n=n.replace(/com\.mycompany\.MyProject/g,r)),n}async function KXe(t,e,r,n=Ir()){let o=r.projectName,i=r.bundleIdentifier,s=r.customizeNames,a=e;if(s){let h=(0,Ys.basename)(e),_=(0,Ys.dirname)(e),v=h.replace(/MyProject/g,o);a=(0,Ys.join)(_,v)}let c=[".swift",".h",".m",".mm",".cpp",".c",".pbxproj",".plist",".xcscheme",".xctestplan",".xcworkspacedata",".xcconfig",".json",".xml",".entitlements",".storyboard",".xib",".md"],u=t.toLowerCase(),p=c.some(h=>u.endsWith(h)),f=t.endsWith(".xcconfig"),m=t.endsWith("Package.swift");if(p&&s){let h=await n.readFile(t,"utf-8"),_;if(f)_=HXe(h,r);else if(m)_=VXe(h,r);else{let v=i??`com.example.${o.toLowerCase().replace(/[^a-z0-9]/g,"")}`;_=WXe(h,o,v)}await n.mkdir((0,Ys.dirname)(a),{recursive:!0}),await n.writeFile(a,_,"utf-8")}else await n.mkdir((0,Ys.dirname)(a),{recursive:!0}),await n.cp(t,a)}async function eie(t,e,r,n=Ir()){let o=await n.readdir(t,{withFileTypes:!0});for(let i of o){let s=i,a=(0,Ys.join)(t,s.name),c=s.name;r.customizeNames&&(c=c.replace(/MyProject/g,r.projectName));let u=(0,Ys.join)(e,c);if(s.isDirectory()){if(s.name===".git"||s.name==="xcuserdata")continue;await n.mkdir(u,{recursive:!0}),await eie(a,u,r,n)}else if(s.isFile()){if(s.name===".DS_Store"||s.name.endsWith(".xcuserstate"))continue;await KXe(a,u,r,n)}}}async function tie(t,e,r){try{let n={...t,platform:"iOS"},o=await ZXe(n,e,r),i={success:!0,projectPath:o,platform:"iOS",message:`Successfully scaffolded iOS project "${t.projectName}" in ${o}`,nextSteps:["Important: Before working on the project make sure to read the README.md file in the workspace root directory.",`Build for simulator: build_sim({ workspacePath: "${o}/${t.customizeNames?t.projectName:"MyProject"}.xcworkspace", scheme: "${t.customizeNames?t.projectName:"MyProject"}", simulatorName: "iPhone 16" })`,`Build and run on simulator: build_run_sim({ workspacePath: "${o}/${t.customizeNames?t.projectName:"MyProject"}.xcworkspace", scheme: "${t.customizeNames?t.projectName:"MyProject"}", simulatorName: "iPhone 16" })`]};return{content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(n){return A("error",`Failed to scaffold iOS project: ${n instanceof Error?n.message:String(n)}`),{content:[{type:"text",text:JSON.stringify({success:!1,error:n instanceof Error?n.message:"Unknown error occurred"},null,2)}],isError:!0}}}async function ZXe(t,e,r=Ir()){let n=t.projectName,o=t.outputPath,i=t.platform,s=t.customizeNames??!0;if(A("info",`Scaffolding project: ${n} (${i}) at ${o}`),!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(n))throw new Ka("Project name must start with a letter and contain only letters, numbers, and underscores");let a;try{e??=z(),a=await Xu.getTemplatePath(i,e,r)}catch(f){throw new Ka(`Failed to get template for ${i}: ${f instanceof Error?f.message:String(f)}`)}let c=o,u=r.existsSync((0,Ys.join)(c,`${s?n:"MyProject"}.xcworkspace`)),p=r.existsSync((0,Ys.join)(c,`${s?n:"MyProject"}.xcodeproj`));if(u||p)throw new Ka(`Xcode project files already exist in ${c}`);try{return await eie(a,c,t,r),c}finally{await Xu.cleanup(a,r)}}var Ys,BXe,Xoe,YXe,nie=O(()=>{"use strict";Te();Ys=require("path");De();hr();nF();ze();BXe=k({projectName:T().min(1).describe("Name of the new project"),outputPath:T().describe("Path where the project should be created"),bundleIdentifier:T().optional().describe("Bundle identifier (e.g., com.example.myapp). If not provided, will use com.example.projectname"),displayName:T().optional().describe("App display name (shown on home screen/dock). If not provided, will use projectName"),marketingVersion:T().optional().describe("Marketing version (e.g., 1.0, 2.1.3). If not provided, will use 1.0"),currentProjectVersion:T().optional().describe("Build number (e.g., 1, 42, 100). If not provided, will use 1"),customizeNames:ue().default(!0).describe("Whether to customize project names and identifiers. Default is true.")}),Xoe=BXe.extend({deploymentTarget:T().optional().describe("iOS deployment target (e.g., 18.4, 17.0). If not provided, will use 18.4"),targetedDeviceFamily:H(Ze(["iphone","ipad","universal"])).optional().describe("Targeted device families"),supportedOrientations:H(Ze(["portrait","landscape-left","landscape-right","portrait-upside-down"])).optional().describe("Supported orientations for iPhone"),supportedOrientationsIpad:H(Ze(["portrait","landscape-left","landscape-right","portrait-upside-down"])).optional().describe("Supported orientations for iPad")});YXe={name:"scaffold_ios_project",description:"Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration.",schema:Xoe.shape,annotations:{title:"Scaffold iOS Project",destructiveHint:!0},async handler(t){let e=Xoe.parse(t);return tie(e,z(),Ir())}}});var aie={};Y(aie,{default:()=>nQe,scaffold_macos_projectLogic:()=>sie});function XXe(t,e){let r=t,n=`${e.projectName}Feature`,o=`${e.projectName}FeatureTests`;if(r=r.replace(/MyProjectFeatureTests/g,o),r=r.replace(/MyProjectFeature/g,n),e.platform==="macOS"&&e.deploymentTarget){let i=e.deploymentTarget.split(".")[0];r=r.replace(/\.macOS\(\.v\d+\)/,`.macOS(.v${i})`)}return r}function QXe(t,e){let r=t;return r=r.replace(/PRODUCT_NAME = .+/g,`PRODUCT_NAME = ${e.projectName}`),r=r.replace(/PRODUCT_DISPLAY_NAME = .+/g,`PRODUCT_DISPLAY_NAME = ${e.displayName??e.projectName}`),r=r.replace(/PRODUCT_BUNDLE_IDENTIFIER = .+/g,`PRODUCT_BUNDLE_IDENTIFIER = ${e.bundleIdentifier??`com.example.${e.projectName.toLowerCase().replace(/[^a-z0-9]/g,"")}`}`),r=r.replace(/MARKETING_VERSION = .+/g,`MARKETING_VERSION = ${e.marketingVersion??"1.0"}`),r=r.replace(/CURRENT_PROJECT_VERSION = .+/g,`CURRENT_PROJECT_VERSION = ${e.currentProjectVersion??"1"}`),e.platform==="macOS"&&(e.deploymentTarget&&(r=r.replace(/MACOSX_DEPLOYMENT_TARGET = .+/g,`MACOSX_DEPLOYMENT_TARGET = ${e.deploymentTarget}`)),r=r.replace(/CODE_SIGN_ENTITLEMENTS = .+/g,`CODE_SIGN_ENTITLEMENTS = Config/${e.projectName}.entitlements`)),r=r.replace(/TEST_TARGET_NAME = .+/g,`TEST_TARGET_NAME = ${e.projectName}`),r=r.replace(/Config\/MyProject\.entitlements/g,`Config/${e.projectName}.entitlements`),r}function eQe(t,e,r){let n=t;return n=n.replace(/MyProject/g,e),r&&(n=n.replace(/com\.example\.MyProject/g,r),n=n.replace(/com\.mycompany\.MyProject/g,r)),n}async function tQe(t,e,r,n){let o=e;if(r.customizeNames){let p=(0,Js.basename)(e),f=(0,Js.dirname)(e),m=p.replace(/MyProject/g,r.projectName);o=(0,Js.join)(f,m)}let i=[".swift",".h",".m",".mm",".cpp",".c",".pbxproj",".plist",".xcscheme",".xctestplan",".xcworkspacedata",".xcconfig",".json",".xml",".entitlements",".storyboard",".xib",".md"],s=t.toLowerCase(),a=i.some(p=>s.endsWith(p)),c=t.endsWith(".xcconfig"),u=t.endsWith("Package.swift");if(a&&r.customizeNames){let p=await n.readFile(t,"utf-8"),f;if(c)f=QXe(p,r);else if(u)f=XXe(p,r);else{let m=r.bundleIdentifier??`com.example.${r.projectName.toLowerCase().replace(/[^a-z0-9]/g,"")}`;f=eQe(p,r.projectName,m)}await n.mkdir((0,Js.dirname)(o),{recursive:!0}),await n.writeFile(o,f,"utf-8")}else await n.mkdir((0,Js.dirname)(o),{recursive:!0}),await n.cp(t,o,{recursive:!0})}async function iie(t,e,r,n){let o=await n.readdir(t,{withFileTypes:!0});for(let i of o){let s=i,a=(0,Js.join)(t,s.name),c=s.name;r.customizeNames&&(c=c.replace(/MyProject/g,r.projectName));let u=(0,Js.join)(e,c);if(s.isDirectory()){if(s.name===".git"||s.name==="xcuserdata")continue;await n.mkdir(u,{recursive:!0}),await iie(a,u,r,n)}else if(s.isFile()){if(s.name===".DS_Store"||s.name.endsWith(".xcuserstate"))continue;await tQe(a,u,r,n)}}}async function rQe(t,e,r){let n=t.projectName,o=t.outputPath,i=t.platform,s=t.customizeNames??!0;if(A("info",`Scaffolding project: ${n} (${i}) at ${o}`),!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(n))throw new Ka("Project name must start with a letter and contain only letters, numbers, and underscores");let a;try{a=await Xu.getTemplatePath(i,e,r)}catch(f){throw new Ka(`Failed to get template for ${i}: ${f instanceof Error?f.message:String(f)}`)}let c=o,u=r.existsSync((0,Js.join)(c,`${s?n:"MyProject"}.xcworkspace`)),p=r.existsSync((0,Js.join)(c,`${s?n:"MyProject"}.xcodeproj`));if(u||p)throw new Ka(`Xcode project files already exist in ${c}`);try{return await iie(a,c,t,r),c}finally{await Xu.cleanup(a,r)}}async function sie(t,e,r=Ir()){try{let n={...t,platform:"macOS"},o=await rQe(n,e,r),i={success:!0,projectPath:o,platform:"macOS",message:`Successfully scaffolded macOS project "${t.projectName}" in ${o}`,nextSteps:["Important: Before working on the project make sure to read the README.md file in the workspace root directory.",`Build for macOS: build_macos({ workspacePath: "${o}/${t.customizeNames?t.projectName:"MyProject"}.xcworkspace", scheme: "${t.customizeNames?t.projectName:"MyProject"}" })`,`Build & Run on macOS: build_run_macos({ workspacePath: "${o}/${t.customizeNames?t.projectName:"MyProject"}.xcworkspace", scheme: "${t.customizeNames?t.projectName:"MyProject"}" })`]};return{content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(n){return A("error",`Failed to scaffold macOS project: ${n instanceof Error?n.message:String(n)}`),{content:[{type:"text",text:JSON.stringify({success:!1,error:n instanceof Error?n.message:"Unknown error occurred"},null,2)}],isError:!0}}}var Js,JXe,oie,nQe,cie=O(()=>{"use strict";Te();Js=require("path");De();hr();nF();hs();JXe=k({projectName:T().min(1).describe("Name of the new project"),outputPath:T().describe("Path where the project should be created"),bundleIdentifier:T().optional().describe("Bundle identifier (e.g., com.example.myapp). If not provided, will use com.example.projectname"),displayName:T().optional().describe("App display name (shown on home screen/dock). If not provided, will use projectName"),marketingVersion:T().optional().describe("Marketing version (e.g., 1.0, 2.1.3). If not provided, will use 1.0"),currentProjectVersion:T().optional().describe("Build number (e.g., 1, 42, 100). If not provided, will use 1"),customizeNames:ue().default(!0).describe("Whether to customize project names and identifiers. Default is true.")}),oie=JXe.extend({deploymentTarget:T().optional().describe("macOS deployment target (e.g., 15.4, 14.0). If not provided, will use 15.4")});nQe={name:"scaffold_macos_project",description:"Scaffold a new macOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper macOS configuration.",schema:oie.shape,annotations:{title:"Scaffold macOS Project",destructiveHint:!0},async handler(t){let e=oie.parse(t);return sie(e,z(),Ir())}}});var uie={};Y(uie,{workflow:()=>oQe});var oQe,lie=O(()=>{"use strict";oQe={name:"session-management",description:"Manage session defaults for projectPath/workspacePath, scheme, configuration, simulatorName/simulatorId, deviceId, useLatestOS and arch. These defaults are required by many tools and must be set before attempting to call tools that would depend on these values."}});var fie={};Y(fie,{default:()=>sQe,sessionClearDefaultsLogic:()=>die});async function die(t){return t.all||!t.keys?gs.clear():gs.clear(t.keys),{content:[{type:"text",text:"Session defaults cleared"}],isError:!1}}var iQe,pie,sQe,mie=O(()=>{"use strict";Te();tg();Le();ze();iQe=["projectPath","workspacePath","scheme","configuration","simulatorName","simulatorId","deviceId","useLatestOS","arch"],pie=k({keys:H(Ze(iQe)).optional(),all:ue().optional()});sQe={name:"session-clear-defaults",description:"Clear selected or all session defaults.",schema:pie.shape,annotations:{title:"Clear Session Defaults",destructiveHint:!0},handler:Ht(pie,die,z)}});var _ie={};Y(_ie,{default:()=>cQe,sessionSetDefaultsLogic:()=>gie});async function gie(t){let e=new Set;Object.prototype.hasOwnProperty.call(t,"projectPath")&&e.add("workspacePath"),Object.prototype.hasOwnProperty.call(t,"workspacePath")&&e.add("projectPath"),Object.prototype.hasOwnProperty.call(t,"simulatorId")&&e.add("simulatorName"),Object.prototype.hasOwnProperty.call(t,"simulatorName")&&e.add("simulatorId"),e.size>0&&gs.clear(Array.from(e)),gs.setDefaults(t);let r=gs.getAll();return{content:[{type:"text",text:`Defaults updated: -${JSON.stringify(r,null,2)}`}],isError:!1}}var hie,aQe,cQe,vie=O(()=>{"use strict";Te();tg();Le();ze();hie=k({projectPath:T().optional(),workspacePath:T().optional(),scheme:T().optional(),configuration:T().optional(),simulatorName:T().optional(),simulatorId:T().optional(),deviceId:T().optional(),useLatestOS:ue().optional(),arch:Ze(["arm64","x86_64"]).optional(),suppressWarnings:ue().optional().describe("When true, warning messages are filtered from build output to conserve context")}),aQe=hie.refine(t=>!(t.projectPath&&t.workspacePath),{message:"projectPath and workspacePath are mutually exclusive",path:["projectPath"]}).refine(t=>!(t.simulatorId&&t.simulatorName),{message:"simulatorId and simulatorName are mutually exclusive",path:["simulatorId"]});cQe={name:"session-set-defaults",description:"Set the session defaults needed by many tools. Most tools require one or more session defaults to be set before they can be used. Agents should set all relevant defaults up front in a single call (e.g., project/workspace, scheme, simulator or device ID, useLatestOS) to avoid iterative prompts; only set the keys your workflow needs.",schema:hie.shape,annotations:{title:"Set Session Defaults",destructiveHint:!0},handler:Ht(aQe,gie,z)}});var Sie={};Y(Sie,{default:()=>uQe});var uQe,yie=O(()=>{"use strict";tg();uQe={name:"session-show-defaults",description:"Show current session defaults.",schema:{},annotations:{title:"Show Session Defaults",readOnlyHint:!0},handler:async()=>{let t=gs.getAll();return{content:[{type:"text",text:JSON.stringify(t,null,2)}],isError:!1}}}});var Eie={};Y(Eie,{workflow:()=>lQe});var lQe,Tie=O(()=>{"use strict";lQe={name:"iOS Simulator Development",description:"Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators."}});var xie={};Y(xie,{boot_simLogic:()=>bie,default:()=>iF});async function bie(t,e){A("info",`Starting xcrun simctl boot request for simulator ${t.simulatorId}`);try{let r=["xcrun","simctl","boot",t.simulatorId],n=await e(r,"Boot Simulator",!0);return n.success?{content:[{type:"text",text:`\u2705 Simulator booted successfully. To make it visible, use: open_sim() - -Next steps: -1. Open the Simulator app (makes it visible): open_sim() -2. Install an app: install_app_sim({ simulatorId: "${t.simulatorId}", appPath: "PATH_TO_YOUR_APP" }) -3. Launch an app: launch_app_sim({ simulatorId: "${t.simulatorId}", bundleId: "YOUR_APP_BUNDLE_ID" })`}]}:{content:[{type:"text",text:`Boot simulator operation failed: ${n.error}`}]}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error during boot simulator operation: ${n}`),{content:[{type:"text",text:`Boot simulator operation failed: ${n}`}]}}}var oF,pQe,iF,sF=O(()=>{"use strict";Te();De();ze();Le();oF=k({simulatorId:T().describe("UUID of the simulator to use (obtained from list_sims)")}),pQe=st(oF.omit({simulatorId:!0}).shape);iF={name:"boot_sim",description:"Boots an iOS simulator.",schema:de({sessionAware:pQe,legacy:oF}),annotations:{title:"Boot Simulator",destructiveHint:!0},handler:fe({internalSchema:oF,logicFunction:bie,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});async function Aie(t,e){let r=t.simulatorUuid??t.simulatorId;if(r)return A("info",`Using provided simulator UUID: ${r}`),{uuid:r};if(t.simulatorName){if(dQe.test(t.simulatorName))return A("info",`Simulator name '${t.simulatorName}' appears to be a UUID, using it directly`),{uuid:t.simulatorName,warning:`The simulatorName '${t.simulatorName}' appears to be a UUID. Consider using simulatorUuid parameter instead.`};A("info",`Looking up simulator UUID for name: ${t.simulatorName}`);let n=await e(["xcrun","simctl","list","devices","available","-j"],"List available simulators");if(!n.success)return{error:ge("Failed to list simulators",n.error??"Unknown error")};try{let o=JSON.parse(n.output??"{}");for(let i of Object.keys(o.devices)){let s=o.devices[i];if(!Array.isArray(s))continue;let a=s.find(c=>c.name===t.simulatorName&&c.isAvailable===!0);if(a)return A("info",`Found simulator '${t.simulatorName}' with UUID: ${a.udid}`),{uuid:a.udid}}for(let i of Object.keys(o.devices)){let s=o.devices[i];if(!Array.isArray(s))continue;if(s.find(c=>c.name===t.simulatorName&&c.isAvailable===!1))return{error:ge(`Simulator '${t.simulatorName}' exists but is not available`,"The simulator may need to be downloaded or is incompatible with the current Xcode version")}}return{error:ge(`Simulator '${t.simulatorName}' not found`,'Please check the simulator name or use "xcrun simctl list devices" to see available simulators')}}catch(o){return{error:ge("Failed to parse simulator list",o instanceof Error?o.message:String(o))}}}return{error:ge("No simulator identifier provided","Either simulatorUuid or simulatorName is required")}}var dQe,wie=O(()=>{"use strict";De();hr();dQe=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i});var Pie={};Y(Pie,{build_run_simLogic:()=>Rie,default:()=>_Qe});async function hQe(t,e,r=Ln){let n=t.projectPath?"project":"workspace",o=t.projectPath??t.workspacePath;t.simulatorId&&t.useLatestOS!==void 0&&A("warning","useLatestOS parameter is ignored when using simulatorId (UUID implies exact device/OS)"),A("info",`Starting iOS Simulator build for scheme ${t.scheme} from ${n}: ${o}`);let i={workspacePath:t.workspacePath,projectPath:t.projectPath,scheme:t.scheme,configuration:t.configuration??"Debug",derivedDataPath:t.derivedDataPath,extraArgs:t.extraArgs};return r(i,{platform:"iOS Simulator",simulatorId:t.simulatorId,simulatorName:t.simulatorName,useLatestOS:t.simulatorId?!1:t.useLatestOS,logPrefix:"iOS Simulator Build"},t.preferXcodebuild,"build",e)}async function Rie(t,e,r=Ln){let n=t.projectPath?"project":"workspace",o=t.projectPath??t.workspacePath;A("info",`Starting iOS Simulator build and run for scheme ${t.scheme} from ${n}: ${o}`);try{let i=await hQe(t,e,r);if(i.isError)return i;let s=["xcodebuild","-showBuildSettings"];t.workspacePath?s.push("-workspace",t.workspacePath):t.projectPath&&s.push("-project",t.projectPath),s.push("-scheme",t.scheme),s.push("-configuration",t.configuration??"Debug");let a;t.simulatorId?a=`platform=iOS Simulator,id=${t.simulatorId}`:t.simulatorName?a=`platform=iOS Simulator,name=${t.simulatorName}${t.useLatestOS??!0?",OS=latest":""}`:a="platform=iOS Simulator",s.push("-destination",a),t.derivedDataPath&&s.push("-derivedDataPath",t.derivedDataPath),t.extraArgs&&t.extraArgs.length>0&&s.push(...t.extraArgs);let c=await e(s,"Get App Path",!0,void 0);if(!c.success)return le(`Build succeeded, but failed to get app path: ${c.error??"Unknown error"}`,!0);let u=c.output,p=null,f=u.match(/CODESIGNING_FOLDER_PATH = (.+\.app)/);if(f?.[1])p=f[1].trim();else{let w=u.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/m),I=u.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/m);if(w&&I){let N=w[1].trim(),$=I[1].trim();p=`${N}/${$}`}}if(!p)return le("Build succeeded, but could not find app path in build settings.",!0);A("info",`App bundle path for run: ${p}`);let m=await Aie({simulatorId:t.simulatorId,simulatorName:t.simulatorName},e);if(m.error)return le(`Build succeeded, but ${m.error.content[0].text}`,!0);m.warning&&A("warning",m.warning);let h=m.uuid;if(!h)return le("Build succeeded, but no simulator specified and failed to find a suitable one.",!0);try{A("info",`Checking simulator state for UUID: ${h}`);let w=await e(["xcrun","simctl","list","devices","available","--json"],"List Simulators");if(!w.success)throw new Error(w.error??"Failed to list simulators");let I=JSON.parse(w.output),N=null;for(let $ in I.devices){let B=I.devices[$];if(Array.isArray(B)){for(let G of B)if(typeof G=="object"&&G!==null&&"udid"in G&&"name"in G&&"state"in G&&typeof G.udid=="string"&&typeof G.name=="string"&&typeof G.state=="string"&&G.udid===h){N={udid:G.udid,name:G.name,state:G.state};break}if(N)break}}if(!N)return le(`Build succeeded, but could not find simulator with UUID: ${h}`,!0);if(N.state!=="Booted"){A("info",`Booting simulator ${N.name}...`);let $=await e(["xcrun","simctl","boot",h],"Boot Simulator");if(!$.success)throw new Error($.error??"Failed to boot simulator")}else A("info",`Simulator ${h} is already booted`)}catch(w){let I=w instanceof Error?w.message:String(w);return A("error",`Error checking/booting simulator: ${I}`),le(`Build succeeded, but error checking/booting simulator: ${I}`,!0)}try{A("info","Opening Simulator app");let w=await e(["open","-a","Simulator"],"Open Simulator App");if(!w.success)throw new Error(w.error??"Failed to open Simulator app")}catch(w){let I=w instanceof Error?w.message:String(w);A("warning",`Warning: Could not open Simulator app: ${I}`)}try{A("info",`Installing app at path: ${p} to simulator: ${h}`);let w=await e(["xcrun","simctl","install",h,p],"Install App");if(!w.success)throw new Error(w.error??"Failed to install app")}catch(w){let I=w instanceof Error?w.message:String(w);return A("error",`Error installing app: ${I}`),le(`Build succeeded, but error installing app on simulator: ${I}`,!0)}let _;try{A("info",`Extracting bundle ID from app: ${p}`);let w=null;try{w=await e(["/usr/libexec/PlistBuddy","-c","Print :CFBundleIdentifier",`${p}/Info.plist`],"Get Bundle ID with PlistBuddy"),w.success&&(_=w.output.trim())}catch{}if(!_)try{w=await e(["plutil","-extract","CFBundleIdentifier","raw",`${p}/Info.plist`],"Get Bundle ID with plutil"),w?.success&&(_=w.output?.trim())}catch{}if(!_)try{w=await e(["defaults","read",`${p}/Info`,"CFBundleIdentifier"],"Get Bundle ID with defaults"),w?.success&&(_=w.output?.trim())}catch{}if(!_)throw new Error("Could not extract bundle ID from Info.plist using any method");A("info",`Bundle ID for run: ${_}`)}catch(w){let I=w instanceof Error?w.message:String(w);return A("error",`Error getting bundle ID: ${I}`),le(`Build and install succeeded, but error getting bundle ID: ${I}`,!0)}try{A("info",`Launching app with bundle ID: ${_} on simulator: ${h}`);let w=await e(["xcrun","simctl","launch",h,_],"Launch App");if(!w.success)throw new Error(w.error??"Failed to launch app")}catch(w){let I=w instanceof Error?w.message:String(w);return A("error",`Error launching app: ${I}`),le(`Build and install succeeded, but error launching app on simulator: ${I}`,!0)}A("info","\u2705 iOS simulator build & run succeeded.");let v=t.simulatorId?`simulator UUID '${t.simulatorId}'`:`simulator name '${t.simulatorName}'`,E=t.projectPath?"project":"workspace",x=t.projectPath??t.workspacePath;return{content:[{type:"text",text:`\u2705 iOS simulator build and run succeeded for scheme ${t.scheme} from ${E} ${x} targeting ${v}. - -The app (${_}) is now running in the iOS Simulator. -If you don't see the simulator window, it may be hidden behind other windows. The Simulator app should be open. - -Next Steps: -- Option 1: Capture structured logs only (app continues running): - start_simulator_log_capture({ simulatorId: '${h}', bundleId: '${_}' }) -- Option 2: Capture both console and structured logs (app will restart): - start_simulator_log_capture({ simulatorId: '${h}', bundleId: '${_}', captureConsole: true }) -- Option 3: Launch app with logs in one step (for a fresh start): - launch_app_with_logs_in_simulator({ simulatorId: '${h}', bundleId: '${_}' }) - -When done with any option, use: stop_sim_log_cap({ logSessionId: 'SESSION_ID' })`}],isError:!1}}catch(i){let s=i instanceof Error?i.message:String(i);return A("error",`Error in iOS Simulator build and run: ${s}`),le(`Error in iOS Simulator build and run: ${s}`,!0)}}var fQe,aF,mQe,gQe,_Qe,Iie=O(()=>{"use strict";Te();Dn();De();ze();Le();hr();Uc();wie();wo();fQe={scheme:T().describe("The scheme to use (Required)"),simulatorId:T().optional().describe("UUID of the simulator (from list_sims). Provide EITHER this OR simulatorName, not both"),simulatorName:T().optional().describe("Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Path where build products and other derived data will go"),extraArgs:H(T()).optional().describe("Additional xcodebuild arguments"),useLatestOS:ue().optional().describe("Whether to use the latest OS version for the named simulator"),preferXcodebuild:ue().optional().describe("If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.")},aF=k({projectPath:T().optional().describe("Path to .xcodeproj file. Provide EITHER this OR workspacePath, not both"),workspacePath:T().optional().describe("Path to .xcworkspace file. Provide EITHER this OR projectPath, not both"),...fQe}),mQe=Bt(pr,aF.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}).refine(t=>t.simulatorId!==void 0||t.simulatorName!==void 0,{message:"Either simulatorId or simulatorName is required."}).refine(t=>!(t.simulatorId!==void 0&&t.simulatorName!==void 0),{message:"simulatorId and simulatorName are mutually exclusive. Provide only one."}));gQe=aF.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0,simulatorId:!0,simulatorName:!0,useLatestOS:!0}),_Qe={name:"build_run_sim",description:"Builds and runs an app on an iOS simulator.",schema:de({sessionAware:gQe,legacy:aF}),annotations:{title:"Build Run Simulator",destructiveHint:!0},handler:fe({internalSchema:mQe,logicFunction:Rie,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"},{oneOf:["simulatorId","simulatorName"],message:"Provide simulatorId or simulatorName"}],exclusivePairs:[["projectPath","workspacePath"],["simulatorId","simulatorName"]]})}});var Nie={};Y(Nie,{build_simLogic:()=>Oie,default:()=>TQe});async function yQe(t,e=z()){let r=t.projectPath?"project":"workspace",n=t.projectPath??t.workspacePath;t.simulatorId&&t.useLatestOS!==void 0&&A("warning","useLatestOS parameter is ignored when using simulatorId (UUID implies exact device/OS)"),A("info",`Starting iOS Simulator build for scheme ${t.scheme} from ${r}: ${n}`);let o={...t,configuration:t.configuration??"Debug"};return Ln(o,{platform:"iOS Simulator",simulatorName:t.simulatorName,simulatorId:t.simulatorId,useLatestOS:t.simulatorId?!1:t.useLatestOS,logPrefix:"iOS Simulator Build"},t.preferXcodebuild??!1,"build",e)}async function Oie(t,e){let r={...t,configuration:t.configuration??"Debug",useLatestOS:t.useLatestOS??!0,preferXcodebuild:t.preferXcodebuild??!1};return yQe(r,e)}var vQe,cF,SQe,EQe,TQe,Cie=O(()=>{"use strict";Te();De();Uc();Dn();ze();Le();wo();vQe={scheme:T().describe("The scheme to use (Required)"),simulatorId:T().optional().describe("UUID of the simulator (from list_sims). Provide EITHER this OR simulatorName, not both"),simulatorName:T().optional().describe("Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Path where build products and other derived data will go"),extraArgs:H(T()).optional().describe("Additional xcodebuild arguments"),useLatestOS:ue().optional().describe("Whether to use the latest OS version for the named simulator"),preferXcodebuild:ue().optional().describe("If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.")},cF=k({projectPath:T().optional().describe("Path to .xcodeproj file. Provide EITHER this OR workspacePath, not both"),workspacePath:T().optional().describe("Path to .xcworkspace file. Provide EITHER this OR projectPath, not both"),...vQe}),SQe=Bt(pr,cF.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}).refine(t=>t.simulatorId!==void 0||t.simulatorName!==void 0,{message:"Either simulatorId or simulatorName is required."}).refine(t=>!(t.simulatorId!==void 0&&t.simulatorName!==void 0),{message:"simulatorId and simulatorName are mutually exclusive. Provide only one."}));EQe=cF.omit({projectPath:!0,workspacePath:!0,scheme:!0,configuration:!0,simulatorId:!0,simulatorName:!0,useLatestOS:!0}),TQe={name:"build_sim",description:"Builds an app for an iOS simulator.",schema:de({sessionAware:EQe,legacy:cF}),annotations:{title:"Build Simulator",destructiveHint:!0},handler:fe({internalSchema:SQe,logicFunction:Oie,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"},{oneOf:["simulatorId","simulatorName"],message:"Provide simulatorId or simulatorName"}],exclusivePairs:[["projectPath","workspacePath"],["simulatorId","simulatorName"]]})}});var $ie={};Y($ie,{default:()=>bf});var kie=O(()=>{"use strict";PE()});function bQe(){let t=process.argv[1];if(t){let e=(0,LE.dirname)(t);return(0,LE.dirname)(e)}return process.cwd()}function Tt(){return(0,Die.existsSync)(Mie)?Mie:null}function Nt(){return{}}function og(){return Tt()!==null}function Ct(){return le(`Bundled axe tool not found. UI automation features are not available. - -This is likely an installation issue with the npm package. -Please reinstall xcodebuildmcp or report this issue.`,!0)}function xQe(t,e){let r=t.split(".").map(i=>parseInt(i,10)),n=e.split(".").map(i=>parseInt(i,10)),o=Math.max(r.length,n.length);for(let i=0;ia)return 1;if(s=0}catch{return!1}}var Die,LE,Mie,Qu=O(()=>{"use strict";Die=require("fs"),LE=require("path");Qh();ze();Mie=(0,LE.join)(bQe(),"bundled","axe")});var Uie={};Y(Uie,{default:()=>pF,describe_uiLogic:()=>Lie});function wQe(t){AQe.set(t,{timestamp:Date.now(),simulatorId:t})}async function Lie(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let n="describe_ui",{simulatorId:o}=t,i=["describe-ui"];A("info",`${UE}/${n}: Starting for ${o}`);try{let s=await PQe(i,o,"describe-ui",e,r);return wQe(o),A("info",`${UE}/${n}: Success for ${o}`),{content:[{type:"text",text:"Accessibility hierarchy retrieved successfully:\n```json\n"+s+"\n```"},{type:"text",text:`Next Steps: -- Use frame coordinates for tap/swipe (center: x+width/2, y+height/2) -- Re-run describe_ui after layout changes -- Screenshots are for visual verification only`}]}}catch(s){return A("error",`${UE}/${n}: Failed - ${s}`),s instanceof sr?r.createAxeNotAvailableResponse():s instanceof gt?ge(`Failed to get accessibility hierarchy: ${s.message}`,s.axeOutput):s instanceof at?ge(`System error executing axe: ${s.message}`,s.originalError?.stack):ge(`An unexpected error occurred: ${s instanceof Error?s.message:String(s)}`)}}async function PQe(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${UE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);return u.error&&A("warn",`${UE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`),u.output.trim()}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var lF,UE,AQe,RQe,pF,dF=O(()=>{"use strict";Te();De();hr();yp();ze();Qu();Le();lF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"})}),UE="[AXe]",AQe=new Map;RQe=st(lF.omit({simulatorId:!0}).shape),pF={name:"describe_ui",description:"Gets entire view hierarchy with precise frame coordinates (x, y, width, height) for all visible elements. Use this before UI interactions or after layout changes - do NOT guess coordinates from screenshots. Returns JSON tree with frame data for accurate automation.",schema:de({sessionAware:RQe,legacy:lF}),annotations:{title:"Describe UI",readOnlyHint:!0},handler:fe({internalSchema:lF,logicFunction:(t,e)=>Lie(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var jie={};Y(jie,{default:()=>pF});var zie=O(()=>{"use strict";dF()});var Fie={};Y(Fie,{default:()=>xf});var qie=O(()=>{"use strict";IE()});var Bie={};Y(Bie,{default:()=>OE});var Gie=O(()=>{"use strict";jI()});var Hie={};Y(Hie,{default:()=>CQe,get_sim_app_pathLogic:()=>Vie});function IQe(t,e,r,n=!0,o){let i=[un.iOSSimulator,un.watchOSSimulator,un.tvOSSimulator,un.visionOSSimulator].includes(t);if(i&&r)return`platform=${t},id=${r}`;if(i&&e)return`platform=${t},name=${e}${n?",OS=latest":""}`;if(i&&!r&&!e)throw A("warning",`Constructing generic destination for ${t} without name or ID. This might not be specific enough.`),new Error(`Simulator name or ID is required for specific ${t} operations`);switch(t){case un.macOS:return o?`platform=macOS,arch=${o}`:"platform=macOS";case un.iOS:return"generic/platform=iOS";case un.watchOS:return"generic/platform=watchOS";case un.tvOS:return"generic/platform=tvOS";case un.visionOS:return"generic/platform=visionOS"}return A("error",`Reached unexpected point in constructDestinationString for platform: ${t}`),`platform=${t}`}async function Vie(t,e){let r=t.projectPath,n=t.workspacePath,o=t.scheme,i=t.platform,s=t.simulatorId,a=t.simulatorName,c=t.configuration??"Debug",u=t.useLatestOS??!0,p=t.arch;s&&t.useLatestOS!==void 0&&A("warning","useLatestOS parameter is ignored when using simulatorId (UUID implies exact device/OS)"),A("info",`Getting app path for scheme ${o} on platform ${i}`);try{let f=["xcodebuild","-showBuildSettings"];n?f.push("-workspace",n):r&&f.push("-project",r),f.push("-scheme",o),f.push("-configuration",c);let m=[un.iOSSimulator,un.watchOSSimulator,un.tvOSSimulator,un.visionOSSimulator].includes(i),h="";if(m)if(s)h=`platform=${i},id=${s}`;else if(a)h=`platform=${i},name=${a}${!s&&u?",OS=latest":""}`;else return le(`For ${i} platform, either simulatorId or simulatorName must be provided`,!0);else if(i===un.macOS)h=IQe(i,"","",!1,p);else if(i===un.iOS)h="generic/platform=iOS";else if(i===un.watchOS)h="generic/platform=watchOS";else if(i===un.tvOS)h="generic/platform=tvOS";else if(i===un.visionOS)h="generic/platform=visionOS";else return le(`Unsupported platform: ${i}`,!0);f.push("-destination",h);let _=await e(f,"Get App Path",!0,void 0);if(!_.success)return le(`Failed to get app path: ${_.error}`,!0);if(!_.output)return le("Failed to extract build settings output from the result.",!0);let v=_.output,E=v.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/m),x=v.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/m);if(!E||!x)return le("Failed to extract app path from build settings. Make sure the app has been built first.",!0);let w=E[1].trim(),I=x[1].trim(),N=`${w}/${I}`,$="";return i===un.macOS?$=`Next Steps: -1. Get bundle ID: get_mac_bundle_id({ appPath: "${N}" }) -2. Launch the app: launch_mac_app({ appPath: "${N}" })`:m?$=`Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${N}" }) -2. Boot simulator: boot_sim({ simulatorId: "SIMULATOR_UUID" }) -3. Install app: install_app_sim({ simulatorId: "SIMULATOR_UUID", appPath: "${N}" }) -4. Launch app: launch_app_sim({ simulatorId: "SIMULATOR_UUID", bundleId: "BUNDLE_ID" })`:[un.iOS,un.watchOS,un.tvOS,un.visionOS].includes(i)?$=`Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${N}" }) -2. Install app on device: install_app_device({ deviceId: "DEVICE_UDID", appPath: "${N}" }) -3. Launch app on device: launch_app_device({ deviceId: "DEVICE_UDID", bundleId: "BUNDLE_ID" })`:$=`Next Steps: -1. The app has been built for ${i} -2. Use platform-specific deployment tools to install and run the app`,{content:[{type:"text",text:`\u2705 App path retrieved successfully: ${N}`},{type:"text",text:$}],isError:!1}}catch(f){let m=f instanceof Error?f.message:String(f);return A("error",`Error retrieving app path: ${m}`),le(`Error retrieving app path: ${m}`,!0)}}var un,fF,OQe,NQe,CQe,Wie=O(()=>{"use strict";Te();De();hr();ze();Le();wo();un={macOS:"macOS",iOS:"iOS",iOSSimulator:"iOS Simulator",watchOS:"watchOS",watchOSSimulator:"watchOS Simulator",tvOS:"tvOS",tvOSSimulator:"tvOS Simulator",visionOS:"visionOS",visionOSSimulator:"visionOS Simulator"};fF=k({projectPath:T().optional().describe("Path to .xcodeproj file. Provide EITHER this OR workspacePath, not both"),workspacePath:T().optional().describe("Path to .xcworkspace file. Provide EITHER this OR projectPath, not both"),scheme:T().describe("The scheme to use (Required)"),platform:Ze(["iOS Simulator","watchOS Simulator","tvOS Simulator","visionOS Simulator"]).describe("Target simulator platform (Required)"),simulatorId:T().optional().describe("UUID of the simulator (from list_sims). Provide EITHER this OR simulatorName, not both"),simulatorName:T().optional().describe("Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),useLatestOS:ue().optional().describe("Whether to use the latest OS version for the named simulator"),arch:T().optional().describe("Optional architecture")}),OQe=Bt(pr,fF.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}).refine(t=>t.simulatorId!==void 0||t.simulatorName!==void 0,{message:"Either simulatorId or simulatorName is required."}).refine(t=>!(t.simulatorId!==void 0&&t.simulatorName!==void 0),{message:"simulatorId and simulatorName are mutually exclusive. Provide only one."}));NQe=fF.omit({projectPath:!0,workspacePath:!0,scheme:!0,simulatorId:!0,simulatorName:!0,configuration:!0,useLatestOS:!0,arch:!0}),CQe={name:"get_sim_app_path",description:"Retrieves the built app path for an iOS simulator.",schema:de({sessionAware:NQe,legacy:fF}),annotations:{title:"Get Simulator App Path",readOnlyHint:!0},handler:fe({internalSchema:OQe,logicFunction:Vie,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"},{oneOf:["simulatorId","simulatorName"],message:"Provide simulatorId or simulatorName"}],exclusivePairs:[["projectPath","workspacePath"],["simulatorId","simulatorName"]]})}});var Zie={};Y(Zie,{default:()=>kQe,install_app_simLogic:()=>Kie});async function Kie(t,e,r){let n=DI(t.appPath,r);if(!n.isValid)return n.errorResponse;A("info",`Starting xcrun simctl install request for simulator ${t.simulatorId}`);try{let o=["xcrun","simctl","install",t.simulatorId,t.appPath],i=await e(o,"Install App in Simulator",!0,void 0);if(!i.success)return{content:[{type:"text",text:`Install app in simulator operation failed: ${i.error}`}]};let s="";try{let a=await e(["defaults","read",`${t.appPath}/Info`,"CFBundleIdentifier"],"Extract Bundle ID",!1,void 0);a.success&&(s=a.output.trim())}catch(a){A("warning",`Could not extract bundle ID from app: ${a}`)}return{content:[{type:"text",text:`App installed successfully in simulator ${t.simulatorId}`},{type:"text",text:`Next Steps: -1. Open the Simulator app: open_sim({}) -2. Launch the app: launch_app_sim({ simulatorId: "${t.simulatorId}"${s?`, bundleId: "${s}"`:', bundleId: "YOUR_APP_BUNDLE_ID"'} })`}]}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error during install app in simulator operation: ${i}`),{content:[{type:"text",text:`Install app in simulator operation failed: ${i}`}]}}}var mF,$Qe,kQe,Yie=O(()=>{"use strict";Te();De();Q2();ze();Le();mF=k({simulatorId:T().describe("UUID of the simulator to use (obtained from list_sims)"),appPath:T().describe("Path to the .app bundle to install (full path to the .app directory)")}),$Qe=st(mF.omit({simulatorId:!0}).shape);kQe={name:"install_app_sim",description:"Installs an app in an iOS simulator.",schema:de({sessionAware:$Qe,legacy:mF}),annotations:{title:"Install App Simulator",destructiveHint:!0},handler:fe({internalSchema:mF,logicFunction:Kie,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Xie={};Y(Xie,{default:()=>DQe,launch_app_logs_simLogic:()=>Jie});async function Jie(t,e=z(),r=ME){A("info",`Starting app launch with logs for simulator ${t.simulatorId}`);let n={simulatorUuid:t.simulatorId,bundleId:t.bundleId,captureConsole:!0,...t.args&&t.args.length>0?{args:t.args}:{}},{sessionId:o,error:i}=await r(n,e);return i?{content:[Lt(`App was launched but log capture failed: ${i}`)],isError:!0}:{content:[Lt(`App launched successfully in simulator ${t.simulatorId} with log capture enabled. - -Log capture session ID: ${o} - -Next Steps: -1. Interact with your app in the simulator. -2. Use 'stop_and_get_simulator_log({ logSessionId: "${o}" })' to stop capture and retrieve logs.`)],isError:!1}}var hF,MQe,DQe,Qie=O(()=>{"use strict";Te();Dn();De();WI();ze();Le();hF=k({simulatorId:T().describe("UUID of the simulator to use (obtained from list_sims)"),bundleId:T().describe("Bundle identifier of the app to launch (e.g., 'com.example.MyApp')"),args:H(T()).optional().describe("Additional arguments to pass to the app")}),MQe=st(hF.omit({simulatorId:!0}).shape);DQe={name:"launch_app_logs_sim",description:"Launches an app in an iOS simulator and captures its logs.",schema:de({sessionAware:MQe,legacy:hF}),annotations:{title:"Launch App Logs Simulator",destructiveHint:!0},handler:fe({internalSchema:hF,logicFunction:Jie,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var tse={};Y(tse,{default:()=>jQe,launch_app_simLogic:()=>ese});async function ese(t,e){let r=t.simulatorId,n=r??"";if(t.simulatorName&&!r){A("info",`Looking up simulator by name: ${t.simulatorName}`);let o=await e(["xcrun","simctl","list","devices","available","--json"],"List Simulators",!0);if(!o.success)return{content:[{type:"text",text:`Failed to list simulators: ${o.error}`}],isError:!0};let i=JSON.parse(o.output),s=null;for(let a in i.devices){let u=i.devices[a].find(p=>p.name===t.simulatorName);if(u){s=u;break}}if(!s)return{content:[{type:"text",text:`Simulator named "${t.simulatorName}" not found. Use list_sims to see available simulators.`}],isError:!0};r=s.udid,n=`"${t.simulatorName}" (${s.udid})`}if(!r)return{content:[{type:"text",text:"No simulator identifier provided"}],isError:!0};A("info",`Starting xcrun simctl launch request for simulator ${r}`);try{let o=["xcrun","simctl","get_app_container",r,t.bundleId,"app"];if(!(await e(o,"Check App Installed",!0,void 0)).success)return{content:[{type:"text",text:`App is not installed on the simulator. Please use install_app_sim before launching. - -Workflow: build \u2192 install \u2192 launch.`}],isError:!0}}catch{return{content:[{type:"text",text:`App is not installed on the simulator (check failed). Please use install_app_sim before launching. - -Workflow: build \u2192 install \u2192 launch.`}],isError:!0}}try{let o=["xcrun","simctl","launch",r,t.bundleId];t.args&&t.args.length>0&&o.push(...t.args);let i=await e(o,"Launch App in Simulator",!0,void 0);if(!i.success)return{content:[{type:"text",text:`Launch app in simulator operation failed: ${i.error}`}]};let s=t.simulatorId?"simulatorId":"simulatorName",a=t.simulatorId??t.simulatorName??r;return{content:[{type:"text",text:`\u2705 App launched successfully in simulator ${n||r}. - -Next Steps: -1. To see simulator: open_sim() -2. Log capture: start_sim_log_cap({ ${s}: "${a}", bundleId: "${t.bundleId}" }) - With console: start_sim_log_cap({ ${s}: "${a}", bundleId: "${t.bundleId}", captureConsole: true }) -3. Stop logs: stop_sim_log_cap({ logSessionId: 'SESSION_ID' })`}]}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error during launch app in simulator operation: ${i}`),{content:[{type:"text",text:`Launch app in simulator operation failed: ${i}`}]}}}var gF,LQe,UQe,jQe,rse=O(()=>{"use strict";Te();De();ze();wo();Le();gF=k({simulatorId:T().optional().describe("UUID of the simulator to use (obtained from list_sims). Provide EITHER this OR simulatorName, not both"),simulatorName:T().optional().describe("Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both"),bundleId:T().describe("Bundle identifier of the app to launch (e.g., 'com.example.MyApp')"),args:H(T()).optional().describe("Additional arguments to pass to the app")}),LQe=Bt(pr,gF.refine(t=>t.simulatorId!==void 0||t.simulatorName!==void 0,{message:"Either simulatorId or simulatorName is required."}).refine(t=>!(t.simulatorId!==void 0&&t.simulatorName!==void 0),{message:"simulatorId and simulatorName are mutually exclusive. Provide only one."}));UQe=st(gF.omit({simulatorId:!0,simulatorName:!0}).shape),jQe={name:"launch_app_sim",description:"Launches an app in an iOS simulator.",schema:de({sessionAware:UQe,legacy:gF}),annotations:{title:"Launch App Simulator",destructiveHint:!0},handler:fe({internalSchema:LQe,logicFunction:ese,getExecutor:z,requirements:[{oneOf:["simulatorId","simulatorName"],message:"Provide simulatorId or simulatorName"}],exclusivePairs:[["simulatorId","simulatorName"]]})}});var nse={};Y(nse,{default:()=>Af});var ose=O(()=>{"use strict";NE()});var sse={};Y(sse,{default:()=>_F,list_simsLogic:()=>KI});function zQe(t){let e=[],r=t.split(` -`),n="";for(let o of r){let i=o.match(/^-- ([\w\s.]+) --$/);if(i){n=i[1];continue}let s=o.match(/^\s+(.+?)\s+\(([^)]+)\)\s+\((Booted|Shutdown|Booting|Shutting Down)\)(\s+\(unavailable.*\))?$/i);if(s&&n){let[,a,c,u,p]=s;!!p||e.push({name:a.trim(),udid:c,state:u,isAvailable:!0,runtime:n})}}return e}function FQe(t){if(!t||typeof t!="object")return!1;let e=t;if(!e.devices||typeof e.devices!="object")return!1;let r=e.devices;for(let n in r){let o=r[n];if(!Array.isArray(o))return!1;for(let i of o){if(!i||typeof i!="object")return!1;let s=i;if(typeof s.name!="string"||typeof s.udid!="string"||typeof s.state!="string"||typeof s.isAvailable!="boolean")return!1}}return!0}async function KI(t,e){A("info","Starting xcrun simctl list devices request");try{let n=await e(["xcrun","simctl","list","devices","--json"],"List Simulators (JSON)",!0);if(!n.success)return{content:[{type:"text",text:`Failed to list simulators: ${n.error}`}]};let o={};try{let f=JSON.parse(n.output);FQe(f)&&(o=f.devices)}catch{A("warn","Failed to parse JSON output, falling back to text parsing")}let s=await e(["xcrun","simctl","list","devices"],"List Simulators (Text)",!0),a=s.success?zQe(s.output):[],c={...o},u=new Set;for(let f in o)for(let m of o[f])m.isAvailable&&u.add(m.udid);for(let f of a)if(!u.has(f.udid)){let m=f.runtime??"Unknown Runtime";c[m]||(c[m]=[]),c[m].push(f),A("info",`Added missing device from text parsing: ${f.name} (${f.udid})`)}let p=`Available iOS Simulators: - -`;for(let f in c){let m=c[f].filter(h=>h.isAvailable);if(m.length!==0){p+=`${f}: -`;for(let h of m)p+=`- ${h.name} (${h.udid})${h.state==="Booted"?" [Booted]":""} -`;p+=` -`}}return p+=`Next Steps: -`,p+=`1. Boot a simulator: boot_sim({ simulatorId: 'UUID_FROM_ABOVE' }) -`,p+=`2. Open the simulator UI: open_sim({}) -`,p+=`3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }) -`,p+="4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })",{content:[{type:"text",text:p}]}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error listing simulators: ${n}`),{content:[{type:"text",text:`Failed to list simulators: ${n}`}]}}}var ise,_F,ZI=O(()=>{"use strict";Te();De();ze();Le();ise=k({enabled:ue().optional().describe("Optional flag to enable the listing operation.")});_F={name:"list_sims",description:"Lists available iOS simulators with their UUIDs. ",schema:ise.shape,annotations:{title:"List Simulators",readOnlyHint:!0},handler:Ht(ise,KI,z)}});var use={};Y(use,{default:()=>vF,open_simLogic:()=>cse});async function cse(t,e){A("info","Starting open simulator request");try{let n=await e(["open","-a","Simulator"],"Open Simulator",!0);return n.success?{content:[{type:"text",text:"Simulator app opened successfully"},{type:"text",text:`Next Steps: -1. Boot a simulator if needed: boot_sim({ simulatorId: 'UUID_FROM_LIST_SIMULATORS' }) -2. Launch your app and interact with it -3. Log capture options: - - Option 1: Capture structured logs only (app continues running): - start_sim_log_cap({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' }) - - Option 2: Capture both console and structured logs (app will restart): - start_sim_log_cap({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID', captureConsole: true }) - - Option 3: Launch app with logs in one step: - launch_app_logs_sim({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' })`}]}:{content:[{type:"text",text:`Open simulator operation failed: ${n.error}`}]}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error during open simulator operation: ${n}`),{content:[{type:"text",text:`Open simulator operation failed: ${n}`}]}}}var ase,vF,SF=O(()=>{"use strict";Te();De();ze();Le();ase=k({});vF={name:"open_sim",description:"Opens the iOS Simulator app.",schema:ase.shape,annotations:{title:"Open Simulator",destructiveHint:!0},handler:Ht(ase,cse,z)}});var Pf=O(()=>{"use strict";Qu()});function qQe(){if(lse)return;lse=!0;let t=()=>{for(let[e,r]of ig)try{r.process?.kill?.("SIGINT")}catch{}finally{ig.delete(e)}};try{process.on("SIGINT",t),process.on("SIGTERM",t),process.on("exit",t)}catch{}}function BQe(t){if(!t)return null;let e=[...t.matchAll(/(\s|^)(\/[^\s'"]+\.mp4)\b/gi)];return e.length===0?null:e[e.length-1]?.[2]??null}function GQe(t){return`${t}:${Date.now()}`}async function yF(t,e,r){let n=t.simulatorUuid;if(!n)return{started:!1,error:"simulatorUuid is required"};if(ig.has(n))return{started:!1,error:"A video recording session is already active for this simulator. Stop it first."};let o=r??{getAxePath:Tt,getBundledAxeEnvironment:Nt},i=o.getAxePath();if(!i)return{started:!1,error:"Bundled AXe binary not found"};let s=Number.isFinite(t.fps)?Number(t.fps):30,a=[i,"record-video","--udid",n,"--fps",String(s)],c=o.getBundledAxeEnvironment?.()??{};A("info",`Starting AXe video recording for simulator ${n} at ${s} fps`);let u=await e(a,"Start Simulator Video Capture",!0,{env:c},!0);if(!u.success||!u.process)return{started:!1,error:u.error??"Failed to start video capture process"};let p=u.process,f={process:p,sessionId:GQe(n),startedAt:Date.now(),buffer:"",ended:!1};try{p.stdout?.on("data",m=>{try{f.buffer+=String(m??"")}catch{}}),p.stderr?.on("data",m=>{try{f.buffer+=String(m??"")}catch{}})}catch{}try{p.once?.("exit",()=>{f.ended=!0}),p.once?.("close",()=>{f.ended=!0})}catch{}return ig.set(n,f),qQe(),{started:!0,sessionId:f.sessionId,warning:s!==(t.fps??30)?`FPS coerced to ${s}`:void 0}}async function EF(t,e){let r=t.simulatorUuid;if(!r)return{stopped:!1,error:"simulatorUuid is required"};let n=ig.get(r);if(!n)return{stopped:!1,error:"No active video recording session for this simulator"};let o=n.process;try{o?.kill?.("SIGINT")}catch{try{o?.kill?.()}catch{}}await new Promise(a=>{if(!o)return a();let c=n.ended===!0,u=o.exitCode!==null,p=o.signalCode!=null;if(c||u||p)return a();let f=!1,m=()=>{f||(f=!0,a())};try{o.once("close",m),o.once("exit",m)}catch{return m()}setTimeout(m,5e3)});let i=n.buffer,s=BQe(i)??void 0;return ig.delete(r),A("info",`Stopped AXe video recording for simulator ${r}. ${s?`Detected file: ${s}`:"No file detected in output."}`),{stopped:!0,sessionId:n.sessionId,stdout:i,parsedPath:s}}var ig,lse,pse=O(()=>{"use strict";De();Qu();ig=new Map,lse=!1});var dse=O(()=>{"use strict";pse()});var hse={};Y(hse,{default:()=>WQe,record_sim_videoLogic:()=>mse});async function mse(t,e,r={areAxeToolsAvailable:og,isAxeAtLeastVersion:uF,createAxeNotAvailableResponse:Ct},n={startSimulatorVideoCapture:yF,stopSimulatorVideoCapture:EF},o=Ir()){if(!r.areAxeToolsAvailable())return r.createAxeNotAvailableResponse();if(!await r.isAxeAtLeastVersion("1.1.0",e))return le("AXe v1.1.0 or newer is required for simulator video capture. Please update bundled AXe artifacts.",!0);if(t.start){let u=Number.isFinite(t.fps)?Number(t.fps):30,p=await n.startSimulatorVideoCapture({simulatorUuid:t.simulatorId,fps:u},e);if(!p.started)return le(`Failed to start video recording: ${p.error??"Unknown error"}`,!0);let f=[];typeof t.outputFile=="string"&&t.outputFile.length>0&&f.push("Note: outputFile is ignored when start=true; provide it when stopping to move/rename the recorded file."),p.warning&&f.push(p.warning);let m=`Next Steps: -Stop and save the recording: -record_sim_video({ simulatorId: "${t.simulatorId}", stop: true, outputFile: "/path/to/output.mp4" })`;return{content:[{type:"text",text:`\u{1F3A5} Video recording started for simulator ${t.simulatorId} at ${u} fps. -Session: ${p.sessionId}`},...f.length>0?[{type:"text",text:f.join(` -`)}]:[],{type:"text",text:m}],isError:!1}}let s=await n.stopSimulatorVideoCapture({simulatorUuid:t.simulatorId},e);if(!s.stopped)return le(`Failed to stop video recording: ${s.error??"Unknown error"}`,!0);let a=[],c=t.outputFile??s.parsedPath??"";try{if(t.outputFile){if(!s.parsedPath)return le(`Recording stopped but could not determine the recorded file path from AXe output. -Raw output: -${s.stdout??"(no output captured)"}`,!0);let u=s.parsedPath,p=t.outputFile;await o.mkdir((0,fse.dirname)(p),{recursive:!0}),await o.cp(u,p);try{await o.rm(u,{recursive:!1})}catch{}c=p,a.push(`Original file: ${u}`),a.push(`Saved to: ${p}`)}else s.parsedPath&&(a.push(`Saved to: ${s.parsedPath}`),c=s.parsedPath)}catch(u){let p=u instanceof Error?u.message:String(u);return le(`Recording stopped but failed to save/move the video file: ${p}`,!0)}return{content:[{type:"text",text:`\u2705 Video recording stopped for simulator ${t.simulatorId}.`},...a.length>0?[{type:"text",text:a.join(` -`)}]:[],...!a.length&&s.stdout?[{type:"text",text:`AXe output: -${s.stdout}`}]:[]],isError:!1,_meta:c?{outputFile:c}:void 0}}var fse,TF,VQe,HQe,WQe,gse=O(()=>{"use strict";Te();hr();ze();Pf();dse();Le();fse=require("path"),TF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}).describe("UUID of the simulator to record"),start:ue().optional().describe("Start recording if true"),stop:ue().optional().describe("Stop recording if true"),fps:ce().int().min(1).max(120).optional().describe("Frames per second (default 30)"),outputFile:T().optional().describe("Destination MP4 path to move the recorded video to on stop")}),VQe=TF.refine(t=>{let e=t.start===!0?1:0,r=t.stop===!0?1:0;return e+r===1},{message:"Provide exactly one of start=true or stop=true; these options are mutually exclusive",path:["start"]}).refine(t=>t.stop?typeof t.outputFile=="string"&&t.outputFile.length>0:!0,{message:"outputFile is required when stop=true",path:["outputFile"]});HQe=st(TF.omit({simulatorId:!0}).shape),WQe={name:"record_sim_video",description:"Starts or stops video capture for an iOS simulator.",schema:de({sessionAware:HQe,legacy:TF}),annotations:{title:"Record Simulator Video",destructiveHint:!0},handler:fe({internalSchema:VQe,logicFunction:mse,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Sse={};Y(Sse,{default:()=>xF,screenshotLogic:()=>vse});async function vse(t,e,r=Ir(),n={...KQe,tmpdir:_se.tmpdir},o={v4:Rf}){let{simulatorId:i}=t,s=n.tmpdir(),a=`screenshot_${o.v4()}.png`,c=n.join(s,a),u=`screenshot_optimized_${o.v4()}.jpg`,p=n.join(s,u),f=["xcrun","simctl","io",i,"screenshot",c];A("info",`${Ja}/screenshot: Starting capture to ${c} on ${i}`);try{let m=await e(f,`${Ja}: screenshot`,!1);if(!m.success)throw new at(`Failed to capture screenshot: ${m.error??m.output}`);A("info",`${Ja}/screenshot: Success for ${i}`);try{if(!(await e(["sips","-Z","800","-s","format","jpeg","-s","formatOptions","75",c,"--out",p],`${Ja}: optimize image`,!1)).success){A("warning",`${Ja}/screenshot: Image optimization failed, using original PNG`);let E=await r.readFile(c,"base64");try{await r.rm(c)}catch(x){A("warning",`${Ja}/screenshot: Failed to delete temp file: ${x}`)}return{content:[g2(E,"image/png")],isError:!1}}A("info",`${Ja}/screenshot: Image optimized successfully`);let v=await r.readFile(p,"base64");A("info",`${Ja}/screenshot: Successfully encoded image as Base64`);try{await r.rm(c),await r.rm(p)}catch(E){A("warning",`${Ja}/screenshot: Failed to delete temporary files: ${E}`)}return{content:[g2(v,"image/jpeg")],isError:!1}}catch(h){return A("error",`${Ja}/screenshot: Failed to process image file: ${h}`),ge(`Screenshot captured but failed to process image file: ${h instanceof Error?h.message:String(h)}`)}}catch(m){return A("error",`${Ja}/screenshot: Failed - ${m}`),m instanceof at?ge(`System error executing screenshot: ${m.message}`,m.originalError?.stack):ge(`An unexpected error occurred: ${m instanceof Error?m.message:String(m)}`)}}var KQe,_se,Ja,bF,ZQe,xF,AF=O(()=>{"use strict";KQe=W(require("path"),1),_se=require("os");Te();qI();Dn();De();hr();ze();Le();Ja="[Screenshot]",bF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"})}),ZQe=st(bF.omit({simulatorId:!0}).shape);xF={name:"screenshot",description:"Captures screenshot for visual verification. For UI coordinates, use describe_ui instead (don't determine coordinates from screenshots).",schema:de({sessionAware:ZQe,legacy:bF}),annotations:{title:"Screenshot",readOnlyHint:!0},handler:fe({internalSchema:bF,logicFunction:(t,e)=>vse(t,e),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var yse={};Y(yse,{default:()=>xF});var Ese=O(()=>{"use strict";AF()});var Tse={};Y(Tse,{default:()=>wf});var bse=O(()=>{"use strict";CE()});var Ase={};Y(Ase,{default:()=>XQe,stop_app_simLogic:()=>xse});async function xse(t,e){let r=t.simulatorId,n=r??"";if(t.simulatorName&&!r){A("info",`Looking up simulator by name: ${t.simulatorName}`);let o=await e(["xcrun","simctl","list","devices","available","--json"],"List Simulators",!0);if(!o.success)return{content:[{type:"text",text:`Failed to list simulators: ${o.error}`}],isError:!0};let i=JSON.parse(o.output),s=null;for(let a in i.devices){let u=i.devices[a].find(p=>p.name===t.simulatorName);if(u){s=u;break}}if(!s)return{content:[{type:"text",text:`Simulator named "${t.simulatorName}" not found. Use list_sims to see available simulators.`}],isError:!0};r=s.udid,n=`"${t.simulatorName}" (${s.udid})`}if(!r)return{content:[{type:"text",text:"No simulator identifier provided"}],isError:!0};A("info",`Stopping app ${t.bundleId} in simulator ${r}`);try{let o=["xcrun","simctl","terminate",r,t.bundleId],i=await e(o,"Stop App in Simulator",!0,void 0);return i.success?{content:[{type:"text",text:`\u2705 App ${t.bundleId} stopped successfully in simulator ${n||r}`}]}:{content:[{type:"text",text:`Stop app in simulator operation failed: ${i.error}`}],isError:!0}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Error stopping app in simulator: ${i}`),{content:[{type:"text",text:`Stop app in simulator operation failed: ${i}`}],isError:!0}}}var wF,YQe,JQe,XQe,wse=O(()=>{"use strict";Te();De();ze();wo();Le();wF=k({simulatorId:T().optional().describe("UUID of the simulator to use (obtained from list_sims). Provide EITHER this OR simulatorName, not both"),simulatorName:T().optional().describe("Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both"),bundleId:T().describe("Bundle identifier of the app to stop (e.g., 'com.example.MyApp')")}),YQe=Bt(pr,wF.refine(t=>t.simulatorId!==void 0||t.simulatorName!==void 0,{message:"Either simulatorId or simulatorName is required."}).refine(t=>!(t.simulatorId!==void 0&&t.simulatorName!==void 0),{message:"simulatorId and simulatorName are mutually exclusive. Provide only one."}));JQe=st(wF.omit({simulatorId:!0,simulatorName:!0}).shape),XQe={name:"stop_app_sim",description:"Stops an app running in an iOS simulator.",schema:de({sessionAware:JQe,legacy:wF}),annotations:{title:"Stop App Simulator",destructiveHint:!0},handler:fe({internalSchema:YQe,logicFunction:xse,getExecutor:z,requirements:[{oneOf:["simulatorId","simulatorName"],message:"Provide simulatorId or simulatorName"}],exclusivePairs:[["simulatorId","simulatorName"]]})}});async function QQe(t){try{let e=(0,Rse.promisify)(Pse.exec),{stdout:r}=await e(`xcrun xcresulttool get test-results summary --path "${t}"`),n=JSON.parse(r);return eet(n)}catch(e){let r=e instanceof Error?e.message:String(e);throw A("error",`Error parsing xcresult bundle: ${r}`),e}}function eet(t){let e=[];if(e.push(`Test Summary: ${t.title??"Unknown"}`),e.push(`Overall Result: ${t.result??"Unknown"}`),e.push(""),e.push("Test Counts:"),e.push(` Total: ${t.totalTestCount??0}`),e.push(` Passed: ${t.passedTests??0}`),e.push(` Failed: ${t.failedTests??0}`),e.push(` Skipped: ${t.skippedTests??0}`),e.push(` Expected Failures: ${t.expectedFailures??0}`),e.push(""),t.environmentDescription&&(e.push(`Environment: ${t.environmentDescription}`),e.push("")),t.devicesAndConfigurations&&Array.isArray(t.devicesAndConfigurations)&&t.devicesAndConfigurations.length>0){let r=t.devicesAndConfigurations[0].device;r&&(e.push(`Device: ${r.deviceName??"Unknown"} (${r.platform??"Unknown"} ${r.osVersion??"Unknown"})`),e.push(""))}return t.testFailures&&Array.isArray(t.testFailures)&&t.testFailures.length>0&&(e.push("Test Failures:"),t.testFailures.forEach((r,n)=>{e.push(` ${n+1}. ${r.testName??"Unknown Test"} (${r.targetName??"Unknown Target"})`),r.failureText&&e.push(` ${r.failureText}`)}),e.push("")),t.topInsights&&Array.isArray(t.topInsights)&&t.topInsights.length>0&&(e.push("Insights:"),t.topInsights.forEach((r,n)=>{e.push(` ${n+1}. [${r.impact??"Unknown"}] ${r.text??"No description"}`)})),e.join(` -`)}async function PF(t,e){A("info",`Starting test run for scheme ${t.scheme} on platform ${t.platform} (internal)`);try{let r=await(0,jE.mkdtemp)((0,RF.join)((0,Ise.tmpdir)(),"xcodebuild-test-")),n=(0,RF.join)(r,"TestResults.xcresult"),o=[...t.extraArgs??[],"-resultBundlePath",n],i=t.testRunnerEnv?{env:Jh(t.testRunnerEnv)}:void 0,s=await Ln({...t,extraArgs:o},{platform:t.platform,simulatorName:t.simulatorName,simulatorId:t.simulatorId,deviceId:t.deviceId,useLatestOS:t.useLatestOS,logPrefix:"Test Run"},t.preferXcodebuild,"test",e??z(),i);try{A("info",`Attempting to parse xcresult bundle at: ${n}`);try{let{stat:u}=await import("fs/promises");await u(n),A("info",`xcresult bundle exists at: ${n}`)}catch{throw A("warn",`xcresult bundle does not exist at: ${n}`),new Error(`xcresult bundle not found at ${n}`)}let a=await QQe(n);A("info","Successfully parsed xcresult bundle"),await(0,jE.rm)(r,{recursive:!0,force:!0});let c={content:[...s.content||[],{type:"text",text:` -Test Results Summary: -`+a}],isError:s.isError};return Sp(c)}catch(a){A("warn",`Failed to parse xcresult bundle: ${a}`);try{await(0,jE.rm)(r,{recursive:!0,force:!0})}catch(c){A("warn",`Failed to clean up temporary directory: ${c}`)}return Sp(s)}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error during test run: ${n}`),Sp(le(`Error during test run: ${n}`,!0))}}var Rse,Pse,jE,Ise,RF,Ose=O(()=>{"use strict";Rse=require("util"),Pse=require("child_process"),jE=require("fs/promises"),Ise=require("os"),RF=require("path");Go();Uc();Qh();Xh();hs()});var Nse=O(()=>{"use strict";Ose()});var $se={};Y($se,{default:()=>net,test_simLogic:()=>Cse});async function Cse(t,e){return t.simulatorId&&t.useLatestOS!==void 0&&A("warning","useLatestOS parameter is ignored when using simulatorId (UUID implies exact device/OS)"),PF({projectPath:t.projectPath,workspacePath:t.workspacePath,scheme:t.scheme,simulatorId:t.simulatorId,simulatorName:t.simulatorName,configuration:t.configuration??"Debug",derivedDataPath:t.derivedDataPath,extraArgs:t.extraArgs,useLatestOS:t.simulatorId?!1:t.useLatestOS??!1,preferXcodebuild:t.preferXcodebuild??!1,platform:"iOS Simulator",testRunnerEnv:t.testRunnerEnv},e)}var IF,tet,ret,net,kse=O(()=>{"use strict";Te();Nse();De();Dn();ze();wo();Le();IF=k({projectPath:T().optional().describe("Path to .xcodeproj file. Provide EITHER this OR workspacePath, not both"),workspacePath:T().optional().describe("Path to .xcworkspace file. Provide EITHER this OR projectPath, not both"),scheme:T().describe("The scheme to use (Required)"),simulatorId:T().optional().describe("UUID of the simulator (from list_sims). Provide EITHER this OR simulatorName, not both"),simulatorName:T().optional().describe("Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both"),configuration:T().optional().describe("Build configuration (Debug, Release, etc.)"),derivedDataPath:T().optional().describe("Path where build products and other derived data will go"),extraArgs:H(T()).optional().describe("Additional xcodebuild arguments"),useLatestOS:ue().optional().describe("Whether to use the latest OS version for the named simulator"),preferXcodebuild:ue().optional().describe("If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails."),testRunnerEnv:ir(T(),T()).optional().describe("Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)")}),tet=Bt(pr,IF.refine(t=>t.projectPath!==void 0||t.workspacePath!==void 0,{message:"Either projectPath or workspacePath is required."}).refine(t=>!(t.projectPath!==void 0&&t.workspacePath!==void 0),{message:"projectPath and workspacePath are mutually exclusive. Provide only one."}).refine(t=>t.simulatorId!==void 0||t.simulatorName!==void 0,{message:"Either simulatorId or simulatorName is required."}).refine(t=>!(t.simulatorId!==void 0&&t.simulatorName!==void 0),{message:"simulatorId and simulatorName are mutually exclusive. Provide only one."}));ret=IF.omit({projectPath:!0,workspacePath:!0,scheme:!0,simulatorId:!0,simulatorName:!0,configuration:!0,useLatestOS:!0}),net={name:"test_sim",description:"Runs tests on an iOS simulator.",schema:de({sessionAware:ret,legacy:IF}),annotations:{title:"Test Simulator",destructiveHint:!0},handler:fe({internalSchema:tet,logicFunction:Cse,getExecutor:z,requirements:[{allOf:["scheme"],message:"scheme is required"},{oneOf:["projectPath","workspacePath"],message:"Provide a project or workspace"},{oneOf:["simulatorId","simulatorName"],message:"Provide simulatorId or simulatorName"}],exclusivePairs:[["projectPath","workspacePath"],["simulatorId","simulatorName"]]})}});var Mse={};Y(Mse,{workflow:()=>oet});var oet,Dse=O(()=>{"use strict";oet={name:"Simulator Management",description:"Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance."}});var Lse={};Y(Lse,{default:()=>iF});var Use=O(()=>{"use strict";sF()});var zse={};Y(zse,{default:()=>aet,erase_simsLogic:()=>jse});async function jse(t,e){try{let r=t.simulatorId;if(A("info",`Erasing simulator ${r}${t.shutdownFirst?" (shutdownFirst=true)":""}`),t.shutdownFirst)try{await e(["xcrun","simctl","shutdown",r],"Shutdown Simulator",!0,void 0)}catch{}let n=await e(["xcrun","simctl","erase",r],"Erase Simulator",!0,void 0);if(n.success)return{content:[{type:"text",text:`Successfully erased simulator ${r}`}]};let o=n.error??"Unknown error";return/Unable to erase contents and settings.*Booted/i.test(o)&&!t.shutdownFirst?{content:[{type:"text",text:`Failed to erase simulator: ${o}`},{type:"text",text:`Tool hint: The simulator appears to be Booted. Re-run erase_sims with { simulatorId: '${r}', shutdownFirst: true } to shut it down before erasing.`}]}:{content:[{type:"text",text:`Failed to erase simulator: ${o}`}]}}catch(r){let n=r instanceof Error?r.message:String(r);return A("error",`Error erasing simulators: ${n}`),{content:[{type:"text",text:`Failed to erase simulators: ${n}`}]}}}var iet,OF,set,aet,Fse=O(()=>{"use strict";Te();De();ze();Le();iet=k({simulatorId:Dt().describe("UDID of the simulator to erase."),shutdownFirst:ue().optional().describe("If true, shuts down the simulator before erasing.")}).passthrough(),OF=iet;set=OF.omit({simulatorId:!0}).passthrough(),aet={name:"erase_sims",description:"Erases a simulator by UDID.",schema:de({sessionAware:set,legacy:OF}),annotations:{title:"Erase Simulators",destructiveHint:!0},handler:fe({internalSchema:OF,logicFunction:jse,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var qse={};Y(qse,{default:()=>_F});var Bse=O(()=>{"use strict";ZI()});var Gse={};Y(Gse,{default:()=>vF});var Vse=O(()=>{"use strict";SF()});var Wse={};Y(Wse,{default:()=>pet,reset_sim_locationLogic:()=>Hse});async function cet(t,e,r,n,o,i,s,a){if(a){let c=a();if(c)return c}try{let c=["xcrun","simctl",...e],u=await s(c,r,!0,{});if(!u.success){let p=`${o}: ${u.error}`;return A("error",`${p} (operation: ${i}, simulator: ${t.simulatorId})`),{content:[{type:"text",text:p}]}}return A("info",`${n} (operation: ${i}, simulator: ${t.simulatorId})`),{content:[{type:"text",text:n}]}}catch(c){let u=c instanceof Error?c.message:String(c),p=`${o}: ${u}`;return A("error",`Error during ${i} for simulator ${t.simulatorId}: ${u}`),{content:[{type:"text",text:p}]}}}async function Hse(t,e){return A("info",`Resetting simulator ${t.simulatorId} location`),cet(t,["location",t.simulatorId,"clear"],"Reset Simulator Location",`Successfully reset simulator ${t.simulatorId} location.`,"Failed to reset simulator location","reset simulator location",e)}var NF,uet,pet,Kse=O(()=>{"use strict";Te();De();ze();Le();NF=k({simulatorId:Dt().describe("UUID of the simulator to use (obtained from list_simulators)")});uet=st(NF.omit({simulatorId:!0}).shape),pet={name:"reset_sim_location",description:"Resets the simulator's location to default.",schema:de({sessionAware:uet,legacy:NF}),annotations:{title:"Reset Simulator Location",destructiveHint:!0},handler:fe({internalSchema:NF,logicFunction:Hse,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Yse={};Y(Yse,{default:()=>met,set_sim_appearanceLogic:()=>Zse});async function det(t,e,r,n,o,i,s,a=z()){if(s){let c=s();if(c)return c}try{let c=["xcrun","simctl",...e],u=await a(c,r,!0,void 0);if(!u.success){let p=`${o}: ${u.error}`;return A("error",`${p} (operation: ${i}, simulator: ${t.simulatorId})`),{content:[{type:"text",text:p}]}}return A("info",`${n} (operation: ${i}, simulator: ${t.simulatorId})`),{content:[{type:"text",text:n}]}}catch(c){let u=c instanceof Error?c.message:String(c),p=`${o}: ${u}`;return A("error",`Error during ${i} for simulator ${t.simulatorId}: ${u}`),{content:[{type:"text",text:p}]}}}async function Zse(t,e){return A("info",`Setting simulator ${t.simulatorId} appearance to ${t.mode} mode`),det(t,["ui",t.simulatorId,"appearance",t.mode],"Set Simulator Appearance",`Successfully set simulator ${t.simulatorId} appearance to ${t.mode} mode`,"Failed to set simulator appearance","set simulator appearance",void 0,e)}var CF,fet,met,Jse=O(()=>{"use strict";Te();De();ze();Le();CF=k({simulatorId:Dt().describe("UUID of the simulator to use (obtained from list_simulators)"),mode:Ze(["dark","light"]).describe('The appearance mode to set (either "dark" or "light")')});fet=st(CF.omit({simulatorId:!0}).shape),met={name:"set_sim_appearance",description:"Sets the appearance mode (dark/light) of an iOS simulator.",schema:de({sessionAware:fet,legacy:CF}),annotations:{title:"Set Simulator Appearance",destructiveHint:!0},handler:fe({internalSchema:CF,logicFunction:Zse,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Qse={};Y(Qse,{default:()=>_et,set_sim_locationLogic:()=>Xse});async function het(t,e,r,n,o,i,s=z(),a){if(a){let c=a();if(c)return c}try{let c=["xcrun","simctl",...e],u=await s(c,r,!0,{});if(!u.success){let p=`${o}: ${u.error}`;return A("error",`${p} (operation: ${i}, simulator: ${t.simulatorId})`),{content:[{type:"text",text:p}]}}return A("info",`${n} (operation: ${i}, simulator: ${t.simulatorId})`),{content:[{type:"text",text:n}]}}catch(c){let u=c instanceof Error?c.message:String(c),p=`${o}: ${u}`;return A("error",`Error during ${i} for simulator ${t.simulatorId}: ${u}`),{content:[{type:"text",text:p}]}}}async function Xse(t,e){let r=()=>t.latitude<-90||t.latitude>90?{content:[{type:"text",text:"Latitude must be between -90 and 90 degrees"}]}:t.longitude<-180||t.longitude>180?{content:[{type:"text",text:"Longitude must be between -180 and 180 degrees"}]}:null;return A("info",`Setting simulator ${t.simulatorId} location to ${t.latitude},${t.longitude}`),het(t,["location",t.simulatorId,"set",`${t.latitude},${t.longitude}`],"Set Simulator Location",`Successfully set simulator ${t.simulatorId} location to ${t.latitude},${t.longitude}`,"Failed to set simulator location","set simulator location",e,r)}var $F,get,_et,eae=O(()=>{"use strict";Te();De();ze();Le();$F=k({simulatorId:Dt().describe("UUID of the simulator to use (obtained from list_simulators)"),latitude:ce().describe("The latitude for the custom location."),longitude:ce().describe("The longitude for the custom location.")});get=st($F.omit({simulatorId:!0}).shape),_et={name:"set_sim_location",description:"Sets a custom GPS location for the simulator.",schema:de({sessionAware:get,legacy:$F}),annotations:{title:"Set Simulator Location",destructiveHint:!0},handler:fe({internalSchema:$F,logicFunction:Xse,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var rae={};Y(rae,{default:()=>yet,sim_statusbarLogic:()=>tae});async function tae(t,e){A("info",`Setting simulator ${t.simulatorId} status bar data network to ${t.dataNetwork}`);try{let r,n;t.dataNetwork==="clear"?(r=["xcrun","simctl","status_bar",t.simulatorId,"clear"],n=`Successfully cleared status bar overrides for simulator ${t.simulatorId}`):(r=["xcrun","simctl","status_bar",t.simulatorId,"override","--dataNetwork",t.dataNetwork],n=`Successfully set simulator ${t.simulatorId} status bar data network to ${t.dataNetwork}`);let o=await e(r,"Set Status Bar",!0,void 0);if(!o.success){let i=`Failed to set status bar: ${o.error}`;return A("error",`${i} (simulator: ${t.simulatorId})`),{content:[{type:"text",text:i}],isError:!0}}return A("info",`${n} (simulator: ${t.simulatorId})`),{content:[{type:"text",text:n}]}}catch(r){let n=r instanceof Error?r.message:String(r),o=`Failed to set status bar: ${n}`;return A("error",`Error setting status bar for simulator ${t.simulatorId}: ${n}`),{content:[{type:"text",text:o}],isError:!0}}}var kF,vet,yet,nae=O(()=>{"use strict";Te();De();ze();Le();kF=k({simulatorId:Dt().describe("UUID of the simulator to use (obtained from list_simulators)"),dataNetwork:Ze(["clear","hide","wifi","3g","4g","lte","lte-a","lte+","5g","5g+","5g-uwb","5g-uc"]).describe('Data network type to display in status bar. Use "clear" to reset all overrides. Valid values: clear, hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc.')});vet=st(kF.omit({simulatorId:!0}).shape),yet={name:"sim_statusbar",description:'Sets the data network indicator in the iOS simulator status bar. Use "clear" to reset all overrides, or specify a network type (hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc).',schema:de({sessionAware:vet,legacy:kF}),annotations:{title:"Simulator Statusbar",destructiveHint:!0},handler:fe({internalSchema:kF,logicFunction:tae,getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var oae={};Y(oae,{workflow:()=>Eet});var Eet,iae=O(()=>{"use strict";Eet={name:"Swift Package Manager",description:"Swift Package Manager operations for building, testing, running, and managing Swift packages and dependencies. Complete SPM workflow support."}});var uae={};Y(uae,{default:()=>Tet,swift_package_buildLogic:()=>cae});async function cae(t,e){let n=["build","--package-path",aae.default.resolve(t.packagePath)];if(t.configuration&&t.configuration.toLowerCase()==="release"&&n.push("-c","release"),t.targetName&&n.push("--target",t.targetName),t.architectures)for(let o of t.architectures)n.push("--arch",o);t.parseAsLibrary&&n.push("-Xswiftc","-parse-as-library"),A("info",`Running swift ${n.join(" ")}`);try{let o=await e(["swift",...n],"Swift Package Build",!0,void 0);if(!o.success){let i=o.error??o.output??"Unknown error";return ge("Swift package build failed",i)}return{content:[{type:"text",text:"\u2705 Swift package build succeeded."},{type:"text",text:"\u{1F4A1} Next: Run tests with swift_package_test or execute with swift_package_run"},{type:"text",text:o.output}],isError:!1}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Swift package build failed: ${i}`),ge("Failed to execute swift build",i)}}var aae,sae,Tet,lae=O(()=>{"use strict";Te();aae=W(require("node:path"),1);hr();De();ze();Le();sae=k({packagePath:T().describe("Path to the Swift package root (Required)"),targetName:T().optional().describe("Optional target to build"),configuration:Ze(["debug","release"]).optional().describe("Swift package configuration (debug, release)"),architectures:H(T()).optional().describe("Target architectures to build for"),parseAsLibrary:ue().optional().describe("Build as library instead of executable")});Tet={name:"swift_package_build",description:"Builds a Swift Package with swift build",schema:sae.shape,annotations:{title:"Swift Package Build",destructiveHint:!0},handler:Ht(sae,cae,z)}});var mae={};Y(mae,{default:()=>bet,swift_package_cleanLogic:()=>fae});async function fae(t,e){let n=["package","--package-path",dae.default.resolve(t.packagePath),"clean"];A("info",`Running swift ${n.join(" ")}`);try{let o=await e(["swift",...n],"Swift Package Clean",!0,void 0);if(!o.success){let i=o.error??o.output??"Unknown error";return ge("Swift package clean failed",i)}return{content:[{type:"text",text:"\u2705 Swift package cleaned successfully."},{type:"text",text:"\u{1F4A1} Build artifacts and derived data removed. Ready for fresh build."},{type:"text",text:o.output||"(clean completed silently)"}],isError:!1}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Swift package clean failed: ${i}`),ge("Failed to execute swift package clean",i)}}var dae,pae,bet,hae=O(()=>{"use strict";Te();dae=W(require("node:path"),1);ze();hr();De();Le();pae=k({packagePath:T().describe("Path to the Swift package root (Required)")});bet={name:"swift_package_clean",description:"Cleans Swift Package build artifacts and derived data",schema:pae.shape,annotations:{title:"Swift Package Clean",destructiveHint:!0},handler:Ht(pae,fae,z)}});var vae={};Y(vae,{default:()=>Aet,swift_package_listLogic:()=>_ae});async function _ae(t,e){let r=e?.processMap??xet,n=e?.arrayFrom??Array.from,o=e?.dateNow??Date.now,i=n(r.entries());if(i.length===0)return{content:[Lt("\u2139\uFE0F No Swift Package processes currently running."),Lt("\u{1F4A1} Use swift_package_run to start an executable.")]};let s=[Lt(`\u{1F4CB} Active Swift Package processes (${i.length}):`)];for(let[a,c]of i){let u=c.executableName||"default",p=Math.max(1,Math.round((o()-c.startedAt.getTime())/1e3));s.push(Lt(` \u2022 PID ${a}: ${u} (${c.packagePath}) - running ${p}s`))}return s.push(Lt("\u{1F4A1} Use swift_package_stop with a PID to terminate a process.")),{content:s}}var xet,gae,Aet,Sae=O(()=>{"use strict";Te();Dn();Le();hs();xet=new Map;gae=k({}),Aet={name:"swift_package_list",description:"Lists currently running Swift Package processes",schema:gae.shape,annotations:{title:"Swift Package List",readOnlyHint:!0},handler:Ht(gae,t=>_ae(t),z)}});var MF,yae,Eae,Tae,DF=O(()=>{"use strict";MF=new Map,yae=t=>MF.get(t),Eae=(t,e)=>{MF.set(t,e)},Tae=t=>MF.delete(t)});var wae={};Y(wae,{default:()=>wet,swift_package_runLogic:()=>Aae});async function Aae(t,e){let r=xae.default.resolve(t.packagePath),n=Math.min(t.timeout??30,300)*1e3,o=process.env.VITEST==="true"||!1,i=["run","--package-path",r];if(t.configuration&&t.configuration.toLowerCase()==="release")i.push("-c","release");else if(t.configuration&&t.configuration.toLowerCase()!=="debug")return le("Invalid configuration. Use 'debug' or 'release'.",!0);t.parseAsLibrary&&i.push("-Xswiftc","-parse-as-library"),t.executableName&&i.push(t.executableName),t.arguments&&t.arguments.length>0&&(i.push("--"),i.push(...t.arguments)),A("info",`Running swift ${i.join(" ")}`);try{if(t.background){if(o)return{content:[Lt(`\u{1F680} Started executable in background (PID: 12345) -\u{1F4A1} Process is running independently. Use swift_package_stop with PID 12345 to terminate when needed.`)]};{let s=["swift",...i],a=Object.fromEntries(Object.entries(process.env).filter(([,u])=>u!==void 0)),c=await e(s,"Swift Package Run (Background)",!0,a,!0);return c.process?.pid?(Eae(c.process.pid,{process:{kill:u=>{c.process&&c.process.kill(u)},on:(u,p)=>{c.process&&c.process.on(u,p)},pid:c.process.pid},startedAt:new Date}),{content:[Lt(`\u{1F680} Started executable in background (PID: ${c.process.pid}) -\u{1F4A1} Process is running independently. Use swift_package_stop with PID ${c.process.pid} to terminate when needed.`)]}):{content:[Lt(`\u{1F680} Started executable in background -\u{1F4A1} Process is running independently. PID not available for this execution.`)]}}}else{let s=["swift",...i],a=e(s,"Swift Package Run",!0,void 0),c=new Promise(p=>{setTimeout(()=>{p({success:!1,output:"",error:`Process timed out after ${n/1e3} seconds`,timedOut:!0})},n)}),u=await Promise.race([a,c]);if("timedOut"in u&&u.timedOut)return o?{content:[Lt(`\u23F1\uFE0F Process timed out after ${n/1e3} seconds but may continue running.`),Lt("PID: 12345 (mock)"),Lt("\u{1F4A1} Process may still be running. Use swift_package_stop with PID 12345 to terminate when needed."),Lt(u.output||"(no output so far)")]}:{content:[Lt(`\u23F1\uFE0F Process timed out after ${n/1e3} seconds.`),Lt("\u{1F4A1} Process execution exceeded the timeout limit. Consider using background mode for long-running executables."),Lt(u.output||"(no output so far)")]};if(u.success)return{content:[Lt("\u2705 Swift executable completed successfully."),Lt("\u{1F4A1} Process finished cleanly. Check output for results."),Lt(u.output||"(no output)")]};{let p=[Lt("\u274C Swift executable failed."),Lt(u.output||"(no output)")];return u.error&&p.push(Lt(`Errors: -${u.error}`)),{content:p}}}}catch(s){let a=s instanceof Error?s.message:String(s);return A("error",`Swift run failed: ${a}`),ge("Failed to execute swift run",a)}}var xae,bae,wet,Rae=O(()=>{"use strict";Te();xae=W(require("node:path"),1);hr();De();ze();Dn();DF();Le();bae=k({packagePath:T().describe("Path to the Swift package root (Required)"),executableName:T().optional().describe("Name of executable to run (defaults to package name)"),arguments:H(T()).optional().describe("Arguments to pass to the executable"),configuration:Ze(["debug","release"]).optional().describe("Build configuration: 'debug' (default) or 'release'"),timeout:ce().optional().describe("Timeout in seconds (default: 30, max: 300)"),background:ue().optional().describe("Run in background and return immediately (default: false)"),parseAsLibrary:ue().optional().describe("Add -parse-as-library flag for @main support (default: false)")});wet={name:"swift_package_run",description:"Runs an executable target from a Swift Package with swift run",schema:bae.shape,annotations:{title:"Swift Package Run",destructiveHint:!0},handler:Ht(bae,Aae,z)}});var Nae={};Y(Nae,{createMockProcessManager:()=>Pet,default:()=>Iet,getDefaultProcessManager:()=>Iae,swift_package_stopLogic:()=>Oae});function Iae(){return Ret}function Pet(t){return{getProcess:()=>{},removeProcess:()=>!0,...t}}async function Oae(t,e=Iae(),r=5e3){let n=e.getProcess(t.pid);if(!n)return le(`\u26A0\uFE0F No running process found with PID ${t.pid}. Use swift_package_run to check active processes.`,!0);try{return n.process.kill("SIGTERM"),await new Promise(o=>{let i=!1;n.process.on("exit",()=>{i=!0,o(!0)}),setTimeout(()=>{i||n.process.kill("SIGKILL"),o(!0)},r)}),e.removeProcess(t.pid),{content:[{type:"text",text:`\u2705 Stopped executable (was running since ${n.startedAt.toISOString()})`},{type:"text",text:"\u{1F4A1} Process terminated. You can now run swift_package_run again if needed."}]}}catch(o){let i=o instanceof Error?o.message:String(o);return ge("Failed to stop process",i)}}var Pae,Ret,Iet,Cae=O(()=>{"use strict";Te();hr();DF();Pae=k({pid:ce().describe("Process ID (PID) of the running executable")}),Ret={getProcess:yae,removeProcess:Tae};Iet={name:"swift_package_stop",description:"Stops a running Swift Package executable started with swift_package_run",schema:Pae.shape,annotations:{title:"Swift Package Stop",destructiveHint:!0},async handler(t){let e=Pae.safeParse(t);return e.success?Oae(e.data):ge("Parameter validation failed",e.error.issues.map(r=>`${r.path.join(".")}: ${r.message}`).join(", "))}}});var Dae={};Y(Dae,{default:()=>Oet,swift_package_testLogic:()=>Mae});async function Mae(t,e){let n=["test","--package-path",kae.default.resolve(t.packagePath)];if(t.configuration&&t.configuration.toLowerCase()==="release")n.push("-c","release");else if(t.configuration&&t.configuration.toLowerCase()!=="debug")return le("Invalid configuration. Use 'debug' or 'release'.",!0);t.testProduct&&n.push("--test-product",t.testProduct),t.filter&&n.push("--filter",t.filter),t.parallel===!1&&n.push("--no-parallel"),t.showCodecov&&n.push("--show-code-coverage"),t.parseAsLibrary&&n.push("-Xswiftc","-parse-as-library"),A("info",`Running swift ${n.join(" ")}`);try{let o=await e(["swift",...n],"Swift Package Test",!0,void 0);if(!o.success){let i=o.error??o.output??"Unknown error";return ge("Swift package tests failed",i)}return{content:[{type:"text",text:"\u2705 Swift package tests completed."},{type:"text",text:"\u{1F4A1} Next: Execute your app with swift_package_run if tests passed"},{type:"text",text:o.output}],isError:!1}}catch(o){let i=o instanceof Error?o.message:String(o);return A("error",`Swift package test failed: ${i}`),ge("Failed to execute swift test",i)}}var kae,$ae,Oet,Lae=O(()=>{"use strict";Te();kae=W(require("node:path"),1);ze();hr();De();Le();$ae=k({packagePath:T().describe("Path to the Swift package root (Required)"),testProduct:T().optional().describe("Optional specific test product to run"),filter:T().optional().describe("Filter tests by name (regex pattern)"),configuration:Ze(["debug","release"]).optional().describe("Swift package configuration (debug, release)"),parallel:ue().optional().describe("Run tests in parallel (default: true)"),showCodecov:ue().optional().describe("Show code coverage (default: false)"),parseAsLibrary:ue().optional().describe("Add -parse-as-library flag for @main support (default: false)")});Oet={name:"swift_package_test",description:"Runs tests for a Swift Package with swift test",schema:$ae.shape,annotations:{title:"Swift Package Test",destructiveHint:!0},handler:Ht($ae,Mae,z)}});var Uae={};Y(Uae,{workflow:()=>Net});var Net,jae=O(()=>{"use strict";Net={name:"UI Testing & Automation",description:"UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows."}});var Fae={};Y(Fae,{buttonLogic:()=>zae,default:()=>$et});async function zae(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let n="button",{simulatorId:o,buttonType:i,duration:s}=t,a=["button",i];s!==void 0&&a.push("--duration",String(s)),A("info",`${zE}/${n}: Starting ${i} button press on ${o}`);try{return await ket(a,o,"button",e,r),A("info",`${zE}/${n}: Success for ${o}`),le(`Hardware button '${i}' pressed successfully.`)}catch(c){return A("error",`${zE}/${n}: Failed - ${c}`),c instanceof sr?r.createAxeNotAvailableResponse():c instanceof gt?ge(`Failed to press button '${i}': ${c.message}`,c.axeOutput):c instanceof at?ge(`System error executing axe: ${c.message}`,c.originalError?.stack):ge(`An unexpected error occurred: ${c instanceof Error?c.message:String(c)}`)}}async function ket(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${zE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);u.error&&A("warn",`${zE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`)}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var LF,zE,Cet,$et,qae=O(()=>{"use strict";Te();De();hr();ze();Qu();yp();Le();LF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),buttonType:Ze(["apple-pay","home","lock","side-button","siri"]),duration:ce().min(0,{message:"Duration must be non-negative"}).optional()}),zE="[AXe]";Cet=st(LF.omit({simulatorId:!0}).shape),$et={name:"button",description:"Press hardware button on iOS simulator. Supported buttons: apple-pay, home, lock, side-button, siri",schema:de({sessionAware:Cet,legacy:LF}),annotations:{title:"Hardware Button",destructiveHint:!0},handler:fe({internalSchema:LF,logicFunction:(t,e)=>zae(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Gae={};Y(Gae,{default:()=>Det,gestureLogic:()=>Bae});async function Bae(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let n="gesture",{simulatorId:o,preset:i,screenWidth:s,screenHeight:a,duration:c,delta:u,preDelay:p,postDelay:f}=t,m=["gesture",i];s!==void 0&&m.push("--screen-width",String(s)),a!==void 0&&m.push("--screen-height",String(a)),c!==void 0&&m.push("--duration",String(c)),u!==void 0&&m.push("--delta",String(u)),p!==void 0&&m.push("--pre-delay",String(p)),f!==void 0&&m.push("--post-delay",String(f)),A("info",`${FE}/${n}: Starting gesture '${i}' on ${o}`);try{return await Let(m,o,"gesture",e,r),A("info",`${FE}/${n}: Success for ${o}`),le(`Gesture '${i}' executed successfully.`)}catch(h){return A("error",`${FE}/${n}: Failed - ${h}`),h instanceof sr?r.createAxeNotAvailableResponse():h instanceof gt?ge(`Failed to execute gesture '${i}': ${h.message}`,h.axeOutput):h instanceof at?ge(`System error executing axe: ${h.message}`,h.originalError?.stack):ge(`An unexpected error occurred: ${h instanceof Error?h.message:String(h)}`)}}async function Let(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${FE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);u.error&&A("warn",`${FE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`)}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var UF,FE,Met,Det,Vae=O(()=>{"use strict";Te();De();hr();ze();Pf();Le();UF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),preset:Ze(["scroll-up","scroll-down","scroll-left","scroll-right","swipe-from-left-edge","swipe-from-right-edge","swipe-from-top-edge","swipe-from-bottom-edge"]).describe("The gesture preset to perform. Must be one of: scroll-up, scroll-down, scroll-left, scroll-right, swipe-from-left-edge, swipe-from-right-edge, swipe-from-top-edge, swipe-from-bottom-edge."),screenWidth:ce().int().min(1).optional().describe("Optional: Screen width in pixels. Used for gesture calculations. Auto-detected if not provided."),screenHeight:ce().int().min(1).optional().describe("Optional: Screen height in pixels. Used for gesture calculations. Auto-detected if not provided."),duration:ce().min(0,{message:"Duration must be non-negative"}).optional().describe("Optional: Duration of the gesture in seconds."),delta:ce().min(0,{message:"Delta must be non-negative"}).optional().describe("Optional: Distance to move in pixels."),preDelay:ce().min(0,{message:"Pre-delay must be non-negative"}).optional().describe("Optional: Delay before starting the gesture in seconds."),postDelay:ce().min(0,{message:"Post-delay must be non-negative"}).optional().describe("Optional: Delay after completing the gesture in seconds.")}),FE="[AXe]";Met=st(UF.omit({simulatorId:!0}).shape),Det={name:"gesture",description:"Perform gesture on iOS simulator using preset gestures: scroll-up, scroll-down, scroll-left, scroll-right, swipe-from-left-edge, swipe-from-right-edge, swipe-from-top-edge, swipe-from-bottom-edge",schema:de({sessionAware:Met,legacy:UF}),annotations:{title:"Gesture",destructiveHint:!0},handler:fe({internalSchema:UF,logicFunction:(t,e)=>Bae(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Wae={};Y(Wae,{default:()=>jet,key_pressLogic:()=>Hae});async function Hae(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let n="key_press",{simulatorId:o,keyCode:i,duration:s}=t,a=["key",String(i)];s!==void 0&&a.push("--duration",String(s)),A("info",`${qE}/${n}: Starting key press ${i} on ${o}`);try{return await zet(a,o,"key",e,r),A("info",`${qE}/${n}: Success for ${o}`),le(`Key press (code: ${i}) simulated successfully.`)}catch(c){return A("error",`${qE}/${n}: Failed - ${c}`),c instanceof sr?r.createAxeNotAvailableResponse():c instanceof gt?ge(`Failed to simulate key press (code: ${i}): ${c.message}`,c.axeOutput):c instanceof at?ge(`System error executing axe: ${c.message}`,c.originalError?.stack):ge(`An unexpected error occurred: ${c instanceof Error?c.message:String(c)}`)}}async function zet(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${qE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);u.error&&A("warn",`${qE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`)}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var jF,qE,Uet,jet,Kae=O(()=>{"use strict";Te();De();hr();ze();Pf();Le();jF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),keyCode:ce().int({message:"HID keycode to press (0-255)"}).min(0).max(255),duration:ce().min(0,{message:"Duration must be non-negative"}).optional()}),qE="[AXe]";Uet=st(jF.omit({simulatorId:!0}).shape),jet={name:"key_press",description:"Press a single key by keycode on the simulator. Common keycodes: 40=Return, 42=Backspace, 43=Tab, 44=Space, 58-67=F1-F10.",schema:de({sessionAware:Uet,legacy:jF}),annotations:{title:"Key Press",destructiveHint:!0},handler:fe({internalSchema:jF,logicFunction:(t,e)=>Hae(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Yae={};Y(Yae,{default:()=>qet,key_sequenceLogic:()=>Zae});async function Zae(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let n="key_sequence",{simulatorId:o,keyCodes:i,delay:s}=t,a=["key-sequence","--keycodes",i.join(",")];s!==void 0&&a.push("--delay",String(s)),A("info",`${BE}/${n}: Starting key sequence [${i.join(",")}] on ${o}`);try{return await Bet(a,o,"key-sequence",e,r),A("info",`${BE}/${n}: Success for ${o}`),le(`Key sequence [${i.join(",")}] executed successfully.`)}catch(c){return A("error",`${BE}/${n}: Failed - ${c}`),c instanceof sr?r.createAxeNotAvailableResponse():c instanceof gt?ge(`Failed to execute key sequence: ${c.message}`,c.axeOutput):c instanceof at?ge(`System error executing axe: ${c.message}`,c.originalError?.stack):ge(`An unexpected error occurred: ${c instanceof Error?c.message:String(c)}`)}}async function Bet(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${BE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);u.error&&A("warn",`${BE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`)}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var zF,BE,Fet,qet,Jae=O(()=>{"use strict";Te();De();hr();ze();Pf();Le();zF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),keyCodes:H(ce().int().min(0).max(255)).min(1,{message:"At least one key code required"}),delay:ce().min(0,{message:"Delay must be non-negative"}).optional()}),BE="[AXe]";Fet=st(zF.omit({simulatorId:!0}).shape),qet={name:"key_sequence",description:"Press key sequence using HID keycodes on iOS simulator with configurable delay",schema:de({sessionAware:Fet,legacy:zF}),annotations:{title:"Key Sequence",destructiveHint:!0},handler:fe({internalSchema:zF,logicFunction:(t,e)=>Zae(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var Qae={};Y(Qae,{default:()=>Vet,long_pressLogic:()=>Xae});async function Xae(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let n="long_press",{simulatorId:o,x:i,y:s,duration:a}=t,c=Number(a)/1e3,u=["touch","-x",String(i),"-y",String(s),"--down","--up","--delay",String(c)];A("info",`${GE}/${n}: Starting for (${i}, ${s}), ${a}ms on ${o}`);try{await Zet(u,o,"touch",e,r),A("info",`${GE}/${n}: Success for ${o}`);let p=Ket(o),f=`Long press at (${i}, ${s}) for ${a}ms simulated successfully.`;return p?le(`${f} - -${p}`):le(f)}catch(p){return A("error",`${GE}/${n}: Failed - ${p}`),p instanceof sr?r.createAxeNotAvailableResponse():p instanceof gt?ge(`Failed to simulate long press at (${i}, ${s}): ${p.message}`,p.axeOutput):p instanceof at?ge(`System error executing axe: ${p.message}`,p.originalError?.stack):ge(`An unexpected error occurred: ${p instanceof Error?p.message:String(p)}`)}}function Ket(t){let e=Het.get(t);if(!e)return"Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.";let r=Date.now()-e.timestamp;return r>Wet?`Warning: describe_ui was last called ${Math.round(r/1e3)} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`:null}async function Zet(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${GE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);u.error&&A("warn",`${GE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`)}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var FF,Get,GE,Vet,Het,Wet,ece=O(()=>{"use strict";Te();De();hr();ze();Pf();Le();FF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),x:ce().int({message:"X coordinate for the long press"}),y:ce().int({message:"Y coordinate for the long press"}),duration:ce().positive({message:"Duration of the long press in milliseconds"})}),Get=st(FF.omit({simulatorId:!0}).shape),GE="[AXe]";Vet={name:"long_press",description:"Long press at specific coordinates for given duration (ms). Use describe_ui for precise coordinates (don't guess from screenshots).",schema:de({sessionAware:Get,legacy:FF}),annotations:{title:"Long Press",destructiveHint:!0},handler:fe({internalSchema:FF,logicFunction:(t,e)=>Xae(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})},Het=new Map,Wet=6e4});var rce={};Y(rce,{default:()=>Jet,swipeLogic:()=>tce});async function tce(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let n="swipe",{simulatorId:o,x1:i,y1:s,x2:a,y2:c,duration:u,delta:p,preDelay:f,postDelay:m}=t,h=["swipe","--start-x",String(i),"--start-y",String(s),"--end-x",String(a),"--end-y",String(c)];u!==void 0&&h.push("--duration",String(u)),p!==void 0&&h.push("--delta",String(p)),f!==void 0&&h.push("--pre-delay",String(f)),m!==void 0&&h.push("--post-delay",String(m));let _=u?` duration=${u}s`:"";A("info",`${VE}/${n}: Starting swipe (${i},${s})->(${a},${c})${_} on ${o}`);try{await ttt(h,o,"swipe",e,r),A("info",`${VE}/${n}: Success for ${o}`);let v=ett(o),E=`Swipe from (${i}, ${s}) to (${a}, ${c})${_} simulated successfully.`;return v?le(`${E} - -${v}`):le(E)}catch(v){return A("error",`${VE}/${n}: Failed - ${v}`),v instanceof sr?r.createAxeNotAvailableResponse():v instanceof gt?ge(`Failed to simulate swipe: ${v.message}`,v.axeOutput):v instanceof at?ge(`System error executing axe: ${v.message}`,v.originalError?.stack):ge(`An unexpected error occurred: ${v instanceof Error?v.message:String(v)}`)}}function ett(t){let e=Xet.get(t);if(!e)return"Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.";let r=Date.now()-e.timestamp;return r>Qet?`Warning: describe_ui was last called ${Math.round(r/1e3)} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`:null}async function ttt(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${VE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);u.error&&A("warn",`${VE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`)}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var qF,Yet,VE,Jet,Xet,Qet,nce=O(()=>{"use strict";Te();De();hr();yp();ze();Qu();Le();qF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),x1:ce().int({message:"Start X coordinate"}),y1:ce().int({message:"Start Y coordinate"}),x2:ce().int({message:"End X coordinate"}),y2:ce().int({message:"End Y coordinate"}),duration:ce().min(0,{message:"Duration must be non-negative"}).optional(),delta:ce().min(0,{message:"Delta must be non-negative"}).optional(),preDelay:ce().min(0,{message:"Pre-delay must be non-negative"}).optional(),postDelay:ce().min(0,{message:"Post-delay must be non-negative"}).optional()}),Yet=st(qF.omit({simulatorId:!0}).shape),VE="[AXe]";Jet={name:"swipe",description:"Swipe from one point to another. Use describe_ui for precise coordinates (don't guess from screenshots). Supports configurable timing.",schema:de({sessionAware:Yet,legacy:qF}),annotations:{title:"Swipe",destructiveHint:!0},handler:fe({internalSchema:qF,logicFunction:(t,e)=>tce(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})},Xet=new Map,Qet=6e4});var ice={};Y(ice,{default:()=>att,tapLogic:()=>oce});function stt(t){let e=ott.get(t);if(!e)return"Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.";let r=Date.now()-e.timestamp;return r>itt?`Warning: describe_ui was last called ${Math.round(r/1e3)} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`:null}async function oce(t,e,r={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let{simulatorId:o,x:i,y:s,id:a,label:c,preDelay:u,postDelay:p}=t,f="",m="",h=!1,_=["tap"];if(i!==void 0&&s!==void 0)h=!0,f=`(${i}, ${s})`,m=`Tap at ${f}`,_.push("-x",String(i),"-y",String(s));else if(a!==void 0)f=`element id "${a}"`,m=`Tap on ${f}`,_.push("--id",a);else if(c!==void 0)f=`element label "${c}"`,m=`Tap on ${f}`,_.push("--label",c);else return ge("Parameter validation failed",`Invalid parameters: -root: Missing tap target`);u!==void 0&&_.push("--pre-delay",String(u)),p!==void 0&&_.push("--post-delay",String(p)),A("info",`${HE}/tap: Starting for ${f} on ${o}`);try{await ctt(_,o,"tap",e,r),A("info",`${HE}/tap: Success for ${o}`);let v=h?stt(o):null,E=`${m} simulated successfully.`;return v?le(`${E} - -${v}`):le(E)}catch(v){let E=v instanceof Error?v.message:String(v);return A("error",`${HE}/tap: Failed - ${E}`),v instanceof sr?r.createAxeNotAvailableResponse():v instanceof gt?ge(`Failed to simulate ${m.toLowerCase()}: ${v.message}`,v.axeOutput):v instanceof at?ge(`System error executing axe: ${v.message}`,v.originalError?.stack):ge(`An unexpected error occurred: ${v instanceof Error?v.message:String(v)}`)}}async function ctt(t,e,r,n=z(),o={getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}){let i=o.getAxePath();if(!i)throw new sr("AXe binary not found");let s=[...t,"--udid",e],a=[i,...s];try{let c=i!=="axe"?o.getBundledAxeEnvironment():void 0,u=await n(a,`${HE}: ${r}`,!1,c);if(!u.success)throw new gt(`axe command '${r}' failed.`,r,u.error??u.output,e);u.error&&A("warn",`${HE}: Command '${r}' produced stderr output but exited successfully. Output: ${u.error}`)}catch(c){throw c instanceof Error?c instanceof gt?c:new at(`Failed to execute axe command: ${c.message}`,c):new at(`Failed to execute axe command: ${String(c)}`)}}var BF,rtt,ntt,HE,ott,itt,att,sce=O(()=>{"use strict";Te();De();hr();ze();Qu();yp();Le();BF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),x:ce().int({message:"X coordinate must be an integer"}).optional(),y:ce().int({message:"Y coordinate must be an integer"}).optional(),id:T().min(1,{message:"Id must be non-empty"}).optional(),label:T().min(1,{message:"Label must be non-empty"}).optional(),preDelay:ce().min(0,{message:"Pre-delay must be non-negative"}).optional(),postDelay:ce().min(0,{message:"Post-delay must be non-negative"}).optional()}),rtt=BF.superRefine((t,e)=>{let r=t.x!==void 0,n=t.y!==void 0,o=t.id!==void 0,i=t.label!==void 0;!r&&!n&&o&&i&&e.addIssue({code:gp.custom,path:["id"],message:"Provide either id or label, not both."}),r!==n&&(r||e.addIssue({code:gp.custom,path:["x"],message:"X coordinate is required when y is provided."}),n||e.addIssue({code:gp.custom,path:["y"],message:"Y coordinate is required when x is provided."})),!r&&!n&&!o&&!i&&e.addIssue({code:gp.custom,path:["x"],message:"Provide x/y coordinates or an element id/label."})}),ntt=st(BF.omit({simulatorId:!0}).shape),HE="[AXe]",ott=new Map,itt=6e4;att={name:"tap",description:"Tap at specific coordinates or target elements by accessibility id or label. Use describe_ui to get precise element coordinates prior to using x/y parameters (don't guess from screenshots). Supports optional timing delays.",schema:de({sessionAware:ntt,legacy:BF}),annotations:{title:"Tap",destructiveHint:!0},handler:fe({internalSchema:rtt,logicFunction:(t,e)=>oce(t,e,{getAxePath:Tt,getBundledAxeEnvironment:Nt,createAxeNotAvailableResponse:Ct}),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var cce={};Y(cce,{default:()=>ltt,touchLogic:()=>ace});async function ace(t,e,r){let n="touch",{simulatorId:o,x:i,y:s,down:a,up:c,delay:u}=t;if(!a&&!c)return ge('At least one of "down" or "up" must be true');let p=["touch","-x",String(i),"-y",String(s)];a&&p.push("--down"),c&&p.push("--up"),u!==void 0&&p.push("--delay",String(u));let f=a&&c?"touch down+up":a?"touch down":"touch up";A("info",`${WE}/${n}: Starting ${f} at (${i}, ${s}) on ${o}`);try{await mtt(p,o,"touch",e,r),A("info",`${WE}/${n}: Success for ${o}`);let m=ftt(o),h=`Touch event (${f}) at (${i}, ${s}) executed successfully.`;return m?le(`${h} - -${m}`):le(h)}catch(m){return A("error",`${WE}/${n}: Failed - ${m instanceof Error?m.message:String(m)}`),m instanceof sr?Ct():m instanceof gt?ge(`Failed to execute touch event: ${m.message}`,m.axeOutput):m instanceof at?ge(`System error executing axe: ${m.message}`,m.originalError?.stack):ge(`An unexpected error occurred: ${m instanceof Error?m.message:String(m)}`)}}function ftt(t){let e=ptt.get(t);if(!e)return"Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.";let r=Date.now()-e.timestamp;return r>dtt?`Warning: describe_ui was last called ${Math.round(r/1e3)} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`:null}async function mtt(t,e,r,n=z(),o){let i=o??{getAxePath:Tt,getBundledAxeEnvironment:Nt},s=i.getAxePath();if(!s)throw new sr("AXe binary not found");let a=[...t,"--udid",e],c=[s,...a];try{let u=s!=="axe"?i.getBundledAxeEnvironment():void 0,p=await n(c,`${WE}: ${r}`,!1,u);if(!p.success)throw new gt(`axe command '${r}' failed.`,r,p.error??p.output,e);p.error&&A("warn",`${WE}: Command '${r}' produced stderr output but exited successfully. Output: ${p.error}`)}catch(u){throw u instanceof Error?u instanceof gt?u:new at(`Failed to execute axe command: ${u.message}`,u):new at(`Failed to execute axe command: ${String(u)}`)}}var GF,utt,WE,ltt,ptt,dtt,uce=O(()=>{"use strict";Te();De();hr();yp();ze();Qu();Le();GF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),x:ce().int({message:"X coordinate must be an integer"}),y:ce().int({message:"Y coordinate must be an integer"}),down:ue().optional(),up:ue().optional(),delay:ce().min(0,{message:"Delay must be non-negative"}).optional()}),utt=st(GF.omit({simulatorId:!0}).shape),WE="[AXe]";ltt={name:"touch",description:"Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots).",schema:de({sessionAware:utt,legacy:GF}),annotations:{title:"Touch",destructiveHint:!0},handler:fe({internalSchema:GF,logicFunction:(t,e)=>ace(t,e),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})},ptt=new Map,dtt=6e4});var pce={};Y(pce,{default:()=>gtt,type_textLogic:()=>lce});async function lce(t,e,r){let n="type_text",{simulatorId:o,text:i}=t,s=["type",i];A("info",`${KE}/${n}: Starting type "${i.substring(0,20)}..." on ${o}`);try{return await _tt(s,o,"type",e,r),A("info",`${KE}/${n}: Success for ${o}`),le("Text typing simulated successfully.")}catch(a){return A("error",`${KE}/${n}: Failed - ${a instanceof Error?a.message:String(a)}`),a instanceof sr?Ct():a instanceof gt?ge(`Failed to simulate text typing: ${a.message}`,a.axeOutput):a instanceof at?ge(`System error executing axe: ${a.message}`,a.originalError?.stack):ge(`An unexpected error occurred: ${a instanceof Error?a.message:String(a)}`)}}async function _tt(t,e,r,n=z(),o){let i=o??{getAxePath:Tt,getBundledAxeEnvironment:Nt},s=i.getAxePath();if(!s)throw new sr("AXe binary not found");let a=[...t,"--udid",e],c=[s,...a];try{let u=s!=="axe"?i.getBundledAxeEnvironment():void 0,p=await n(c,`${KE}: ${r}`,!1,u);if(!p.success)throw new gt(`axe command '${r}' failed.`,r,p.error??p.output,e);p.error&&A("warn",`${KE}: Command '${r}' produced stderr output but exited successfully. Output: ${p.error}`)}catch(u){throw u instanceof Error?u instanceof gt?u:new at(`Failed to execute axe command: ${u.message}`,u):new at(`Failed to execute axe command: ${String(u)}`)}}var KE,VF,htt,gtt,dce=O(()=>{"use strict";Te();De();hr();yp();ze();Qu();Le();KE="[AXe]",VF=k({simulatorId:Dt({message:"Invalid Simulator UUID format"}),text:T().min(1,{message:"Text cannot be empty"})}),htt=st(VF.omit({simulatorId:!0}).shape);gtt={name:"type_text",description:"Type text (supports US keyboard characters). Use describe_ui to find text field, tap to focus, then type.",schema:de({sessionAware:htt,legacy:VF}),annotations:{title:"Type Text",destructiveHint:!0},handler:fe({internalSchema:VF,logicFunction:(t,e)=>lce(t,e),getExecutor:z,requirements:[{allOf:["simulatorId"],message:"simulatorId is required"}]})}});var fce={};Y(fce,{workflow:()=>vtt});var vtt,mce=O(()=>{"use strict";vtt={name:"Project Utilities",description:"Essential project maintenance utilities for cleaning and managing existing projects. Provides clean operations for both .xcodeproj and .xcworkspace files."}});var hce,gce=O(()=>{"use strict";hce={device:async()=>{let{workflow:t}=await Promise.resolve().then(()=>(kre(),$re)),e=await Promise.resolve().then(()=>(Bre(),qre)).then(v=>v.default),r=await Promise.resolve().then(()=>(Wre(),Hre)).then(v=>v.default),n=await Promise.resolve().then(()=>(ene(),Qre)).then(v=>v.default),o=await Promise.resolve().then(()=>(sne(),ine)).then(v=>v.default),i=await Promise.resolve().then(()=>(une(),cne)).then(v=>v.default),s=await Promise.resolve().then(()=>(dne(),pne)).then(v=>v.default),a=await Promise.resolve().then(()=>(_ne(),gne)).then(v=>v.default),c=await Promise.resolve().then(()=>(E2(),wre)).then(v=>v.default),u=await Promise.resolve().then(()=>(Ene(),yne)).then(v=>v.default),p=await Promise.resolve().then(()=>(Ane(),xne)).then(v=>v.default),f=await Promise.resolve().then(()=>(zne(),jne)).then(v=>v.default),m=await Promise.resolve().then(()=>(Bne(),qne)).then(v=>v.default),h=await Promise.resolve().then(()=>(Wne(),Hne)).then(v=>v.default),_=await Promise.resolve().then(()=>(Yne(),Zne)).then(v=>v.default);return{workflow:t,build_device:e,clean:r,discover_projs:n,get_app_bundle_id:o,get_device_app_path:i,install_app_device:s,launch_app_device:a,list_devices:c,list_schemes:u,show_build_settings:p,start_device_log_cap:f,stop_app_device:m,stop_device_log_cap:h,test_device:_}},doctor:async()=>{let{workflow:t}=await Promise.resolve().then(()=>(Xne(),Jne)),e=await Promise.resolve().then(()=>(HF(),_ce)).then(r=>r.default);return{workflow:t,doctor:e}},logging:async()=>{let{workflow:t}=await Promise.resolve().then(()=>(eoe(),Qne)),e=await Promise.resolve().then(()=>(VI(),Une)).then(i=>i.default),r=await Promise.resolve().then(()=>(ioe(),ooe)).then(i=>i.default),n=await Promise.resolve().then(()=>(F2(),Vne)).then(i=>i.default),o=await Promise.resolve().then(()=>(uoe(),coe)).then(i=>i.default);return{workflow:t,start_device_log_cap:e,start_sim_log_cap:r,stop_device_log_cap:n,stop_sim_log_cap:o}},macos:async()=>{let{workflow:t}=await Promise.resolve().then(()=>(poe(),loe)),e=await Promise.resolve().then(()=>(moe(),foe)).then(m=>m.default),r=await Promise.resolve().then(()=>(_oe(),goe)).then(m=>m.default),n=await Promise.resolve().then(()=>(Soe(),voe)).then(m=>m.default),o=await Promise.resolve().then(()=>(Eoe(),yoe)).then(m=>m.default),i=await Promise.resolve().then(()=>(xoe(),boe)).then(m=>m.default),s=await Promise.resolve().then(()=>(Ooe(),Ioe)).then(m=>m.default),a=await Promise.resolve().then(()=>(koe(),$oe)).then(m=>m.default),c=await Promise.resolve().then(()=>(Doe(),Moe)).then(m=>m.default),u=await Promise.resolve().then(()=>(Uoe(),Loe)).then(m=>m.default),p=await Promise.resolve().then(()=>(qoe(),Foe)).then(m=>m.default),f=await Promise.resolve().then(()=>(Voe(),Goe)).then(m=>m.default);return{workflow:t,build_macos:e,build_run_macos:r,clean:n,discover_projs:o,get_mac_app_path:i,get_mac_bundle_id:s,launch_mac_app:a,list_schemes:c,show_build_settings:u,stop_mac_app:p,test_macos:f}},"project-discovery":async()=>{let{workflow:t}=await Promise.resolve().then(()=>(Woe(),Hoe)),e=await Promise.resolve().then(()=>(IE(),Xre)).then(s=>s.default),r=await Promise.resolve().then(()=>(jI(),one)).then(s=>s.default),n=await Promise.resolve().then(()=>(X2(),Poe)).then(s=>s.default),o=await Promise.resolve().then(()=>(NE(),Sne)).then(s=>s.default),i=await Promise.resolve().then(()=>(CE(),bne)).then(s=>s.default);return{workflow:t,discover_projs:e,get_app_bundle_id:r,get_mac_bundle_id:n,list_schemes:o,show_build_settings:i}},"project-scaffolding":async()=>{let{workflow:t}=await Promise.resolve().then(()=>(Zoe(),Koe)),e=await Promise.resolve().then(()=>(nie(),rie)).then(n=>n.default),r=await Promise.resolve().then(()=>(cie(),aie)).then(n=>n.default);return{workflow:t,scaffold_ios_project:e,scaffold_macos_project:r}},"session-management":async()=>{let{workflow:t}=await Promise.resolve().then(()=>(lie(),uie)),e=await Promise.resolve().then(()=>(mie(),fie)).then(o=>o.default),r=await Promise.resolve().then(()=>(vie(),_ie)).then(o=>o.default),n=await Promise.resolve().then(()=>(yie(),Sie)).then(o=>o.default);return{workflow:t,session_clear_defaults:e,session_set_defaults:r,session_show_defaults:n}},simulator:async()=>{let{workflow:t}=await Promise.resolve().then(()=>(Tie(),Eie)),e=await Promise.resolve().then(()=>(sF(),xie)).then(N=>N.default),r=await Promise.resolve().then(()=>(Iie(),Pie)).then(N=>N.default),n=await Promise.resolve().then(()=>(Cie(),Nie)).then(N=>N.default),o=await Promise.resolve().then(()=>(kie(),$ie)).then(N=>N.default),i=await Promise.resolve().then(()=>(zie(),jie)).then(N=>N.default),s=await Promise.resolve().then(()=>(qie(),Fie)).then(N=>N.default),a=await Promise.resolve().then(()=>(Gie(),Bie)).then(N=>N.default),c=await Promise.resolve().then(()=>(Wie(),Hie)).then(N=>N.default),u=await Promise.resolve().then(()=>(Yie(),Zie)).then(N=>N.default),p=await Promise.resolve().then(()=>(Qie(),Xie)).then(N=>N.default),f=await Promise.resolve().then(()=>(rse(),tse)).then(N=>N.default),m=await Promise.resolve().then(()=>(ose(),nse)).then(N=>N.default),h=await Promise.resolve().then(()=>(ZI(),sse)).then(N=>N.default),_=await Promise.resolve().then(()=>(SF(),use)).then(N=>N.default),v=await Promise.resolve().then(()=>(gse(),hse)).then(N=>N.default),E=await Promise.resolve().then(()=>(Ese(),yse)).then(N=>N.default),x=await Promise.resolve().then(()=>(bse(),Tse)).then(N=>N.default),w=await Promise.resolve().then(()=>(wse(),Ase)).then(N=>N.default),I=await Promise.resolve().then(()=>(kse(),$se)).then(N=>N.default);return{workflow:t,boot_sim:e,build_run_sim:r,build_sim:n,clean:o,describe_ui:i,discover_projs:s,get_app_bundle_id:a,get_sim_app_path:c,install_app_sim:u,launch_app_logs_sim:p,launch_app_sim:f,list_schemes:m,list_sims:h,open_sim:_,record_sim_video:v,screenshot:E,show_build_settings:x,stop_app_sim:w,test_sim:I}},"simulator-management":async()=>{let{workflow:t}=await Promise.resolve().then(()=>(Dse(),Mse)),e=await Promise.resolve().then(()=>(Use(),Lse)).then(u=>u.default),r=await Promise.resolve().then(()=>(Fse(),zse)).then(u=>u.default),n=await Promise.resolve().then(()=>(Bse(),qse)).then(u=>u.default),o=await Promise.resolve().then(()=>(Vse(),Gse)).then(u=>u.default),i=await Promise.resolve().then(()=>(Kse(),Wse)).then(u=>u.default),s=await Promise.resolve().then(()=>(Jse(),Yse)).then(u=>u.default),a=await Promise.resolve().then(()=>(eae(),Qse)).then(u=>u.default),c=await Promise.resolve().then(()=>(nae(),rae)).then(u=>u.default);return{workflow:t,boot_sim:e,erase_sims:r,list_sims:n,open_sim:o,reset_sim_location:i,set_sim_appearance:s,set_sim_location:a,sim_statusbar:c}},"swift-package":async()=>{let{workflow:t}=await Promise.resolve().then(()=>(iae(),oae)),e=await Promise.resolve().then(()=>(lae(),uae)).then(a=>a.default),r=await Promise.resolve().then(()=>(hae(),mae)).then(a=>a.default),n=await Promise.resolve().then(()=>(Sae(),vae)).then(a=>a.default),o=await Promise.resolve().then(()=>(Rae(),wae)).then(a=>a.default),i=await Promise.resolve().then(()=>(Cae(),Nae)).then(a=>a.default),s=await Promise.resolve().then(()=>(Lae(),Dae)).then(a=>a.default);return{workflow:t,swift_package_build:e,swift_package_clean:r,swift_package_list:n,swift_package_run:o,swift_package_stop:i,swift_package_test:s}},"ui-testing":async()=>{let{workflow:t}=await Promise.resolve().then(()=>(jae(),Uae)),e=await Promise.resolve().then(()=>(qae(),Fae)).then(m=>m.default),r=await Promise.resolve().then(()=>(dF(),Uie)).then(m=>m.default),n=await Promise.resolve().then(()=>(Vae(),Gae)).then(m=>m.default),o=await Promise.resolve().then(()=>(Kae(),Wae)).then(m=>m.default),i=await Promise.resolve().then(()=>(Jae(),Yae)).then(m=>m.default),s=await Promise.resolve().then(()=>(ece(),Qae)).then(m=>m.default),a=await Promise.resolve().then(()=>(AF(),Sse)).then(m=>m.default),c=await Promise.resolve().then(()=>(nce(),rce)).then(m=>m.default),u=await Promise.resolve().then(()=>(sce(),ice)).then(m=>m.default),p=await Promise.resolve().then(()=>(uce(),cce)).then(m=>m.default),f=await Promise.resolve().then(()=>(dce(),pce)).then(m=>m.default);return{workflow:t,button:e,describe_ui:r,gesture:n,key_press:o,key_sequence:i,long_press:s,screenshot:a,swipe:c,tap:u,touch:p,type_text:f}},utilities:async()=>{let{workflow:t}=await Promise.resolve().then(()=>(mce(),fce)),e=await Promise.resolve().then(()=>(PE(),Vre)).then(r=>r.default);return{workflow:t,clean:e}}}});async function sg(){let t=new Map;for(let[e,r]of Object.entries(hce))try{let n=await r();if(!n.workflow)throw new Error(`Workflow metadata missing in ${e}/index.js`);let o=n.workflow;if(!o.name||typeof o.name!="string")throw new Error(`Invalid workflow.name in ${e}/index.js: must be a non-empty string`);if(!o.description||typeof o.description!="string")throw new Error(`Invalid workflow.description in ${e}/index.js: must be a non-empty string`);t.set(e,{workflow:o,tools:await Stt(n),directoryName:e})}catch(n){throw new Error(`Failed to load workflow '${e}': ${n instanceof Error?n.message:"Unknown error"}`)}return t}async function Stt(t){let e=[];for(let[r,n]of Object.entries(t))if(r!=="workflow"&&n&&typeof n=="object"){let o=n;o.name&&typeof o.handler=="function"&&e.push(o)}return e}var WF=O(()=>{"use strict";gce()});var vce=O(()=>{"use strict";WF()});function yce(t){let e=[...new Set(t.enabledWorkflows)],r=[...new Set(t.enabledTools)];Sce={mode:"runtime",enabledWorkflows:e,enabledTools:r,totalRegistered:r.length}}function Ece(){return Sce}var Sce,KF=O(()=>{"use strict";Sce=null});function Ett(t){return t.map(e=>e.trim().toLowerCase()).filter(Boolean)}function Ttt(t){return!!t}function btt(){let t=process.env.XCODEBUILDMCP_DEBUG??"";return t.toLowerCase()==="true"||t==="1"}function YI(t,e=[]){let r=Ett(e),n=btt()?[Tce,ytt]:[Tce],o=r.length>0?[...new Set([...n,...r])]:null;return{selectedWorkflows:o?o.map(s=>t.get(s)).filter(Ttt):[...t.values()],selectedNames:o}}function bce(t){let e=new Set;for(let r of t)for(let n of r.tools)n?.name&&e.add(n.name);return[...e]}var Tce,ytt,ZF=O(()=>{"use strict";Tce="session-management",ytt="doctor"});var xce=O(()=>{"use strict";b2()});function Ace(t){return{binaryChecker:{async checkBinaryAvailability(a){if(a==="axe"&&og())return{available:!0,version:"Bundled"};try{if(!(await t(["which",a],"Check Binary Availability")).success)return{available:!1}}catch{return{available:!1}}let c,u={axe:"axe --version",mise:"mise --version"};if(a in u)try{let p=await t(u[a].split(" "),"Get Binary Version");p.success&&p.output&&(c=p.output.trim())}catch{}return{available:!0,version:c??"Available (version info not available)"}}},xcode:{async getXcodeInfo(){try{let a=await t(["xcodebuild","-version"],"Get Xcode Version");if(!a.success)throw new Error("xcodebuild command failed");let c=a.output.trim().split(` -`).slice(0,2).join(" - "),u=await t(["xcode-select","-p"],"Get Xcode Path");if(!u.success)throw new Error("xcode-select command failed");let p=u.output.trim(),f=await t(["xcrun","--find","xcodebuild"],"Find Xcodebuild");if(!f.success)throw new Error("xcrun --find command failed");let m=f.output.trim(),h=await t(["xcrun","--version"],"Get Xcrun Version");if(!h.success)throw new Error("xcrun --version command failed");let _=h.output.trim();return{version:c,path:p,selectedXcode:m,xcrunVersion:_}}catch(a){return{error:a instanceof Error?a.message:String(a)}}}},env:{getEnvironmentVariables(){let a=["INCREMENTAL_BUILDS_ENABLED","PATH","DEVELOPER_DIR","HOME","USER","TMPDIR","NODE_ENV","SENTRY_DISABLED"],c={};for(let u of a)c[u]=process.env[u];return Object.keys(process.env).forEach(u=>{u.startsWith("XCODEBUILDMCP_")&&(c[u]=process.env[u])}),c},getSystemInfo(){return{platform:Wn.platform(),release:Wn.release(),arch:Wn.arch(),cpus:`${Wn.cpus().length} x ${Wn.cpus()[0]?.model??"Unknown"}`,memory:`${Math.round(Wn.totalmem()/(1024*1024*1024))} GB`,hostname:Wn.hostname(),username:Wn.userInfo().username,homedir:Wn.homedir(),tmpdir:Wn.tmpdir()}},getNodeInfo(){return{version:process.version,execPath:process.execPath,pid:process.pid.toString(),ppid:process.ppid.toString(),platform:process.platform,arch:process.arch,cwd:process.cwd(),argv:process.argv.join(" ")}}},plugins:{async getPluginSystemInfo(){try{let a=await sg(),c={},u=0;for(let[p,f]of a.entries()){let m=f.tools.map(h=>h.name).filter(Boolean);u+=m.length,c[p]=m}return{totalPlugins:u,pluginDirectories:a.size,pluginsByDirectory:c,systemMode:"plugin-based"}}catch(a){return{error:`Failed to load plugins: ${a instanceof Error?a.message:"Unknown error"}`,systemMode:"error"}}}},runtime:{async getRuntimeToolInfo(){let a=Ece();if(a)return a;let c=await sg(),p=(process.env.XCODEBUILDMCP_ENABLED_WORKFLOWS??"").split(",").map(_=>_.trim()).filter(Boolean),f=YI(c,p),m=f.selectedWorkflows.map(_=>_.directoryName),h=bce(f.selectedWorkflows);return{mode:"static",enabledWorkflows:m,enabledTools:h,totalRegistered:h.length,note:"Runtime registry unavailable; showing expected tools from selection rules."}}},features:{areAxeToolsAvailable:og,isXcodemakeEnabled:ng,isXcodemakeAvailable:wE,doesMakefileExist:RE}}}var Wn,wce=O(()=>{"use strict";Wn=W(require("os"),1);vce();KF();ZF();Pf();xce()});var _ce={};Y(_ce,{default:()=>wtt,doctorLogic:()=>JI,runDoctor:()=>Pce});async function Pce(t,e,r=!1){let n=process.env.XCODEBUILDMCP_SILENCE_LOGS;process.env.XCODEBUILDMCP_SILENCE_LOGS="true",A("info",`${xtt}: Running doctor tool`);let o=["axe","xcodemake","mise"],i={};for(let Q of o)i[Q]=await e.binaryChecker.checkBinaryAvailability(Q);let s=await e.xcode.getXcodeInfo(),a=e.env.getEnvironmentVariables(),c=e.env.getSystemInfo(),u=e.env.getNodeInfo(),p=e.features.areAxeToolsAvailable(),f=await e.plugins.getPluginSystemInfo(),m=await e.runtime.getRuntimeToolInfo(),h=e.features.isXcodemakeEnabled(),_=await e.features.isXcodemakeAvailable(),v=e.features.doesMakefileExist("./"),E={serverVersion:Ep,timestamp:new Date().toISOString(),system:c,node:u,xcode:s,dependencies:i,environmentVariables:a,features:{axe:{available:p,uiAutomationSupported:p},xcodemake:{enabled:h,available:_,makefileExists:v},mise:{running_under_mise:!!process.env.XCODEBUILDMCP_RUNNING_UNDER_MISE,available:i.mise.available}},pluginSystem:f},x=` -\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 -\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 - \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D - \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u255D -\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 -\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D - -\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 -\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 -\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D -\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 -\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 -\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D -`,w="\x1B[0m",I="\x1B[38;5;209m",N="\x1B[38;5;217m";function $(Q){let me=Q.split(` -`),J=[],Oe=new Set(["\u2554","\u2557","\u255D","\u255A","\u2550","\u2551","\u2566","\u2569","\u2560","\u2563","\u256C","\u250C","\u2510","\u2514","\u2518","\u2502","\u2500"]);for(let pt of me){let Ke="";for(let it of pt)it==="\u2588"?Ke+=`${I}${it}${w}`:Oe.has(it)?Ke+=`${N}${it}${w}`:Ke+=it;J.push(Ke+w)}return J.join(` -`)}let B=[];r&&B.push($(x)),B.push("XcodeBuildMCP Doctor",` -Generated: ${E.timestamp}`,`Server Version: ${E.serverVersion}`);let he={content:[{type:"text",text:[...B,` -## System Information`,...Object.entries(E.system).map(([Q,me])=>`- ${Q}: ${me}`),` -## Node.js Information`,...Object.entries(E.node).map(([Q,me])=>`- ${Q}: ${me}`),` -## Xcode Information`,..."error"in E.xcode?[`- Error: ${E.xcode.error}`]:Object.entries(E.xcode).map(([Q,me])=>`- ${Q}: ${me}`),` -## Dependencies`,...Object.entries(E.dependencies).map(([Q,me])=>`- ${Q}: ${me.available?`\u2705 ${me.version??"Available"}`:"\u274C Not found"}`),` -## Environment Variables`,...Object.entries(E.environmentVariables).filter(([Q])=>Q!=="PATH"&&Q!=="PYTHONPATH").map(([Q,me])=>`- ${Q}: ${me??"(not set)"}`),` -### PATH`,"```",`${E.environmentVariables.PATH??"(not set)"}`.split(":").join(` -`),"```",` -## Feature Status`,` -### UI Automation (axe)`,`- Available: ${E.features.axe.available?"\u2705 Yes":"\u274C No"}`,`- UI Automation Supported: ${E.features.axe.uiAutomationSupported?"\u2705 Yes":"\u274C No"}`,` -### Incremental Builds`,`- Enabled: ${E.features.xcodemake.enabled?"\u2705 Yes":"\u274C No"}`,`- Available: ${E.features.xcodemake.available?"\u2705 Yes":"\u274C No"}`,`- Makefile exists: ${E.features.xcodemake.makefileExists?"\u2705 Yes":"\u274C No"}`,` -### Mise Integration`,`- Running under mise: ${E.features.mise.running_under_mise?"\u2705 Yes":"\u274C No"}`,`- Mise available: ${E.features.mise.available?"\u2705 Yes":"\u274C No"}`,` -### Available Tools`,`- Total Plugins: ${"totalPlugins"in E.pluginSystem?E.pluginSystem.totalPlugins:0}`,`- Plugin Directories: ${"pluginDirectories"in E.pluginSystem?E.pluginSystem.pluginDirectories:0}`,..."pluginsByDirectory"in E.pluginSystem&&E.pluginSystem.pluginDirectories>0?Object.entries(E.pluginSystem.pluginsByDirectory).map(([Q,me])=>`- ${Q}: ${Array.isArray(me)?me.length:0} tools`):["- Plugin directory grouping unavailable in this build"],` -### Runtime Tool Registration`,`- Mode: ${m.mode}`,`- Enabled Workflows: ${m.enabledWorkflows.length}`,`- Registered Tools: ${m.totalRegistered}`,...m.mode==="static"?[`- Note: ${m.note}`]:[],...m.enabledWorkflows.length>0?[`- Workflows: ${m.enabledWorkflows.join(", ")}`]:[],` -## Tool Availability Summary`,`- Build Tools: ${"error"in E.xcode?"\u274C Not available":"\u2705 Available"}`,`- UI Automation Tools: ${E.features.axe.uiAutomationSupported?"\u2705 Available":"\u274C Not available"}`,`- Incremental Build Support: ${E.features.xcodemake.available&&E.features.xcodemake.enabled?"\u2705 Available & Enabled":E.features.xcodemake.available?"\u2705 Available but Disabled":"\u274C Not available"}`,` -## Sentry`,`- Sentry enabled: ${E.environmentVariables.SENTRY_DISABLED!=="true"?"\u2705 Yes":"\u274C No"}`,` -## Troubleshooting Tips`,"- If UI automation tools are not available, install axe: `brew tap cameroncooke/axe && brew install axe`","- If incremental build support is not available, you can download the tool from https://github.com/cameroncooke/xcodemake. Make sure it's executable and available in your PATH","- To enable xcodemake, set environment variable: `export INCREMENTAL_BUILDS_ENABLED=1`","- For mise integration, follow instructions in the README.md file"].join(` -`)}]};return n===void 0?delete process.env.XCODEBUILDMCP_SILENCE_LOGS:process.env.XCODEBUILDMCP_SILENCE_LOGS=n,he}async function JI(t,e,r=!1){let n=Ace(e);return Pce(t,n,r)}async function Att(t,e){return JI(t,e,!1)}var xtt,Rce,wtt,HF=O(()=>{"use strict";Te();De();ze();Cre();Le();wce();xtt="[Doctor]",Rce=k({enabled:ue().optional().describe("Optional: dummy parameter to satisfy MCP protocol")});wtt={name:"doctor",description:"Provides comprehensive information about the MCP server environment, available dependencies, and configuration status.",schema:Rce.shape,annotations:{title:"Doctor",readOnlyHint:!0},handler:Ht(Rce,Att,z)}});var Oce={};Y(Oce,{default:()=>Rtt,doctorResourceLogic:()=>Ice});async function Ice(t=z()){try{A("info","Processing doctor resource request");let e=await JI({},t);if(e.isError){let o=e.content.find(s=>s.type==="text")?.text,i=typeof o=="string"?o:"Failed to retrieve doctor data";return A("error",`Error in doctor resource handler: ${i}`),{contents:[{text:`Error retrieving doctor data: ${i}`}]}}return{contents:[{text:e.content.find(n=>n.type==="text")?.text??"No doctor data available"}]}}catch(e){let r=e instanceof Error?e.message:String(e);return A("error",`Error in doctor resource handler: ${r}`),{contents:[{text:`Error retrieving doctor data: ${r}`}]}}}var Rtt,Nce=O(()=>{"use strict";De();ze();HF();Rtt={uri:"xcodebuildmcp://doctor",name:"doctor",description:"Comprehensive development environment diagnostic information and configuration status",mimeType:"text/plain",async handler(){return Ice()}}});var $ce={};Y($ce,{default:()=>Ptt,simulatorsResourceLogic:()=>Cce});async function Cce(t=z()){try{A("info","Processing simulators resource request");let e=await KI({},t);if(e.isError){let r=e.content[0]?.text;throw new Error(typeof r=="string"?r:"Failed to retrieve simulator data")}return{contents:[{text:typeof e.content[0]?.text=="string"?e.content[0].text:"No simulator data available"}]}}catch(e){let r=e instanceof Error?e.message:String(e);return A("error",`Error in simulators resource handler: ${r}`),{contents:[{text:`Error retrieving simulator data: ${r}`}]}}}var Ptt,kce=O(()=>{"use strict";De();ze();ZI();Ptt={uri:"xcodebuildmcp://simulators",name:"simulators",description:"Available iOS simulators with their UUIDs and states",mimeType:"text/plain",async handler(){return Cce()}}});var lT=S(ar=>{"use strict";Object.defineProperty(ar,"__esModule",{value:!0});ar.regexpCode=ar.getEsmExportName=ar.getProperty=ar.safeStringify=ar.stringify=ar.strConcat=ar.addCodeArg=ar.str=ar._=ar.nil=ar._Code=ar.Name=ar.IDENTIFIER=ar._CodeOrName=void 0;var cT=class{};ar._CodeOrName=cT;ar.IDENTIFIER=/^[a-z$_][a-z$_0-9]*$/i;var Cf=class extends cT{constructor(e){if(super(),!ar.IDENTIFIER.test(e))throw new Error("CodeGen: name must be a valid identifier");this.str=e}toString(){return this.str}emptyStr(){return!1}get names(){return{[this.str]:1}}};ar.Name=Cf;var ea=class extends cT{constructor(e){super(),this._items=typeof e=="string"?[e]:e}toString(){return this.str}emptyStr(){if(this._items.length>1)return!1;let e=this._items[0];return e===""||e==='""'}get str(){var e;return(e=this._str)!==null&&e!==void 0?e:this._str=this._items.reduce((r,n)=>`${r}${n}`,"")}get names(){var e;return(e=this._names)!==null&&e!==void 0?e:this._names=this._items.reduce((r,n)=>(n instanceof Cf&&(r[n.str]=(r[n.str]||0)+1),r),{})}};ar._Code=ea;ar.nil=new ea("");function Iue(t,...e){let r=[t[0]],n=0;for(;n{"use strict";Object.defineProperty(Bi,"__esModule",{value:!0});Bi.ValueScope=Bi.ValueScopeName=Bi.Scope=Bi.varKinds=Bi.UsedValueState=void 0;var qi=lT(),gq=class extends Error{constructor(e){super(`CodeGen: "code" for ${e} not defined`),this.value=e.value}},uO;(function(t){t[t.Started=0]="Started",t[t.Completed=1]="Completed"})(uO||(Bi.UsedValueState=uO={}));Bi.varKinds={const:new qi.Name("const"),let:new qi.Name("let"),var:new qi.Name("var")};var lO=class{constructor({prefixes:e,parent:r}={}){this._names={},this._prefixes=e,this._parent=r}toName(e){return e instanceof qi.Name?e:this.name(e)}name(e){return new qi.Name(this._newName(e))}_newName(e){let r=this._names[e]||this._nameGroup(e);return`${e}${r.index++}`}_nameGroup(e){var r,n;if(!((n=(r=this._parent)===null||r===void 0?void 0:r._prefixes)===null||n===void 0)&&n.has(e)||this._prefixes&&!this._prefixes.has(e))throw new Error(`CodeGen: prefix "${e}" is not allowed in this scope`);return this._names[e]={prefix:e,index:0}}};Bi.Scope=lO;var pO=class extends qi.Name{constructor(e,r){super(r),this.prefix=e}setValue(e,{property:r,itemIndex:n}){this.value=e,this.scopePath=(0,qi._)`.${new qi.Name(r)}[${n}]`}};Bi.ValueScopeName=pO;var Prt=(0,qi._)`\n`,_q=class extends lO{constructor(e){super(e),this._values={},this._scope=e.scope,this.opts={...e,_n:e.lines?Prt:qi.nil}}get(){return this._scope}name(e){return new pO(e,this._newName(e))}value(e,r){var n;if(r.ref===void 0)throw new Error("CodeGen: ref must be passed in value");let o=this.toName(e),{prefix:i}=o,s=(n=r.key)!==null&&n!==void 0?n:r.ref,a=this._values[i];if(a){let p=a.get(s);if(p)return p}else a=this._values[i]=new Map;a.set(s,o);let c=this._scope[i]||(this._scope[i]=[]),u=c.length;return c[u]=r.ref,o.setValue(r,{property:i,itemIndex:u}),o}getValue(e,r){let n=this._values[e];if(n)return n.get(r)}scopeRefs(e,r=this._values){return this._reduceValues(r,n=>{if(n.scopePath===void 0)throw new Error(`CodeGen: name "${n}" has no value`);return(0,qi._)`${e}${n.scopePath}`})}scopeCode(e=this._values,r,n){return this._reduceValues(e,o=>{if(o.value===void 0)throw new Error(`CodeGen: name "${o}" has no value`);return o.value.code},r,n)}_reduceValues(e,r,n={},o){let i=qi.nil;for(let s in e){let a=e[s];if(!a)continue;let c=n[s]=n[s]||new Map;a.forEach(u=>{if(c.has(u))return;c.set(u,uO.Started);let p=r(u);if(p){let f=this.opts.es5?Bi.varKinds.var:Bi.varKinds.const;i=(0,qi._)`${i}${f} ${u} = ${p};${this.opts._n}`}else if(p=o?.(u))i=(0,qi._)`${i}${p}${this.opts._n}`;else throw new gq(u);c.set(u,uO.Completed)})}return i}};Bi.ValueScope=_q});var Ut=S(kt=>{"use strict";Object.defineProperty(kt,"__esModule",{value:!0});kt.or=kt.and=kt.not=kt.CodeGen=kt.operators=kt.varKinds=kt.ValueScopeName=kt.ValueScope=kt.Scope=kt.Name=kt.regexpCode=kt.stringify=kt.getProperty=kt.nil=kt.strConcat=kt.str=kt._=void 0;var Xt=lT(),rc=vq(),Ip=lT();Object.defineProperty(kt,"_",{enumerable:!0,get:function(){return Ip._}});Object.defineProperty(kt,"str",{enumerable:!0,get:function(){return Ip.str}});Object.defineProperty(kt,"strConcat",{enumerable:!0,get:function(){return Ip.strConcat}});Object.defineProperty(kt,"nil",{enumerable:!0,get:function(){return Ip.nil}});Object.defineProperty(kt,"getProperty",{enumerable:!0,get:function(){return Ip.getProperty}});Object.defineProperty(kt,"stringify",{enumerable:!0,get:function(){return Ip.stringify}});Object.defineProperty(kt,"regexpCode",{enumerable:!0,get:function(){return Ip.regexpCode}});Object.defineProperty(kt,"Name",{enumerable:!0,get:function(){return Ip.Name}});var hO=vq();Object.defineProperty(kt,"Scope",{enumerable:!0,get:function(){return hO.Scope}});Object.defineProperty(kt,"ValueScope",{enumerable:!0,get:function(){return hO.ValueScope}});Object.defineProperty(kt,"ValueScopeName",{enumerable:!0,get:function(){return hO.ValueScopeName}});Object.defineProperty(kt,"varKinds",{enumerable:!0,get:function(){return hO.varKinds}});kt.operators={GT:new Xt._Code(">"),GTE:new Xt._Code(">="),LT:new Xt._Code("<"),LTE:new Xt._Code("<="),EQ:new Xt._Code("==="),NEQ:new Xt._Code("!=="),NOT:new Xt._Code("!"),OR:new Xt._Code("||"),AND:new Xt._Code("&&"),ADD:new Xt._Code("+")};var ol=class{optimizeNodes(){return this}optimizeNames(e,r){return this}},Sq=class extends ol{constructor(e,r,n){super(),this.varKind=e,this.name=r,this.rhs=n}render({es5:e,_n:r}){let n=e?rc.varKinds.var:this.varKind,o=this.rhs===void 0?"":` = ${this.rhs}`;return`${n} ${this.name}${o};`+r}optimizeNames(e,r){if(e[this.name.str])return this.rhs&&(this.rhs=bg(this.rhs,e,r)),this}get names(){return this.rhs instanceof Xt._CodeOrName?this.rhs.names:{}}},dO=class extends ol{constructor(e,r,n){super(),this.lhs=e,this.rhs=r,this.sideEffects=n}render({_n:e}){return`${this.lhs} = ${this.rhs};`+e}optimizeNames(e,r){if(!(this.lhs instanceof Xt.Name&&!e[this.lhs.str]&&!this.sideEffects))return this.rhs=bg(this.rhs,e,r),this}get names(){let e=this.lhs instanceof Xt.Name?{}:{...this.lhs.names};return mO(e,this.rhs)}},yq=class extends dO{constructor(e,r,n,o){super(e,n,o),this.op=r}render({_n:e}){return`${this.lhs} ${this.op}= ${this.rhs};`+e}},Eq=class extends ol{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`${this.label}:`+e}},Tq=class extends ol{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`break${this.label?` ${this.label}`:""};`+e}},bq=class extends ol{constructor(e){super(),this.error=e}render({_n:e}){return`throw ${this.error};`+e}get names(){return this.error.names}},xq=class extends ol{constructor(e){super(),this.code=e}render({_n:e}){return`${this.code};`+e}optimizeNodes(){return`${this.code}`?this:void 0}optimizeNames(e,r){return this.code=bg(this.code,e,r),this}get names(){return this.code instanceof Xt._CodeOrName?this.code.names:{}}},pT=class extends ol{constructor(e=[]){super(),this.nodes=e}render(e){return this.nodes.reduce((r,n)=>r+n.render(e),"")}optimizeNodes(){let{nodes:e}=this,r=e.length;for(;r--;){let n=e[r].optimizeNodes();Array.isArray(n)?e.splice(r,1,...n):n?e[r]=n:e.splice(r,1)}return e.length>0?this:void 0}optimizeNames(e,r){let{nodes:n}=this,o=n.length;for(;o--;){let i=n[o];i.optimizeNames(e,r)||(Irt(e,i.names),n.splice(o,1))}return n.length>0?this:void 0}get names(){return this.nodes.reduce((e,r)=>Mf(e,r.names),{})}},il=class extends pT{render(e){return"{"+e._n+super.render(e)+"}"+e._n}},Aq=class extends pT{},Tg=class extends il{};Tg.kind="else";var $f=class t extends il{constructor(e,r){super(r),this.condition=e}render(e){let r=`if(${this.condition})`+super.render(e);return this.else&&(r+="else "+this.else.render(e)),r}optimizeNodes(){super.optimizeNodes();let e=this.condition;if(e===!0)return this.nodes;let r=this.else;if(r){let n=r.optimizeNodes();r=this.else=Array.isArray(n)?new Tg(n):n}if(r)return e===!1?r instanceof t?r:r.nodes:this.nodes.length?this:new t(Nue(e),r instanceof t?[r]:r.nodes);if(!(e===!1||!this.nodes.length))return this}optimizeNames(e,r){var n;if(this.else=(n=this.else)===null||n===void 0?void 0:n.optimizeNames(e,r),!!(super.optimizeNames(e,r)||this.else))return this.condition=bg(this.condition,e,r),this}get names(){let e=super.names;return mO(e,this.condition),this.else&&Mf(e,this.else.names),e}};$f.kind="if";var kf=class extends il{};kf.kind="for";var wq=class extends kf{constructor(e){super(),this.iteration=e}render(e){return`for(${this.iteration})`+super.render(e)}optimizeNames(e,r){if(super.optimizeNames(e,r))return this.iteration=bg(this.iteration,e,r),this}get names(){return Mf(super.names,this.iteration.names)}},Rq=class extends kf{constructor(e,r,n,o){super(),this.varKind=e,this.name=r,this.from=n,this.to=o}render(e){let r=e.es5?rc.varKinds.var:this.varKind,{name:n,from:o,to:i}=this;return`for(${r} ${n}=${o}; ${n}<${i}; ${n}++)`+super.render(e)}get names(){let e=mO(super.names,this.from);return mO(e,this.to)}},fO=class extends kf{constructor(e,r,n,o){super(),this.loop=e,this.varKind=r,this.name=n,this.iterable=o}render(e){return`for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})`+super.render(e)}optimizeNames(e,r){if(super.optimizeNames(e,r))return this.iterable=bg(this.iterable,e,r),this}get names(){return Mf(super.names,this.iterable.names)}},dT=class extends il{constructor(e,r,n){super(),this.name=e,this.args=r,this.async=n}render(e){return`${this.async?"async ":""}function ${this.name}(${this.args})`+super.render(e)}};dT.kind="func";var fT=class extends pT{render(e){return"return "+super.render(e)}};fT.kind="return";var Pq=class extends il{render(e){let r="try"+super.render(e);return this.catch&&(r+=this.catch.render(e)),this.finally&&(r+=this.finally.render(e)),r}optimizeNodes(){var e,r;return super.optimizeNodes(),(e=this.catch)===null||e===void 0||e.optimizeNodes(),(r=this.finally)===null||r===void 0||r.optimizeNodes(),this}optimizeNames(e,r){var n,o;return super.optimizeNames(e,r),(n=this.catch)===null||n===void 0||n.optimizeNames(e,r),(o=this.finally)===null||o===void 0||o.optimizeNames(e,r),this}get names(){let e=super.names;return this.catch&&Mf(e,this.catch.names),this.finally&&Mf(e,this.finally.names),e}},mT=class extends il{constructor(e){super(),this.error=e}render(e){return`catch(${this.error})`+super.render(e)}};mT.kind="catch";var hT=class extends il{render(e){return"finally"+super.render(e)}};hT.kind="finally";var Iq=class{constructor(e,r={}){this._values={},this._blockStarts=[],this._constants={},this.opts={...r,_n:r.lines?` -`:""},this._extScope=e,this._scope=new rc.Scope({parent:e}),this._nodes=[new Aq]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){return this._extScope.name(e)}scopeValue(e,r){let n=this._extScope.value(e,r);return(this._values[n.prefix]||(this._values[n.prefix]=new Set)).add(n),n}getScopeValue(e,r){return this._extScope.getValue(e,r)}scopeRefs(e){return this._extScope.scopeRefs(e,this._values)}scopeCode(){return this._extScope.scopeCode(this._values)}_def(e,r,n,o){let i=this._scope.toName(r);return n!==void 0&&o&&(this._constants[i.str]=n),this._leafNode(new Sq(e,i,n)),i}const(e,r,n){return this._def(rc.varKinds.const,e,r,n)}let(e,r,n){return this._def(rc.varKinds.let,e,r,n)}var(e,r,n){return this._def(rc.varKinds.var,e,r,n)}assign(e,r,n){return this._leafNode(new dO(e,r,n))}add(e,r){return this._leafNode(new yq(e,kt.operators.ADD,r))}code(e){return typeof e=="function"?e():e!==Xt.nil&&this._leafNode(new xq(e)),this}object(...e){let r=["{"];for(let[n,o]of e)r.length>1&&r.push(","),r.push(n),(n!==o||this.opts.es5)&&(r.push(":"),(0,Xt.addCodeArg)(r,o));return r.push("}"),new Xt._Code(r)}if(e,r,n){if(this._blockNode(new $f(e)),r&&n)this.code(r).else().code(n).endIf();else if(r)this.code(r).endIf();else if(n)throw new Error('CodeGen: "else" body without "then" body');return this}elseIf(e){return this._elseNode(new $f(e))}else(){return this._elseNode(new Tg)}endIf(){return this._endBlockNode($f,Tg)}_for(e,r){return this._blockNode(e),r&&this.code(r).endFor(),this}for(e,r){return this._for(new wq(e),r)}forRange(e,r,n,o,i=this.opts.es5?rc.varKinds.var:rc.varKinds.let){let s=this._scope.toName(e);return this._for(new Rq(i,s,r,n),()=>o(s))}forOf(e,r,n,o=rc.varKinds.const){let i=this._scope.toName(e);if(this.opts.es5){let s=r instanceof Xt.Name?r:this.var("_arr",r);return this.forRange("_i",0,(0,Xt._)`${s}.length`,a=>{this.var(i,(0,Xt._)`${s}[${a}]`),n(i)})}return this._for(new fO("of",o,i,r),()=>n(i))}forIn(e,r,n,o=this.opts.es5?rc.varKinds.var:rc.varKinds.const){if(this.opts.ownProperties)return this.forOf(e,(0,Xt._)`Object.keys(${r})`,n);let i=this._scope.toName(e);return this._for(new fO("in",o,i,r),()=>n(i))}endFor(){return this._endBlockNode(kf)}label(e){return this._leafNode(new Eq(e))}break(e){return this._leafNode(new Tq(e))}return(e){let r=new fT;if(this._blockNode(r),this.code(e),r.nodes.length!==1)throw new Error('CodeGen: "return" should have one node');return this._endBlockNode(fT)}try(e,r,n){if(!r&&!n)throw new Error('CodeGen: "try" without "catch" and "finally"');let o=new Pq;if(this._blockNode(o),this.code(e),r){let i=this.name("e");this._currNode=o.catch=new mT(i),r(i)}return n&&(this._currNode=o.finally=new hT,this.code(n)),this._endBlockNode(mT,hT)}throw(e){return this._leafNode(new bq(e))}block(e,r){return this._blockStarts.push(this._nodes.length),e&&this.code(e).endBlock(r),this}endBlock(e){let r=this._blockStarts.pop();if(r===void 0)throw new Error("CodeGen: not in self-balancing block");let n=this._nodes.length-r;if(n<0||e!==void 0&&n!==e)throw new Error(`CodeGen: wrong number of nodes: ${n} vs ${e} expected`);return this._nodes.length=r,this}func(e,r=Xt.nil,n,o){return this._blockNode(new dT(e,r,n)),o&&this.code(o).endFunc(),this}endFunc(){return this._endBlockNode(dT)}optimize(e=1){for(;e-- >0;)this._root.optimizeNodes(),this._root.optimizeNames(this._root.names,this._constants)}_leafNode(e){return this._currNode.nodes.push(e),this}_blockNode(e){this._currNode.nodes.push(e),this._nodes.push(e)}_endBlockNode(e,r){let n=this._currNode;if(n instanceof e||r&&n instanceof r)return this._nodes.pop(),this;throw new Error(`CodeGen: not in block "${r?`${e.kind}/${r.kind}`:e.kind}"`)}_elseNode(e){let r=this._currNode;if(!(r instanceof $f))throw new Error('CodeGen: "else" without "if"');return this._currNode=r.else=e,this}get _root(){return this._nodes[0]}get _currNode(){let e=this._nodes;return e[e.length-1]}set _currNode(e){let r=this._nodes;r[r.length-1]=e}};kt.CodeGen=Iq;function Mf(t,e){for(let r in e)t[r]=(t[r]||0)+(e[r]||0);return t}function mO(t,e){return e instanceof Xt._CodeOrName?Mf(t,e.names):t}function bg(t,e,r){if(t instanceof Xt.Name)return n(t);if(!o(t))return t;return new Xt._Code(t._items.reduce((i,s)=>(s instanceof Xt.Name&&(s=n(s)),s instanceof Xt._Code?i.push(...s._items):i.push(s),i),[]));function n(i){let s=r[i.str];return s===void 0||e[i.str]!==1?i:(delete e[i.str],s)}function o(i){return i instanceof Xt._Code&&i._items.some(s=>s instanceof Xt.Name&&e[s.str]===1&&r[s.str]!==void 0)}}function Irt(t,e){for(let r in e)t[r]=(t[r]||0)-(e[r]||0)}function Nue(t){return typeof t=="boolean"||typeof t=="number"||t===null?!t:(0,Xt._)`!${Oq(t)}`}kt.not=Nue;var Ort=Cue(kt.operators.AND);function Nrt(...t){return t.reduce(Ort)}kt.and=Nrt;var Crt=Cue(kt.operators.OR);function $rt(...t){return t.reduce(Crt)}kt.or=$rt;function Cue(t){return(e,r)=>e===Xt.nil?r:r===Xt.nil?e:(0,Xt._)`${Oq(e)} ${t} ${Oq(r)}`}function Oq(t){return t instanceof Xt.Name?t:(0,Xt._)`(${t})`}});var cr=S(jt=>{"use strict";Object.defineProperty(jt,"__esModule",{value:!0});jt.checkStrictMode=jt.getErrorPath=jt.Type=jt.useFunc=jt.setEvaluated=jt.evaluatedPropsToName=jt.mergeEvaluated=jt.eachItem=jt.unescapeJsonPointer=jt.escapeJsonPointer=jt.escapeFragment=jt.unescapeFragment=jt.schemaRefOrVal=jt.schemaHasRulesButRef=jt.schemaHasRules=jt.checkUnknownRules=jt.alwaysValidSchema=jt.toHash=void 0;var Ur=Ut(),krt=lT();function Mrt(t){let e={};for(let r of t)e[r]=!0;return e}jt.toHash=Mrt;function Drt(t,e){return typeof e=="boolean"?e:Object.keys(e).length===0?!0:(Mue(t,e),!Due(e,t.self.RULES.all))}jt.alwaysValidSchema=Drt;function Mue(t,e=t.schema){let{opts:r,self:n}=t;if(!r.strictSchema||typeof e=="boolean")return;let o=n.RULES.keywords;for(let i in e)o[i]||jue(t,`unknown keyword: "${i}"`)}jt.checkUnknownRules=Mue;function Due(t,e){if(typeof t=="boolean")return!t;for(let r in t)if(e[r])return!0;return!1}jt.schemaHasRules=Due;function Lrt(t,e){if(typeof t=="boolean")return!t;for(let r in t)if(r!=="$ref"&&e.all[r])return!0;return!1}jt.schemaHasRulesButRef=Lrt;function Urt({topSchemaRef:t,schemaPath:e},r,n,o){if(!o){if(typeof r=="number"||typeof r=="boolean")return r;if(typeof r=="string")return(0,Ur._)`${r}`}return(0,Ur._)`${t}${e}${(0,Ur.getProperty)(n)}`}jt.schemaRefOrVal=Urt;function jrt(t){return Lue(decodeURIComponent(t))}jt.unescapeFragment=jrt;function zrt(t){return encodeURIComponent(Cq(t))}jt.escapeFragment=zrt;function Cq(t){return typeof t=="number"?`${t}`:t.replace(/~/g,"~0").replace(/\//g,"~1")}jt.escapeJsonPointer=Cq;function Lue(t){return t.replace(/~1/g,"/").replace(/~0/g,"~")}jt.unescapeJsonPointer=Lue;function Frt(t,e){if(Array.isArray(t))for(let r of t)e(r);else e(t)}jt.eachItem=Frt;function $ue({mergeNames:t,mergeToName:e,mergeValues:r,resultToName:n}){return(o,i,s,a)=>{let c=s===void 0?i:s instanceof Ur.Name?(i instanceof Ur.Name?t(o,i,s):e(o,i,s),s):i instanceof Ur.Name?(e(o,s,i),i):r(i,s);return a===Ur.Name&&!(c instanceof Ur.Name)?n(o,c):c}}jt.mergeEvaluated={props:$ue({mergeNames:(t,e,r)=>t.if((0,Ur._)`${r} !== true && ${e} !== undefined`,()=>{t.if((0,Ur._)`${e} === true`,()=>t.assign(r,!0),()=>t.assign(r,(0,Ur._)`${r} || {}`).code((0,Ur._)`Object.assign(${r}, ${e})`))}),mergeToName:(t,e,r)=>t.if((0,Ur._)`${r} !== true`,()=>{e===!0?t.assign(r,!0):(t.assign(r,(0,Ur._)`${r} || {}`),$q(t,r,e))}),mergeValues:(t,e)=>t===!0?!0:{...t,...e},resultToName:Uue}),items:$ue({mergeNames:(t,e,r)=>t.if((0,Ur._)`${r} !== true && ${e} !== undefined`,()=>t.assign(r,(0,Ur._)`${e} === true ? true : ${r} > ${e} ? ${r} : ${e}`)),mergeToName:(t,e,r)=>t.if((0,Ur._)`${r} !== true`,()=>t.assign(r,e===!0?!0:(0,Ur._)`${r} > ${e} ? ${r} : ${e}`)),mergeValues:(t,e)=>t===!0?!0:Math.max(t,e),resultToName:(t,e)=>t.var("items",e)})};function Uue(t,e){if(e===!0)return t.var("props",!0);let r=t.var("props",(0,Ur._)`{}`);return e!==void 0&&$q(t,r,e),r}jt.evaluatedPropsToName=Uue;function $q(t,e,r){Object.keys(r).forEach(n=>t.assign((0,Ur._)`${e}${(0,Ur.getProperty)(n)}`,!0))}jt.setEvaluated=$q;var kue={};function qrt(t,e){return t.scopeValue("func",{ref:e,code:kue[e.code]||(kue[e.code]=new krt._Code(e.code))})}jt.useFunc=qrt;var Nq;(function(t){t[t.Num=0]="Num",t[t.Str=1]="Str"})(Nq||(jt.Type=Nq={}));function Brt(t,e,r){if(t instanceof Ur.Name){let n=e===Nq.Num;return r?n?(0,Ur._)`"[" + ${t} + "]"`:(0,Ur._)`"['" + ${t} + "']"`:n?(0,Ur._)`"/" + ${t}`:(0,Ur._)`"/" + ${t}.replace(/~/g, "~0").replace(/\\//g, "~1")`}return r?(0,Ur.getProperty)(t).toString():"/"+Cq(t)}jt.getErrorPath=Brt;function jue(t,e,r=t.opts.strictSchema){if(r){if(e=`strict mode: ${e}`,r===!0)throw new Error(e);t.self.logger.warn(e)}}jt.checkStrictMode=jue});var sl=S(kq=>{"use strict";Object.defineProperty(kq,"__esModule",{value:!0});var Ho=Ut(),Grt={data:new Ho.Name("data"),valCxt:new Ho.Name("valCxt"),instancePath:new Ho.Name("instancePath"),parentData:new Ho.Name("parentData"),parentDataProperty:new Ho.Name("parentDataProperty"),rootData:new Ho.Name("rootData"),dynamicAnchors:new Ho.Name("dynamicAnchors"),vErrors:new Ho.Name("vErrors"),errors:new Ho.Name("errors"),this:new Ho.Name("this"),self:new Ho.Name("self"),scope:new Ho.Name("scope"),json:new Ho.Name("json"),jsonPos:new Ho.Name("jsonPos"),jsonLen:new Ho.Name("jsonLen"),jsonPart:new Ho.Name("jsonPart")};kq.default=Grt});var gT=S(Wo=>{"use strict";Object.defineProperty(Wo,"__esModule",{value:!0});Wo.extendErrors=Wo.resetErrorsCount=Wo.reportExtraError=Wo.reportError=Wo.keyword$DataError=Wo.keywordError=void 0;var tr=Ut(),gO=cr(),Si=sl();Wo.keywordError={message:({keyword:t})=>(0,tr.str)`must pass "${t}" keyword validation`};Wo.keyword$DataError={message:({keyword:t,schemaType:e})=>e?(0,tr.str)`"${t}" keyword must be ${e} ($data)`:(0,tr.str)`"${t}" keyword is invalid ($data)`};function Vrt(t,e=Wo.keywordError,r,n){let{it:o}=t,{gen:i,compositeRule:s,allErrors:a}=o,c=que(t,e,r);n??(s||a)?zue(i,c):Fue(o,(0,tr._)`[${c}]`)}Wo.reportError=Vrt;function Hrt(t,e=Wo.keywordError,r){let{it:n}=t,{gen:o,compositeRule:i,allErrors:s}=n,a=que(t,e,r);zue(o,a),i||s||Fue(n,Si.default.vErrors)}Wo.reportExtraError=Hrt;function Wrt(t,e){t.assign(Si.default.errors,e),t.if((0,tr._)`${Si.default.vErrors} !== null`,()=>t.if(e,()=>t.assign((0,tr._)`${Si.default.vErrors}.length`,e),()=>t.assign(Si.default.vErrors,null)))}Wo.resetErrorsCount=Wrt;function Krt({gen:t,keyword:e,schemaValue:r,data:n,errsCount:o,it:i}){if(o===void 0)throw new Error("ajv implementation error");let s=t.name("err");t.forRange("i",o,Si.default.errors,a=>{t.const(s,(0,tr._)`${Si.default.vErrors}[${a}]`),t.if((0,tr._)`${s}.instancePath === undefined`,()=>t.assign((0,tr._)`${s}.instancePath`,(0,tr.strConcat)(Si.default.instancePath,i.errorPath))),t.assign((0,tr._)`${s}.schemaPath`,(0,tr.str)`${i.errSchemaPath}/${e}`),i.opts.verbose&&(t.assign((0,tr._)`${s}.schema`,r),t.assign((0,tr._)`${s}.data`,n))})}Wo.extendErrors=Krt;function zue(t,e){let r=t.const("err",e);t.if((0,tr._)`${Si.default.vErrors} === null`,()=>t.assign(Si.default.vErrors,(0,tr._)`[${r}]`),(0,tr._)`${Si.default.vErrors}.push(${r})`),t.code((0,tr._)`${Si.default.errors}++`)}function Fue(t,e){let{gen:r,validateName:n,schemaEnv:o}=t;o.$async?r.throw((0,tr._)`new ${t.ValidationError}(${e})`):(r.assign((0,tr._)`${n}.errors`,e),r.return(!1))}var Df={keyword:new tr.Name("keyword"),schemaPath:new tr.Name("schemaPath"),params:new tr.Name("params"),propertyName:new tr.Name("propertyName"),message:new tr.Name("message"),schema:new tr.Name("schema"),parentSchema:new tr.Name("parentSchema")};function que(t,e,r){let{createErrors:n}=t.it;return n===!1?(0,tr._)`{}`:Zrt(t,e,r)}function Zrt(t,e,r={}){let{gen:n,it:o}=t,i=[Yrt(o,r),Jrt(t,r)];return Xrt(t,e,i),n.object(...i)}function Yrt({errorPath:t},{instancePath:e}){let r=e?(0,tr.str)`${t}${(0,gO.getErrorPath)(e,gO.Type.Str)}`:t;return[Si.default.instancePath,(0,tr.strConcat)(Si.default.instancePath,r)]}function Jrt({keyword:t,it:{errSchemaPath:e}},{schemaPath:r,parentSchema:n}){let o=n?e:(0,tr.str)`${e}/${t}`;return r&&(o=(0,tr.str)`${o}${(0,gO.getErrorPath)(r,gO.Type.Str)}`),[Df.schemaPath,o]}function Xrt(t,{params:e,message:r},n){let{keyword:o,data:i,schemaValue:s,it:a}=t,{opts:c,propertyName:u,topSchemaRef:p,schemaPath:f}=a;n.push([Df.keyword,o],[Df.params,typeof e=="function"?e(t):e||(0,tr._)`{}`]),c.messages&&n.push([Df.message,typeof r=="function"?r(t):r]),c.verbose&&n.push([Df.schema,s],[Df.parentSchema,(0,tr._)`${p}${f}`],[Si.default.data,i]),u&&n.push([Df.propertyName,u])}});var Gue=S(xg=>{"use strict";Object.defineProperty(xg,"__esModule",{value:!0});xg.boolOrEmptySchema=xg.topBoolOrEmptySchema=void 0;var Qrt=gT(),ent=Ut(),tnt=sl(),rnt={message:"boolean schema is false"};function nnt(t){let{gen:e,schema:r,validateName:n}=t;r===!1?Bue(t,!1):typeof r=="object"&&r.$async===!0?e.return(tnt.default.data):(e.assign((0,ent._)`${n}.errors`,null),e.return(!0))}xg.topBoolOrEmptySchema=nnt;function ont(t,e){let{gen:r,schema:n}=t;n===!1?(r.var(e,!1),Bue(t)):r.var(e,!0)}xg.boolOrEmptySchema=ont;function Bue(t,e){let{gen:r,data:n}=t,o={gen:r,keyword:"false schema",data:n,schema:!1,schemaCode:!1,schemaValue:!1,params:{},it:t};(0,Qrt.reportError)(o,rnt,void 0,e)}});var Mq=S(Ag=>{"use strict";Object.defineProperty(Ag,"__esModule",{value:!0});Ag.getRules=Ag.isJSONType=void 0;var int=["string","number","integer","boolean","null","object","array"],snt=new Set(int);function ant(t){return typeof t=="string"&&snt.has(t)}Ag.isJSONType=ant;function cnt(){let t={number:{type:"number",rules:[]},string:{type:"string",rules:[]},array:{type:"array",rules:[]},object:{type:"object",rules:[]}};return{types:{...t,integer:!0,boolean:!0,null:!0},rules:[{rules:[]},t.number,t.string,t.array,t.object],post:{rules:[]},all:{},keywords:{}}}Ag.getRules=cnt});var Dq=S(Op=>{"use strict";Object.defineProperty(Op,"__esModule",{value:!0});Op.shouldUseRule=Op.shouldUseGroup=Op.schemaHasRulesForType=void 0;function unt({schema:t,self:e},r){let n=e.RULES.types[r];return n&&n!==!0&&Vue(t,n)}Op.schemaHasRulesForType=unt;function Vue(t,e){return e.rules.some(r=>Hue(t,r))}Op.shouldUseGroup=Vue;function Hue(t,e){var r;return t[e.keyword]!==void 0||((r=e.definition.implements)===null||r===void 0?void 0:r.some(n=>t[n]!==void 0))}Op.shouldUseRule=Hue});var _T=S(Ko=>{"use strict";Object.defineProperty(Ko,"__esModule",{value:!0});Ko.reportTypeError=Ko.checkDataTypes=Ko.checkDataType=Ko.coerceAndCheckDataType=Ko.getJSONTypes=Ko.getSchemaTypes=Ko.DataType=void 0;var lnt=Mq(),pnt=Dq(),dnt=gT(),bt=Ut(),Wue=cr(),wg;(function(t){t[t.Correct=0]="Correct",t[t.Wrong=1]="Wrong"})(wg||(Ko.DataType=wg={}));function fnt(t){let e=Kue(t.type);if(e.includes("null")){if(t.nullable===!1)throw new Error("type: null contradicts nullable: false")}else{if(!e.length&&t.nullable!==void 0)throw new Error('"nullable" cannot be used without "type"');t.nullable===!0&&e.push("null")}return e}Ko.getSchemaTypes=fnt;function Kue(t){let e=Array.isArray(t)?t:t?[t]:[];if(e.every(lnt.isJSONType))return e;throw new Error("type must be JSONType or JSONType[]: "+e.join(","))}Ko.getJSONTypes=Kue;function mnt(t,e){let{gen:r,data:n,opts:o}=t,i=hnt(e,o.coerceTypes),s=e.length>0&&!(i.length===0&&e.length===1&&(0,pnt.schemaHasRulesForType)(t,e[0]));if(s){let a=Uq(e,n,o.strictNumbers,wg.Wrong);r.if(a,()=>{i.length?gnt(t,e,i):jq(t)})}return s}Ko.coerceAndCheckDataType=mnt;var Zue=new Set(["string","number","integer","boolean","null"]);function hnt(t,e){return e?t.filter(r=>Zue.has(r)||e==="array"&&r==="array"):[]}function gnt(t,e,r){let{gen:n,data:o,opts:i}=t,s=n.let("dataType",(0,bt._)`typeof ${o}`),a=n.let("coerced",(0,bt._)`undefined`);i.coerceTypes==="array"&&n.if((0,bt._)`${s} == 'object' && Array.isArray(${o}) && ${o}.length == 1`,()=>n.assign(o,(0,bt._)`${o}[0]`).assign(s,(0,bt._)`typeof ${o}`).if(Uq(e,o,i.strictNumbers),()=>n.assign(a,o))),n.if((0,bt._)`${a} !== undefined`);for(let u of r)(Zue.has(u)||u==="array"&&i.coerceTypes==="array")&&c(u);n.else(),jq(t),n.endIf(),n.if((0,bt._)`${a} !== undefined`,()=>{n.assign(o,a),_nt(t,a)});function c(u){switch(u){case"string":n.elseIf((0,bt._)`${s} == "number" || ${s} == "boolean"`).assign(a,(0,bt._)`"" + ${o}`).elseIf((0,bt._)`${o} === null`).assign(a,(0,bt._)`""`);return;case"number":n.elseIf((0,bt._)`${s} == "boolean" || ${o} === null - || (${s} == "string" && ${o} && ${o} == +${o})`).assign(a,(0,bt._)`+${o}`);return;case"integer":n.elseIf((0,bt._)`${s} === "boolean" || ${o} === null - || (${s} === "string" && ${o} && ${o} == +${o} && !(${o} % 1))`).assign(a,(0,bt._)`+${o}`);return;case"boolean":n.elseIf((0,bt._)`${o} === "false" || ${o} === 0 || ${o} === null`).assign(a,!1).elseIf((0,bt._)`${o} === "true" || ${o} === 1`).assign(a,!0);return;case"null":n.elseIf((0,bt._)`${o} === "" || ${o} === 0 || ${o} === false`),n.assign(a,null);return;case"array":n.elseIf((0,bt._)`${s} === "string" || ${s} === "number" - || ${s} === "boolean" || ${o} === null`).assign(a,(0,bt._)`[${o}]`)}}}function _nt({gen:t,parentData:e,parentDataProperty:r},n){t.if((0,bt._)`${e} !== undefined`,()=>t.assign((0,bt._)`${e}[${r}]`,n))}function Lq(t,e,r,n=wg.Correct){let o=n===wg.Correct?bt.operators.EQ:bt.operators.NEQ,i;switch(t){case"null":return(0,bt._)`${e} ${o} null`;case"array":i=(0,bt._)`Array.isArray(${e})`;break;case"object":i=(0,bt._)`${e} && typeof ${e} == "object" && !Array.isArray(${e})`;break;case"integer":i=s((0,bt._)`!(${e} % 1) && !isNaN(${e})`);break;case"number":i=s();break;default:return(0,bt._)`typeof ${e} ${o} ${t}`}return n===wg.Correct?i:(0,bt.not)(i);function s(a=bt.nil){return(0,bt.and)((0,bt._)`typeof ${e} == "number"`,a,r?(0,bt._)`isFinite(${e})`:bt.nil)}}Ko.checkDataType=Lq;function Uq(t,e,r,n){if(t.length===1)return Lq(t[0],e,r,n);let o,i=(0,Wue.toHash)(t);if(i.array&&i.object){let s=(0,bt._)`typeof ${e} != "object"`;o=i.null?s:(0,bt._)`!${e} || ${s}`,delete i.null,delete i.array,delete i.object}else o=bt.nil;i.number&&delete i.integer;for(let s in i)o=(0,bt.and)(o,Lq(s,e,r,n));return o}Ko.checkDataTypes=Uq;var vnt={message:({schema:t})=>`must be ${t}`,params:({schema:t,schemaValue:e})=>typeof t=="string"?(0,bt._)`{type: ${t}}`:(0,bt._)`{type: ${e}}`};function jq(t){let e=Snt(t);(0,dnt.reportError)(e,vnt)}Ko.reportTypeError=jq;function Snt(t){let{gen:e,data:r,schema:n}=t,o=(0,Wue.schemaRefOrVal)(t,n,"type");return{gen:e,keyword:"type",data:r,schema:n.type,schemaCode:o,schemaValue:o,parentSchema:n,params:{},it:t}}});var Jue=S(_O=>{"use strict";Object.defineProperty(_O,"__esModule",{value:!0});_O.assignDefaults=void 0;var Rg=Ut(),ynt=cr();function Ent(t,e){let{properties:r,items:n}=t.schema;if(e==="object"&&r)for(let o in r)Yue(t,o,r[o].default);else e==="array"&&Array.isArray(n)&&n.forEach((o,i)=>Yue(t,i,o.default))}_O.assignDefaults=Ent;function Yue(t,e,r){let{gen:n,compositeRule:o,data:i,opts:s}=t;if(r===void 0)return;let a=(0,Rg._)`${i}${(0,Rg.getProperty)(e)}`;if(o){(0,ynt.checkStrictMode)(t,`default is ignored for: ${a}`);return}let c=(0,Rg._)`${a} === undefined`;s.useDefaults==="empty"&&(c=(0,Rg._)`${c} || ${a} === null || ${a} === ""`),n.if(c,(0,Rg._)`${a} = ${(0,Rg.stringify)(r)}`)}});var ta=S(Or=>{"use strict";Object.defineProperty(Or,"__esModule",{value:!0});Or.validateUnion=Or.validateArray=Or.usePattern=Or.callValidateCode=Or.schemaProperties=Or.allSchemaProperties=Or.noPropertyInData=Or.propertyInData=Or.isOwnProperty=Or.hasPropFunc=Or.reportMissingProp=Or.checkMissingProp=Or.checkReportMissingProp=void 0;var Wr=Ut(),zq=cr(),Np=sl(),Tnt=cr();function bnt(t,e){let{gen:r,data:n,it:o}=t;r.if(qq(r,n,e,o.opts.ownProperties),()=>{t.setParams({missingProperty:(0,Wr._)`${e}`},!0),t.error()})}Or.checkReportMissingProp=bnt;function xnt({gen:t,data:e,it:{opts:r}},n,o){return(0,Wr.or)(...n.map(i=>(0,Wr.and)(qq(t,e,i,r.ownProperties),(0,Wr._)`${o} = ${i}`)))}Or.checkMissingProp=xnt;function Ant(t,e){t.setParams({missingProperty:e},!0),t.error()}Or.reportMissingProp=Ant;function Xue(t){return t.scopeValue("func",{ref:Object.prototype.hasOwnProperty,code:(0,Wr._)`Object.prototype.hasOwnProperty`})}Or.hasPropFunc=Xue;function Fq(t,e,r){return(0,Wr._)`${Xue(t)}.call(${e}, ${r})`}Or.isOwnProperty=Fq;function wnt(t,e,r,n){let o=(0,Wr._)`${e}${(0,Wr.getProperty)(r)} !== undefined`;return n?(0,Wr._)`${o} && ${Fq(t,e,r)}`:o}Or.propertyInData=wnt;function qq(t,e,r,n){let o=(0,Wr._)`${e}${(0,Wr.getProperty)(r)} === undefined`;return n?(0,Wr.or)(o,(0,Wr.not)(Fq(t,e,r))):o}Or.noPropertyInData=qq;function Que(t){return t?Object.keys(t).filter(e=>e!=="__proto__"):[]}Or.allSchemaProperties=Que;function Rnt(t,e){return Que(e).filter(r=>!(0,zq.alwaysValidSchema)(t,e[r]))}Or.schemaProperties=Rnt;function Pnt({schemaCode:t,data:e,it:{gen:r,topSchemaRef:n,schemaPath:o,errorPath:i},it:s},a,c,u){let p=u?(0,Wr._)`${t}, ${e}, ${n}${o}`:e,f=[[Np.default.instancePath,(0,Wr.strConcat)(Np.default.instancePath,i)],[Np.default.parentData,s.parentData],[Np.default.parentDataProperty,s.parentDataProperty],[Np.default.rootData,Np.default.rootData]];s.opts.dynamicRef&&f.push([Np.default.dynamicAnchors,Np.default.dynamicAnchors]);let m=(0,Wr._)`${p}, ${r.object(...f)}`;return c!==Wr.nil?(0,Wr._)`${a}.call(${c}, ${m})`:(0,Wr._)`${a}(${m})`}Or.callValidateCode=Pnt;var Int=(0,Wr._)`new RegExp`;function Ont({gen:t,it:{opts:e}},r){let n=e.unicodeRegExp?"u":"",{regExp:o}=e.code,i=o(r,n);return t.scopeValue("pattern",{key:i.toString(),ref:i,code:(0,Wr._)`${o.code==="new RegExp"?Int:(0,Tnt.useFunc)(t,o)}(${r}, ${n})`})}Or.usePattern=Ont;function Nnt(t){let{gen:e,data:r,keyword:n,it:o}=t,i=e.name("valid");if(o.allErrors){let a=e.let("valid",!0);return s(()=>e.assign(a,!1)),a}return e.var(i,!0),s(()=>e.break()),i;function s(a){let c=e.const("len",(0,Wr._)`${r}.length`);e.forRange("i",0,c,u=>{t.subschema({keyword:n,dataProp:u,dataPropType:zq.Type.Num},i),e.if((0,Wr.not)(i),a)})}}Or.validateArray=Nnt;function Cnt(t){let{gen:e,schema:r,keyword:n,it:o}=t;if(!Array.isArray(r))throw new Error("ajv implementation error");if(r.some(c=>(0,zq.alwaysValidSchema)(o,c))&&!o.opts.unevaluated)return;let s=e.let("valid",!1),a=e.name("_valid");e.block(()=>r.forEach((c,u)=>{let p=t.subschema({keyword:n,schemaProp:u,compositeRule:!0},a);e.assign(s,(0,Wr._)`${s} || ${a}`),t.mergeValidEvaluated(p,a)||e.if((0,Wr.not)(s))})),t.result(s,()=>t.reset(),()=>t.error(!0))}Or.validateUnion=Cnt});var rle=S(zc=>{"use strict";Object.defineProperty(zc,"__esModule",{value:!0});zc.validateKeywordUsage=zc.validSchemaType=zc.funcKeywordCode=zc.macroKeywordCode=void 0;var yi=Ut(),Lf=sl(),$nt=ta(),knt=gT();function Mnt(t,e){let{gen:r,keyword:n,schema:o,parentSchema:i,it:s}=t,a=e.macro.call(s.self,o,i,s),c=tle(r,n,a);s.opts.validateSchema!==!1&&s.self.validateSchema(a,!0);let u=r.name("valid");t.subschema({schema:a,schemaPath:yi.nil,errSchemaPath:`${s.errSchemaPath}/${n}`,topSchemaRef:c,compositeRule:!0},u),t.pass(u,()=>t.error(!0))}zc.macroKeywordCode=Mnt;function Dnt(t,e){var r;let{gen:n,keyword:o,schema:i,parentSchema:s,$data:a,it:c}=t;Unt(c,e);let u=!a&&e.compile?e.compile.call(c.self,i,s,c):e.validate,p=tle(n,o,u),f=n.let("valid");t.block$data(f,m),t.ok((r=e.valid)!==null&&r!==void 0?r:f);function m(){if(e.errors===!1)v(),e.modifying&&ele(t),E(()=>t.error());else{let x=e.async?h():_();e.modifying&&ele(t),E(()=>Lnt(t,x))}}function h(){let x=n.let("ruleErrs",null);return n.try(()=>v((0,yi._)`await `),w=>n.assign(f,!1).if((0,yi._)`${w} instanceof ${c.ValidationError}`,()=>n.assign(x,(0,yi._)`${w}.errors`),()=>n.throw(w))),x}function _(){let x=(0,yi._)`${p}.errors`;return n.assign(x,null),v(yi.nil),x}function v(x=e.async?(0,yi._)`await `:yi.nil){let w=c.opts.passContext?Lf.default.this:Lf.default.self,I=!("compile"in e&&!a||e.schema===!1);n.assign(f,(0,yi._)`${x}${(0,$nt.callValidateCode)(t,p,w,I)}`,e.modifying)}function E(x){var w;n.if((0,yi.not)((w=e.valid)!==null&&w!==void 0?w:f),x)}}zc.funcKeywordCode=Dnt;function ele(t){let{gen:e,data:r,it:n}=t;e.if(n.parentData,()=>e.assign(r,(0,yi._)`${n.parentData}[${n.parentDataProperty}]`))}function Lnt(t,e){let{gen:r}=t;r.if((0,yi._)`Array.isArray(${e})`,()=>{r.assign(Lf.default.vErrors,(0,yi._)`${Lf.default.vErrors} === null ? ${e} : ${Lf.default.vErrors}.concat(${e})`).assign(Lf.default.errors,(0,yi._)`${Lf.default.vErrors}.length`),(0,knt.extendErrors)(t)},()=>t.error())}function Unt({schemaEnv:t},e){if(e.async&&!t.$async)throw new Error("async keyword in sync schema")}function tle(t,e,r){if(r===void 0)throw new Error(`keyword "${e}" failed to compile`);return t.scopeValue("keyword",typeof r=="function"?{ref:r}:{ref:r,code:(0,yi.stringify)(r)})}function jnt(t,e,r=!1){return!e.length||e.some(n=>n==="array"?Array.isArray(t):n==="object"?t&&typeof t=="object"&&!Array.isArray(t):typeof t==n||r&&typeof t>"u")}zc.validSchemaType=jnt;function znt({schema:t,opts:e,self:r,errSchemaPath:n},o,i){if(Array.isArray(o.keyword)?!o.keyword.includes(i):o.keyword!==i)throw new Error("ajv implementation error");let s=o.dependencies;if(s?.some(a=>!Object.prototype.hasOwnProperty.call(t,a)))throw new Error(`parent schema must have dependencies of ${i}: ${s.join(",")}`);if(o.validateSchema&&!o.validateSchema(t[i])){let c=`keyword "${i}" value is invalid at path "${n}": `+r.errorsText(o.validateSchema.errors);if(e.validateSchema==="log")r.logger.error(c);else throw new Error(c)}}zc.validateKeywordUsage=znt});var ole=S(Cp=>{"use strict";Object.defineProperty(Cp,"__esModule",{value:!0});Cp.extendSubschemaMode=Cp.extendSubschemaData=Cp.getSubschema=void 0;var Fc=Ut(),nle=cr();function Fnt(t,{keyword:e,schemaProp:r,schema:n,schemaPath:o,errSchemaPath:i,topSchemaRef:s}){if(e!==void 0&&n!==void 0)throw new Error('both "keyword" and "schema" passed, only one allowed');if(e!==void 0){let a=t.schema[e];return r===void 0?{schema:a,schemaPath:(0,Fc._)`${t.schemaPath}${(0,Fc.getProperty)(e)}`,errSchemaPath:`${t.errSchemaPath}/${e}`}:{schema:a[r],schemaPath:(0,Fc._)`${t.schemaPath}${(0,Fc.getProperty)(e)}${(0,Fc.getProperty)(r)}`,errSchemaPath:`${t.errSchemaPath}/${e}/${(0,nle.escapeFragment)(r)}`}}if(n!==void 0){if(o===void 0||i===void 0||s===void 0)throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"');return{schema:n,schemaPath:o,topSchemaRef:s,errSchemaPath:i}}throw new Error('either "keyword" or "schema" must be passed')}Cp.getSubschema=Fnt;function qnt(t,e,{dataProp:r,dataPropType:n,data:o,dataTypes:i,propertyName:s}){if(o!==void 0&&r!==void 0)throw new Error('both "data" and "dataProp" passed, only one allowed');let{gen:a}=e;if(r!==void 0){let{errorPath:u,dataPathArr:p,opts:f}=e,m=a.let("data",(0,Fc._)`${e.data}${(0,Fc.getProperty)(r)}`,!0);c(m),t.errorPath=(0,Fc.str)`${u}${(0,nle.getErrorPath)(r,n,f.jsPropertySyntax)}`,t.parentDataProperty=(0,Fc._)`${r}`,t.dataPathArr=[...p,t.parentDataProperty]}if(o!==void 0){let u=o instanceof Fc.Name?o:a.let("data",o,!0);c(u),s!==void 0&&(t.propertyName=s)}i&&(t.dataTypes=i);function c(u){t.data=u,t.dataLevel=e.dataLevel+1,t.dataTypes=[],e.definedProperties=new Set,t.parentData=e.data,t.dataNames=[...e.dataNames,u]}}Cp.extendSubschemaData=qnt;function Bnt(t,{jtdDiscriminator:e,jtdMetadata:r,compositeRule:n,createErrors:o,allErrors:i}){n!==void 0&&(t.compositeRule=n),o!==void 0&&(t.createErrors=o),i!==void 0&&(t.allErrors=i),t.jtdDiscriminator=e,t.jtdMetadata=r}Cp.extendSubschemaMode=Bnt});var vT=S((Qcr,ile)=>{"use strict";ile.exports=function t(e,r){if(e===r)return!0;if(e&&r&&typeof e=="object"&&typeof r=="object"){if(e.constructor!==r.constructor)return!1;var n,o,i;if(Array.isArray(e)){if(n=e.length,n!=r.length)return!1;for(o=n;o--!==0;)if(!t(e[o],r[o]))return!1;return!0}if(e.constructor===RegExp)return e.source===r.source&&e.flags===r.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===r.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===r.toString();if(i=Object.keys(e),n=i.length,n!==Object.keys(r).length)return!1;for(o=n;o--!==0;)if(!Object.prototype.hasOwnProperty.call(r,i[o]))return!1;for(o=n;o--!==0;){var s=i[o];if(!t(e[s],r[s]))return!1}return!0}return e!==e&&r!==r}});var ale=S((eur,sle)=>{"use strict";var $p=sle.exports=function(t,e,r){typeof e=="function"&&(r=e,e={}),r=e.cb||r;var n=typeof r=="function"?r:r.pre||function(){},o=r.post||function(){};vO(e,n,o,t,"",t)};$p.keywords={additionalItems:!0,items:!0,contains:!0,additionalProperties:!0,propertyNames:!0,not:!0,if:!0,then:!0,else:!0};$p.arrayKeywords={items:!0,allOf:!0,anyOf:!0,oneOf:!0};$p.propsKeywords={$defs:!0,definitions:!0,properties:!0,patternProperties:!0,dependencies:!0};$p.skipKeywords={default:!0,enum:!0,const:!0,required:!0,maximum:!0,minimum:!0,exclusiveMaximum:!0,exclusiveMinimum:!0,multipleOf:!0,maxLength:!0,minLength:!0,pattern:!0,format:!0,maxItems:!0,minItems:!0,uniqueItems:!0,maxProperties:!0,minProperties:!0};function vO(t,e,r,n,o,i,s,a,c,u){if(n&&typeof n=="object"&&!Array.isArray(n)){e(n,o,i,s,a,c,u);for(var p in n){var f=n[p];if(Array.isArray(f)){if(p in $p.arrayKeywords)for(var m=0;m{"use strict";Object.defineProperty(Gi,"__esModule",{value:!0});Gi.getSchemaRefs=Gi.resolveUrl=Gi.normalizeId=Gi._getFullPath=Gi.getFullPath=Gi.inlineRef=void 0;var Vnt=cr(),Hnt=vT(),Wnt=ale(),Knt=new Set(["type","format","pattern","maxLength","minLength","maxProperties","minProperties","maxItems","minItems","maximum","minimum","uniqueItems","multipleOf","required","enum","const"]);function Znt(t,e=!0){return typeof t=="boolean"?!0:e===!0?!Bq(t):e?cle(t)<=e:!1}Gi.inlineRef=Znt;var Ynt=new Set(["$ref","$recursiveRef","$recursiveAnchor","$dynamicRef","$dynamicAnchor"]);function Bq(t){for(let e in t){if(Ynt.has(e))return!0;let r=t[e];if(Array.isArray(r)&&r.some(Bq)||typeof r=="object"&&Bq(r))return!0}return!1}function cle(t){let e=0;for(let r in t){if(r==="$ref")return 1/0;if(e++,!Knt.has(r)&&(typeof t[r]=="object"&&(0,Vnt.eachItem)(t[r],n=>e+=cle(n)),e===1/0))return 1/0}return e}function ule(t,e="",r){r!==!1&&(e=Pg(e));let n=t.parse(e);return lle(t,n)}Gi.getFullPath=ule;function lle(t,e){return t.serialize(e).split("#")[0]+"#"}Gi._getFullPath=lle;var Jnt=/#\/?$/;function Pg(t){return t?t.replace(Jnt,""):""}Gi.normalizeId=Pg;function Xnt(t,e,r){return r=Pg(r),t.resolve(e,r)}Gi.resolveUrl=Xnt;var Qnt=/^[a-z_][-a-z0-9._]*$/i;function eot(t,e){if(typeof t=="boolean")return{};let{schemaId:r,uriResolver:n}=this.opts,o=Pg(t[r]||e),i={"":o},s=ule(n,o,!1),a={},c=new Set;return Wnt(t,{allKeys:!0},(f,m,h,_)=>{if(_===void 0)return;let v=s+m,E=i[_];typeof f[r]=="string"&&(E=x.call(this,f[r])),w.call(this,f.$anchor),w.call(this,f.$dynamicAnchor),i[m]=E;function x(I){let N=this.opts.uriResolver.resolve;if(I=Pg(E?N(E,I):I),c.has(I))throw p(I);c.add(I);let $=this.refs[I];return typeof $=="string"&&($=this.refs[$]),typeof $=="object"?u(f,$.schema,I):I!==Pg(v)&&(I[0]==="#"?(u(f,a[I],I),a[I]=f):this.refs[I]=v),I}function w(I){if(typeof I=="string"){if(!Qnt.test(I))throw new Error(`invalid anchor "${I}"`);x.call(this,`#${I}`)}}}),a;function u(f,m,h){if(m!==void 0&&!Hnt(f,m))throw p(h)}function p(f){return new Error(`reference "${f}" resolves to more than one schema`)}}Gi.getSchemaRefs=eot});var TT=S(kp=>{"use strict";Object.defineProperty(kp,"__esModule",{value:!0});kp.getData=kp.KeywordCxt=kp.validateFunctionCode=void 0;var hle=Gue(),ple=_T(),Vq=Dq(),SO=_T(),tot=Jue(),ET=rle(),Gq=ole(),Fe=Ut(),ct=sl(),rot=ST(),al=cr(),yT=gT();function not(t){if(vle(t)&&(Sle(t),_le(t))){sot(t);return}gle(t,()=>(0,hle.topBoolOrEmptySchema)(t))}kp.validateFunctionCode=not;function gle({gen:t,validateName:e,schema:r,schemaEnv:n,opts:o},i){o.code.es5?t.func(e,(0,Fe._)`${ct.default.data}, ${ct.default.valCxt}`,n.$async,()=>{t.code((0,Fe._)`"use strict"; ${dle(r,o)}`),iot(t,o),t.code(i)}):t.func(e,(0,Fe._)`${ct.default.data}, ${oot(o)}`,n.$async,()=>t.code(dle(r,o)).code(i))}function oot(t){return(0,Fe._)`{${ct.default.instancePath}="", ${ct.default.parentData}, ${ct.default.parentDataProperty}, ${ct.default.rootData}=${ct.default.data}${t.dynamicRef?(0,Fe._)`, ${ct.default.dynamicAnchors}={}`:Fe.nil}}={}`}function iot(t,e){t.if(ct.default.valCxt,()=>{t.var(ct.default.instancePath,(0,Fe._)`${ct.default.valCxt}.${ct.default.instancePath}`),t.var(ct.default.parentData,(0,Fe._)`${ct.default.valCxt}.${ct.default.parentData}`),t.var(ct.default.parentDataProperty,(0,Fe._)`${ct.default.valCxt}.${ct.default.parentDataProperty}`),t.var(ct.default.rootData,(0,Fe._)`${ct.default.valCxt}.${ct.default.rootData}`),e.dynamicRef&&t.var(ct.default.dynamicAnchors,(0,Fe._)`${ct.default.valCxt}.${ct.default.dynamicAnchors}`)},()=>{t.var(ct.default.instancePath,(0,Fe._)`""`),t.var(ct.default.parentData,(0,Fe._)`undefined`),t.var(ct.default.parentDataProperty,(0,Fe._)`undefined`),t.var(ct.default.rootData,ct.default.data),e.dynamicRef&&t.var(ct.default.dynamicAnchors,(0,Fe._)`{}`)})}function sot(t){let{schema:e,opts:r,gen:n}=t;gle(t,()=>{r.$comment&&e.$comment&&Ele(t),pot(t),n.let(ct.default.vErrors,null),n.let(ct.default.errors,0),r.unevaluated&&aot(t),yle(t),mot(t)})}function aot(t){let{gen:e,validateName:r}=t;t.evaluated=e.const("evaluated",(0,Fe._)`${r}.evaluated`),e.if((0,Fe._)`${t.evaluated}.dynamicProps`,()=>e.assign((0,Fe._)`${t.evaluated}.props`,(0,Fe._)`undefined`)),e.if((0,Fe._)`${t.evaluated}.dynamicItems`,()=>e.assign((0,Fe._)`${t.evaluated}.items`,(0,Fe._)`undefined`))}function dle(t,e){let r=typeof t=="object"&&t[e.schemaId];return r&&(e.code.source||e.code.process)?(0,Fe._)`/*# sourceURL=${r} */`:Fe.nil}function cot(t,e){if(vle(t)&&(Sle(t),_le(t))){uot(t,e);return}(0,hle.boolOrEmptySchema)(t,e)}function _le({schema:t,self:e}){if(typeof t=="boolean")return!t;for(let r in t)if(e.RULES.all[r])return!0;return!1}function vle(t){return typeof t.schema!="boolean"}function uot(t,e){let{schema:r,gen:n,opts:o}=t;o.$comment&&r.$comment&&Ele(t),dot(t),fot(t);let i=n.const("_errs",ct.default.errors);yle(t,i),n.var(e,(0,Fe._)`${i} === ${ct.default.errors}`)}function Sle(t){(0,al.checkUnknownRules)(t),lot(t)}function yle(t,e){if(t.opts.jtd)return fle(t,[],!1,e);let r=(0,ple.getSchemaTypes)(t.schema),n=(0,ple.coerceAndCheckDataType)(t,r);fle(t,r,!n,e)}function lot(t){let{schema:e,errSchemaPath:r,opts:n,self:o}=t;e.$ref&&n.ignoreKeywordsWithRef&&(0,al.schemaHasRulesButRef)(e,o.RULES)&&o.logger.warn(`$ref: keywords ignored in schema at path "${r}"`)}function pot(t){let{schema:e,opts:r}=t;e.default!==void 0&&r.useDefaults&&r.strictSchema&&(0,al.checkStrictMode)(t,"default is ignored in the schema root")}function dot(t){let e=t.schema[t.opts.schemaId];e&&(t.baseId=(0,rot.resolveUrl)(t.opts.uriResolver,t.baseId,e))}function fot(t){if(t.schema.$async&&!t.schemaEnv.$async)throw new Error("async schema in sync schema")}function Ele({gen:t,schemaEnv:e,schema:r,errSchemaPath:n,opts:o}){let i=r.$comment;if(o.$comment===!0)t.code((0,Fe._)`${ct.default.self}.logger.log(${i})`);else if(typeof o.$comment=="function"){let s=(0,Fe.str)`${n}/$comment`,a=t.scopeValue("root",{ref:e.root});t.code((0,Fe._)`${ct.default.self}.opts.$comment(${i}, ${s}, ${a}.schema)`)}}function mot(t){let{gen:e,schemaEnv:r,validateName:n,ValidationError:o,opts:i}=t;r.$async?e.if((0,Fe._)`${ct.default.errors} === 0`,()=>e.return(ct.default.data),()=>e.throw((0,Fe._)`new ${o}(${ct.default.vErrors})`)):(e.assign((0,Fe._)`${n}.errors`,ct.default.vErrors),i.unevaluated&&hot(t),e.return((0,Fe._)`${ct.default.errors} === 0`))}function hot({gen:t,evaluated:e,props:r,items:n}){r instanceof Fe.Name&&t.assign((0,Fe._)`${e}.props`,r),n instanceof Fe.Name&&t.assign((0,Fe._)`${e}.items`,n)}function fle(t,e,r,n){let{gen:o,schema:i,data:s,allErrors:a,opts:c,self:u}=t,{RULES:p}=u;if(i.$ref&&(c.ignoreKeywordsWithRef||!(0,al.schemaHasRulesButRef)(i,p))){o.block(()=>ble(t,"$ref",p.all.$ref.definition));return}c.jtd||got(t,e),o.block(()=>{for(let m of p.rules)f(m);f(p.post)});function f(m){(0,Vq.shouldUseGroup)(i,m)&&(m.type?(o.if((0,SO.checkDataType)(m.type,s,c.strictNumbers)),mle(t,m),e.length===1&&e[0]===m.type&&r&&(o.else(),(0,SO.reportTypeError)(t)),o.endIf()):mle(t,m),a||o.if((0,Fe._)`${ct.default.errors} === ${n||0}`))}}function mle(t,e){let{gen:r,schema:n,opts:{useDefaults:o}}=t;o&&(0,tot.assignDefaults)(t,e.type),r.block(()=>{for(let i of e.rules)(0,Vq.shouldUseRule)(n,i)&&ble(t,i.keyword,i.definition,e.type)})}function got(t,e){t.schemaEnv.meta||!t.opts.strictTypes||(_ot(t,e),t.opts.allowUnionTypes||vot(t,e),Sot(t,t.dataTypes))}function _ot(t,e){if(e.length){if(!t.dataTypes.length){t.dataTypes=e;return}e.forEach(r=>{Tle(t.dataTypes,r)||Hq(t,`type "${r}" not allowed by context "${t.dataTypes.join(",")}"`)}),Eot(t,e)}}function vot(t,e){e.length>1&&!(e.length===2&&e.includes("null"))&&Hq(t,"use allowUnionTypes to allow union type keyword")}function Sot(t,e){let r=t.self.RULES.all;for(let n in r){let o=r[n];if(typeof o=="object"&&(0,Vq.shouldUseRule)(t.schema,o)){let{type:i}=o.definition;i.length&&!i.some(s=>yot(e,s))&&Hq(t,`missing type "${i.join(",")}" for keyword "${n}"`)}}}function yot(t,e){return t.includes(e)||e==="number"&&t.includes("integer")}function Tle(t,e){return t.includes(e)||e==="integer"&&t.includes("number")}function Eot(t,e){let r=[];for(let n of t.dataTypes)Tle(e,n)?r.push(n):e.includes("integer")&&n==="number"&&r.push("integer");t.dataTypes=r}function Hq(t,e){let r=t.schemaEnv.baseId+t.errSchemaPath;e+=` at "${r}" (strictTypes)`,(0,al.checkStrictMode)(t,e,t.opts.strictTypes)}var yO=class{constructor(e,r,n){if((0,ET.validateKeywordUsage)(e,r,n),this.gen=e.gen,this.allErrors=e.allErrors,this.keyword=n,this.data=e.data,this.schema=e.schema[n],this.$data=r.$data&&e.opts.$data&&this.schema&&this.schema.$data,this.schemaValue=(0,al.schemaRefOrVal)(e,this.schema,n,this.$data),this.schemaType=r.schemaType,this.parentSchema=e.schema,this.params={},this.it=e,this.def=r,this.$data)this.schemaCode=e.gen.const("vSchema",xle(this.$data,e));else if(this.schemaCode=this.schemaValue,!(0,ET.validSchemaType)(this.schema,r.schemaType,r.allowUndefined))throw new Error(`${n} value must be ${JSON.stringify(r.schemaType)}`);("code"in r?r.trackErrors:r.errors!==!1)&&(this.errsCount=e.gen.const("_errs",ct.default.errors))}result(e,r,n){this.failResult((0,Fe.not)(e),r,n)}failResult(e,r,n){this.gen.if(e),n?n():this.error(),r?(this.gen.else(),r(),this.allErrors&&this.gen.endIf()):this.allErrors?this.gen.endIf():this.gen.else()}pass(e,r){this.failResult((0,Fe.not)(e),void 0,r)}fail(e){if(e===void 0){this.error(),this.allErrors||this.gen.if(!1);return}this.gen.if(e),this.error(),this.allErrors?this.gen.endIf():this.gen.else()}fail$data(e){if(!this.$data)return this.fail(e);let{schemaCode:r}=this;this.fail((0,Fe._)`${r} !== undefined && (${(0,Fe.or)(this.invalid$data(),e)})`)}error(e,r,n){if(r){this.setParams(r),this._error(e,n),this.setParams({});return}this._error(e,n)}_error(e,r){(e?yT.reportExtraError:yT.reportError)(this,this.def.error,r)}$dataError(){(0,yT.reportError)(this,this.def.$dataError||yT.keyword$DataError)}reset(){if(this.errsCount===void 0)throw new Error('add "trackErrors" to keyword definition');(0,yT.resetErrorsCount)(this.gen,this.errsCount)}ok(e){this.allErrors||this.gen.if(e)}setParams(e,r){r?Object.assign(this.params,e):this.params=e}block$data(e,r,n=Fe.nil){this.gen.block(()=>{this.check$data(e,n),r()})}check$data(e=Fe.nil,r=Fe.nil){if(!this.$data)return;let{gen:n,schemaCode:o,schemaType:i,def:s}=this;n.if((0,Fe.or)((0,Fe._)`${o} === undefined`,r)),e!==Fe.nil&&n.assign(e,!0),(i.length||s.validateSchema)&&(n.elseIf(this.invalid$data()),this.$dataError(),e!==Fe.nil&&n.assign(e,!1)),n.else()}invalid$data(){let{gen:e,schemaCode:r,schemaType:n,def:o,it:i}=this;return(0,Fe.or)(s(),a());function s(){if(n.length){if(!(r instanceof Fe.Name))throw new Error("ajv implementation error");let c=Array.isArray(n)?n:[n];return(0,Fe._)`${(0,SO.checkDataTypes)(c,r,i.opts.strictNumbers,SO.DataType.Wrong)}`}return Fe.nil}function a(){if(o.validateSchema){let c=e.scopeValue("validate$data",{ref:o.validateSchema});return(0,Fe._)`!${c}(${r})`}return Fe.nil}}subschema(e,r){let n=(0,Gq.getSubschema)(this.it,e);(0,Gq.extendSubschemaData)(n,this.it,e),(0,Gq.extendSubschemaMode)(n,e);let o={...this.it,...n,items:void 0,props:void 0};return cot(o,r),o}mergeEvaluated(e,r){let{it:n,gen:o}=this;n.opts.unevaluated&&(n.props!==!0&&e.props!==void 0&&(n.props=al.mergeEvaluated.props(o,e.props,n.props,r)),n.items!==!0&&e.items!==void 0&&(n.items=al.mergeEvaluated.items(o,e.items,n.items,r)))}mergeValidEvaluated(e,r){let{it:n,gen:o}=this;if(n.opts.unevaluated&&(n.props!==!0||n.items!==!0))return o.if(r,()=>this.mergeEvaluated(e,Fe.Name)),!0}};kp.KeywordCxt=yO;function ble(t,e,r,n){let o=new yO(t,r,e);"code"in r?r.code(o,n):o.$data&&r.validate?(0,ET.funcKeywordCode)(o,r):"macro"in r?(0,ET.macroKeywordCode)(o,r):(r.compile||r.validate)&&(0,ET.funcKeywordCode)(o,r)}var Tot=/^\/(?:[^~]|~0|~1)*$/,bot=/^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;function xle(t,{dataLevel:e,dataNames:r,dataPathArr:n}){let o,i;if(t==="")return ct.default.rootData;if(t[0]==="/"){if(!Tot.test(t))throw new Error(`Invalid JSON-pointer: ${t}`);o=t,i=ct.default.rootData}else{let u=bot.exec(t);if(!u)throw new Error(`Invalid JSON-pointer: ${t}`);let p=+u[1];if(o=u[2],o==="#"){if(p>=e)throw new Error(c("property/index",p));return n[e-p]}if(p>e)throw new Error(c("data",p));if(i=r[e-p],!o)return i}let s=i,a=o.split("/");for(let u of a)u&&(i=(0,Fe._)`${i}${(0,Fe.getProperty)((0,al.unescapeJsonPointer)(u))}`,s=(0,Fe._)`${s} && ${i}`);return s;function c(u,p){return`Cannot access ${u} ${p} levels up, current level is ${e}`}}kp.getData=xle});var EO=S(Kq=>{"use strict";Object.defineProperty(Kq,"__esModule",{value:!0});var Wq=class extends Error{constructor(e){super("validation failed"),this.errors=e,this.ajv=this.validation=!0}};Kq.default=Wq});var bT=S(Jq=>{"use strict";Object.defineProperty(Jq,"__esModule",{value:!0});var Zq=ST(),Yq=class extends Error{constructor(e,r,n,o){super(o||`can't resolve reference ${n} from id ${r}`),this.missingRef=(0,Zq.resolveUrl)(e,r,n),this.missingSchema=(0,Zq.normalizeId)((0,Zq.getFullPath)(e,this.missingRef))}};Jq.default=Yq});var bO=S(ra=>{"use strict";Object.defineProperty(ra,"__esModule",{value:!0});ra.resolveSchema=ra.getCompilingSchema=ra.resolveRef=ra.compileSchema=ra.SchemaEnv=void 0;var nc=Ut(),xot=EO(),Uf=sl(),oc=ST(),Ale=cr(),Aot=TT(),Ig=class{constructor(e){var r;this.refs={},this.dynamicAnchors={};let n;typeof e.schema=="object"&&(n=e.schema),this.schema=e.schema,this.schemaId=e.schemaId,this.root=e.root||this,this.baseId=(r=e.baseId)!==null&&r!==void 0?r:(0,oc.normalizeId)(n?.[e.schemaId||"$id"]),this.schemaPath=e.schemaPath,this.localRefs=e.localRefs,this.meta=e.meta,this.$async=n?.$async,this.refs={}}};ra.SchemaEnv=Ig;function Qq(t){let e=wle.call(this,t);if(e)return e;let r=(0,oc.getFullPath)(this.opts.uriResolver,t.root.baseId),{es5:n,lines:o}=this.opts.code,{ownProperties:i}=this.opts,s=new nc.CodeGen(this.scope,{es5:n,lines:o,ownProperties:i}),a;t.$async&&(a=s.scopeValue("Error",{ref:xot.default,code:(0,nc._)`require("ajv/dist/runtime/validation_error").default`}));let c=s.scopeName("validate");t.validateName=c;let u={gen:s,allErrors:this.opts.allErrors,data:Uf.default.data,parentData:Uf.default.parentData,parentDataProperty:Uf.default.parentDataProperty,dataNames:[Uf.default.data],dataPathArr:[nc.nil],dataLevel:0,dataTypes:[],definedProperties:new Set,topSchemaRef:s.scopeValue("schema",this.opts.code.source===!0?{ref:t.schema,code:(0,nc.stringify)(t.schema)}:{ref:t.schema}),validateName:c,ValidationError:a,schema:t.schema,schemaEnv:t,rootId:r,baseId:t.baseId||r,schemaPath:nc.nil,errSchemaPath:t.schemaPath||(this.opts.jtd?"":"#"),errorPath:(0,nc._)`""`,opts:this.opts,self:this},p;try{this._compilations.add(t),(0,Aot.validateFunctionCode)(u),s.optimize(this.opts.code.optimize);let f=s.toString();p=`${s.scopeRefs(Uf.default.scope)}return ${f}`,this.opts.code.process&&(p=this.opts.code.process(p,t));let h=new Function(`${Uf.default.self}`,`${Uf.default.scope}`,p)(this,this.scope.get());if(this.scope.value(c,{ref:h}),h.errors=null,h.schema=t.schema,h.schemaEnv=t,t.$async&&(h.$async=!0),this.opts.code.source===!0&&(h.source={validateName:c,validateCode:f,scopeValues:s._values}),this.opts.unevaluated){let{props:_,items:v}=u;h.evaluated={props:_ instanceof nc.Name?void 0:_,items:v instanceof nc.Name?void 0:v,dynamicProps:_ instanceof nc.Name,dynamicItems:v instanceof nc.Name},h.source&&(h.source.evaluated=(0,nc.stringify)(h.evaluated))}return t.validate=h,t}catch(f){throw delete t.validate,delete t.validateName,p&&this.logger.error("Error compiling schema, function code:",p),f}finally{this._compilations.delete(t)}}ra.compileSchema=Qq;function wot(t,e,r){var n;r=(0,oc.resolveUrl)(this.opts.uriResolver,e,r);let o=t.refs[r];if(o)return o;let i=Iot.call(this,t,r);if(i===void 0){let s=(n=t.localRefs)===null||n===void 0?void 0:n[r],{schemaId:a}=this.opts;s&&(i=new Ig({schema:s,schemaId:a,root:t,baseId:e}))}if(i!==void 0)return t.refs[r]=Rot.call(this,i)}ra.resolveRef=wot;function Rot(t){return(0,oc.inlineRef)(t.schema,this.opts.inlineRefs)?t.schema:t.validate?t:Qq.call(this,t)}function wle(t){for(let e of this._compilations)if(Pot(e,t))return e}ra.getCompilingSchema=wle;function Pot(t,e){return t.schema===e.schema&&t.root===e.root&&t.baseId===e.baseId}function Iot(t,e){let r;for(;typeof(r=this.refs[e])=="string";)e=r;return r||this.schemas[e]||TO.call(this,t,e)}function TO(t,e){let r=this.opts.uriResolver.parse(e),n=(0,oc._getFullPath)(this.opts.uriResolver,r),o=(0,oc.getFullPath)(this.opts.uriResolver,t.baseId,void 0);if(Object.keys(t.schema).length>0&&n===o)return Xq.call(this,r,t);let i=(0,oc.normalizeId)(n),s=this.refs[i]||this.schemas[i];if(typeof s=="string"){let a=TO.call(this,t,s);return typeof a?.schema!="object"?void 0:Xq.call(this,r,a)}if(typeof s?.schema=="object"){if(s.validate||Qq.call(this,s),i===(0,oc.normalizeId)(e)){let{schema:a}=s,{schemaId:c}=this.opts,u=a[c];return u&&(o=(0,oc.resolveUrl)(this.opts.uriResolver,o,u)),new Ig({schema:a,schemaId:c,root:t,baseId:o})}return Xq.call(this,r,s)}}ra.resolveSchema=TO;var Oot=new Set(["properties","patternProperties","enum","dependencies","definitions"]);function Xq(t,{baseId:e,schema:r,root:n}){var o;if(((o=t.fragment)===null||o===void 0?void 0:o[0])!=="/")return;for(let a of t.fragment.slice(1).split("/")){if(typeof r=="boolean")return;let c=r[(0,Ale.unescapeFragment)(a)];if(c===void 0)return;r=c;let u=typeof r=="object"&&r[this.opts.schemaId];!Oot.has(a)&&u&&(e=(0,oc.resolveUrl)(this.opts.uriResolver,e,u))}let i;if(typeof r!="boolean"&&r.$ref&&!(0,Ale.schemaHasRulesButRef)(r,this.RULES)){let a=(0,oc.resolveUrl)(this.opts.uriResolver,e,r.$ref);i=TO.call(this,n,a)}let{schemaId:s}=this.opts;if(i=i||new Ig({schema:r,schemaId:s,root:n,baseId:e}),i.schema!==i.root.schema)return i}});var Rle=S((sur,Not)=>{Not.exports={$id:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#",description:"Meta-schema for $data reference (JSON AnySchema extension proposal)",type:"object",required:["$data"],properties:{$data:{type:"string",anyOf:[{format:"relative-json-pointer"},{format:"json-pointer"}]}},additionalProperties:!1}});var t6=S((aur,Nle)=>{"use strict";var Cot=RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu),Ile=RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);function e6(t){let e="",r=0,n=0;for(n=0;n=48&&r<=57||r>=65&&r<=70||r>=97&&r<=102))return"";e+=t[n];break}for(n+=1;n=48&&r<=57||r>=65&&r<=70||r>=97&&r<=102))return"";e+=t[n]}return e}var $ot=RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);function Ple(t){return t.length=0,!0}function kot(t,e,r){if(t.length){let n=e6(t);if(n!=="")e.push(n);else return r.error=!0,!1;t.length=0}return!0}function Mot(t){let e=0,r={error:!1,address:"",zone:""},n=[],o=[],i=!1,s=!1,a=kot;for(let c=0;c7){r.error=!0;break}c>0&&t[c-1]===":"&&(i=!0),n.push(":");continue}else if(u==="%"){if(!a(o,n,r))break;a=Ple}else{o.push(u);continue}}return o.length&&(a===Ple?r.zone=o.join(""):s?n.push(o.join("")):n.push(e6(o))),r.address=n.join(""),r}function Ole(t){if(Dot(t,":")<2)return{host:t,isIPV6:!1};let e=Mot(t);if(e.error)return{host:t,isIPV6:!1};{let r=e.address,n=e.address;return e.zone&&(r+="%"+e.zone,n+="%25"+e.zone),{host:r,isIPV6:!0,escapedHost:n}}}function Dot(t,e){let r=0;for(let n=0;n{"use strict";var{isUUID:zot}=t6(),Fot=/([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu,qot=["http","https","ws","wss","urn","urn:uuid"];function Bot(t){return qot.indexOf(t)!==-1}function r6(t){return t.secure===!0?!0:t.secure===!1?!1:t.scheme?t.scheme.length===3&&(t.scheme[0]==="w"||t.scheme[0]==="W")&&(t.scheme[1]==="s"||t.scheme[1]==="S")&&(t.scheme[2]==="s"||t.scheme[2]==="S"):!1}function Cle(t){return t.host||(t.error=t.error||"HTTP URIs must have a host."),t}function $le(t){let e=String(t.scheme).toLowerCase()==="https";return(t.port===(e?443:80)||t.port==="")&&(t.port=void 0),t.path||(t.path="/"),t}function Got(t){return t.secure=r6(t),t.resourceName=(t.path||"/")+(t.query?"?"+t.query:""),t.path=void 0,t.query=void 0,t}function Vot(t){if((t.port===(r6(t)?443:80)||t.port==="")&&(t.port=void 0),typeof t.secure=="boolean"&&(t.scheme=t.secure?"wss":"ws",t.secure=void 0),t.resourceName){let[e,r]=t.resourceName.split("?");t.path=e&&e!=="/"?e:void 0,t.query=r,t.resourceName=void 0}return t.fragment=void 0,t}function Hot(t,e){if(!t.path)return t.error="URN can not be parsed",t;let r=t.path.match(Fot);if(r){let n=e.scheme||t.scheme||"urn";t.nid=r[1].toLowerCase(),t.nss=r[2];let o=`${n}:${e.nid||t.nid}`,i=n6(o);t.path=void 0,i&&(t=i.parse(t,e))}else t.error=t.error||"URN can not be parsed.";return t}function Wot(t,e){if(t.nid===void 0)throw new Error("URN without nid cannot be serialized");let r=e.scheme||t.scheme||"urn",n=t.nid.toLowerCase(),o=`${r}:${e.nid||n}`,i=n6(o);i&&(t=i.serialize(t,e));let s=t,a=t.nss;return s.path=`${n||e.nid}:${a}`,e.skipEscape=!0,s}function Kot(t,e){let r=t;return r.uuid=r.nss,r.nss=void 0,!e.tolerant&&(!r.uuid||!zot(r.uuid))&&(r.error=r.error||"UUID is not valid."),r}function Zot(t){let e=t;return e.nss=(t.uuid||"").toLowerCase(),e}var kle={scheme:"http",domainHost:!0,parse:Cle,serialize:$le},Yot={scheme:"https",domainHost:kle.domainHost,parse:Cle,serialize:$le},xO={scheme:"ws",domainHost:!0,parse:Got,serialize:Vot},Jot={scheme:"wss",domainHost:xO.domainHost,parse:xO.parse,serialize:xO.serialize},Xot={scheme:"urn",parse:Hot,serialize:Wot,skipNormalize:!0},Qot={scheme:"urn:uuid",parse:Kot,serialize:Zot,skipNormalize:!0},AO={http:kle,https:Yot,ws:xO,wss:Jot,urn:Xot,"urn:uuid":Qot};Object.setPrototypeOf(AO,null);function n6(t){return t&&(AO[t]||AO[t.toLowerCase()])||void 0}Mle.exports={wsIsSecure:r6,SCHEMES:AO,isValidSchemeName:Bot,getSchemeHandler:n6}});var i6=S((uur,RO)=>{"use strict";var{normalizeIPv6:eit,removeDotSegments:xT,recomposeAuthority:tit,normalizeComponentEncoding:wO,isIPv4:rit,nonSimpleDomain:nit}=t6(),{SCHEMES:oit,getSchemeHandler:Lle}=Dle();function iit(t,e){return typeof t=="string"?t=qc(cl(t,e),e):typeof t=="object"&&(t=cl(qc(t,e),e)),t}function sit(t,e,r){let n=r?Object.assign({scheme:"null"},r):{scheme:"null"},o=Ule(cl(t,n),cl(e,n),n,!0);return n.skipEscape=!0,qc(o,n)}function Ule(t,e,r,n){let o={};return n||(t=cl(qc(t,r),r),e=cl(qc(e,r),r)),r=r||{},!r.tolerant&&e.scheme?(o.scheme=e.scheme,o.userinfo=e.userinfo,o.host=e.host,o.port=e.port,o.path=xT(e.path||""),o.query=e.query):(e.userinfo!==void 0||e.host!==void 0||e.port!==void 0?(o.userinfo=e.userinfo,o.host=e.host,o.port=e.port,o.path=xT(e.path||""),o.query=e.query):(e.path?(e.path[0]==="/"?o.path=xT(e.path):((t.userinfo!==void 0||t.host!==void 0||t.port!==void 0)&&!t.path?o.path="/"+e.path:t.path?o.path=t.path.slice(0,t.path.lastIndexOf("/")+1)+e.path:o.path=e.path,o.path=xT(o.path)),o.query=e.query):(o.path=t.path,e.query!==void 0?o.query=e.query:o.query=t.query),o.userinfo=t.userinfo,o.host=t.host,o.port=t.port),o.scheme=t.scheme),o.fragment=e.fragment,o}function ait(t,e,r){return typeof t=="string"?(t=unescape(t),t=qc(wO(cl(t,r),!0),{...r,skipEscape:!0})):typeof t=="object"&&(t=qc(wO(t,!0),{...r,skipEscape:!0})),typeof e=="string"?(e=unescape(e),e=qc(wO(cl(e,r),!0),{...r,skipEscape:!0})):typeof e=="object"&&(e=qc(wO(e,!0),{...r,skipEscape:!0})),t.toLowerCase()===e.toLowerCase()}function qc(t,e){let r={host:t.host,scheme:t.scheme,userinfo:t.userinfo,port:t.port,path:t.path,query:t.query,nid:t.nid,nss:t.nss,uuid:t.uuid,fragment:t.fragment,reference:t.reference,resourceName:t.resourceName,secure:t.secure,error:""},n=Object.assign({},e),o=[],i=Lle(n.scheme||r.scheme);i&&i.serialize&&i.serialize(r,n),r.path!==void 0&&(n.skipEscape?r.path=unescape(r.path):(r.path=escape(r.path),r.scheme!==void 0&&(r.path=r.path.split("%3A").join(":")))),n.reference!=="suffix"&&r.scheme&&o.push(r.scheme,":");let s=tit(r);if(s!==void 0&&(n.reference!=="suffix"&&o.push("//"),o.push(s),r.path&&r.path[0]!=="/"&&o.push("/")),r.path!==void 0){let a=r.path;!n.absolutePath&&(!i||!i.absolutePath)&&(a=xT(a)),s===void 0&&a[0]==="/"&&a[1]==="/"&&(a="/%2F"+a.slice(2)),o.push(a)}return r.query!==void 0&&o.push("?",r.query),r.fragment!==void 0&&o.push("#",r.fragment),o.join("")}var cit=/^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;function cl(t,e){let r=Object.assign({},e),n={scheme:void 0,userinfo:void 0,host:"",port:void 0,path:"",query:void 0,fragment:void 0},o=!1;r.reference==="suffix"&&(r.scheme?t=r.scheme+":"+t:t="//"+t);let i=t.match(cit);if(i){if(n.scheme=i[1],n.userinfo=i[3],n.host=i[4],n.port=parseInt(i[5],10),n.path=i[6]||"",n.query=i[7],n.fragment=i[8],isNaN(n.port)&&(n.port=i[5]),n.host)if(rit(n.host)===!1){let c=eit(n.host);n.host=c.host.toLowerCase(),o=c.isIPV6}else o=!0;n.scheme===void 0&&n.userinfo===void 0&&n.host===void 0&&n.port===void 0&&n.query===void 0&&!n.path?n.reference="same-document":n.scheme===void 0?n.reference="relative":n.fragment===void 0?n.reference="absolute":n.reference="uri",r.reference&&r.reference!=="suffix"&&r.reference!==n.reference&&(n.error=n.error||"URI is not a "+r.reference+" reference.");let s=Lle(r.scheme||n.scheme);if(!r.unicodeSupport&&(!s||!s.unicodeSupport)&&n.host&&(r.domainHost||s&&s.domainHost)&&o===!1&&nit(n.host))try{n.host=URL.domainToASCII(n.host.toLowerCase())}catch(a){n.error=n.error||"Host's domain name can not be converted to ASCII: "+a}(!s||s&&!s.skipNormalize)&&(t.indexOf("%")!==-1&&(n.scheme!==void 0&&(n.scheme=unescape(n.scheme)),n.host!==void 0&&(n.host=unescape(n.host))),n.path&&(n.path=escape(unescape(n.path))),n.fragment&&(n.fragment=encodeURI(decodeURIComponent(n.fragment)))),s&&s.parse&&s.parse(n,r)}else n.error=n.error||"URI can not be parsed.";return n}var o6={SCHEMES:oit,normalize:iit,resolve:sit,resolveComponent:Ule,equal:ait,serialize:qc,parse:cl};RO.exports=o6;RO.exports.default=o6;RO.exports.fastUri=o6});var zle=S(s6=>{"use strict";Object.defineProperty(s6,"__esModule",{value:!0});var jle=i6();jle.code='require("ajv/dist/runtime/uri").default';s6.default=jle});var Kle=S(Po=>{"use strict";Object.defineProperty(Po,"__esModule",{value:!0});Po.CodeGen=Po.Name=Po.nil=Po.stringify=Po.str=Po._=Po.KeywordCxt=void 0;var uit=TT();Object.defineProperty(Po,"KeywordCxt",{enumerable:!0,get:function(){return uit.KeywordCxt}});var Og=Ut();Object.defineProperty(Po,"_",{enumerable:!0,get:function(){return Og._}});Object.defineProperty(Po,"str",{enumerable:!0,get:function(){return Og.str}});Object.defineProperty(Po,"stringify",{enumerable:!0,get:function(){return Og.stringify}});Object.defineProperty(Po,"nil",{enumerable:!0,get:function(){return Og.nil}});Object.defineProperty(Po,"Name",{enumerable:!0,get:function(){return Og.Name}});Object.defineProperty(Po,"CodeGen",{enumerable:!0,get:function(){return Og.CodeGen}});var lit=EO(),Vle=bT(),pit=Mq(),AT=bO(),dit=Ut(),wT=ST(),PO=_T(),c6=cr(),Fle=Rle(),fit=zle(),Hle=(t,e)=>new RegExp(t,e);Hle.code="new RegExp";var mit=["removeAdditional","useDefaults","coerceTypes"],hit=new Set(["validate","serialize","parse","wrapper","root","schema","keyword","pattern","formats","validate$data","func","obj","Error"]),git={errorDataPath:"",format:"`validateFormats: false` can be used instead.",nullable:'"nullable" keyword is supported by default.',jsonPointers:"Deprecated jsPropertySyntax can be used instead.",extendRefs:"Deprecated ignoreKeywordsWithRef can be used instead.",missingRefs:"Pass empty schema with $id that should be ignored to ajv.addSchema.",processCode:"Use option `code: {process: (code, schemaEnv: object) => string}`",sourceCode:"Use option `code: {source: true}`",strictDefaults:"It is default now, see option `strict`.",strictKeywords:"It is default now, see option `strict`.",uniqueItems:'"uniqueItems" keyword is always validated.',unknownFormats:"Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).",cache:"Map is used as cache, schema object as key.",serialize:"Map is used as cache, schema object as key.",ajvErrors:"It is default now."},_it={ignoreKeywordsWithRef:"",jsPropertySyntax:"",unicode:'"minLength"/"maxLength" account for unicode characters by default.'},qle=200;function vit(t){var e,r,n,o,i,s,a,c,u,p,f,m,h,_,v,E,x,w,I,N,$,B,G,he,Q;let me=t.strict,J=(e=t.code)===null||e===void 0?void 0:e.optimize,Oe=J===!0||J===void 0?1:J||0,pt=(n=(r=t.code)===null||r===void 0?void 0:r.regExp)!==null&&n!==void 0?n:Hle,Ke=(o=t.uriResolver)!==null&&o!==void 0?o:fit.default;return{strictSchema:(s=(i=t.strictSchema)!==null&&i!==void 0?i:me)!==null&&s!==void 0?s:!0,strictNumbers:(c=(a=t.strictNumbers)!==null&&a!==void 0?a:me)!==null&&c!==void 0?c:!0,strictTypes:(p=(u=t.strictTypes)!==null&&u!==void 0?u:me)!==null&&p!==void 0?p:"log",strictTuples:(m=(f=t.strictTuples)!==null&&f!==void 0?f:me)!==null&&m!==void 0?m:"log",strictRequired:(_=(h=t.strictRequired)!==null&&h!==void 0?h:me)!==null&&_!==void 0?_:!1,code:t.code?{...t.code,optimize:Oe,regExp:pt}:{optimize:Oe,regExp:pt},loopRequired:(v=t.loopRequired)!==null&&v!==void 0?v:qle,loopEnum:(E=t.loopEnum)!==null&&E!==void 0?E:qle,meta:(x=t.meta)!==null&&x!==void 0?x:!0,messages:(w=t.messages)!==null&&w!==void 0?w:!0,inlineRefs:(I=t.inlineRefs)!==null&&I!==void 0?I:!0,schemaId:(N=t.schemaId)!==null&&N!==void 0?N:"$id",addUsedSchema:($=t.addUsedSchema)!==null&&$!==void 0?$:!0,validateSchema:(B=t.validateSchema)!==null&&B!==void 0?B:!0,validateFormats:(G=t.validateFormats)!==null&&G!==void 0?G:!0,unicodeRegExp:(he=t.unicodeRegExp)!==null&&he!==void 0?he:!0,int32range:(Q=t.int32range)!==null&&Q!==void 0?Q:!0,uriResolver:Ke}}var RT=class{constructor(e={}){this.schemas={},this.refs={},this.formats={},this._compilations=new Set,this._loading={},this._cache=new Map,e=this.opts={...e,...vit(e)};let{es5:r,lines:n}=this.opts.code;this.scope=new dit.ValueScope({scope:{},prefixes:hit,es5:r,lines:n}),this.logger=xit(e.logger);let o=e.validateFormats;e.validateFormats=!1,this.RULES=(0,pit.getRules)(),Ble.call(this,git,e,"NOT SUPPORTED"),Ble.call(this,_it,e,"DEPRECATED","warn"),this._metaOpts=Tit.call(this),e.formats&&yit.call(this),this._addVocabularies(),this._addDefaultMetaSchema(),e.keywords&&Eit.call(this,e.keywords),typeof e.meta=="object"&&this.addMetaSchema(e.meta),Sit.call(this),e.validateFormats=o}_addVocabularies(){this.addKeyword("$async")}_addDefaultMetaSchema(){let{$data:e,meta:r,schemaId:n}=this.opts,o=Fle;n==="id"&&(o={...Fle},o.id=o.$id,delete o.$id),r&&e&&this.addMetaSchema(o,o[n],!1)}defaultMeta(){let{meta:e,schemaId:r}=this.opts;return this.opts.defaultMeta=typeof e=="object"?e[r]||e:void 0}validate(e,r){let n;if(typeof e=="string"){if(n=this.getSchema(e),!n)throw new Error(`no schema with key or ref "${e}"`)}else n=this.compile(e);let o=n(r);return"$async"in n||(this.errors=n.errors),o}compile(e,r){let n=this._addSchema(e,r);return n.validate||this._compileSchemaEnv(n)}compileAsync(e,r){if(typeof this.opts.loadSchema!="function")throw new Error("options.loadSchema should be a function");let{loadSchema:n}=this.opts;return o.call(this,e,r);async function o(p,f){await i.call(this,p.$schema);let m=this._addSchema(p,f);return m.validate||s.call(this,m)}async function i(p){p&&!this.getSchema(p)&&await o.call(this,{$ref:p},!0)}async function s(p){try{return this._compileSchemaEnv(p)}catch(f){if(!(f instanceof Vle.default))throw f;return a.call(this,f),await c.call(this,f.missingSchema),s.call(this,p)}}function a({missingSchema:p,missingRef:f}){if(this.refs[p])throw new Error(`AnySchema ${p} is loaded but ${f} cannot be resolved`)}async function c(p){let f=await u.call(this,p);this.refs[p]||await i.call(this,f.$schema),this.refs[p]||this.addSchema(f,p,r)}async function u(p){let f=this._loading[p];if(f)return f;try{return await(this._loading[p]=n(p))}finally{delete this._loading[p]}}}addSchema(e,r,n,o=this.opts.validateSchema){if(Array.isArray(e)){for(let s of e)this.addSchema(s,void 0,n,o);return this}let i;if(typeof e=="object"){let{schemaId:s}=this.opts;if(i=e[s],i!==void 0&&typeof i!="string")throw new Error(`schema ${s} must be string`)}return r=(0,wT.normalizeId)(r||i),this._checkUnique(r),this.schemas[r]=this._addSchema(e,n,r,o,!0),this}addMetaSchema(e,r,n=this.opts.validateSchema){return this.addSchema(e,r,!0,n),this}validateSchema(e,r){if(typeof e=="boolean")return!0;let n;if(n=e.$schema,n!==void 0&&typeof n!="string")throw new Error("$schema must be a string");if(n=n||this.opts.defaultMeta||this.defaultMeta(),!n)return this.logger.warn("meta-schema not available"),this.errors=null,!0;let o=this.validate(n,e);if(!o&&r){let i="schema is invalid: "+this.errorsText();if(this.opts.validateSchema==="log")this.logger.error(i);else throw new Error(i)}return o}getSchema(e){let r;for(;typeof(r=Gle.call(this,e))=="string";)e=r;if(r===void 0){let{schemaId:n}=this.opts,o=new AT.SchemaEnv({schema:{},schemaId:n});if(r=AT.resolveSchema.call(this,o,e),!r)return;this.refs[e]=r}return r.validate||this._compileSchemaEnv(r)}removeSchema(e){if(e instanceof RegExp)return this._removeAllSchemas(this.schemas,e),this._removeAllSchemas(this.refs,e),this;switch(typeof e){case"undefined":return this._removeAllSchemas(this.schemas),this._removeAllSchemas(this.refs),this._cache.clear(),this;case"string":{let r=Gle.call(this,e);return typeof r=="object"&&this._cache.delete(r.schema),delete this.schemas[e],delete this.refs[e],this}case"object":{let r=e;this._cache.delete(r);let n=e[this.opts.schemaId];return n&&(n=(0,wT.normalizeId)(n),delete this.schemas[n],delete this.refs[n]),this}default:throw new Error("ajv.removeSchema: invalid parameter")}}addVocabulary(e){for(let r of e)this.addKeyword(r);return this}addKeyword(e,r){let n;if(typeof e=="string")n=e,typeof r=="object"&&(this.logger.warn("these parameters are deprecated, see docs for addKeyword"),r.keyword=n);else if(typeof e=="object"&&r===void 0){if(r=e,n=r.keyword,Array.isArray(n)&&!n.length)throw new Error("addKeywords: keyword must be string or non-empty array")}else throw new Error("invalid addKeywords parameters");if(wit.call(this,n,r),!r)return(0,c6.eachItem)(n,i=>a6.call(this,i)),this;Pit.call(this,r);let o={...r,type:(0,PO.getJSONTypes)(r.type),schemaType:(0,PO.getJSONTypes)(r.schemaType)};return(0,c6.eachItem)(n,o.type.length===0?i=>a6.call(this,i,o):i=>o.type.forEach(s=>a6.call(this,i,o,s))),this}getKeyword(e){let r=this.RULES.all[e];return typeof r=="object"?r.definition:!!r}removeKeyword(e){let{RULES:r}=this;delete r.keywords[e],delete r.all[e];for(let n of r.rules){let o=n.rules.findIndex(i=>i.keyword===e);o>=0&&n.rules.splice(o,1)}return this}addFormat(e,r){return typeof r=="string"&&(r=new RegExp(r)),this.formats[e]=r,this}errorsText(e=this.errors,{separator:r=", ",dataVar:n="data"}={}){return!e||e.length===0?"No errors":e.map(o=>`${n}${o.instancePath} ${o.message}`).reduce((o,i)=>o+r+i)}$dataMetaSchema(e,r){let n=this.RULES.all;e=JSON.parse(JSON.stringify(e));for(let o of r){let i=o.split("/").slice(1),s=e;for(let a of i)s=s[a];for(let a in n){let c=n[a];if(typeof c!="object")continue;let{$data:u}=c.definition,p=s[a];u&&p&&(s[a]=Wle(p))}}return e}_removeAllSchemas(e,r){for(let n in e){let o=e[n];(!r||r.test(n))&&(typeof o=="string"?delete e[n]:o&&!o.meta&&(this._cache.delete(o.schema),delete e[n]))}}_addSchema(e,r,n,o=this.opts.validateSchema,i=this.opts.addUsedSchema){let s,{schemaId:a}=this.opts;if(typeof e=="object")s=e[a];else{if(this.opts.jtd)throw new Error("schema must be object");if(typeof e!="boolean")throw new Error("schema must be object or boolean")}let c=this._cache.get(e);if(c!==void 0)return c;n=(0,wT.normalizeId)(s||n);let u=wT.getSchemaRefs.call(this,e,n);return c=new AT.SchemaEnv({schema:e,schemaId:a,meta:r,baseId:n,localRefs:u}),this._cache.set(c.schema,c),i&&!n.startsWith("#")&&(n&&this._checkUnique(n),this.refs[n]=c),o&&this.validateSchema(e,!0),c}_checkUnique(e){if(this.schemas[e]||this.refs[e])throw new Error(`schema with key or id "${e}" already exists`)}_compileSchemaEnv(e){if(e.meta?this._compileMetaSchema(e):AT.compileSchema.call(this,e),!e.validate)throw new Error("ajv implementation error");return e.validate}_compileMetaSchema(e){let r=this.opts;this.opts=this._metaOpts;try{AT.compileSchema.call(this,e)}finally{this.opts=r}}};RT.ValidationError=lit.default;RT.MissingRefError=Vle.default;Po.default=RT;function Ble(t,e,r,n="error"){for(let o in t){let i=o;i in e&&this.logger[n](`${r}: option ${o}. ${t[i]}`)}}function Gle(t){return t=(0,wT.normalizeId)(t),this.schemas[t]||this.refs[t]}function Sit(){let t=this.opts.schemas;if(t)if(Array.isArray(t))this.addSchema(t);else for(let e in t)this.addSchema(t[e],e)}function yit(){for(let t in this.opts.formats){let e=this.opts.formats[t];e&&this.addFormat(t,e)}}function Eit(t){if(Array.isArray(t)){this.addVocabulary(t);return}this.logger.warn("keywords option as map is deprecated, pass array");for(let e in t){let r=t[e];r.keyword||(r.keyword=e),this.addKeyword(r)}}function Tit(){let t={...this.opts};for(let e of mit)delete t[e];return t}var bit={log(){},warn(){},error(){}};function xit(t){if(t===!1)return bit;if(t===void 0)return console;if(t.log&&t.warn&&t.error)return t;throw new Error("logger must implement log, warn and error methods")}var Ait=/^[a-z_$][a-z0-9_$:-]*$/i;function wit(t,e){let{RULES:r}=this;if((0,c6.eachItem)(t,n=>{if(r.keywords[n])throw new Error(`Keyword ${n} is already defined`);if(!Ait.test(n))throw new Error(`Keyword ${n} has invalid name`)}),!!e&&e.$data&&!("code"in e||"validate"in e))throw new Error('$data keyword must have "code" or "validate" function')}function a6(t,e,r){var n;let o=e?.post;if(r&&o)throw new Error('keyword with "post" flag cannot have "type"');let{RULES:i}=this,s=o?i.post:i.rules.find(({type:c})=>c===r);if(s||(s={type:r,rules:[]},i.rules.push(s)),i.keywords[t]=!0,!e)return;let a={keyword:t,definition:{...e,type:(0,PO.getJSONTypes)(e.type),schemaType:(0,PO.getJSONTypes)(e.schemaType)}};e.before?Rit.call(this,s,a,e.before):s.rules.push(a),i.all[t]=a,(n=e.implements)===null||n===void 0||n.forEach(c=>this.addKeyword(c))}function Rit(t,e,r){let n=t.rules.findIndex(o=>o.keyword===r);n>=0?t.rules.splice(n,0,e):(t.rules.push(e),this.logger.warn(`rule ${r} is not defined`))}function Pit(t){let{metaSchema:e}=t;e!==void 0&&(t.$data&&this.opts.$data&&(e=Wle(e)),t.validateSchema=this.compile(e,!0))}var Iit={$ref:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#"};function Wle(t){return{anyOf:[t,Iit]}}});var Zle=S(u6=>{"use strict";Object.defineProperty(u6,"__esModule",{value:!0});var Oit={keyword:"id",code(){throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID')}};u6.default=Oit});var Qle=S(jf=>{"use strict";Object.defineProperty(jf,"__esModule",{value:!0});jf.callRef=jf.getValidate=void 0;var Nit=bT(),Yle=ta(),Vi=Ut(),Ng=sl(),Jle=bO(),IO=cr(),Cit={keyword:"$ref",schemaType:"string",code(t){let{gen:e,schema:r,it:n}=t,{baseId:o,schemaEnv:i,validateName:s,opts:a,self:c}=n,{root:u}=i;if((r==="#"||r==="#/")&&o===u.baseId)return f();let p=Jle.resolveRef.call(c,u,o,r);if(p===void 0)throw new Nit.default(n.opts.uriResolver,o,r);if(p instanceof Jle.SchemaEnv)return m(p);return h(p);function f(){if(i===u)return OO(t,s,i,i.$async);let _=e.scopeValue("root",{ref:u});return OO(t,(0,Vi._)`${_}.validate`,u,u.$async)}function m(_){let v=Xle(t,_);OO(t,v,_,_.$async)}function h(_){let v=e.scopeValue("schema",a.code.source===!0?{ref:_,code:(0,Vi.stringify)(_)}:{ref:_}),E=e.name("valid"),x=t.subschema({schema:_,dataTypes:[],schemaPath:Vi.nil,topSchemaRef:v,errSchemaPath:r},E);t.mergeEvaluated(x),t.ok(E)}}};function Xle(t,e){let{gen:r}=t;return e.validate?r.scopeValue("validate",{ref:e.validate}):(0,Vi._)`${r.scopeValue("wrapper",{ref:e})}.validate`}jf.getValidate=Xle;function OO(t,e,r,n){let{gen:o,it:i}=t,{allErrors:s,schemaEnv:a,opts:c}=i,u=c.passContext?Ng.default.this:Vi.nil;n?p():f();function p(){if(!a.$async)throw new Error("async schema referenced by sync schema");let _=o.let("valid");o.try(()=>{o.code((0,Vi._)`await ${(0,Yle.callValidateCode)(t,e,u)}`),h(e),s||o.assign(_,!0)},v=>{o.if((0,Vi._)`!(${v} instanceof ${i.ValidationError})`,()=>o.throw(v)),m(v),s||o.assign(_,!1)}),t.ok(_)}function f(){t.result((0,Yle.callValidateCode)(t,e,u),()=>h(e),()=>m(e))}function m(_){let v=(0,Vi._)`${_}.errors`;o.assign(Ng.default.vErrors,(0,Vi._)`${Ng.default.vErrors} === null ? ${v} : ${Ng.default.vErrors}.concat(${v})`),o.assign(Ng.default.errors,(0,Vi._)`${Ng.default.vErrors}.length`)}function h(_){var v;if(!i.opts.unevaluated)return;let E=(v=r?.validate)===null||v===void 0?void 0:v.evaluated;if(i.props!==!0)if(E&&!E.dynamicProps)E.props!==void 0&&(i.props=IO.mergeEvaluated.props(o,E.props,i.props));else{let x=o.var("props",(0,Vi._)`${_}.evaluated.props`);i.props=IO.mergeEvaluated.props(o,x,i.props,Vi.Name)}if(i.items!==!0)if(E&&!E.dynamicItems)E.items!==void 0&&(i.items=IO.mergeEvaluated.items(o,E.items,i.items));else{let x=o.var("items",(0,Vi._)`${_}.evaluated.items`);i.items=IO.mergeEvaluated.items(o,x,i.items,Vi.Name)}}}jf.callRef=OO;jf.default=Cit});var epe=S(l6=>{"use strict";Object.defineProperty(l6,"__esModule",{value:!0});var $it=Zle(),kit=Qle(),Mit=["$schema","$id","$defs","$vocabulary",{keyword:"$comment"},"definitions",$it.default,kit.default];l6.default=Mit});var tpe=S(p6=>{"use strict";Object.defineProperty(p6,"__esModule",{value:!0});var NO=Ut(),Mp=NO.operators,CO={maximum:{okStr:"<=",ok:Mp.LTE,fail:Mp.GT},minimum:{okStr:">=",ok:Mp.GTE,fail:Mp.LT},exclusiveMaximum:{okStr:"<",ok:Mp.LT,fail:Mp.GTE},exclusiveMinimum:{okStr:">",ok:Mp.GT,fail:Mp.LTE}},Dit={message:({keyword:t,schemaCode:e})=>(0,NO.str)`must be ${CO[t].okStr} ${e}`,params:({keyword:t,schemaCode:e})=>(0,NO._)`{comparison: ${CO[t].okStr}, limit: ${e}}`},Lit={keyword:Object.keys(CO),type:"number",schemaType:"number",$data:!0,error:Dit,code(t){let{keyword:e,data:r,schemaCode:n}=t;t.fail$data((0,NO._)`${r} ${CO[e].fail} ${n} || isNaN(${r})`)}};p6.default=Lit});var rpe=S(d6=>{"use strict";Object.defineProperty(d6,"__esModule",{value:!0});var PT=Ut(),Uit={message:({schemaCode:t})=>(0,PT.str)`must be multiple of ${t}`,params:({schemaCode:t})=>(0,PT._)`{multipleOf: ${t}}`},jit={keyword:"multipleOf",type:"number",schemaType:"number",$data:!0,error:Uit,code(t){let{gen:e,data:r,schemaCode:n,it:o}=t,i=o.opts.multipleOfPrecision,s=e.let("res"),a=i?(0,PT._)`Math.abs(Math.round(${s}) - ${s}) > 1e-${i}`:(0,PT._)`${s} !== parseInt(${s})`;t.fail$data((0,PT._)`(${n} === 0 || (${s} = ${r}/${n}, ${a}))`)}};d6.default=jit});var ope=S(f6=>{"use strict";Object.defineProperty(f6,"__esModule",{value:!0});function npe(t){let e=t.length,r=0,n=0,o;for(;n=55296&&o<=56319&&n{"use strict";Object.defineProperty(m6,"__esModule",{value:!0});var zf=Ut(),zit=cr(),Fit=ope(),qit={message({keyword:t,schemaCode:e}){let r=t==="maxLength"?"more":"fewer";return(0,zf.str)`must NOT have ${r} than ${e} characters`},params:({schemaCode:t})=>(0,zf._)`{limit: ${t}}`},Bit={keyword:["maxLength","minLength"],type:"string",schemaType:"number",$data:!0,error:qit,code(t){let{keyword:e,data:r,schemaCode:n,it:o}=t,i=e==="maxLength"?zf.operators.GT:zf.operators.LT,s=o.opts.unicode===!1?(0,zf._)`${r}.length`:(0,zf._)`${(0,zit.useFunc)(t.gen,Fit.default)}(${r})`;t.fail$data((0,zf._)`${s} ${i} ${n}`)}};m6.default=Bit});var spe=S(h6=>{"use strict";Object.defineProperty(h6,"__esModule",{value:!0});var Git=ta(),$O=Ut(),Vit={message:({schemaCode:t})=>(0,$O.str)`must match pattern "${t}"`,params:({schemaCode:t})=>(0,$O._)`{pattern: ${t}}`},Hit={keyword:"pattern",type:"string",schemaType:"string",$data:!0,error:Vit,code(t){let{data:e,$data:r,schema:n,schemaCode:o,it:i}=t,s=i.opts.unicodeRegExp?"u":"",a=r?(0,$O._)`(new RegExp(${o}, ${s}))`:(0,Git.usePattern)(t,n);t.fail$data((0,$O._)`!${a}.test(${e})`)}};h6.default=Hit});var ape=S(g6=>{"use strict";Object.defineProperty(g6,"__esModule",{value:!0});var IT=Ut(),Wit={message({keyword:t,schemaCode:e}){let r=t==="maxProperties"?"more":"fewer";return(0,IT.str)`must NOT have ${r} than ${e} properties`},params:({schemaCode:t})=>(0,IT._)`{limit: ${t}}`},Kit={keyword:["maxProperties","minProperties"],type:"object",schemaType:"number",$data:!0,error:Wit,code(t){let{keyword:e,data:r,schemaCode:n}=t,o=e==="maxProperties"?IT.operators.GT:IT.operators.LT;t.fail$data((0,IT._)`Object.keys(${r}).length ${o} ${n}`)}};g6.default=Kit});var cpe=S(_6=>{"use strict";Object.defineProperty(_6,"__esModule",{value:!0});var OT=ta(),NT=Ut(),Zit=cr(),Yit={message:({params:{missingProperty:t}})=>(0,NT.str)`must have required property '${t}'`,params:({params:{missingProperty:t}})=>(0,NT._)`{missingProperty: ${t}}`},Jit={keyword:"required",type:"object",schemaType:"array",$data:!0,error:Yit,code(t){let{gen:e,schema:r,schemaCode:n,data:o,$data:i,it:s}=t,{opts:a}=s;if(!i&&r.length===0)return;let c=r.length>=a.loopRequired;if(s.allErrors?u():p(),a.strictRequired){let h=t.parentSchema.properties,{definedProperties:_}=t.it;for(let v of r)if(h?.[v]===void 0&&!_.has(v)){let E=s.schemaEnv.baseId+s.errSchemaPath,x=`required property "${v}" is not defined at "${E}" (strictRequired)`;(0,Zit.checkStrictMode)(s,x,s.opts.strictRequired)}}function u(){if(c||i)t.block$data(NT.nil,f);else for(let h of r)(0,OT.checkReportMissingProp)(t,h)}function p(){let h=e.let("missing");if(c||i){let _=e.let("valid",!0);t.block$data(_,()=>m(h,_)),t.ok(_)}else e.if((0,OT.checkMissingProp)(t,r,h)),(0,OT.reportMissingProp)(t,h),e.else()}function f(){e.forOf("prop",n,h=>{t.setParams({missingProperty:h}),e.if((0,OT.noPropertyInData)(e,o,h,a.ownProperties),()=>t.error())})}function m(h,_){t.setParams({missingProperty:h}),e.forOf(h,n,()=>{e.assign(_,(0,OT.propertyInData)(e,o,h,a.ownProperties)),e.if((0,NT.not)(_),()=>{t.error(),e.break()})},NT.nil)}}};_6.default=Jit});var upe=S(v6=>{"use strict";Object.defineProperty(v6,"__esModule",{value:!0});var CT=Ut(),Xit={message({keyword:t,schemaCode:e}){let r=t==="maxItems"?"more":"fewer";return(0,CT.str)`must NOT have ${r} than ${e} items`},params:({schemaCode:t})=>(0,CT._)`{limit: ${t}}`},Qit={keyword:["maxItems","minItems"],type:"array",schemaType:"number",$data:!0,error:Xit,code(t){let{keyword:e,data:r,schemaCode:n}=t,o=e==="maxItems"?CT.operators.GT:CT.operators.LT;t.fail$data((0,CT._)`${r}.length ${o} ${n}`)}};v6.default=Qit});var kO=S(S6=>{"use strict";Object.defineProperty(S6,"__esModule",{value:!0});var lpe=vT();lpe.code='require("ajv/dist/runtime/equal").default';S6.default=lpe});var ppe=S(E6=>{"use strict";Object.defineProperty(E6,"__esModule",{value:!0});var y6=_T(),Io=Ut(),est=cr(),tst=kO(),rst={message:({params:{i:t,j:e}})=>(0,Io.str)`must NOT have duplicate items (items ## ${e} and ${t} are identical)`,params:({params:{i:t,j:e}})=>(0,Io._)`{i: ${t}, j: ${e}}`},nst={keyword:"uniqueItems",type:"array",schemaType:"boolean",$data:!0,error:rst,code(t){let{gen:e,data:r,$data:n,schema:o,parentSchema:i,schemaCode:s,it:a}=t;if(!n&&!o)return;let c=e.let("valid"),u=i.items?(0,y6.getSchemaTypes)(i.items):[];t.block$data(c,p,(0,Io._)`${s} === false`),t.ok(c);function p(){let _=e.let("i",(0,Io._)`${r}.length`),v=e.let("j");t.setParams({i:_,j:v}),e.assign(c,!0),e.if((0,Io._)`${_} > 1`,()=>(f()?m:h)(_,v))}function f(){return u.length>0&&!u.some(_=>_==="object"||_==="array")}function m(_,v){let E=e.name("item"),x=(0,y6.checkDataTypes)(u,E,a.opts.strictNumbers,y6.DataType.Wrong),w=e.const("indices",(0,Io._)`{}`);e.for((0,Io._)`;${_}--;`,()=>{e.let(E,(0,Io._)`${r}[${_}]`),e.if(x,(0,Io._)`continue`),u.length>1&&e.if((0,Io._)`typeof ${E} == "string"`,(0,Io._)`${E} += "_"`),e.if((0,Io._)`typeof ${w}[${E}] == "number"`,()=>{e.assign(v,(0,Io._)`${w}[${E}]`),t.error(),e.assign(c,!1).break()}).code((0,Io._)`${w}[${E}] = ${_}`)})}function h(_,v){let E=(0,est.useFunc)(e,tst.default),x=e.name("outer");e.label(x).for((0,Io._)`;${_}--;`,()=>e.for((0,Io._)`${v} = ${_}; ${v}--;`,()=>e.if((0,Io._)`${E}(${r}[${_}], ${r}[${v}])`,()=>{t.error(),e.assign(c,!1).break(x)})))}}};E6.default=nst});var dpe=S(b6=>{"use strict";Object.defineProperty(b6,"__esModule",{value:!0});var T6=Ut(),ost=cr(),ist=kO(),sst={message:"must be equal to constant",params:({schemaCode:t})=>(0,T6._)`{allowedValue: ${t}}`},ast={keyword:"const",$data:!0,error:sst,code(t){let{gen:e,data:r,$data:n,schemaCode:o,schema:i}=t;n||i&&typeof i=="object"?t.fail$data((0,T6._)`!${(0,ost.useFunc)(e,ist.default)}(${r}, ${o})`):t.fail((0,T6._)`${i} !== ${r}`)}};b6.default=ast});var fpe=S(x6=>{"use strict";Object.defineProperty(x6,"__esModule",{value:!0});var $T=Ut(),cst=cr(),ust=kO(),lst={message:"must be equal to one of the allowed values",params:({schemaCode:t})=>(0,$T._)`{allowedValues: ${t}}`},pst={keyword:"enum",schemaType:"array",$data:!0,error:lst,code(t){let{gen:e,data:r,$data:n,schema:o,schemaCode:i,it:s}=t;if(!n&&o.length===0)throw new Error("enum must have non-empty array");let a=o.length>=s.opts.loopEnum,c,u=()=>c??(c=(0,cst.useFunc)(e,ust.default)),p;if(a||n)p=e.let("valid"),t.block$data(p,f);else{if(!Array.isArray(o))throw new Error("ajv implementation error");let h=e.const("vSchema",i);p=(0,$T.or)(...o.map((_,v)=>m(h,v)))}t.pass(p);function f(){e.assign(p,!1),e.forOf("v",i,h=>e.if((0,$T._)`${u()}(${r}, ${h})`,()=>e.assign(p,!0).break()))}function m(h,_){let v=o[_];return typeof v=="object"&&v!==null?(0,$T._)`${u()}(${r}, ${h}[${_}])`:(0,$T._)`${r} === ${v}`}}};x6.default=pst});var mpe=S(A6=>{"use strict";Object.defineProperty(A6,"__esModule",{value:!0});var dst=tpe(),fst=rpe(),mst=ipe(),hst=spe(),gst=ape(),_st=cpe(),vst=upe(),Sst=ppe(),yst=dpe(),Est=fpe(),Tst=[dst.default,fst.default,mst.default,hst.default,gst.default,_st.default,vst.default,Sst.default,{keyword:"type",schemaType:["string","array"]},{keyword:"nullable",schemaType:"boolean"},yst.default,Est.default];A6.default=Tst});var R6=S(kT=>{"use strict";Object.defineProperty(kT,"__esModule",{value:!0});kT.validateAdditionalItems=void 0;var Ff=Ut(),w6=cr(),bst={message:({params:{len:t}})=>(0,Ff.str)`must NOT have more than ${t} items`,params:({params:{len:t}})=>(0,Ff._)`{limit: ${t}}`},xst={keyword:"additionalItems",type:"array",schemaType:["boolean","object"],before:"uniqueItems",error:bst,code(t){let{parentSchema:e,it:r}=t,{items:n}=e;if(!Array.isArray(n)){(0,w6.checkStrictMode)(r,'"additionalItems" is ignored when "items" is not an array of schemas');return}hpe(t,n)}};function hpe(t,e){let{gen:r,schema:n,data:o,keyword:i,it:s}=t;s.items=!0;let a=r.const("len",(0,Ff._)`${o}.length`);if(n===!1)t.setParams({len:e.length}),t.pass((0,Ff._)`${a} <= ${e.length}`);else if(typeof n=="object"&&!(0,w6.alwaysValidSchema)(s,n)){let u=r.var("valid",(0,Ff._)`${a} <= ${e.length}`);r.if((0,Ff.not)(u),()=>c(u)),t.ok(u)}function c(u){r.forRange("i",e.length,a,p=>{t.subschema({keyword:i,dataProp:p,dataPropType:w6.Type.Num},u),s.allErrors||r.if((0,Ff.not)(u),()=>r.break())})}}kT.validateAdditionalItems=hpe;kT.default=xst});var P6=S(MT=>{"use strict";Object.defineProperty(MT,"__esModule",{value:!0});MT.validateTuple=void 0;var gpe=Ut(),MO=cr(),Ast=ta(),wst={keyword:"items",type:"array",schemaType:["object","array","boolean"],before:"uniqueItems",code(t){let{schema:e,it:r}=t;if(Array.isArray(e))return _pe(t,"additionalItems",e);r.items=!0,!(0,MO.alwaysValidSchema)(r,e)&&t.ok((0,Ast.validateArray)(t))}};function _pe(t,e,r=t.schema){let{gen:n,parentSchema:o,data:i,keyword:s,it:a}=t;p(o),a.opts.unevaluated&&r.length&&a.items!==!0&&(a.items=MO.mergeEvaluated.items(n,r.length,a.items));let c=n.name("valid"),u=n.const("len",(0,gpe._)`${i}.length`);r.forEach((f,m)=>{(0,MO.alwaysValidSchema)(a,f)||(n.if((0,gpe._)`${u} > ${m}`,()=>t.subschema({keyword:s,schemaProp:m,dataProp:m},c)),t.ok(c))});function p(f){let{opts:m,errSchemaPath:h}=a,_=r.length,v=_===f.minItems&&(_===f.maxItems||f[e]===!1);if(m.strictTuples&&!v){let E=`"${s}" is ${_}-tuple, but minItems or maxItems/${e} are not specified or different at path "${h}"`;(0,MO.checkStrictMode)(a,E,m.strictTuples)}}}MT.validateTuple=_pe;MT.default=wst});var vpe=S(I6=>{"use strict";Object.defineProperty(I6,"__esModule",{value:!0});var Rst=P6(),Pst={keyword:"prefixItems",type:"array",schemaType:["array"],before:"uniqueItems",code:t=>(0,Rst.validateTuple)(t,"items")};I6.default=Pst});var ype=S(O6=>{"use strict";Object.defineProperty(O6,"__esModule",{value:!0});var Spe=Ut(),Ist=cr(),Ost=ta(),Nst=R6(),Cst={message:({params:{len:t}})=>(0,Spe.str)`must NOT have more than ${t} items`,params:({params:{len:t}})=>(0,Spe._)`{limit: ${t}}`},$st={keyword:"items",type:"array",schemaType:["object","boolean"],before:"uniqueItems",error:Cst,code(t){let{schema:e,parentSchema:r,it:n}=t,{prefixItems:o}=r;n.items=!0,!(0,Ist.alwaysValidSchema)(n,e)&&(o?(0,Nst.validateAdditionalItems)(t,o):t.ok((0,Ost.validateArray)(t)))}};O6.default=$st});var Epe=S(N6=>{"use strict";Object.defineProperty(N6,"__esModule",{value:!0});var na=Ut(),DO=cr(),kst={message:({params:{min:t,max:e}})=>e===void 0?(0,na.str)`must contain at least ${t} valid item(s)`:(0,na.str)`must contain at least ${t} and no more than ${e} valid item(s)`,params:({params:{min:t,max:e}})=>e===void 0?(0,na._)`{minContains: ${t}}`:(0,na._)`{minContains: ${t}, maxContains: ${e}}`},Mst={keyword:"contains",type:"array",schemaType:["object","boolean"],before:"uniqueItems",trackErrors:!0,error:kst,code(t){let{gen:e,schema:r,parentSchema:n,data:o,it:i}=t,s,a,{minContains:c,maxContains:u}=n;i.opts.next?(s=c===void 0?1:c,a=u):s=1;let p=e.const("len",(0,na._)`${o}.length`);if(t.setParams({min:s,max:a}),a===void 0&&s===0){(0,DO.checkStrictMode)(i,'"minContains" == 0 without "maxContains": "contains" keyword ignored');return}if(a!==void 0&&s>a){(0,DO.checkStrictMode)(i,'"minContains" > "maxContains" is always invalid'),t.fail();return}if((0,DO.alwaysValidSchema)(i,r)){let v=(0,na._)`${p} >= ${s}`;a!==void 0&&(v=(0,na._)`${v} && ${p} <= ${a}`),t.pass(v);return}i.items=!0;let f=e.name("valid");a===void 0&&s===1?h(f,()=>e.if(f,()=>e.break())):s===0?(e.let(f,!0),a!==void 0&&e.if((0,na._)`${o}.length > 0`,m)):(e.let(f,!1),m()),t.result(f,()=>t.reset());function m(){let v=e.name("_valid"),E=e.let("count",0);h(v,()=>e.if(v,()=>_(E)))}function h(v,E){e.forRange("i",0,p,x=>{t.subschema({keyword:"contains",dataProp:x,dataPropType:DO.Type.Num,compositeRule:!0},v),E()})}function _(v){e.code((0,na._)`${v}++`),a===void 0?e.if((0,na._)`${v} >= ${s}`,()=>e.assign(f,!0).break()):(e.if((0,na._)`${v} > ${a}`,()=>e.assign(f,!1).break()),s===1?e.assign(f,!0):e.if((0,na._)`${v} >= ${s}`,()=>e.assign(f,!0)))}}};N6.default=Mst});var xpe=S(Bc=>{"use strict";Object.defineProperty(Bc,"__esModule",{value:!0});Bc.validateSchemaDeps=Bc.validatePropertyDeps=Bc.error=void 0;var C6=Ut(),Dst=cr(),DT=ta();Bc.error={message:({params:{property:t,depsCount:e,deps:r}})=>{let n=e===1?"property":"properties";return(0,C6.str)`must have ${n} ${r} when property ${t} is present`},params:({params:{property:t,depsCount:e,deps:r,missingProperty:n}})=>(0,C6._)`{property: ${t}, - missingProperty: ${n}, - depsCount: ${e}, - deps: ${r}}`};var Lst={keyword:"dependencies",type:"object",schemaType:"object",error:Bc.error,code(t){let[e,r]=Ust(t);Tpe(t,e),bpe(t,r)}};function Ust({schema:t}){let e={},r={};for(let n in t){if(n==="__proto__")continue;let o=Array.isArray(t[n])?e:r;o[n]=t[n]}return[e,r]}function Tpe(t,e=t.schema){let{gen:r,data:n,it:o}=t;if(Object.keys(e).length===0)return;let i=r.let("missing");for(let s in e){let a=e[s];if(a.length===0)continue;let c=(0,DT.propertyInData)(r,n,s,o.opts.ownProperties);t.setParams({property:s,depsCount:a.length,deps:a.join(", ")}),o.allErrors?r.if(c,()=>{for(let u of a)(0,DT.checkReportMissingProp)(t,u)}):(r.if((0,C6._)`${c} && (${(0,DT.checkMissingProp)(t,a,i)})`),(0,DT.reportMissingProp)(t,i),r.else())}}Bc.validatePropertyDeps=Tpe;function bpe(t,e=t.schema){let{gen:r,data:n,keyword:o,it:i}=t,s=r.name("valid");for(let a in e)(0,Dst.alwaysValidSchema)(i,e[a])||(r.if((0,DT.propertyInData)(r,n,a,i.opts.ownProperties),()=>{let c=t.subschema({keyword:o,schemaProp:a},s);t.mergeValidEvaluated(c,s)},()=>r.var(s,!0)),t.ok(s))}Bc.validateSchemaDeps=bpe;Bc.default=Lst});var wpe=S($6=>{"use strict";Object.defineProperty($6,"__esModule",{value:!0});var Ape=Ut(),jst=cr(),zst={message:"property name must be valid",params:({params:t})=>(0,Ape._)`{propertyName: ${t.propertyName}}`},Fst={keyword:"propertyNames",type:"object",schemaType:["object","boolean"],error:zst,code(t){let{gen:e,schema:r,data:n,it:o}=t;if((0,jst.alwaysValidSchema)(o,r))return;let i=e.name("valid");e.forIn("key",n,s=>{t.setParams({propertyName:s}),t.subschema({keyword:"propertyNames",data:s,dataTypes:["string"],propertyName:s,compositeRule:!0},i),e.if((0,Ape.not)(i),()=>{t.error(!0),o.allErrors||e.break()})}),t.ok(i)}};$6.default=Fst});var M6=S(k6=>{"use strict";Object.defineProperty(k6,"__esModule",{value:!0});var LO=ta(),ic=Ut(),qst=sl(),UO=cr(),Bst={message:"must NOT have additional properties",params:({params:t})=>(0,ic._)`{additionalProperty: ${t.additionalProperty}}`},Gst={keyword:"additionalProperties",type:["object"],schemaType:["boolean","object"],allowUndefined:!0,trackErrors:!0,error:Bst,code(t){let{gen:e,schema:r,parentSchema:n,data:o,errsCount:i,it:s}=t;if(!i)throw new Error("ajv implementation error");let{allErrors:a,opts:c}=s;if(s.props=!0,c.removeAdditional!=="all"&&(0,UO.alwaysValidSchema)(s,r))return;let u=(0,LO.allSchemaProperties)(n.properties),p=(0,LO.allSchemaProperties)(n.patternProperties);f(),t.ok((0,ic._)`${i} === ${qst.default.errors}`);function f(){e.forIn("key",o,E=>{!u.length&&!p.length?_(E):e.if(m(E),()=>_(E))})}function m(E){let x;if(u.length>8){let w=(0,UO.schemaRefOrVal)(s,n.properties,"properties");x=(0,LO.isOwnProperty)(e,w,E)}else u.length?x=(0,ic.or)(...u.map(w=>(0,ic._)`${E} === ${w}`)):x=ic.nil;return p.length&&(x=(0,ic.or)(x,...p.map(w=>(0,ic._)`${(0,LO.usePattern)(t,w)}.test(${E})`))),(0,ic.not)(x)}function h(E){e.code((0,ic._)`delete ${o}[${E}]`)}function _(E){if(c.removeAdditional==="all"||c.removeAdditional&&r===!1){h(E);return}if(r===!1){t.setParams({additionalProperty:E}),t.error(),a||e.break();return}if(typeof r=="object"&&!(0,UO.alwaysValidSchema)(s,r)){let x=e.name("valid");c.removeAdditional==="failing"?(v(E,x,!1),e.if((0,ic.not)(x),()=>{t.reset(),h(E)})):(v(E,x),a||e.if((0,ic.not)(x),()=>e.break()))}}function v(E,x,w){let I={keyword:"additionalProperties",dataProp:E,dataPropType:UO.Type.Str};w===!1&&Object.assign(I,{compositeRule:!0,createErrors:!1,allErrors:!1}),t.subschema(I,x)}}};k6.default=Gst});var Ipe=S(L6=>{"use strict";Object.defineProperty(L6,"__esModule",{value:!0});var Vst=TT(),Rpe=ta(),D6=cr(),Ppe=M6(),Hst={keyword:"properties",type:"object",schemaType:"object",code(t){let{gen:e,schema:r,parentSchema:n,data:o,it:i}=t;i.opts.removeAdditional==="all"&&n.additionalProperties===void 0&&Ppe.default.code(new Vst.KeywordCxt(i,Ppe.default,"additionalProperties"));let s=(0,Rpe.allSchemaProperties)(r);for(let f of s)i.definedProperties.add(f);i.opts.unevaluated&&s.length&&i.props!==!0&&(i.props=D6.mergeEvaluated.props(e,(0,D6.toHash)(s),i.props));let a=s.filter(f=>!(0,D6.alwaysValidSchema)(i,r[f]));if(a.length===0)return;let c=e.name("valid");for(let f of a)u(f)?p(f):(e.if((0,Rpe.propertyInData)(e,o,f,i.opts.ownProperties)),p(f),i.allErrors||e.else().var(c,!0),e.endIf()),t.it.definedProperties.add(f),t.ok(c);function u(f){return i.opts.useDefaults&&!i.compositeRule&&r[f].default!==void 0}function p(f){t.subschema({keyword:"properties",schemaProp:f,dataProp:f},c)}}};L6.default=Hst});var $pe=S(U6=>{"use strict";Object.defineProperty(U6,"__esModule",{value:!0});var Ope=ta(),jO=Ut(),Npe=cr(),Cpe=cr(),Wst={keyword:"patternProperties",type:"object",schemaType:"object",code(t){let{gen:e,schema:r,data:n,parentSchema:o,it:i}=t,{opts:s}=i,a=(0,Ope.allSchemaProperties)(r),c=a.filter(v=>(0,Npe.alwaysValidSchema)(i,r[v]));if(a.length===0||c.length===a.length&&(!i.opts.unevaluated||i.props===!0))return;let u=s.strictSchema&&!s.allowMatchingProperties&&o.properties,p=e.name("valid");i.props!==!0&&!(i.props instanceof jO.Name)&&(i.props=(0,Cpe.evaluatedPropsToName)(e,i.props));let{props:f}=i;m();function m(){for(let v of a)u&&h(v),i.allErrors?_(v):(e.var(p,!0),_(v),e.if(p))}function h(v){for(let E in u)new RegExp(v).test(E)&&(0,Npe.checkStrictMode)(i,`property ${E} matches pattern ${v} (use allowMatchingProperties)`)}function _(v){e.forIn("key",n,E=>{e.if((0,jO._)`${(0,Ope.usePattern)(t,v)}.test(${E})`,()=>{let x=c.includes(v);x||t.subschema({keyword:"patternProperties",schemaProp:v,dataProp:E,dataPropType:Cpe.Type.Str},p),i.opts.unevaluated&&f!==!0?e.assign((0,jO._)`${f}[${E}]`,!0):!x&&!i.allErrors&&e.if((0,jO.not)(p),()=>e.break())})})}}};U6.default=Wst});var kpe=S(j6=>{"use strict";Object.defineProperty(j6,"__esModule",{value:!0});var Kst=cr(),Zst={keyword:"not",schemaType:["object","boolean"],trackErrors:!0,code(t){let{gen:e,schema:r,it:n}=t;if((0,Kst.alwaysValidSchema)(n,r)){t.fail();return}let o=e.name("valid");t.subschema({keyword:"not",compositeRule:!0,createErrors:!1,allErrors:!1},o),t.failResult(o,()=>t.reset(),()=>t.error())},error:{message:"must NOT be valid"}};j6.default=Zst});var Mpe=S(z6=>{"use strict";Object.defineProperty(z6,"__esModule",{value:!0});var Yst=ta(),Jst={keyword:"anyOf",schemaType:"array",trackErrors:!0,code:Yst.validateUnion,error:{message:"must match a schema in anyOf"}};z6.default=Jst});var Dpe=S(F6=>{"use strict";Object.defineProperty(F6,"__esModule",{value:!0});var zO=Ut(),Xst=cr(),Qst={message:"must match exactly one schema in oneOf",params:({params:t})=>(0,zO._)`{passingSchemas: ${t.passing}}`},eat={keyword:"oneOf",schemaType:"array",trackErrors:!0,error:Qst,code(t){let{gen:e,schema:r,parentSchema:n,it:o}=t;if(!Array.isArray(r))throw new Error("ajv implementation error");if(o.opts.discriminator&&n.discriminator)return;let i=r,s=e.let("valid",!1),a=e.let("passing",null),c=e.name("_valid");t.setParams({passing:a}),e.block(u),t.result(s,()=>t.reset(),()=>t.error(!0));function u(){i.forEach((p,f)=>{let m;(0,Xst.alwaysValidSchema)(o,p)?e.var(c,!0):m=t.subschema({keyword:"oneOf",schemaProp:f,compositeRule:!0},c),f>0&&e.if((0,zO._)`${c} && ${s}`).assign(s,!1).assign(a,(0,zO._)`[${a}, ${f}]`).else(),e.if(c,()=>{e.assign(s,!0),e.assign(a,f),m&&t.mergeEvaluated(m,zO.Name)})})}}};F6.default=eat});var Lpe=S(q6=>{"use strict";Object.defineProperty(q6,"__esModule",{value:!0});var tat=cr(),rat={keyword:"allOf",schemaType:"array",code(t){let{gen:e,schema:r,it:n}=t;if(!Array.isArray(r))throw new Error("ajv implementation error");let o=e.name("valid");r.forEach((i,s)=>{if((0,tat.alwaysValidSchema)(n,i))return;let a=t.subschema({keyword:"allOf",schemaProp:s},o);t.ok(o),t.mergeEvaluated(a)})}};q6.default=rat});var zpe=S(B6=>{"use strict";Object.defineProperty(B6,"__esModule",{value:!0});var FO=Ut(),jpe=cr(),nat={message:({params:t})=>(0,FO.str)`must match "${t.ifClause}" schema`,params:({params:t})=>(0,FO._)`{failingKeyword: ${t.ifClause}}`},oat={keyword:"if",schemaType:["object","boolean"],trackErrors:!0,error:nat,code(t){let{gen:e,parentSchema:r,it:n}=t;r.then===void 0&&r.else===void 0&&(0,jpe.checkStrictMode)(n,'"if" without "then" and "else" is ignored');let o=Upe(n,"then"),i=Upe(n,"else");if(!o&&!i)return;let s=e.let("valid",!0),a=e.name("_valid");if(c(),t.reset(),o&&i){let p=e.let("ifClause");t.setParams({ifClause:p}),e.if(a,u("then",p),u("else",p))}else o?e.if(a,u("then")):e.if((0,FO.not)(a),u("else"));t.pass(s,()=>t.error(!0));function c(){let p=t.subschema({keyword:"if",compositeRule:!0,createErrors:!1,allErrors:!1},a);t.mergeEvaluated(p)}function u(p,f){return()=>{let m=t.subschema({keyword:p},a);e.assign(s,a),t.mergeValidEvaluated(m,s),f?e.assign(f,(0,FO._)`${p}`):t.setParams({ifClause:p})}}}};function Upe(t,e){let r=t.schema[e];return r!==void 0&&!(0,jpe.alwaysValidSchema)(t,r)}B6.default=oat});var Fpe=S(G6=>{"use strict";Object.defineProperty(G6,"__esModule",{value:!0});var iat=cr(),sat={keyword:["then","else"],schemaType:["object","boolean"],code({keyword:t,parentSchema:e,it:r}){e.if===void 0&&(0,iat.checkStrictMode)(r,`"${t}" without "if" is ignored`)}};G6.default=sat});var qpe=S(V6=>{"use strict";Object.defineProperty(V6,"__esModule",{value:!0});var aat=R6(),cat=vpe(),uat=P6(),lat=ype(),pat=Epe(),dat=xpe(),fat=wpe(),mat=M6(),hat=Ipe(),gat=$pe(),_at=kpe(),vat=Mpe(),Sat=Dpe(),yat=Lpe(),Eat=zpe(),Tat=Fpe();function bat(t=!1){let e=[_at.default,vat.default,Sat.default,yat.default,Eat.default,Tat.default,fat.default,mat.default,dat.default,hat.default,gat.default];return t?e.push(cat.default,lat.default):e.push(aat.default,uat.default),e.push(pat.default),e}V6.default=bat});var Bpe=S(H6=>{"use strict";Object.defineProperty(H6,"__esModule",{value:!0});var Rn=Ut(),xat={message:({schemaCode:t})=>(0,Rn.str)`must match format "${t}"`,params:({schemaCode:t})=>(0,Rn._)`{format: ${t}}`},Aat={keyword:"format",type:["number","string"],schemaType:"string",$data:!0,error:xat,code(t,e){let{gen:r,data:n,$data:o,schema:i,schemaCode:s,it:a}=t,{opts:c,errSchemaPath:u,schemaEnv:p,self:f}=a;if(!c.validateFormats)return;o?m():h();function m(){let _=r.scopeValue("formats",{ref:f.formats,code:c.code.formats}),v=r.const("fDef",(0,Rn._)`${_}[${s}]`),E=r.let("fType"),x=r.let("format");r.if((0,Rn._)`typeof ${v} == "object" && !(${v} instanceof RegExp)`,()=>r.assign(E,(0,Rn._)`${v}.type || "string"`).assign(x,(0,Rn._)`${v}.validate`),()=>r.assign(E,(0,Rn._)`"string"`).assign(x,v)),t.fail$data((0,Rn.or)(w(),I()));function w(){return c.strictSchema===!1?Rn.nil:(0,Rn._)`${s} && !${x}`}function I(){let N=p.$async?(0,Rn._)`(${v}.async ? await ${x}(${n}) : ${x}(${n}))`:(0,Rn._)`${x}(${n})`,$=(0,Rn._)`(typeof ${x} == "function" ? ${N} : ${x}.test(${n}))`;return(0,Rn._)`${x} && ${x} !== true && ${E} === ${e} && !${$}`}}function h(){let _=f.formats[i];if(!_){w();return}if(_===!0)return;let[v,E,x]=I(_);v===e&&t.pass(N());function w(){if(c.strictSchema===!1){f.logger.warn($());return}throw new Error($());function $(){return`unknown format "${i}" ignored in schema at path "${u}"`}}function I($){let B=$ instanceof RegExp?(0,Rn.regexpCode)($):c.code.formats?(0,Rn._)`${c.code.formats}${(0,Rn.getProperty)(i)}`:void 0,G=r.scopeValue("formats",{key:i,ref:$,code:B});return typeof $=="object"&&!($ instanceof RegExp)?[$.type||"string",$.validate,(0,Rn._)`${G}.validate`]:["string",$,G]}function N(){if(typeof _=="object"&&!(_ instanceof RegExp)&&_.async){if(!p.$async)throw new Error("async format in sync schema");return(0,Rn._)`await ${x}(${n})`}return typeof E=="function"?(0,Rn._)`${x}(${n})`:(0,Rn._)`${x}.test(${n})`}}}};H6.default=Aat});var Gpe=S(W6=>{"use strict";Object.defineProperty(W6,"__esModule",{value:!0});var wat=Bpe(),Rat=[wat.default];W6.default=Rat});var Vpe=S(Cg=>{"use strict";Object.defineProperty(Cg,"__esModule",{value:!0});Cg.contentVocabulary=Cg.metadataVocabulary=void 0;Cg.metadataVocabulary=["title","description","default","deprecated","readOnly","writeOnly","examples"];Cg.contentVocabulary=["contentMediaType","contentEncoding","contentSchema"]});var Wpe=S(K6=>{"use strict";Object.defineProperty(K6,"__esModule",{value:!0});var Pat=epe(),Iat=mpe(),Oat=qpe(),Nat=Gpe(),Hpe=Vpe(),Cat=[Pat.default,Iat.default,(0,Oat.default)(),Nat.default,Hpe.metadataVocabulary,Hpe.contentVocabulary];K6.default=Cat});var Zpe=S(qO=>{"use strict";Object.defineProperty(qO,"__esModule",{value:!0});qO.DiscrError=void 0;var Kpe;(function(t){t.Tag="tag",t.Mapping="mapping"})(Kpe||(qO.DiscrError=Kpe={}))});var Jpe=S(Y6=>{"use strict";Object.defineProperty(Y6,"__esModule",{value:!0});var $g=Ut(),Z6=Zpe(),Ype=bO(),$at=bT(),kat=cr(),Mat={message:({params:{discrError:t,tagName:e}})=>t===Z6.DiscrError.Tag?`tag "${e}" must be string`:`value of tag "${e}" must be in oneOf`,params:({params:{discrError:t,tag:e,tagName:r}})=>(0,$g._)`{error: ${t}, tag: ${r}, tagValue: ${e}}`},Dat={keyword:"discriminator",type:"object",schemaType:"object",error:Mat,code(t){let{gen:e,data:r,schema:n,parentSchema:o,it:i}=t,{oneOf:s}=o;if(!i.opts.discriminator)throw new Error("discriminator: requires discriminator option");let a=n.propertyName;if(typeof a!="string")throw new Error("discriminator: requires propertyName");if(n.mapping)throw new Error("discriminator: mapping is not supported");if(!s)throw new Error("discriminator: requires oneOf keyword");let c=e.let("valid",!1),u=e.const("tag",(0,$g._)`${r}${(0,$g.getProperty)(a)}`);e.if((0,$g._)`typeof ${u} == "string"`,()=>p(),()=>t.error(!1,{discrError:Z6.DiscrError.Tag,tag:u,tagName:a})),t.ok(c);function p(){let h=m();e.if(!1);for(let _ in h)e.elseIf((0,$g._)`${u} === ${_}`),e.assign(c,f(h[_]));e.else(),t.error(!1,{discrError:Z6.DiscrError.Mapping,tag:u,tagName:a}),e.endIf()}function f(h){let _=e.name("valid"),v=t.subschema({keyword:"oneOf",schemaProp:h},_);return t.mergeEvaluated(v,$g.Name),_}function m(){var h;let _={},v=x(o),E=!0;for(let N=0;N{Lat.exports={$schema:"http://json-schema.org/draft-07/schema#",$id:"http://json-schema.org/draft-07/schema#",title:"Core schema meta-schema",definitions:{schemaArray:{type:"array",minItems:1,items:{$ref:"#"}},nonNegativeInteger:{type:"integer",minimum:0},nonNegativeIntegerDefault0:{allOf:[{$ref:"#/definitions/nonNegativeInteger"},{default:0}]},simpleTypes:{enum:["array","boolean","integer","null","number","object","string"]},stringArray:{type:"array",items:{type:"string"},uniqueItems:!0,default:[]}},type:["object","boolean"],properties:{$id:{type:"string",format:"uri-reference"},$schema:{type:"string",format:"uri"},$ref:{type:"string",format:"uri-reference"},$comment:{type:"string"},title:{type:"string"},description:{type:"string"},default:!0,readOnly:{type:"boolean",default:!1},examples:{type:"array",items:!0},multipleOf:{type:"number",exclusiveMinimum:0},maximum:{type:"number"},exclusiveMaximum:{type:"number"},minimum:{type:"number"},exclusiveMinimum:{type:"number"},maxLength:{$ref:"#/definitions/nonNegativeInteger"},minLength:{$ref:"#/definitions/nonNegativeIntegerDefault0"},pattern:{type:"string",format:"regex"},additionalItems:{$ref:"#"},items:{anyOf:[{$ref:"#"},{$ref:"#/definitions/schemaArray"}],default:!0},maxItems:{$ref:"#/definitions/nonNegativeInteger"},minItems:{$ref:"#/definitions/nonNegativeIntegerDefault0"},uniqueItems:{type:"boolean",default:!1},contains:{$ref:"#"},maxProperties:{$ref:"#/definitions/nonNegativeInteger"},minProperties:{$ref:"#/definitions/nonNegativeIntegerDefault0"},required:{$ref:"#/definitions/stringArray"},additionalProperties:{$ref:"#"},definitions:{type:"object",additionalProperties:{$ref:"#"},default:{}},properties:{type:"object",additionalProperties:{$ref:"#"},default:{}},patternProperties:{type:"object",additionalProperties:{$ref:"#"},propertyNames:{format:"regex"},default:{}},dependencies:{type:"object",additionalProperties:{anyOf:[{$ref:"#"},{$ref:"#/definitions/stringArray"}]}},propertyNames:{$ref:"#"},const:!0,enum:{type:"array",items:!0,minItems:1,uniqueItems:!0},type:{anyOf:[{$ref:"#/definitions/simpleTypes"},{type:"array",items:{$ref:"#/definitions/simpleTypes"},minItems:1,uniqueItems:!0}]},format:{type:"string"},contentMediaType:{type:"string"},contentEncoding:{type:"string"},if:{$ref:"#"},then:{$ref:"#"},else:{$ref:"#"},allOf:{$ref:"#/definitions/schemaArray"},anyOf:{$ref:"#/definitions/schemaArray"},oneOf:{$ref:"#/definitions/schemaArray"},not:{$ref:"#"}},default:!0}});var ede=S((Kr,J6)=>{"use strict";Object.defineProperty(Kr,"__esModule",{value:!0});Kr.MissingRefError=Kr.ValidationError=Kr.CodeGen=Kr.Name=Kr.nil=Kr.stringify=Kr.str=Kr._=Kr.KeywordCxt=Kr.Ajv=void 0;var Uat=Kle(),jat=Wpe(),zat=Jpe(),Qpe=Xpe(),Fat=["/properties"],BO="http://json-schema.org/draft-07/schema",kg=class extends Uat.default{_addVocabularies(){super._addVocabularies(),jat.default.forEach(e=>this.addVocabulary(e)),this.opts.discriminator&&this.addKeyword(zat.default)}_addDefaultMetaSchema(){if(super._addDefaultMetaSchema(),!this.opts.meta)return;let e=this.opts.$data?this.$dataMetaSchema(Qpe,Fat):Qpe;this.addMetaSchema(e,BO,!1),this.refs["http://json-schema.org/schema"]=BO}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(BO)?BO:void 0)}};Kr.Ajv=kg;J6.exports=Kr=kg;J6.exports.Ajv=kg;Object.defineProperty(Kr,"__esModule",{value:!0});Kr.default=kg;var qat=TT();Object.defineProperty(Kr,"KeywordCxt",{enumerable:!0,get:function(){return qat.KeywordCxt}});var Mg=Ut();Object.defineProperty(Kr,"_",{enumerable:!0,get:function(){return Mg._}});Object.defineProperty(Kr,"str",{enumerable:!0,get:function(){return Mg.str}});Object.defineProperty(Kr,"stringify",{enumerable:!0,get:function(){return Mg.stringify}});Object.defineProperty(Kr,"nil",{enumerable:!0,get:function(){return Mg.nil}});Object.defineProperty(Kr,"Name",{enumerable:!0,get:function(){return Mg.Name}});Object.defineProperty(Kr,"CodeGen",{enumerable:!0,get:function(){return Mg.CodeGen}});var Bat=EO();Object.defineProperty(Kr,"ValidationError",{enumerable:!0,get:function(){return Bat.default}});var Gat=bT();Object.defineProperty(Kr,"MissingRefError",{enumerable:!0,get:function(){return Gat.default}})});var cde=S(Vc=>{"use strict";Object.defineProperty(Vc,"__esModule",{value:!0});Vc.formatNames=Vc.fastFormats=Vc.fullFormats=void 0;function Gc(t,e){return{validate:t,compare:e}}Vc.fullFormats={date:Gc(ode,tB),time:Gc(Q6(!0),rB),"date-time":Gc(tde(!0),sde),"iso-time":Gc(Q6(),ide),"iso-date-time":Gc(tde(),ade),duration:/^P(?!$)((\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?|(\d+W)?)$/,uri:Yat,"uri-reference":/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,"uri-template":/^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,url:/^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:/^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,ipv6:/^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i,regex:nct,uuid:/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,"json-pointer":/^(?:\/(?:[^~/]|~0|~1)*)*$/,"json-pointer-uri-fragment":/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,"relative-json-pointer":/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/,byte:Jat,int32:{type:"number",validate:ect},int64:{type:"number",validate:tct},float:{type:"number",validate:nde},double:{type:"number",validate:nde},password:!0,binary:!0};Vc.fastFormats={...Vc.fullFormats,date:Gc(/^\d\d\d\d-[0-1]\d-[0-3]\d$/,tB),time:Gc(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,rB),"date-time":Gc(/^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,sde),"iso-time":Gc(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,ide),"iso-date-time":Gc(/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,ade),uri:/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+\-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i};Vc.formatNames=Object.keys(Vc.fullFormats);function Vat(t){return t%4===0&&(t%100!==0||t%400===0)}var Hat=/^(\d\d\d\d)-(\d\d)-(\d\d)$/,Wat=[0,31,28,31,30,31,30,31,31,30,31,30,31];function ode(t){let e=Hat.exec(t);if(!e)return!1;let r=+e[1],n=+e[2],o=+e[3];return n>=1&&n<=12&&o>=1&&o<=(n===2&&Vat(r)?29:Wat[n])}function tB(t,e){if(t&&e)return t>e?1:t23||p>59||t&&!a)return!1;if(o<=23&&i<=59&&s<60)return!0;let f=i-p*c,m=o-u*c-(f<0?1:0);return(m===23||m===-1)&&(f===59||f===-1)&&s<61}}function rB(t,e){if(!(t&&e))return;let r=new Date("2020-01-01T"+t).valueOf(),n=new Date("2020-01-01T"+e).valueOf();if(r&&n)return r-n}function ide(t,e){if(!(t&&e))return;let r=X6.exec(t),n=X6.exec(e);if(r&&n)return t=r[1]+r[2]+r[3],e=n[1]+n[2]+n[3],t>e?1:t=Xat}function tct(t){return Number.isInteger(t)}function nde(){return!0}var rct=/[^\\]\\Z/;function nct(t){if(rct.test(t))return!1;try{return new RegExp(t),!0}catch{return!1}}});var jT=S(ur=>{"use strict";Object.defineProperty(ur,"__esModule",{value:!0});ur.regexpCode=ur.getEsmExportName=ur.getProperty=ur.safeStringify=ur.stringify=ur.strConcat=ur.addCodeArg=ur.str=ur._=ur.nil=ur._Code=ur.Name=ur.IDENTIFIER=ur._CodeOrName=void 0;var LT=class{};ur._CodeOrName=LT;ur.IDENTIFIER=/^[a-z$_][a-z$_0-9]*$/i;var qf=class extends LT{constructor(e){if(super(),!ur.IDENTIFIER.test(e))throw new Error("CodeGen: name must be a valid identifier");this.str=e}toString(){return this.str}emptyStr(){return!1}get names(){return{[this.str]:1}}};ur.Name=qf;var oa=class extends LT{constructor(e){super(),this._items=typeof e=="string"?[e]:e}toString(){return this.str}emptyStr(){if(this._items.length>1)return!1;let e=this._items[0];return e===""||e==='""'}get str(){var e;return(e=this._str)!==null&&e!==void 0?e:this._str=this._items.reduce((r,n)=>`${r}${n}`,"")}get names(){var e;return(e=this._names)!==null&&e!==void 0?e:this._names=this._items.reduce((r,n)=>(n instanceof qf&&(r[n.str]=(r[n.str]||0)+1),r),{})}};ur._Code=oa;ur.nil=new oa("");function ude(t,...e){let r=[t[0]],n=0;for(;n{"use strict";Object.defineProperty(Wi,"__esModule",{value:!0});Wi.ValueScope=Wi.ValueScopeName=Wi.Scope=Wi.varKinds=Wi.UsedValueState=void 0;var Hi=jT(),iB=class extends Error{constructor(e){super(`CodeGen: "code" for ${e} not defined`),this.value=e.value}},GO;(function(t){t[t.Started=0]="Started",t[t.Completed=1]="Completed"})(GO||(Wi.UsedValueState=GO={}));Wi.varKinds={const:new Hi.Name("const"),let:new Hi.Name("let"),var:new Hi.Name("var")};var VO=class{constructor({prefixes:e,parent:r}={}){this._names={},this._prefixes=e,this._parent=r}toName(e){return e instanceof Hi.Name?e:this.name(e)}name(e){return new Hi.Name(this._newName(e))}_newName(e){let r=this._names[e]||this._nameGroup(e);return`${e}${r.index++}`}_nameGroup(e){var r,n;if(!((n=(r=this._parent)===null||r===void 0?void 0:r._prefixes)===null||n===void 0)&&n.has(e)||this._prefixes&&!this._prefixes.has(e))throw new Error(`CodeGen: prefix "${e}" is not allowed in this scope`);return this._names[e]={prefix:e,index:0}}};Wi.Scope=VO;var HO=class extends Hi.Name{constructor(e,r){super(r),this.prefix=e}setValue(e,{property:r,itemIndex:n}){this.value=e,this.scopePath=(0,Hi._)`.${new Hi.Name(r)}[${n}]`}};Wi.ValueScopeName=HO;var dct=(0,Hi._)`\n`,sB=class extends VO{constructor(e){super(e),this._values={},this._scope=e.scope,this.opts={...e,_n:e.lines?dct:Hi.nil}}get(){return this._scope}name(e){return new HO(e,this._newName(e))}value(e,r){var n;if(r.ref===void 0)throw new Error("CodeGen: ref must be passed in value");let o=this.toName(e),{prefix:i}=o,s=(n=r.key)!==null&&n!==void 0?n:r.ref,a=this._values[i];if(a){let p=a.get(s);if(p)return p}else a=this._values[i]=new Map;a.set(s,o);let c=this._scope[i]||(this._scope[i]=[]),u=c.length;return c[u]=r.ref,o.setValue(r,{property:i,itemIndex:u}),o}getValue(e,r){let n=this._values[e];if(n)return n.get(r)}scopeRefs(e,r=this._values){return this._reduceValues(r,n=>{if(n.scopePath===void 0)throw new Error(`CodeGen: name "${n}" has no value`);return(0,Hi._)`${e}${n.scopePath}`})}scopeCode(e=this._values,r,n){return this._reduceValues(e,o=>{if(o.value===void 0)throw new Error(`CodeGen: name "${o}" has no value`);return o.value.code},r,n)}_reduceValues(e,r,n={},o){let i=Hi.nil;for(let s in e){let a=e[s];if(!a)continue;let c=n[s]=n[s]||new Map;a.forEach(u=>{if(c.has(u))return;c.set(u,GO.Started);let p=r(u);if(p){let f=this.opts.es5?Wi.varKinds.var:Wi.varKinds.const;i=(0,Hi._)`${i}${f} ${u} = ${p};${this.opts._n}`}else if(p=o?.(u))i=(0,Hi._)`${i}${p}${this.opts._n}`;else throw new iB(u);c.set(u,GO.Completed)})}return i}};Wi.ValueScope=sB});var Pt=S(Mt=>{"use strict";Object.defineProperty(Mt,"__esModule",{value:!0});Mt.or=Mt.and=Mt.not=Mt.CodeGen=Mt.operators=Mt.varKinds=Mt.ValueScopeName=Mt.ValueScope=Mt.Scope=Mt.Name=Mt.regexpCode=Mt.stringify=Mt.getProperty=Mt.nil=Mt.strConcat=Mt.str=Mt._=void 0;var Qt=jT(),sc=aB(),Dp=jT();Object.defineProperty(Mt,"_",{enumerable:!0,get:function(){return Dp._}});Object.defineProperty(Mt,"str",{enumerable:!0,get:function(){return Dp.str}});Object.defineProperty(Mt,"strConcat",{enumerable:!0,get:function(){return Dp.strConcat}});Object.defineProperty(Mt,"nil",{enumerable:!0,get:function(){return Dp.nil}});Object.defineProperty(Mt,"getProperty",{enumerable:!0,get:function(){return Dp.getProperty}});Object.defineProperty(Mt,"stringify",{enumerable:!0,get:function(){return Dp.stringify}});Object.defineProperty(Mt,"regexpCode",{enumerable:!0,get:function(){return Dp.regexpCode}});Object.defineProperty(Mt,"Name",{enumerable:!0,get:function(){return Dp.Name}});var YO=aB();Object.defineProperty(Mt,"Scope",{enumerable:!0,get:function(){return YO.Scope}});Object.defineProperty(Mt,"ValueScope",{enumerable:!0,get:function(){return YO.ValueScope}});Object.defineProperty(Mt,"ValueScopeName",{enumerable:!0,get:function(){return YO.ValueScopeName}});Object.defineProperty(Mt,"varKinds",{enumerable:!0,get:function(){return YO.varKinds}});Mt.operators={GT:new Qt._Code(">"),GTE:new Qt._Code(">="),LT:new Qt._Code("<"),LTE:new Qt._Code("<="),EQ:new Qt._Code("==="),NEQ:new Qt._Code("!=="),NOT:new Qt._Code("!"),OR:new Qt._Code("||"),AND:new Qt._Code("&&"),ADD:new Qt._Code("+")};var ul=class{optimizeNodes(){return this}optimizeNames(e,r){return this}},cB=class extends ul{constructor(e,r,n){super(),this.varKind=e,this.name=r,this.rhs=n}render({es5:e,_n:r}){let n=e?sc.varKinds.var:this.varKind,o=this.rhs===void 0?"":` = ${this.rhs}`;return`${n} ${this.name}${o};`+r}optimizeNames(e,r){if(e[this.name.str])return this.rhs&&(this.rhs=Lg(this.rhs,e,r)),this}get names(){return this.rhs instanceof Qt._CodeOrName?this.rhs.names:{}}},WO=class extends ul{constructor(e,r,n){super(),this.lhs=e,this.rhs=r,this.sideEffects=n}render({_n:e}){return`${this.lhs} = ${this.rhs};`+e}optimizeNames(e,r){if(!(this.lhs instanceof Qt.Name&&!e[this.lhs.str]&&!this.sideEffects))return this.rhs=Lg(this.rhs,e,r),this}get names(){let e=this.lhs instanceof Qt.Name?{}:{...this.lhs.names};return ZO(e,this.rhs)}},uB=class extends WO{constructor(e,r,n,o){super(e,n,o),this.op=r}render({_n:e}){return`${this.lhs} ${this.op}= ${this.rhs};`+e}},lB=class extends ul{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`${this.label}:`+e}},pB=class extends ul{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`break${this.label?` ${this.label}`:""};`+e}},dB=class extends ul{constructor(e){super(),this.error=e}render({_n:e}){return`throw ${this.error};`+e}get names(){return this.error.names}},fB=class extends ul{constructor(e){super(),this.code=e}render({_n:e}){return`${this.code};`+e}optimizeNodes(){return`${this.code}`?this:void 0}optimizeNames(e,r){return this.code=Lg(this.code,e,r),this}get names(){return this.code instanceof Qt._CodeOrName?this.code.names:{}}},zT=class extends ul{constructor(e=[]){super(),this.nodes=e}render(e){return this.nodes.reduce((r,n)=>r+n.render(e),"")}optimizeNodes(){let{nodes:e}=this,r=e.length;for(;r--;){let n=e[r].optimizeNodes();Array.isArray(n)?e.splice(r,1,...n):n?e[r]=n:e.splice(r,1)}return e.length>0?this:void 0}optimizeNames(e,r){let{nodes:n}=this,o=n.length;for(;o--;){let i=n[o];i.optimizeNames(e,r)||(fct(e,i.names),n.splice(o,1))}return n.length>0?this:void 0}get names(){return this.nodes.reduce((e,r)=>Vf(e,r.names),{})}},ll=class extends zT{render(e){return"{"+e._n+super.render(e)+"}"+e._n}},mB=class extends zT{},Dg=class extends ll{};Dg.kind="else";var Bf=class t extends ll{constructor(e,r){super(r),this.condition=e}render(e){let r=`if(${this.condition})`+super.render(e);return this.else&&(r+="else "+this.else.render(e)),r}optimizeNodes(){super.optimizeNodes();let e=this.condition;if(e===!0)return this.nodes;let r=this.else;if(r){let n=r.optimizeNodes();r=this.else=Array.isArray(n)?new Dg(n):n}if(r)return e===!1?r instanceof t?r:r.nodes:this.nodes.length?this:new t(pde(e),r instanceof t?[r]:r.nodes);if(!(e===!1||!this.nodes.length))return this}optimizeNames(e,r){var n;if(this.else=(n=this.else)===null||n===void 0?void 0:n.optimizeNames(e,r),!!(super.optimizeNames(e,r)||this.else))return this.condition=Lg(this.condition,e,r),this}get names(){let e=super.names;return ZO(e,this.condition),this.else&&Vf(e,this.else.names),e}};Bf.kind="if";var Gf=class extends ll{};Gf.kind="for";var hB=class extends Gf{constructor(e){super(),this.iteration=e}render(e){return`for(${this.iteration})`+super.render(e)}optimizeNames(e,r){if(super.optimizeNames(e,r))return this.iteration=Lg(this.iteration,e,r),this}get names(){return Vf(super.names,this.iteration.names)}},gB=class extends Gf{constructor(e,r,n,o){super(),this.varKind=e,this.name=r,this.from=n,this.to=o}render(e){let r=e.es5?sc.varKinds.var:this.varKind,{name:n,from:o,to:i}=this;return`for(${r} ${n}=${o}; ${n}<${i}; ${n}++)`+super.render(e)}get names(){let e=ZO(super.names,this.from);return ZO(e,this.to)}},KO=class extends Gf{constructor(e,r,n,o){super(),this.loop=e,this.varKind=r,this.name=n,this.iterable=o}render(e){return`for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})`+super.render(e)}optimizeNames(e,r){if(super.optimizeNames(e,r))return this.iterable=Lg(this.iterable,e,r),this}get names(){return Vf(super.names,this.iterable.names)}},FT=class extends ll{constructor(e,r,n){super(),this.name=e,this.args=r,this.async=n}render(e){return`${this.async?"async ":""}function ${this.name}(${this.args})`+super.render(e)}};FT.kind="func";var qT=class extends zT{render(e){return"return "+super.render(e)}};qT.kind="return";var _B=class extends ll{render(e){let r="try"+super.render(e);return this.catch&&(r+=this.catch.render(e)),this.finally&&(r+=this.finally.render(e)),r}optimizeNodes(){var e,r;return super.optimizeNodes(),(e=this.catch)===null||e===void 0||e.optimizeNodes(),(r=this.finally)===null||r===void 0||r.optimizeNodes(),this}optimizeNames(e,r){var n,o;return super.optimizeNames(e,r),(n=this.catch)===null||n===void 0||n.optimizeNames(e,r),(o=this.finally)===null||o===void 0||o.optimizeNames(e,r),this}get names(){let e=super.names;return this.catch&&Vf(e,this.catch.names),this.finally&&Vf(e,this.finally.names),e}},BT=class extends ll{constructor(e){super(),this.error=e}render(e){return`catch(${this.error})`+super.render(e)}};BT.kind="catch";var GT=class extends ll{render(e){return"finally"+super.render(e)}};GT.kind="finally";var vB=class{constructor(e,r={}){this._values={},this._blockStarts=[],this._constants={},this.opts={...r,_n:r.lines?` -`:""},this._extScope=e,this._scope=new sc.Scope({parent:e}),this._nodes=[new mB]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){return this._extScope.name(e)}scopeValue(e,r){let n=this._extScope.value(e,r);return(this._values[n.prefix]||(this._values[n.prefix]=new Set)).add(n),n}getScopeValue(e,r){return this._extScope.getValue(e,r)}scopeRefs(e){return this._extScope.scopeRefs(e,this._values)}scopeCode(){return this._extScope.scopeCode(this._values)}_def(e,r,n,o){let i=this._scope.toName(r);return n!==void 0&&o&&(this._constants[i.str]=n),this._leafNode(new cB(e,i,n)),i}const(e,r,n){return this._def(sc.varKinds.const,e,r,n)}let(e,r,n){return this._def(sc.varKinds.let,e,r,n)}var(e,r,n){return this._def(sc.varKinds.var,e,r,n)}assign(e,r,n){return this._leafNode(new WO(e,r,n))}add(e,r){return this._leafNode(new uB(e,Mt.operators.ADD,r))}code(e){return typeof e=="function"?e():e!==Qt.nil&&this._leafNode(new fB(e)),this}object(...e){let r=["{"];for(let[n,o]of e)r.length>1&&r.push(","),r.push(n),(n!==o||this.opts.es5)&&(r.push(":"),(0,Qt.addCodeArg)(r,o));return r.push("}"),new Qt._Code(r)}if(e,r,n){if(this._blockNode(new Bf(e)),r&&n)this.code(r).else().code(n).endIf();else if(r)this.code(r).endIf();else if(n)throw new Error('CodeGen: "else" body without "then" body');return this}elseIf(e){return this._elseNode(new Bf(e))}else(){return this._elseNode(new Dg)}endIf(){return this._endBlockNode(Bf,Dg)}_for(e,r){return this._blockNode(e),r&&this.code(r).endFor(),this}for(e,r){return this._for(new hB(e),r)}forRange(e,r,n,o,i=this.opts.es5?sc.varKinds.var:sc.varKinds.let){let s=this._scope.toName(e);return this._for(new gB(i,s,r,n),()=>o(s))}forOf(e,r,n,o=sc.varKinds.const){let i=this._scope.toName(e);if(this.opts.es5){let s=r instanceof Qt.Name?r:this.var("_arr",r);return this.forRange("_i",0,(0,Qt._)`${s}.length`,a=>{this.var(i,(0,Qt._)`${s}[${a}]`),n(i)})}return this._for(new KO("of",o,i,r),()=>n(i))}forIn(e,r,n,o=this.opts.es5?sc.varKinds.var:sc.varKinds.const){if(this.opts.ownProperties)return this.forOf(e,(0,Qt._)`Object.keys(${r})`,n);let i=this._scope.toName(e);return this._for(new KO("in",o,i,r),()=>n(i))}endFor(){return this._endBlockNode(Gf)}label(e){return this._leafNode(new lB(e))}break(e){return this._leafNode(new pB(e))}return(e){let r=new qT;if(this._blockNode(r),this.code(e),r.nodes.length!==1)throw new Error('CodeGen: "return" should have one node');return this._endBlockNode(qT)}try(e,r,n){if(!r&&!n)throw new Error('CodeGen: "try" without "catch" and "finally"');let o=new _B;if(this._blockNode(o),this.code(e),r){let i=this.name("e");this._currNode=o.catch=new BT(i),r(i)}return n&&(this._currNode=o.finally=new GT,this.code(n)),this._endBlockNode(BT,GT)}throw(e){return this._leafNode(new dB(e))}block(e,r){return this._blockStarts.push(this._nodes.length),e&&this.code(e).endBlock(r),this}endBlock(e){let r=this._blockStarts.pop();if(r===void 0)throw new Error("CodeGen: not in self-balancing block");let n=this._nodes.length-r;if(n<0||e!==void 0&&n!==e)throw new Error(`CodeGen: wrong number of nodes: ${n} vs ${e} expected`);return this._nodes.length=r,this}func(e,r=Qt.nil,n,o){return this._blockNode(new FT(e,r,n)),o&&this.code(o).endFunc(),this}endFunc(){return this._endBlockNode(FT)}optimize(e=1){for(;e-- >0;)this._root.optimizeNodes(),this._root.optimizeNames(this._root.names,this._constants)}_leafNode(e){return this._currNode.nodes.push(e),this}_blockNode(e){this._currNode.nodes.push(e),this._nodes.push(e)}_endBlockNode(e,r){let n=this._currNode;if(n instanceof e||r&&n instanceof r)return this._nodes.pop(),this;throw new Error(`CodeGen: not in block "${r?`${e.kind}/${r.kind}`:e.kind}"`)}_elseNode(e){let r=this._currNode;if(!(r instanceof Bf))throw new Error('CodeGen: "else" without "if"');return this._currNode=r.else=e,this}get _root(){return this._nodes[0]}get _currNode(){let e=this._nodes;return e[e.length-1]}set _currNode(e){let r=this._nodes;r[r.length-1]=e}};Mt.CodeGen=vB;function Vf(t,e){for(let r in e)t[r]=(t[r]||0)+(e[r]||0);return t}function ZO(t,e){return e instanceof Qt._CodeOrName?Vf(t,e.names):t}function Lg(t,e,r){if(t instanceof Qt.Name)return n(t);if(!o(t))return t;return new Qt._Code(t._items.reduce((i,s)=>(s instanceof Qt.Name&&(s=n(s)),s instanceof Qt._Code?i.push(...s._items):i.push(s),i),[]));function n(i){let s=r[i.str];return s===void 0||e[i.str]!==1?i:(delete e[i.str],s)}function o(i){return i instanceof Qt._Code&&i._items.some(s=>s instanceof Qt.Name&&e[s.str]===1&&r[s.str]!==void 0)}}function fct(t,e){for(let r in e)t[r]=(t[r]||0)-(e[r]||0)}function pde(t){return typeof t=="boolean"||typeof t=="number"||t===null?!t:(0,Qt._)`!${SB(t)}`}Mt.not=pde;var mct=dde(Mt.operators.AND);function hct(...t){return t.reduce(mct)}Mt.and=hct;var gct=dde(Mt.operators.OR);function _ct(...t){return t.reduce(gct)}Mt.or=_ct;function dde(t){return(e,r)=>e===Qt.nil?r:r===Qt.nil?e:(0,Qt._)`${SB(e)} ${t} ${SB(r)}`}function SB(t){return t instanceof Qt.Name?t:(0,Qt._)`(${t})`}});var lr=S(zt=>{"use strict";Object.defineProperty(zt,"__esModule",{value:!0});zt.checkStrictMode=zt.getErrorPath=zt.Type=zt.useFunc=zt.setEvaluated=zt.evaluatedPropsToName=zt.mergeEvaluated=zt.eachItem=zt.unescapeJsonPointer=zt.escapeJsonPointer=zt.escapeFragment=zt.unescapeFragment=zt.schemaRefOrVal=zt.schemaHasRulesButRef=zt.schemaHasRules=zt.checkUnknownRules=zt.alwaysValidSchema=zt.toHash=void 0;var jr=Pt(),vct=jT();function Sct(t){let e={};for(let r of t)e[r]=!0;return e}zt.toHash=Sct;function yct(t,e){return typeof e=="boolean"?e:Object.keys(e).length===0?!0:(hde(t,e),!gde(e,t.self.RULES.all))}zt.alwaysValidSchema=yct;function hde(t,e=t.schema){let{opts:r,self:n}=t;if(!r.strictSchema||typeof e=="boolean")return;let o=n.RULES.keywords;for(let i in e)o[i]||Sde(t,`unknown keyword: "${i}"`)}zt.checkUnknownRules=hde;function gde(t,e){if(typeof t=="boolean")return!t;for(let r in t)if(e[r])return!0;return!1}zt.schemaHasRules=gde;function Ect(t,e){if(typeof t=="boolean")return!t;for(let r in t)if(r!=="$ref"&&e.all[r])return!0;return!1}zt.schemaHasRulesButRef=Ect;function Tct({topSchemaRef:t,schemaPath:e},r,n,o){if(!o){if(typeof r=="number"||typeof r=="boolean")return r;if(typeof r=="string")return(0,jr._)`${r}`}return(0,jr._)`${t}${e}${(0,jr.getProperty)(n)}`}zt.schemaRefOrVal=Tct;function bct(t){return _de(decodeURIComponent(t))}zt.unescapeFragment=bct;function xct(t){return encodeURIComponent(EB(t))}zt.escapeFragment=xct;function EB(t){return typeof t=="number"?`${t}`:t.replace(/~/g,"~0").replace(/\//g,"~1")}zt.escapeJsonPointer=EB;function _de(t){return t.replace(/~1/g,"/").replace(/~0/g,"~")}zt.unescapeJsonPointer=_de;function Act(t,e){if(Array.isArray(t))for(let r of t)e(r);else e(t)}zt.eachItem=Act;function fde({mergeNames:t,mergeToName:e,mergeValues:r,resultToName:n}){return(o,i,s,a)=>{let c=s===void 0?i:s instanceof jr.Name?(i instanceof jr.Name?t(o,i,s):e(o,i,s),s):i instanceof jr.Name?(e(o,s,i),i):r(i,s);return a===jr.Name&&!(c instanceof jr.Name)?n(o,c):c}}zt.mergeEvaluated={props:fde({mergeNames:(t,e,r)=>t.if((0,jr._)`${r} !== true && ${e} !== undefined`,()=>{t.if((0,jr._)`${e} === true`,()=>t.assign(r,!0),()=>t.assign(r,(0,jr._)`${r} || {}`).code((0,jr._)`Object.assign(${r}, ${e})`))}),mergeToName:(t,e,r)=>t.if((0,jr._)`${r} !== true`,()=>{e===!0?t.assign(r,!0):(t.assign(r,(0,jr._)`${r} || {}`),TB(t,r,e))}),mergeValues:(t,e)=>t===!0?!0:{...t,...e},resultToName:vde}),items:fde({mergeNames:(t,e,r)=>t.if((0,jr._)`${r} !== true && ${e} !== undefined`,()=>t.assign(r,(0,jr._)`${e} === true ? true : ${r} > ${e} ? ${r} : ${e}`)),mergeToName:(t,e,r)=>t.if((0,jr._)`${r} !== true`,()=>t.assign(r,e===!0?!0:(0,jr._)`${r} > ${e} ? ${r} : ${e}`)),mergeValues:(t,e)=>t===!0?!0:Math.max(t,e),resultToName:(t,e)=>t.var("items",e)})};function vde(t,e){if(e===!0)return t.var("props",!0);let r=t.var("props",(0,jr._)`{}`);return e!==void 0&&TB(t,r,e),r}zt.evaluatedPropsToName=vde;function TB(t,e,r){Object.keys(r).forEach(n=>t.assign((0,jr._)`${e}${(0,jr.getProperty)(n)}`,!0))}zt.setEvaluated=TB;var mde={};function wct(t,e){return t.scopeValue("func",{ref:e,code:mde[e.code]||(mde[e.code]=new vct._Code(e.code))})}zt.useFunc=wct;var yB;(function(t){t[t.Num=0]="Num",t[t.Str=1]="Str"})(yB||(zt.Type=yB={}));function Rct(t,e,r){if(t instanceof jr.Name){let n=e===yB.Num;return r?n?(0,jr._)`"[" + ${t} + "]"`:(0,jr._)`"['" + ${t} + "']"`:n?(0,jr._)`"/" + ${t}`:(0,jr._)`"/" + ${t}.replace(/~/g, "~0").replace(/\\//g, "~1")`}return r?(0,jr.getProperty)(t).toString():"/"+EB(t)}zt.getErrorPath=Rct;function Sde(t,e,r=t.opts.strictSchema){if(r){if(e=`strict mode: ${e}`,r===!0)throw new Error(e);t.self.logger.warn(e)}}zt.checkStrictMode=Sde});var pl=S(bB=>{"use strict";Object.defineProperty(bB,"__esModule",{value:!0});var Zo=Pt(),Pct={data:new Zo.Name("data"),valCxt:new Zo.Name("valCxt"),instancePath:new Zo.Name("instancePath"),parentData:new Zo.Name("parentData"),parentDataProperty:new Zo.Name("parentDataProperty"),rootData:new Zo.Name("rootData"),dynamicAnchors:new Zo.Name("dynamicAnchors"),vErrors:new Zo.Name("vErrors"),errors:new Zo.Name("errors"),this:new Zo.Name("this"),self:new Zo.Name("self"),scope:new Zo.Name("scope"),json:new Zo.Name("json"),jsonPos:new Zo.Name("jsonPos"),jsonLen:new Zo.Name("jsonLen"),jsonPart:new Zo.Name("jsonPart")};bB.default=Pct});var VT=S(Yo=>{"use strict";Object.defineProperty(Yo,"__esModule",{value:!0});Yo.extendErrors=Yo.resetErrorsCount=Yo.reportExtraError=Yo.reportError=Yo.keyword$DataError=Yo.keywordError=void 0;var rr=Pt(),JO=lr(),Ei=pl();Yo.keywordError={message:({keyword:t})=>(0,rr.str)`must pass "${t}" keyword validation`};Yo.keyword$DataError={message:({keyword:t,schemaType:e})=>e?(0,rr.str)`"${t}" keyword must be ${e} ($data)`:(0,rr.str)`"${t}" keyword is invalid ($data)`};function Ict(t,e=Yo.keywordError,r,n){let{it:o}=t,{gen:i,compositeRule:s,allErrors:a}=o,c=Tde(t,e,r);n??(s||a)?yde(i,c):Ede(o,(0,rr._)`[${c}]`)}Yo.reportError=Ict;function Oct(t,e=Yo.keywordError,r){let{it:n}=t,{gen:o,compositeRule:i,allErrors:s}=n,a=Tde(t,e,r);yde(o,a),i||s||Ede(n,Ei.default.vErrors)}Yo.reportExtraError=Oct;function Nct(t,e){t.assign(Ei.default.errors,e),t.if((0,rr._)`${Ei.default.vErrors} !== null`,()=>t.if(e,()=>t.assign((0,rr._)`${Ei.default.vErrors}.length`,e),()=>t.assign(Ei.default.vErrors,null)))}Yo.resetErrorsCount=Nct;function Cct({gen:t,keyword:e,schemaValue:r,data:n,errsCount:o,it:i}){if(o===void 0)throw new Error("ajv implementation error");let s=t.name("err");t.forRange("i",o,Ei.default.errors,a=>{t.const(s,(0,rr._)`${Ei.default.vErrors}[${a}]`),t.if((0,rr._)`${s}.instancePath === undefined`,()=>t.assign((0,rr._)`${s}.instancePath`,(0,rr.strConcat)(Ei.default.instancePath,i.errorPath))),t.assign((0,rr._)`${s}.schemaPath`,(0,rr.str)`${i.errSchemaPath}/${e}`),i.opts.verbose&&(t.assign((0,rr._)`${s}.schema`,r),t.assign((0,rr._)`${s}.data`,n))})}Yo.extendErrors=Cct;function yde(t,e){let r=t.const("err",e);t.if((0,rr._)`${Ei.default.vErrors} === null`,()=>t.assign(Ei.default.vErrors,(0,rr._)`[${r}]`),(0,rr._)`${Ei.default.vErrors}.push(${r})`),t.code((0,rr._)`${Ei.default.errors}++`)}function Ede(t,e){let{gen:r,validateName:n,schemaEnv:o}=t;o.$async?r.throw((0,rr._)`new ${t.ValidationError}(${e})`):(r.assign((0,rr._)`${n}.errors`,e),r.return(!1))}var Hf={keyword:new rr.Name("keyword"),schemaPath:new rr.Name("schemaPath"),params:new rr.Name("params"),propertyName:new rr.Name("propertyName"),message:new rr.Name("message"),schema:new rr.Name("schema"),parentSchema:new rr.Name("parentSchema")};function Tde(t,e,r){let{createErrors:n}=t.it;return n===!1?(0,rr._)`{}`:$ct(t,e,r)}function $ct(t,e,r={}){let{gen:n,it:o}=t,i=[kct(o,r),Mct(t,r)];return Dct(t,e,i),n.object(...i)}function kct({errorPath:t},{instancePath:e}){let r=e?(0,rr.str)`${t}${(0,JO.getErrorPath)(e,JO.Type.Str)}`:t;return[Ei.default.instancePath,(0,rr.strConcat)(Ei.default.instancePath,r)]}function Mct({keyword:t,it:{errSchemaPath:e}},{schemaPath:r,parentSchema:n}){let o=n?e:(0,rr.str)`${e}/${t}`;return r&&(o=(0,rr.str)`${o}${(0,JO.getErrorPath)(r,JO.Type.Str)}`),[Hf.schemaPath,o]}function Dct(t,{params:e,message:r},n){let{keyword:o,data:i,schemaValue:s,it:a}=t,{opts:c,propertyName:u,topSchemaRef:p,schemaPath:f}=a;n.push([Hf.keyword,o],[Hf.params,typeof e=="function"?e(t):e||(0,rr._)`{}`]),c.messages&&n.push([Hf.message,typeof r=="function"?r(t):r]),c.verbose&&n.push([Hf.schema,s],[Hf.parentSchema,(0,rr._)`${p}${f}`],[Ei.default.data,i]),u&&n.push([Hf.propertyName,u])}});var xde=S(Ug=>{"use strict";Object.defineProperty(Ug,"__esModule",{value:!0});Ug.boolOrEmptySchema=Ug.topBoolOrEmptySchema=void 0;var Lct=VT(),Uct=Pt(),jct=pl(),zct={message:"boolean schema is false"};function Fct(t){let{gen:e,schema:r,validateName:n}=t;r===!1?bde(t,!1):typeof r=="object"&&r.$async===!0?e.return(jct.default.data):(e.assign((0,Uct._)`${n}.errors`,null),e.return(!0))}Ug.topBoolOrEmptySchema=Fct;function qct(t,e){let{gen:r,schema:n}=t;n===!1?(r.var(e,!1),bde(t)):r.var(e,!0)}Ug.boolOrEmptySchema=qct;function bde(t,e){let{gen:r,data:n}=t,o={gen:r,keyword:"false schema",data:n,schema:!1,schemaCode:!1,schemaValue:!1,params:{},it:t};(0,Lct.reportError)(o,zct,void 0,e)}});var xB=S(jg=>{"use strict";Object.defineProperty(jg,"__esModule",{value:!0});jg.getRules=jg.isJSONType=void 0;var Bct=["string","number","integer","boolean","null","object","array"],Gct=new Set(Bct);function Vct(t){return typeof t=="string"&&Gct.has(t)}jg.isJSONType=Vct;function Hct(){let t={number:{type:"number",rules:[]},string:{type:"string",rules:[]},array:{type:"array",rules:[]},object:{type:"object",rules:[]}};return{types:{...t,integer:!0,boolean:!0,null:!0},rules:[{rules:[]},t.number,t.string,t.array,t.object],post:{rules:[]},all:{},keywords:{}}}jg.getRules=Hct});var AB=S(Lp=>{"use strict";Object.defineProperty(Lp,"__esModule",{value:!0});Lp.shouldUseRule=Lp.shouldUseGroup=Lp.schemaHasRulesForType=void 0;function Wct({schema:t,self:e},r){let n=e.RULES.types[r];return n&&n!==!0&&Ade(t,n)}Lp.schemaHasRulesForType=Wct;function Ade(t,e){return e.rules.some(r=>wde(t,r))}Lp.shouldUseGroup=Ade;function wde(t,e){var r;return t[e.keyword]!==void 0||((r=e.definition.implements)===null||r===void 0?void 0:r.some(n=>t[n]!==void 0))}Lp.shouldUseRule=wde});var HT=S(Jo=>{"use strict";Object.defineProperty(Jo,"__esModule",{value:!0});Jo.reportTypeError=Jo.checkDataTypes=Jo.checkDataType=Jo.coerceAndCheckDataType=Jo.getJSONTypes=Jo.getSchemaTypes=Jo.DataType=void 0;var Kct=xB(),Zct=AB(),Yct=VT(),xt=Pt(),Rde=lr(),zg;(function(t){t[t.Correct=0]="Correct",t[t.Wrong=1]="Wrong"})(zg||(Jo.DataType=zg={}));function Jct(t){let e=Pde(t.type);if(e.includes("null")){if(t.nullable===!1)throw new Error("type: null contradicts nullable: false")}else{if(!e.length&&t.nullable!==void 0)throw new Error('"nullable" cannot be used without "type"');t.nullable===!0&&e.push("null")}return e}Jo.getSchemaTypes=Jct;function Pde(t){let e=Array.isArray(t)?t:t?[t]:[];if(e.every(Kct.isJSONType))return e;throw new Error("type must be JSONType or JSONType[]: "+e.join(","))}Jo.getJSONTypes=Pde;function Xct(t,e){let{gen:r,data:n,opts:o}=t,i=Qct(e,o.coerceTypes),s=e.length>0&&!(i.length===0&&e.length===1&&(0,Zct.schemaHasRulesForType)(t,e[0]));if(s){let a=RB(e,n,o.strictNumbers,zg.Wrong);r.if(a,()=>{i.length?eut(t,e,i):PB(t)})}return s}Jo.coerceAndCheckDataType=Xct;var Ide=new Set(["string","number","integer","boolean","null"]);function Qct(t,e){return e?t.filter(r=>Ide.has(r)||e==="array"&&r==="array"):[]}function eut(t,e,r){let{gen:n,data:o,opts:i}=t,s=n.let("dataType",(0,xt._)`typeof ${o}`),a=n.let("coerced",(0,xt._)`undefined`);i.coerceTypes==="array"&&n.if((0,xt._)`${s} == 'object' && Array.isArray(${o}) && ${o}.length == 1`,()=>n.assign(o,(0,xt._)`${o}[0]`).assign(s,(0,xt._)`typeof ${o}`).if(RB(e,o,i.strictNumbers),()=>n.assign(a,o))),n.if((0,xt._)`${a} !== undefined`);for(let u of r)(Ide.has(u)||u==="array"&&i.coerceTypes==="array")&&c(u);n.else(),PB(t),n.endIf(),n.if((0,xt._)`${a} !== undefined`,()=>{n.assign(o,a),tut(t,a)});function c(u){switch(u){case"string":n.elseIf((0,xt._)`${s} == "number" || ${s} == "boolean"`).assign(a,(0,xt._)`"" + ${o}`).elseIf((0,xt._)`${o} === null`).assign(a,(0,xt._)`""`);return;case"number":n.elseIf((0,xt._)`${s} == "boolean" || ${o} === null - || (${s} == "string" && ${o} && ${o} == +${o})`).assign(a,(0,xt._)`+${o}`);return;case"integer":n.elseIf((0,xt._)`${s} === "boolean" || ${o} === null - || (${s} === "string" && ${o} && ${o} == +${o} && !(${o} % 1))`).assign(a,(0,xt._)`+${o}`);return;case"boolean":n.elseIf((0,xt._)`${o} === "false" || ${o} === 0 || ${o} === null`).assign(a,!1).elseIf((0,xt._)`${o} === "true" || ${o} === 1`).assign(a,!0);return;case"null":n.elseIf((0,xt._)`${o} === "" || ${o} === 0 || ${o} === false`),n.assign(a,null);return;case"array":n.elseIf((0,xt._)`${s} === "string" || ${s} === "number" - || ${s} === "boolean" || ${o} === null`).assign(a,(0,xt._)`[${o}]`)}}}function tut({gen:t,parentData:e,parentDataProperty:r},n){t.if((0,xt._)`${e} !== undefined`,()=>t.assign((0,xt._)`${e}[${r}]`,n))}function wB(t,e,r,n=zg.Correct){let o=n===zg.Correct?xt.operators.EQ:xt.operators.NEQ,i;switch(t){case"null":return(0,xt._)`${e} ${o} null`;case"array":i=(0,xt._)`Array.isArray(${e})`;break;case"object":i=(0,xt._)`${e} && typeof ${e} == "object" && !Array.isArray(${e})`;break;case"integer":i=s((0,xt._)`!(${e} % 1) && !isNaN(${e})`);break;case"number":i=s();break;default:return(0,xt._)`typeof ${e} ${o} ${t}`}return n===zg.Correct?i:(0,xt.not)(i);function s(a=xt.nil){return(0,xt.and)((0,xt._)`typeof ${e} == "number"`,a,r?(0,xt._)`isFinite(${e})`:xt.nil)}}Jo.checkDataType=wB;function RB(t,e,r,n){if(t.length===1)return wB(t[0],e,r,n);let o,i=(0,Rde.toHash)(t);if(i.array&&i.object){let s=(0,xt._)`typeof ${e} != "object"`;o=i.null?s:(0,xt._)`!${e} || ${s}`,delete i.null,delete i.array,delete i.object}else o=xt.nil;i.number&&delete i.integer;for(let s in i)o=(0,xt.and)(o,wB(s,e,r,n));return o}Jo.checkDataTypes=RB;var rut={message:({schema:t})=>`must be ${t}`,params:({schema:t,schemaValue:e})=>typeof t=="string"?(0,xt._)`{type: ${t}}`:(0,xt._)`{type: ${e}}`};function PB(t){let e=nut(t);(0,Yct.reportError)(e,rut)}Jo.reportTypeError=PB;function nut(t){let{gen:e,data:r,schema:n}=t,o=(0,Rde.schemaRefOrVal)(t,n,"type");return{gen:e,keyword:"type",data:r,schema:n.type,schemaCode:o,schemaValue:o,parentSchema:n,params:{},it:t}}});var Nde=S(XO=>{"use strict";Object.defineProperty(XO,"__esModule",{value:!0});XO.assignDefaults=void 0;var Fg=Pt(),out=lr();function iut(t,e){let{properties:r,items:n}=t.schema;if(e==="object"&&r)for(let o in r)Ode(t,o,r[o].default);else e==="array"&&Array.isArray(n)&&n.forEach((o,i)=>Ode(t,i,o.default))}XO.assignDefaults=iut;function Ode(t,e,r){let{gen:n,compositeRule:o,data:i,opts:s}=t;if(r===void 0)return;let a=(0,Fg._)`${i}${(0,Fg.getProperty)(e)}`;if(o){(0,out.checkStrictMode)(t,`default is ignored for: ${a}`);return}let c=(0,Fg._)`${a} === undefined`;s.useDefaults==="empty"&&(c=(0,Fg._)`${c} || ${a} === null || ${a} === ""`),n.if(c,(0,Fg._)`${a} = ${(0,Fg.stringify)(r)}`)}});var ia=S(Nr=>{"use strict";Object.defineProperty(Nr,"__esModule",{value:!0});Nr.validateUnion=Nr.validateArray=Nr.usePattern=Nr.callValidateCode=Nr.schemaProperties=Nr.allSchemaProperties=Nr.noPropertyInData=Nr.propertyInData=Nr.isOwnProperty=Nr.hasPropFunc=Nr.reportMissingProp=Nr.checkMissingProp=Nr.checkReportMissingProp=void 0;var Zr=Pt(),IB=lr(),Up=pl(),sut=lr();function aut(t,e){let{gen:r,data:n,it:o}=t;r.if(NB(r,n,e,o.opts.ownProperties),()=>{t.setParams({missingProperty:(0,Zr._)`${e}`},!0),t.error()})}Nr.checkReportMissingProp=aut;function cut({gen:t,data:e,it:{opts:r}},n,o){return(0,Zr.or)(...n.map(i=>(0,Zr.and)(NB(t,e,i,r.ownProperties),(0,Zr._)`${o} = ${i}`)))}Nr.checkMissingProp=cut;function uut(t,e){t.setParams({missingProperty:e},!0),t.error()}Nr.reportMissingProp=uut;function Cde(t){return t.scopeValue("func",{ref:Object.prototype.hasOwnProperty,code:(0,Zr._)`Object.prototype.hasOwnProperty`})}Nr.hasPropFunc=Cde;function OB(t,e,r){return(0,Zr._)`${Cde(t)}.call(${e}, ${r})`}Nr.isOwnProperty=OB;function lut(t,e,r,n){let o=(0,Zr._)`${e}${(0,Zr.getProperty)(r)} !== undefined`;return n?(0,Zr._)`${o} && ${OB(t,e,r)}`:o}Nr.propertyInData=lut;function NB(t,e,r,n){let o=(0,Zr._)`${e}${(0,Zr.getProperty)(r)} === undefined`;return n?(0,Zr.or)(o,(0,Zr.not)(OB(t,e,r))):o}Nr.noPropertyInData=NB;function $de(t){return t?Object.keys(t).filter(e=>e!=="__proto__"):[]}Nr.allSchemaProperties=$de;function put(t,e){return $de(e).filter(r=>!(0,IB.alwaysValidSchema)(t,e[r]))}Nr.schemaProperties=put;function dut({schemaCode:t,data:e,it:{gen:r,topSchemaRef:n,schemaPath:o,errorPath:i},it:s},a,c,u){let p=u?(0,Zr._)`${t}, ${e}, ${n}${o}`:e,f=[[Up.default.instancePath,(0,Zr.strConcat)(Up.default.instancePath,i)],[Up.default.parentData,s.parentData],[Up.default.parentDataProperty,s.parentDataProperty],[Up.default.rootData,Up.default.rootData]];s.opts.dynamicRef&&f.push([Up.default.dynamicAnchors,Up.default.dynamicAnchors]);let m=(0,Zr._)`${p}, ${r.object(...f)}`;return c!==Zr.nil?(0,Zr._)`${a}.call(${c}, ${m})`:(0,Zr._)`${a}(${m})`}Nr.callValidateCode=dut;var fut=(0,Zr._)`new RegExp`;function mut({gen:t,it:{opts:e}},r){let n=e.unicodeRegExp?"u":"",{regExp:o}=e.code,i=o(r,n);return t.scopeValue("pattern",{key:i.toString(),ref:i,code:(0,Zr._)`${o.code==="new RegExp"?fut:(0,sut.useFunc)(t,o)}(${r}, ${n})`})}Nr.usePattern=mut;function hut(t){let{gen:e,data:r,keyword:n,it:o}=t,i=e.name("valid");if(o.allErrors){let a=e.let("valid",!0);return s(()=>e.assign(a,!1)),a}return e.var(i,!0),s(()=>e.break()),i;function s(a){let c=e.const("len",(0,Zr._)`${r}.length`);e.forRange("i",0,c,u=>{t.subschema({keyword:n,dataProp:u,dataPropType:IB.Type.Num},i),e.if((0,Zr.not)(i),a)})}}Nr.validateArray=hut;function gut(t){let{gen:e,schema:r,keyword:n,it:o}=t;if(!Array.isArray(r))throw new Error("ajv implementation error");if(r.some(c=>(0,IB.alwaysValidSchema)(o,c))&&!o.opts.unevaluated)return;let s=e.let("valid",!1),a=e.name("_valid");e.block(()=>r.forEach((c,u)=>{let p=t.subschema({keyword:n,schemaProp:u,compositeRule:!0},a);e.assign(s,(0,Zr._)`${s} || ${a}`),t.mergeValidEvaluated(p,a)||e.if((0,Zr.not)(s))})),t.result(s,()=>t.reset(),()=>t.error(!0))}Nr.validateUnion=gut});var Dde=S(Hc=>{"use strict";Object.defineProperty(Hc,"__esModule",{value:!0});Hc.validateKeywordUsage=Hc.validSchemaType=Hc.funcKeywordCode=Hc.macroKeywordCode=void 0;var Ti=Pt(),Wf=pl(),_ut=ia(),vut=VT();function Sut(t,e){let{gen:r,keyword:n,schema:o,parentSchema:i,it:s}=t,a=e.macro.call(s.self,o,i,s),c=Mde(r,n,a);s.opts.validateSchema!==!1&&s.self.validateSchema(a,!0);let u=r.name("valid");t.subschema({schema:a,schemaPath:Ti.nil,errSchemaPath:`${s.errSchemaPath}/${n}`,topSchemaRef:c,compositeRule:!0},u),t.pass(u,()=>t.error(!0))}Hc.macroKeywordCode=Sut;function yut(t,e){var r;let{gen:n,keyword:o,schema:i,parentSchema:s,$data:a,it:c}=t;Tut(c,e);let u=!a&&e.compile?e.compile.call(c.self,i,s,c):e.validate,p=Mde(n,o,u),f=n.let("valid");t.block$data(f,m),t.ok((r=e.valid)!==null&&r!==void 0?r:f);function m(){if(e.errors===!1)v(),e.modifying&&kde(t),E(()=>t.error());else{let x=e.async?h():_();e.modifying&&kde(t),E(()=>Eut(t,x))}}function h(){let x=n.let("ruleErrs",null);return n.try(()=>v((0,Ti._)`await `),w=>n.assign(f,!1).if((0,Ti._)`${w} instanceof ${c.ValidationError}`,()=>n.assign(x,(0,Ti._)`${w}.errors`),()=>n.throw(w))),x}function _(){let x=(0,Ti._)`${p}.errors`;return n.assign(x,null),v(Ti.nil),x}function v(x=e.async?(0,Ti._)`await `:Ti.nil){let w=c.opts.passContext?Wf.default.this:Wf.default.self,I=!("compile"in e&&!a||e.schema===!1);n.assign(f,(0,Ti._)`${x}${(0,_ut.callValidateCode)(t,p,w,I)}`,e.modifying)}function E(x){var w;n.if((0,Ti.not)((w=e.valid)!==null&&w!==void 0?w:f),x)}}Hc.funcKeywordCode=yut;function kde(t){let{gen:e,data:r,it:n}=t;e.if(n.parentData,()=>e.assign(r,(0,Ti._)`${n.parentData}[${n.parentDataProperty}]`))}function Eut(t,e){let{gen:r}=t;r.if((0,Ti._)`Array.isArray(${e})`,()=>{r.assign(Wf.default.vErrors,(0,Ti._)`${Wf.default.vErrors} === null ? ${e} : ${Wf.default.vErrors}.concat(${e})`).assign(Wf.default.errors,(0,Ti._)`${Wf.default.vErrors}.length`),(0,vut.extendErrors)(t)},()=>t.error())}function Tut({schemaEnv:t},e){if(e.async&&!t.$async)throw new Error("async keyword in sync schema")}function Mde(t,e,r){if(r===void 0)throw new Error(`keyword "${e}" failed to compile`);return t.scopeValue("keyword",typeof r=="function"?{ref:r}:{ref:r,code:(0,Ti.stringify)(r)})}function but(t,e,r=!1){return!e.length||e.some(n=>n==="array"?Array.isArray(t):n==="object"?t&&typeof t=="object"&&!Array.isArray(t):typeof t==n||r&&typeof t>"u")}Hc.validSchemaType=but;function xut({schema:t,opts:e,self:r,errSchemaPath:n},o,i){if(Array.isArray(o.keyword)?!o.keyword.includes(i):o.keyword!==i)throw new Error("ajv implementation error");let s=o.dependencies;if(s?.some(a=>!Object.prototype.hasOwnProperty.call(t,a)))throw new Error(`parent schema must have dependencies of ${i}: ${s.join(",")}`);if(o.validateSchema&&!o.validateSchema(t[i])){let c=`keyword "${i}" value is invalid at path "${n}": `+r.errorsText(o.validateSchema.errors);if(e.validateSchema==="log")r.logger.error(c);else throw new Error(c)}}Hc.validateKeywordUsage=xut});var Ude=S(jp=>{"use strict";Object.defineProperty(jp,"__esModule",{value:!0});jp.extendSubschemaMode=jp.extendSubschemaData=jp.getSubschema=void 0;var Wc=Pt(),Lde=lr();function Aut(t,{keyword:e,schemaProp:r,schema:n,schemaPath:o,errSchemaPath:i,topSchemaRef:s}){if(e!==void 0&&n!==void 0)throw new Error('both "keyword" and "schema" passed, only one allowed');if(e!==void 0){let a=t.schema[e];return r===void 0?{schema:a,schemaPath:(0,Wc._)`${t.schemaPath}${(0,Wc.getProperty)(e)}`,errSchemaPath:`${t.errSchemaPath}/${e}`}:{schema:a[r],schemaPath:(0,Wc._)`${t.schemaPath}${(0,Wc.getProperty)(e)}${(0,Wc.getProperty)(r)}`,errSchemaPath:`${t.errSchemaPath}/${e}/${(0,Lde.escapeFragment)(r)}`}}if(n!==void 0){if(o===void 0||i===void 0||s===void 0)throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"');return{schema:n,schemaPath:o,topSchemaRef:s,errSchemaPath:i}}throw new Error('either "keyword" or "schema" must be passed')}jp.getSubschema=Aut;function wut(t,e,{dataProp:r,dataPropType:n,data:o,dataTypes:i,propertyName:s}){if(o!==void 0&&r!==void 0)throw new Error('both "data" and "dataProp" passed, only one allowed');let{gen:a}=e;if(r!==void 0){let{errorPath:u,dataPathArr:p,opts:f}=e,m=a.let("data",(0,Wc._)`${e.data}${(0,Wc.getProperty)(r)}`,!0);c(m),t.errorPath=(0,Wc.str)`${u}${(0,Lde.getErrorPath)(r,n,f.jsPropertySyntax)}`,t.parentDataProperty=(0,Wc._)`${r}`,t.dataPathArr=[...p,t.parentDataProperty]}if(o!==void 0){let u=o instanceof Wc.Name?o:a.let("data",o,!0);c(u),s!==void 0&&(t.propertyName=s)}i&&(t.dataTypes=i);function c(u){t.data=u,t.dataLevel=e.dataLevel+1,t.dataTypes=[],e.definedProperties=new Set,t.parentData=e.data,t.dataNames=[...e.dataNames,u]}}jp.extendSubschemaData=wut;function Rut(t,{jtdDiscriminator:e,jtdMetadata:r,compositeRule:n,createErrors:o,allErrors:i}){n!==void 0&&(t.compositeRule=n),o!==void 0&&(t.createErrors=o),i!==void 0&&(t.allErrors=i),t.jtdDiscriminator=e,t.jtdMetadata=r}jp.extendSubschemaMode=Rut});var zde=S((flr,jde)=>{"use strict";var zp=jde.exports=function(t,e,r){typeof e=="function"&&(r=e,e={}),r=e.cb||r;var n=typeof r=="function"?r:r.pre||function(){},o=r.post||function(){};QO(e,n,o,t,"",t)};zp.keywords={additionalItems:!0,items:!0,contains:!0,additionalProperties:!0,propertyNames:!0,not:!0,if:!0,then:!0,else:!0};zp.arrayKeywords={items:!0,allOf:!0,anyOf:!0,oneOf:!0};zp.propsKeywords={$defs:!0,definitions:!0,properties:!0,patternProperties:!0,dependencies:!0};zp.skipKeywords={default:!0,enum:!0,const:!0,required:!0,maximum:!0,minimum:!0,exclusiveMaximum:!0,exclusiveMinimum:!0,multipleOf:!0,maxLength:!0,minLength:!0,pattern:!0,format:!0,maxItems:!0,minItems:!0,uniqueItems:!0,maxProperties:!0,minProperties:!0};function QO(t,e,r,n,o,i,s,a,c,u){if(n&&typeof n=="object"&&!Array.isArray(n)){e(n,o,i,s,a,c,u);for(var p in n){var f=n[p];if(Array.isArray(f)){if(p in zp.arrayKeywords)for(var m=0;m{"use strict";Object.defineProperty(Ki,"__esModule",{value:!0});Ki.getSchemaRefs=Ki.resolveUrl=Ki.normalizeId=Ki._getFullPath=Ki.getFullPath=Ki.inlineRef=void 0;var Iut=lr(),Out=vT(),Nut=zde(),Cut=new Set(["type","format","pattern","maxLength","minLength","maxProperties","minProperties","maxItems","minItems","maximum","minimum","uniqueItems","multipleOf","required","enum","const"]);function $ut(t,e=!0){return typeof t=="boolean"?!0:e===!0?!CB(t):e?Fde(t)<=e:!1}Ki.inlineRef=$ut;var kut=new Set(["$ref","$recursiveRef","$recursiveAnchor","$dynamicRef","$dynamicAnchor"]);function CB(t){for(let e in t){if(kut.has(e))return!0;let r=t[e];if(Array.isArray(r)&&r.some(CB)||typeof r=="object"&&CB(r))return!0}return!1}function Fde(t){let e=0;for(let r in t){if(r==="$ref")return 1/0;if(e++,!Cut.has(r)&&(typeof t[r]=="object"&&(0,Iut.eachItem)(t[r],n=>e+=Fde(n)),e===1/0))return 1/0}return e}function qde(t,e="",r){r!==!1&&(e=qg(e));let n=t.parse(e);return Bde(t,n)}Ki.getFullPath=qde;function Bde(t,e){return t.serialize(e).split("#")[0]+"#"}Ki._getFullPath=Bde;var Mut=/#\/?$/;function qg(t){return t?t.replace(Mut,""):""}Ki.normalizeId=qg;function Dut(t,e,r){return r=qg(r),t.resolve(e,r)}Ki.resolveUrl=Dut;var Lut=/^[a-z_][-a-z0-9._]*$/i;function Uut(t,e){if(typeof t=="boolean")return{};let{schemaId:r,uriResolver:n}=this.opts,o=qg(t[r]||e),i={"":o},s=qde(n,o,!1),a={},c=new Set;return Nut(t,{allKeys:!0},(f,m,h,_)=>{if(_===void 0)return;let v=s+m,E=i[_];typeof f[r]=="string"&&(E=x.call(this,f[r])),w.call(this,f.$anchor),w.call(this,f.$dynamicAnchor),i[m]=E;function x(I){let N=this.opts.uriResolver.resolve;if(I=qg(E?N(E,I):I),c.has(I))throw p(I);c.add(I);let $=this.refs[I];return typeof $=="string"&&($=this.refs[$]),typeof $=="object"?u(f,$.schema,I):I!==qg(v)&&(I[0]==="#"?(u(f,a[I],I),a[I]=f):this.refs[I]=v),I}function w(I){if(typeof I=="string"){if(!Lut.test(I))throw new Error(`invalid anchor "${I}"`);x.call(this,`#${I}`)}}}),a;function u(f,m,h){if(m!==void 0&&!Out(f,m))throw p(h)}function p(f){return new Error(`reference "${f}" resolves to more than one schema`)}}Ki.getSchemaRefs=Uut});var YT=S(Fp=>{"use strict";Object.defineProperty(Fp,"__esModule",{value:!0});Fp.getData=Fp.KeywordCxt=Fp.validateFunctionCode=void 0;var Kde=xde(),Gde=HT(),kB=AB(),eN=HT(),jut=Nde(),ZT=Dde(),$B=Ude(),qe=Pt(),ut=pl(),zut=WT(),dl=lr(),KT=VT();function Fut(t){if(Jde(t)&&(Xde(t),Yde(t))){Gut(t);return}Zde(t,()=>(0,Kde.topBoolOrEmptySchema)(t))}Fp.validateFunctionCode=Fut;function Zde({gen:t,validateName:e,schema:r,schemaEnv:n,opts:o},i){o.code.es5?t.func(e,(0,qe._)`${ut.default.data}, ${ut.default.valCxt}`,n.$async,()=>{t.code((0,qe._)`"use strict"; ${Vde(r,o)}`),But(t,o),t.code(i)}):t.func(e,(0,qe._)`${ut.default.data}, ${qut(o)}`,n.$async,()=>t.code(Vde(r,o)).code(i))}function qut(t){return(0,qe._)`{${ut.default.instancePath}="", ${ut.default.parentData}, ${ut.default.parentDataProperty}, ${ut.default.rootData}=${ut.default.data}${t.dynamicRef?(0,qe._)`, ${ut.default.dynamicAnchors}={}`:qe.nil}}={}`}function But(t,e){t.if(ut.default.valCxt,()=>{t.var(ut.default.instancePath,(0,qe._)`${ut.default.valCxt}.${ut.default.instancePath}`),t.var(ut.default.parentData,(0,qe._)`${ut.default.valCxt}.${ut.default.parentData}`),t.var(ut.default.parentDataProperty,(0,qe._)`${ut.default.valCxt}.${ut.default.parentDataProperty}`),t.var(ut.default.rootData,(0,qe._)`${ut.default.valCxt}.${ut.default.rootData}`),e.dynamicRef&&t.var(ut.default.dynamicAnchors,(0,qe._)`${ut.default.valCxt}.${ut.default.dynamicAnchors}`)},()=>{t.var(ut.default.instancePath,(0,qe._)`""`),t.var(ut.default.parentData,(0,qe._)`undefined`),t.var(ut.default.parentDataProperty,(0,qe._)`undefined`),t.var(ut.default.rootData,ut.default.data),e.dynamicRef&&t.var(ut.default.dynamicAnchors,(0,qe._)`{}`)})}function Gut(t){let{schema:e,opts:r,gen:n}=t;Zde(t,()=>{r.$comment&&e.$comment&&efe(t),Zut(t),n.let(ut.default.vErrors,null),n.let(ut.default.errors,0),r.unevaluated&&Vut(t),Qde(t),Xut(t)})}function Vut(t){let{gen:e,validateName:r}=t;t.evaluated=e.const("evaluated",(0,qe._)`${r}.evaluated`),e.if((0,qe._)`${t.evaluated}.dynamicProps`,()=>e.assign((0,qe._)`${t.evaluated}.props`,(0,qe._)`undefined`)),e.if((0,qe._)`${t.evaluated}.dynamicItems`,()=>e.assign((0,qe._)`${t.evaluated}.items`,(0,qe._)`undefined`))}function Vde(t,e){let r=typeof t=="object"&&t[e.schemaId];return r&&(e.code.source||e.code.process)?(0,qe._)`/*# sourceURL=${r} */`:qe.nil}function Hut(t,e){if(Jde(t)&&(Xde(t),Yde(t))){Wut(t,e);return}(0,Kde.boolOrEmptySchema)(t,e)}function Yde({schema:t,self:e}){if(typeof t=="boolean")return!t;for(let r in t)if(e.RULES.all[r])return!0;return!1}function Jde(t){return typeof t.schema!="boolean"}function Wut(t,e){let{schema:r,gen:n,opts:o}=t;o.$comment&&r.$comment&&efe(t),Yut(t),Jut(t);let i=n.const("_errs",ut.default.errors);Qde(t,i),n.var(e,(0,qe._)`${i} === ${ut.default.errors}`)}function Xde(t){(0,dl.checkUnknownRules)(t),Kut(t)}function Qde(t,e){if(t.opts.jtd)return Hde(t,[],!1,e);let r=(0,Gde.getSchemaTypes)(t.schema),n=(0,Gde.coerceAndCheckDataType)(t,r);Hde(t,r,!n,e)}function Kut(t){let{schema:e,errSchemaPath:r,opts:n,self:o}=t;e.$ref&&n.ignoreKeywordsWithRef&&(0,dl.schemaHasRulesButRef)(e,o.RULES)&&o.logger.warn(`$ref: keywords ignored in schema at path "${r}"`)}function Zut(t){let{schema:e,opts:r}=t;e.default!==void 0&&r.useDefaults&&r.strictSchema&&(0,dl.checkStrictMode)(t,"default is ignored in the schema root")}function Yut(t){let e=t.schema[t.opts.schemaId];e&&(t.baseId=(0,zut.resolveUrl)(t.opts.uriResolver,t.baseId,e))}function Jut(t){if(t.schema.$async&&!t.schemaEnv.$async)throw new Error("async schema in sync schema")}function efe({gen:t,schemaEnv:e,schema:r,errSchemaPath:n,opts:o}){let i=r.$comment;if(o.$comment===!0)t.code((0,qe._)`${ut.default.self}.logger.log(${i})`);else if(typeof o.$comment=="function"){let s=(0,qe.str)`${n}/$comment`,a=t.scopeValue("root",{ref:e.root});t.code((0,qe._)`${ut.default.self}.opts.$comment(${i}, ${s}, ${a}.schema)`)}}function Xut(t){let{gen:e,schemaEnv:r,validateName:n,ValidationError:o,opts:i}=t;r.$async?e.if((0,qe._)`${ut.default.errors} === 0`,()=>e.return(ut.default.data),()=>e.throw((0,qe._)`new ${o}(${ut.default.vErrors})`)):(e.assign((0,qe._)`${n}.errors`,ut.default.vErrors),i.unevaluated&&Qut(t),e.return((0,qe._)`${ut.default.errors} === 0`))}function Qut({gen:t,evaluated:e,props:r,items:n}){r instanceof qe.Name&&t.assign((0,qe._)`${e}.props`,r),n instanceof qe.Name&&t.assign((0,qe._)`${e}.items`,n)}function Hde(t,e,r,n){let{gen:o,schema:i,data:s,allErrors:a,opts:c,self:u}=t,{RULES:p}=u;if(i.$ref&&(c.ignoreKeywordsWithRef||!(0,dl.schemaHasRulesButRef)(i,p))){o.block(()=>rfe(t,"$ref",p.all.$ref.definition));return}c.jtd||elt(t,e),o.block(()=>{for(let m of p.rules)f(m);f(p.post)});function f(m){(0,kB.shouldUseGroup)(i,m)&&(m.type?(o.if((0,eN.checkDataType)(m.type,s,c.strictNumbers)),Wde(t,m),e.length===1&&e[0]===m.type&&r&&(o.else(),(0,eN.reportTypeError)(t)),o.endIf()):Wde(t,m),a||o.if((0,qe._)`${ut.default.errors} === ${n||0}`))}}function Wde(t,e){let{gen:r,schema:n,opts:{useDefaults:o}}=t;o&&(0,jut.assignDefaults)(t,e.type),r.block(()=>{for(let i of e.rules)(0,kB.shouldUseRule)(n,i)&&rfe(t,i.keyword,i.definition,e.type)})}function elt(t,e){t.schemaEnv.meta||!t.opts.strictTypes||(tlt(t,e),t.opts.allowUnionTypes||rlt(t,e),nlt(t,t.dataTypes))}function tlt(t,e){if(e.length){if(!t.dataTypes.length){t.dataTypes=e;return}e.forEach(r=>{tfe(t.dataTypes,r)||MB(t,`type "${r}" not allowed by context "${t.dataTypes.join(",")}"`)}),ilt(t,e)}}function rlt(t,e){e.length>1&&!(e.length===2&&e.includes("null"))&&MB(t,"use allowUnionTypes to allow union type keyword")}function nlt(t,e){let r=t.self.RULES.all;for(let n in r){let o=r[n];if(typeof o=="object"&&(0,kB.shouldUseRule)(t.schema,o)){let{type:i}=o.definition;i.length&&!i.some(s=>olt(e,s))&&MB(t,`missing type "${i.join(",")}" for keyword "${n}"`)}}}function olt(t,e){return t.includes(e)||e==="number"&&t.includes("integer")}function tfe(t,e){return t.includes(e)||e==="integer"&&t.includes("number")}function ilt(t,e){let r=[];for(let n of t.dataTypes)tfe(e,n)?r.push(n):e.includes("integer")&&n==="number"&&r.push("integer");t.dataTypes=r}function MB(t,e){let r=t.schemaEnv.baseId+t.errSchemaPath;e+=` at "${r}" (strictTypes)`,(0,dl.checkStrictMode)(t,e,t.opts.strictTypes)}var tN=class{constructor(e,r,n){if((0,ZT.validateKeywordUsage)(e,r,n),this.gen=e.gen,this.allErrors=e.allErrors,this.keyword=n,this.data=e.data,this.schema=e.schema[n],this.$data=r.$data&&e.opts.$data&&this.schema&&this.schema.$data,this.schemaValue=(0,dl.schemaRefOrVal)(e,this.schema,n,this.$data),this.schemaType=r.schemaType,this.parentSchema=e.schema,this.params={},this.it=e,this.def=r,this.$data)this.schemaCode=e.gen.const("vSchema",nfe(this.$data,e));else if(this.schemaCode=this.schemaValue,!(0,ZT.validSchemaType)(this.schema,r.schemaType,r.allowUndefined))throw new Error(`${n} value must be ${JSON.stringify(r.schemaType)}`);("code"in r?r.trackErrors:r.errors!==!1)&&(this.errsCount=e.gen.const("_errs",ut.default.errors))}result(e,r,n){this.failResult((0,qe.not)(e),r,n)}failResult(e,r,n){this.gen.if(e),n?n():this.error(),r?(this.gen.else(),r(),this.allErrors&&this.gen.endIf()):this.allErrors?this.gen.endIf():this.gen.else()}pass(e,r){this.failResult((0,qe.not)(e),void 0,r)}fail(e){if(e===void 0){this.error(),this.allErrors||this.gen.if(!1);return}this.gen.if(e),this.error(),this.allErrors?this.gen.endIf():this.gen.else()}fail$data(e){if(!this.$data)return this.fail(e);let{schemaCode:r}=this;this.fail((0,qe._)`${r} !== undefined && (${(0,qe.or)(this.invalid$data(),e)})`)}error(e,r,n){if(r){this.setParams(r),this._error(e,n),this.setParams({});return}this._error(e,n)}_error(e,r){(e?KT.reportExtraError:KT.reportError)(this,this.def.error,r)}$dataError(){(0,KT.reportError)(this,this.def.$dataError||KT.keyword$DataError)}reset(){if(this.errsCount===void 0)throw new Error('add "trackErrors" to keyword definition');(0,KT.resetErrorsCount)(this.gen,this.errsCount)}ok(e){this.allErrors||this.gen.if(e)}setParams(e,r){r?Object.assign(this.params,e):this.params=e}block$data(e,r,n=qe.nil){this.gen.block(()=>{this.check$data(e,n),r()})}check$data(e=qe.nil,r=qe.nil){if(!this.$data)return;let{gen:n,schemaCode:o,schemaType:i,def:s}=this;n.if((0,qe.or)((0,qe._)`${o} === undefined`,r)),e!==qe.nil&&n.assign(e,!0),(i.length||s.validateSchema)&&(n.elseIf(this.invalid$data()),this.$dataError(),e!==qe.nil&&n.assign(e,!1)),n.else()}invalid$data(){let{gen:e,schemaCode:r,schemaType:n,def:o,it:i}=this;return(0,qe.or)(s(),a());function s(){if(n.length){if(!(r instanceof qe.Name))throw new Error("ajv implementation error");let c=Array.isArray(n)?n:[n];return(0,qe._)`${(0,eN.checkDataTypes)(c,r,i.opts.strictNumbers,eN.DataType.Wrong)}`}return qe.nil}function a(){if(o.validateSchema){let c=e.scopeValue("validate$data",{ref:o.validateSchema});return(0,qe._)`!${c}(${r})`}return qe.nil}}subschema(e,r){let n=(0,$B.getSubschema)(this.it,e);(0,$B.extendSubschemaData)(n,this.it,e),(0,$B.extendSubschemaMode)(n,e);let o={...this.it,...n,items:void 0,props:void 0};return Hut(o,r),o}mergeEvaluated(e,r){let{it:n,gen:o}=this;n.opts.unevaluated&&(n.props!==!0&&e.props!==void 0&&(n.props=dl.mergeEvaluated.props(o,e.props,n.props,r)),n.items!==!0&&e.items!==void 0&&(n.items=dl.mergeEvaluated.items(o,e.items,n.items,r)))}mergeValidEvaluated(e,r){let{it:n,gen:o}=this;if(n.opts.unevaluated&&(n.props!==!0||n.items!==!0))return o.if(r,()=>this.mergeEvaluated(e,qe.Name)),!0}};Fp.KeywordCxt=tN;function rfe(t,e,r,n){let o=new tN(t,r,e);"code"in r?r.code(o,n):o.$data&&r.validate?(0,ZT.funcKeywordCode)(o,r):"macro"in r?(0,ZT.macroKeywordCode)(o,r):(r.compile||r.validate)&&(0,ZT.funcKeywordCode)(o,r)}var slt=/^\/(?:[^~]|~0|~1)*$/,alt=/^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;function nfe(t,{dataLevel:e,dataNames:r,dataPathArr:n}){let o,i;if(t==="")return ut.default.rootData;if(t[0]==="/"){if(!slt.test(t))throw new Error(`Invalid JSON-pointer: ${t}`);o=t,i=ut.default.rootData}else{let u=alt.exec(t);if(!u)throw new Error(`Invalid JSON-pointer: ${t}`);let p=+u[1];if(o=u[2],o==="#"){if(p>=e)throw new Error(c("property/index",p));return n[e-p]}if(p>e)throw new Error(c("data",p));if(i=r[e-p],!o)return i}let s=i,a=o.split("/");for(let u of a)u&&(i=(0,qe._)`${i}${(0,qe.getProperty)((0,dl.unescapeJsonPointer)(u))}`,s=(0,qe._)`${s} && ${i}`);return s;function c(u,p){return`Cannot access ${u} ${p} levels up, current level is ${e}`}}Fp.getData=nfe});var rN=S(LB=>{"use strict";Object.defineProperty(LB,"__esModule",{value:!0});var DB=class extends Error{constructor(e){super("validation failed"),this.errors=e,this.ajv=this.validation=!0}};LB.default=DB});var JT=S(zB=>{"use strict";Object.defineProperty(zB,"__esModule",{value:!0});var UB=WT(),jB=class extends Error{constructor(e,r,n,o){super(o||`can't resolve reference ${n} from id ${r}`),this.missingRef=(0,UB.resolveUrl)(e,r,n),this.missingSchema=(0,UB.normalizeId)((0,UB.getFullPath)(e,this.missingRef))}};zB.default=jB});var oN=S(sa=>{"use strict";Object.defineProperty(sa,"__esModule",{value:!0});sa.resolveSchema=sa.getCompilingSchema=sa.resolveRef=sa.compileSchema=sa.SchemaEnv=void 0;var ac=Pt(),clt=rN(),Kf=pl(),cc=WT(),ofe=lr(),ult=YT(),Bg=class{constructor(e){var r;this.refs={},this.dynamicAnchors={};let n;typeof e.schema=="object"&&(n=e.schema),this.schema=e.schema,this.schemaId=e.schemaId,this.root=e.root||this,this.baseId=(r=e.baseId)!==null&&r!==void 0?r:(0,cc.normalizeId)(n?.[e.schemaId||"$id"]),this.schemaPath=e.schemaPath,this.localRefs=e.localRefs,this.meta=e.meta,this.$async=n?.$async,this.refs={}}};sa.SchemaEnv=Bg;function qB(t){let e=ife.call(this,t);if(e)return e;let r=(0,cc.getFullPath)(this.opts.uriResolver,t.root.baseId),{es5:n,lines:o}=this.opts.code,{ownProperties:i}=this.opts,s=new ac.CodeGen(this.scope,{es5:n,lines:o,ownProperties:i}),a;t.$async&&(a=s.scopeValue("Error",{ref:clt.default,code:(0,ac._)`require("ajv/dist/runtime/validation_error").default`}));let c=s.scopeName("validate");t.validateName=c;let u={gen:s,allErrors:this.opts.allErrors,data:Kf.default.data,parentData:Kf.default.parentData,parentDataProperty:Kf.default.parentDataProperty,dataNames:[Kf.default.data],dataPathArr:[ac.nil],dataLevel:0,dataTypes:[],definedProperties:new Set,topSchemaRef:s.scopeValue("schema",this.opts.code.source===!0?{ref:t.schema,code:(0,ac.stringify)(t.schema)}:{ref:t.schema}),validateName:c,ValidationError:a,schema:t.schema,schemaEnv:t,rootId:r,baseId:t.baseId||r,schemaPath:ac.nil,errSchemaPath:t.schemaPath||(this.opts.jtd?"":"#"),errorPath:(0,ac._)`""`,opts:this.opts,self:this},p;try{this._compilations.add(t),(0,ult.validateFunctionCode)(u),s.optimize(this.opts.code.optimize);let f=s.toString();p=`${s.scopeRefs(Kf.default.scope)}return ${f}`,this.opts.code.process&&(p=this.opts.code.process(p,t));let h=new Function(`${Kf.default.self}`,`${Kf.default.scope}`,p)(this,this.scope.get());if(this.scope.value(c,{ref:h}),h.errors=null,h.schema=t.schema,h.schemaEnv=t,t.$async&&(h.$async=!0),this.opts.code.source===!0&&(h.source={validateName:c,validateCode:f,scopeValues:s._values}),this.opts.unevaluated){let{props:_,items:v}=u;h.evaluated={props:_ instanceof ac.Name?void 0:_,items:v instanceof ac.Name?void 0:v,dynamicProps:_ instanceof ac.Name,dynamicItems:v instanceof ac.Name},h.source&&(h.source.evaluated=(0,ac.stringify)(h.evaluated))}return t.validate=h,t}catch(f){throw delete t.validate,delete t.validateName,p&&this.logger.error("Error compiling schema, function code:",p),f}finally{this._compilations.delete(t)}}sa.compileSchema=qB;function llt(t,e,r){var n;r=(0,cc.resolveUrl)(this.opts.uriResolver,e,r);let o=t.refs[r];if(o)return o;let i=flt.call(this,t,r);if(i===void 0){let s=(n=t.localRefs)===null||n===void 0?void 0:n[r],{schemaId:a}=this.opts;s&&(i=new Bg({schema:s,schemaId:a,root:t,baseId:e}))}if(i!==void 0)return t.refs[r]=plt.call(this,i)}sa.resolveRef=llt;function plt(t){return(0,cc.inlineRef)(t.schema,this.opts.inlineRefs)?t.schema:t.validate?t:qB.call(this,t)}function ife(t){for(let e of this._compilations)if(dlt(e,t))return e}sa.getCompilingSchema=ife;function dlt(t,e){return t.schema===e.schema&&t.root===e.root&&t.baseId===e.baseId}function flt(t,e){let r;for(;typeof(r=this.refs[e])=="string";)e=r;return r||this.schemas[e]||nN.call(this,t,e)}function nN(t,e){let r=this.opts.uriResolver.parse(e),n=(0,cc._getFullPath)(this.opts.uriResolver,r),o=(0,cc.getFullPath)(this.opts.uriResolver,t.baseId,void 0);if(Object.keys(t.schema).length>0&&n===o)return FB.call(this,r,t);let i=(0,cc.normalizeId)(n),s=this.refs[i]||this.schemas[i];if(typeof s=="string"){let a=nN.call(this,t,s);return typeof a?.schema!="object"?void 0:FB.call(this,r,a)}if(typeof s?.schema=="object"){if(s.validate||qB.call(this,s),i===(0,cc.normalizeId)(e)){let{schema:a}=s,{schemaId:c}=this.opts,u=a[c];return u&&(o=(0,cc.resolveUrl)(this.opts.uriResolver,o,u)),new Bg({schema:a,schemaId:c,root:t,baseId:o})}return FB.call(this,r,s)}}sa.resolveSchema=nN;var mlt=new Set(["properties","patternProperties","enum","dependencies","definitions"]);function FB(t,{baseId:e,schema:r,root:n}){var o;if(((o=t.fragment)===null||o===void 0?void 0:o[0])!=="/")return;for(let a of t.fragment.slice(1).split("/")){if(typeof r=="boolean")return;let c=r[(0,ofe.unescapeFragment)(a)];if(c===void 0)return;r=c;let u=typeof r=="object"&&r[this.opts.schemaId];!mlt.has(a)&&u&&(e=(0,cc.resolveUrl)(this.opts.uriResolver,e,u))}let i;if(typeof r!="boolean"&&r.$ref&&!(0,ofe.schemaHasRulesButRef)(r,this.RULES)){let a=(0,cc.resolveUrl)(this.opts.uriResolver,e,r.$ref);i=nN.call(this,n,a)}let{schemaId:s}=this.opts;if(i=i||new Bg({schema:r,schemaId:s,root:n,baseId:e}),i.schema!==i.root.schema)return i}});var sfe=S((Slr,hlt)=>{hlt.exports={$id:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#",description:"Meta-schema for $data reference (JSON AnySchema extension proposal)",type:"object",required:["$data"],properties:{$data:{type:"string",anyOf:[{format:"relative-json-pointer"},{format:"json-pointer"}]}},additionalProperties:!1}});var cfe=S(BB=>{"use strict";Object.defineProperty(BB,"__esModule",{value:!0});var afe=i6();afe.code='require("ajv/dist/runtime/uri").default';BB.default=afe});var gfe=S(Oo=>{"use strict";Object.defineProperty(Oo,"__esModule",{value:!0});Oo.CodeGen=Oo.Name=Oo.nil=Oo.stringify=Oo.str=Oo._=Oo.KeywordCxt=void 0;var glt=YT();Object.defineProperty(Oo,"KeywordCxt",{enumerable:!0,get:function(){return glt.KeywordCxt}});var Gg=Pt();Object.defineProperty(Oo,"_",{enumerable:!0,get:function(){return Gg._}});Object.defineProperty(Oo,"str",{enumerable:!0,get:function(){return Gg.str}});Object.defineProperty(Oo,"stringify",{enumerable:!0,get:function(){return Gg.stringify}});Object.defineProperty(Oo,"nil",{enumerable:!0,get:function(){return Gg.nil}});Object.defineProperty(Oo,"Name",{enumerable:!0,get:function(){return Gg.Name}});Object.defineProperty(Oo,"CodeGen",{enumerable:!0,get:function(){return Gg.CodeGen}});var _lt=rN(),ffe=JT(),vlt=xB(),XT=oN(),Slt=Pt(),QT=WT(),iN=HT(),VB=lr(),ufe=sfe(),ylt=cfe(),mfe=(t,e)=>new RegExp(t,e);mfe.code="new RegExp";var Elt=["removeAdditional","useDefaults","coerceTypes"],Tlt=new Set(["validate","serialize","parse","wrapper","root","schema","keyword","pattern","formats","validate$data","func","obj","Error"]),blt={errorDataPath:"",format:"`validateFormats: false` can be used instead.",nullable:'"nullable" keyword is supported by default.',jsonPointers:"Deprecated jsPropertySyntax can be used instead.",extendRefs:"Deprecated ignoreKeywordsWithRef can be used instead.",missingRefs:"Pass empty schema with $id that should be ignored to ajv.addSchema.",processCode:"Use option `code: {process: (code, schemaEnv: object) => string}`",sourceCode:"Use option `code: {source: true}`",strictDefaults:"It is default now, see option `strict`.",strictKeywords:"It is default now, see option `strict`.",uniqueItems:'"uniqueItems" keyword is always validated.',unknownFormats:"Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).",cache:"Map is used as cache, schema object as key.",serialize:"Map is used as cache, schema object as key.",ajvErrors:"It is default now."},xlt={ignoreKeywordsWithRef:"",jsPropertySyntax:"",unicode:'"minLength"/"maxLength" account for unicode characters by default.'},lfe=200;function Alt(t){var e,r,n,o,i,s,a,c,u,p,f,m,h,_,v,E,x,w,I,N,$,B,G,he,Q;let me=t.strict,J=(e=t.code)===null||e===void 0?void 0:e.optimize,Oe=J===!0||J===void 0?1:J||0,pt=(n=(r=t.code)===null||r===void 0?void 0:r.regExp)!==null&&n!==void 0?n:mfe,Ke=(o=t.uriResolver)!==null&&o!==void 0?o:ylt.default;return{strictSchema:(s=(i=t.strictSchema)!==null&&i!==void 0?i:me)!==null&&s!==void 0?s:!0,strictNumbers:(c=(a=t.strictNumbers)!==null&&a!==void 0?a:me)!==null&&c!==void 0?c:!0,strictTypes:(p=(u=t.strictTypes)!==null&&u!==void 0?u:me)!==null&&p!==void 0?p:"log",strictTuples:(m=(f=t.strictTuples)!==null&&f!==void 0?f:me)!==null&&m!==void 0?m:"log",strictRequired:(_=(h=t.strictRequired)!==null&&h!==void 0?h:me)!==null&&_!==void 0?_:!1,code:t.code?{...t.code,optimize:Oe,regExp:pt}:{optimize:Oe,regExp:pt},loopRequired:(v=t.loopRequired)!==null&&v!==void 0?v:lfe,loopEnum:(E=t.loopEnum)!==null&&E!==void 0?E:lfe,meta:(x=t.meta)!==null&&x!==void 0?x:!0,messages:(w=t.messages)!==null&&w!==void 0?w:!0,inlineRefs:(I=t.inlineRefs)!==null&&I!==void 0?I:!0,schemaId:(N=t.schemaId)!==null&&N!==void 0?N:"$id",addUsedSchema:($=t.addUsedSchema)!==null&&$!==void 0?$:!0,validateSchema:(B=t.validateSchema)!==null&&B!==void 0?B:!0,validateFormats:(G=t.validateFormats)!==null&&G!==void 0?G:!0,unicodeRegExp:(he=t.unicodeRegExp)!==null&&he!==void 0?he:!0,int32range:(Q=t.int32range)!==null&&Q!==void 0?Q:!0,uriResolver:Ke}}var eb=class{constructor(e={}){this.schemas={},this.refs={},this.formats={},this._compilations=new Set,this._loading={},this._cache=new Map,e=this.opts={...e,...Alt(e)};let{es5:r,lines:n}=this.opts.code;this.scope=new Slt.ValueScope({scope:{},prefixes:Tlt,es5:r,lines:n}),this.logger=Nlt(e.logger);let o=e.validateFormats;e.validateFormats=!1,this.RULES=(0,vlt.getRules)(),pfe.call(this,blt,e,"NOT SUPPORTED"),pfe.call(this,xlt,e,"DEPRECATED","warn"),this._metaOpts=Ilt.call(this),e.formats&&Rlt.call(this),this._addVocabularies(),this._addDefaultMetaSchema(),e.keywords&&Plt.call(this,e.keywords),typeof e.meta=="object"&&this.addMetaSchema(e.meta),wlt.call(this),e.validateFormats=o}_addVocabularies(){this.addKeyword("$async")}_addDefaultMetaSchema(){let{$data:e,meta:r,schemaId:n}=this.opts,o=ufe;n==="id"&&(o={...ufe},o.id=o.$id,delete o.$id),r&&e&&this.addMetaSchema(o,o[n],!1)}defaultMeta(){let{meta:e,schemaId:r}=this.opts;return this.opts.defaultMeta=typeof e=="object"?e[r]||e:void 0}validate(e,r){let n;if(typeof e=="string"){if(n=this.getSchema(e),!n)throw new Error(`no schema with key or ref "${e}"`)}else n=this.compile(e);let o=n(r);return"$async"in n||(this.errors=n.errors),o}compile(e,r){let n=this._addSchema(e,r);return n.validate||this._compileSchemaEnv(n)}compileAsync(e,r){if(typeof this.opts.loadSchema!="function")throw new Error("options.loadSchema should be a function");let{loadSchema:n}=this.opts;return o.call(this,e,r);async function o(p,f){await i.call(this,p.$schema);let m=this._addSchema(p,f);return m.validate||s.call(this,m)}async function i(p){p&&!this.getSchema(p)&&await o.call(this,{$ref:p},!0)}async function s(p){try{return this._compileSchemaEnv(p)}catch(f){if(!(f instanceof ffe.default))throw f;return a.call(this,f),await c.call(this,f.missingSchema),s.call(this,p)}}function a({missingSchema:p,missingRef:f}){if(this.refs[p])throw new Error(`AnySchema ${p} is loaded but ${f} cannot be resolved`)}async function c(p){let f=await u.call(this,p);this.refs[p]||await i.call(this,f.$schema),this.refs[p]||this.addSchema(f,p,r)}async function u(p){let f=this._loading[p];if(f)return f;try{return await(this._loading[p]=n(p))}finally{delete this._loading[p]}}}addSchema(e,r,n,o=this.opts.validateSchema){if(Array.isArray(e)){for(let s of e)this.addSchema(s,void 0,n,o);return this}let i;if(typeof e=="object"){let{schemaId:s}=this.opts;if(i=e[s],i!==void 0&&typeof i!="string")throw new Error(`schema ${s} must be string`)}return r=(0,QT.normalizeId)(r||i),this._checkUnique(r),this.schemas[r]=this._addSchema(e,n,r,o,!0),this}addMetaSchema(e,r,n=this.opts.validateSchema){return this.addSchema(e,r,!0,n),this}validateSchema(e,r){if(typeof e=="boolean")return!0;let n;if(n=e.$schema,n!==void 0&&typeof n!="string")throw new Error("$schema must be a string");if(n=n||this.opts.defaultMeta||this.defaultMeta(),!n)return this.logger.warn("meta-schema not available"),this.errors=null,!0;let o=this.validate(n,e);if(!o&&r){let i="schema is invalid: "+this.errorsText();if(this.opts.validateSchema==="log")this.logger.error(i);else throw new Error(i)}return o}getSchema(e){let r;for(;typeof(r=dfe.call(this,e))=="string";)e=r;if(r===void 0){let{schemaId:n}=this.opts,o=new XT.SchemaEnv({schema:{},schemaId:n});if(r=XT.resolveSchema.call(this,o,e),!r)return;this.refs[e]=r}return r.validate||this._compileSchemaEnv(r)}removeSchema(e){if(e instanceof RegExp)return this._removeAllSchemas(this.schemas,e),this._removeAllSchemas(this.refs,e),this;switch(typeof e){case"undefined":return this._removeAllSchemas(this.schemas),this._removeAllSchemas(this.refs),this._cache.clear(),this;case"string":{let r=dfe.call(this,e);return typeof r=="object"&&this._cache.delete(r.schema),delete this.schemas[e],delete this.refs[e],this}case"object":{let r=e;this._cache.delete(r);let n=e[this.opts.schemaId];return n&&(n=(0,QT.normalizeId)(n),delete this.schemas[n],delete this.refs[n]),this}default:throw new Error("ajv.removeSchema: invalid parameter")}}addVocabulary(e){for(let r of e)this.addKeyword(r);return this}addKeyword(e,r){let n;if(typeof e=="string")n=e,typeof r=="object"&&(this.logger.warn("these parameters are deprecated, see docs for addKeyword"),r.keyword=n);else if(typeof e=="object"&&r===void 0){if(r=e,n=r.keyword,Array.isArray(n)&&!n.length)throw new Error("addKeywords: keyword must be string or non-empty array")}else throw new Error("invalid addKeywords parameters");if($lt.call(this,n,r),!r)return(0,VB.eachItem)(n,i=>GB.call(this,i)),this;Mlt.call(this,r);let o={...r,type:(0,iN.getJSONTypes)(r.type),schemaType:(0,iN.getJSONTypes)(r.schemaType)};return(0,VB.eachItem)(n,o.type.length===0?i=>GB.call(this,i,o):i=>o.type.forEach(s=>GB.call(this,i,o,s))),this}getKeyword(e){let r=this.RULES.all[e];return typeof r=="object"?r.definition:!!r}removeKeyword(e){let{RULES:r}=this;delete r.keywords[e],delete r.all[e];for(let n of r.rules){let o=n.rules.findIndex(i=>i.keyword===e);o>=0&&n.rules.splice(o,1)}return this}addFormat(e,r){return typeof r=="string"&&(r=new RegExp(r)),this.formats[e]=r,this}errorsText(e=this.errors,{separator:r=", ",dataVar:n="data"}={}){return!e||e.length===0?"No errors":e.map(o=>`${n}${o.instancePath} ${o.message}`).reduce((o,i)=>o+r+i)}$dataMetaSchema(e,r){let n=this.RULES.all;e=JSON.parse(JSON.stringify(e));for(let o of r){let i=o.split("/").slice(1),s=e;for(let a of i)s=s[a];for(let a in n){let c=n[a];if(typeof c!="object")continue;let{$data:u}=c.definition,p=s[a];u&&p&&(s[a]=hfe(p))}}return e}_removeAllSchemas(e,r){for(let n in e){let o=e[n];(!r||r.test(n))&&(typeof o=="string"?delete e[n]:o&&!o.meta&&(this._cache.delete(o.schema),delete e[n]))}}_addSchema(e,r,n,o=this.opts.validateSchema,i=this.opts.addUsedSchema){let s,{schemaId:a}=this.opts;if(typeof e=="object")s=e[a];else{if(this.opts.jtd)throw new Error("schema must be object");if(typeof e!="boolean")throw new Error("schema must be object or boolean")}let c=this._cache.get(e);if(c!==void 0)return c;n=(0,QT.normalizeId)(s||n);let u=QT.getSchemaRefs.call(this,e,n);return c=new XT.SchemaEnv({schema:e,schemaId:a,meta:r,baseId:n,localRefs:u}),this._cache.set(c.schema,c),i&&!n.startsWith("#")&&(n&&this._checkUnique(n),this.refs[n]=c),o&&this.validateSchema(e,!0),c}_checkUnique(e){if(this.schemas[e]||this.refs[e])throw new Error(`schema with key or id "${e}" already exists`)}_compileSchemaEnv(e){if(e.meta?this._compileMetaSchema(e):XT.compileSchema.call(this,e),!e.validate)throw new Error("ajv implementation error");return e.validate}_compileMetaSchema(e){let r=this.opts;this.opts=this._metaOpts;try{XT.compileSchema.call(this,e)}finally{this.opts=r}}};eb.ValidationError=_lt.default;eb.MissingRefError=ffe.default;Oo.default=eb;function pfe(t,e,r,n="error"){for(let o in t){let i=o;i in e&&this.logger[n](`${r}: option ${o}. ${t[i]}`)}}function dfe(t){return t=(0,QT.normalizeId)(t),this.schemas[t]||this.refs[t]}function wlt(){let t=this.opts.schemas;if(t)if(Array.isArray(t))this.addSchema(t);else for(let e in t)this.addSchema(t[e],e)}function Rlt(){for(let t in this.opts.formats){let e=this.opts.formats[t];e&&this.addFormat(t,e)}}function Plt(t){if(Array.isArray(t)){this.addVocabulary(t);return}this.logger.warn("keywords option as map is deprecated, pass array");for(let e in t){let r=t[e];r.keyword||(r.keyword=e),this.addKeyword(r)}}function Ilt(){let t={...this.opts};for(let e of Elt)delete t[e];return t}var Olt={log(){},warn(){},error(){}};function Nlt(t){if(t===!1)return Olt;if(t===void 0)return console;if(t.log&&t.warn&&t.error)return t;throw new Error("logger must implement log, warn and error methods")}var Clt=/^[a-z_$][a-z0-9_$:-]*$/i;function $lt(t,e){let{RULES:r}=this;if((0,VB.eachItem)(t,n=>{if(r.keywords[n])throw new Error(`Keyword ${n} is already defined`);if(!Clt.test(n))throw new Error(`Keyword ${n} has invalid name`)}),!!e&&e.$data&&!("code"in e||"validate"in e))throw new Error('$data keyword must have "code" or "validate" function')}function GB(t,e,r){var n;let o=e?.post;if(r&&o)throw new Error('keyword with "post" flag cannot have "type"');let{RULES:i}=this,s=o?i.post:i.rules.find(({type:c})=>c===r);if(s||(s={type:r,rules:[]},i.rules.push(s)),i.keywords[t]=!0,!e)return;let a={keyword:t,definition:{...e,type:(0,iN.getJSONTypes)(e.type),schemaType:(0,iN.getJSONTypes)(e.schemaType)}};e.before?klt.call(this,s,a,e.before):s.rules.push(a),i.all[t]=a,(n=e.implements)===null||n===void 0||n.forEach(c=>this.addKeyword(c))}function klt(t,e,r){let n=t.rules.findIndex(o=>o.keyword===r);n>=0?t.rules.splice(n,0,e):(t.rules.push(e),this.logger.warn(`rule ${r} is not defined`))}function Mlt(t){let{metaSchema:e}=t;e!==void 0&&(t.$data&&this.opts.$data&&(e=hfe(e)),t.validateSchema=this.compile(e,!0))}var Dlt={$ref:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#"};function hfe(t){return{anyOf:[t,Dlt]}}});var _fe=S(HB=>{"use strict";Object.defineProperty(HB,"__esModule",{value:!0});var Llt={keyword:"id",code(){throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID')}};HB.default=Llt});var Efe=S(Zf=>{"use strict";Object.defineProperty(Zf,"__esModule",{value:!0});Zf.callRef=Zf.getValidate=void 0;var Ult=JT(),vfe=ia(),Zi=Pt(),Vg=pl(),Sfe=oN(),sN=lr(),jlt={keyword:"$ref",schemaType:"string",code(t){let{gen:e,schema:r,it:n}=t,{baseId:o,schemaEnv:i,validateName:s,opts:a,self:c}=n,{root:u}=i;if((r==="#"||r==="#/")&&o===u.baseId)return f();let p=Sfe.resolveRef.call(c,u,o,r);if(p===void 0)throw new Ult.default(n.opts.uriResolver,o,r);if(p instanceof Sfe.SchemaEnv)return m(p);return h(p);function f(){if(i===u)return aN(t,s,i,i.$async);let _=e.scopeValue("root",{ref:u});return aN(t,(0,Zi._)`${_}.validate`,u,u.$async)}function m(_){let v=yfe(t,_);aN(t,v,_,_.$async)}function h(_){let v=e.scopeValue("schema",a.code.source===!0?{ref:_,code:(0,Zi.stringify)(_)}:{ref:_}),E=e.name("valid"),x=t.subschema({schema:_,dataTypes:[],schemaPath:Zi.nil,topSchemaRef:v,errSchemaPath:r},E);t.mergeEvaluated(x),t.ok(E)}}};function yfe(t,e){let{gen:r}=t;return e.validate?r.scopeValue("validate",{ref:e.validate}):(0,Zi._)`${r.scopeValue("wrapper",{ref:e})}.validate`}Zf.getValidate=yfe;function aN(t,e,r,n){let{gen:o,it:i}=t,{allErrors:s,schemaEnv:a,opts:c}=i,u=c.passContext?Vg.default.this:Zi.nil;n?p():f();function p(){if(!a.$async)throw new Error("async schema referenced by sync schema");let _=o.let("valid");o.try(()=>{o.code((0,Zi._)`await ${(0,vfe.callValidateCode)(t,e,u)}`),h(e),s||o.assign(_,!0)},v=>{o.if((0,Zi._)`!(${v} instanceof ${i.ValidationError})`,()=>o.throw(v)),m(v),s||o.assign(_,!1)}),t.ok(_)}function f(){t.result((0,vfe.callValidateCode)(t,e,u),()=>h(e),()=>m(e))}function m(_){let v=(0,Zi._)`${_}.errors`;o.assign(Vg.default.vErrors,(0,Zi._)`${Vg.default.vErrors} === null ? ${v} : ${Vg.default.vErrors}.concat(${v})`),o.assign(Vg.default.errors,(0,Zi._)`${Vg.default.vErrors}.length`)}function h(_){var v;if(!i.opts.unevaluated)return;let E=(v=r?.validate)===null||v===void 0?void 0:v.evaluated;if(i.props!==!0)if(E&&!E.dynamicProps)E.props!==void 0&&(i.props=sN.mergeEvaluated.props(o,E.props,i.props));else{let x=o.var("props",(0,Zi._)`${_}.evaluated.props`);i.props=sN.mergeEvaluated.props(o,x,i.props,Zi.Name)}if(i.items!==!0)if(E&&!E.dynamicItems)E.items!==void 0&&(i.items=sN.mergeEvaluated.items(o,E.items,i.items));else{let x=o.var("items",(0,Zi._)`${_}.evaluated.items`);i.items=sN.mergeEvaluated.items(o,x,i.items,Zi.Name)}}}Zf.callRef=aN;Zf.default=jlt});var Tfe=S(WB=>{"use strict";Object.defineProperty(WB,"__esModule",{value:!0});var zlt=_fe(),Flt=Efe(),qlt=["$schema","$id","$defs","$vocabulary",{keyword:"$comment"},"definitions",zlt.default,Flt.default];WB.default=qlt});var bfe=S(KB=>{"use strict";Object.defineProperty(KB,"__esModule",{value:!0});var cN=Pt(),qp=cN.operators,uN={maximum:{okStr:"<=",ok:qp.LTE,fail:qp.GT},minimum:{okStr:">=",ok:qp.GTE,fail:qp.LT},exclusiveMaximum:{okStr:"<",ok:qp.LT,fail:qp.GTE},exclusiveMinimum:{okStr:">",ok:qp.GT,fail:qp.LTE}},Blt={message:({keyword:t,schemaCode:e})=>(0,cN.str)`must be ${uN[t].okStr} ${e}`,params:({keyword:t,schemaCode:e})=>(0,cN._)`{comparison: ${uN[t].okStr}, limit: ${e}}`},Glt={keyword:Object.keys(uN),type:"number",schemaType:"number",$data:!0,error:Blt,code(t){let{keyword:e,data:r,schemaCode:n}=t;t.fail$data((0,cN._)`${r} ${uN[e].fail} ${n} || isNaN(${r})`)}};KB.default=Glt});var xfe=S(ZB=>{"use strict";Object.defineProperty(ZB,"__esModule",{value:!0});var tb=Pt(),Vlt={message:({schemaCode:t})=>(0,tb.str)`must be multiple of ${t}`,params:({schemaCode:t})=>(0,tb._)`{multipleOf: ${t}}`},Hlt={keyword:"multipleOf",type:"number",schemaType:"number",$data:!0,error:Vlt,code(t){let{gen:e,data:r,schemaCode:n,it:o}=t,i=o.opts.multipleOfPrecision,s=e.let("res"),a=i?(0,tb._)`Math.abs(Math.round(${s}) - ${s}) > 1e-${i}`:(0,tb._)`${s} !== parseInt(${s})`;t.fail$data((0,tb._)`(${n} === 0 || (${s} = ${r}/${n}, ${a}))`)}};ZB.default=Hlt});var wfe=S(YB=>{"use strict";Object.defineProperty(YB,"__esModule",{value:!0});function Afe(t){let e=t.length,r=0,n=0,o;for(;n=55296&&o<=56319&&n{"use strict";Object.defineProperty(JB,"__esModule",{value:!0});var Yf=Pt(),Wlt=lr(),Klt=wfe(),Zlt={message({keyword:t,schemaCode:e}){let r=t==="maxLength"?"more":"fewer";return(0,Yf.str)`must NOT have ${r} than ${e} characters`},params:({schemaCode:t})=>(0,Yf._)`{limit: ${t}}`},Ylt={keyword:["maxLength","minLength"],type:"string",schemaType:"number",$data:!0,error:Zlt,code(t){let{keyword:e,data:r,schemaCode:n,it:o}=t,i=e==="maxLength"?Yf.operators.GT:Yf.operators.LT,s=o.opts.unicode===!1?(0,Yf._)`${r}.length`:(0,Yf._)`${(0,Wlt.useFunc)(t.gen,Klt.default)}(${r})`;t.fail$data((0,Yf._)`${s} ${i} ${n}`)}};JB.default=Ylt});var Pfe=S(XB=>{"use strict";Object.defineProperty(XB,"__esModule",{value:!0});var Jlt=ia(),lN=Pt(),Xlt={message:({schemaCode:t})=>(0,lN.str)`must match pattern "${t}"`,params:({schemaCode:t})=>(0,lN._)`{pattern: ${t}}`},Qlt={keyword:"pattern",type:"string",schemaType:"string",$data:!0,error:Xlt,code(t){let{data:e,$data:r,schema:n,schemaCode:o,it:i}=t,s=i.opts.unicodeRegExp?"u":"",a=r?(0,lN._)`(new RegExp(${o}, ${s}))`:(0,Jlt.usePattern)(t,n);t.fail$data((0,lN._)`!${a}.test(${e})`)}};XB.default=Qlt});var Ife=S(QB=>{"use strict";Object.defineProperty(QB,"__esModule",{value:!0});var rb=Pt(),ept={message({keyword:t,schemaCode:e}){let r=t==="maxProperties"?"more":"fewer";return(0,rb.str)`must NOT have ${r} than ${e} properties`},params:({schemaCode:t})=>(0,rb._)`{limit: ${t}}`},tpt={keyword:["maxProperties","minProperties"],type:"object",schemaType:"number",$data:!0,error:ept,code(t){let{keyword:e,data:r,schemaCode:n}=t,o=e==="maxProperties"?rb.operators.GT:rb.operators.LT;t.fail$data((0,rb._)`Object.keys(${r}).length ${o} ${n}`)}};QB.default=tpt});var Ofe=S(eG=>{"use strict";Object.defineProperty(eG,"__esModule",{value:!0});var nb=ia(),ob=Pt(),rpt=lr(),npt={message:({params:{missingProperty:t}})=>(0,ob.str)`must have required property '${t}'`,params:({params:{missingProperty:t}})=>(0,ob._)`{missingProperty: ${t}}`},opt={keyword:"required",type:"object",schemaType:"array",$data:!0,error:npt,code(t){let{gen:e,schema:r,schemaCode:n,data:o,$data:i,it:s}=t,{opts:a}=s;if(!i&&r.length===0)return;let c=r.length>=a.loopRequired;if(s.allErrors?u():p(),a.strictRequired){let h=t.parentSchema.properties,{definedProperties:_}=t.it;for(let v of r)if(h?.[v]===void 0&&!_.has(v)){let E=s.schemaEnv.baseId+s.errSchemaPath,x=`required property "${v}" is not defined at "${E}" (strictRequired)`;(0,rpt.checkStrictMode)(s,x,s.opts.strictRequired)}}function u(){if(c||i)t.block$data(ob.nil,f);else for(let h of r)(0,nb.checkReportMissingProp)(t,h)}function p(){let h=e.let("missing");if(c||i){let _=e.let("valid",!0);t.block$data(_,()=>m(h,_)),t.ok(_)}else e.if((0,nb.checkMissingProp)(t,r,h)),(0,nb.reportMissingProp)(t,h),e.else()}function f(){e.forOf("prop",n,h=>{t.setParams({missingProperty:h}),e.if((0,nb.noPropertyInData)(e,o,h,a.ownProperties),()=>t.error())})}function m(h,_){t.setParams({missingProperty:h}),e.forOf(h,n,()=>{e.assign(_,(0,nb.propertyInData)(e,o,h,a.ownProperties)),e.if((0,ob.not)(_),()=>{t.error(),e.break()})},ob.nil)}}};eG.default=opt});var Nfe=S(tG=>{"use strict";Object.defineProperty(tG,"__esModule",{value:!0});var ib=Pt(),ipt={message({keyword:t,schemaCode:e}){let r=t==="maxItems"?"more":"fewer";return(0,ib.str)`must NOT have ${r} than ${e} items`},params:({schemaCode:t})=>(0,ib._)`{limit: ${t}}`},spt={keyword:["maxItems","minItems"],type:"array",schemaType:"number",$data:!0,error:ipt,code(t){let{keyword:e,data:r,schemaCode:n}=t,o=e==="maxItems"?ib.operators.GT:ib.operators.LT;t.fail$data((0,ib._)`${r}.length ${o} ${n}`)}};tG.default=spt});var pN=S(rG=>{"use strict";Object.defineProperty(rG,"__esModule",{value:!0});var Cfe=vT();Cfe.code='require("ajv/dist/runtime/equal").default';rG.default=Cfe});var $fe=S(oG=>{"use strict";Object.defineProperty(oG,"__esModule",{value:!0});var nG=HT(),No=Pt(),apt=lr(),cpt=pN(),upt={message:({params:{i:t,j:e}})=>(0,No.str)`must NOT have duplicate items (items ## ${e} and ${t} are identical)`,params:({params:{i:t,j:e}})=>(0,No._)`{i: ${t}, j: ${e}}`},lpt={keyword:"uniqueItems",type:"array",schemaType:"boolean",$data:!0,error:upt,code(t){let{gen:e,data:r,$data:n,schema:o,parentSchema:i,schemaCode:s,it:a}=t;if(!n&&!o)return;let c=e.let("valid"),u=i.items?(0,nG.getSchemaTypes)(i.items):[];t.block$data(c,p,(0,No._)`${s} === false`),t.ok(c);function p(){let _=e.let("i",(0,No._)`${r}.length`),v=e.let("j");t.setParams({i:_,j:v}),e.assign(c,!0),e.if((0,No._)`${_} > 1`,()=>(f()?m:h)(_,v))}function f(){return u.length>0&&!u.some(_=>_==="object"||_==="array")}function m(_,v){let E=e.name("item"),x=(0,nG.checkDataTypes)(u,E,a.opts.strictNumbers,nG.DataType.Wrong),w=e.const("indices",(0,No._)`{}`);e.for((0,No._)`;${_}--;`,()=>{e.let(E,(0,No._)`${r}[${_}]`),e.if(x,(0,No._)`continue`),u.length>1&&e.if((0,No._)`typeof ${E} == "string"`,(0,No._)`${E} += "_"`),e.if((0,No._)`typeof ${w}[${E}] == "number"`,()=>{e.assign(v,(0,No._)`${w}[${E}]`),t.error(),e.assign(c,!1).break()}).code((0,No._)`${w}[${E}] = ${_}`)})}function h(_,v){let E=(0,apt.useFunc)(e,cpt.default),x=e.name("outer");e.label(x).for((0,No._)`;${_}--;`,()=>e.for((0,No._)`${v} = ${_}; ${v}--;`,()=>e.if((0,No._)`${E}(${r}[${_}], ${r}[${v}])`,()=>{t.error(),e.assign(c,!1).break(x)})))}}};oG.default=lpt});var kfe=S(sG=>{"use strict";Object.defineProperty(sG,"__esModule",{value:!0});var iG=Pt(),ppt=lr(),dpt=pN(),fpt={message:"must be equal to constant",params:({schemaCode:t})=>(0,iG._)`{allowedValue: ${t}}`},mpt={keyword:"const",$data:!0,error:fpt,code(t){let{gen:e,data:r,$data:n,schemaCode:o,schema:i}=t;n||i&&typeof i=="object"?t.fail$data((0,iG._)`!${(0,ppt.useFunc)(e,dpt.default)}(${r}, ${o})`):t.fail((0,iG._)`${i} !== ${r}`)}};sG.default=mpt});var Mfe=S(aG=>{"use strict";Object.defineProperty(aG,"__esModule",{value:!0});var sb=Pt(),hpt=lr(),gpt=pN(),_pt={message:"must be equal to one of the allowed values",params:({schemaCode:t})=>(0,sb._)`{allowedValues: ${t}}`},vpt={keyword:"enum",schemaType:"array",$data:!0,error:_pt,code(t){let{gen:e,data:r,$data:n,schema:o,schemaCode:i,it:s}=t;if(!n&&o.length===0)throw new Error("enum must have non-empty array");let a=o.length>=s.opts.loopEnum,c,u=()=>c??(c=(0,hpt.useFunc)(e,gpt.default)),p;if(a||n)p=e.let("valid"),t.block$data(p,f);else{if(!Array.isArray(o))throw new Error("ajv implementation error");let h=e.const("vSchema",i);p=(0,sb.or)(...o.map((_,v)=>m(h,v)))}t.pass(p);function f(){e.assign(p,!1),e.forOf("v",i,h=>e.if((0,sb._)`${u()}(${r}, ${h})`,()=>e.assign(p,!0).break()))}function m(h,_){let v=o[_];return typeof v=="object"&&v!==null?(0,sb._)`${u()}(${r}, ${h}[${_}])`:(0,sb._)`${r} === ${v}`}}};aG.default=vpt});var Dfe=S(cG=>{"use strict";Object.defineProperty(cG,"__esModule",{value:!0});var Spt=bfe(),ypt=xfe(),Ept=Rfe(),Tpt=Pfe(),bpt=Ife(),xpt=Ofe(),Apt=Nfe(),wpt=$fe(),Rpt=kfe(),Ppt=Mfe(),Ipt=[Spt.default,ypt.default,Ept.default,Tpt.default,bpt.default,xpt.default,Apt.default,wpt.default,{keyword:"type",schemaType:["string","array"]},{keyword:"nullable",schemaType:"boolean"},Rpt.default,Ppt.default];cG.default=Ipt});var lG=S(ab=>{"use strict";Object.defineProperty(ab,"__esModule",{value:!0});ab.validateAdditionalItems=void 0;var Jf=Pt(),uG=lr(),Opt={message:({params:{len:t}})=>(0,Jf.str)`must NOT have more than ${t} items`,params:({params:{len:t}})=>(0,Jf._)`{limit: ${t}}`},Npt={keyword:"additionalItems",type:"array",schemaType:["boolean","object"],before:"uniqueItems",error:Opt,code(t){let{parentSchema:e,it:r}=t,{items:n}=e;if(!Array.isArray(n)){(0,uG.checkStrictMode)(r,'"additionalItems" is ignored when "items" is not an array of schemas');return}Lfe(t,n)}};function Lfe(t,e){let{gen:r,schema:n,data:o,keyword:i,it:s}=t;s.items=!0;let a=r.const("len",(0,Jf._)`${o}.length`);if(n===!1)t.setParams({len:e.length}),t.pass((0,Jf._)`${a} <= ${e.length}`);else if(typeof n=="object"&&!(0,uG.alwaysValidSchema)(s,n)){let u=r.var("valid",(0,Jf._)`${a} <= ${e.length}`);r.if((0,Jf.not)(u),()=>c(u)),t.ok(u)}function c(u){r.forRange("i",e.length,a,p=>{t.subschema({keyword:i,dataProp:p,dataPropType:uG.Type.Num},u),s.allErrors||r.if((0,Jf.not)(u),()=>r.break())})}}ab.validateAdditionalItems=Lfe;ab.default=Npt});var pG=S(cb=>{"use strict";Object.defineProperty(cb,"__esModule",{value:!0});cb.validateTuple=void 0;var Ufe=Pt(),dN=lr(),Cpt=ia(),$pt={keyword:"items",type:"array",schemaType:["object","array","boolean"],before:"uniqueItems",code(t){let{schema:e,it:r}=t;if(Array.isArray(e))return jfe(t,"additionalItems",e);r.items=!0,!(0,dN.alwaysValidSchema)(r,e)&&t.ok((0,Cpt.validateArray)(t))}};function jfe(t,e,r=t.schema){let{gen:n,parentSchema:o,data:i,keyword:s,it:a}=t;p(o),a.opts.unevaluated&&r.length&&a.items!==!0&&(a.items=dN.mergeEvaluated.items(n,r.length,a.items));let c=n.name("valid"),u=n.const("len",(0,Ufe._)`${i}.length`);r.forEach((f,m)=>{(0,dN.alwaysValidSchema)(a,f)||(n.if((0,Ufe._)`${u} > ${m}`,()=>t.subschema({keyword:s,schemaProp:m,dataProp:m},c)),t.ok(c))});function p(f){let{opts:m,errSchemaPath:h}=a,_=r.length,v=_===f.minItems&&(_===f.maxItems||f[e]===!1);if(m.strictTuples&&!v){let E=`"${s}" is ${_}-tuple, but minItems or maxItems/${e} are not specified or different at path "${h}"`;(0,dN.checkStrictMode)(a,E,m.strictTuples)}}}cb.validateTuple=jfe;cb.default=$pt});var zfe=S(dG=>{"use strict";Object.defineProperty(dG,"__esModule",{value:!0});var kpt=pG(),Mpt={keyword:"prefixItems",type:"array",schemaType:["array"],before:"uniqueItems",code:t=>(0,kpt.validateTuple)(t,"items")};dG.default=Mpt});var qfe=S(fG=>{"use strict";Object.defineProperty(fG,"__esModule",{value:!0});var Ffe=Pt(),Dpt=lr(),Lpt=ia(),Upt=lG(),jpt={message:({params:{len:t}})=>(0,Ffe.str)`must NOT have more than ${t} items`,params:({params:{len:t}})=>(0,Ffe._)`{limit: ${t}}`},zpt={keyword:"items",type:"array",schemaType:["object","boolean"],before:"uniqueItems",error:jpt,code(t){let{schema:e,parentSchema:r,it:n}=t,{prefixItems:o}=r;n.items=!0,!(0,Dpt.alwaysValidSchema)(n,e)&&(o?(0,Upt.validateAdditionalItems)(t,o):t.ok((0,Lpt.validateArray)(t)))}};fG.default=zpt});var Bfe=S(mG=>{"use strict";Object.defineProperty(mG,"__esModule",{value:!0});var aa=Pt(),fN=lr(),Fpt={message:({params:{min:t,max:e}})=>e===void 0?(0,aa.str)`must contain at least ${t} valid item(s)`:(0,aa.str)`must contain at least ${t} and no more than ${e} valid item(s)`,params:({params:{min:t,max:e}})=>e===void 0?(0,aa._)`{minContains: ${t}}`:(0,aa._)`{minContains: ${t}, maxContains: ${e}}`},qpt={keyword:"contains",type:"array",schemaType:["object","boolean"],before:"uniqueItems",trackErrors:!0,error:Fpt,code(t){let{gen:e,schema:r,parentSchema:n,data:o,it:i}=t,s,a,{minContains:c,maxContains:u}=n;i.opts.next?(s=c===void 0?1:c,a=u):s=1;let p=e.const("len",(0,aa._)`${o}.length`);if(t.setParams({min:s,max:a}),a===void 0&&s===0){(0,fN.checkStrictMode)(i,'"minContains" == 0 without "maxContains": "contains" keyword ignored');return}if(a!==void 0&&s>a){(0,fN.checkStrictMode)(i,'"minContains" > "maxContains" is always invalid'),t.fail();return}if((0,fN.alwaysValidSchema)(i,r)){let v=(0,aa._)`${p} >= ${s}`;a!==void 0&&(v=(0,aa._)`${v} && ${p} <= ${a}`),t.pass(v);return}i.items=!0;let f=e.name("valid");a===void 0&&s===1?h(f,()=>e.if(f,()=>e.break())):s===0?(e.let(f,!0),a!==void 0&&e.if((0,aa._)`${o}.length > 0`,m)):(e.let(f,!1),m()),t.result(f,()=>t.reset());function m(){let v=e.name("_valid"),E=e.let("count",0);h(v,()=>e.if(v,()=>_(E)))}function h(v,E){e.forRange("i",0,p,x=>{t.subschema({keyword:"contains",dataProp:x,dataPropType:fN.Type.Num,compositeRule:!0},v),E()})}function _(v){e.code((0,aa._)`${v}++`),a===void 0?e.if((0,aa._)`${v} >= ${s}`,()=>e.assign(f,!0).break()):(e.if((0,aa._)`${v} > ${a}`,()=>e.assign(f,!1).break()),s===1?e.assign(f,!0):e.if((0,aa._)`${v} >= ${s}`,()=>e.assign(f,!0)))}}};mG.default=qpt});var Hfe=S(Kc=>{"use strict";Object.defineProperty(Kc,"__esModule",{value:!0});Kc.validateSchemaDeps=Kc.validatePropertyDeps=Kc.error=void 0;var hG=Pt(),Bpt=lr(),ub=ia();Kc.error={message:({params:{property:t,depsCount:e,deps:r}})=>{let n=e===1?"property":"properties";return(0,hG.str)`must have ${n} ${r} when property ${t} is present`},params:({params:{property:t,depsCount:e,deps:r,missingProperty:n}})=>(0,hG._)`{property: ${t}, - missingProperty: ${n}, - depsCount: ${e}, - deps: ${r}}`};var Gpt={keyword:"dependencies",type:"object",schemaType:"object",error:Kc.error,code(t){let[e,r]=Vpt(t);Gfe(t,e),Vfe(t,r)}};function Vpt({schema:t}){let e={},r={};for(let n in t){if(n==="__proto__")continue;let o=Array.isArray(t[n])?e:r;o[n]=t[n]}return[e,r]}function Gfe(t,e=t.schema){let{gen:r,data:n,it:o}=t;if(Object.keys(e).length===0)return;let i=r.let("missing");for(let s in e){let a=e[s];if(a.length===0)continue;let c=(0,ub.propertyInData)(r,n,s,o.opts.ownProperties);t.setParams({property:s,depsCount:a.length,deps:a.join(", ")}),o.allErrors?r.if(c,()=>{for(let u of a)(0,ub.checkReportMissingProp)(t,u)}):(r.if((0,hG._)`${c} && (${(0,ub.checkMissingProp)(t,a,i)})`),(0,ub.reportMissingProp)(t,i),r.else())}}Kc.validatePropertyDeps=Gfe;function Vfe(t,e=t.schema){let{gen:r,data:n,keyword:o,it:i}=t,s=r.name("valid");for(let a in e)(0,Bpt.alwaysValidSchema)(i,e[a])||(r.if((0,ub.propertyInData)(r,n,a,i.opts.ownProperties),()=>{let c=t.subschema({keyword:o,schemaProp:a},s);t.mergeValidEvaluated(c,s)},()=>r.var(s,!0)),t.ok(s))}Kc.validateSchemaDeps=Vfe;Kc.default=Gpt});var Kfe=S(gG=>{"use strict";Object.defineProperty(gG,"__esModule",{value:!0});var Wfe=Pt(),Hpt=lr(),Wpt={message:"property name must be valid",params:({params:t})=>(0,Wfe._)`{propertyName: ${t.propertyName}}`},Kpt={keyword:"propertyNames",type:"object",schemaType:["object","boolean"],error:Wpt,code(t){let{gen:e,schema:r,data:n,it:o}=t;if((0,Hpt.alwaysValidSchema)(o,r))return;let i=e.name("valid");e.forIn("key",n,s=>{t.setParams({propertyName:s}),t.subschema({keyword:"propertyNames",data:s,dataTypes:["string"],propertyName:s,compositeRule:!0},i),e.if((0,Wfe.not)(i),()=>{t.error(!0),o.allErrors||e.break()})}),t.ok(i)}};gG.default=Kpt});var vG=S(_G=>{"use strict";Object.defineProperty(_G,"__esModule",{value:!0});var mN=ia(),uc=Pt(),Zpt=pl(),hN=lr(),Ypt={message:"must NOT have additional properties",params:({params:t})=>(0,uc._)`{additionalProperty: ${t.additionalProperty}}`},Jpt={keyword:"additionalProperties",type:["object"],schemaType:["boolean","object"],allowUndefined:!0,trackErrors:!0,error:Ypt,code(t){let{gen:e,schema:r,parentSchema:n,data:o,errsCount:i,it:s}=t;if(!i)throw new Error("ajv implementation error");let{allErrors:a,opts:c}=s;if(s.props=!0,c.removeAdditional!=="all"&&(0,hN.alwaysValidSchema)(s,r))return;let u=(0,mN.allSchemaProperties)(n.properties),p=(0,mN.allSchemaProperties)(n.patternProperties);f(),t.ok((0,uc._)`${i} === ${Zpt.default.errors}`);function f(){e.forIn("key",o,E=>{!u.length&&!p.length?_(E):e.if(m(E),()=>_(E))})}function m(E){let x;if(u.length>8){let w=(0,hN.schemaRefOrVal)(s,n.properties,"properties");x=(0,mN.isOwnProperty)(e,w,E)}else u.length?x=(0,uc.or)(...u.map(w=>(0,uc._)`${E} === ${w}`)):x=uc.nil;return p.length&&(x=(0,uc.or)(x,...p.map(w=>(0,uc._)`${(0,mN.usePattern)(t,w)}.test(${E})`))),(0,uc.not)(x)}function h(E){e.code((0,uc._)`delete ${o}[${E}]`)}function _(E){if(c.removeAdditional==="all"||c.removeAdditional&&r===!1){h(E);return}if(r===!1){t.setParams({additionalProperty:E}),t.error(),a||e.break();return}if(typeof r=="object"&&!(0,hN.alwaysValidSchema)(s,r)){let x=e.name("valid");c.removeAdditional==="failing"?(v(E,x,!1),e.if((0,uc.not)(x),()=>{t.reset(),h(E)})):(v(E,x),a||e.if((0,uc.not)(x),()=>e.break()))}}function v(E,x,w){let I={keyword:"additionalProperties",dataProp:E,dataPropType:hN.Type.Str};w===!1&&Object.assign(I,{compositeRule:!0,createErrors:!1,allErrors:!1}),t.subschema(I,x)}}};_G.default=Jpt});var Jfe=S(yG=>{"use strict";Object.defineProperty(yG,"__esModule",{value:!0});var Xpt=YT(),Zfe=ia(),SG=lr(),Yfe=vG(),Qpt={keyword:"properties",type:"object",schemaType:"object",code(t){let{gen:e,schema:r,parentSchema:n,data:o,it:i}=t;i.opts.removeAdditional==="all"&&n.additionalProperties===void 0&&Yfe.default.code(new Xpt.KeywordCxt(i,Yfe.default,"additionalProperties"));let s=(0,Zfe.allSchemaProperties)(r);for(let f of s)i.definedProperties.add(f);i.opts.unevaluated&&s.length&&i.props!==!0&&(i.props=SG.mergeEvaluated.props(e,(0,SG.toHash)(s),i.props));let a=s.filter(f=>!(0,SG.alwaysValidSchema)(i,r[f]));if(a.length===0)return;let c=e.name("valid");for(let f of a)u(f)?p(f):(e.if((0,Zfe.propertyInData)(e,o,f,i.opts.ownProperties)),p(f),i.allErrors||e.else().var(c,!0),e.endIf()),t.it.definedProperties.add(f),t.ok(c);function u(f){return i.opts.useDefaults&&!i.compositeRule&&r[f].default!==void 0}function p(f){t.subschema({keyword:"properties",schemaProp:f,dataProp:f},c)}}};yG.default=Qpt});var tme=S(EG=>{"use strict";Object.defineProperty(EG,"__esModule",{value:!0});var Xfe=ia(),gN=Pt(),Qfe=lr(),eme=lr(),edt={keyword:"patternProperties",type:"object",schemaType:"object",code(t){let{gen:e,schema:r,data:n,parentSchema:o,it:i}=t,{opts:s}=i,a=(0,Xfe.allSchemaProperties)(r),c=a.filter(v=>(0,Qfe.alwaysValidSchema)(i,r[v]));if(a.length===0||c.length===a.length&&(!i.opts.unevaluated||i.props===!0))return;let u=s.strictSchema&&!s.allowMatchingProperties&&o.properties,p=e.name("valid");i.props!==!0&&!(i.props instanceof gN.Name)&&(i.props=(0,eme.evaluatedPropsToName)(e,i.props));let{props:f}=i;m();function m(){for(let v of a)u&&h(v),i.allErrors?_(v):(e.var(p,!0),_(v),e.if(p))}function h(v){for(let E in u)new RegExp(v).test(E)&&(0,Qfe.checkStrictMode)(i,`property ${E} matches pattern ${v} (use allowMatchingProperties)`)}function _(v){e.forIn("key",n,E=>{e.if((0,gN._)`${(0,Xfe.usePattern)(t,v)}.test(${E})`,()=>{let x=c.includes(v);x||t.subschema({keyword:"patternProperties",schemaProp:v,dataProp:E,dataPropType:eme.Type.Str},p),i.opts.unevaluated&&f!==!0?e.assign((0,gN._)`${f}[${E}]`,!0):!x&&!i.allErrors&&e.if((0,gN.not)(p),()=>e.break())})})}}};EG.default=edt});var rme=S(TG=>{"use strict";Object.defineProperty(TG,"__esModule",{value:!0});var tdt=lr(),rdt={keyword:"not",schemaType:["object","boolean"],trackErrors:!0,code(t){let{gen:e,schema:r,it:n}=t;if((0,tdt.alwaysValidSchema)(n,r)){t.fail();return}let o=e.name("valid");t.subschema({keyword:"not",compositeRule:!0,createErrors:!1,allErrors:!1},o),t.failResult(o,()=>t.reset(),()=>t.error())},error:{message:"must NOT be valid"}};TG.default=rdt});var nme=S(bG=>{"use strict";Object.defineProperty(bG,"__esModule",{value:!0});var ndt=ia(),odt={keyword:"anyOf",schemaType:"array",trackErrors:!0,code:ndt.validateUnion,error:{message:"must match a schema in anyOf"}};bG.default=odt});var ome=S(xG=>{"use strict";Object.defineProperty(xG,"__esModule",{value:!0});var _N=Pt(),idt=lr(),sdt={message:"must match exactly one schema in oneOf",params:({params:t})=>(0,_N._)`{passingSchemas: ${t.passing}}`},adt={keyword:"oneOf",schemaType:"array",trackErrors:!0,error:sdt,code(t){let{gen:e,schema:r,parentSchema:n,it:o}=t;if(!Array.isArray(r))throw new Error("ajv implementation error");if(o.opts.discriminator&&n.discriminator)return;let i=r,s=e.let("valid",!1),a=e.let("passing",null),c=e.name("_valid");t.setParams({passing:a}),e.block(u),t.result(s,()=>t.reset(),()=>t.error(!0));function u(){i.forEach((p,f)=>{let m;(0,idt.alwaysValidSchema)(o,p)?e.var(c,!0):m=t.subschema({keyword:"oneOf",schemaProp:f,compositeRule:!0},c),f>0&&e.if((0,_N._)`${c} && ${s}`).assign(s,!1).assign(a,(0,_N._)`[${a}, ${f}]`).else(),e.if(c,()=>{e.assign(s,!0),e.assign(a,f),m&&t.mergeEvaluated(m,_N.Name)})})}}};xG.default=adt});var ime=S(AG=>{"use strict";Object.defineProperty(AG,"__esModule",{value:!0});var cdt=lr(),udt={keyword:"allOf",schemaType:"array",code(t){let{gen:e,schema:r,it:n}=t;if(!Array.isArray(r))throw new Error("ajv implementation error");let o=e.name("valid");r.forEach((i,s)=>{if((0,cdt.alwaysValidSchema)(n,i))return;let a=t.subschema({keyword:"allOf",schemaProp:s},o);t.ok(o),t.mergeEvaluated(a)})}};AG.default=udt});var cme=S(wG=>{"use strict";Object.defineProperty(wG,"__esModule",{value:!0});var vN=Pt(),ame=lr(),ldt={message:({params:t})=>(0,vN.str)`must match "${t.ifClause}" schema`,params:({params:t})=>(0,vN._)`{failingKeyword: ${t.ifClause}}`},pdt={keyword:"if",schemaType:["object","boolean"],trackErrors:!0,error:ldt,code(t){let{gen:e,parentSchema:r,it:n}=t;r.then===void 0&&r.else===void 0&&(0,ame.checkStrictMode)(n,'"if" without "then" and "else" is ignored');let o=sme(n,"then"),i=sme(n,"else");if(!o&&!i)return;let s=e.let("valid",!0),a=e.name("_valid");if(c(),t.reset(),o&&i){let p=e.let("ifClause");t.setParams({ifClause:p}),e.if(a,u("then",p),u("else",p))}else o?e.if(a,u("then")):e.if((0,vN.not)(a),u("else"));t.pass(s,()=>t.error(!0));function c(){let p=t.subschema({keyword:"if",compositeRule:!0,createErrors:!1,allErrors:!1},a);t.mergeEvaluated(p)}function u(p,f){return()=>{let m=t.subschema({keyword:p},a);e.assign(s,a),t.mergeValidEvaluated(m,s),f?e.assign(f,(0,vN._)`${p}`):t.setParams({ifClause:p})}}}};function sme(t,e){let r=t.schema[e];return r!==void 0&&!(0,ame.alwaysValidSchema)(t,r)}wG.default=pdt});var ume=S(RG=>{"use strict";Object.defineProperty(RG,"__esModule",{value:!0});var ddt=lr(),fdt={keyword:["then","else"],schemaType:["object","boolean"],code({keyword:t,parentSchema:e,it:r}){e.if===void 0&&(0,ddt.checkStrictMode)(r,`"${t}" without "if" is ignored`)}};RG.default=fdt});var lme=S(PG=>{"use strict";Object.defineProperty(PG,"__esModule",{value:!0});var mdt=lG(),hdt=zfe(),gdt=pG(),_dt=qfe(),vdt=Bfe(),Sdt=Hfe(),ydt=Kfe(),Edt=vG(),Tdt=Jfe(),bdt=tme(),xdt=rme(),Adt=nme(),wdt=ome(),Rdt=ime(),Pdt=cme(),Idt=ume();function Odt(t=!1){let e=[xdt.default,Adt.default,wdt.default,Rdt.default,Pdt.default,Idt.default,ydt.default,Edt.default,Sdt.default,Tdt.default,bdt.default];return t?e.push(hdt.default,_dt.default):e.push(mdt.default,gdt.default),e.push(vdt.default),e}PG.default=Odt});var pme=S(IG=>{"use strict";Object.defineProperty(IG,"__esModule",{value:!0});var Pn=Pt(),Ndt={message:({schemaCode:t})=>(0,Pn.str)`must match format "${t}"`,params:({schemaCode:t})=>(0,Pn._)`{format: ${t}}`},Cdt={keyword:"format",type:["number","string"],schemaType:"string",$data:!0,error:Ndt,code(t,e){let{gen:r,data:n,$data:o,schema:i,schemaCode:s,it:a}=t,{opts:c,errSchemaPath:u,schemaEnv:p,self:f}=a;if(!c.validateFormats)return;o?m():h();function m(){let _=r.scopeValue("formats",{ref:f.formats,code:c.code.formats}),v=r.const("fDef",(0,Pn._)`${_}[${s}]`),E=r.let("fType"),x=r.let("format");r.if((0,Pn._)`typeof ${v} == "object" && !(${v} instanceof RegExp)`,()=>r.assign(E,(0,Pn._)`${v}.type || "string"`).assign(x,(0,Pn._)`${v}.validate`),()=>r.assign(E,(0,Pn._)`"string"`).assign(x,v)),t.fail$data((0,Pn.or)(w(),I()));function w(){return c.strictSchema===!1?Pn.nil:(0,Pn._)`${s} && !${x}`}function I(){let N=p.$async?(0,Pn._)`(${v}.async ? await ${x}(${n}) : ${x}(${n}))`:(0,Pn._)`${x}(${n})`,$=(0,Pn._)`(typeof ${x} == "function" ? ${N} : ${x}.test(${n}))`;return(0,Pn._)`${x} && ${x} !== true && ${E} === ${e} && !${$}`}}function h(){let _=f.formats[i];if(!_){w();return}if(_===!0)return;let[v,E,x]=I(_);v===e&&t.pass(N());function w(){if(c.strictSchema===!1){f.logger.warn($());return}throw new Error($());function $(){return`unknown format "${i}" ignored in schema at path "${u}"`}}function I($){let B=$ instanceof RegExp?(0,Pn.regexpCode)($):c.code.formats?(0,Pn._)`${c.code.formats}${(0,Pn.getProperty)(i)}`:void 0,G=r.scopeValue("formats",{key:i,ref:$,code:B});return typeof $=="object"&&!($ instanceof RegExp)?[$.type||"string",$.validate,(0,Pn._)`${G}.validate`]:["string",$,G]}function N(){if(typeof _=="object"&&!(_ instanceof RegExp)&&_.async){if(!p.$async)throw new Error("async format in sync schema");return(0,Pn._)`await ${x}(${n})`}return typeof E=="function"?(0,Pn._)`${x}(${n})`:(0,Pn._)`${x}.test(${n})`}}}};IG.default=Cdt});var dme=S(OG=>{"use strict";Object.defineProperty(OG,"__esModule",{value:!0});var $dt=pme(),kdt=[$dt.default];OG.default=kdt});var fme=S(Hg=>{"use strict";Object.defineProperty(Hg,"__esModule",{value:!0});Hg.contentVocabulary=Hg.metadataVocabulary=void 0;Hg.metadataVocabulary=["title","description","default","deprecated","readOnly","writeOnly","examples"];Hg.contentVocabulary=["contentMediaType","contentEncoding","contentSchema"]});var hme=S(NG=>{"use strict";Object.defineProperty(NG,"__esModule",{value:!0});var Mdt=Tfe(),Ddt=Dfe(),Ldt=lme(),Udt=dme(),mme=fme(),jdt=[Mdt.default,Ddt.default,(0,Ldt.default)(),Udt.default,mme.metadataVocabulary,mme.contentVocabulary];NG.default=jdt});var _me=S(SN=>{"use strict";Object.defineProperty(SN,"__esModule",{value:!0});SN.DiscrError=void 0;var gme;(function(t){t.Tag="tag",t.Mapping="mapping"})(gme||(SN.DiscrError=gme={}))});var Sme=S($G=>{"use strict";Object.defineProperty($G,"__esModule",{value:!0});var Wg=Pt(),CG=_me(),vme=oN(),zdt=JT(),Fdt=lr(),qdt={message:({params:{discrError:t,tagName:e}})=>t===CG.DiscrError.Tag?`tag "${e}" must be string`:`value of tag "${e}" must be in oneOf`,params:({params:{discrError:t,tag:e,tagName:r}})=>(0,Wg._)`{error: ${t}, tag: ${r}, tagValue: ${e}}`},Bdt={keyword:"discriminator",type:"object",schemaType:"object",error:qdt,code(t){let{gen:e,data:r,schema:n,parentSchema:o,it:i}=t,{oneOf:s}=o;if(!i.opts.discriminator)throw new Error("discriminator: requires discriminator option");let a=n.propertyName;if(typeof a!="string")throw new Error("discriminator: requires propertyName");if(n.mapping)throw new Error("discriminator: mapping is not supported");if(!s)throw new Error("discriminator: requires oneOf keyword");let c=e.let("valid",!1),u=e.const("tag",(0,Wg._)`${r}${(0,Wg.getProperty)(a)}`);e.if((0,Wg._)`typeof ${u} == "string"`,()=>p(),()=>t.error(!1,{discrError:CG.DiscrError.Tag,tag:u,tagName:a})),t.ok(c);function p(){let h=m();e.if(!1);for(let _ in h)e.elseIf((0,Wg._)`${u} === ${_}`),e.assign(c,f(h[_]));e.else(),t.error(!1,{discrError:CG.DiscrError.Mapping,tag:u,tagName:a}),e.endIf()}function f(h){let _=e.name("valid"),v=t.subschema({keyword:"oneOf",schemaProp:h},_);return t.mergeEvaluated(v,Wg.Name),_}function m(){var h;let _={},v=x(o),E=!0;for(let N=0;N{Gdt.exports={$schema:"http://json-schema.org/draft-07/schema#",$id:"http://json-schema.org/draft-07/schema#",title:"Core schema meta-schema",definitions:{schemaArray:{type:"array",minItems:1,items:{$ref:"#"}},nonNegativeInteger:{type:"integer",minimum:0},nonNegativeIntegerDefault0:{allOf:[{$ref:"#/definitions/nonNegativeInteger"},{default:0}]},simpleTypes:{enum:["array","boolean","integer","null","number","object","string"]},stringArray:{type:"array",items:{type:"string"},uniqueItems:!0,default:[]}},type:["object","boolean"],properties:{$id:{type:"string",format:"uri-reference"},$schema:{type:"string",format:"uri"},$ref:{type:"string",format:"uri-reference"},$comment:{type:"string"},title:{type:"string"},description:{type:"string"},default:!0,readOnly:{type:"boolean",default:!1},examples:{type:"array",items:!0},multipleOf:{type:"number",exclusiveMinimum:0},maximum:{type:"number"},exclusiveMaximum:{type:"number"},minimum:{type:"number"},exclusiveMinimum:{type:"number"},maxLength:{$ref:"#/definitions/nonNegativeInteger"},minLength:{$ref:"#/definitions/nonNegativeIntegerDefault0"},pattern:{type:"string",format:"regex"},additionalItems:{$ref:"#"},items:{anyOf:[{$ref:"#"},{$ref:"#/definitions/schemaArray"}],default:!0},maxItems:{$ref:"#/definitions/nonNegativeInteger"},minItems:{$ref:"#/definitions/nonNegativeIntegerDefault0"},uniqueItems:{type:"boolean",default:!1},contains:{$ref:"#"},maxProperties:{$ref:"#/definitions/nonNegativeInteger"},minProperties:{$ref:"#/definitions/nonNegativeIntegerDefault0"},required:{$ref:"#/definitions/stringArray"},additionalProperties:{$ref:"#"},definitions:{type:"object",additionalProperties:{$ref:"#"},default:{}},properties:{type:"object",additionalProperties:{$ref:"#"},default:{}},patternProperties:{type:"object",additionalProperties:{$ref:"#"},propertyNames:{format:"regex"},default:{}},dependencies:{type:"object",additionalProperties:{anyOf:[{$ref:"#"},{$ref:"#/definitions/stringArray"}]}},propertyNames:{$ref:"#"},const:!0,enum:{type:"array",items:!0,minItems:1,uniqueItems:!0},type:{anyOf:[{$ref:"#/definitions/simpleTypes"},{type:"array",items:{$ref:"#/definitions/simpleTypes"},minItems:1,uniqueItems:!0}]},format:{type:"string"},contentMediaType:{type:"string"},contentEncoding:{type:"string"},if:{$ref:"#"},then:{$ref:"#"},else:{$ref:"#"},allOf:{$ref:"#/definitions/schemaArray"},anyOf:{$ref:"#/definitions/schemaArray"},oneOf:{$ref:"#/definitions/schemaArray"},not:{$ref:"#"}},default:!0}});var Tme=S((Yr,kG)=>{"use strict";Object.defineProperty(Yr,"__esModule",{value:!0});Yr.MissingRefError=Yr.ValidationError=Yr.CodeGen=Yr.Name=Yr.nil=Yr.stringify=Yr.str=Yr._=Yr.KeywordCxt=Yr.Ajv=void 0;var Vdt=gfe(),Hdt=hme(),Wdt=Sme(),Eme=yme(),Kdt=["/properties"],yN="http://json-schema.org/draft-07/schema",Kg=class extends Vdt.default{_addVocabularies(){super._addVocabularies(),Hdt.default.forEach(e=>this.addVocabulary(e)),this.opts.discriminator&&this.addKeyword(Wdt.default)}_addDefaultMetaSchema(){if(super._addDefaultMetaSchema(),!this.opts.meta)return;let e=this.opts.$data?this.$dataMetaSchema(Eme,Kdt):Eme;this.addMetaSchema(e,yN,!1),this.refs["http://json-schema.org/schema"]=yN}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(yN)?yN:void 0)}};Yr.Ajv=Kg;kG.exports=Yr=Kg;kG.exports.Ajv=Kg;Object.defineProperty(Yr,"__esModule",{value:!0});Yr.default=Kg;var Zdt=YT();Object.defineProperty(Yr,"KeywordCxt",{enumerable:!0,get:function(){return Zdt.KeywordCxt}});var Zg=Pt();Object.defineProperty(Yr,"_",{enumerable:!0,get:function(){return Zg._}});Object.defineProperty(Yr,"str",{enumerable:!0,get:function(){return Zg.str}});Object.defineProperty(Yr,"stringify",{enumerable:!0,get:function(){return Zg.stringify}});Object.defineProperty(Yr,"nil",{enumerable:!0,get:function(){return Zg.nil}});Object.defineProperty(Yr,"Name",{enumerable:!0,get:function(){return Zg.Name}});Object.defineProperty(Yr,"CodeGen",{enumerable:!0,get:function(){return Zg.CodeGen}});var Ydt=rN();Object.defineProperty(Yr,"ValidationError",{enumerable:!0,get:function(){return Ydt.default}});var Jdt=JT();Object.defineProperty(Yr,"MissingRefError",{enumerable:!0,get:function(){return Jdt.default}})});var bme=S(Yg=>{"use strict";Object.defineProperty(Yg,"__esModule",{value:!0});Yg.formatLimitDefinition=void 0;var Xdt=Tme(),lc=Pt(),Bp=lc.operators,EN={formatMaximum:{okStr:"<=",ok:Bp.LTE,fail:Bp.GT},formatMinimum:{okStr:">=",ok:Bp.GTE,fail:Bp.LT},formatExclusiveMaximum:{okStr:"<",ok:Bp.LT,fail:Bp.GTE},formatExclusiveMinimum:{okStr:">",ok:Bp.GT,fail:Bp.LTE}},Qdt={message:({keyword:t,schemaCode:e})=>(0,lc.str)`should be ${EN[t].okStr} ${e}`,params:({keyword:t,schemaCode:e})=>(0,lc._)`{comparison: ${EN[t].okStr}, limit: ${e}}`};Yg.formatLimitDefinition={keyword:Object.keys(EN),type:"string",schemaType:"string",$data:!0,error:Qdt,code(t){let{gen:e,data:r,schemaCode:n,keyword:o,it:i}=t,{opts:s,self:a}=i;if(!s.validateFormats)return;let c=new Xdt.KeywordCxt(i,a.RULES.all.format.definition,"format");c.$data?u():p();function u(){let m=e.scopeValue("formats",{ref:a.formats,code:s.code.formats}),h=e.const("fmt",(0,lc._)`${m}[${c.schemaCode}]`);t.fail$data((0,lc.or)((0,lc._)`typeof ${h} != "object"`,(0,lc._)`${h} instanceof RegExp`,(0,lc._)`typeof ${h}.compare != "function"`,f(h)))}function p(){let m=c.schema,h=a.formats[m];if(!h||h===!0)return;if(typeof h!="object"||h instanceof RegExp||typeof h.compare!="function")throw new Error(`"${o}": format "${m}" does not define "compare" function`);let _=e.scopeValue("formats",{key:m,ref:h,code:s.code.formats?(0,lc._)`${s.code.formats}${(0,lc.getProperty)(m)}`:void 0});t.fail$data(f(_))}function f(m){return(0,lc._)`${m}.compare(${r}, ${n}) ${EN[o].fail} 0`}},dependencies:["format"]};var eft=t=>(t.addKeyword(Yg.formatLimitDefinition),t);Yg.default=eft});var Rme=S((lb,wme)=>{"use strict";Object.defineProperty(lb,"__esModule",{value:!0});var Jg=cde(),tft=bme(),MG=Pt(),xme=new MG.Name("fullFormats"),rft=new MG.Name("fastFormats"),DG=(t,e={keywords:!0})=>{if(Array.isArray(e))return Ame(t,e,Jg.fullFormats,xme),t;let[r,n]=e.mode==="fast"?[Jg.fastFormats,rft]:[Jg.fullFormats,xme],o=e.formats||Jg.formatNames;return Ame(t,o,r,n),e.keywords&&(0,tft.default)(t),t};DG.get=(t,e="full")=>{let n=(e==="fast"?Jg.fastFormats:Jg.fullFormats)[t];if(!n)throw new Error(`Unknown format "${t}"`);return n};function Ame(t,e,r,n){var o,i;(o=(i=t.opts.code).formats)!==null&&o!==void 0||(i.formats=(0,MG._)`require("ajv-formats/dist/formats").${n}`);for(let s of e)t.addFormat(s,r[s])}wme.exports=lb=DG;Object.defineProperty(lb,"__esModule",{value:!0});lb.default=DG});var Ume,jme=O(()=>{Ume=typeof globalThis=="object"?globalThis:global});var zme=O(()=>{jme()});var Fme=O(()=>{zme()});var fl,zG=O(()=>{fl="1.9.0"});function lft(t){var e=new Set([t]),r=new Set,n=t.match(qme);if(!n)return function(){return!1};var o={major:+n[1],minor:+n[2],patch:+n[3],prerelease:n[4]};if(o.prerelease!=null)return function(c){return c===t};function i(a){return r.add(a),!1}function s(a){return e.add(a),!0}return function(c){if(e.has(c))return!0;if(r.has(c))return!1;var u=c.match(qme);if(!u)return i(c);var p={major:+u[1],minor:+u[2],patch:+u[3],prerelease:u[4]};return p.prerelease!=null||o.major!==p.major?i(c):o.major===0?o.minor===p.minor&&o.patch<=p.patch?s(c):i(c):o.minor<=p.minor?s(c):i(c)}}var qme,Bme,Gme=O(()=>{zG();qme=/^(\d+)\.(\d+)\.(\d+)(-(.+))?$/;Bme=lft(fl)});function Zc(t,e,r,n){var o;n===void 0&&(n=!1);var i=fb[db]=(o=fb[db])!==null&&o!==void 0?o:{version:fl};if(!n&&i[t]){var s=new Error("@opentelemetry/api: Attempted duplicate registration of API: "+t);return r.error(s.stack||s.message),!1}if(i.version!==fl){var s=new Error("@opentelemetry/api: Registration of version v"+i.version+" for "+t+" does not match previously registered API v"+fl);return r.error(s.stack||s.message),!1}return i[t]=e,r.debug("@opentelemetry/api: Registered a global for "+t+" v"+fl+"."),!0}function Es(t){var e,r,n=(e=fb[db])===null||e===void 0?void 0:e.version;if(!(!n||!Bme(n)))return(r=fb[db])===null||r===void 0?void 0:r[t]}function Yc(t,e){e.debug("@opentelemetry/api: Unregistering a global for "+t+" v"+fl+".");var r=fb[db];r&&delete r[t]}var pft,db,fb,Xf=O(()=>{Fme();zG();Gme();pft=fl.split(".")[0],db=Symbol.for("opentelemetry.js.api."+pft),fb=Ume});function mb(t,e,r){var n=Es("diag");if(n)return r.unshift(e),n[t].apply(n,fft([],dft(r),!1))}var dft,fft,Vme,Hme=O(()=>{Xf();dft=function(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),o,i=[],s;try{for(;(e===void 0||e-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i},fft=function(t,e,r){if(r||arguments.length===2)for(var n=0,o=e.length,i;n{(function(t){t[t.NONE=0]="NONE",t[t.ERROR=30]="ERROR",t[t.WARN=50]="WARN",t[t.INFO=60]="INFO",t[t.DEBUG=70]="DEBUG",t[t.VERBOSE=80]="VERBOSE",t[t.ALL=9999]="ALL"})(Kn||(Kn={}))});function Wme(t,e){tKn.ALL&&(t=Kn.ALL),e=e||{};function r(n,o){var i=e[n];return typeof i=="function"&&t>=o?i.bind(e):function(){}}return{error:r("error",Kn.ERROR),warn:r("warn",Kn.WARN),info:r("info",Kn.INFO),debug:r("debug",Kn.DEBUG),verbose:r("verbose",Kn.VERBOSE)}}var Kme=O(()=>{RN()});var mft,hft,gft,Xo,Qf=O(()=>{Hme();Kme();RN();Xf();mft=function(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),o,i=[],s;try{for(;(e===void 0||e-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i},hft=function(t,e,r){if(r||arguments.length===2)for(var n=0,o=e.length,i;n";p.warn("Current logger will be overwritten from "+m),f.warn("Current logger will overwrite one already registered from "+m)}return Zc("diag",f,r,!0)};r.setLogger=n,r.disable=function(){Yc(gft,r)},r.createComponentLogger=function(o){return new Vme(o)},r.verbose=e("verbose"),r.debug=e("debug"),r.info=e("info"),r.warn=e("warn"),r.error=e("error")}return t.instance=function(){return this._instance||(this._instance=new t),this._instance},t})()});var _ft,vft,Zme,Yme=O(()=>{_ft=function(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),o,i=[],s;try{for(;(e===void 0||e-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i},vft=function(t){var e=typeof Symbol=="function"&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&typeof t.length=="number")return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},Zme=(function(){function t(e){this._entries=e?new Map(e):new Map}return t.prototype.getEntry=function(e){var r=this._entries.get(e);if(r)return Object.assign({},r)},t.prototype.getAllEntries=function(){return Array.from(this._entries.entries()).map(function(e){var r=_ft(e,2),n=r[0],o=r[1];return[n,o]})},t.prototype.setEntry=function(e,r){var n=new t(this._entries);return n._entries.set(e,r),n},t.prototype.removeEntry=function(e){var r=new t(this._entries);return r._entries.delete(e),r},t.prototype.removeEntries=function(){for(var e,r,n=[],o=0;o{Jme=Symbol("BaggageEntryMetadata")});function Qme(t){return t===void 0&&(t={}),new Zme(new Map(Object.entries(t)))}function ehe(t){return typeof t!="string"&&(Sft.error("Cannot create baggage metadata from unknown type: "+typeof t),t=""),{__TYPE__:Jme,toString:function(){return t}}}var Sft,FG=O(()=>{Qf();Yme();Xme();Sft=Xo.instance()});function pc(t){return Symbol.for(t)}var yft,Xg,hb=O(()=>{yft=(function(){function t(e){var r=this;r._currentContext=e?new Map(e):new Map,r.getValue=function(n){return r._currentContext.get(n)},r.setValue=function(n,o){var i=new t(r._currentContext);return i._currentContext.set(n,o),i},r.deleteValue=function(n){var o=new t(r._currentContext);return o._currentContext.delete(n),o}}return t})(),Xg=new yft});var qG,the,rhe=O(()=>{qG=[{n:"error",c:"error"},{n:"warn",c:"warn"},{n:"info",c:"info"},{n:"debug",c:"debug"},{n:"verbose",c:"trace"}],the=(function(){function t(){function e(n){return function(){for(var o=[],i=0;i{em=(function(){var t=function(e,r){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,o){n.__proto__=o}||function(n,o){for(var i in o)Object.prototype.hasOwnProperty.call(o,i)&&(n[i]=o[i])},t(e,r)};return function(e,r){if(typeof r!="function"&&r!==null)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");t(e,r);function n(){this.constructor=e}e.prototype=r===null?Object.create(r):(n.prototype=r.prototype,new n)}})(),Eft=(function(){function t(){}return t.prototype.createGauge=function(e,r){return Oft},t.prototype.createHistogram=function(e,r){return Nft},t.prototype.createCounter=function(e,r){return Ift},t.prototype.createUpDownCounter=function(e,r){return Cft},t.prototype.createObservableGauge=function(e,r){return kft},t.prototype.createObservableCounter=function(e,r){return $ft},t.prototype.createObservableUpDownCounter=function(e,r){return Mft},t.prototype.addBatchObservableCallback=function(e,r){},t.prototype.removeBatchObservableCallback=function(e){},t})(),PN=(function(){function t(){}return t})(),Tft=(function(t){em(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e.prototype.add=function(r,n){},e})(PN),bft=(function(t){em(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e.prototype.add=function(r,n){},e})(PN),xft=(function(t){em(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e.prototype.record=function(r,n){},e})(PN),Aft=(function(t){em(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e.prototype.record=function(r,n){},e})(PN),BG=(function(){function t(){}return t.prototype.addCallback=function(e){},t.prototype.removeCallback=function(e){},t})(),wft=(function(t){em(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e})(BG),Rft=(function(t){em(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e})(BG),Pft=(function(t){em(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e})(BG),GG=new Eft,Ift=new Tft,Oft=new xft,Nft=new Aft,Cft=new bft,$ft=new wft,kft=new Rft,Mft=new Pft});var IN,ohe=O(()=>{(function(t){t[t.INT=0]="INT",t[t.DOUBLE=1]="DOUBLE"})(IN||(IN={}))});var ON,NN,HG=O(()=>{ON={get:function(t,e){if(t!=null)return t[e]},keys:function(t){return t==null?[]:Object.keys(t)}},NN={set:function(t,e,r){t!=null&&(t[e]=r)}}});var Dft,Lft,ihe,she=O(()=>{hb();Dft=function(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),o,i=[],s;try{for(;(e===void 0||e-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i},Lft=function(t,e,r){if(r||arguments.length===2)for(var n=0,o=e.length,i;n{she();Xf();Qf();Uft=function(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),o,i=[],s;try{for(;(e===void 0||e-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i},jft=function(t,e,r){if(r||arguments.length===2)for(var n=0,o=e.length,i;n{(function(t){t[t.NONE=0]="NONE",t[t.SAMPLED=1]="SAMPLED"})(ca||(ca={}))});var _b,tm,CN,$N=O(()=>{KG();_b="0000000000000000",tm="00000000000000000000000000000000",CN={traceId:tm,spanId:_b,traceFlags:ca.NONE}});var Hp,kN=O(()=>{$N();Hp=(function(){function t(e){e===void 0&&(e=CN),this._spanContext=e}return t.prototype.spanContext=function(){return this._spanContext},t.prototype.setAttribute=function(e,r){return this},t.prototype.setAttributes=function(e){return this},t.prototype.addEvent=function(e,r){return this},t.prototype.addLink=function(e){return this},t.prototype.addLinks=function(e){return this},t.prototype.setStatus=function(e){return this},t.prototype.updateName=function(e){return this},t.prototype.end=function(e){},t.prototype.isRecording=function(){return!1},t.prototype.recordException=function(e,r){},t})()});function MN(t){return t.getValue(ZG)||void 0}function ahe(){return MN(Vp.getInstance().active())}function vb(t,e){return t.setValue(ZG,e)}function che(t){return t.deleteValue(ZG)}function uhe(t,e){return vb(t,new Hp(e))}function DN(t){var e;return(e=MN(t))===null||e===void 0?void 0:e.spanContext()}var ZG,YG=O(()=>{hb();kN();gb();ZG=pc("OpenTelemetry Context Key SPAN")});function JG(t){return Fft.test(t)&&t!==tm}function XG(t){return qft.test(t)&&t!==_b}function ml(t){return JG(t.traceId)&&XG(t.spanId)}function lhe(t){return new Hp(t)}var Fft,qft,LN=O(()=>{$N();kN();Fft=/^([0-9a-f]{32})$/i,qft=/^[0-9a-f]{16}$/i});function Bft(t){return typeof t=="object"&&typeof t.spanId=="string"&&typeof t.traceId=="string"&&typeof t.traceFlags=="number"}var QG,UN,e5=O(()=>{gb();YG();kN();LN();QG=Vp.getInstance(),UN=(function(){function t(){}return t.prototype.startSpan=function(e,r,n){n===void 0&&(n=QG.active());var o=!!r?.root;if(o)return new Hp;var i=n&&DN(n);return Bft(i)&&ml(i)?new Hp(i):new Hp},t.prototype.startActiveSpan=function(e,r,n,o){var i,s,a;if(!(arguments.length<2)){arguments.length===2?a=r:arguments.length===3?(i=r,a=n):(i=r,s=n,a=o);var c=s??QG.active(),u=this.startSpan(e,i,c),p=vb(c,u);return QG.with(p,a,void 0,u)}},t})()});var Gft,jN,t5=O(()=>{e5();Gft=new UN,jN=(function(){function t(e,r,n,o){this._provider=e,this.name=r,this.version=n,this.options=o}return t.prototype.startSpan=function(e,r,n){return this._getTracer().startSpan(e,r,n)},t.prototype.startActiveSpan=function(e,r,n,o){var i=this._getTracer();return Reflect.apply(i.startActiveSpan,i,arguments)},t.prototype._getTracer=function(){if(this._delegate)return this._delegate;var e=this._provider.getDelegateTracer(this.name,this.version,this.options);return e?(this._delegate=e,this._delegate):Gft},t})()});var phe,dhe=O(()=>{e5();phe=(function(){function t(){}return t.prototype.getTracer=function(e,r,n){return new UN},t})()});var Vft,Sb,r5=O(()=>{t5();dhe();Vft=new phe,Sb=(function(){function t(){}return t.prototype.getTracer=function(e,r,n){var o;return(o=this.getDelegateTracer(e,r,n))!==null&&o!==void 0?o:new jN(this,e,r,n)},t.prototype.getDelegate=function(){var e;return(e=this._delegate)!==null&&e!==void 0?e:Vft},t.prototype.setDelegate=function(e){this._delegate=e},t.prototype.getDelegateTracer=function(e,r,n){var o;return(o=this._delegate)===null||o===void 0?void 0:o.getTracer(e,r,n)},t})()});var zN,fhe=O(()=>{(function(t){t[t.NOT_RECORD=0]="NOT_RECORD",t[t.RECORD=1]="RECORD",t[t.RECORD_AND_SAMPLED=2]="RECORD_AND_SAMPLED"})(zN||(zN={}))});var Zn,mhe=O(()=>{(function(t){t[t.INTERNAL=0]="INTERNAL",t[t.SERVER=1]="SERVER",t[t.CLIENT=2]="CLIENT",t[t.PRODUCER=3]="PRODUCER",t[t.CONSUMER=4]="CONSUMER"})(Zn||(Zn={}))});var hn,hhe=O(()=>{(function(t){t[t.UNSET=0]="UNSET",t[t.OK=1]="OK",t[t.ERROR=2]="ERROR"})(hn||(hn={}))});function ghe(t){return Kft.test(t)}function _he(t){return Zft.test(t)&&!Yft.test(t)}var n5,Hft,Wft,Kft,Zft,Yft,vhe=O(()=>{n5="[_0-9a-z-*/]",Hft="[a-z]"+n5+"{0,255}",Wft="[a-z0-9]"+n5+"{0,240}@[a-z]"+n5+"{0,13}",Kft=new RegExp("^(?:"+Hft+"|"+Wft+")$"),Zft=/^[ -~]{0,255}[!-~]$/,Yft=/,|=/});var She,Jft,yhe,Ehe,The,bhe=O(()=>{vhe();She=32,Jft=512,yhe=",",Ehe="=",The=(function(){function t(e){this._internalState=new Map,e&&this._parse(e)}return t.prototype.set=function(e,r){var n=this._clone();return n._internalState.has(e)&&n._internalState.delete(e),n._internalState.set(e,r),n},t.prototype.unset=function(e){var r=this._clone();return r._internalState.delete(e),r},t.prototype.get=function(e){return this._internalState.get(e)},t.prototype.serialize=function(){var e=this;return this._keys().reduce(function(r,n){return r.push(n+Ehe+e.get(n)),r},[]).join(yhe)},t.prototype._parse=function(e){e.length>Jft||(this._internalState=e.split(yhe).reverse().reduce(function(r,n){var o=n.trim(),i=o.indexOf(Ehe);if(i!==-1){var s=o.slice(0,i),a=o.slice(i+1,n.length);ghe(s)&&_he(a)&&r.set(s,a)}return r},new Map),this._internalState.size>She&&(this._internalState=new Map(Array.from(this._internalState.entries()).reverse().slice(0,She))))},t.prototype._keys=function(){return Array.from(this._internalState.keys()).reverse()},t.prototype._clone=function(){var e=new t;return e._internalState=new Map(this._internalState),e},t})()});function xhe(t){return new The(t)}var Ahe=O(()=>{bhe()});var Je,whe=O(()=>{gb();Je=Vp.getInstance()});var ua,Rhe=O(()=>{Qf();ua=Xo.instance()});var Xft,Phe,Ihe=O(()=>{VG();Xft=(function(){function t(){}return t.prototype.getMeter=function(e,r,n){return GG},t})(),Phe=new Xft});var o5,Ohe,Nhe=O(()=>{Ihe();Xf();Qf();o5="metrics",Ohe=(function(){function t(){}return t.getInstance=function(){return this._instance||(this._instance=new t),this._instance},t.prototype.setGlobalMeterProvider=function(e){return Zc(o5,e,Xo.instance())},t.prototype.getMeterProvider=function(){return Es(o5)||Phe},t.prototype.getMeter=function(e,r,n){return this.getMeterProvider().getMeter(e,r,n)},t.prototype.disable=function(){Yc(o5,Xo.instance())},t})()});var i5,Che=O(()=>{Nhe();i5=Ohe.getInstance()});var $he,khe=O(()=>{$he=(function(){function t(){}return t.prototype.inject=function(e,r){},t.prototype.extract=function(e,r){return e},t.prototype.fields=function(){return[]},t})()});function a5(t){return t.getValue(s5)||void 0}function Mhe(){return a5(Vp.getInstance().active())}function Dhe(t,e){return t.setValue(s5,e)}function Lhe(t){return t.deleteValue(s5)}var s5,Uhe=O(()=>{gb();hb();s5=pc("OpenTelemetry Baggage Key")});var c5,Qft,jhe,zhe=O(()=>{Xf();khe();HG();Uhe();FG();Qf();c5="propagation",Qft=new $he,jhe=(function(){function t(){this.createBaggage=Qme,this.getBaggage=a5,this.getActiveBaggage=Mhe,this.setBaggage=Dhe,this.deleteBaggage=Lhe}return t.getInstance=function(){return this._instance||(this._instance=new t),this._instance},t.prototype.setGlobalPropagator=function(e){return Zc(c5,e,Xo.instance())},t.prototype.inject=function(e,r,n){return n===void 0&&(n=NN),this._getGlobalPropagator().inject(e,r,n)},t.prototype.extract=function(e,r,n){return n===void 0&&(n=ON),this._getGlobalPropagator().extract(e,r,n)},t.prototype.fields=function(){return this._getGlobalPropagator().fields()},t.prototype.disable=function(){Yc(c5,Xo.instance())},t.prototype._getGlobalPropagator=function(){return Es(c5)||Qft},t})()});var bi,Fhe=O(()=>{zhe();bi=jhe.getInstance()});var u5,qhe,Bhe=O(()=>{Xf();r5();LN();YG();Qf();u5="trace",qhe=(function(){function t(){this._proxyTracerProvider=new Sb,this.wrapSpanContext=lhe,this.isSpanContextValid=ml,this.deleteSpan=che,this.getSpan=MN,this.getActiveSpan=ahe,this.getSpanContext=DN,this.setSpan=vb,this.setSpanContext=uhe}return t.getInstance=function(){return this._instance||(this._instance=new t),this._instance},t.prototype.setGlobalTracerProvider=function(e){var r=Zc(u5,this._proxyTracerProvider,Xo.instance());return r&&this._proxyTracerProvider.setDelegate(e),r},t.prototype.getTracerProvider=function(){return Es(u5)||this._proxyTracerProvider},t.prototype.getTracer=function(e,r){return this.getTracerProvider().getTracer(e,r)},t.prototype.disable=function(){Yc(u5,Xo.instance()),this._proxyTracerProvider=new Sb},t})()});var yt,Ghe=O(()=>{Bhe();yt=qhe.getInstance()});var Pe={};Y(Pe,{DiagConsoleLogger:()=>the,DiagLogLevel:()=>Kn,INVALID_SPANID:()=>_b,INVALID_SPAN_CONTEXT:()=>CN,INVALID_TRACEID:()=>tm,ProxyTracer:()=>jN,ProxyTracerProvider:()=>Sb,ROOT_CONTEXT:()=>Xg,SamplingDecision:()=>zN,SpanKind:()=>Zn,SpanStatusCode:()=>hn,TraceFlags:()=>ca,ValueType:()=>IN,baggageEntryMetadataFromString:()=>ehe,context:()=>Je,createContextKey:()=>pc,createNoopMeter:()=>nhe,createTraceState:()=>xhe,default:()=>emt,defaultTextMapGetter:()=>ON,defaultTextMapSetter:()=>NN,diag:()=>ua,isSpanContextValid:()=>ml,isValidSpanId:()=>XG,isValidTraceId:()=>JG,metrics:()=>i5,propagation:()=>bi,trace:()=>yt});var emt,pe=O(()=>{FG();hb();rhe();RN();VG();ohe();HG();t5();r5();fhe();mhe();hhe();KG();Ahe();LN();$N();whe();Rhe();Che();Fhe();Ghe();emt={context:Je,diag:ua,metrics:i5,propagation:bi,trace:yt}});var yb=S(Wp=>{"use strict";Object.defineProperty(Wp,"__esModule",{value:!0});Wp.isTracingSuppressed=Wp.unsuppressTracing=Wp.suppressTracing=void 0;var tmt=(pe(),se(Pe)),l5=(0,tmt.createContextKey)("OpenTelemetry SDK Context Key SUPPRESS_TRACING");function rmt(t){return t.setValue(l5,!0)}Wp.suppressTracing=rmt;function nmt(t){return t.deleteValue(l5)}Wp.unsuppressTracing=nmt;function omt(t){return t.getValue(l5)===!0}Wp.isTracingSuppressed=omt});var p5=S(Qo=>{"use strict";Object.defineProperty(Qo,"__esModule",{value:!0});Qo.BAGGAGE_MAX_TOTAL_LENGTH=Qo.BAGGAGE_MAX_PER_NAME_VALUE_PAIRS=Qo.BAGGAGE_MAX_NAME_VALUE_PAIRS=Qo.BAGGAGE_HEADER=Qo.BAGGAGE_ITEMS_SEPARATOR=Qo.BAGGAGE_PROPERTIES_SEPARATOR=Qo.BAGGAGE_KEY_PAIR_SEPARATOR=void 0;Qo.BAGGAGE_KEY_PAIR_SEPARATOR="=";Qo.BAGGAGE_PROPERTIES_SEPARATOR=";";Qo.BAGGAGE_ITEMS_SEPARATOR=",";Qo.BAGGAGE_HEADER="baggage";Qo.BAGGAGE_MAX_NAME_VALUE_PAIRS=180;Qo.BAGGAGE_MAX_PER_NAME_VALUE_PAIRS=4096;Qo.BAGGAGE_MAX_TOTAL_LENGTH=8192});var d5=S(Jc=>{"use strict";Object.defineProperty(Jc,"__esModule",{value:!0});Jc.parseKeyPairsIntoRecord=Jc.parsePairKeyValue=Jc.getKeyPairs=Jc.serializeKeyPairs=void 0;var imt=(pe(),se(Pe)),rm=p5();function smt(t){return t.reduce((e,r)=>{let n=`${e}${e!==""?rm.BAGGAGE_ITEMS_SEPARATOR:""}${r}`;return n.length>rm.BAGGAGE_MAX_TOTAL_LENGTH?e:n},"")}Jc.serializeKeyPairs=smt;function amt(t){return t.getAllEntries().map(([e,r])=>{let n=`${encodeURIComponent(e)}=${encodeURIComponent(r.value)}`;return r.metadata!==void 0&&(n+=rm.BAGGAGE_PROPERTIES_SEPARATOR+r.metadata.toString()),n})}Jc.getKeyPairs=amt;function Vhe(t){let e=t.split(rm.BAGGAGE_PROPERTIES_SEPARATOR);if(e.length<=0)return;let r=e.shift();if(!r)return;let n=r.indexOf(rm.BAGGAGE_KEY_PAIR_SEPARATOR);if(n<=0)return;let o=decodeURIComponent(r.substring(0,n).trim()),i=decodeURIComponent(r.substring(n+1).trim()),s;return e.length>0&&(s=(0,imt.baggageEntryMetadataFromString)(e.join(rm.BAGGAGE_PROPERTIES_SEPARATOR))),{key:o,value:i,metadata:s}}Jc.parsePairKeyValue=Vhe;function cmt(t){let e={};return typeof t=="string"&&t.length>0&&t.split(rm.BAGGAGE_ITEMS_SEPARATOR).forEach(r=>{let n=Vhe(r);n!==void 0&&n.value.length>0&&(e[n.key]=n.value)}),e}Jc.parseKeyPairsIntoRecord=cmt});var Hhe=S(FN=>{"use strict";Object.defineProperty(FN,"__esModule",{value:!0});FN.W3CBaggagePropagator=void 0;var f5=(pe(),se(Pe)),umt=yb(),nm=p5(),m5=d5(),h5=class{inject(e,r,n){let o=f5.propagation.getBaggage(e);if(!o||(0,umt.isTracingSuppressed)(e))return;let i=(0,m5.getKeyPairs)(o).filter(a=>a.length<=nm.BAGGAGE_MAX_PER_NAME_VALUE_PAIRS).slice(0,nm.BAGGAGE_MAX_NAME_VALUE_PAIRS),s=(0,m5.serializeKeyPairs)(i);s.length>0&&n.set(r,nm.BAGGAGE_HEADER,s)}extract(e,r,n){let o=n.get(r,nm.BAGGAGE_HEADER),i=Array.isArray(o)?o.join(nm.BAGGAGE_ITEMS_SEPARATOR):o;if(!i)return e;let s={};return i.length===0||(i.split(nm.BAGGAGE_ITEMS_SEPARATOR).forEach(c=>{let u=(0,m5.parsePairKeyValue)(c);if(u){let p={value:u.value};u.metadata&&(p.metadata=u.metadata),s[u.key]=p}}),Object.entries(s).length===0)?e:f5.propagation.setBaggage(e,f5.propagation.createBaggage(s))}fields(){return[nm.BAGGAGE_HEADER]}};FN.W3CBaggagePropagator=h5});var Whe=S(qN=>{"use strict";Object.defineProperty(qN,"__esModule",{value:!0});qN.AnchoredClock=void 0;var g5=class{_monotonicClock;_epochMillis;_performanceMillis;constructor(e,r){this._monotonicClock=r,this._epochMillis=e.now(),this._performanceMillis=r.now()}now(){let e=this._monotonicClock.now()-this._performanceMillis;return this._epochMillis+e}};qN.AnchoredClock=g5});var Xhe=S(Kp=>{"use strict";Object.defineProperty(Kp,"__esModule",{value:!0});Kp.isAttributeValue=Kp.isAttributeKey=Kp.sanitizeAttributes=void 0;var Khe=(pe(),se(Pe));function lmt(t){let e={};if(typeof t!="object"||t==null)return e;for(let[r,n]of Object.entries(t)){if(!Zhe(r)){Khe.diag.warn(`Invalid attribute key: ${r}`);continue}if(!Yhe(n)){Khe.diag.warn(`Invalid attribute value set for key: ${r}`);continue}Array.isArray(n)?e[r]=n.slice():e[r]=n}return e}Kp.sanitizeAttributes=lmt;function Zhe(t){return typeof t=="string"&&t.length>0}Kp.isAttributeKey=Zhe;function Yhe(t){return t==null?!0:Array.isArray(t)?pmt(t):Jhe(t)}Kp.isAttributeValue=Yhe;function pmt(t){let e;for(let r of t)if(r!=null){if(!e){if(Jhe(r)){e=typeof r;continue}return!1}if(typeof r!==e)return!1}return!0}function Jhe(t){switch(typeof t){case"number":case"boolean":case"string":return!0}return!1}});var _5=S(BN=>{"use strict";Object.defineProperty(BN,"__esModule",{value:!0});BN.loggingErrorHandler=void 0;var dmt=(pe(),se(Pe));function fmt(){return t=>{dmt.diag.error(mmt(t))}}BN.loggingErrorHandler=fmt;function mmt(t){return typeof t=="string"?t:JSON.stringify(hmt(t))}function hmt(t){let e={},r=t;for(;r!==null;)Object.getOwnPropertyNames(r).forEach(n=>{if(e[n])return;let o=r[n];o&&(e[n]=String(o))}),r=Object.getPrototypeOf(r);return e}});var ege=S(Qg=>{"use strict";Object.defineProperty(Qg,"__esModule",{value:!0});Qg.globalErrorHandler=Qg.setGlobalErrorHandler=void 0;var gmt=_5(),Qhe=(0,gmt.loggingErrorHandler)();function _mt(t){Qhe=t}Qg.setGlobalErrorHandler=_mt;function vmt(t){try{Qhe(t)}catch{}}Qg.globalErrorHandler=vmt});var oge=S(Xc=>{"use strict";Object.defineProperty(Xc,"__esModule",{value:!0});Xc.getStringListFromEnv=Xc.getBooleanFromEnv=Xc.getStringFromEnv=Xc.getNumberFromEnv=void 0;var tge=(pe(),se(Pe)),rge=require("util");function Smt(t){let e=process.env[t];if(e==null||e.trim()==="")return;let r=Number(e);if(isNaN(r)){tge.diag.warn(`Unknown value ${(0,rge.inspect)(e)} for ${t}, expected a number, using defaults`);return}return r}Xc.getNumberFromEnv=Smt;function nge(t){let e=process.env[t];if(!(e==null||e.trim()===""))return e}Xc.getStringFromEnv=nge;function ymt(t){let e=process.env[t]?.trim().toLowerCase();return e==null||e===""?!1:e==="true"?!0:(e==="false"||tge.diag.warn(`Unknown value ${(0,rge.inspect)(e)} for ${t}, expected 'true' or 'false', falling back to 'false' (default)`),!1)}Xc.getBooleanFromEnv=ymt;function Emt(t){return nge(t)?.split(",").map(e=>e.trim()).filter(e=>e!=="")}Xc.getStringListFromEnv=Emt});var ige=S(GN=>{"use strict";Object.defineProperty(GN,"__esModule",{value:!0});GN._globalThis=void 0;GN._globalThis=typeof globalThis=="object"?globalThis:global});var sge=S(VN=>{"use strict";Object.defineProperty(VN,"__esModule",{value:!0});VN.otperformance=void 0;var Tmt=require("perf_hooks");VN.otperformance=Tmt.performance});var age=S(HN=>{"use strict";Object.defineProperty(HN,"__esModule",{value:!0});HN.VERSION=void 0;HN.VERSION="2.0.1"});function In(t){let e={},r=t.length;for(let n=0;n{});var cge,uge,lge,pge,dge,fge,mge,hge,gge,_ge,vge,Sge,yge,Ege,Tge,bge,xge,Age,wge,Rge,Pge,Ige,Oge,Nge,Cge,$ge,kge,Mge,Dge,Lge,Uge,jge,zge,Fge,qge,Bge,Gge,Vge,Hge,Wge,Kge,Zge,Yge,Jge,Xge,Qge,e_e,t_e,r_e,n_e,o_e,i_e,s_e,a_e,c_e,u_e,l_e,p_e,d_e,f_e,m_e,h_e,g_e,__e,v_e,S_e,y_e,E_e,T_e,b_e,x_e,A_e,w_e,R_e,P_e,I_e,O_e,N_e,C_e,$_e,k_e,M_e,D_e,L_e,U_e,j_e,z_e,F_e,q_e,B_e,G_e,V_e,H_e,W_e,K_e,Z_e,Y_e,J_e,X_e,Q_e,eve,tve,rve,nve,ove,ive,sve,ave,cve,uve,lve,pve,dve,fve,mve,hve,gve,_ve,vve,Sve,yve,Eve,Tve,bve,xve,Ave,wve,Rve,bmt,S5,xmt,Amt,wmt,Rmt,y5,Pmt,Imt,Omt,Nmt,Cmt,$mt,kmt,Mmt,Dmt,Lmt,Umt,jmt,zmt,Fmt,qmt,Bmt,Gmt,Vmt,E5,Hmt,Wmt,Kmt,Zmt,Ymt,Jmt,Xmt,Qmt,eht,tht,rht,nht,oht,iht,sht,aht,cht,uht,lht,pht,dht,fht,mht,hht,ght,_ht,vht,Sht,yht,Eht,Tht,bht,xht,Aht,Eb,e_,T5,wht,Rht,WN,Pht,Iht,Oht,Nht,Cht,$ht,kht,b5,Mht,Dht,Lht,Uht,jht,zht,Fht,qht,Bht,Ght,Vht,Hht,Wht,Kht,Zht,Yht,Jht,Xht,Qht,egt,tgt,rgt,ngt,x5,ogt,igt,sgt,agt,cgt,ugt,lgt,pgt,dgt,fgt,mgt,hgt,ggt,_gt,vgt,Sgt,ygt,Egt,Tgt,A5,bgt,w5,xgt,Agt,wgt,Rgt,Pgt,Igt,Ogt,Ngt,Cgt,Pve,Ive,Ove,Nve,Cve,$ve,kve,Mve,Dve,Lve,Uve,jve,zve,Fve,qve,Bve,Gve,Vve,Hve,Wve,Kve,Zve,Yve,Jve,Xve,Qve,eSe,tSe,rSe,nSe,oSe,iSe,sSe,aSe,cSe,uSe,lSe,pSe,dSe,fSe,mSe,hSe,gSe,_Se,vSe,SSe,ySe,$gt,kgt,Mgt,Dgt,Lgt,Ugt,jgt,zgt,Fgt,qgt,Bgt,Ggt,Vgt,Hgt,Wgt,Kgt,Zgt,Ygt,Jgt,Xgt,Qgt,e_t,t_t,r_t,n_t,o_t,i_t,s_t,a_t,c_t,u_t,l_t,p_t,d_t,f_t,m_t,h_t,g_t,__t,v_t,S_t,y_t,E_t,T_t,b_t,x_t,A_t,w_t,ESe,TSe,bSe,xSe,ASe,wSe,RSe,PSe,ISe,OSe,NSe,R_t,P_t,I_t,O_t,N_t,C_t,$_t,k_t,M_t,D_t,L_t,U_t,CSe,$Se,kSe,MSe,DSe,j_t,z_t,F_t,q_t,B_t,G_t,LSe,USe,jSe,V_t,H_t,W_t,K_t,zSe,FSe,qSe,BSe,Z_t,Y_t,J_t,X_t,Q_t,GSe,VSe,HSe,WSe,KSe,ZSe,YSe,evt,tvt,rvt,nvt,ovt,ivt,svt,avt,JSe,XSe,QSe,eye,tye,cvt,uvt,lvt,pvt,dvt,fvt,rye,nye,oye,iye,sye,aye,cye,uye,lye,pye,dye,fye,mye,hye,gye,_ye,vye,Sye,yye,Eye,Tye,mvt,hvt,gvt,_vt,vvt,Svt,yvt,Evt,Tvt,bvt,xvt,Avt,wvt,Rvt,Pvt,Ivt,Ovt,Nvt,Cvt,$vt,kvt,Mvt,bye,xye,Aye,wye,Rye,Dvt,Lvt,Uvt,jvt,zvt,Fvt,Pye,Iye,qvt,Bvt,Gvt,Oye,Nye,Vvt,Hvt,Wvt,Cye,$ye,kye,Mye,Dye,Lye,Uye,jye,zye,Fye,qye,Bye,Gye,Vye,Hye,Wye,Kye,Kvt,Zvt,Yvt,Jvt,Xvt,Qvt,eSt,tSt,rSt,nSt,oSt,iSt,sSt,aSt,cSt,uSt,lSt,pSt,Zye,Yye,dSt,fSt,mSt,Jye=O(()=>{v5();cge="aws.lambda.invoked_arn",uge="db.system",lge="db.connection_string",pge="db.user",dge="db.jdbc.driver_classname",fge="db.name",mge="db.statement",hge="db.operation",gge="db.mssql.instance_name",_ge="db.cassandra.keyspace",vge="db.cassandra.page_size",Sge="db.cassandra.consistency_level",yge="db.cassandra.table",Ege="db.cassandra.idempotence",Tge="db.cassandra.speculative_execution_count",bge="db.cassandra.coordinator.id",xge="db.cassandra.coordinator.dc",Age="db.hbase.namespace",wge="db.redis.database_index",Rge="db.mongodb.collection",Pge="db.sql.table",Ige="exception.type",Oge="exception.message",Nge="exception.stacktrace",Cge="exception.escaped",$ge="faas.trigger",kge="faas.execution",Mge="faas.document.collection",Dge="faas.document.operation",Lge="faas.document.time",Uge="faas.document.name",jge="faas.time",zge="faas.cron",Fge="faas.coldstart",qge="faas.invoked_name",Bge="faas.invoked_provider",Gge="faas.invoked_region",Vge="net.transport",Hge="net.peer.ip",Wge="net.peer.port",Kge="net.peer.name",Zge="net.host.ip",Yge="net.host.port",Jge="net.host.name",Xge="net.host.connection.type",Qge="net.host.connection.subtype",e_e="net.host.carrier.name",t_e="net.host.carrier.mcc",r_e="net.host.carrier.mnc",n_e="net.host.carrier.icc",o_e="peer.service",i_e="enduser.id",s_e="enduser.role",a_e="enduser.scope",c_e="thread.id",u_e="thread.name",l_e="code.function",p_e="code.namespace",d_e="code.filepath",f_e="code.lineno",m_e="http.method",h_e="http.url",g_e="http.target",__e="http.host",v_e="http.scheme",S_e="http.status_code",y_e="http.flavor",E_e="http.user_agent",T_e="http.request_content_length",b_e="http.request_content_length_uncompressed",x_e="http.response_content_length",A_e="http.response_content_length_uncompressed",w_e="http.server_name",R_e="http.route",P_e="http.client_ip",I_e="aws.dynamodb.table_names",O_e="aws.dynamodb.consumed_capacity",N_e="aws.dynamodb.item_collection_metrics",C_e="aws.dynamodb.provisioned_read_capacity",$_e="aws.dynamodb.provisioned_write_capacity",k_e="aws.dynamodb.consistent_read",M_e="aws.dynamodb.projection",D_e="aws.dynamodb.limit",L_e="aws.dynamodb.attributes_to_get",U_e="aws.dynamodb.index_name",j_e="aws.dynamodb.select",z_e="aws.dynamodb.global_secondary_indexes",F_e="aws.dynamodb.local_secondary_indexes",q_e="aws.dynamodb.exclusive_start_table",B_e="aws.dynamodb.table_count",G_e="aws.dynamodb.scan_forward",V_e="aws.dynamodb.segment",H_e="aws.dynamodb.total_segments",W_e="aws.dynamodb.count",K_e="aws.dynamodb.scanned_count",Z_e="aws.dynamodb.attribute_definitions",Y_e="aws.dynamodb.global_secondary_index_updates",J_e="messaging.system",X_e="messaging.destination",Q_e="messaging.destination_kind",eve="messaging.temp_destination",tve="messaging.protocol",rve="messaging.protocol_version",nve="messaging.url",ove="messaging.message_id",ive="messaging.conversation_id",sve="messaging.message_payload_size_bytes",ave="messaging.message_payload_compressed_size_bytes",cve="messaging.operation",uve="messaging.consumer_id",lve="messaging.rabbitmq.routing_key",pve="messaging.kafka.message_key",dve="messaging.kafka.consumer_group",fve="messaging.kafka.client_id",mve="messaging.kafka.partition",hve="messaging.kafka.tombstone",gve="rpc.system",_ve="rpc.service",vve="rpc.method",Sve="rpc.grpc.status_code",yve="rpc.jsonrpc.version",Eve="rpc.jsonrpc.request_id",Tve="rpc.jsonrpc.error_code",bve="rpc.jsonrpc.error_message",xve="message.type",Ave="message.id",wve="message.compressed_size",Rve="message.uncompressed_size",bmt=cge,S5=uge,xmt=lge,Amt=pge,wmt=dge,Rmt=fge,y5=mge,Pmt=hge,Imt=gge,Omt=_ge,Nmt=vge,Cmt=Sge,$mt=yge,kmt=Ege,Mmt=Tge,Dmt=bge,Lmt=xge,Umt=Age,jmt=wge,zmt=Rge,Fmt=Pge,qmt=Ige,Bmt=Oge,Gmt=Nge,Vmt=Cge,E5=$ge,Hmt=kge,Wmt=Mge,Kmt=Dge,Zmt=Lge,Ymt=Uge,Jmt=jge,Xmt=zge,Qmt=Fge,eht=qge,tht=Bge,rht=Gge,nht=Vge,oht=Hge,iht=Wge,sht=Kge,aht=Zge,cht=Yge,uht=Jge,lht=Xge,pht=Qge,dht=e_e,fht=t_e,mht=r_e,hht=n_e,ght=o_e,_ht=i_e,vht=s_e,Sht=a_e,yht=c_e,Eht=u_e,Tht=l_e,bht=p_e,xht=d_e,Aht=f_e,Eb=m_e,e_=h_e,T5=g_e,wht=__e,Rht=v_e,WN=S_e,Pht=y_e,Iht=E_e,Oht=T_e,Nht=b_e,Cht=x_e,$ht=A_e,kht=w_e,b5=R_e,Mht=P_e,Dht=I_e,Lht=O_e,Uht=N_e,jht=C_e,zht=$_e,Fht=k_e,qht=M_e,Bht=D_e,Ght=L_e,Vht=U_e,Hht=j_e,Wht=z_e,Kht=F_e,Zht=q_e,Yht=B_e,Jht=G_e,Xht=V_e,Qht=H_e,egt=W_e,tgt=K_e,rgt=Z_e,ngt=Y_e,x5=J_e,ogt=X_e,igt=Q_e,sgt=eve,agt=tve,cgt=rve,ugt=nve,lgt=ove,pgt=ive,dgt=sve,fgt=ave,mgt=cve,hgt=uve,ggt=lve,_gt=pve,vgt=dve,Sgt=fve,ygt=mve,Egt=hve,Tgt=gve,A5=_ve,bgt=vve,w5=Sve,xgt=yve,Agt=Eve,wgt=Tve,Rgt=bve,Pgt=xve,Igt=Ave,Ogt=wve,Ngt=Rve,Cgt=In([cge,uge,lge,pge,dge,fge,mge,hge,gge,_ge,vge,Sge,yge,Ege,Tge,bge,xge,Age,wge,Rge,Pge,Ige,Oge,Nge,Cge,$ge,kge,Mge,Dge,Lge,Uge,jge,zge,Fge,qge,Bge,Gge,Vge,Hge,Wge,Kge,Zge,Yge,Jge,Xge,Qge,e_e,t_e,r_e,n_e,o_e,i_e,s_e,a_e,c_e,u_e,l_e,p_e,d_e,f_e,m_e,h_e,g_e,__e,v_e,S_e,y_e,E_e,T_e,b_e,x_e,A_e,w_e,R_e,P_e,I_e,O_e,N_e,C_e,$_e,k_e,M_e,D_e,L_e,U_e,j_e,z_e,F_e,q_e,B_e,G_e,V_e,H_e,W_e,K_e,Z_e,Y_e,J_e,X_e,Q_e,eve,tve,rve,nve,ove,ive,sve,ave,cve,uve,lve,pve,dve,fve,mve,hve,gve,_ve,vve,Sve,yve,Eve,Tve,bve,xve,Ave,wve,Rve]),Pve="other_sql",Ive="mssql",Ove="mysql",Nve="oracle",Cve="db2",$ve="postgresql",kve="redshift",Mve="hive",Dve="cloudscape",Lve="hsqldb",Uve="progress",jve="maxdb",zve="hanadb",Fve="ingres",qve="firstsql",Bve="edb",Gve="cache",Vve="adabas",Hve="firebird",Wve="derby",Kve="filemaker",Zve="informix",Yve="instantdb",Jve="interbase",Xve="mariadb",Qve="netezza",eSe="pervasive",tSe="pointbase",rSe="sqlite",nSe="sybase",oSe="teradata",iSe="vertica",sSe="h2",aSe="coldfusion",cSe="cassandra",uSe="hbase",lSe="mongodb",pSe="redis",dSe="couchbase",fSe="couchdb",mSe="cosmosdb",hSe="dynamodb",gSe="neo4j",_Se="geode",vSe="elasticsearch",SSe="memcached",ySe="cockroachdb",$gt=Pve,kgt=Ive,Mgt=Ove,Dgt=Nve,Lgt=Cve,Ugt=$ve,jgt=kve,zgt=Mve,Fgt=Dve,qgt=Lve,Bgt=Uve,Ggt=jve,Vgt=zve,Hgt=Fve,Wgt=qve,Kgt=Bve,Zgt=Gve,Ygt=Vve,Jgt=Hve,Xgt=Wve,Qgt=Kve,e_t=Zve,t_t=Yve,r_t=Jve,n_t=Xve,o_t=Qve,i_t=eSe,s_t=tSe,a_t=rSe,c_t=nSe,u_t=oSe,l_t=iSe,p_t=sSe,d_t=aSe,f_t=cSe,m_t=uSe,h_t=lSe,g_t=pSe,__t=dSe,v_t=fSe,S_t=mSe,y_t=hSe,E_t=gSe,T_t=_Se,b_t=vSe,x_t=SSe,A_t=ySe,w_t=In([Pve,Ive,Ove,Nve,Cve,$ve,kve,Mve,Dve,Lve,Uve,jve,zve,Fve,qve,Bve,Gve,Vve,Hve,Wve,Kve,Zve,Yve,Jve,Xve,Qve,eSe,tSe,rSe,nSe,oSe,iSe,sSe,aSe,cSe,uSe,lSe,pSe,dSe,fSe,mSe,hSe,gSe,_Se,vSe,SSe,ySe]),ESe="all",TSe="each_quorum",bSe="quorum",xSe="local_quorum",ASe="one",wSe="two",RSe="three",PSe="local_one",ISe="any",OSe="serial",NSe="local_serial",R_t=ESe,P_t=TSe,I_t=bSe,O_t=xSe,N_t=ASe,C_t=wSe,$_t=RSe,k_t=PSe,M_t=ISe,D_t=OSe,L_t=NSe,U_t=In([ESe,TSe,bSe,xSe,ASe,wSe,RSe,PSe,ISe,OSe,NSe]),CSe="datasource",$Se="http",kSe="pubsub",MSe="timer",DSe="other",j_t=CSe,z_t=$Se,F_t=kSe,q_t=MSe,B_t=DSe,G_t=In([CSe,$Se,kSe,MSe,DSe]),LSe="insert",USe="edit",jSe="delete",V_t=LSe,H_t=USe,W_t=jSe,K_t=In([LSe,USe,jSe]),zSe="alibaba_cloud",FSe="aws",qSe="azure",BSe="gcp",Z_t=zSe,Y_t=FSe,J_t=qSe,X_t=BSe,Q_t=In([zSe,FSe,qSe,BSe]),GSe="ip_tcp",VSe="ip_udp",HSe="ip",WSe="unix",KSe="pipe",ZSe="inproc",YSe="other",evt=GSe,tvt=VSe,rvt=HSe,nvt=WSe,ovt=KSe,ivt=ZSe,svt=YSe,avt=In([GSe,VSe,HSe,WSe,KSe,ZSe,YSe]),JSe="wifi",XSe="wired",QSe="cell",eye="unavailable",tye="unknown",cvt=JSe,uvt=XSe,lvt=QSe,pvt=eye,dvt=tye,fvt=In([JSe,XSe,QSe,eye,tye]),rye="gprs",nye="edge",oye="umts",iye="cdma",sye="evdo_0",aye="evdo_a",cye="cdma2000_1xrtt",uye="hsdpa",lye="hsupa",pye="hspa",dye="iden",fye="evdo_b",mye="lte",hye="ehrpd",gye="hspap",_ye="gsm",vye="td_scdma",Sye="iwlan",yye="nr",Eye="nrnsa",Tye="lte_ca",mvt=rye,hvt=nye,gvt=oye,_vt=iye,vvt=sye,Svt=aye,yvt=cye,Evt=uye,Tvt=lye,bvt=pye,xvt=dye,Avt=fye,wvt=mye,Rvt=hye,Pvt=gye,Ivt=_ye,Ovt=vye,Nvt=Sye,Cvt=yye,$vt=Eye,kvt=Tye,Mvt=In([rye,nye,oye,iye,sye,aye,cye,uye,lye,pye,dye,fye,mye,hye,gye,_ye,vye,Sye,yye,Eye,Tye]),bye="1.0",xye="1.1",Aye="2.0",wye="SPDY",Rye="QUIC",Dvt=bye,Lvt=xye,Uvt=Aye,jvt=wye,zvt=Rye,Fvt={HTTP_1_0:bye,HTTP_1_1:xye,HTTP_2_0:Aye,SPDY:wye,QUIC:Rye},Pye="queue",Iye="topic",qvt=Pye,Bvt=Iye,Gvt=In([Pye,Iye]),Oye="receive",Nye="process",Vvt=Oye,Hvt=Nye,Wvt=In([Oye,Nye]),Cye=0,$ye=1,kye=2,Mye=3,Dye=4,Lye=5,Uye=6,jye=7,zye=8,Fye=9,qye=10,Bye=11,Gye=12,Vye=13,Hye=14,Wye=15,Kye=16,Kvt=Cye,Zvt=$ye,Yvt=kye,Jvt=Mye,Xvt=Dye,Qvt=Lye,eSt=Uye,tSt=jye,rSt=zye,nSt=Fye,oSt=qye,iSt=Bye,sSt=Gye,aSt=Vye,cSt=Hye,uSt=Wye,lSt=Kye,pSt={OK:Cye,CANCELLED:$ye,UNKNOWN:kye,INVALID_ARGUMENT:Mye,DEADLINE_EXCEEDED:Dye,NOT_FOUND:Lye,ALREADY_EXISTS:Uye,PERMISSION_DENIED:jye,RESOURCE_EXHAUSTED:zye,FAILED_PRECONDITION:Fye,ABORTED:qye,OUT_OF_RANGE:Bye,UNIMPLEMENTED:Gye,INTERNAL:Vye,UNAVAILABLE:Hye,DATA_LOSS:Wye,UNAUTHENTICATED:Kye},Zye="SENT",Yye="RECEIVED",dSt=Zye,fSt=Yye,mSt=In([Zye,Yye])});var Xye=O(()=>{Jye()});var Qye,eEe,tEe,rEe,nEe,oEe,iEe,sEe,aEe,cEe,uEe,lEe,pEe,dEe,fEe,mEe,hEe,gEe,_Ee,vEe,SEe,yEe,EEe,TEe,bEe,xEe,AEe,wEe,REe,PEe,IEe,OEe,NEe,CEe,$Ee,kEe,MEe,DEe,LEe,UEe,jEe,zEe,FEe,qEe,BEe,GEe,VEe,HEe,WEe,KEe,ZEe,YEe,JEe,XEe,QEe,eTe,tTe,rTe,nTe,oTe,iTe,sTe,aTe,cTe,uTe,lTe,pTe,dTe,fTe,mTe,hTe,gTe,_Te,vTe,STe,yTe,ETe,TTe,bTe,xTe,ATe,hSt,gSt,_St,vSt,SSt,ySt,ESt,TSt,bSt,xSt,ASt,wSt,RSt,PSt,ISt,OSt,NSt,CSt,$St,kSt,MSt,DSt,LSt,USt,jSt,zSt,FSt,qSt,BSt,GSt,VSt,HSt,WSt,KSt,ZSt,YSt,JSt,XSt,QSt,eyt,tyt,ryt,nyt,oyt,iyt,syt,ayt,cyt,uyt,lyt,pyt,dyt,fyt,myt,hyt,gyt,_yt,vyt,Syt,yyt,Eyt,Tyt,byt,xyt,Ayt,wyt,Ryt,Pyt,Iyt,Oyt,Nyt,R5,Cyt,$yt,kyt,Myt,Dyt,Lyt,Uyt,jyt,zyt,Fyt,wTe,RTe,PTe,ITe,qyt,Byt,Gyt,Vyt,Hyt,OTe,NTe,CTe,$Te,kTe,MTe,DTe,LTe,UTe,jTe,zTe,FTe,qTe,BTe,GTe,VTe,HTe,Wyt,Kyt,Zyt,Yyt,Jyt,Xyt,Qyt,eEt,tEt,rEt,nEt,oEt,iEt,sEt,aEt,cEt,uEt,lEt,WTe,KTe,pEt,dEt,fEt,ZTe,YTe,JTe,XTe,QTe,ebe,tbe,mEt,hEt,gEt,_Et,vEt,SEt,yEt,EEt,rbe,nbe,obe,ibe,sbe,abe,cbe,ube,lbe,pbe,dbe,TEt,bEt,xEt,AEt,wEt,REt,PEt,IEt,OEt,NEt,CEt,$Et,fbe,mbe,hbe,gbe,_be,vbe,Sbe,ybe,Ebe,Tbe,kEt,MEt,DEt,LEt,UEt,jEt,zEt,FEt,qEt,BEt,GEt,bbe=O(()=>{v5();Qye="cloud.provider",eEe="cloud.account.id",tEe="cloud.region",rEe="cloud.availability_zone",nEe="cloud.platform",oEe="aws.ecs.container.arn",iEe="aws.ecs.cluster.arn",sEe="aws.ecs.launchtype",aEe="aws.ecs.task.arn",cEe="aws.ecs.task.family",uEe="aws.ecs.task.revision",lEe="aws.eks.cluster.arn",pEe="aws.log.group.names",dEe="aws.log.group.arns",fEe="aws.log.stream.names",mEe="aws.log.stream.arns",hEe="container.name",gEe="container.id",_Ee="container.runtime",vEe="container.image.name",SEe="container.image.tag",yEe="deployment.environment",EEe="device.id",TEe="device.model.identifier",bEe="device.model.name",xEe="faas.name",AEe="faas.id",wEe="faas.version",REe="faas.instance",PEe="faas.max_memory",IEe="host.id",OEe="host.name",NEe="host.type",CEe="host.arch",$Ee="host.image.name",kEe="host.image.id",MEe="host.image.version",DEe="k8s.cluster.name",LEe="k8s.node.name",UEe="k8s.node.uid",jEe="k8s.namespace.name",zEe="k8s.pod.uid",FEe="k8s.pod.name",qEe="k8s.container.name",BEe="k8s.replicaset.uid",GEe="k8s.replicaset.name",VEe="k8s.deployment.uid",HEe="k8s.deployment.name",WEe="k8s.statefulset.uid",KEe="k8s.statefulset.name",ZEe="k8s.daemonset.uid",YEe="k8s.daemonset.name",JEe="k8s.job.uid",XEe="k8s.job.name",QEe="k8s.cronjob.uid",eTe="k8s.cronjob.name",tTe="os.type",rTe="os.description",nTe="os.name",oTe="os.version",iTe="process.pid",sTe="process.executable.name",aTe="process.executable.path",cTe="process.command",uTe="process.command_line",lTe="process.command_args",pTe="process.owner",dTe="process.runtime.name",fTe="process.runtime.version",mTe="process.runtime.description",hTe="service.name",gTe="service.namespace",_Te="service.instance.id",vTe="service.version",STe="telemetry.sdk.name",yTe="telemetry.sdk.language",ETe="telemetry.sdk.version",TTe="telemetry.auto.version",bTe="webengine.name",xTe="webengine.version",ATe="webengine.description",hSt=Qye,gSt=eEe,_St=tEe,vSt=rEe,SSt=nEe,ySt=oEe,ESt=iEe,TSt=sEe,bSt=aEe,xSt=cEe,ASt=uEe,wSt=lEe,RSt=pEe,PSt=dEe,ISt=fEe,OSt=mEe,NSt=hEe,CSt=gEe,$St=_Ee,kSt=vEe,MSt=SEe,DSt=yEe,LSt=EEe,USt=TEe,jSt=bEe,zSt=xEe,FSt=AEe,qSt=wEe,BSt=REe,GSt=PEe,VSt=IEe,HSt=OEe,WSt=NEe,KSt=CEe,ZSt=$Ee,YSt=kEe,JSt=MEe,XSt=DEe,QSt=LEe,eyt=UEe,tyt=jEe,ryt=zEe,nyt=FEe,oyt=qEe,iyt=BEe,syt=GEe,ayt=VEe,cyt=HEe,uyt=WEe,lyt=KEe,pyt=ZEe,dyt=YEe,fyt=JEe,myt=XEe,hyt=QEe,gyt=eTe,_yt=tTe,vyt=rTe,Syt=nTe,yyt=oTe,Eyt=iTe,Tyt=sTe,byt=aTe,xyt=cTe,Ayt=uTe,wyt=lTe,Ryt=pTe,Pyt=dTe,Iyt=fTe,Oyt=mTe,Nyt=hTe,R5=gTe,Cyt=_Te,$yt=vTe,kyt=STe,Myt=yTe,Dyt=ETe,Lyt=TTe,Uyt=bTe,jyt=xTe,zyt=ATe,Fyt=In([Qye,eEe,tEe,rEe,nEe,oEe,iEe,sEe,aEe,cEe,uEe,lEe,pEe,dEe,fEe,mEe,hEe,gEe,_Ee,vEe,SEe,yEe,EEe,TEe,bEe,xEe,AEe,wEe,REe,PEe,IEe,OEe,NEe,CEe,$Ee,kEe,MEe,DEe,LEe,UEe,jEe,zEe,FEe,qEe,BEe,GEe,VEe,HEe,WEe,KEe,ZEe,YEe,JEe,XEe,QEe,eTe,tTe,rTe,nTe,oTe,iTe,sTe,aTe,cTe,uTe,lTe,pTe,dTe,fTe,mTe,hTe,gTe,_Te,vTe,STe,yTe,ETe,TTe,bTe,xTe,ATe]),wTe="alibaba_cloud",RTe="aws",PTe="azure",ITe="gcp",qyt=wTe,Byt=RTe,Gyt=PTe,Vyt=ITe,Hyt=In([wTe,RTe,PTe,ITe]),OTe="alibaba_cloud_ecs",NTe="alibaba_cloud_fc",CTe="aws_ec2",$Te="aws_ecs",kTe="aws_eks",MTe="aws_lambda",DTe="aws_elastic_beanstalk",LTe="azure_vm",UTe="azure_container_instances",jTe="azure_aks",zTe="azure_functions",FTe="azure_app_service",qTe="gcp_compute_engine",BTe="gcp_cloud_run",GTe="gcp_kubernetes_engine",VTe="gcp_cloud_functions",HTe="gcp_app_engine",Wyt=OTe,Kyt=NTe,Zyt=CTe,Yyt=$Te,Jyt=kTe,Xyt=MTe,Qyt=DTe,eEt=LTe,tEt=UTe,rEt=jTe,nEt=zTe,oEt=FTe,iEt=qTe,sEt=BTe,aEt=GTe,cEt=VTe,uEt=HTe,lEt=In([OTe,NTe,CTe,$Te,kTe,MTe,DTe,LTe,UTe,jTe,zTe,FTe,qTe,BTe,GTe,VTe,HTe]),WTe="ec2",KTe="fargate",pEt=WTe,dEt=KTe,fEt=In([WTe,KTe]),ZTe="amd64",YTe="arm32",JTe="arm64",XTe="ia64",QTe="ppc32",ebe="ppc64",tbe="x86",mEt=ZTe,hEt=YTe,gEt=JTe,_Et=XTe,vEt=QTe,SEt=ebe,yEt=tbe,EEt=In([ZTe,YTe,JTe,XTe,QTe,ebe,tbe]),rbe="windows",nbe="linux",obe="darwin",ibe="freebsd",sbe="netbsd",abe="openbsd",cbe="dragonflybsd",ube="hpux",lbe="aix",pbe="solaris",dbe="z_os",TEt=rbe,bEt=nbe,xEt=obe,AEt=ibe,wEt=sbe,REt=abe,PEt=cbe,IEt=ube,OEt=lbe,NEt=pbe,CEt=dbe,$Et=In([rbe,nbe,obe,ibe,sbe,abe,cbe,ube,lbe,pbe,dbe]),fbe="cpp",mbe="dotnet",hbe="erlang",gbe="go",_be="java",vbe="nodejs",Sbe="php",ybe="python",Ebe="ruby",Tbe="webjs",kEt=fbe,MEt=mbe,DEt=hbe,LEt=gbe,UEt=_be,jEt=vbe,zEt=Sbe,FEt=ybe,qEt=Ebe,BEt=Tbe,GEt=In([fbe,mbe,hbe,gbe,_be,vbe,Sbe,ybe,Ebe,Tbe])});var xbe=O(()=>{bbe()});var VEt,HEt,WEt,KEt,ZEt,YEt,JEt,XEt,QEt,eTt,tTt,rTt,nTt,oTt,iTt,sTt,aTt,cTt,uTt,lTt,pTt,dTt,fTt,mTt,P5,Tb,hTt,bb,gTt,I5,O5,_Tt,xb,vTt,STt,yTt,ETt,TTt,bTt,xTt,ATt,xmr,Amr,N5,wTt,RTt,PTt,ITt,OTt,NTt,om,CTt,$Tt,kTt,wmr,MTt,DTt,LTt,UTt,Rmr,jTt,zTt,FTt,qTt,Zp,Qc,BTt,GTt,VTt,HTt,WTt,KTt,ZTt,YTt,JTt,Pmr,XTt,QTt,ebt,tbt,rbt,nbt,obt,ibt,sbt,abt,cbt,ubt,lbt,Imr,Omr,pbt,dbt,fbt,mbt,hbt,gbt,_bt,vbt,Nmr,Sbt,Ab,wb,Ts,C5,ybt,Ebt,Tbt,bbt,xbt,Abt,wbt,Rbt,Pbt,Cmr,Ibt,Obt,$mr,Nbt,Cbt,kmr,$bt,kbt,Mbt,Dbt,Lbt,Ubt,jbt,zbt,t_,Fbt,qbt,Bbt,Gbt,Abe=O(()=>{VEt="aspnetcore.diagnostics.exception.result",HEt="aborted",WEt="handled",KEt="skipped",ZEt="unhandled",YEt="aspnetcore.diagnostics.handler.type",JEt="aspnetcore.rate_limiting.policy",XEt="aspnetcore.rate_limiting.result",QEt="acquired",eTt="endpoint_limiter",tTt="global_limiter",rTt="request_canceled",nTt="aspnetcore.request.is_unhandled",oTt="aspnetcore.routing.is_fallback",iTt="aspnetcore.routing.match_status",sTt="failure",aTt="success",cTt="client.address",uTt="client.port",lTt="code.column.number",pTt="code.file.path",dTt="code.function.name",fTt="code.line.number",mTt="code.stacktrace",P5="db.collection.name",Tb="db.namespace",hTt="db.operation.batch.size",bb="db.operation.name",gTt="db.query.summary",I5="db.query.text",O5="db.response.status_code",_Tt="db.stored_procedure.name",xb="db.system.name",vTt="mariadb",STt="microsoft.sql_server",yTt="mysql",ETt="postgresql",TTt="dotnet.gc.heap.generation",bTt="gen0",xTt="gen1",ATt="gen2",xmr="loh",Amr="poh",N5="error.type",wTt="_OTHER",RTt="exception.escaped",PTt="exception.message",ITt="exception.stacktrace",OTt="exception.type",NTt=t=>`http.request.header.${t}`,om="http.request.method",CTt="_OTHER",$Tt="CONNECT",kTt="DELETE",wmr="GET",MTt="HEAD",DTt="OPTIONS",LTt="PATCH",UTt="POST",Rmr="PUT",jTt="TRACE",zTt="http.request.method_original",FTt="http.request.resend_count",qTt=t=>`http.response.header.${t}`,Zp="http.response.status_code",Qc="http.route",BTt="jvm.gc.action",GTt="jvm.gc.name",VTt="jvm.memory.pool.name",HTt="jvm.memory.type",WTt="heap",KTt="non_heap",ZTt="jvm.thread.daemon",YTt="jvm.thread.state",JTt="blocked",Pmr="new",XTt="runnable",QTt="terminated",ebt="timed_waiting",tbt="waiting",rbt="network.local.address",nbt="network.local.port",obt="network.peer.address",ibt="network.peer.port",sbt="network.protocol.name",abt="network.protocol.version",cbt="network.transport",ubt="pipe",lbt="quic",Imr="tcp",Omr="udp",pbt="unix",dbt="network.type",fbt="ipv4",mbt="ipv6",hbt="otel.scope.name",gbt="otel.scope.version",_bt="otel.status_code",vbt="ERROR",Nmr="OK",Sbt="otel.status_description",Ab="server.address",wb="server.port",Ts="service.name",C5="service.version",ybt="signalr.connection.status",Ebt="app_shutdown",Tbt="normal_closure",bbt="timeout",xbt="signalr.transport",Abt="long_polling",wbt="server_sent_events",Rbt="web_sockets",Pbt="telemetry.sdk.language",Cmr="cpp",Ibt="dotnet",Obt="erlang",$mr="go",Nbt="java",Cbt="nodejs",kmr="php",$bt="python",kbt="ruby",Mbt="rust",Dbt="swift",Lbt="webjs",Ubt="telemetry.sdk.name",jbt="telemetry.sdk.version",zbt="url.fragment",t_="url.full",Fbt="url.path",qbt="url.query",Bbt="url.scheme",Gbt="user_agent.original"});var Vbt,Hbt,Wbt,Kbt,Zbt,Ybt,Jbt,Xbt,Qbt,ext,txt,rxt,nxt,oxt,ixt,sxt,axt,cxt,uxt,lxt,pxt,dxt,fxt,mxt,hxt,gxt,_xt,vxt,Sxt,yxt,Ext,Txt,bxt,xxt,Axt,wxt,Rxt,Pxt,Ixt,Oxt,Nxt,Cxt,$xt,kxt,Mxt,Dxt,Lxt,Uxt,jxt,zxt,Fxt,wbe=O(()=>{Vbt="aspnetcore.diagnostics.exceptions",Hbt="aspnetcore.rate_limiting.active_request_leases",Wbt="aspnetcore.rate_limiting.queued_requests",Kbt="aspnetcore.rate_limiting.request.time_in_queue",Zbt="aspnetcore.rate_limiting.request_lease.duration",Ybt="aspnetcore.rate_limiting.requests",Jbt="aspnetcore.routing.match_attempts",Xbt="db.client.operation.duration",Qbt="dotnet.assembly.count",ext="dotnet.exceptions",txt="dotnet.gc.collections",rxt="dotnet.gc.heap.total_allocated",nxt="dotnet.gc.last_collection.heap.fragmentation.size",oxt="dotnet.gc.last_collection.heap.size",ixt="dotnet.gc.last_collection.memory.committed_size",sxt="dotnet.gc.pause.time",axt="dotnet.jit.compilation.time",cxt="dotnet.jit.compiled_il.size",uxt="dotnet.jit.compiled_methods",lxt="dotnet.monitor.lock_contentions",pxt="dotnet.process.cpu.count",dxt="dotnet.process.cpu.time",fxt="dotnet.process.memory.working_set",mxt="dotnet.thread_pool.queue.length",hxt="dotnet.thread_pool.thread.count",gxt="dotnet.thread_pool.work_item.count",_xt="dotnet.timer.count",vxt="http.client.request.duration",Sxt="http.server.request.duration",yxt="jvm.class.count",Ext="jvm.class.loaded",Txt="jvm.class.unloaded",bxt="jvm.cpu.count",xxt="jvm.cpu.recent_utilization",Axt="jvm.cpu.time",wxt="jvm.gc.duration",Rxt="jvm.memory.committed",Pxt="jvm.memory.limit",Ixt="jvm.memory.used",Oxt="jvm.memory.used_after_last_gc",Nxt="jvm.thread.count",Cxt="kestrel.active_connections",$xt="kestrel.active_tls_handshakes",kxt="kestrel.connection.duration",Mxt="kestrel.queued_connections",Dxt="kestrel.queued_requests",Lxt="kestrel.rejected_connections",Uxt="kestrel.tls_handshake.duration",jxt="kestrel.upgraded_connections",zxt="signalr.server.active_connections",Fxt="signalr.server.connection.duration"});var Cr={};Y(Cr,{ASPNETCORE_DIAGNOSTICS_EXCEPTION_RESULT_VALUE_ABORTED:()=>HEt,ASPNETCORE_DIAGNOSTICS_EXCEPTION_RESULT_VALUE_HANDLED:()=>WEt,ASPNETCORE_DIAGNOSTICS_EXCEPTION_RESULT_VALUE_SKIPPED:()=>KEt,ASPNETCORE_DIAGNOSTICS_EXCEPTION_RESULT_VALUE_UNHANDLED:()=>ZEt,ASPNETCORE_RATE_LIMITING_RESULT_VALUE_ACQUIRED:()=>QEt,ASPNETCORE_RATE_LIMITING_RESULT_VALUE_ENDPOINT_LIMITER:()=>eTt,ASPNETCORE_RATE_LIMITING_RESULT_VALUE_GLOBAL_LIMITER:()=>tTt,ASPNETCORE_RATE_LIMITING_RESULT_VALUE_REQUEST_CANCELED:()=>rTt,ASPNETCORE_ROUTING_MATCH_STATUS_VALUE_FAILURE:()=>sTt,ASPNETCORE_ROUTING_MATCH_STATUS_VALUE_SUCCESS:()=>aTt,ATTR_ASPNETCORE_DIAGNOSTICS_EXCEPTION_RESULT:()=>VEt,ATTR_ASPNETCORE_DIAGNOSTICS_HANDLER_TYPE:()=>YEt,ATTR_ASPNETCORE_RATE_LIMITING_POLICY:()=>JEt,ATTR_ASPNETCORE_RATE_LIMITING_RESULT:()=>XEt,ATTR_ASPNETCORE_REQUEST_IS_UNHANDLED:()=>nTt,ATTR_ASPNETCORE_ROUTING_IS_FALLBACK:()=>oTt,ATTR_ASPNETCORE_ROUTING_MATCH_STATUS:()=>iTt,ATTR_CLIENT_ADDRESS:()=>cTt,ATTR_CLIENT_PORT:()=>uTt,ATTR_CODE_COLUMN_NUMBER:()=>lTt,ATTR_CODE_FILE_PATH:()=>pTt,ATTR_CODE_FUNCTION_NAME:()=>dTt,ATTR_CODE_LINE_NUMBER:()=>fTt,ATTR_CODE_STACKTRACE:()=>mTt,ATTR_DB_COLLECTION_NAME:()=>P5,ATTR_DB_NAMESPACE:()=>Tb,ATTR_DB_OPERATION_BATCH_SIZE:()=>hTt,ATTR_DB_OPERATION_NAME:()=>bb,ATTR_DB_QUERY_SUMMARY:()=>gTt,ATTR_DB_QUERY_TEXT:()=>I5,ATTR_DB_RESPONSE_STATUS_CODE:()=>O5,ATTR_DB_STORED_PROCEDURE_NAME:()=>_Tt,ATTR_DB_SYSTEM_NAME:()=>xb,ATTR_DOTNET_GC_HEAP_GENERATION:()=>TTt,ATTR_ERROR_TYPE:()=>N5,ATTR_EXCEPTION_ESCAPED:()=>RTt,ATTR_EXCEPTION_MESSAGE:()=>PTt,ATTR_EXCEPTION_STACKTRACE:()=>ITt,ATTR_EXCEPTION_TYPE:()=>OTt,ATTR_HTTP_REQUEST_HEADER:()=>NTt,ATTR_HTTP_REQUEST_METHOD:()=>om,ATTR_HTTP_REQUEST_METHOD_ORIGINAL:()=>zTt,ATTR_HTTP_REQUEST_RESEND_COUNT:()=>FTt,ATTR_HTTP_RESPONSE_HEADER:()=>qTt,ATTR_HTTP_RESPONSE_STATUS_CODE:()=>Zp,ATTR_HTTP_ROUTE:()=>Qc,ATTR_JVM_GC_ACTION:()=>BTt,ATTR_JVM_GC_NAME:()=>GTt,ATTR_JVM_MEMORY_POOL_NAME:()=>VTt,ATTR_JVM_MEMORY_TYPE:()=>HTt,ATTR_JVM_THREAD_DAEMON:()=>ZTt,ATTR_JVM_THREAD_STATE:()=>YTt,ATTR_NETWORK_LOCAL_ADDRESS:()=>rbt,ATTR_NETWORK_LOCAL_PORT:()=>nbt,ATTR_NETWORK_PEER_ADDRESS:()=>obt,ATTR_NETWORK_PEER_PORT:()=>ibt,ATTR_NETWORK_PROTOCOL_NAME:()=>sbt,ATTR_NETWORK_PROTOCOL_VERSION:()=>abt,ATTR_NETWORK_TRANSPORT:()=>cbt,ATTR_NETWORK_TYPE:()=>dbt,ATTR_OTEL_SCOPE_NAME:()=>hbt,ATTR_OTEL_SCOPE_VERSION:()=>gbt,ATTR_OTEL_STATUS_CODE:()=>_bt,ATTR_OTEL_STATUS_DESCRIPTION:()=>Sbt,ATTR_SERVER_ADDRESS:()=>Ab,ATTR_SERVER_PORT:()=>wb,ATTR_SERVICE_NAME:()=>Ts,ATTR_SERVICE_VERSION:()=>C5,ATTR_SIGNALR_CONNECTION_STATUS:()=>ybt,ATTR_SIGNALR_TRANSPORT:()=>xbt,ATTR_TELEMETRY_SDK_LANGUAGE:()=>Pbt,ATTR_TELEMETRY_SDK_NAME:()=>Ubt,ATTR_TELEMETRY_SDK_VERSION:()=>jbt,ATTR_URL_FRAGMENT:()=>zbt,ATTR_URL_FULL:()=>t_,ATTR_URL_PATH:()=>Fbt,ATTR_URL_QUERY:()=>qbt,ATTR_URL_SCHEME:()=>Bbt,ATTR_USER_AGENT_ORIGINAL:()=>Gbt,AWSECSLAUNCHTYPEVALUES_EC2:()=>pEt,AWSECSLAUNCHTYPEVALUES_FARGATE:()=>dEt,AwsEcsLaunchtypeValues:()=>fEt,CLOUDPLATFORMVALUES_ALIBABA_CLOUD_ECS:()=>Wyt,CLOUDPLATFORMVALUES_ALIBABA_CLOUD_FC:()=>Kyt,CLOUDPLATFORMVALUES_AWS_EC2:()=>Zyt,CLOUDPLATFORMVALUES_AWS_ECS:()=>Yyt,CLOUDPLATFORMVALUES_AWS_EKS:()=>Jyt,CLOUDPLATFORMVALUES_AWS_ELASTIC_BEANSTALK:()=>Qyt,CLOUDPLATFORMVALUES_AWS_LAMBDA:()=>Xyt,CLOUDPLATFORMVALUES_AZURE_AKS:()=>rEt,CLOUDPLATFORMVALUES_AZURE_APP_SERVICE:()=>oEt,CLOUDPLATFORMVALUES_AZURE_CONTAINER_INSTANCES:()=>tEt,CLOUDPLATFORMVALUES_AZURE_FUNCTIONS:()=>nEt,CLOUDPLATFORMVALUES_AZURE_VM:()=>eEt,CLOUDPLATFORMVALUES_GCP_APP_ENGINE:()=>uEt,CLOUDPLATFORMVALUES_GCP_CLOUD_FUNCTIONS:()=>cEt,CLOUDPLATFORMVALUES_GCP_CLOUD_RUN:()=>sEt,CLOUDPLATFORMVALUES_GCP_COMPUTE_ENGINE:()=>iEt,CLOUDPLATFORMVALUES_GCP_KUBERNETES_ENGINE:()=>aEt,CLOUDPROVIDERVALUES_ALIBABA_CLOUD:()=>qyt,CLOUDPROVIDERVALUES_AWS:()=>Byt,CLOUDPROVIDERVALUES_AZURE:()=>Gyt,CLOUDPROVIDERVALUES_GCP:()=>Vyt,CloudPlatformValues:()=>lEt,CloudProviderValues:()=>Hyt,DBCASSANDRACONSISTENCYLEVELVALUES_ALL:()=>R_t,DBCASSANDRACONSISTENCYLEVELVALUES_ANY:()=>M_t,DBCASSANDRACONSISTENCYLEVELVALUES_EACH_QUORUM:()=>P_t,DBCASSANDRACONSISTENCYLEVELVALUES_LOCAL_ONE:()=>k_t,DBCASSANDRACONSISTENCYLEVELVALUES_LOCAL_QUORUM:()=>O_t,DBCASSANDRACONSISTENCYLEVELVALUES_LOCAL_SERIAL:()=>L_t,DBCASSANDRACONSISTENCYLEVELVALUES_ONE:()=>N_t,DBCASSANDRACONSISTENCYLEVELVALUES_QUORUM:()=>I_t,DBCASSANDRACONSISTENCYLEVELVALUES_SERIAL:()=>D_t,DBCASSANDRACONSISTENCYLEVELVALUES_THREE:()=>$_t,DBCASSANDRACONSISTENCYLEVELVALUES_TWO:()=>C_t,DBSYSTEMVALUES_ADABAS:()=>Ygt,DBSYSTEMVALUES_CACHE:()=>Zgt,DBSYSTEMVALUES_CASSANDRA:()=>f_t,DBSYSTEMVALUES_CLOUDSCAPE:()=>Fgt,DBSYSTEMVALUES_COCKROACHDB:()=>A_t,DBSYSTEMVALUES_COLDFUSION:()=>d_t,DBSYSTEMVALUES_COSMOSDB:()=>S_t,DBSYSTEMVALUES_COUCHBASE:()=>__t,DBSYSTEMVALUES_COUCHDB:()=>v_t,DBSYSTEMVALUES_DB2:()=>Lgt,DBSYSTEMVALUES_DERBY:()=>Xgt,DBSYSTEMVALUES_DYNAMODB:()=>y_t,DBSYSTEMVALUES_EDB:()=>Kgt,DBSYSTEMVALUES_ELASTICSEARCH:()=>b_t,DBSYSTEMVALUES_FILEMAKER:()=>Qgt,DBSYSTEMVALUES_FIREBIRD:()=>Jgt,DBSYSTEMVALUES_FIRSTSQL:()=>Wgt,DBSYSTEMVALUES_GEODE:()=>T_t,DBSYSTEMVALUES_H2:()=>p_t,DBSYSTEMVALUES_HANADB:()=>Vgt,DBSYSTEMVALUES_HBASE:()=>m_t,DBSYSTEMVALUES_HIVE:()=>zgt,DBSYSTEMVALUES_HSQLDB:()=>qgt,DBSYSTEMVALUES_INFORMIX:()=>e_t,DBSYSTEMVALUES_INGRES:()=>Hgt,DBSYSTEMVALUES_INSTANTDB:()=>t_t,DBSYSTEMVALUES_INTERBASE:()=>r_t,DBSYSTEMVALUES_MARIADB:()=>n_t,DBSYSTEMVALUES_MAXDB:()=>Ggt,DBSYSTEMVALUES_MEMCACHED:()=>x_t,DBSYSTEMVALUES_MONGODB:()=>h_t,DBSYSTEMVALUES_MSSQL:()=>kgt,DBSYSTEMVALUES_MYSQL:()=>Mgt,DBSYSTEMVALUES_NEO4J:()=>E_t,DBSYSTEMVALUES_NETEZZA:()=>o_t,DBSYSTEMVALUES_ORACLE:()=>Dgt,DBSYSTEMVALUES_OTHER_SQL:()=>$gt,DBSYSTEMVALUES_PERVASIVE:()=>i_t,DBSYSTEMVALUES_POINTBASE:()=>s_t,DBSYSTEMVALUES_POSTGRESQL:()=>Ugt,DBSYSTEMVALUES_PROGRESS:()=>Bgt,DBSYSTEMVALUES_REDIS:()=>g_t,DBSYSTEMVALUES_REDSHIFT:()=>jgt,DBSYSTEMVALUES_SQLITE:()=>a_t,DBSYSTEMVALUES_SYBASE:()=>c_t,DBSYSTEMVALUES_TERADATA:()=>u_t,DBSYSTEMVALUES_VERTICA:()=>l_t,DB_SYSTEM_NAME_VALUE_MARIADB:()=>vTt,DB_SYSTEM_NAME_VALUE_MICROSOFT_SQL_SERVER:()=>STt,DB_SYSTEM_NAME_VALUE_MYSQL:()=>yTt,DB_SYSTEM_NAME_VALUE_POSTGRESQL:()=>ETt,DOTNET_GC_HEAP_GENERATION_VALUE_GEN0:()=>bTt,DOTNET_GC_HEAP_GENERATION_VALUE_GEN1:()=>xTt,DOTNET_GC_HEAP_GENERATION_VALUE_GEN2:()=>ATt,DOTNET_GC_HEAP_GENERATION_VALUE_LOH:()=>xmr,DOTNET_GC_HEAP_GENERATION_VALUE_POH:()=>Amr,DbCassandraConsistencyLevelValues:()=>U_t,DbSystemValues:()=>w_t,ERROR_TYPE_VALUE_OTHER:()=>wTt,FAASDOCUMENTOPERATIONVALUES_DELETE:()=>W_t,FAASDOCUMENTOPERATIONVALUES_EDIT:()=>H_t,FAASDOCUMENTOPERATIONVALUES_INSERT:()=>V_t,FAASINVOKEDPROVIDERVALUES_ALIBABA_CLOUD:()=>Z_t,FAASINVOKEDPROVIDERVALUES_AWS:()=>Y_t,FAASINVOKEDPROVIDERVALUES_AZURE:()=>J_t,FAASINVOKEDPROVIDERVALUES_GCP:()=>X_t,FAASTRIGGERVALUES_DATASOURCE:()=>j_t,FAASTRIGGERVALUES_HTTP:()=>z_t,FAASTRIGGERVALUES_OTHER:()=>B_t,FAASTRIGGERVALUES_PUBSUB:()=>F_t,FAASTRIGGERVALUES_TIMER:()=>q_t,FaasDocumentOperationValues:()=>K_t,FaasInvokedProviderValues:()=>Q_t,FaasTriggerValues:()=>G_t,HOSTARCHVALUES_AMD64:()=>mEt,HOSTARCHVALUES_ARM32:()=>hEt,HOSTARCHVALUES_ARM64:()=>gEt,HOSTARCHVALUES_IA64:()=>_Et,HOSTARCHVALUES_PPC32:()=>vEt,HOSTARCHVALUES_PPC64:()=>SEt,HOSTARCHVALUES_X86:()=>yEt,HTTPFLAVORVALUES_HTTP_1_0:()=>Dvt,HTTPFLAVORVALUES_HTTP_1_1:()=>Lvt,HTTPFLAVORVALUES_HTTP_2_0:()=>Uvt,HTTPFLAVORVALUES_QUIC:()=>zvt,HTTPFLAVORVALUES_SPDY:()=>jvt,HTTP_REQUEST_METHOD_VALUE_CONNECT:()=>$Tt,HTTP_REQUEST_METHOD_VALUE_DELETE:()=>kTt,HTTP_REQUEST_METHOD_VALUE_GET:()=>wmr,HTTP_REQUEST_METHOD_VALUE_HEAD:()=>MTt,HTTP_REQUEST_METHOD_VALUE_OPTIONS:()=>DTt,HTTP_REQUEST_METHOD_VALUE_OTHER:()=>CTt,HTTP_REQUEST_METHOD_VALUE_PATCH:()=>LTt,HTTP_REQUEST_METHOD_VALUE_POST:()=>UTt,HTTP_REQUEST_METHOD_VALUE_PUT:()=>Rmr,HTTP_REQUEST_METHOD_VALUE_TRACE:()=>jTt,HostArchValues:()=>EEt,HttpFlavorValues:()=>Fvt,JVM_MEMORY_TYPE_VALUE_HEAP:()=>WTt,JVM_MEMORY_TYPE_VALUE_NON_HEAP:()=>KTt,JVM_THREAD_STATE_VALUE_BLOCKED:()=>JTt,JVM_THREAD_STATE_VALUE_NEW:()=>Pmr,JVM_THREAD_STATE_VALUE_RUNNABLE:()=>XTt,JVM_THREAD_STATE_VALUE_TERMINATED:()=>QTt,JVM_THREAD_STATE_VALUE_TIMED_WAITING:()=>ebt,JVM_THREAD_STATE_VALUE_WAITING:()=>tbt,MESSAGETYPEVALUES_RECEIVED:()=>fSt,MESSAGETYPEVALUES_SENT:()=>dSt,MESSAGINGDESTINATIONKINDVALUES_QUEUE:()=>qvt,MESSAGINGDESTINATIONKINDVALUES_TOPIC:()=>Bvt,MESSAGINGOPERATIONVALUES_PROCESS:()=>Hvt,MESSAGINGOPERATIONVALUES_RECEIVE:()=>Vvt,METRIC_ASPNETCORE_DIAGNOSTICS_EXCEPTIONS:()=>Vbt,METRIC_ASPNETCORE_RATE_LIMITING_ACTIVE_REQUEST_LEASES:()=>Hbt,METRIC_ASPNETCORE_RATE_LIMITING_QUEUED_REQUESTS:()=>Wbt,METRIC_ASPNETCORE_RATE_LIMITING_REQUESTS:()=>Ybt,METRIC_ASPNETCORE_RATE_LIMITING_REQUEST_LEASE_DURATION:()=>Zbt,METRIC_ASPNETCORE_RATE_LIMITING_REQUEST_TIME_IN_QUEUE:()=>Kbt,METRIC_ASPNETCORE_ROUTING_MATCH_ATTEMPTS:()=>Jbt,METRIC_DB_CLIENT_OPERATION_DURATION:()=>Xbt,METRIC_DOTNET_ASSEMBLY_COUNT:()=>Qbt,METRIC_DOTNET_EXCEPTIONS:()=>ext,METRIC_DOTNET_GC_COLLECTIONS:()=>txt,METRIC_DOTNET_GC_HEAP_TOTAL_ALLOCATED:()=>rxt,METRIC_DOTNET_GC_LAST_COLLECTION_HEAP_FRAGMENTATION_SIZE:()=>nxt,METRIC_DOTNET_GC_LAST_COLLECTION_HEAP_SIZE:()=>oxt,METRIC_DOTNET_GC_LAST_COLLECTION_MEMORY_COMMITTED_SIZE:()=>ixt,METRIC_DOTNET_GC_PAUSE_TIME:()=>sxt,METRIC_DOTNET_JIT_COMPILATION_TIME:()=>axt,METRIC_DOTNET_JIT_COMPILED_IL_SIZE:()=>cxt,METRIC_DOTNET_JIT_COMPILED_METHODS:()=>uxt,METRIC_DOTNET_MONITOR_LOCK_CONTENTIONS:()=>lxt,METRIC_DOTNET_PROCESS_CPU_COUNT:()=>pxt,METRIC_DOTNET_PROCESS_CPU_TIME:()=>dxt,METRIC_DOTNET_PROCESS_MEMORY_WORKING_SET:()=>fxt,METRIC_DOTNET_THREAD_POOL_QUEUE_LENGTH:()=>mxt,METRIC_DOTNET_THREAD_POOL_THREAD_COUNT:()=>hxt,METRIC_DOTNET_THREAD_POOL_WORK_ITEM_COUNT:()=>gxt,METRIC_DOTNET_TIMER_COUNT:()=>_xt,METRIC_HTTP_CLIENT_REQUEST_DURATION:()=>vxt,METRIC_HTTP_SERVER_REQUEST_DURATION:()=>Sxt,METRIC_JVM_CLASS_COUNT:()=>yxt,METRIC_JVM_CLASS_LOADED:()=>Ext,METRIC_JVM_CLASS_UNLOADED:()=>Txt,METRIC_JVM_CPU_COUNT:()=>bxt,METRIC_JVM_CPU_RECENT_UTILIZATION:()=>xxt,METRIC_JVM_CPU_TIME:()=>Axt,METRIC_JVM_GC_DURATION:()=>wxt,METRIC_JVM_MEMORY_COMMITTED:()=>Rxt,METRIC_JVM_MEMORY_LIMIT:()=>Pxt,METRIC_JVM_MEMORY_USED:()=>Ixt,METRIC_JVM_MEMORY_USED_AFTER_LAST_GC:()=>Oxt,METRIC_JVM_THREAD_COUNT:()=>Nxt,METRIC_KESTREL_ACTIVE_CONNECTIONS:()=>Cxt,METRIC_KESTREL_ACTIVE_TLS_HANDSHAKES:()=>$xt,METRIC_KESTREL_CONNECTION_DURATION:()=>kxt,METRIC_KESTREL_QUEUED_CONNECTIONS:()=>Mxt,METRIC_KESTREL_QUEUED_REQUESTS:()=>Dxt,METRIC_KESTREL_REJECTED_CONNECTIONS:()=>Lxt,METRIC_KESTREL_TLS_HANDSHAKE_DURATION:()=>Uxt,METRIC_KESTREL_UPGRADED_CONNECTIONS:()=>jxt,METRIC_SIGNALR_SERVER_ACTIVE_CONNECTIONS:()=>zxt,METRIC_SIGNALR_SERVER_CONNECTION_DURATION:()=>Fxt,MessageTypeValues:()=>mSt,MessagingDestinationKindValues:()=>Gvt,MessagingOperationValues:()=>Wvt,NETHOSTCONNECTIONSUBTYPEVALUES_CDMA:()=>_vt,NETHOSTCONNECTIONSUBTYPEVALUES_CDMA2000_1XRTT:()=>yvt,NETHOSTCONNECTIONSUBTYPEVALUES_EDGE:()=>hvt,NETHOSTCONNECTIONSUBTYPEVALUES_EHRPD:()=>Rvt,NETHOSTCONNECTIONSUBTYPEVALUES_EVDO_0:()=>vvt,NETHOSTCONNECTIONSUBTYPEVALUES_EVDO_A:()=>Svt,NETHOSTCONNECTIONSUBTYPEVALUES_EVDO_B:()=>Avt,NETHOSTCONNECTIONSUBTYPEVALUES_GPRS:()=>mvt,NETHOSTCONNECTIONSUBTYPEVALUES_GSM:()=>Ivt,NETHOSTCONNECTIONSUBTYPEVALUES_HSDPA:()=>Evt,NETHOSTCONNECTIONSUBTYPEVALUES_HSPA:()=>bvt,NETHOSTCONNECTIONSUBTYPEVALUES_HSPAP:()=>Pvt,NETHOSTCONNECTIONSUBTYPEVALUES_HSUPA:()=>Tvt,NETHOSTCONNECTIONSUBTYPEVALUES_IDEN:()=>xvt,NETHOSTCONNECTIONSUBTYPEVALUES_IWLAN:()=>Nvt,NETHOSTCONNECTIONSUBTYPEVALUES_LTE:()=>wvt,NETHOSTCONNECTIONSUBTYPEVALUES_LTE_CA:()=>kvt,NETHOSTCONNECTIONSUBTYPEVALUES_NR:()=>Cvt,NETHOSTCONNECTIONSUBTYPEVALUES_NRNSA:()=>$vt,NETHOSTCONNECTIONSUBTYPEVALUES_TD_SCDMA:()=>Ovt,NETHOSTCONNECTIONSUBTYPEVALUES_UMTS:()=>gvt,NETHOSTCONNECTIONTYPEVALUES_CELL:()=>lvt,NETHOSTCONNECTIONTYPEVALUES_UNAVAILABLE:()=>pvt,NETHOSTCONNECTIONTYPEVALUES_UNKNOWN:()=>dvt,NETHOSTCONNECTIONTYPEVALUES_WIFI:()=>cvt,NETHOSTCONNECTIONTYPEVALUES_WIRED:()=>uvt,NETTRANSPORTVALUES_INPROC:()=>ivt,NETTRANSPORTVALUES_IP:()=>rvt,NETTRANSPORTVALUES_IP_TCP:()=>evt,NETTRANSPORTVALUES_IP_UDP:()=>tvt,NETTRANSPORTVALUES_OTHER:()=>svt,NETTRANSPORTVALUES_PIPE:()=>ovt,NETTRANSPORTVALUES_UNIX:()=>nvt,NETWORK_TRANSPORT_VALUE_PIPE:()=>ubt,NETWORK_TRANSPORT_VALUE_QUIC:()=>lbt,NETWORK_TRANSPORT_VALUE_TCP:()=>Imr,NETWORK_TRANSPORT_VALUE_UDP:()=>Omr,NETWORK_TRANSPORT_VALUE_UNIX:()=>pbt,NETWORK_TYPE_VALUE_IPV4:()=>fbt,NETWORK_TYPE_VALUE_IPV6:()=>mbt,NetHostConnectionSubtypeValues:()=>Mvt,NetHostConnectionTypeValues:()=>fvt,NetTransportValues:()=>avt,OSTYPEVALUES_AIX:()=>OEt,OSTYPEVALUES_DARWIN:()=>xEt,OSTYPEVALUES_DRAGONFLYBSD:()=>PEt,OSTYPEVALUES_FREEBSD:()=>AEt,OSTYPEVALUES_HPUX:()=>IEt,OSTYPEVALUES_LINUX:()=>bEt,OSTYPEVALUES_NETBSD:()=>wEt,OSTYPEVALUES_OPENBSD:()=>REt,OSTYPEVALUES_SOLARIS:()=>NEt,OSTYPEVALUES_WINDOWS:()=>TEt,OSTYPEVALUES_Z_OS:()=>CEt,OTEL_STATUS_CODE_VALUE_ERROR:()=>vbt,OTEL_STATUS_CODE_VALUE_OK:()=>Nmr,OsTypeValues:()=>$Et,RPCGRPCSTATUSCODEVALUES_ABORTED:()=>oSt,RPCGRPCSTATUSCODEVALUES_ALREADY_EXISTS:()=>eSt,RPCGRPCSTATUSCODEVALUES_CANCELLED:()=>Zvt,RPCGRPCSTATUSCODEVALUES_DATA_LOSS:()=>uSt,RPCGRPCSTATUSCODEVALUES_DEADLINE_EXCEEDED:()=>Xvt,RPCGRPCSTATUSCODEVALUES_FAILED_PRECONDITION:()=>nSt,RPCGRPCSTATUSCODEVALUES_INTERNAL:()=>aSt,RPCGRPCSTATUSCODEVALUES_INVALID_ARGUMENT:()=>Jvt,RPCGRPCSTATUSCODEVALUES_NOT_FOUND:()=>Qvt,RPCGRPCSTATUSCODEVALUES_OK:()=>Kvt,RPCGRPCSTATUSCODEVALUES_OUT_OF_RANGE:()=>iSt,RPCGRPCSTATUSCODEVALUES_PERMISSION_DENIED:()=>tSt,RPCGRPCSTATUSCODEVALUES_RESOURCE_EXHAUSTED:()=>rSt,RPCGRPCSTATUSCODEVALUES_UNAUTHENTICATED:()=>lSt,RPCGRPCSTATUSCODEVALUES_UNAVAILABLE:()=>cSt,RPCGRPCSTATUSCODEVALUES_UNIMPLEMENTED:()=>sSt,RPCGRPCSTATUSCODEVALUES_UNKNOWN:()=>Yvt,RpcGrpcStatusCodeValues:()=>pSt,SEMATTRS_AWS_DYNAMODB_ATTRIBUTES_TO_GET:()=>Ght,SEMATTRS_AWS_DYNAMODB_ATTRIBUTE_DEFINITIONS:()=>rgt,SEMATTRS_AWS_DYNAMODB_CONSISTENT_READ:()=>Fht,SEMATTRS_AWS_DYNAMODB_CONSUMED_CAPACITY:()=>Lht,SEMATTRS_AWS_DYNAMODB_COUNT:()=>egt,SEMATTRS_AWS_DYNAMODB_EXCLUSIVE_START_TABLE:()=>Zht,SEMATTRS_AWS_DYNAMODB_GLOBAL_SECONDARY_INDEXES:()=>Wht,SEMATTRS_AWS_DYNAMODB_GLOBAL_SECONDARY_INDEX_UPDATES:()=>ngt,SEMATTRS_AWS_DYNAMODB_INDEX_NAME:()=>Vht,SEMATTRS_AWS_DYNAMODB_ITEM_COLLECTION_METRICS:()=>Uht,SEMATTRS_AWS_DYNAMODB_LIMIT:()=>Bht,SEMATTRS_AWS_DYNAMODB_LOCAL_SECONDARY_INDEXES:()=>Kht,SEMATTRS_AWS_DYNAMODB_PROJECTION:()=>qht,SEMATTRS_AWS_DYNAMODB_PROVISIONED_READ_CAPACITY:()=>jht,SEMATTRS_AWS_DYNAMODB_PROVISIONED_WRITE_CAPACITY:()=>zht,SEMATTRS_AWS_DYNAMODB_SCANNED_COUNT:()=>tgt,SEMATTRS_AWS_DYNAMODB_SCAN_FORWARD:()=>Jht,SEMATTRS_AWS_DYNAMODB_SEGMENT:()=>Xht,SEMATTRS_AWS_DYNAMODB_SELECT:()=>Hht,SEMATTRS_AWS_DYNAMODB_TABLE_COUNT:()=>Yht,SEMATTRS_AWS_DYNAMODB_TABLE_NAMES:()=>Dht,SEMATTRS_AWS_DYNAMODB_TOTAL_SEGMENTS:()=>Qht,SEMATTRS_AWS_LAMBDA_INVOKED_ARN:()=>bmt,SEMATTRS_CODE_FILEPATH:()=>xht,SEMATTRS_CODE_FUNCTION:()=>Tht,SEMATTRS_CODE_LINENO:()=>Aht,SEMATTRS_CODE_NAMESPACE:()=>bht,SEMATTRS_DB_CASSANDRA_CONSISTENCY_LEVEL:()=>Cmt,SEMATTRS_DB_CASSANDRA_COORDINATOR_DC:()=>Lmt,SEMATTRS_DB_CASSANDRA_COORDINATOR_ID:()=>Dmt,SEMATTRS_DB_CASSANDRA_IDEMPOTENCE:()=>kmt,SEMATTRS_DB_CASSANDRA_KEYSPACE:()=>Omt,SEMATTRS_DB_CASSANDRA_PAGE_SIZE:()=>Nmt,SEMATTRS_DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT:()=>Mmt,SEMATTRS_DB_CASSANDRA_TABLE:()=>$mt,SEMATTRS_DB_CONNECTION_STRING:()=>xmt,SEMATTRS_DB_HBASE_NAMESPACE:()=>Umt,SEMATTRS_DB_JDBC_DRIVER_CLASSNAME:()=>wmt,SEMATTRS_DB_MONGODB_COLLECTION:()=>zmt,SEMATTRS_DB_MSSQL_INSTANCE_NAME:()=>Imt,SEMATTRS_DB_NAME:()=>Rmt,SEMATTRS_DB_OPERATION:()=>Pmt,SEMATTRS_DB_REDIS_DATABASE_INDEX:()=>jmt,SEMATTRS_DB_SQL_TABLE:()=>Fmt,SEMATTRS_DB_STATEMENT:()=>y5,SEMATTRS_DB_SYSTEM:()=>S5,SEMATTRS_DB_USER:()=>Amt,SEMATTRS_ENDUSER_ID:()=>_ht,SEMATTRS_ENDUSER_ROLE:()=>vht,SEMATTRS_ENDUSER_SCOPE:()=>Sht,SEMATTRS_EXCEPTION_ESCAPED:()=>Vmt,SEMATTRS_EXCEPTION_MESSAGE:()=>Bmt,SEMATTRS_EXCEPTION_STACKTRACE:()=>Gmt,SEMATTRS_EXCEPTION_TYPE:()=>qmt,SEMATTRS_FAAS_COLDSTART:()=>Qmt,SEMATTRS_FAAS_CRON:()=>Xmt,SEMATTRS_FAAS_DOCUMENT_COLLECTION:()=>Wmt,SEMATTRS_FAAS_DOCUMENT_NAME:()=>Ymt,SEMATTRS_FAAS_DOCUMENT_OPERATION:()=>Kmt,SEMATTRS_FAAS_DOCUMENT_TIME:()=>Zmt,SEMATTRS_FAAS_EXECUTION:()=>Hmt,SEMATTRS_FAAS_INVOKED_NAME:()=>eht,SEMATTRS_FAAS_INVOKED_PROVIDER:()=>tht,SEMATTRS_FAAS_INVOKED_REGION:()=>rht,SEMATTRS_FAAS_TIME:()=>Jmt,SEMATTRS_FAAS_TRIGGER:()=>E5,SEMATTRS_HTTP_CLIENT_IP:()=>Mht,SEMATTRS_HTTP_FLAVOR:()=>Pht,SEMATTRS_HTTP_HOST:()=>wht,SEMATTRS_HTTP_METHOD:()=>Eb,SEMATTRS_HTTP_REQUEST_CONTENT_LENGTH:()=>Oht,SEMATTRS_HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED:()=>Nht,SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH:()=>Cht,SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED:()=>$ht,SEMATTRS_HTTP_ROUTE:()=>b5,SEMATTRS_HTTP_SCHEME:()=>Rht,SEMATTRS_HTTP_SERVER_NAME:()=>kht,SEMATTRS_HTTP_STATUS_CODE:()=>WN,SEMATTRS_HTTP_TARGET:()=>T5,SEMATTRS_HTTP_URL:()=>e_,SEMATTRS_HTTP_USER_AGENT:()=>Iht,SEMATTRS_MESSAGE_COMPRESSED_SIZE:()=>Ogt,SEMATTRS_MESSAGE_ID:()=>Igt,SEMATTRS_MESSAGE_TYPE:()=>Pgt,SEMATTRS_MESSAGE_UNCOMPRESSED_SIZE:()=>Ngt,SEMATTRS_MESSAGING_CONSUMER_ID:()=>hgt,SEMATTRS_MESSAGING_CONVERSATION_ID:()=>pgt,SEMATTRS_MESSAGING_DESTINATION:()=>ogt,SEMATTRS_MESSAGING_DESTINATION_KIND:()=>igt,SEMATTRS_MESSAGING_KAFKA_CLIENT_ID:()=>Sgt,SEMATTRS_MESSAGING_KAFKA_CONSUMER_GROUP:()=>vgt,SEMATTRS_MESSAGING_KAFKA_MESSAGE_KEY:()=>_gt,SEMATTRS_MESSAGING_KAFKA_PARTITION:()=>ygt,SEMATTRS_MESSAGING_KAFKA_TOMBSTONE:()=>Egt,SEMATTRS_MESSAGING_MESSAGE_ID:()=>lgt,SEMATTRS_MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES:()=>fgt,SEMATTRS_MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES:()=>dgt,SEMATTRS_MESSAGING_OPERATION:()=>mgt,SEMATTRS_MESSAGING_PROTOCOL:()=>agt,SEMATTRS_MESSAGING_PROTOCOL_VERSION:()=>cgt,SEMATTRS_MESSAGING_RABBITMQ_ROUTING_KEY:()=>ggt,SEMATTRS_MESSAGING_SYSTEM:()=>x5,SEMATTRS_MESSAGING_TEMP_DESTINATION:()=>sgt,SEMATTRS_MESSAGING_URL:()=>ugt,SEMATTRS_NET_HOST_CARRIER_ICC:()=>hht,SEMATTRS_NET_HOST_CARRIER_MCC:()=>fht,SEMATTRS_NET_HOST_CARRIER_MNC:()=>mht,SEMATTRS_NET_HOST_CARRIER_NAME:()=>dht,SEMATTRS_NET_HOST_CONNECTION_SUBTYPE:()=>pht,SEMATTRS_NET_HOST_CONNECTION_TYPE:()=>lht,SEMATTRS_NET_HOST_IP:()=>aht,SEMATTRS_NET_HOST_NAME:()=>uht,SEMATTRS_NET_HOST_PORT:()=>cht,SEMATTRS_NET_PEER_IP:()=>oht,SEMATTRS_NET_PEER_NAME:()=>sht,SEMATTRS_NET_PEER_PORT:()=>iht,SEMATTRS_NET_TRANSPORT:()=>nht,SEMATTRS_PEER_SERVICE:()=>ght,SEMATTRS_RPC_GRPC_STATUS_CODE:()=>w5,SEMATTRS_RPC_JSONRPC_ERROR_CODE:()=>wgt,SEMATTRS_RPC_JSONRPC_ERROR_MESSAGE:()=>Rgt,SEMATTRS_RPC_JSONRPC_REQUEST_ID:()=>Agt,SEMATTRS_RPC_JSONRPC_VERSION:()=>xgt,SEMATTRS_RPC_METHOD:()=>bgt,SEMATTRS_RPC_SERVICE:()=>A5,SEMATTRS_RPC_SYSTEM:()=>Tgt,SEMATTRS_THREAD_ID:()=>yht,SEMATTRS_THREAD_NAME:()=>Eht,SEMRESATTRS_AWS_ECS_CLUSTER_ARN:()=>ESt,SEMRESATTRS_AWS_ECS_CONTAINER_ARN:()=>ySt,SEMRESATTRS_AWS_ECS_LAUNCHTYPE:()=>TSt,SEMRESATTRS_AWS_ECS_TASK_ARN:()=>bSt,SEMRESATTRS_AWS_ECS_TASK_FAMILY:()=>xSt,SEMRESATTRS_AWS_ECS_TASK_REVISION:()=>ASt,SEMRESATTRS_AWS_EKS_CLUSTER_ARN:()=>wSt,SEMRESATTRS_AWS_LOG_GROUP_ARNS:()=>PSt,SEMRESATTRS_AWS_LOG_GROUP_NAMES:()=>RSt,SEMRESATTRS_AWS_LOG_STREAM_ARNS:()=>OSt,SEMRESATTRS_AWS_LOG_STREAM_NAMES:()=>ISt,SEMRESATTRS_CLOUD_ACCOUNT_ID:()=>gSt,SEMRESATTRS_CLOUD_AVAILABILITY_ZONE:()=>vSt,SEMRESATTRS_CLOUD_PLATFORM:()=>SSt,SEMRESATTRS_CLOUD_PROVIDER:()=>hSt,SEMRESATTRS_CLOUD_REGION:()=>_St,SEMRESATTRS_CONTAINER_ID:()=>CSt,SEMRESATTRS_CONTAINER_IMAGE_NAME:()=>kSt,SEMRESATTRS_CONTAINER_IMAGE_TAG:()=>MSt,SEMRESATTRS_CONTAINER_NAME:()=>NSt,SEMRESATTRS_CONTAINER_RUNTIME:()=>$St,SEMRESATTRS_DEPLOYMENT_ENVIRONMENT:()=>DSt,SEMRESATTRS_DEVICE_ID:()=>LSt,SEMRESATTRS_DEVICE_MODEL_IDENTIFIER:()=>USt,SEMRESATTRS_DEVICE_MODEL_NAME:()=>jSt,SEMRESATTRS_FAAS_ID:()=>FSt,SEMRESATTRS_FAAS_INSTANCE:()=>BSt,SEMRESATTRS_FAAS_MAX_MEMORY:()=>GSt,SEMRESATTRS_FAAS_NAME:()=>zSt,SEMRESATTRS_FAAS_VERSION:()=>qSt,SEMRESATTRS_HOST_ARCH:()=>KSt,SEMRESATTRS_HOST_ID:()=>VSt,SEMRESATTRS_HOST_IMAGE_ID:()=>YSt,SEMRESATTRS_HOST_IMAGE_NAME:()=>ZSt,SEMRESATTRS_HOST_IMAGE_VERSION:()=>JSt,SEMRESATTRS_HOST_NAME:()=>HSt,SEMRESATTRS_HOST_TYPE:()=>WSt,SEMRESATTRS_K8S_CLUSTER_NAME:()=>XSt,SEMRESATTRS_K8S_CONTAINER_NAME:()=>oyt,SEMRESATTRS_K8S_CRONJOB_NAME:()=>gyt,SEMRESATTRS_K8S_CRONJOB_UID:()=>hyt,SEMRESATTRS_K8S_DAEMONSET_NAME:()=>dyt,SEMRESATTRS_K8S_DAEMONSET_UID:()=>pyt,SEMRESATTRS_K8S_DEPLOYMENT_NAME:()=>cyt,SEMRESATTRS_K8S_DEPLOYMENT_UID:()=>ayt,SEMRESATTRS_K8S_JOB_NAME:()=>myt,SEMRESATTRS_K8S_JOB_UID:()=>fyt,SEMRESATTRS_K8S_NAMESPACE_NAME:()=>tyt,SEMRESATTRS_K8S_NODE_NAME:()=>QSt,SEMRESATTRS_K8S_NODE_UID:()=>eyt,SEMRESATTRS_K8S_POD_NAME:()=>nyt,SEMRESATTRS_K8S_POD_UID:()=>ryt,SEMRESATTRS_K8S_REPLICASET_NAME:()=>syt,SEMRESATTRS_K8S_REPLICASET_UID:()=>iyt,SEMRESATTRS_K8S_STATEFULSET_NAME:()=>lyt,SEMRESATTRS_K8S_STATEFULSET_UID:()=>uyt,SEMRESATTRS_OS_DESCRIPTION:()=>vyt,SEMRESATTRS_OS_NAME:()=>Syt,SEMRESATTRS_OS_TYPE:()=>_yt,SEMRESATTRS_OS_VERSION:()=>yyt,SEMRESATTRS_PROCESS_COMMAND:()=>xyt,SEMRESATTRS_PROCESS_COMMAND_ARGS:()=>wyt,SEMRESATTRS_PROCESS_COMMAND_LINE:()=>Ayt,SEMRESATTRS_PROCESS_EXECUTABLE_NAME:()=>Tyt,SEMRESATTRS_PROCESS_EXECUTABLE_PATH:()=>byt,SEMRESATTRS_PROCESS_OWNER:()=>Ryt,SEMRESATTRS_PROCESS_PID:()=>Eyt,SEMRESATTRS_PROCESS_RUNTIME_DESCRIPTION:()=>Oyt,SEMRESATTRS_PROCESS_RUNTIME_NAME:()=>Pyt,SEMRESATTRS_PROCESS_RUNTIME_VERSION:()=>Iyt,SEMRESATTRS_SERVICE_INSTANCE_ID:()=>Cyt,SEMRESATTRS_SERVICE_NAME:()=>Nyt,SEMRESATTRS_SERVICE_NAMESPACE:()=>R5,SEMRESATTRS_SERVICE_VERSION:()=>$yt,SEMRESATTRS_TELEMETRY_AUTO_VERSION:()=>Lyt,SEMRESATTRS_TELEMETRY_SDK_LANGUAGE:()=>Myt,SEMRESATTRS_TELEMETRY_SDK_NAME:()=>kyt,SEMRESATTRS_TELEMETRY_SDK_VERSION:()=>Dyt,SEMRESATTRS_WEBENGINE_DESCRIPTION:()=>zyt,SEMRESATTRS_WEBENGINE_NAME:()=>Uyt,SEMRESATTRS_WEBENGINE_VERSION:()=>jyt,SIGNALR_CONNECTION_STATUS_VALUE_APP_SHUTDOWN:()=>Ebt,SIGNALR_CONNECTION_STATUS_VALUE_NORMAL_CLOSURE:()=>Tbt,SIGNALR_CONNECTION_STATUS_VALUE_TIMEOUT:()=>bbt,SIGNALR_TRANSPORT_VALUE_LONG_POLLING:()=>Abt,SIGNALR_TRANSPORT_VALUE_SERVER_SENT_EVENTS:()=>wbt,SIGNALR_TRANSPORT_VALUE_WEB_SOCKETS:()=>Rbt,SemanticAttributes:()=>Cgt,SemanticResourceAttributes:()=>Fyt,TELEMETRYSDKLANGUAGEVALUES_CPP:()=>kEt,TELEMETRYSDKLANGUAGEVALUES_DOTNET:()=>MEt,TELEMETRYSDKLANGUAGEVALUES_ERLANG:()=>DEt,TELEMETRYSDKLANGUAGEVALUES_GO:()=>LEt,TELEMETRYSDKLANGUAGEVALUES_JAVA:()=>UEt,TELEMETRYSDKLANGUAGEVALUES_NODEJS:()=>jEt,TELEMETRYSDKLANGUAGEVALUES_PHP:()=>zEt,TELEMETRYSDKLANGUAGEVALUES_PYTHON:()=>FEt,TELEMETRYSDKLANGUAGEVALUES_RUBY:()=>qEt,TELEMETRYSDKLANGUAGEVALUES_WEBJS:()=>BEt,TELEMETRY_SDK_LANGUAGE_VALUE_CPP:()=>Cmr,TELEMETRY_SDK_LANGUAGE_VALUE_DOTNET:()=>Ibt,TELEMETRY_SDK_LANGUAGE_VALUE_ERLANG:()=>Obt,TELEMETRY_SDK_LANGUAGE_VALUE_GO:()=>$mr,TELEMETRY_SDK_LANGUAGE_VALUE_JAVA:()=>Nbt,TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS:()=>Cbt,TELEMETRY_SDK_LANGUAGE_VALUE_PHP:()=>kmr,TELEMETRY_SDK_LANGUAGE_VALUE_PYTHON:()=>$bt,TELEMETRY_SDK_LANGUAGE_VALUE_RUBY:()=>kbt,TELEMETRY_SDK_LANGUAGE_VALUE_RUST:()=>Mbt,TELEMETRY_SDK_LANGUAGE_VALUE_SWIFT:()=>Dbt,TELEMETRY_SDK_LANGUAGE_VALUE_WEBJS:()=>Lbt,TelemetrySdkLanguageValues:()=>GEt});var er=O(()=>{Xye();xbe();Abe();wbe()});var Rbe=S(KN=>{"use strict";Object.defineProperty(KN,"__esModule",{value:!0});KN.ATTR_PROCESS_RUNTIME_NAME=void 0;KN.ATTR_PROCESS_RUNTIME_NAME="process.runtime.name"});var Pbe=S(YN=>{"use strict";Object.defineProperty(YN,"__esModule",{value:!0});YN.SDK_INFO=void 0;var qxt=age(),ZN=(er(),se(Cr)),Bxt=Rbe();YN.SDK_INFO={[ZN.ATTR_TELEMETRY_SDK_NAME]:"opentelemetry",[Bxt.ATTR_PROCESS_RUNTIME_NAME]:"node",[ZN.ATTR_TELEMETRY_SDK_LANGUAGE]:ZN.TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,[ZN.ATTR_TELEMETRY_SDK_VERSION]:qxt.VERSION}});var Ibe=S(JN=>{"use strict";Object.defineProperty(JN,"__esModule",{value:!0});JN.unrefTimer=void 0;function Gxt(t){t.unref()}JN.unrefTimer=Gxt});var Obe=S(so=>{"use strict";Object.defineProperty(so,"__esModule",{value:!0});so.unrefTimer=so.SDK_INFO=so.otperformance=so._globalThis=so.getStringListFromEnv=so.getNumberFromEnv=so.getBooleanFromEnv=so.getStringFromEnv=void 0;var XN=oge();Object.defineProperty(so,"getStringFromEnv",{enumerable:!0,get:function(){return XN.getStringFromEnv}});Object.defineProperty(so,"getBooleanFromEnv",{enumerable:!0,get:function(){return XN.getBooleanFromEnv}});Object.defineProperty(so,"getNumberFromEnv",{enumerable:!0,get:function(){return XN.getNumberFromEnv}});Object.defineProperty(so,"getStringListFromEnv",{enumerable:!0,get:function(){return XN.getStringListFromEnv}});var Vxt=ige();Object.defineProperty(so,"_globalThis",{enumerable:!0,get:function(){return Vxt._globalThis}});var Hxt=sge();Object.defineProperty(so,"otperformance",{enumerable:!0,get:function(){return Hxt.otperformance}});var Wxt=Pbe();Object.defineProperty(so,"SDK_INFO",{enumerable:!0,get:function(){return Wxt.SDK_INFO}});var Kxt=Ibe();Object.defineProperty(so,"unrefTimer",{enumerable:!0,get:function(){return Kxt.unrefTimer}})});var $5=S(ao=>{"use strict";Object.defineProperty(ao,"__esModule",{value:!0});ao.getStringListFromEnv=ao.getNumberFromEnv=ao.getStringFromEnv=ao.getBooleanFromEnv=ao.unrefTimer=ao.otperformance=ao._globalThis=ao.SDK_INFO=void 0;var Yp=Obe();Object.defineProperty(ao,"SDK_INFO",{enumerable:!0,get:function(){return Yp.SDK_INFO}});Object.defineProperty(ao,"_globalThis",{enumerable:!0,get:function(){return Yp._globalThis}});Object.defineProperty(ao,"otperformance",{enumerable:!0,get:function(){return Yp.otperformance}});Object.defineProperty(ao,"unrefTimer",{enumerable:!0,get:function(){return Yp.unrefTimer}});Object.defineProperty(ao,"getBooleanFromEnv",{enumerable:!0,get:function(){return Yp.getBooleanFromEnv}});Object.defineProperty(ao,"getStringFromEnv",{enumerable:!0,get:function(){return Yp.getStringFromEnv}});Object.defineProperty(ao,"getNumberFromEnv",{enumerable:!0,get:function(){return Yp.getNumberFromEnv}});Object.defineProperty(ao,"getStringListFromEnv",{enumerable:!0,get:function(){return Yp.getStringListFromEnv}})});var kbe=S(Br=>{"use strict";Object.defineProperty(Br,"__esModule",{value:!0});Br.addHrTimes=Br.isTimeInput=Br.isTimeInputHrTime=Br.hrTimeToMicroseconds=Br.hrTimeToMilliseconds=Br.hrTimeToNanoseconds=Br.hrTimeToTimeStamp=Br.hrTimeDuration=Br.timeInputToHrTime=Br.hrTime=Br.getTimeOrigin=Br.millisToHrTime=void 0;var k5=$5(),Nbe=9,Zxt=6,Yxt=Math.pow(10,Zxt),QN=Math.pow(10,Nbe);function Rb(t){let e=t/1e3,r=Math.trunc(e),n=Math.round(t%1e3*Yxt);return[r,n]}Br.millisToHrTime=Rb;function M5(){let t=k5.otperformance.timeOrigin;if(typeof t!="number"){let e=k5.otperformance;t=e.timing&&e.timing.fetchStart}return t}Br.getTimeOrigin=M5;function Cbe(t){let e=Rb(M5()),r=Rb(typeof t=="number"?t:k5.otperformance.now());return $be(e,r)}Br.hrTime=Cbe;function Jxt(t){if(D5(t))return t;if(typeof t=="number")return t=QN&&(r[1]-=QN,r[0]+=1),r}Br.addHrTimes=$be});var Mbe=S(Pb=>{"use strict";Object.defineProperty(Pb,"__esModule",{value:!0});Pb.ExportResultCode=void 0;var o0t;(function(t){t[t.SUCCESS=0]="SUCCESS",t[t.FAILED=1]="FAILED"})(o0t=Pb.ExportResultCode||(Pb.ExportResultCode={}))});var Lbe=S(eC=>{"use strict";Object.defineProperty(eC,"__esModule",{value:!0});eC.CompositePropagator=void 0;var Dbe=(pe(),se(Pe)),L5=class{_propagators;_fields;constructor(e={}){this._propagators=e.propagators??[],this._fields=Array.from(new Set(this._propagators.map(r=>typeof r.fields=="function"?r.fields():[]).reduce((r,n)=>r.concat(n),[])))}inject(e,r,n){for(let o of this._propagators)try{o.inject(e,r,n)}catch(i){Dbe.diag.warn(`Failed to inject with ${o.constructor.name}. Err: ${i.message}`)}}extract(e,r,n){return this._propagators.reduce((o,i)=>{try{return i.extract(o,r,n)}catch(s){Dbe.diag.warn(`Failed to extract with ${i.constructor.name}. Err: ${s.message}`)}return o},e)}fields(){return this._fields.slice()}};eC.CompositePropagator=L5});var Ube=S(r_=>{"use strict";Object.defineProperty(r_,"__esModule",{value:!0});r_.validateValue=r_.validateKey=void 0;var U5="[_0-9a-z-*/]",i0t=`[a-z]${U5}{0,255}`,s0t=`[a-z0-9]${U5}{0,240}@[a-z]${U5}{0,13}`,a0t=new RegExp(`^(?:${i0t}|${s0t})$`),c0t=/^[ -~]{0,255}[!-~]$/,u0t=/,|=/;function l0t(t){return a0t.test(t)}r_.validateKey=l0t;function p0t(t){return c0t.test(t)&&!u0t.test(t)}r_.validateValue=p0t});var z5=S(tC=>{"use strict";Object.defineProperty(tC,"__esModule",{value:!0});tC.TraceState=void 0;var jbe=Ube(),zbe=32,d0t=512,Fbe=",",qbe="=",j5=class t{_internalState=new Map;constructor(e){e&&this._parse(e)}set(e,r){let n=this._clone();return n._internalState.has(e)&&n._internalState.delete(e),n._internalState.set(e,r),n}unset(e){let r=this._clone();return r._internalState.delete(e),r}get(e){return this._internalState.get(e)}serialize(){return this._keys().reduce((e,r)=>(e.push(r+qbe+this.get(r)),e),[]).join(Fbe)}_parse(e){e.length>d0t||(this._internalState=e.split(Fbe).reverse().reduce((r,n)=>{let o=n.trim(),i=o.indexOf(qbe);if(i!==-1){let s=o.slice(0,i),a=o.slice(i+1,n.length);(0,jbe.validateKey)(s)&&(0,jbe.validateValue)(a)&&r.set(s,a)}return r},new Map),this._internalState.size>zbe&&(this._internalState=new Map(Array.from(this._internalState.entries()).reverse().slice(0,zbe))))}_keys(){return Array.from(this._internalState.keys()).reverse()}_clone(){let e=new t;return e._internalState=new Map(this._internalState),e}};tC.TraceState=j5});var Gbe=S(ei=>{"use strict";Object.defineProperty(ei,"__esModule",{value:!0});ei.W3CTraceContextPropagator=ei.parseTraceParent=ei.TRACE_STATE_HEADER=ei.TRACE_PARENT_HEADER=void 0;var rC=(pe(),se(Pe)),f0t=yb(),m0t=z5();ei.TRACE_PARENT_HEADER="traceparent";ei.TRACE_STATE_HEADER="tracestate";var h0t="00",g0t="(?!ff)[\\da-f]{2}",_0t="(?![0]{32})[\\da-f]{32}",v0t="(?![0]{16})[\\da-f]{16}",S0t="[\\da-f]{2}",y0t=new RegExp(`^\\s?(${g0t})-(${_0t})-(${v0t})-(${S0t})(-.*)?\\s?$`);function Bbe(t){let e=y0t.exec(t);return!e||e[1]==="00"&&e[5]?null:{traceId:e[2],spanId:e[3],traceFlags:parseInt(e[4],16)}}ei.parseTraceParent=Bbe;var F5=class{inject(e,r,n){let o=rC.trace.getSpanContext(e);if(!o||(0,f0t.isTracingSuppressed)(e)||!(0,rC.isSpanContextValid)(o))return;let i=`${h0t}-${o.traceId}-${o.spanId}-0${Number(o.traceFlags||rC.TraceFlags.NONE).toString(16)}`;n.set(r,ei.TRACE_PARENT_HEADER,i),o.traceState&&n.set(r,ei.TRACE_STATE_HEADER,o.traceState.serialize())}extract(e,r,n){let o=n.get(r,ei.TRACE_PARENT_HEADER);if(!o)return e;let i=Array.isArray(o)?o[0]:o;if(typeof i!="string")return e;let s=Bbe(i);if(!s)return e;s.isRemote=!0;let a=n.get(r,ei.TRACE_STATE_HEADER);if(a){let c=Array.isArray(a)?a.join(","):a;s.traceState=new m0t.TraceState(typeof c=="string"?c:void 0)}return rC.trace.setSpanContext(e,s)}fields(){return[ei.TRACE_PARENT_HEADER,ei.TRACE_STATE_HEADER]}};ei.W3CTraceContextPropagator=F5});var Vbe=S(dc=>{"use strict";Object.defineProperty(dc,"__esModule",{value:!0});dc.getRPCMetadata=dc.deleteRPCMetadata=dc.setRPCMetadata=dc.RPCType=void 0;var E0t=(pe(),se(Pe)),q5=(0,E0t.createContextKey)("OpenTelemetry SDK Context Key RPC_METADATA"),T0t;(function(t){t.HTTP="http"})(T0t=dc.RPCType||(dc.RPCType={}));function b0t(t,e){return t.setValue(q5,e)}dc.setRPCMetadata=b0t;function x0t(t){return t.deleteValue(q5)}dc.deleteRPCMetadata=x0t;function A0t(t){return t.getValue(q5)}dc.getRPCMetadata=A0t});var Ybe=S(nC=>{"use strict";Object.defineProperty(nC,"__esModule",{value:!0});nC.isPlainObject=void 0;var w0t="[object Object]",R0t="[object Null]",P0t="[object Undefined]",I0t=Function.prototype,Hbe=I0t.toString,O0t=Hbe.call(Object),N0t=Object.getPrototypeOf,Wbe=Object.prototype,Kbe=Wbe.hasOwnProperty,im=Symbol?Symbol.toStringTag:void 0,Zbe=Wbe.toString;function C0t(t){if(!$0t(t)||k0t(t)!==w0t)return!1;let e=N0t(t);if(e===null)return!0;let r=Kbe.call(e,"constructor")&&e.constructor;return typeof r=="function"&&r instanceof r&&Hbe.call(r)===O0t}nC.isPlainObject=C0t;function $0t(t){return t!=null&&typeof t=="object"}function k0t(t){return t==null?t===void 0?P0t:R0t:im&&im in Object(t)?M0t(t):D0t(t)}function M0t(t){let e=Kbe.call(t,im),r=t[im],n=!1;try{t[im]=void 0,n=!0}catch{}let o=Zbe.call(t);return n&&(e?t[im]=r:delete t[im]),o}function D0t(t){return Zbe.call(t)}});var txe=S(sC=>{"use strict";Object.defineProperty(sC,"__esModule",{value:!0});sC.merge=void 0;var Jbe=Ybe(),L0t=20;function U0t(...t){let e=t.shift(),r=new WeakMap;for(;t.length>0;)e=Qbe(e,t.shift(),0,r);return e}sC.merge=U0t;function B5(t){return iC(t)?t.slice():t}function Qbe(t,e,r=0,n){let o;if(!(r>L0t)){if(r++,oC(t)||oC(e)||exe(e))o=B5(e);else if(iC(t)){if(o=t.slice(),iC(e))for(let i=0,s=e.length;i"u"?delete o[c]:o[c]=u;else{let p=o[c],f=u;if(Xbe(t,c,n)||Xbe(e,c,n))delete o[c];else{if(Ib(p)&&Ib(f)){let m=n.get(p)||[],h=n.get(f)||[];m.push({obj:t,key:c}),h.push({obj:e,key:c}),n.set(p,m),n.set(f,h)}o[c]=Qbe(o[c],u,r,n)}}}}else o=e;return o}}function Xbe(t,e,r){let n=r.get(t[e])||[];for(let o=0,i=n.length;o"u"||t instanceof Date||t instanceof RegExp||t===null}function j0t(t,e){return!(!(0,Jbe.isPlainObject)(t)||!(0,Jbe.isPlainObject)(e))}});var rxe=S(n_=>{"use strict";Object.defineProperty(n_,"__esModule",{value:!0});n_.callWithTimeout=n_.TimeoutError=void 0;var aC=class t extends Error{constructor(e){super(e),Object.setPrototypeOf(this,t.prototype)}};n_.TimeoutError=aC;function z0t(t,e){let r,n=new Promise(function(i,s){r=setTimeout(function(){s(new aC("Operation timed out."))},e)});return Promise.race([t,n]).then(o=>(clearTimeout(r),o),o=>{throw clearTimeout(r),o})}n_.callWithTimeout=z0t});var oxe=S(o_=>{"use strict";Object.defineProperty(o_,"__esModule",{value:!0});o_.isUrlIgnored=o_.urlMatches=void 0;function nxe(t,e){return typeof e=="string"?t===e:!!t.match(e)}o_.urlMatches=nxe;function F0t(t,e){if(!e)return!1;for(let r of e)if(nxe(t,r))return!0;return!1}o_.isUrlIgnored=F0t});var ixe=S(cC=>{"use strict";Object.defineProperty(cC,"__esModule",{value:!0});cC.Deferred=void 0;var G5=class{_promise;_resolve;_reject;constructor(){this._promise=new Promise((e,r)=>{this._resolve=e,this._reject=r})}get promise(){return this._promise}resolve(e){this._resolve(e)}reject(e){this._reject(e)}};cC.Deferred=G5});var sxe=S(uC=>{"use strict";Object.defineProperty(uC,"__esModule",{value:!0});uC.BindOnceFuture=void 0;var q0t=ixe(),V5=class{_callback;_that;_isCalled=!1;_deferred=new q0t.Deferred;constructor(e,r){this._callback=e,this._that=r}get isCalled(){return this._isCalled}get promise(){return this._deferred.promise}call(...e){if(!this._isCalled){this._isCalled=!0;try{Promise.resolve(this._callback.call(this._that,...e)).then(r=>this._deferred.resolve(r),r=>this._deferred.reject(r))}catch(r){this._deferred.reject(r)}}return this._deferred.promise}};uC.BindOnceFuture=V5});var cxe=S(lC=>{"use strict";Object.defineProperty(lC,"__esModule",{value:!0});lC.diagLogLevelFromString=void 0;var hl=(pe(),se(Pe)),axe={ALL:hl.DiagLogLevel.ALL,VERBOSE:hl.DiagLogLevel.VERBOSE,DEBUG:hl.DiagLogLevel.DEBUG,INFO:hl.DiagLogLevel.INFO,WARN:hl.DiagLogLevel.WARN,ERROR:hl.DiagLogLevel.ERROR,NONE:hl.DiagLogLevel.NONE};function B0t(t){if(t==null)return;let e=axe[t.toUpperCase()];return e??(hl.diag.warn(`Unknown log level "${t}", expected one of ${Object.keys(axe)}, using default`),hl.DiagLogLevel.INFO)}lC.diagLogLevelFromString=B0t});var lxe=S(pC=>{"use strict";Object.defineProperty(pC,"__esModule",{value:!0});pC._export=void 0;var uxe=(pe(),se(Pe)),G0t=yb();function V0t(t,e){return new Promise(r=>{uxe.context.with((0,G0t.suppressTracing)(uxe.context.active()),()=>{t.export(e,n=>{r(n)})})})}pC._export=V0t});var _r=S(ee=>{"use strict";Object.defineProperty(ee,"__esModule",{value:!0});ee.internal=ee.diagLogLevelFromString=ee.BindOnceFuture=ee.urlMatches=ee.isUrlIgnored=ee.callWithTimeout=ee.TimeoutError=ee.merge=ee.TraceState=ee.unsuppressTracing=ee.suppressTracing=ee.isTracingSuppressed=ee.setRPCMetadata=ee.getRPCMetadata=ee.deleteRPCMetadata=ee.RPCType=ee.parseTraceParent=ee.W3CTraceContextPropagator=ee.TRACE_STATE_HEADER=ee.TRACE_PARENT_HEADER=ee.CompositePropagator=ee.unrefTimer=ee.otperformance=ee.getStringListFromEnv=ee.getNumberFromEnv=ee.getBooleanFromEnv=ee.getStringFromEnv=ee._globalThis=ee.SDK_INFO=ee.parseKeyPairsIntoRecord=ee.ExportResultCode=ee.timeInputToHrTime=ee.millisToHrTime=ee.isTimeInputHrTime=ee.isTimeInput=ee.hrTimeToTimeStamp=ee.hrTimeToNanoseconds=ee.hrTimeToMilliseconds=ee.hrTimeToMicroseconds=ee.hrTimeDuration=ee.hrTime=ee.getTimeOrigin=ee.addHrTimes=ee.loggingErrorHandler=ee.setGlobalErrorHandler=ee.globalErrorHandler=ee.sanitizeAttributes=ee.isAttributeValue=ee.AnchoredClock=ee.W3CBaggagePropagator=void 0;var H0t=Hhe();Object.defineProperty(ee,"W3CBaggagePropagator",{enumerable:!0,get:function(){return H0t.W3CBaggagePropagator}});var W0t=Whe();Object.defineProperty(ee,"AnchoredClock",{enumerable:!0,get:function(){return W0t.AnchoredClock}});var pxe=Xhe();Object.defineProperty(ee,"isAttributeValue",{enumerable:!0,get:function(){return pxe.isAttributeValue}});Object.defineProperty(ee,"sanitizeAttributes",{enumerable:!0,get:function(){return pxe.sanitizeAttributes}});var dxe=ege();Object.defineProperty(ee,"globalErrorHandler",{enumerable:!0,get:function(){return dxe.globalErrorHandler}});Object.defineProperty(ee,"setGlobalErrorHandler",{enumerable:!0,get:function(){return dxe.setGlobalErrorHandler}});var K0t=_5();Object.defineProperty(ee,"loggingErrorHandler",{enumerable:!0,get:function(){return K0t.loggingErrorHandler}});var la=kbe();Object.defineProperty(ee,"addHrTimes",{enumerable:!0,get:function(){return la.addHrTimes}});Object.defineProperty(ee,"getTimeOrigin",{enumerable:!0,get:function(){return la.getTimeOrigin}});Object.defineProperty(ee,"hrTime",{enumerable:!0,get:function(){return la.hrTime}});Object.defineProperty(ee,"hrTimeDuration",{enumerable:!0,get:function(){return la.hrTimeDuration}});Object.defineProperty(ee,"hrTimeToMicroseconds",{enumerable:!0,get:function(){return la.hrTimeToMicroseconds}});Object.defineProperty(ee,"hrTimeToMilliseconds",{enumerable:!0,get:function(){return la.hrTimeToMilliseconds}});Object.defineProperty(ee,"hrTimeToNanoseconds",{enumerable:!0,get:function(){return la.hrTimeToNanoseconds}});Object.defineProperty(ee,"hrTimeToTimeStamp",{enumerable:!0,get:function(){return la.hrTimeToTimeStamp}});Object.defineProperty(ee,"isTimeInput",{enumerable:!0,get:function(){return la.isTimeInput}});Object.defineProperty(ee,"isTimeInputHrTime",{enumerable:!0,get:function(){return la.isTimeInputHrTime}});Object.defineProperty(ee,"millisToHrTime",{enumerable:!0,get:function(){return la.millisToHrTime}});Object.defineProperty(ee,"timeInputToHrTime",{enumerable:!0,get:function(){return la.timeInputToHrTime}});var Z0t=Mbe();Object.defineProperty(ee,"ExportResultCode",{enumerable:!0,get:function(){return Z0t.ExportResultCode}});var Y0t=d5();Object.defineProperty(ee,"parseKeyPairsIntoRecord",{enumerable:!0,get:function(){return Y0t.parseKeyPairsIntoRecord}});var Jp=$5();Object.defineProperty(ee,"SDK_INFO",{enumerable:!0,get:function(){return Jp.SDK_INFO}});Object.defineProperty(ee,"_globalThis",{enumerable:!0,get:function(){return Jp._globalThis}});Object.defineProperty(ee,"getStringFromEnv",{enumerable:!0,get:function(){return Jp.getStringFromEnv}});Object.defineProperty(ee,"getBooleanFromEnv",{enumerable:!0,get:function(){return Jp.getBooleanFromEnv}});Object.defineProperty(ee,"getNumberFromEnv",{enumerable:!0,get:function(){return Jp.getNumberFromEnv}});Object.defineProperty(ee,"getStringListFromEnv",{enumerable:!0,get:function(){return Jp.getStringListFromEnv}});Object.defineProperty(ee,"otperformance",{enumerable:!0,get:function(){return Jp.otperformance}});Object.defineProperty(ee,"unrefTimer",{enumerable:!0,get:function(){return Jp.unrefTimer}});var J0t=Lbe();Object.defineProperty(ee,"CompositePropagator",{enumerable:!0,get:function(){return J0t.CompositePropagator}});var dC=Gbe();Object.defineProperty(ee,"TRACE_PARENT_HEADER",{enumerable:!0,get:function(){return dC.TRACE_PARENT_HEADER}});Object.defineProperty(ee,"TRACE_STATE_HEADER",{enumerable:!0,get:function(){return dC.TRACE_STATE_HEADER}});Object.defineProperty(ee,"W3CTraceContextPropagator",{enumerable:!0,get:function(){return dC.W3CTraceContextPropagator}});Object.defineProperty(ee,"parseTraceParent",{enumerable:!0,get:function(){return dC.parseTraceParent}});var fC=Vbe();Object.defineProperty(ee,"RPCType",{enumerable:!0,get:function(){return fC.RPCType}});Object.defineProperty(ee,"deleteRPCMetadata",{enumerable:!0,get:function(){return fC.deleteRPCMetadata}});Object.defineProperty(ee,"getRPCMetadata",{enumerable:!0,get:function(){return fC.getRPCMetadata}});Object.defineProperty(ee,"setRPCMetadata",{enumerable:!0,get:function(){return fC.setRPCMetadata}});var H5=yb();Object.defineProperty(ee,"isTracingSuppressed",{enumerable:!0,get:function(){return H5.isTracingSuppressed}});Object.defineProperty(ee,"suppressTracing",{enumerable:!0,get:function(){return H5.suppressTracing}});Object.defineProperty(ee,"unsuppressTracing",{enumerable:!0,get:function(){return H5.unsuppressTracing}});var X0t=z5();Object.defineProperty(ee,"TraceState",{enumerable:!0,get:function(){return X0t.TraceState}});var Q0t=txe();Object.defineProperty(ee,"merge",{enumerable:!0,get:function(){return Q0t.merge}});var fxe=rxe();Object.defineProperty(ee,"TimeoutError",{enumerable:!0,get:function(){return fxe.TimeoutError}});Object.defineProperty(ee,"callWithTimeout",{enumerable:!0,get:function(){return fxe.callWithTimeout}});var mxe=oxe();Object.defineProperty(ee,"isUrlIgnored",{enumerable:!0,get:function(){return mxe.isUrlIgnored}});Object.defineProperty(ee,"urlMatches",{enumerable:!0,get:function(){return mxe.urlMatches}});var eAt=sxe();Object.defineProperty(ee,"BindOnceFuture",{enumerable:!0,get:function(){return eAt.BindOnceFuture}});var tAt=cxe();Object.defineProperty(ee,"diagLogLevelFromString",{enumerable:!0,get:function(){return tAt.diagLogLevelFromString}});var rAt=lxe();ee.internal={_export:rAt._export}});var hxe=S(mC=>{"use strict";Object.defineProperty(mC,"__esModule",{value:!0});mC.VERSION=void 0;mC.VERSION="0.203.0"});var gxe=S(Ob=>{"use strict";Object.defineProperty(Ob,"__esModule",{value:!0});Ob.SeverityNumber=void 0;var nAt;(function(t){t[t.UNSPECIFIED=0]="UNSPECIFIED",t[t.TRACE=1]="TRACE",t[t.TRACE2=2]="TRACE2",t[t.TRACE3=3]="TRACE3",t[t.TRACE4=4]="TRACE4",t[t.DEBUG=5]="DEBUG",t[t.DEBUG2=6]="DEBUG2",t[t.DEBUG3=7]="DEBUG3",t[t.DEBUG4=8]="DEBUG4",t[t.INFO=9]="INFO",t[t.INFO2=10]="INFO2",t[t.INFO3=11]="INFO3",t[t.INFO4=12]="INFO4",t[t.WARN=13]="WARN",t[t.WARN2=14]="WARN2",t[t.WARN3=15]="WARN3",t[t.WARN4=16]="WARN4",t[t.ERROR=17]="ERROR",t[t.ERROR2=18]="ERROR2",t[t.ERROR3=19]="ERROR3",t[t.ERROR4=20]="ERROR4",t[t.FATAL=21]="FATAL",t[t.FATAL2=22]="FATAL2",t[t.FATAL3=23]="FATAL3",t[t.FATAL4=24]="FATAL4"})(nAt=Ob.SeverityNumber||(Ob.SeverityNumber={}))});var gC=S(i_=>{"use strict";Object.defineProperty(i_,"__esModule",{value:!0});i_.NOOP_LOGGER=i_.NoopLogger=void 0;var hC=class{emit(e){}};i_.NoopLogger=hC;i_.NOOP_LOGGER=new hC});var vC=S(s_=>{"use strict";Object.defineProperty(s_,"__esModule",{value:!0});s_.NOOP_LOGGER_PROVIDER=s_.NoopLoggerProvider=void 0;var oAt=gC(),_C=class{getLogger(e,r,n){return new oAt.NoopLogger}};s_.NoopLoggerProvider=_C;s_.NOOP_LOGGER_PROVIDER=new _C});var K5=S(SC=>{"use strict";Object.defineProperty(SC,"__esModule",{value:!0});SC.ProxyLogger=void 0;var iAt=gC(),W5=class{constructor(e,r,n,o){this._provider=e,this.name=r,this.version=n,this.options=o}emit(e){this._getLogger().emit(e)}_getLogger(){if(this._delegate)return this._delegate;let e=this._provider.getDelegateLogger(this.name,this.version,this.options);return e?(this._delegate=e,this._delegate):iAt.NOOP_LOGGER}};SC.ProxyLogger=W5});var Y5=S(yC=>{"use strict";Object.defineProperty(yC,"__esModule",{value:!0});yC.ProxyLoggerProvider=void 0;var sAt=vC(),aAt=K5(),Z5=class{getLogger(e,r,n){var o;return(o=this.getDelegateLogger(e,r,n))!==null&&o!==void 0?o:new aAt.ProxyLogger(this,e,r,n)}getDelegate(){var e;return(e=this._delegate)!==null&&e!==void 0?e:sAt.NOOP_LOGGER_PROVIDER}setDelegate(e){this._delegate=e}getDelegateLogger(e,r,n){var o;return(o=this._delegate)===null||o===void 0?void 0:o.getLogger(e,r,n)}};yC.ProxyLoggerProvider=Z5});var _xe=S(EC=>{"use strict";Object.defineProperty(EC,"__esModule",{value:!0});EC._globalThis=void 0;EC._globalThis=typeof globalThis=="object"?globalThis:global});var vxe=S(TC=>{"use strict";Object.defineProperty(TC,"__esModule",{value:!0});TC._globalThis=void 0;var cAt=_xe();Object.defineProperty(TC,"_globalThis",{enumerable:!0,get:function(){return cAt._globalThis}})});var Sxe=S(bC=>{"use strict";Object.defineProperty(bC,"__esModule",{value:!0});bC._globalThis=void 0;var uAt=vxe();Object.defineProperty(bC,"_globalThis",{enumerable:!0,get:function(){return uAt._globalThis}})});var yxe=S(eu=>{"use strict";Object.defineProperty(eu,"__esModule",{value:!0});eu.API_BACKWARDS_COMPATIBILITY_VERSION=eu.makeGetter=eu._global=eu.GLOBAL_LOGS_API_KEY=void 0;var lAt=Sxe();eu.GLOBAL_LOGS_API_KEY=Symbol.for("io.opentelemetry.js.api.logs");eu._global=lAt._globalThis;function pAt(t,e,r){return n=>n===t?e:r}eu.makeGetter=pAt;eu.API_BACKWARDS_COMPATIBILITY_VERSION=1});var Txe=S(xC=>{"use strict";Object.defineProperty(xC,"__esModule",{value:!0});xC.LogsAPI=void 0;var pa=yxe(),dAt=vC(),Exe=Y5(),J5=class t{constructor(){this._proxyLoggerProvider=new Exe.ProxyLoggerProvider}static getInstance(){return this._instance||(this._instance=new t),this._instance}setGlobalLoggerProvider(e){return pa._global[pa.GLOBAL_LOGS_API_KEY]?this.getLoggerProvider():(pa._global[pa.GLOBAL_LOGS_API_KEY]=(0,pa.makeGetter)(pa.API_BACKWARDS_COMPATIBILITY_VERSION,e,dAt.NOOP_LOGGER_PROVIDER),this._proxyLoggerProvider.setDelegate(e),e)}getLoggerProvider(){var e,r;return(r=(e=pa._global[pa.GLOBAL_LOGS_API_KEY])===null||e===void 0?void 0:e.call(pa._global,pa.API_BACKWARDS_COMPATIBILITY_VERSION))!==null&&r!==void 0?r:this._proxyLoggerProvider}getLogger(e,r,n){return this.getLoggerProvider().getLogger(e,r,n)}disable(){delete pa._global[pa.GLOBAL_LOGS_API_KEY],this._proxyLoggerProvider=new Exe.ProxyLoggerProvider}};xC.LogsAPI=J5});var X5=S(co=>{"use strict";Object.defineProperty(co,"__esModule",{value:!0});co.logs=co.ProxyLoggerProvider=co.ProxyLogger=co.NoopLoggerProvider=co.NOOP_LOGGER_PROVIDER=co.NoopLogger=co.NOOP_LOGGER=co.SeverityNumber=void 0;var fAt=gxe();Object.defineProperty(co,"SeverityNumber",{enumerable:!0,get:function(){return fAt.SeverityNumber}});var bxe=gC();Object.defineProperty(co,"NOOP_LOGGER",{enumerable:!0,get:function(){return bxe.NOOP_LOGGER}});Object.defineProperty(co,"NoopLogger",{enumerable:!0,get:function(){return bxe.NoopLogger}});var xxe=vC();Object.defineProperty(co,"NOOP_LOGGER_PROVIDER",{enumerable:!0,get:function(){return xxe.NOOP_LOGGER_PROVIDER}});Object.defineProperty(co,"NoopLoggerProvider",{enumerable:!0,get:function(){return xxe.NoopLoggerProvider}});var mAt=K5();Object.defineProperty(co,"ProxyLogger",{enumerable:!0,get:function(){return mAt.ProxyLogger}});var hAt=Y5();Object.defineProperty(co,"ProxyLoggerProvider",{enumerable:!0,get:function(){return hAt.ProxyLoggerProvider}});var gAt=Txe();co.logs=gAt.LogsAPI.getInstance()});var Axe=S(a_=>{"use strict";Object.defineProperty(a_,"__esModule",{value:!0});a_.disableInstrumentations=a_.enableInstrumentations=void 0;function _At(t,e,r,n){for(let o=0,i=t.length;oe.disable())}a_.disableInstrumentations=vAt});var Pxe=S(AC=>{"use strict";Object.defineProperty(AC,"__esModule",{value:!0});AC.registerInstrumentations=void 0;var wxe=(pe(),se(Pe)),SAt=X5(),Rxe=Axe();function yAt(t){let e=t.tracerProvider||wxe.trace.getTracerProvider(),r=t.meterProvider||wxe.metrics.getMeterProvider(),n=t.loggerProvider||SAt.logs.getLoggerProvider(),o=t.instrumentations?.flat()??[];return(0,Rxe.enableInstrumentations)(o,e,r,n),()=>{(0,Rxe.disableInstrumentations)(o)}}AC.registerInstrumentations=yAt});var Lxe=S(wC=>{"use strict";Object.defineProperty(wC,"__esModule",{value:!0});wC.satisfies=void 0;var r8=(pe(),se(Pe)),$xe=/^(?:v)?(?(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*))(?:-(?(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,EAt=/^(?<|>|=|==|<=|>=|~|\^|~>)?\s*(?:v)?(?(?x|X|\*|0|[1-9]\d*)(?:\.(?x|X|\*|0|[1-9]\d*))?(?:\.(?x|X|\*|0|[1-9]\d*))?)(?:-(?(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,TAt={">":[1],">=":[0,1],"=":[0],"<=":[-1,0],"<":[-1],"!=":[-1,1]};function bAt(t,e,r){if(!xAt(t))return r8.diag.error(`Invalid version: ${t}`),!1;if(!e)return!0;e=e.replace(/([<>=~^]+)\s+/g,"$1");let n=PAt(t);if(!n)return!1;let o=[],i=kxe(n,e,o,r);return i&&!r?.includePrerelease?wAt(n,o):i}wC.satisfies=bAt;function xAt(t){return typeof t=="string"&&$xe.test(t)}function kxe(t,e,r,n){if(e.includes("||")){let o=e.trim().split("||");for(let i of o)if(Q5(t,i,r,n))return!0;return!1}else if(e.includes(" - "))e=ZAt(e,n);else if(e.includes(" ")){let o=e.trim().replace(/\s{2,}/g," ").split(" ");for(let i of o)if(!Q5(t,i,r,n))return!1;return!0}return Q5(t,e,r,n)}function Q5(t,e,r,n){if(e=RAt(e,n),e.includes(" "))return kxe(t,e,r,n);{let o=IAt(e);return r.push(o),AAt(t,o)}}function AAt(t,e){if(e.invalid)return!1;if(!e.version||t8(e.version))return!0;let r=Oxe(t.versionSegments||[],e.versionSegments||[]);if(r===0){let n=t.prereleaseSegments||[],o=e.prereleaseSegments||[];!n.length&&!o.length?r=0:!n.length&&o.length?r=1:n.length&&!o.length?r=-1:r=Oxe(n,o)}return TAt[e.op]?.includes(r)}function wAt(t,e){return t.prerelease?e.some(r=>r.prerelease&&r.version===t.version):!0}function RAt(t,e){return t=t.trim(),t=WAt(t,e),t=HAt(t),t=KAt(t,e),t=t.trim(),t}function xi(t){return!t||t.toLowerCase()==="x"||t==="*"}function PAt(t){let e=t.match($xe);if(!e){r8.diag.error(`Invalid version: ${t}`);return}let r=e.groups.version,n=e.groups.prerelease,o=e.groups.build,i=r.split("."),s=n?.split(".");return{op:void 0,version:r,versionSegments:i,versionSegmentCount:i.length,prerelease:n,prereleaseSegments:s,prereleaseSegmentCount:s?s.length:0,build:o}}function IAt(t){if(!t)return{};let e=t.match(EAt);if(!e)return r8.diag.error(`Invalid range: ${t}`),{invalid:!0};let r=e.groups.op,n=e.groups.version,o=e.groups.prerelease,i=e.groups.build,s=n.split("."),a=o?.split(".");return r==="=="&&(r="="),{op:r||"=",version:n,versionSegments:s,versionSegmentCount:s.length,prerelease:o,prereleaseSegments:a,prereleaseSegmentCount:a?a.length:0,build:i}}function t8(t){return t==="*"||t==="x"||t==="X"}function Ixe(t){let e=parseInt(t,10);return isNaN(e)?t:e}function OAt(t,e){if(typeof t==typeof e){if(typeof t=="number")return[t,e];if(typeof t=="string")return[t,e];throw new Error("Version segments can only be strings or numbers")}else return[String(t),String(e)]}function NAt(t,e){if(t8(t)||t8(e))return 0;let[r,n]=OAt(Ixe(t),Ixe(e));return r>n?1:r)?=?)",Nxe=`(?:${Dxe}|${CAt})`,kAt=`(?:-(${Nxe}(?:\\.${Nxe})*))`,Cxe=`${Mxe}+`,MAt=`(?:\\+(${Cxe}(?:\\.${Cxe})*))`,e8=`${Dxe}|x|X|\\*`,Nb=`[v=\\s]*(${e8})(?:\\.(${e8})(?:\\.(${e8})(?:${kAt})?${MAt}?)?)?`,DAt=`^${$At}\\s*${Nb}$`,LAt=new RegExp(DAt),UAt=`^\\s*(${Nb})\\s+-\\s+(${Nb})\\s*$`,jAt=new RegExp(UAt),zAt="(?:~>?)",FAt=`^${zAt}${Nb}$`,qAt=new RegExp(FAt),BAt="(?:\\^)",GAt=`^${BAt}${Nb}$`,VAt=new RegExp(GAt);function HAt(t){let e=qAt;return t.replace(e,(r,n,o,i,s)=>{let a;return xi(n)?a="":xi(o)?a=`>=${n}.0.0 <${+n+1}.0.0-0`:xi(i)?a=`>=${n}.${o}.0 <${n}.${+o+1}.0-0`:s?a=`>=${n}.${o}.${i}-${s} <${n}.${+o+1}.0-0`:a=`>=${n}.${o}.${i} <${n}.${+o+1}.0-0`,a})}function WAt(t,e){let r=VAt,n=e?.includePrerelease?"-0":"";return t.replace(r,(o,i,s,a,c)=>{let u;return xi(i)?u="":xi(s)?u=`>=${i}.0.0${n} <${+i+1}.0.0-0`:xi(a)?i==="0"?u=`>=${i}.${s}.0${n} <${i}.${+s+1}.0-0`:u=`>=${i}.${s}.0${n} <${+i+1}.0.0-0`:c?i==="0"?s==="0"?u=`>=${i}.${s}.${a}-${c} <${i}.${s}.${+a+1}-0`:u=`>=${i}.${s}.${a}-${c} <${i}.${+s+1}.0-0`:u=`>=${i}.${s}.${a}-${c} <${+i+1}.0.0-0`:i==="0"?s==="0"?u=`>=${i}.${s}.${a}${n} <${i}.${s}.${+a+1}-0`:u=`>=${i}.${s}.${a}${n} <${i}.${+s+1}.0-0`:u=`>=${i}.${s}.${a} <${+i+1}.0.0-0`,u})}function KAt(t,e){let r=LAt;return t.replace(r,(n,o,i,s,a,c)=>{let u=xi(i),p=u||xi(s),f=p||xi(a),m=f;return o==="="&&m&&(o=""),c=e?.includePrerelease?"-0":"",u?o===">"||o==="<"?n="<0.0.0-0":n="*":o&&m?(p&&(s=0),a=0,o===">"?(o=">=",p?(i=+i+1,s=0,a=0):(s=+s+1,a=0)):o==="<="&&(o="<",p?i=+i+1:s=+s+1),o==="<"&&(c="-0"),n=`${o+i}.${s}.${a}${c}`):p?n=`>=${i}.0.0${c} <${+i+1}.0.0-0`:f&&(n=`>=${i}.${s}.0${c} <${i}.${+s+1}.0-0`),n})}function ZAt(t,e){let r=jAt;return t.replace(r,(n,o,i,s,a,c,u,p,f,m,h,_)=>(xi(i)?o="":xi(s)?o=`>=${i}.0.0${e?.includePrerelease?"-0":""}`:xi(a)?o=`>=${i}.${s}.0${e?.includePrerelease?"-0":""}`:c?o=`>=${o}`:o=`>=${o}${e?.includePrerelease?"-0":""}`,xi(f)?p="":xi(m)?p=`<${+f+1}.0.0-0`:xi(h)?p=`<${f}.${+m+1}.0-0`:_?p=`<=${f}.${m}.${h}-${_}`:e?.includePrerelease?p=`<${f}.${m}.${+h+1}-0`:p=`<=${p}`,`${o} ${p}`.trim()))}});var n8=S(Co=>{"use strict";Object.defineProperty(Co,"__esModule",{value:!0});Co.massUnwrap=Co.unwrap=Co.massWrap=Co.wrap=void 0;var Ai=console.error.bind(console);function Cb(t,e,r){let n=!!t[e]&&Object.prototype.propertyIsEnumerable.call(t,e);Object.defineProperty(t,e,{configurable:!0,enumerable:n,writable:!0,value:r})}var YAt=(t,e,r)=>{if(!t||!t[e]){Ai("no original function "+String(e)+" to wrap");return}if(!r){Ai("no wrapper function"),Ai(new Error().stack);return}let n=t[e];if(typeof n!="function"||typeof r!="function"){Ai("original object and wrapper must be functions");return}let o=r(n,e);return Cb(o,"__original",n),Cb(o,"__unwrap",()=>{t[e]===o&&Cb(t,e,n)}),Cb(o,"__wrapped",!0),Cb(t,e,o),o};Co.wrap=YAt;var JAt=(t,e,r)=>{if(t)Array.isArray(t)||(t=[t]);else{Ai("must provide one or more modules to patch"),Ai(new Error().stack);return}if(!(e&&Array.isArray(e))){Ai("must provide one or more functions to wrap on modules");return}t.forEach(n=>{e.forEach(o=>{(0,Co.wrap)(n,o,r)})})};Co.massWrap=JAt;var XAt=(t,e)=>{if(!t||!t[e]){Ai("no function to unwrap."),Ai(new Error().stack);return}let r=t[e];if(!r.__unwrap)Ai("no original to unwrap to -- has "+String(e)+" already been unwrapped?");else{r.__unwrap();return}};Co.unwrap=XAt;var QAt=(t,e)=>{if(t)Array.isArray(t)||(t=[t]);else{Ai("must provide one or more modules to patch"),Ai(new Error().stack);return}if(!(e&&Array.isArray(e))){Ai("must provide one or more functions to unwrap on modules");return}t.forEach(r=>{e.forEach(n=>{(0,Co.unwrap)(r,n)})})};Co.massUnwrap=QAt;function $b(t){t&&t.logger&&(typeof t.logger!="function"?Ai("new logger isn't a function, not replacing"):Ai=t.logger)}Co.default=$b;$b.wrap=Co.wrap;$b.massWrap=Co.massWrap;$b.unwrap=Co.unwrap;$b.massUnwrap=Co.massUnwrap});var Uxe=S(PC=>{"use strict";Object.defineProperty(PC,"__esModule",{value:!0});PC.InstrumentationAbstract=void 0;var o8=(pe(),se(Pe)),ewt=X5(),RC=n8(),i8=class{instrumentationName;instrumentationVersion;_config={};_tracer;_meter;_logger;_diag;constructor(e,r,n){this.instrumentationName=e,this.instrumentationVersion=r,this.setConfig(n),this._diag=o8.diag.createComponentLogger({namespace:e}),this._tracer=o8.trace.getTracer(e,r),this._meter=o8.metrics.getMeter(e,r),this._logger=ewt.logs.getLogger(e,r),this._updateMetricInstruments()}_wrap=RC.wrap;_unwrap=RC.unwrap;_massWrap=RC.massWrap;_massUnwrap=RC.massUnwrap;get meter(){return this._meter}setMeterProvider(e){this._meter=e.getMeter(this.instrumentationName,this.instrumentationVersion),this._updateMetricInstruments()}get logger(){return this._logger}setLoggerProvider(e){this._logger=e.getLogger(this.instrumentationName,this.instrumentationVersion)}getModuleDefinitions(){let e=this.init()??[];return Array.isArray(e)?e:[e]}_updateMetricInstruments(){}getConfig(){return this._config}setConfig(e){this._config={enabled:!0,...e}}setTracerProvider(e){this._tracer=e.getTracer(this.instrumentationName,this.instrumentationVersion)}get tracer(){return this._tracer}_runSpanCustomizationHook(e,r,n,o){if(e)try{e(n,o)}catch(i){this._diag.error("Error running span customization hook due to exception in handler",{triggerName:r},i)}}};PC.InstrumentationAbstract=i8});var s8=S((Ahr,jxe)=>{var c_=1e3,u_=c_*60,l_=u_*60,sm=l_*24,twt=sm*7,rwt=sm*365.25;jxe.exports=function(t,e){e=e||{};var r=typeof t;if(r==="string"&&t.length>0)return nwt(t);if(r==="number"&&isFinite(t))return e.long?iwt(t):owt(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))};function nwt(t){if(t=String(t),!(t.length>100)){var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return r*rwt;case"weeks":case"week":case"w":return r*twt;case"days":case"day":case"d":return r*sm;case"hours":case"hour":case"hrs":case"hr":case"h":return r*l_;case"minutes":case"minute":case"mins":case"min":case"m":return r*u_;case"seconds":case"second":case"secs":case"sec":case"s":return r*c_;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}function owt(t){var e=Math.abs(t);return e>=sm?Math.round(t/sm)+"d":e>=l_?Math.round(t/l_)+"h":e>=u_?Math.round(t/u_)+"m":e>=c_?Math.round(t/c_)+"s":t+"ms"}function iwt(t){var e=Math.abs(t);return e>=sm?IC(t,e,sm,"day"):e>=l_?IC(t,e,l_,"hour"):e>=u_?IC(t,e,u_,"minute"):e>=c_?IC(t,e,c_,"second"):t+" ms"}function IC(t,e,r,n){var o=e>=r*1.5;return Math.round(t/r)+" "+n+(o?"s":"")}});var a8=S((whr,zxe)=>{function swt(t){r.debug=r,r.default=r,r.coerce=c,r.disable=s,r.enable=o,r.enabled=a,r.humanize=s8(),r.destroy=u,Object.keys(t).forEach(p=>{r[p]=t[p]}),r.names=[],r.skips=[],r.formatters={};function e(p){let f=0;for(let m=0;m{if(B==="%%")return"%";N++;let he=r.formatters[G];if(typeof he=="function"){let Q=E[N];B=he.call(x,Q),E.splice(N,1),N--}return B}),r.formatArgs.call(x,E),(x.log||r.log).apply(x,E)}return v.namespace=p,v.useColors=r.useColors(),v.color=r.selectColor(p),v.extend=n,v.destroy=r.destroy,Object.defineProperty(v,"enabled",{enumerable:!0,configurable:!1,get:()=>m!==null?m:(h!==r.namespaces&&(h=r.namespaces,_=r.enabled(p)),_),set:E=>{m=E}}),typeof r.init=="function"&&r.init(v),v}function n(p,f){let m=r(this.namespace+(typeof f>"u"?":":f)+p);return m.log=this.log,m}function o(p){r.save(p),r.namespaces=p,r.names=[],r.skips=[];let f=(typeof p=="string"?p:"").trim().replace(/\s+/g,",").split(",").filter(Boolean);for(let m of f)m[0]==="-"?r.skips.push(m.slice(1)):r.names.push(m)}function i(p,f){let m=0,h=0,_=-1,v=0;for(;m"-"+f)].join(",");return r.enable(""),p}function a(p){for(let f of r.skips)if(i(p,f))return!1;for(let f of r.names)if(i(p,f))return!0;return!1}function c(p){return p instanceof Error?p.stack||p.message:p}function u(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return r.enable(r.load()),r}zxe.exports=swt});var Fxe=S((Yi,OC)=>{Yi.formatArgs=cwt;Yi.save=uwt;Yi.load=lwt;Yi.useColors=awt;Yi.storage=pwt();Yi.destroy=(()=>{let t=!1;return()=>{t||(t=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();Yi.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function awt(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let t;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(t=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(t[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function cwt(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+OC.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;t.splice(1,0,e,"color: inherit");let r=0,n=0;t[0].replace(/%[a-zA-Z%]/g,o=>{o!=="%%"&&(r++,o==="%c"&&(n=r))}),t.splice(n,0,e)}Yi.log=console.debug||console.log||(()=>{});function uwt(t){try{t?Yi.storage.setItem("debug",t):Yi.storage.removeItem("debug")}catch{}}function lwt(){let t;try{t=Yi.storage.getItem("debug")||Yi.storage.getItem("DEBUG")}catch{}return!t&&typeof process<"u"&&"env"in process&&(t=process.env.DEBUG),t}function pwt(){try{return localStorage}catch{}}OC.exports=a8()(Yi);var{formatters:dwt}=OC.exports;dwt.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var Bxe=S((Rhr,qxe)=>{"use strict";qxe.exports=(t,e=process.argv)=>{let r=t.startsWith("-")?"":t.length===1?"-":"--",n=e.indexOf(r+t),o=e.indexOf("--");return n!==-1&&(o===-1||n{"use strict";var fwt=require("os"),Gxe=require("tty"),da=Bxe(),{env:uo}=process,Xp;da("no-color")||da("no-colors")||da("color=false")||da("color=never")?Xp=0:(da("color")||da("colors")||da("color=true")||da("color=always"))&&(Xp=1);"FORCE_COLOR"in uo&&(uo.FORCE_COLOR==="true"?Xp=1:uo.FORCE_COLOR==="false"?Xp=0:Xp=uo.FORCE_COLOR.length===0?1:Math.min(parseInt(uo.FORCE_COLOR,10),3));function c8(t){return t===0?!1:{level:t,hasBasic:!0,has256:t>=2,has16m:t>=3}}function u8(t,e){if(Xp===0)return 0;if(da("color=16m")||da("color=full")||da("color=truecolor"))return 3;if(da("color=256"))return 2;if(t&&!e&&Xp===void 0)return 0;let r=Xp||0;if(uo.TERM==="dumb")return r;if(process.platform==="win32"){let n=fwt.release().split(".");return Number(n[0])>=10&&Number(n[2])>=10586?Number(n[2])>=14931?3:2:1}if("CI"in uo)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","GITHUB_ACTIONS","BUILDKITE"].some(n=>n in uo)||uo.CI_NAME==="codeship"?1:r;if("TEAMCITY_VERSION"in uo)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(uo.TEAMCITY_VERSION)?1:0;if(uo.COLORTERM==="truecolor")return 3;if("TERM_PROGRAM"in uo){let n=parseInt((uo.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(uo.TERM_PROGRAM){case"iTerm.app":return n>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(uo.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(uo.TERM)||"COLORTERM"in uo?1:r}function mwt(t){let e=u8(t,t&&t.isTTY);return c8(e)}Vxe.exports={supportsColor:mwt,stdout:c8(u8(!0,Gxe.isatty(1))),stderr:c8(u8(!0,Gxe.isatty(2)))}});var Wxe=S((lo,CC)=>{var hwt=require("tty"),NC=require("util");lo.init=Twt;lo.log=Swt;lo.formatArgs=_wt;lo.save=ywt;lo.load=Ewt;lo.useColors=gwt;lo.destroy=NC.deprecate(()=>{},"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");lo.colors=[6,2,3,4,5,1];try{let t=l8();t&&(t.stderr||t).level>=2&&(lo.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221])}catch{}lo.inspectOpts=Object.keys(process.env).filter(t=>/^debug_/i.test(t)).reduce((t,e)=>{let r=e.substring(6).toLowerCase().replace(/_([a-z])/g,(o,i)=>i.toUpperCase()),n=process.env[e];return/^(yes|on|true|enabled)$/i.test(n)?n=!0:/^(no|off|false|disabled)$/i.test(n)?n=!1:n==="null"?n=null:n=Number(n),t[r]=n,t},{});function gwt(){return"colors"in lo.inspectOpts?!!lo.inspectOpts.colors:hwt.isatty(process.stderr.fd)}function _wt(t){let{namespace:e,useColors:r}=this;if(r){let n=this.color,o="\x1B[3"+(n<8?n:"8;5;"+n),i=` ${o};1m${e} \x1B[0m`;t[0]=i+t[0].split(` -`).join(` -`+i),t.push(o+"m+"+CC.exports.humanize(this.diff)+"\x1B[0m")}else t[0]=vwt()+e+" "+t[0]}function vwt(){return lo.inspectOpts.hideDate?"":new Date().toISOString()+" "}function Swt(...t){return process.stderr.write(NC.formatWithOptions(lo.inspectOpts,...t)+` -`)}function ywt(t){t?process.env.DEBUG=t:delete process.env.DEBUG}function Ewt(){return process.env.DEBUG}function Twt(t){t.inspectOpts={};let e=Object.keys(lo.inspectOpts);for(let r=0;re.trim()).join(" ")};Hxe.O=function(t){return this.inspectOpts.colors=this.useColors,NC.inspect(t,this.inspectOpts)}});var bs=S((Ihr,p8)=>{typeof process>"u"||process.type==="renderer"||process.browser===!0||process.__nwjs?p8.exports=Fxe():p8.exports=Wxe()});var f8=S((Ohr,Kxe)=>{"use strict";var d8=require("path").sep;Kxe.exports=function(t){var e=t.split(d8),r=e.lastIndexOf("node_modules");if(r!==-1&&e[r+1]){for(var n=e[r+1][0]==="@",o=n?e[r+1]+"/"+e[r+2]:e[r+1],i=n?3:2,s="",a=r+i-1,c=0;c<=a;c++)c===a?s+=e[c]:s+=e[c]+d8;for(var u="",p=e.length-1,f=r+i;f<=p;f++)f===p?u+=e[f]:u+=e[f]+d8;return{name:o,basedir:s,path:u}}}});var m8=S((Nhr,Zxe)=>{"use strict";var bwt=require("os");Zxe.exports=bwt.homedir||function(){var e=process.env.HOME,r=process.env.LOGNAME||process.env.USER||process.env.LNAME||process.env.USERNAME;return process.platform==="win32"?process.env.USERPROFILE||process.env.HOMEDRIVE+process.env.HOMEPATH||e||null:process.platform==="darwin"?e||(r?"/Users/"+r:null):process.platform==="linux"?e||(process.getuid()===0?"/root":r?"/home/"+r:null):e||null}});var h8=S((Chr,Yxe)=>{Yxe.exports=function(){var t=Error.prepareStackTrace;Error.prepareStackTrace=function(r,n){return n};var e=new Error().stack;return Error.prepareStackTrace=t,e[2].getFileName()}});var Jxe=S(($hr,kb)=>{"use strict";var xwt=process.platform==="win32",Awt=/^(((?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?[\\\/]?)(?:[^\\\/]*[\\\/])*)((\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))[\\\/]*$/,g8={};function wwt(t){return Awt.exec(t).slice(1)}g8.parse=function(t){if(typeof t!="string")throw new TypeError("Parameter 'pathString' must be a string, not "+typeof t);var e=wwt(t);if(!e||e.length!==5)throw new TypeError("Invalid path '"+t+"'");return{root:e[1],dir:e[0]===e[1]?e[0]:e[0].slice(0,-1),base:e[2],ext:e[4],name:e[3]}};var Rwt=/^((\/?)(?:[^\/]*\/)*)((\.{1,2}|[^\/]+?|)(\.[^.\/]*|))[\/]*$/,_8={};function Pwt(t){return Rwt.exec(t).slice(1)}_8.parse=function(t){if(typeof t!="string")throw new TypeError("Parameter 'pathString' must be a string, not "+typeof t);var e=Pwt(t);if(!e||e.length!==5)throw new TypeError("Invalid path '"+t+"'");return{root:e[1],dir:e[0].slice(0,-1),base:e[2],ext:e[4],name:e[3]}};xwt?kb.exports=g8.parse:kb.exports=_8.parse;kb.exports.posix=_8.parse;kb.exports.win32=g8.parse});var v8=S((khr,t0e)=>{var e0e=require("path"),Xxe=e0e.parse||Jxe(),Qxe=function(e,r){var n="/";/^([A-Za-z]:)/.test(e)?n="":/^\\\\/.test(e)&&(n="\\\\");for(var o=[e],i=Xxe(e);i.dir!==o[o.length-1];)o.push(i.dir),i=Xxe(i.dir);return o.reduce(function(s,a){return s.concat(r.map(function(c){return e0e.resolve(n,a,c)}))},[])};t0e.exports=function(e,r,n){var o=r&&r.moduleDirectory?[].concat(r.moduleDirectory):["node_modules"];if(r&&typeof r.paths=="function")return r.paths(n,e,function(){return Qxe(e,o)},r);var i=Qxe(e,o);return r&&r.paths?i.concat(r.paths):i}});var S8=S((Mhr,r0e)=>{r0e.exports=function(t,e){return e||{}}});var i0e=S((Dhr,o0e)=>{"use strict";var Iwt="Function.prototype.bind called on incompatible ",Owt=Object.prototype.toString,Nwt=Math.max,Cwt="[object Function]",n0e=function(e,r){for(var n=[],o=0;o{"use strict";var Mwt=i0e();s0e.exports=Function.prototype.bind||Mwt});var y8=S((Uhr,a0e)=>{"use strict";var Dwt=Function.prototype.call,Lwt=Object.prototype.hasOwnProperty,Uwt=Mb();a0e.exports=Uwt.call(Dwt,Lwt)});var c0e=S((jhr,jwt)=>{jwt.exports={assert:!0,"node:assert":[">= 14.18 && < 15",">= 16"],"assert/strict":">= 15","node:assert/strict":">= 16",async_hooks:">= 8","node:async_hooks":[">= 14.18 && < 15",">= 16"],buffer_ieee754:">= 0.5 && < 0.9.7",buffer:!0,"node:buffer":[">= 14.18 && < 15",">= 16"],child_process:!0,"node:child_process":[">= 14.18 && < 15",">= 16"],cluster:">= 0.5","node:cluster":[">= 14.18 && < 15",">= 16"],console:!0,"node:console":[">= 14.18 && < 15",">= 16"],constants:!0,"node:constants":[">= 14.18 && < 15",">= 16"],crypto:!0,"node:crypto":[">= 14.18 && < 15",">= 16"],_debug_agent:">= 1 && < 8",_debugger:"< 8",dgram:!0,"node:dgram":[">= 14.18 && < 15",">= 16"],diagnostics_channel:[">= 14.17 && < 15",">= 15.1"],"node:diagnostics_channel":[">= 14.18 && < 15",">= 16"],dns:!0,"node:dns":[">= 14.18 && < 15",">= 16"],"dns/promises":">= 15","node:dns/promises":">= 16",domain:">= 0.7.12","node:domain":[">= 14.18 && < 15",">= 16"],events:!0,"node:events":[">= 14.18 && < 15",">= 16"],freelist:"< 6",fs:!0,"node:fs":[">= 14.18 && < 15",">= 16"],"fs/promises":[">= 10 && < 10.1",">= 14"],"node:fs/promises":[">= 14.18 && < 15",">= 16"],_http_agent:">= 0.11.1","node:_http_agent":[">= 14.18 && < 15",">= 16"],_http_client:">= 0.11.1","node:_http_client":[">= 14.18 && < 15",">= 16"],_http_common:">= 0.11.1","node:_http_common":[">= 14.18 && < 15",">= 16"],_http_incoming:">= 0.11.1","node:_http_incoming":[">= 14.18 && < 15",">= 16"],_http_outgoing:">= 0.11.1","node:_http_outgoing":[">= 14.18 && < 15",">= 16"],_http_server:">= 0.11.1","node:_http_server":[">= 14.18 && < 15",">= 16"],http:!0,"node:http":[">= 14.18 && < 15",">= 16"],http2:">= 8.8","node:http2":[">= 14.18 && < 15",">= 16"],https:!0,"node:https":[">= 14.18 && < 15",">= 16"],inspector:">= 8","node:inspector":[">= 14.18 && < 15",">= 16"],"inspector/promises":[">= 19"],"node:inspector/promises":[">= 19"],_linklist:"< 8",module:!0,"node:module":[">= 14.18 && < 15",">= 16"],net:!0,"node:net":[">= 14.18 && < 15",">= 16"],"node-inspect/lib/_inspect":">= 7.6 && < 12","node-inspect/lib/internal/inspect_client":">= 7.6 && < 12","node-inspect/lib/internal/inspect_repl":">= 7.6 && < 12",os:!0,"node:os":[">= 14.18 && < 15",">= 16"],path:!0,"node:path":[">= 14.18 && < 15",">= 16"],"path/posix":">= 15.3","node:path/posix":">= 16","path/win32":">= 15.3","node:path/win32":">= 16",perf_hooks:">= 8.5","node:perf_hooks":[">= 14.18 && < 15",">= 16"],process:">= 1","node:process":[">= 14.18 && < 15",">= 16"],punycode:">= 0.5","node:punycode":[">= 14.18 && < 15",">= 16"],querystring:!0,"node:querystring":[">= 14.18 && < 15",">= 16"],readline:!0,"node:readline":[">= 14.18 && < 15",">= 16"],"readline/promises":">= 17","node:readline/promises":">= 17",repl:!0,"node:repl":[">= 14.18 && < 15",">= 16"],"node:sea":[">= 20.12 && < 21",">= 21.7"],smalloc:">= 0.11.5 && < 3","node:sqlite":[">= 22.13 && < 23",">= 23.4"],_stream_duplex:">= 0.9.4","node:_stream_duplex":[">= 14.18 && < 15",">= 16"],_stream_transform:">= 0.9.4","node:_stream_transform":[">= 14.18 && < 15",">= 16"],_stream_wrap:">= 1.4.1","node:_stream_wrap":[">= 14.18 && < 15",">= 16"],_stream_passthrough:">= 0.9.4","node:_stream_passthrough":[">= 14.18 && < 15",">= 16"],_stream_readable:">= 0.9.4","node:_stream_readable":[">= 14.18 && < 15",">= 16"],_stream_writable:">= 0.9.4","node:_stream_writable":[">= 14.18 && < 15",">= 16"],stream:!0,"node:stream":[">= 14.18 && < 15",">= 16"],"stream/consumers":">= 16.7","node:stream/consumers":">= 16.7","stream/promises":">= 15","node:stream/promises":">= 16","stream/web":">= 16.5","node:stream/web":">= 16.5",string_decoder:!0,"node:string_decoder":[">= 14.18 && < 15",">= 16"],sys:[">= 0.4 && < 0.7",">= 0.8"],"node:sys":[">= 14.18 && < 15",">= 16"],"test/reporters":">= 19.9 && < 20.2","node:test/reporters":[">= 18.17 && < 19",">= 19.9",">= 20"],"test/mock_loader":">= 22.3 && < 22.7","node:test/mock_loader":">= 22.3 && < 22.7","node:test":[">= 16.17 && < 17",">= 18"],timers:!0,"node:timers":[">= 14.18 && < 15",">= 16"],"timers/promises":">= 15","node:timers/promises":">= 16",_tls_common:">= 0.11.13","node:_tls_common":[">= 14.18 && < 15",">= 16"],_tls_legacy:">= 0.11.3 && < 10",_tls_wrap:">= 0.11.3","node:_tls_wrap":[">= 14.18 && < 15",">= 16"],tls:!0,"node:tls":[">= 14.18 && < 15",">= 16"],trace_events:">= 10","node:trace_events":[">= 14.18 && < 15",">= 16"],tty:!0,"node:tty":[">= 14.18 && < 15",">= 16"],url:!0,"node:url":[">= 14.18 && < 15",">= 16"],util:!0,"node:util":[">= 14.18 && < 15",">= 16"],"util/types":">= 15.3","node:util/types":">= 16","v8/tools/arguments":">= 10 && < 12","v8/tools/codemap":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/consarray":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/csvparser":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/logreader":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/profile_view":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/splaytree":[">= 4.4 && < 5",">= 5.2 && < 12"],v8:">= 1","node:v8":[">= 14.18 && < 15",">= 16"],vm:!0,"node:vm":[">= 14.18 && < 15",">= 16"],wasi:[">= 13.4 && < 13.5",">= 18.17 && < 19",">= 20"],"node:wasi":[">= 18.17 && < 19",">= 20"],worker_threads:">= 11.7","node:worker_threads":[">= 14.18 && < 15",">= 16"],zlib:">= 0.5","node:zlib":[">= 14.18 && < 15",">= 16"]}});var Db=S((zhr,p0e)=>{"use strict";var zwt=y8();function Fwt(t,e){for(var r=t.split("."),n=e.split(" "),o=n.length>1?n[0]:"=",i=(n.length>1?n[1]:n[0]).split("."),s=0;s<3;++s){var a=parseInt(r[s]||0,10),c=parseInt(i[s]||0,10);if(a!==c)return o==="<"?a="?a>=c:!1}return o===">="}function u0e(t,e){var r=e.split(/ ?&& ?/);if(r.length===0)return!1;for(var n=0;n"u"?process.versions&&process.versions.node:t;if(typeof r!="string")throw new TypeError(typeof t>"u"?"Unable to determine current node version":"If provided, a valid node version is required");if(e&&typeof e=="object"){for(var n=0;n{var am=require("fs"),Bwt=m8(),Un=require("path"),Gwt=h8(),Vwt=v8(),Hwt=S8(),Wwt=Db(),Kwt=process.platform!=="win32"&&am.realpath&&typeof am.realpath.native=="function"?am.realpath.native:am.realpath,d0e=Bwt(),Zwt=function(){return[Un.join(d0e,".node_modules"),Un.join(d0e,".node_libraries")]},Ywt=function(e,r){am.stat(e,function(n,o){return n?n.code==="ENOENT"||n.code==="ENOTDIR"?r(null,!1):r(n):r(null,o.isFile()||o.isFIFO())})},Jwt=function(e,r){am.stat(e,function(n,o){return n?n.code==="ENOENT"||n.code==="ENOTDIR"?r(null,!1):r(n):r(null,o.isDirectory())})},Xwt=function(e,r){Kwt(e,function(n,o){n&&n.code!=="ENOENT"?r(n):r(null,n?e:o)})},Lb=function(e,r,n,o){n&&n.preserveSymlinks===!1?e(r,o):o(null,r)},Qwt=function(e,r,n){e(r,function(o,i){if(o)n(o);else try{var s=JSON.parse(i);n(null,s)}catch{n(null)}})},eRt=function(e,r,n){for(var o=Vwt(r,n,e),i=0;i{tRt.exports={assert:!0,"node:assert":[">= 14.18 && < 15",">= 16"],"assert/strict":">= 15","node:assert/strict":">= 16",async_hooks:">= 8","node:async_hooks":[">= 14.18 && < 15",">= 16"],buffer_ieee754:">= 0.5 && < 0.9.7",buffer:!0,"node:buffer":[">= 14.18 && < 15",">= 16"],child_process:!0,"node:child_process":[">= 14.18 && < 15",">= 16"],cluster:">= 0.5","node:cluster":[">= 14.18 && < 15",">= 16"],console:!0,"node:console":[">= 14.18 && < 15",">= 16"],constants:!0,"node:constants":[">= 14.18 && < 15",">= 16"],crypto:!0,"node:crypto":[">= 14.18 && < 15",">= 16"],_debug_agent:">= 1 && < 8",_debugger:"< 8",dgram:!0,"node:dgram":[">= 14.18 && < 15",">= 16"],diagnostics_channel:[">= 14.17 && < 15",">= 15.1"],"node:diagnostics_channel":[">= 14.18 && < 15",">= 16"],dns:!0,"node:dns":[">= 14.18 && < 15",">= 16"],"dns/promises":">= 15","node:dns/promises":">= 16",domain:">= 0.7.12","node:domain":[">= 14.18 && < 15",">= 16"],events:!0,"node:events":[">= 14.18 && < 15",">= 16"],freelist:"< 6",fs:!0,"node:fs":[">= 14.18 && < 15",">= 16"],"fs/promises":[">= 10 && < 10.1",">= 14"],"node:fs/promises":[">= 14.18 && < 15",">= 16"],_http_agent:">= 0.11.1","node:_http_agent":[">= 14.18 && < 15",">= 16"],_http_client:">= 0.11.1","node:_http_client":[">= 14.18 && < 15",">= 16"],_http_common:">= 0.11.1","node:_http_common":[">= 14.18 && < 15",">= 16"],_http_incoming:">= 0.11.1","node:_http_incoming":[">= 14.18 && < 15",">= 16"],_http_outgoing:">= 0.11.1","node:_http_outgoing":[">= 14.18 && < 15",">= 16"],_http_server:">= 0.11.1","node:_http_server":[">= 14.18 && < 15",">= 16"],http:!0,"node:http":[">= 14.18 && < 15",">= 16"],http2:">= 8.8","node:http2":[">= 14.18 && < 15",">= 16"],https:!0,"node:https":[">= 14.18 && < 15",">= 16"],inspector:">= 8","node:inspector":[">= 14.18 && < 15",">= 16"],"inspector/promises":[">= 19"],"node:inspector/promises":[">= 19"],_linklist:"< 8",module:!0,"node:module":[">= 14.18 && < 15",">= 16"],net:!0,"node:net":[">= 14.18 && < 15",">= 16"],"node-inspect/lib/_inspect":">= 7.6 && < 12","node-inspect/lib/internal/inspect_client":">= 7.6 && < 12","node-inspect/lib/internal/inspect_repl":">= 7.6 && < 12",os:!0,"node:os":[">= 14.18 && < 15",">= 16"],path:!0,"node:path":[">= 14.18 && < 15",">= 16"],"path/posix":">= 15.3","node:path/posix":">= 16","path/win32":">= 15.3","node:path/win32":">= 16",perf_hooks:">= 8.5","node:perf_hooks":[">= 14.18 && < 15",">= 16"],process:">= 1","node:process":[">= 14.18 && < 15",">= 16"],punycode:">= 0.5","node:punycode":[">= 14.18 && < 15",">= 16"],querystring:!0,"node:querystring":[">= 14.18 && < 15",">= 16"],readline:!0,"node:readline":[">= 14.18 && < 15",">= 16"],"readline/promises":">= 17","node:readline/promises":">= 17",repl:!0,"node:repl":[">= 14.18 && < 15",">= 16"],"node:sea":[">= 20.12 && < 21",">= 21.7"],smalloc:">= 0.11.5 && < 3","node:sqlite":">= 23.4",_stream_duplex:">= 0.9.4","node:_stream_duplex":[">= 14.18 && < 15",">= 16"],_stream_transform:">= 0.9.4","node:_stream_transform":[">= 14.18 && < 15",">= 16"],_stream_wrap:">= 1.4.1","node:_stream_wrap":[">= 14.18 && < 15",">= 16"],_stream_passthrough:">= 0.9.4","node:_stream_passthrough":[">= 14.18 && < 15",">= 16"],_stream_readable:">= 0.9.4","node:_stream_readable":[">= 14.18 && < 15",">= 16"],_stream_writable:">= 0.9.4","node:_stream_writable":[">= 14.18 && < 15",">= 16"],stream:!0,"node:stream":[">= 14.18 && < 15",">= 16"],"stream/consumers":">= 16.7","node:stream/consumers":">= 16.7","stream/promises":">= 15","node:stream/promises":">= 16","stream/web":">= 16.5","node:stream/web":">= 16.5",string_decoder:!0,"node:string_decoder":[">= 14.18 && < 15",">= 16"],sys:[">= 0.4 && < 0.7",">= 0.8"],"node:sys":[">= 14.18 && < 15",">= 16"],"test/reporters":">= 19.9 && < 20.2","node:test/reporters":[">= 18.17 && < 19",">= 19.9",">= 20"],"test/mock_loader":">= 22.3 && < 22.7","node:test/mock_loader":">= 22.3 && < 22.7","node:test":[">= 16.17 && < 17",">= 18"],timers:!0,"node:timers":[">= 14.18 && < 15",">= 16"],"timers/promises":">= 15","node:timers/promises":">= 16",_tls_common:">= 0.11.13","node:_tls_common":[">= 14.18 && < 15",">= 16"],_tls_legacy:">= 0.11.3 && < 10",_tls_wrap:">= 0.11.3","node:_tls_wrap":[">= 14.18 && < 15",">= 16"],tls:!0,"node:tls":[">= 14.18 && < 15",">= 16"],trace_events:">= 10","node:trace_events":[">= 14.18 && < 15",">= 16"],tty:!0,"node:tty":[">= 14.18 && < 15",">= 16"],url:!0,"node:url":[">= 14.18 && < 15",">= 16"],util:!0,"node:util":[">= 14.18 && < 15",">= 16"],"util/types":">= 15.3","node:util/types":">= 16","v8/tools/arguments":">= 10 && < 12","v8/tools/codemap":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/consarray":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/csvparser":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/logreader":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/profile_view":[">= 4.4 && < 5",">= 5.2 && < 12"],"v8/tools/splaytree":[">= 4.4 && < 5",">= 5.2 && < 12"],v8:">= 1","node:v8":[">= 14.18 && < 15",">= 16"],vm:!0,"node:vm":[">= 14.18 && < 15",">= 16"],wasi:[">= 13.4 && < 13.5",">= 18.17 && < 19",">= 20"],"node:wasi":[">= 18.17 && < 19",">= 20"],worker_threads:">= 11.7","node:worker_threads":[">= 14.18 && < 15",">= 16"],zlib:">= 0.5","node:zlib":[">= 14.18 && < 15",">= 16"]}});var S0e=S((Bhr,v0e)=>{"use strict";var rRt=Db(),g0e=h0e(),_0e={};for($C in g0e)Object.prototype.hasOwnProperty.call(g0e,$C)&&(_0e[$C]=rRt($C));var $C;v0e.exports=_0e});var E0e=S((Ghr,y0e)=>{var nRt=Db();y0e.exports=function(e){return nRt(e)}});var x0e=S((Vhr,b0e)=>{var oRt=Db(),cm=require("fs"),ti=require("path"),iRt=m8(),sRt=h8(),aRt=v8(),cRt=S8(),uRt=process.platform!=="win32"&&cm.realpathSync&&typeof cm.realpathSync.native=="function"?cm.realpathSync.native:cm.realpathSync,T0e=iRt(),lRt=function(){return[ti.join(T0e,".node_modules"),ti.join(T0e,".node_libraries")]},pRt=function(e){try{var r=cm.statSync(e,{throwIfNoEntry:!1})}catch(n){if(n&&(n.code==="ENOENT"||n.code==="ENOTDIR"))return!1;throw n}return!!r&&(r.isFile()||r.isFIFO())},dRt=function(e){try{var r=cm.statSync(e,{throwIfNoEntry:!1})}catch(n){if(n&&(n.code==="ENOENT"||n.code==="ENOTDIR"))return!1;throw n}return!!r&&r.isDirectory()},fRt=function(e){try{return uRt(e)}catch(r){if(r.code!=="ENOENT")throw r}return e},Ub=function(e,r,n){return n&&n.preserveSymlinks===!1?e(r):r},mRt=function(e,r){var n=e(r);try{var o=JSON.parse(n);return o}catch{}},hRt=function(e,r,n){for(var o=aRt(r,n,e),i=0;i{var kC=m0e();kC.core=S0e();kC.isCore=E0e();kC.sync=x0e();A0e.exports=kC});var w0e=S((Whr,gRt)=>{gRt.exports={name:"require-in-the-middle",version:"7.5.2",description:"Module to hook into the Node.js require function",main:"index.js",types:"types/index.d.ts",dependencies:{debug:"^4.3.5","module-details-from-path":"^1.0.3",resolve:"^1.22.8"},devDependencies:{"@babel/core":"^7.9.0","@babel/preset-env":"^7.9.5","@babel/preset-typescript":"^7.9.0","@babel/register":"^7.9.0","ipp-printer":"^1.0.0",patterns:"^1.0.3",roundround:"^0.2.0",semver:"^6.3.0",standard:"^14.3.1",tape:"^4.11.0"},scripts:{test:"npm run test:lint && npm run test:tape && npm run test:babel","test:lint":"standard","test:tape":"tape test/*.js","test:babel":"node test/babel/babel-register.js"},repository:{type:"git",url:"git+https://github.com/nodejs/require-in-the-middle.git"},keywords:["require","hook","shim","shimmer","shimming","patch","monkey","monkeypatch","module","load"],files:["types"],author:"Thomas Watson Steen (https://twitter.com/wa7son)",license:"MIT",bugs:{url:"https://github.com/nodejs/require-in-the-middle/issues"},homepage:"https://github.com/nodejs/require-in-the-middle#readme",engines:{node:">=8.6.0"}}});var zb=S((Khr,x8)=>{"use strict";var d_=require("path"),fc=require("module"),On=bs()("require-in-the-middle"),_Rt=f8();x8.exports=jb;x8.exports.Hook=jb;var T8,p_;if(fc.isBuiltin)p_=fc.isBuiltin;else if(fc.builtinModules)p_=t=>t.startsWith("node:")?!0:(T8===void 0&&(T8=new Set(fc.builtinModules)),T8.has(t));else{let t=E8(),[e,r]=process.versions.node.split(".").map(Number);e===8&&r<8?p_=n=>n==="http2"?!0:!!t.core[n]:p_=n=>!!t.core[n]}var MC;function vRt(t,e){if(!MC)if(require.resolve&&require.resolve.paths)MC=function(r,n){return require.resolve(r,{paths:[n]})};else{let r=E8();MC=function(n,o){return r.sync(n,{basedir:o})}}return MC(t,e)}var SRt=/([/\\]index)?(\.js)?$/,b8=class{constructor(){this._localCache=new Map,this._kRitmExports=Symbol("RitmExports")}has(e,r){if(this._localCache.has(e))return!0;if(r)return!1;{let n=require.cache[e];return!!(n&&this._kRitmExports in n)}}get(e,r){let n=this._localCache.get(e);if(n!==void 0)return n;if(!r){let o=require.cache[e];return o&&o[this._kRitmExports]}}set(e,r,n){n?this._localCache.set(e,r):e in require.cache?require.cache[e][this._kRitmExports]=r:(On('non-core module is unexpectedly not in require.cache: "%s"',e),this._localCache.set(e,r))}};function jb(t,e,r){if(!(this instanceof jb))return new jb(t,e,r);if(typeof t=="function"?(r=t,t=null,e=null):typeof e=="function"&&(r=e,e=null),typeof fc._resolveFilename!="function"){console.error("Error: Expected Module._resolveFilename to be a function (was: %s) - aborting!",typeof fc._resolveFilename),console.error("Please report this error as an issue related to Node.js %s at %s",process.version,w0e().bugs.url);return}this._cache=new b8,this._unhooked=!1,this._origRequire=fc.prototype.require;let n=this,o=new Set,i=e?e.internals===!0:!1,s=Array.isArray(t);On("registering require hook"),this._require=fc.prototype.require=function(c){return n._unhooked===!0?(On("ignoring require call - module is soft-unhooked"),n._origRequire.apply(this,arguments)):a.call(this,arguments,!1)},typeof process.getBuiltinModule=="function"&&(this._origGetBuiltinModule=process.getBuiltinModule,this._getBuiltinModule=process.getBuiltinModule=function(c){return n._unhooked===!0?(On("ignoring process.getBuiltinModule call - module is soft-unhooked"),n._origGetBuiltinModule.apply(this,arguments)):a.call(this,arguments,!0)});function a(c,u){let p=c[0],f=p_(p),m;if(f){if(m=p,p.startsWith("node:")){let w=p.slice(5);p_(w)&&(m=w)}}else{if(u)return On("call to process.getBuiltinModule with unknown built-in id"),n._origGetBuiltinModule.apply(this,c);try{m=fc._resolveFilename(p,this)}catch(w){return On('Module._resolveFilename("%s") threw %j, calling original Module.require',p,w.message),n._origRequire.apply(this,c)}}let h,_;if(On("processing %s module require('%s'): %s",f===!0?"core":"non-core",p,m),n._cache.has(m,f)===!0)return On("returning already patched cached module: %s",m),n._cache.get(m,f);let v=o.has(m);v===!1&&o.add(m);let E=u?n._origGetBuiltinModule.apply(this,c):n._origRequire.apply(this,c);if(v===!0)return On("module is in the process of being patched already - ignoring: %s",m),E;if(o.delete(m),f===!0){if(s===!0&&t.includes(m)===!1)return On("ignoring core module not on whitelist: %s",m),E;h=m}else if(s===!0&&t.includes(m)){let w=d_.parse(m);h=w.name,_=w.dir}else{let w=_Rt(m);if(w===void 0)return On("could not parse filename: %s",m),E;h=w.name,_=w.basedir;let I=yRt(w);On("resolved filename to module: %s (id: %s, resolved: %s, basedir: %s)",h,p,I,_);let N=!1;if(s){if(!p.startsWith(".")&&t.includes(p)&&(h=p,N=!0),!t.includes(h)&&!t.includes(I))return E;t.includes(I)&&I!==h&&(h=I,N=!0)}if(!N){let $;try{$=vRt(h,_)}catch{return On("could not resolve module: %s",h),n._cache.set(m,E,f),E}if($!==m)if(i===!0)h=h+d_.sep+d_.relative(_,m),On("preparing to process require of internal file: %s",h);else return On("ignoring require of non-main module file: %s",$),n._cache.set(m,E,f),E}}n._cache.set(m,E,f),On("calling require hook: %s",h);let x=r(E,h,_);return n._cache.set(m,x,f),On("returning module: %s",h),x}}jb.prototype.unhook=function(){this._unhooked=!0,this._require===fc.prototype.require?(fc.prototype.require=this._origRequire,On("require unhook successful")):On("require unhook unsuccessful"),process.getBuiltinModule!==void 0&&(this._getBuiltinModule===process.getBuiltinModule?(process.getBuiltinModule=this._origGetBuiltinModule,On("process.getBuiltinModule unhook successful")):On("process.getBuiltinModule unhook unsuccessful"))};function yRt(t){let e=d_.sep!=="/"?t.path.split(d_.sep).join("/"):t.path;return d_.posix.join(t.name,e).replace(SRt,"")}});var R0e=S(Qp=>{"use strict";Object.defineProperty(Qp,"__esModule",{value:!0});Qp.ModuleNameTrie=Qp.ModuleNameSeparator=void 0;Qp.ModuleNameSeparator="/";var DC=class{hooks=[];children=new Map},A8=class{_trie=new DC;_counter=0;insert(e){let r=this._trie;for(let n of e.moduleName.split(Qp.ModuleNameSeparator)){let o=r.children.get(n);o||(o=new DC,r.children.set(n,o)),r=o}r.hooks.push({hook:e,insertedId:this._counter++})}search(e,{maintainInsertionOrder:r,fullOnly:n}={}){let o=this._trie,i=[],s=!0;for(let a of e.split(Qp.ModuleNameSeparator)){let c=o.children.get(a);if(!c){s=!1;break}n||i.push(...c.hooks),o=c}return n&&s&&i.push(...o.hooks),i.length===0?[]:i.length===1?[i[0].hook]:(r&&i.sort((a,c)=>a.insertedId-c.insertedId),i.map(({hook:a})=>a))}};Qp.ModuleNameTrie=A8});var I0e=S(LC=>{"use strict";Object.defineProperty(LC,"__esModule",{value:!0});LC.RequireInTheMiddleSingleton=void 0;var ERt=zb(),P0e=require("path"),w8=R0e(),TRt=["afterEach","after","beforeEach","before","describe","it"].every(t=>typeof global[t]=="function"),R8=class t{_moduleNameTrie=new w8.ModuleNameTrie;static _instance;constructor(){this._initialize()}_initialize(){new ERt.Hook(null,{internals:!0},(e,r,n)=>{let o=bRt(r),i=this._moduleNameTrie.search(o,{maintainInsertionOrder:!0,fullOnly:n===void 0});for(let{onRequire:s}of i)e=s(e,r,n);return e})}register(e,r){let n={moduleName:e,onRequire:r};return this._moduleNameTrie.insert(n),n}static getInstance(){return TRt?new t:this._instance=this._instance??new t}};LC.RequireInTheMiddleSingleton=R8;function bRt(t){return P0e.sep!==w8.ModuleNameSeparator?t.split(P0e.sep).join(w8.ModuleNameSeparator):t}});var M0e=S(um=>{var O0e=[],P8=new WeakMap,N0e=new WeakMap,C0e=new Map,$0e=[],xRt={set(t,e,r){return P8.get(t)[e](r)},get(t,e){if(e===Symbol.toStringTag)return"Module";let r=N0e.get(t)[e];if(typeof r=="function")return r()},defineProperty(t,e,r){if(!("value"in r))throw new Error("Getters/setters are not supported for exports property descriptors.");return P8.get(t)[e](r.value)}};function ARt(t,e,r,n,o){C0e.set(t,o),P8.set(e,r),N0e.set(e,n);let i=new Proxy(e,xRt);O0e.forEach(s=>s(t,i)),$0e.push([t,i])}var k0e=!1;function wRt(){return k0e}function RRt(t){k0e=t}um.register=ARt;um.importHooks=O0e;um.specifiers=C0e;um.toHook=$0e;um.getExperimentalPatchInternals=wRt;um.setExperimentalPatchInternals=RRt});var qb=S((Xhr,f_)=>{var D0e=require("path"),PRt=f8(),{fileURLToPath:L0e}=require("url"),{MessageChannel:IRt}=require("worker_threads"),{importHooks:I8,specifiers:ORt,toHook:NRt,getExperimentalPatchInternals:CRt}=M0e();function j0e(t){I8.push(t),NRt.forEach(([e,r])=>t(e,r))}function z0e(t){let e=I8.indexOf(t);e>-1&&I8.splice(e,1)}function U0e(t,e,r,n){let o=t(e,r,n);o&&o!==e&&(e.default=o)}var O8;function $Rt(){let{port1:t,port2:e}=new IRt,r=0,n;O8=a=>{r++,t.postMessage(a)},t.on("message",()=>{r--,n&&r<=0&&n()}).unref();function o(){let a=setInterval(()=>{},1e3),c=new Promise(u=>{n=u}).then(()=>{clearInterval(a)});return r===0&&n(),c}let i=e;return{registerOptions:{data:{addHookMessagePort:i,include:[]},transferList:[i]},addHookMessagePort:i,waitForAllMessagesAcknowledged:o}}function Fb(t,e,r){if(!(this instanceof Fb))return new Fb(t,e,r);typeof t=="function"?(r=t,t=null,e=null):typeof e=="function"&&(r=e,e=null);let n=e?e.internals===!0:!1;O8&&Array.isArray(t)&&O8(t),this._iitmHook=(o,i)=>{let s=o,a=o.startsWith("node:"),c;if(a)o=o.replace(/^node:/,"");else{if(o.startsWith("file://"))try{o=L0e(o)}catch{}let u=PRt(o);u&&(o=u.name,c=u.basedir)}if(t){for(let u of t)if(u===o){if(c){if(n)o=o+D0e.sep+D0e.relative(c,L0e(s));else if(!CRt()&&!c.endsWith(ORt.get(s)))continue}U0e(r,i,o,c)}}else U0e(r,i,o,c)},j0e(this._iitmHook)}Fb.prototype.unhook=function(){z0e(this._iitmHook)};f_.exports=Fb;f_.exports.Hook=Fb;f_.exports.addHook=j0e;f_.exports.removeHook=z0e;f_.exports.createAddHookMessageChannel=$Rt});var N8=S(ed=>{"use strict";Object.defineProperty(ed,"__esModule",{value:!0});ed.isWrapped=ed.safeExecuteInTheMiddleAsync=ed.safeExecuteInTheMiddle=void 0;function kRt(t,e,r){let n,o;try{o=t()}catch(i){n=i}finally{if(e(n,o),n&&!r)throw n;return o}}ed.safeExecuteInTheMiddle=kRt;async function MRt(t,e,r){let n,o;try{o=await t()}catch(i){n=i}finally{if(e(n,o),n&&!r)throw n;return o}}ed.safeExecuteInTheMiddleAsync=MRt;function DRt(t){return typeof t=="function"&&typeof t.__original=="function"&&typeof t.__unwrap=="function"&&t.__wrapped===!0}ed.isWrapped=DRt});var B0e=S(UC=>{"use strict";Object.defineProperty(UC,"__esModule",{value:!0});UC.InstrumentationBase=void 0;var Bb=require("path"),F0e=require("util"),LRt=Lxe(),C8=n8(),URt=Uxe(),jRt=I0e(),zRt=qb(),Gb=(pe(),se(Pe)),FRt=zb(),qRt=require("fs"),BRt=N8(),$8=class extends URt.InstrumentationAbstract{_modules;_hooks=[];_requireInTheMiddleSingleton=jRt.RequireInTheMiddleSingleton.getInstance();_enabled=!1;constructor(e,r,n){super(e,r,n);let o=this.init();o&&!Array.isArray(o)&&(o=[o]),this._modules=o||[],this._config.enabled&&this.enable()}_wrap=(e,r,n)=>{if((0,BRt.isWrapped)(e[r])&&this._unwrap(e,r),F0e.types.isProxy(e)){let o=(0,C8.wrap)(Object.assign({},e),r,n);return Object.defineProperty(e,r,{value:o}),o}else return(0,C8.wrap)(e,r,n)};_unwrap=(e,r)=>F0e.types.isProxy(e)?Object.defineProperty(e,r,{value:e[r]}):(0,C8.unwrap)(e,r);_massWrap=(e,r,n)=>{if(e)Array.isArray(e)||(e=[e]);else{Gb.diag.error("must provide one or more modules to patch");return}if(!(r&&Array.isArray(r))){Gb.diag.error("must provide one or more functions to wrap on modules");return}e.forEach(o=>{r.forEach(i=>{this._wrap(o,i,n)})})};_massUnwrap=(e,r)=>{if(e)Array.isArray(e)||(e=[e]);else{Gb.diag.error("must provide one or more modules to patch");return}if(!(r&&Array.isArray(r))){Gb.diag.error("must provide one or more functions to wrap on modules");return}e.forEach(n=>{r.forEach(o=>{this._unwrap(n,o)})})};_warnOnPreloadedModules(){this._modules.forEach(e=>{let{name:r}=e;try{let n=require.resolve(r);require.cache[n]&&this._diag.warn(`Module ${r} has been loaded before ${this.instrumentationName} so it might not work, please initialize it before requiring ${r}`)}catch{}})}_extractPackageVersion(e){try{let r=(0,qRt.readFileSync)(Bb.join(e,"package.json"),{encoding:"utf8"}),n=JSON.parse(r).version;return typeof n=="string"?n:void 0}catch{Gb.diag.warn("Failed extracting version",e)}}_onRequire(e,r,n,o){if(!o)return typeof e.patch=="function"&&(e.moduleExports=r,this._enabled)?(this._diag.debug("Applying instrumentation patch for nodejs core module on require hook",{module:e.name}),e.patch(r)):r;let i=this._extractPackageVersion(o);if(e.moduleVersion=i,e.name===n)return q0e(e.supportedVersions,i,e.includePrerelease)&&typeof e.patch=="function"&&(e.moduleExports=r,this._enabled)?(this._diag.debug("Applying instrumentation patch for module on require hook",{module:e.name,version:e.moduleVersion,baseDir:o}),e.patch(r,e.moduleVersion)):r;let s=e.files??[],a=Bb.normalize(n);return s.filter(u=>u.name===a).filter(u=>q0e(u.supportedVersions,i,e.includePrerelease)).reduce((u,p)=>(p.moduleExports=u,this._enabled?(this._diag.debug("Applying instrumentation patch for nodejs module file on require hook",{module:e.name,version:e.moduleVersion,fileName:p.name,baseDir:o}),p.patch(u,e.moduleVersion)):u),r)}enable(){if(!this._enabled){if(this._enabled=!0,this._hooks.length>0){for(let e of this._modules){typeof e.patch=="function"&&e.moduleExports&&(this._diag.debug("Applying instrumentation patch for nodejs module on instrumentation enabled",{module:e.name,version:e.moduleVersion}),e.patch(e.moduleExports,e.moduleVersion));for(let r of e.files)r.moduleExports&&(this._diag.debug("Applying instrumentation patch for nodejs module file on instrumentation enabled",{module:e.name,version:e.moduleVersion,fileName:r.name}),r.patch(r.moduleExports,e.moduleVersion))}return}this._warnOnPreloadedModules();for(let e of this._modules){let r=(s,a,c)=>{if(!c&&Bb.isAbsolute(a)){let u=Bb.parse(a);a=u.name,c=u.dir}return this._onRequire(e,s,a,c)},n=(s,a,c)=>this._onRequire(e,s,a,c),o=Bb.isAbsolute(e.name)?new FRt.Hook([e.name],{internals:!0},n):this._requireInTheMiddleSingleton.register(e.name,n);this._hooks.push(o);let i=new zRt.Hook([e.name],{internals:!1},r);this._hooks.push(i)}}}disable(){if(this._enabled){this._enabled=!1;for(let e of this._modules){typeof e.unpatch=="function"&&e.moduleExports&&(this._diag.debug("Removing instrumentation patch for nodejs module on instrumentation disabled",{module:e.name,version:e.moduleVersion}),e.unpatch(e.moduleExports,e.moduleVersion));for(let r of e.files)r.moduleExports&&(this._diag.debug("Removing instrumentation patch for nodejs module file on instrumentation disabled",{module:e.name,version:e.moduleVersion,fileName:r.name}),r.unpatch(r.moduleExports,e.moduleVersion))}}}isEnabled(){return this._enabled}};UC.InstrumentationBase=$8;function q0e(t,e,r){return typeof e>"u"?t.includes("*"):t.some(n=>(0,LRt.satisfies)(e,n,{includePrerelease:r}))}});var G0e=S(jC=>{"use strict";Object.defineProperty(jC,"__esModule",{value:!0});jC.normalize=void 0;var GRt=require("path");Object.defineProperty(jC,"normalize",{enumerable:!0,get:function(){return GRt.normalize}})});var V0e=S(m_=>{"use strict";Object.defineProperty(m_,"__esModule",{value:!0});m_.normalize=m_.InstrumentationBase=void 0;var VRt=B0e();Object.defineProperty(m_,"InstrumentationBase",{enumerable:!0,get:function(){return VRt.InstrumentationBase}});var HRt=G0e();Object.defineProperty(m_,"normalize",{enumerable:!0,get:function(){return HRt.normalize}})});var k8=S(h_=>{"use strict";Object.defineProperty(h_,"__esModule",{value:!0});h_.normalize=h_.InstrumentationBase=void 0;var H0e=V0e();Object.defineProperty(h_,"InstrumentationBase",{enumerable:!0,get:function(){return H0e.InstrumentationBase}});Object.defineProperty(h_,"normalize",{enumerable:!0,get:function(){return H0e.normalize}})});var W0e=S(zC=>{"use strict";Object.defineProperty(zC,"__esModule",{value:!0});zC.InstrumentationNodeModuleDefinition=void 0;var M8=class{name;supportedVersions;patch;unpatch;files;constructor(e,r,n,o,i){this.name=e,this.supportedVersions=r,this.patch=n,this.unpatch=o,this.files=i||[]}};zC.InstrumentationNodeModuleDefinition=M8});var K0e=S(FC=>{"use strict";Object.defineProperty(FC,"__esModule",{value:!0});FC.InstrumentationNodeModuleFile=void 0;var WRt=k8(),D8=class{supportedVersions;patch;unpatch;name;constructor(e,r,n,o){this.supportedVersions=r,this.patch=n,this.unpatch=o,this.name=(0,WRt.normalize)(e)}};FC.InstrumentationNodeModuleFile=D8});var Z0e=S(lm=>{"use strict";Object.defineProperty(lm,"__esModule",{value:!0});lm.semconvStabilityFromStr=lm.SemconvStability=void 0;var qC;(function(t){t[t.STABLE=1]="STABLE",t[t.OLD=2]="OLD",t[t.DUPLICATE=3]="DUPLICATE"})(qC=lm.SemconvStability||(lm.SemconvStability={}));function KRt(t,e){let r=qC.OLD,n=e?.split(",").map(o=>o.trim()).filter(o=>o!=="");for(let o of n??[])if(o.toLowerCase()===t+"/dup"){r=qC.DUPLICATE;break}else o.toLowerCase()===t&&(r=qC.STABLE);return r}lm.semconvStabilityFromStr=KRt});var Ft=S(jn=>{"use strict";Object.defineProperty(jn,"__esModule",{value:!0});jn.semconvStabilityFromStr=jn.SemconvStability=jn.safeExecuteInTheMiddleAsync=jn.safeExecuteInTheMiddle=jn.isWrapped=jn.InstrumentationNodeModuleFile=jn.InstrumentationNodeModuleDefinition=jn.InstrumentationBase=jn.registerInstrumentations=void 0;var ZRt=Pxe();Object.defineProperty(jn,"registerInstrumentations",{enumerable:!0,get:function(){return ZRt.registerInstrumentations}});var YRt=k8();Object.defineProperty(jn,"InstrumentationBase",{enumerable:!0,get:function(){return YRt.InstrumentationBase}});var JRt=W0e();Object.defineProperty(jn,"InstrumentationNodeModuleDefinition",{enumerable:!0,get:function(){return JRt.InstrumentationNodeModuleDefinition}});var XRt=K0e();Object.defineProperty(jn,"InstrumentationNodeModuleFile",{enumerable:!0,get:function(){return XRt.InstrumentationNodeModuleFile}});var L8=N8();Object.defineProperty(jn,"isWrapped",{enumerable:!0,get:function(){return L8.isWrapped}});Object.defineProperty(jn,"safeExecuteInTheMiddle",{enumerable:!0,get:function(){return L8.safeExecuteInTheMiddle}});Object.defineProperty(jn,"safeExecuteInTheMiddleAsync",{enumerable:!0,get:function(){return L8.safeExecuteInTheMiddleAsync}});var Y0e=Z0e();Object.defineProperty(jn,"SemconvStability",{enumerable:!0,get:function(){return Y0e.SemconvStability}});Object.defineProperty(jn,"semconvStabilityFromStr",{enumerable:!0,get:function(){return Y0e.semconvStabilityFromStr}})});var J0e=S(Ve=>{"use strict";Object.defineProperty(Ve,"__esModule",{value:!0});Ve.HTTP_FLAVOR_VALUE_HTTP_1_1=Ve.NET_TRANSPORT_VALUE_IP_UDP=Ve.NET_TRANSPORT_VALUE_IP_TCP=Ve.ATTR_NET_TRANSPORT=Ve.ATTR_NET_PEER_PORT=Ve.ATTR_NET_PEER_NAME=Ve.ATTR_NET_PEER_IP=Ve.ATTR_NET_HOST_PORT=Ve.ATTR_NET_HOST_NAME=Ve.ATTR_NET_HOST_IP=Ve.ATTR_HTTP_USER_AGENT=Ve.ATTR_HTTP_URL=Ve.ATTR_HTTP_TARGET=Ve.ATTR_HTTP_STATUS_CODE=Ve.ATTR_HTTP_SERVER_NAME=Ve.ATTR_HTTP_SCHEME=Ve.ATTR_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED=Ve.ATTR_HTTP_RESPONSE_CONTENT_LENGTH=Ve.ATTR_HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED=Ve.ATTR_HTTP_REQUEST_CONTENT_LENGTH=Ve.ATTR_HTTP_METHOD=Ve.ATTR_HTTP_HOST=Ve.ATTR_HTTP_FLAVOR=Ve.ATTR_HTTP_CLIENT_IP=Ve.USER_AGENT_SYNTHETIC_TYPE_VALUE_TEST=Ve.USER_AGENT_SYNTHETIC_TYPE_VALUE_BOT=Ve.ATTR_USER_AGENT_SYNTHETIC_TYPE=void 0;Ve.ATTR_USER_AGENT_SYNTHETIC_TYPE="user_agent.synthetic.type";Ve.USER_AGENT_SYNTHETIC_TYPE_VALUE_BOT="bot";Ve.USER_AGENT_SYNTHETIC_TYPE_VALUE_TEST="test";Ve.ATTR_HTTP_CLIENT_IP="http.client_ip";Ve.ATTR_HTTP_FLAVOR="http.flavor";Ve.ATTR_HTTP_HOST="http.host";Ve.ATTR_HTTP_METHOD="http.method";Ve.ATTR_HTTP_REQUEST_CONTENT_LENGTH="http.request_content_length";Ve.ATTR_HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED="http.request_content_length_uncompressed";Ve.ATTR_HTTP_RESPONSE_CONTENT_LENGTH="http.response_content_length";Ve.ATTR_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED="http.response_content_length_uncompressed";Ve.ATTR_HTTP_SCHEME="http.scheme";Ve.ATTR_HTTP_SERVER_NAME="http.server_name";Ve.ATTR_HTTP_STATUS_CODE="http.status_code";Ve.ATTR_HTTP_TARGET="http.target";Ve.ATTR_HTTP_URL="http.url";Ve.ATTR_HTTP_USER_AGENT="http.user_agent";Ve.ATTR_NET_HOST_IP="net.host.ip";Ve.ATTR_NET_HOST_NAME="net.host.name";Ve.ATTR_NET_HOST_PORT="net.host.port";Ve.ATTR_NET_PEER_IP="net.peer.ip";Ve.ATTR_NET_PEER_NAME="net.peer.name";Ve.ATTR_NET_PEER_PORT="net.peer.port";Ve.ATTR_NET_TRANSPORT="net.transport";Ve.NET_TRANSPORT_VALUE_IP_TCP="ip_tcp";Ve.NET_TRANSPORT_VALUE_IP_UDP="ip_udp";Ve.HTTP_FLAVOR_VALUE_HTTP_1_1="1.1"});var X0e=S(Vb=>{"use strict";Object.defineProperty(Vb,"__esModule",{value:!0});Vb.AttributeNames=void 0;var QRt;(function(t){t.HTTP_ERROR_NAME="http.error_name",t.HTTP_ERROR_MESSAGE="http.error_message",t.HTTP_STATUS_TEXT="http.status_text"})(QRt=Vb.AttributeNames||(Vb.AttributeNames={}))});var Q0e=S(g_=>{"use strict";Object.defineProperty(g_,"__esModule",{value:!0});g_.SYNTHETIC_BOT_NAMES=g_.SYNTHETIC_TEST_NAMES=void 0;g_.SYNTHETIC_TEST_NAMES=["alwayson"];g_.SYNTHETIC_BOT_NAMES=["googlebot","bingbot"]});var tAe=S((pgr,eAe)=>{"use strict";var ePt=require("util");function U8(t,e){Error.captureStackTrace(this,U8),this.name=this.constructor.name,this.message=t,this.input=e}ePt.inherits(U8,Error);eAe.exports=U8});var nAe=S((dgr,rAe)=>{"use strict";function tPt(t){return t===34||t===40||t===41||t===44||t===47||t>=58&&t<=64||t>=91&&t<=93||t===123||t===125}function rPt(t){return t===33||t>=35&&t<=39||t===42||t===43||t===45||t===46||t>=48&&t<=57||t>=65&&t<=90||t>=94&&t<=122||t===124||t===126}function nPt(t){return t>=32&&t<=126}function oPt(t){return t>=128&&t<=255}rAe.exports={isDelimiter:tPt,isTokenChar:rPt,isExtended:oPt,isPrint:nPt}});var cAe=S((fgr,aAe)=>{"use strict";var iPt=require("util"),__=tAe(),BC=nAe(),sPt=BC.isDelimiter,oAe=BC.isTokenChar,iAe=BC.isExtended,aPt=BC.isPrint;function sAe(t){return t.replace(/\\(.)/g,"$1")}function Hb(t,e){return iPt.format("Unexpected character '%s' at index %d",t.charAt(e),e)}function cPt(t){for(var e=!1,r=!1,n=!1,o={},i=[],s=-1,a=-1,c,u,p=0;p{"use strict";Object.defineProperty(He,"__esModule",{value:!0});He.headerCapture=He.getIncomingStableRequestMetricAttributesOnResponse=He.getIncomingRequestMetricAttributesOnResponse=He.getIncomingRequestAttributesOnResponse=He.getIncomingRequestMetricAttributes=He.getIncomingRequestAttributes=He.getRemoteClientAddress=He.getOutgoingStableRequestMetricAttributesOnResponse=He.getOutgoingRequestMetricAttributesOnResponse=He.getOutgoingRequestAttributesOnResponse=He.setAttributesFromHttpKind=He.getOutgoingRequestMetricAttributes=He.getOutgoingRequestAttributes=He.extractHostnameAndPort=He.isValidOptionsType=He.getRequestInfo=He.isCompressed=He.setResponseContentLengthAttribute=He.setRequestContentLengthAttribute=He.setSpanWithError=He.satisfiesPattern=He.parseResponseStatus=He.getAbsoluteUrl=void 0;var Wb=(pe(),se(Pe)),At=(er(),se(Cr)),Be=J0e(),uAe=_r(),tu=Ft(),uPt=require("url"),GC=X0e(),lAe=Q0e(),lPt=cAe(),pPt=(t,e,r="http:")=>{let n=t||{},o=n.protocol||r,i=(n.port||"").toString(),s=n.path||"/",a=n.host||n.hostname||e.host||"localhost";return a.indexOf(":")===-1&&i&&i!=="80"&&i!=="443"&&(a+=`:${i}`),`${o}//${a}${s}`};He.getAbsoluteUrl=pPt;var dPt=(t,e)=>{let r=t===Wb.SpanKind.CLIENT?400:500;return e&&e>=100&&e{if(typeof e=="string")return e===t;if(e instanceof RegExp)return e.test(t);if(typeof e=="function")return e(t);throw new TypeError("Pattern is in unsupported datatype")};He.satisfiesPattern=fPt;var mPt=(t,e,r)=>{let n=e.message;r&tu.SemconvStability.OLD&&(t.setAttribute(GC.AttributeNames.HTTP_ERROR_NAME,e.name),t.setAttribute(GC.AttributeNames.HTTP_ERROR_MESSAGE,n)),r&tu.SemconvStability.STABLE&&t.setAttribute(At.ATTR_ERROR_TYPE,e.name),t.setStatus({code:Wb.SpanStatusCode.ERROR,message:n}),t.recordException(e)};He.setSpanWithError=mPt;var hPt=(t,e)=>{let r=pAe(t.headers);r!==null&&((0,He.isCompressed)(t.headers)?e[Be.ATTR_HTTP_REQUEST_CONTENT_LENGTH]=r:e[Be.ATTR_HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED]=r)};He.setRequestContentLengthAttribute=hPt;var gPt=(t,e)=>{let r=pAe(t.headers);r!==null&&((0,He.isCompressed)(t.headers)?e[Be.ATTR_HTTP_RESPONSE_CONTENT_LENGTH]=r:e[Be.ATTR_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED]=r)};He.setResponseContentLengthAttribute=gPt;function pAe(t){let e=t["content-length"];if(e===void 0)return null;let r=parseInt(e,10);return isNaN(r)?null:r}var _Pt=t=>{let e=t["content-encoding"];return!!e&&e!=="identity"};He.isCompressed=_Pt;function vPt(t){let{hostname:e,pathname:r,port:n,username:o,password:i,search:s,protocol:a,hash:c,href:u,origin:p,host:f}=new URL(t),m={protocol:a,hostname:e&&e[0]==="["?e.slice(1,-1):e,hash:c,search:s,pathname:r,path:`${r||""}${s||""}`,href:u,origin:p,host:f};return n!==""&&(m.port=Number(n)),(o||i)&&(m.auth=`${decodeURIComponent(o)}:${decodeURIComponent(i)}`),m}var SPt=(t,e,r)=>{let n,o,i,s=!1;if(typeof e=="string"){try{let c=vPt(e);i=c,n=c.pathname||"/"}catch(c){s=!0,t.verbose("Unable to parse URL provided to HTTP request, using fallback to determine path. Original error:",c),i={path:e},n=i.path||"/"}o=`${i.protocol||"http:"}//${i.host}`,r!==void 0&&Object.assign(i,r)}else if(e instanceof uPt.URL)i={protocol:e.protocol,hostname:typeof e.hostname=="string"&&e.hostname.startsWith("[")?e.hostname.slice(1,-1):e.hostname,path:`${e.pathname||""}${e.search||""}`},e.port!==""&&(i.port=Number(e.port)),(e.username||e.password)&&(i.auth=`${e.username}:${e.password}`),n=e.pathname,o=e.origin,r!==void 0&&Object.assign(i,r);else{i=Object.assign({protocol:e.host?"http:":void 0},e);let c=i.host||(i.port!=null?`${i.hostname}${i.port}`:i.hostname);if(o=`${i.protocol||"http:"}//${c}`,n=e.pathname,!n&&i.path)try{n=new URL(i.path,o).pathname||"/"}catch{n="/"}}let a=i.method?i.method.toUpperCase():"GET";return{origin:o,pathname:n,method:a,optionsParsed:i,invalidUrl:s}};He.getRequestInfo=SPt;var yPt=t=>{if(!t)return!1;let e=typeof t;return e==="string"||e==="object"&&!Array.isArray(t)};He.isValidOptionsType=yPt;var EPt=t=>{if(t.hostname&&t.port)return{hostname:t.hostname,port:t.port};let e=t.host?.match(/^([^:/ ]+)(:\d{1,5})?/)||null,r=t.hostname||(e===null?"localhost":e[1]),n=t.port;return n||(e&&e[2]?n=e[2].substring(1):n=t.protocol==="https:"?"443":"80"),{hostname:r,port:n}};He.extractHostnameAndPort=EPt;var TPt=(t,e,r,n)=>{let o=e.hostname,i=e.port,s=t.method??"GET",a=mAe(s),c=t.headers||{},u=c["user-agent"],p=(0,He.getAbsoluteUrl)(t,c,`${e.component}:`),f={[Be.ATTR_HTTP_URL]:p,[Be.ATTR_HTTP_METHOD]:s,[Be.ATTR_HTTP_TARGET]:t.path||"/",[Be.ATTR_NET_PEER_NAME]:o,[Be.ATTR_HTTP_HOST]:c.host??`${o}:${i}`},m={[At.ATTR_HTTP_REQUEST_METHOD]:a,[At.ATTR_SERVER_ADDRESS]:o,[At.ATTR_SERVER_PORT]:Number(i),[At.ATTR_URL_FULL]:p,[At.ATTR_USER_AGENT_ORIGINAL]:u};switch(s!==a&&(m[At.ATTR_HTTP_REQUEST_METHOD_ORIGINAL]=s),n&&u&&(m[Be.ATTR_USER_AGENT_SYNTHETIC_TYPE]=dAe(u)),u!==void 0&&(f[Be.ATTR_HTTP_USER_AGENT]=u),r){case tu.SemconvStability.STABLE:return Object.assign(m,e.hookAttributes);case tu.SemconvStability.OLD:return Object.assign(f,e.hookAttributes)}return Object.assign(f,m,e.hookAttributes)};He.getOutgoingRequestAttributes=TPt;var bPt=t=>{let e={};return e[Be.ATTR_HTTP_METHOD]=t[Be.ATTR_HTTP_METHOD],e[Be.ATTR_NET_PEER_NAME]=t[Be.ATTR_NET_PEER_NAME],e};He.getOutgoingRequestMetricAttributes=bPt;var xPt=(t,e)=>{t&&(e[Be.ATTR_HTTP_FLAVOR]=t,t.toUpperCase()!=="QUIC"?e[Be.ATTR_NET_TRANSPORT]=Be.NET_TRANSPORT_VALUE_IP_TCP:e[Be.ATTR_NET_TRANSPORT]=Be.NET_TRANSPORT_VALUE_IP_UDP)};He.setAttributesFromHttpKind=xPt;var dAe=t=>{let e=String(t).toLowerCase();for(let r of lAe.SYNTHETIC_TEST_NAMES)if(e.includes(r))return Be.USER_AGENT_SYNTHETIC_TYPE_VALUE_TEST;for(let r of lAe.SYNTHETIC_BOT_NAMES)if(e.includes(r))return Be.USER_AGENT_SYNTHETIC_TYPE_VALUE_BOT},APt=(t,e)=>{let{statusCode:r,statusMessage:n,httpVersion:o,socket:i}=t,s={},a={};if(r!=null&&(a[At.ATTR_HTTP_RESPONSE_STATUS_CODE]=r),i){let{remoteAddress:c,remotePort:u}=i;s[Be.ATTR_NET_PEER_IP]=c,s[Be.ATTR_NET_PEER_PORT]=u,a[At.ATTR_NETWORK_PEER_ADDRESS]=c,a[At.ATTR_NETWORK_PEER_PORT]=u,a[At.ATTR_NETWORK_PROTOCOL_VERSION]=t.httpVersion}switch((0,He.setResponseContentLengthAttribute)(t,s),r&&(s[Be.ATTR_HTTP_STATUS_CODE]=r,s[GC.AttributeNames.HTTP_STATUS_TEXT]=(n||"").toUpperCase()),(0,He.setAttributesFromHttpKind)(o,s),e){case tu.SemconvStability.STABLE:return a;case tu.SemconvStability.OLD:return s}return Object.assign(s,a)};He.getOutgoingRequestAttributesOnResponse=APt;var wPt=t=>{let e={};return e[Be.ATTR_NET_PEER_PORT]=t[Be.ATTR_NET_PEER_PORT],e[Be.ATTR_HTTP_STATUS_CODE]=t[Be.ATTR_HTTP_STATUS_CODE],e[Be.ATTR_HTTP_FLAVOR]=t[Be.ATTR_HTTP_FLAVOR],e};He.getOutgoingRequestMetricAttributesOnResponse=wPt;var RPt=t=>{let e={};return t[At.ATTR_NETWORK_PROTOCOL_VERSION]&&(e[At.ATTR_NETWORK_PROTOCOL_VERSION]=t[At.ATTR_NETWORK_PROTOCOL_VERSION]),t[At.ATTR_HTTP_RESPONSE_STATUS_CODE]&&(e[At.ATTR_HTTP_RESPONSE_STATUS_CODE]=t[At.ATTR_HTTP_RESPONSE_STATUS_CODE]),e};He.getOutgoingStableRequestMetricAttributesOnResponse=RPt;function td(t,e){let r=t.split(":");if(r.length===1)return e==="http"?{host:r[0],port:"80"}:e==="https"?{host:r[0],port:"443"}:{host:r[0]};if(r.length===2)return{host:r[0],port:r[1]};if(r[0].startsWith("[")){if(r[r.length-1].endsWith("]")){if(e==="http")return{host:t,port:"80"};if(e==="https")return{host:t,port:"443"}}else if(r[r.length-2].endsWith("]"))return{host:r.slice(0,-1).join(":"),port:r[r.length-1]}}return{host:t}}function PPt(t,e){let r=t.headers.forwarded;if(r){for(let i of hAe(r))if(i.host)return td(i.host,i.proto)}let n=t.headers["x-forwarded-host"];if(typeof n=="string")return typeof t.headers["x-forwarded-proto"]=="string"?td(n,t.headers["x-forwarded-proto"]):Array.isArray(t.headers["x-forwarded-proto"])?td(n,t.headers["x-forwarded-proto"][0]):td(n);if(Array.isArray(n)&&typeof n[0]=="string"&&n[0].length>0)return typeof t.headers["x-forwarded-proto"]=="string"?td(n[0],t.headers["x-forwarded-proto"]):Array.isArray(t.headers["x-forwarded-proto"])?td(n[0],t.headers["x-forwarded-proto"][0]):td(n[0]);let o=t.headers.host;return typeof o=="string"&&o.length>0?td(o,e):null}function fAe(t){let e=t.headers.forwarded;if(e){for(let o of hAe(e))if(o.for)return o.for}let r=t.headers["x-forwarded-for"];if(typeof r=="string")return r;if(Array.isArray(r))return r[0];let n=t.socket.remoteAddress;return n||null}He.getRemoteClientAddress=fAe;function IPt(t,e,r){try{if(e.headers.host)return new URL(e.url??"/",`${t}://${e.headers.host}`);{let n=new URL(e.url??"/",`${t}://localhost`);return{pathname:n.pathname,search:n.search,toString:function(){return n.pathname+n.search}}}}catch(n){return r.verbose("Unable to get URL from request",n),{}}}var OPt=(t,e,r)=>{let n=t.headers,o=n["user-agent"],i=n["x-forwarded-for"],s=t.httpVersion,a=n.host,c=a?.replace(/^(.*)(:[0-9]{1,5})/,"$1")||"localhost",u=t.method,p=mAe(u),f=PPt(t,e.component),m=e.serverName,h=fAe(t),_={[At.ATTR_HTTP_REQUEST_METHOD]:p,[At.ATTR_URL_SCHEME]:e.component,[At.ATTR_SERVER_ADDRESS]:f?.host,[At.ATTR_NETWORK_PEER_ADDRESS]:t.socket.remoteAddress,[At.ATTR_NETWORK_PEER_PORT]:t.socket.remotePort,[At.ATTR_NETWORK_PROTOCOL_VERSION]:t.httpVersion,[At.ATTR_USER_AGENT_ORIGINAL]:o},v=IPt(e.component,t,r);v?.pathname!=null&&(_[At.ATTR_URL_PATH]=v.pathname),v.search&&(_[At.ATTR_URL_QUERY]=v.search.slice(1)),h!=null&&(_[At.ATTR_CLIENT_ADDRESS]=h.split(",")[0]),f?.port!=null&&(_[At.ATTR_SERVER_PORT]=Number(f.port)),u!==p&&(_[At.ATTR_HTTP_REQUEST_METHOD_ORIGINAL]=u),e.enableSyntheticSourceDetection&&o&&(_[Be.ATTR_USER_AGENT_SYNTHETIC_TYPE]=dAe(o));let E={[Be.ATTR_HTTP_URL]:v.toString(),[Be.ATTR_HTTP_HOST]:a,[Be.ATTR_NET_HOST_NAME]:c,[Be.ATTR_HTTP_METHOD]:u,[Be.ATTR_HTTP_SCHEME]:e.component};switch(typeof i=="string"&&(E[Be.ATTR_HTTP_CLIENT_IP]=i.split(",")[0]),typeof m=="string"&&(E[Be.ATTR_HTTP_SERVER_NAME]=m),v?.pathname&&(E[Be.ATTR_HTTP_TARGET]=v?.pathname+v?.search||"/"),o!==void 0&&(E[Be.ATTR_HTTP_USER_AGENT]=o),(0,He.setRequestContentLengthAttribute)(t,E),(0,He.setAttributesFromHttpKind)(s,E),e.semconvStability){case tu.SemconvStability.STABLE:return Object.assign(_,e.hookAttributes);case tu.SemconvStability.OLD:return Object.assign(E,e.hookAttributes)}return Object.assign(E,_,e.hookAttributes)};He.getIncomingRequestAttributes=OPt;var NPt=t=>{let e={};return e[Be.ATTR_HTTP_SCHEME]=t[Be.ATTR_HTTP_SCHEME],e[Be.ATTR_HTTP_METHOD]=t[Be.ATTR_HTTP_METHOD],e[Be.ATTR_NET_HOST_NAME]=t[Be.ATTR_NET_HOST_NAME],e[Be.ATTR_HTTP_FLAVOR]=t[Be.ATTR_HTTP_FLAVOR],e};He.getIncomingRequestMetricAttributes=NPt;var CPt=(t,e,r)=>{let{socket:n}=t,{statusCode:o,statusMessage:i}=e,s={[At.ATTR_HTTP_RESPONSE_STATUS_CODE]:o},a=(0,uAe.getRPCMetadata)(Wb.context.active()),c={};if(n){let{localAddress:u,localPort:p,remoteAddress:f,remotePort:m}=n;c[Be.ATTR_NET_HOST_IP]=u,c[Be.ATTR_NET_HOST_PORT]=p,c[Be.ATTR_NET_PEER_IP]=f,c[Be.ATTR_NET_PEER_PORT]=m}switch(c[Be.ATTR_HTTP_STATUS_CODE]=o,c[GC.AttributeNames.HTTP_STATUS_TEXT]=(i||"").toUpperCase(),a?.type===uAe.RPCType.HTTP&&a.route!==void 0&&(c[At.ATTR_HTTP_ROUTE]=a.route,s[At.ATTR_HTTP_ROUTE]=a.route),r){case tu.SemconvStability.STABLE:return s;case tu.SemconvStability.OLD:return c}return Object.assign(c,s)};He.getIncomingRequestAttributesOnResponse=CPt;var $Pt=t=>{let e={};return e[Be.ATTR_HTTP_STATUS_CODE]=t[Be.ATTR_HTTP_STATUS_CODE],e[Be.ATTR_NET_HOST_PORT]=t[Be.ATTR_NET_HOST_PORT],t[At.ATTR_HTTP_ROUTE]!==void 0&&(e[At.ATTR_HTTP_ROUTE]=t[At.ATTR_HTTP_ROUTE]),e};He.getIncomingRequestMetricAttributesOnResponse=$Pt;var kPt=t=>{let e={};return t[At.ATTR_HTTP_ROUTE]!==void 0&&(e[At.ATTR_HTTP_ROUTE]=t[At.ATTR_HTTP_ROUTE]),t[At.ATTR_HTTP_RESPONSE_STATUS_CODE]&&(e[At.ATTR_HTTP_RESPONSE_STATUS_CODE]=t[At.ATTR_HTTP_RESPONSE_STATUS_CODE]),e};He.getIncomingStableRequestMetricAttributesOnResponse=kPt;function MPt(t,e){let r=new Map;for(let n=0,o=e.length;n{for(let i of r.keys()){let s=o(i);if(s===void 0)continue;let a=r.get(i),c=`http.${t}.header.${a}`;typeof s=="string"?n.setAttribute(c,[s]):Array.isArray(s)?n.setAttribute(c,s):n.setAttribute(c,[s])}}}He.headerCapture=MPt;var DPt=new Set(["GET","HEAD","POST","PUT","DELETE","CONNECT","OPTIONS","TRACE","PATCH"]);function mAe(t){if(t==null)return"GET";let e=t.toUpperCase();return DPt.has(e)?e:"_OTHER"}function hAe(t){try{return lPt(t)}catch{return[]}}});var _Ae=S(VC=>{"use strict";Object.defineProperty(VC,"__esModule",{value:!0});VC.HttpInstrumentation=void 0;var ot=(pe(),se(Pe)),rd=_r(),LPt=require("url"),UPt=hxe(),po=Ft(),j8=require("events"),gn=(er(),se(Cr)),rn=gAe(),z8=class extends po.InstrumentationBase{_spanNotEnded=new WeakSet;_headerCapture;_semconvStability=po.SemconvStability.OLD;constructor(e={}){super("@opentelemetry/instrumentation-http",UPt.VERSION,e),this._headerCapture=this._createHeaderCapture(),this._semconvStability=(0,po.semconvStabilityFromStr)("http",process.env.OTEL_SEMCONV_STABILITY_OPT_IN)}_updateMetricInstruments(){this._oldHttpServerDurationHistogram=this.meter.createHistogram("http.server.duration",{description:"Measures the duration of inbound HTTP requests.",unit:"ms",valueType:ot.ValueType.DOUBLE}),this._oldHttpClientDurationHistogram=this.meter.createHistogram("http.client.duration",{description:"Measures the duration of outbound HTTP requests.",unit:"ms",valueType:ot.ValueType.DOUBLE}),this._stableHttpServerDurationHistogram=this.meter.createHistogram(gn.METRIC_HTTP_SERVER_REQUEST_DURATION,{description:"Duration of HTTP server requests.",unit:"s",valueType:ot.ValueType.DOUBLE,advice:{explicitBucketBoundaries:[.005,.01,.025,.05,.075,.1,.25,.5,.75,1,2.5,5,7.5,10]}}),this._stableHttpClientDurationHistogram=this.meter.createHistogram(gn.METRIC_HTTP_CLIENT_REQUEST_DURATION,{description:"Duration of HTTP client requests.",unit:"s",valueType:ot.ValueType.DOUBLE,advice:{explicitBucketBoundaries:[.005,.01,.025,.05,.075,.1,.25,.5,.75,1,2.5,5,7.5,10]}})}_recordServerDuration(e,r,n){this._semconvStability&po.SemconvStability.OLD&&this._oldHttpServerDurationHistogram.record(e,r),this._semconvStability&po.SemconvStability.STABLE&&this._stableHttpServerDurationHistogram.record(e/1e3,n)}_recordClientDuration(e,r,n){this._semconvStability&po.SemconvStability.OLD&&this._oldHttpClientDurationHistogram.record(e,r),this._semconvStability&po.SemconvStability.STABLE&&this._stableHttpClientDurationHistogram.record(e/1e3,n)}setConfig(e={}){super.setConfig(e),this._headerCapture=this._createHeaderCapture()}init(){return[this._getHttpsInstrumentation(),this._getHttpInstrumentation()]}_getHttpInstrumentation(){return new po.InstrumentationNodeModuleDefinition("http",["*"],e=>{let r=e[Symbol.toStringTag]==="Module";if(!this.getConfig().disableOutgoingRequestInstrumentation){let n=this._wrap(e,"request",this._getPatchOutgoingRequestFunction("http")),o=this._wrap(e,"get",this._getPatchOutgoingGetFunction(n));r&&(e.default.request=n,e.default.get=o)}return this.getConfig().disableIncomingRequestInstrumentation||this._wrap(e.Server.prototype,"emit",this._getPatchIncomingRequestFunction("http")),e},e=>{e!==void 0&&(this.getConfig().disableOutgoingRequestInstrumentation||(this._unwrap(e,"request"),this._unwrap(e,"get")),this.getConfig().disableIncomingRequestInstrumentation||this._unwrap(e.Server.prototype,"emit"))})}_getHttpsInstrumentation(){return new po.InstrumentationNodeModuleDefinition("https",["*"],e=>{let r=e[Symbol.toStringTag]==="Module";if(!this.getConfig().disableOutgoingRequestInstrumentation){let n=this._wrap(e,"request",this._getPatchHttpsOutgoingRequestFunction("https")),o=this._wrap(e,"get",this._getPatchHttpsOutgoingGetFunction(n));r&&(e.default.request=n,e.default.get=o)}return this.getConfig().disableIncomingRequestInstrumentation||this._wrap(e.Server.prototype,"emit",this._getPatchIncomingRequestFunction("https")),e},e=>{e!==void 0&&(this.getConfig().disableOutgoingRequestInstrumentation||(this._unwrap(e,"request"),this._unwrap(e,"get")),this.getConfig().disableIncomingRequestInstrumentation||this._unwrap(e.Server.prototype,"emit"))})}_getPatchIncomingRequestFunction(e){return r=>this._incomingRequestFunction(e,r)}_getPatchOutgoingRequestFunction(e){return r=>this._outgoingRequestFunction(e,r)}_getPatchOutgoingGetFunction(e){return r=>function(o,...i){let s=e(o,...i);return s.end(),s}}_getPatchHttpsOutgoingRequestFunction(e){return r=>{let n=this;return function(i,...s){return e==="https"&&typeof i=="object"&&i?.constructor?.name!=="URL"&&(i=Object.assign({},i),n._setDefaultOptions(i)),n._getPatchOutgoingRequestFunction(e)(r)(i,...s)}}}_setDefaultOptions(e){e.protocol=e.protocol||"https:",e.port=e.port||443}_getPatchHttpsOutgoingGetFunction(e){return r=>{let n=this;return function(i,...s){return n._getPatchOutgoingGetFunction(e)(r)(i,...s)}}}_traceClientRequest(e,r,n,o,i){this.getConfig().requestHook&&this._callRequestHook(r,e);let s=!1;return e.prependListener("response",a=>{this._diag.debug("outgoingRequest on response()"),e.listenerCount("response")<=1&&a.resume();let c=(0,rn.getOutgoingRequestAttributesOnResponse)(a,this._semconvStability);r.setAttributes(c),o=Object.assign(o,(0,rn.getOutgoingRequestMetricAttributesOnResponse)(c)),i=Object.assign(i,(0,rn.getOutgoingStableRequestMetricAttributesOnResponse)(c)),this.getConfig().responseHook&&this._callResponseHook(r,a),this._headerCapture.client.captureRequestHeaders(r,p=>e.getHeader(p)),this._headerCapture.client.captureResponseHeaders(r,p=>a.headers[p]),ot.context.bind(ot.context.active(),a);let u=()=>{if(this._diag.debug("outgoingRequest on end()"),s)return;s=!0;let p;a.aborted&&!a.complete?p={code:ot.SpanStatusCode.ERROR}:p={code:(0,rn.parseResponseStatus)(ot.SpanKind.CLIENT,a.statusCode)},r.setStatus(p),this.getConfig().applyCustomAttributesOnSpan&&(0,po.safeExecuteInTheMiddle)(()=>this.getConfig().applyCustomAttributesOnSpan(r,e,a),()=>{},!0),this._closeHttpSpan(r,ot.SpanKind.CLIENT,n,o,i)};a.on("end",u),a.on(j8.errorMonitor,p=>{this._diag.debug("outgoingRequest on error()",p),!s&&(s=!0,(0,rn.setSpanWithError)(r,p,this._semconvStability),r.setStatus({code:ot.SpanStatusCode.ERROR,message:p.message}),this._closeHttpSpan(r,ot.SpanKind.CLIENT,n,o,i))})}),e.on("close",()=>{this._diag.debug("outgoingRequest on request close()"),!(e.aborted||s)&&(s=!0,this._closeHttpSpan(r,ot.SpanKind.CLIENT,n,o,i))}),e.on(j8.errorMonitor,a=>{this._diag.debug("outgoingRequest on request error()",a),!s&&(s=!0,(0,rn.setSpanWithError)(r,a,this._semconvStability),this._closeHttpSpan(r,ot.SpanKind.CLIENT,n,o,i))}),this._diag.debug("http.ClientRequest return request"),e}_incomingRequestFunction(e,r){let n=this;return function(i,...s){if(i!=="request")return r.apply(this,[i,...s]);let a=s[0],c=s[1],u=a.method||"GET";if(n._diag.debug(`${e} instrumentation incomingRequest`),(0,po.safeExecuteInTheMiddle)(()=>n.getConfig().ignoreIncomingRequestHook?.(a),I=>{I!=null&&n._diag.error("caught ignoreIncomingRequestHook error: ",I)},!0))return ot.context.with((0,rd.suppressTracing)(ot.context.active()),()=>(ot.context.bind(ot.context.active(),a),ot.context.bind(ot.context.active(),c),r.apply(this,[i,...s])));let p=a.headers,f=(0,rn.getIncomingRequestAttributes)(a,{component:e,serverName:n.getConfig().serverName,hookAttributes:n._callStartSpanHook(a,n.getConfig().startIncomingSpanHook),semconvStability:n._semconvStability,enableSyntheticSourceDetection:n.getConfig().enableSyntheticSourceDetection||!1},n._diag),m={kind:ot.SpanKind.SERVER,attributes:f},h=(0,rd.hrTime)(),_=(0,rn.getIncomingRequestMetricAttributes)(f),v={[gn.ATTR_HTTP_REQUEST_METHOD]:f[gn.ATTR_HTTP_REQUEST_METHOD],[gn.ATTR_URL_SCHEME]:f[gn.ATTR_URL_SCHEME]};f[gn.ATTR_NETWORK_PROTOCOL_VERSION]&&(v[gn.ATTR_NETWORK_PROTOCOL_VERSION]=f[gn.ATTR_NETWORK_PROTOCOL_VERSION]);let E=ot.propagation.extract(ot.ROOT_CONTEXT,p),x=n._startHttpSpan(u,m,E),w={type:rd.RPCType.HTTP,span:x};return ot.context.with((0,rd.setRPCMetadata)(ot.trace.setSpan(E,x),w),()=>{ot.context.bind(ot.context.active(),a),ot.context.bind(ot.context.active(),c),n.getConfig().requestHook&&n._callRequestHook(x,a),n.getConfig().responseHook&&n._callResponseHook(x,c),n._headerCapture.server.captureRequestHeaders(x,N=>a.headers[N]);let I=!1;return c.on("close",()=>{I||n._onServerResponseFinish(a,c,x,_,v,h)}),c.on(j8.errorMonitor,N=>{I=!0,n._onServerResponseError(x,_,v,h,N)}),(0,po.safeExecuteInTheMiddle)(()=>r.apply(this,[i,...s]),N=>{if(N)throw(0,rn.setSpanWithError)(x,N,n._semconvStability),n._closeHttpSpan(x,ot.SpanKind.SERVER,h,_,v),N})})}}_outgoingRequestFunction(e,r){let n=this;return function(i,...s){if(!(0,rn.isValidOptionsType)(i))return r.apply(this,[i,...s]);let a=typeof s[0]=="object"&&(typeof i=="string"||i instanceof LPt.URL)?s.shift():void 0,{method:c,invalidUrl:u,optionsParsed:p}=(0,rn.getRequestInfo)(n._diag,i,a);if((0,po.safeExecuteInTheMiddle)(()=>n.getConfig().ignoreOutgoingRequestHook?.(p),$=>{$!=null&&n._diag.error("caught ignoreOutgoingRequestHook error: ",$)},!0))return r.apply(this,[p,...s]);let{hostname:f,port:m}=(0,rn.extractHostnameAndPort)(p),h=(0,rn.getOutgoingRequestAttributes)(p,{component:e,port:m,hostname:f,hookAttributes:n._callStartSpanHook(p,n.getConfig().startOutgoingSpanHook)},n._semconvStability,n.getConfig().enableSyntheticSourceDetection||!1),_=(0,rd.hrTime)(),v=(0,rn.getOutgoingRequestMetricAttributes)(h),E={[gn.ATTR_HTTP_REQUEST_METHOD]:h[gn.ATTR_HTTP_REQUEST_METHOD],[gn.ATTR_SERVER_ADDRESS]:h[gn.ATTR_SERVER_ADDRESS],[gn.ATTR_SERVER_PORT]:h[gn.ATTR_SERVER_PORT]};h[gn.ATTR_HTTP_RESPONSE_STATUS_CODE]&&(E[gn.ATTR_HTTP_RESPONSE_STATUS_CODE]=h[gn.ATTR_HTTP_RESPONSE_STATUS_CODE]),h[gn.ATTR_NETWORK_PROTOCOL_VERSION]&&(E[gn.ATTR_NETWORK_PROTOCOL_VERSION]=h[gn.ATTR_NETWORK_PROTOCOL_VERSION]);let x={kind:ot.SpanKind.CLIENT,attributes:h},w=n._startHttpSpan(c,x),I=ot.context.active(),N=ot.trace.setSpan(I,w);return p.headers?p.headers=Object.assign({},p.headers):p.headers={},ot.propagation.inject(N,p.headers),ot.context.with(N,()=>{let $=s[s.length-1];typeof $=="function"&&(s[s.length-1]=ot.context.bind(I,$));let B=(0,po.safeExecuteInTheMiddle)(()=>u?r.apply(this,[i,...s]):r.apply(this,[p,...s]),G=>{if(G)throw(0,rn.setSpanWithError)(w,G,n._semconvStability),n._closeHttpSpan(w,ot.SpanKind.CLIENT,_,v,E),G});return n._diag.debug(`${e} instrumentation outgoingRequest`),ot.context.bind(I,B),n._traceClientRequest(B,w,_,v,E)})}}_onServerResponseFinish(e,r,n,o,i,s){let a=(0,rn.getIncomingRequestAttributesOnResponse)(e,r,this._semconvStability);o=Object.assign(o,(0,rn.getIncomingRequestMetricAttributesOnResponse)(a)),i=Object.assign(i,(0,rn.getIncomingStableRequestMetricAttributesOnResponse)(a)),this._headerCapture.server.captureResponseHeaders(n,u=>r.getHeader(u)),n.setAttributes(a).setStatus({code:(0,rn.parseResponseStatus)(ot.SpanKind.SERVER,r.statusCode)});let c=a[gn.ATTR_HTTP_ROUTE];c&&n.updateName(`${e.method||"GET"} ${c}`),this.getConfig().applyCustomAttributesOnSpan&&(0,po.safeExecuteInTheMiddle)(()=>this.getConfig().applyCustomAttributesOnSpan(n,e,r),()=>{},!0),this._closeHttpSpan(n,ot.SpanKind.SERVER,s,o,i)}_onServerResponseError(e,r,n,o,i){(0,rn.setSpanWithError)(e,i,this._semconvStability),this._closeHttpSpan(e,ot.SpanKind.SERVER,o,r,n)}_startHttpSpan(e,r,n=ot.context.active()){let o=r.kind===ot.SpanKind.CLIENT?this.getConfig().requireParentforOutgoingSpans:this.getConfig().requireParentforIncomingSpans,i,s=ot.trace.getSpan(n);return o===!0&&s===void 0?i=ot.trace.wrapSpanContext(ot.INVALID_SPAN_CONTEXT):o===!0&&s?.spanContext().isRemote?i=s:i=this.tracer.startSpan(e,r,n),this._spanNotEnded.add(i),i}_closeHttpSpan(e,r,n,o,i){if(!this._spanNotEnded.has(e))return;e.end(),this._spanNotEnded.delete(e);let s=(0,rd.hrTimeToMilliseconds)((0,rd.hrTimeDuration)(n,(0,rd.hrTime)()));r===ot.SpanKind.SERVER?this._recordServerDuration(s,o,i):r===ot.SpanKind.CLIENT&&this._recordClientDuration(s,o,i)}_callResponseHook(e,r){(0,po.safeExecuteInTheMiddle)(()=>this.getConfig().responseHook(e,r),()=>{},!0)}_callRequestHook(e,r){(0,po.safeExecuteInTheMiddle)(()=>this.getConfig().requestHook(e,r),()=>{},!0)}_callStartSpanHook(e,r){if(typeof r=="function")return(0,po.safeExecuteInTheMiddle)(()=>r(e),()=>{},!0)}_createHeaderCapture(){let e=this.getConfig();return{client:{captureRequestHeaders:(0,rn.headerCapture)("request",e.headersToSpanAttributes?.client?.requestHeaders??[]),captureResponseHeaders:(0,rn.headerCapture)("response",e.headersToSpanAttributes?.client?.responseHeaders??[])},server:{captureRequestHeaders:(0,rn.headerCapture)("request",e.headersToSpanAttributes?.server?.requestHeaders??[]),captureResponseHeaders:(0,rn.headerCapture)("response",e.headersToSpanAttributes?.server?.responseHeaders??[])}}}};VC.HttpInstrumentation=z8});var vAe=S(HC=>{"use strict";Object.defineProperty(HC,"__esModule",{value:!0});HC.HttpInstrumentation=void 0;var jPt=_Ae();Object.defineProperty(HC,"HttpInstrumentation",{enumerable:!0,get:function(){return jPt.HttpInstrumentation}})});var ZPe=S(F1=>{"use strict";Object.defineProperty(F1,"__esModule",{value:!0});F1.defaultServiceName=void 0;function HNt(){return`unknown_service:${process.argv0}`}F1.defaultServiceName=HNt});var YPe=S(q1=>{"use strict";Object.defineProperty(q1,"__esModule",{value:!0});q1.defaultServiceName=void 0;var WNt=ZPe();Object.defineProperty(q1,"defaultServiceName",{enumerable:!0,get:function(){return WNt.defaultServiceName}})});var cH=S(B1=>{"use strict";Object.defineProperty(B1,"__esModule",{value:!0});B1.defaultServiceName=void 0;var KNt=YPe();Object.defineProperty(B1,"defaultServiceName",{enumerable:!0,get:function(){return KNt.defaultServiceName}})});var JPe=S(q_=>{"use strict";Object.defineProperty(q_,"__esModule",{value:!0});q_.identity=q_.isPromiseLike=void 0;var ZNt=t=>t!==null&&typeof t=="object"&&typeof t.then=="function";q_.isPromiseLike=ZNt;function YNt(t){return t}q_.identity=YNt});var dH=S(lu=>{"use strict";Object.defineProperty(lu,"__esModule",{value:!0});lu.defaultResource=lu.emptyResource=lu.resourceFromDetectedResource=lu.resourceFromAttributes=void 0;var lH=(pe(),se(Pe)),uH=_r(),Tm=(er(),se(Cr)),JNt=cH(),Tx=JPe(),G1=class t{_rawAttributes;_asyncAttributesPending=!1;_memoizedAttributes;static FromAttributeList(e){let r=new t({});return r._rawAttributes=XPe(e),r._asyncAttributesPending=e.filter(([n,o])=>(0,Tx.isPromiseLike)(o)).length>0,r}constructor(e){let r=e.attributes??{};this._rawAttributes=Object.entries(r).map(([n,o])=>((0,Tx.isPromiseLike)(o)&&(this._asyncAttributesPending=!0),[n,o])),this._rawAttributes=XPe(this._rawAttributes)}get asyncAttributesPending(){return this._asyncAttributesPending}async waitForAsyncAttributes(){if(this.asyncAttributesPending){for(let e=0;e(0,Tx.isPromiseLike)(r)?[e,r.catch(n=>{lH.diag.debug("promise rejection for resource attribute: %s - %s",e,n)})]:[e,r])}});var eIe=S(V1=>{"use strict";Object.defineProperty(V1,"__esModule",{value:!0});V1.detectResources=void 0;var QPe=(pe(),se(Pe)),fH=dH(),tCt=(t={})=>(t.detectors||[]).map(r=>{try{let n=(0,fH.resourceFromDetectedResource)(r.detect(t));return QPe.diag.debug(`${r.constructor.name} found resource.`,n),n}catch(n){return QPe.diag.debug(`${r.constructor.name} failed: ${n.message}`),(0,fH.emptyResource)()}}).reduce((r,n)=>r.merge(n),(0,fH.emptyResource)());V1.detectResources=tCt});var rIe=S(H1=>{"use strict";Object.defineProperty(H1,"__esModule",{value:!0});H1.envDetector=void 0;var rCt=(pe(),se(Pe)),nCt=(er(),se(Cr)),tIe=_r(),mH=class{_MAX_LENGTH=255;_COMMA_SEPARATOR=",";_LABEL_KEY_VALUE_SPLITTER="=";_ERROR_MESSAGE_INVALID_CHARS="should be a ASCII string with a length greater than 0 and not exceed "+this._MAX_LENGTH+" characters.";_ERROR_MESSAGE_INVALID_VALUE="should be a ASCII string with a length not exceed "+this._MAX_LENGTH+" characters.";detect(e){let r={},n=(0,tIe.getStringFromEnv)("OTEL_RESOURCE_ATTRIBUTES"),o=(0,tIe.getStringFromEnv)("OTEL_SERVICE_NAME");if(n)try{let i=this._parseResourceAttributes(n);Object.assign(r,i)}catch(i){rCt.diag.debug(`EnvDetector failed: ${i.message}`)}return o&&(r[nCt.ATTR_SERVICE_NAME]=o),{attributes:r}}_parseResourceAttributes(e){if(!e)return{};let r={},n=e.split(this._COMMA_SEPARATOR,-1);for(let o of n){let i=o.split(this._LABEL_KEY_VALUE_SPLITTER,-1);if(i.length!==2)continue;let[s,a]=i;if(s=s.trim(),a=a.trim().split(/^"|"$/).join(""),!this._isValidAndNotEmpty(s))throw new Error(`Attribute key ${this._ERROR_MESSAGE_INVALID_CHARS}`);if(!this._isValid(a))throw new Error(`Attribute value ${this._ERROR_MESSAGE_INVALID_VALUE}`);r[s]=decodeURIComponent(a)}return r}_isValid(e){return e.length<=this._MAX_LENGTH&&this._isBaggageOctetString(e)}_isBaggageOctetString(e){for(let r=0;r126)return!1}return!0}_isValidAndNotEmpty(e){return e.length>0&&this._isValid(e)}};H1.envDetector=new mH});var bx=S(ye=>{"use strict";Object.defineProperty(ye,"__esModule",{value:!0});ye.ATTR_WEBENGINE_VERSION=ye.ATTR_WEBENGINE_NAME=ye.ATTR_WEBENGINE_DESCRIPTION=ye.ATTR_SERVICE_NAMESPACE=ye.ATTR_SERVICE_INSTANCE_ID=ye.ATTR_PROCESS_RUNTIME_VERSION=ye.ATTR_PROCESS_RUNTIME_NAME=ye.ATTR_PROCESS_RUNTIME_DESCRIPTION=ye.ATTR_PROCESS_PID=ye.ATTR_PROCESS_OWNER=ye.ATTR_PROCESS_EXECUTABLE_PATH=ye.ATTR_PROCESS_EXECUTABLE_NAME=ye.ATTR_PROCESS_COMMAND_ARGS=ye.ATTR_PROCESS_COMMAND=ye.ATTR_OS_VERSION=ye.ATTR_OS_TYPE=ye.ATTR_K8S_POD_NAME=ye.ATTR_K8S_NAMESPACE_NAME=ye.ATTR_K8S_DEPLOYMENT_NAME=ye.ATTR_K8S_CLUSTER_NAME=ye.ATTR_HOST_TYPE=ye.ATTR_HOST_NAME=ye.ATTR_HOST_IMAGE_VERSION=ye.ATTR_HOST_IMAGE_NAME=ye.ATTR_HOST_IMAGE_ID=ye.ATTR_HOST_ID=ye.ATTR_HOST_ARCH=ye.ATTR_CONTAINER_NAME=ye.ATTR_CONTAINER_IMAGE_TAGS=ye.ATTR_CONTAINER_IMAGE_NAME=ye.ATTR_CONTAINER_ID=ye.ATTR_CLOUD_REGION=ye.ATTR_CLOUD_PROVIDER=ye.ATTR_CLOUD_AVAILABILITY_ZONE=ye.ATTR_CLOUD_ACCOUNT_ID=void 0;ye.ATTR_CLOUD_ACCOUNT_ID="cloud.account.id";ye.ATTR_CLOUD_AVAILABILITY_ZONE="cloud.availability_zone";ye.ATTR_CLOUD_PROVIDER="cloud.provider";ye.ATTR_CLOUD_REGION="cloud.region";ye.ATTR_CONTAINER_ID="container.id";ye.ATTR_CONTAINER_IMAGE_NAME="container.image.name";ye.ATTR_CONTAINER_IMAGE_TAGS="container.image.tags";ye.ATTR_CONTAINER_NAME="container.name";ye.ATTR_HOST_ARCH="host.arch";ye.ATTR_HOST_ID="host.id";ye.ATTR_HOST_IMAGE_ID="host.image.id";ye.ATTR_HOST_IMAGE_NAME="host.image.name";ye.ATTR_HOST_IMAGE_VERSION="host.image.version";ye.ATTR_HOST_NAME="host.name";ye.ATTR_HOST_TYPE="host.type";ye.ATTR_K8S_CLUSTER_NAME="k8s.cluster.name";ye.ATTR_K8S_DEPLOYMENT_NAME="k8s.deployment.name";ye.ATTR_K8S_NAMESPACE_NAME="k8s.namespace.name";ye.ATTR_K8S_POD_NAME="k8s.pod.name";ye.ATTR_OS_TYPE="os.type";ye.ATTR_OS_VERSION="os.version";ye.ATTR_PROCESS_COMMAND="process.command";ye.ATTR_PROCESS_COMMAND_ARGS="process.command_args";ye.ATTR_PROCESS_EXECUTABLE_NAME="process.executable.name";ye.ATTR_PROCESS_EXECUTABLE_PATH="process.executable.path";ye.ATTR_PROCESS_OWNER="process.owner";ye.ATTR_PROCESS_PID="process.pid";ye.ATTR_PROCESS_RUNTIME_DESCRIPTION="process.runtime.description";ye.ATTR_PROCESS_RUNTIME_NAME="process.runtime.name";ye.ATTR_PROCESS_RUNTIME_VERSION="process.runtime.version";ye.ATTR_SERVICE_INSTANCE_ID="service.instance.id";ye.ATTR_SERVICE_NAMESPACE="service.namespace";ye.ATTR_WEBENGINE_DESCRIPTION="webengine.description";ye.ATTR_WEBENGINE_NAME="webengine.name";ye.ATTR_WEBENGINE_VERSION="webengine.version"});var K1=S(W1=>{"use strict";Object.defineProperty(W1,"__esModule",{value:!0});W1.execAsync=void 0;var oCt=require("child_process"),iCt=require("util");W1.execAsync=iCt.promisify(oCt.exec)});var nIe=S(Z1=>{"use strict";Object.defineProperty(Z1,"__esModule",{value:!0});Z1.getMachineId=void 0;var sCt=K1(),aCt=(pe(),se(Pe));async function cCt(){try{let e=(await(0,sCt.execAsync)('ioreg -rd1 -c "IOPlatformExpertDevice"')).stdout.split(` -`).find(n=>n.includes("IOPlatformUUID"));if(!e)return;let r=e.split('" = "');if(r.length===2)return r[1].slice(0,-1)}catch(t){aCt.diag.debug(`error reading machine id: ${t}`)}}Z1.getMachineId=cCt});var oIe=S(Y1=>{"use strict";Object.defineProperty(Y1,"__esModule",{value:!0});Y1.getMachineId=void 0;var uCt=require("fs"),lCt=(pe(),se(Pe));async function pCt(){let t=["/etc/machine-id","/var/lib/dbus/machine-id"];for(let e of t)try{return(await uCt.promises.readFile(e,{encoding:"utf8"})).trim()}catch(r){lCt.diag.debug(`error reading machine id: ${r}`)}}Y1.getMachineId=pCt});var sIe=S(J1=>{"use strict";Object.defineProperty(J1,"__esModule",{value:!0});J1.getMachineId=void 0;var dCt=require("fs"),fCt=K1(),iIe=(pe(),se(Pe));async function mCt(){try{return(await dCt.promises.readFile("/etc/hostid",{encoding:"utf8"})).trim()}catch(t){iIe.diag.debug(`error reading machine id: ${t}`)}try{return(await(0,fCt.execAsync)("kenv -q smbios.system.uuid")).stdout.trim()}catch(t){iIe.diag.debug(`error reading machine id: ${t}`)}}J1.getMachineId=mCt});var cIe=S(X1=>{"use strict";Object.defineProperty(X1,"__esModule",{value:!0});X1.getMachineId=void 0;var aIe=require("process"),hCt=K1(),gCt=(pe(),se(Pe));async function _Ct(){let t="QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid",e="%windir%\\System32\\REG.exe";aIe.arch==="ia32"&&"PROCESSOR_ARCHITEW6432"in aIe.env&&(e="%windir%\\sysnative\\cmd.exe /c "+e);try{let n=(await(0,hCt.execAsync)(`${e} ${t}`)).stdout.split("REG_SZ");if(n.length===2)return n[1].trim()}catch(r){gCt.diag.debug(`error reading machine id: ${r}`)}}X1.getMachineId=_Ct});var uIe=S(Q1=>{"use strict";Object.defineProperty(Q1,"__esModule",{value:!0});Q1.getMachineId=void 0;var vCt=(pe(),se(Pe));async function SCt(){vCt.diag.debug("could not read machine-id: unsupported platform")}Q1.getMachineId=SCt});var lIe=S(e$=>{"use strict";Object.defineProperty(e$,"__esModule",{value:!0});e$.getMachineId=void 0;var yCt=require("process"),bm;async function ECt(){if(!bm)switch(yCt.platform){case"darwin":bm=(await Promise.resolve().then(()=>W(nIe()))).getMachineId;break;case"linux":bm=(await Promise.resolve().then(()=>W(oIe()))).getMachineId;break;case"freebsd":bm=(await Promise.resolve().then(()=>W(sIe()))).getMachineId;break;case"win32":bm=(await Promise.resolve().then(()=>W(cIe()))).getMachineId;break;default:bm=(await Promise.resolve().then(()=>W(uIe()))).getMachineId;break}return bm()}e$.getMachineId=ECt});var hH=S(B_=>{"use strict";Object.defineProperty(B_,"__esModule",{value:!0});B_.normalizeType=B_.normalizeArch=void 0;var TCt=t=>{switch(t){case"arm":return"arm32";case"ppc":return"ppc32";case"x64":return"amd64";default:return t}};B_.normalizeArch=TCt;var bCt=t=>{switch(t){case"sunos":return"solaris";case"win32":return"windows";default:return t}};B_.normalizeType=bCt});var dIe=S(t$=>{"use strict";Object.defineProperty(t$,"__esModule",{value:!0});t$.hostDetector=void 0;var gH=bx(),pIe=require("os"),xCt=lIe(),ACt=hH(),_H=class{detect(e){return{attributes:{[gH.ATTR_HOST_NAME]:(0,pIe.hostname)(),[gH.ATTR_HOST_ARCH]:(0,ACt.normalizeArch)((0,pIe.arch)()),[gH.ATTR_HOST_ID]:(0,xCt.getMachineId)()}}}};t$.hostDetector=new _H});var hIe=S(r$=>{"use strict";Object.defineProperty(r$,"__esModule",{value:!0});r$.osDetector=void 0;var fIe=bx(),mIe=require("os"),wCt=hH(),vH=class{detect(e){return{attributes:{[fIe.ATTR_OS_TYPE]:(0,wCt.normalizeType)((0,mIe.platform)()),[fIe.ATTR_OS_VERSION]:(0,mIe.release)()}}}};r$.osDetector=new vH});var gIe=S(n$=>{"use strict";Object.defineProperty(n$,"__esModule",{value:!0});n$.processDetector=void 0;var RCt=(pe(),se(Pe)),xl=bx(),PCt=require("os"),SH=class{detect(e){let r={[xl.ATTR_PROCESS_PID]:process.pid,[xl.ATTR_PROCESS_EXECUTABLE_NAME]:process.title,[xl.ATTR_PROCESS_EXECUTABLE_PATH]:process.execPath,[xl.ATTR_PROCESS_COMMAND_ARGS]:[process.argv[0],...process.execArgv,...process.argv.slice(1)],[xl.ATTR_PROCESS_RUNTIME_VERSION]:process.versions.node,[xl.ATTR_PROCESS_RUNTIME_NAME]:"nodejs",[xl.ATTR_PROCESS_RUNTIME_DESCRIPTION]:"Node.js"};process.argv.length>1&&(r[xl.ATTR_PROCESS_COMMAND]=process.argv[1]);try{let n=PCt.userInfo();r[xl.ATTR_PROCESS_OWNER]=n.username}catch(n){RCt.diag.debug(`error obtaining process owner: ${n}`)}return{attributes:r}}};n$.processDetector=new SH});var _Ie=S(o$=>{"use strict";Object.defineProperty(o$,"__esModule",{value:!0});o$.serviceInstanceIdDetector=void 0;var ICt=bx(),OCt=require("crypto"),yH=class{detect(e){return{attributes:{[ICt.ATTR_SERVICE_INSTANCE_ID]:(0,OCt.randomUUID)()}}}};o$.serviceInstanceIdDetector=new yH});var vIe=S(pu=>{"use strict";Object.defineProperty(pu,"__esModule",{value:!0});pu.serviceInstanceIdDetector=pu.processDetector=pu.osDetector=pu.hostDetector=void 0;var NCt=dIe();Object.defineProperty(pu,"hostDetector",{enumerable:!0,get:function(){return NCt.hostDetector}});var CCt=hIe();Object.defineProperty(pu,"osDetector",{enumerable:!0,get:function(){return CCt.osDetector}});var $Ct=gIe();Object.defineProperty(pu,"processDetector",{enumerable:!0,get:function(){return $Ct.processDetector}});var kCt=_Ie();Object.defineProperty(pu,"serviceInstanceIdDetector",{enumerable:!0,get:function(){return kCt.serviceInstanceIdDetector}})});var SIe=S(du=>{"use strict";Object.defineProperty(du,"__esModule",{value:!0});du.serviceInstanceIdDetector=du.processDetector=du.osDetector=du.hostDetector=void 0;var i$=vIe();Object.defineProperty(du,"hostDetector",{enumerable:!0,get:function(){return i$.hostDetector}});Object.defineProperty(du,"osDetector",{enumerable:!0,get:function(){return i$.osDetector}});Object.defineProperty(du,"processDetector",{enumerable:!0,get:function(){return i$.processDetector}});Object.defineProperty(du,"serviceInstanceIdDetector",{enumerable:!0,get:function(){return i$.serviceInstanceIdDetector}})});var yIe=S(G_=>{"use strict";Object.defineProperty(G_,"__esModule",{value:!0});G_.noopDetector=G_.NoopDetector=void 0;var s$=class{detect(){return{attributes:{}}}};G_.NoopDetector=s$;G_.noopDetector=new s$});var EIe=S(es=>{"use strict";Object.defineProperty(es,"__esModule",{value:!0});es.noopDetector=es.serviceInstanceIdDetector=es.processDetector=es.osDetector=es.hostDetector=es.envDetector=void 0;var MCt=rIe();Object.defineProperty(es,"envDetector",{enumerable:!0,get:function(){return MCt.envDetector}});var a$=SIe();Object.defineProperty(es,"hostDetector",{enumerable:!0,get:function(){return a$.hostDetector}});Object.defineProperty(es,"osDetector",{enumerable:!0,get:function(){return a$.osDetector}});Object.defineProperty(es,"processDetector",{enumerable:!0,get:function(){return a$.processDetector}});Object.defineProperty(es,"serviceInstanceIdDetector",{enumerable:!0,get:function(){return a$.serviceInstanceIdDetector}});var DCt=yIe();Object.defineProperty(es,"noopDetector",{enumerable:!0,get:function(){return DCt.noopDetector}})});var TH=S(yn=>{"use strict";Object.defineProperty(yn,"__esModule",{value:!0});yn.defaultServiceName=yn.emptyResource=yn.defaultResource=yn.resourceFromAttributes=yn.serviceInstanceIdDetector=yn.processDetector=yn.osDetector=yn.hostDetector=yn.envDetector=yn.detectResources=void 0;var LCt=eIe();Object.defineProperty(yn,"detectResources",{enumerable:!0,get:function(){return LCt.detectResources}});var xx=EIe();Object.defineProperty(yn,"envDetector",{enumerable:!0,get:function(){return xx.envDetector}});Object.defineProperty(yn,"hostDetector",{enumerable:!0,get:function(){return xx.hostDetector}});Object.defineProperty(yn,"osDetector",{enumerable:!0,get:function(){return xx.osDetector}});Object.defineProperty(yn,"processDetector",{enumerable:!0,get:function(){return xx.processDetector}});Object.defineProperty(yn,"serviceInstanceIdDetector",{enumerable:!0,get:function(){return xx.serviceInstanceIdDetector}});var EH=dH();Object.defineProperty(yn,"resourceFromAttributes",{enumerable:!0,get:function(){return EH.resourceFromAttributes}});Object.defineProperty(yn,"defaultResource",{enumerable:!0,get:function(){return EH.defaultResource}});Object.defineProperty(yn,"emptyResource",{enumerable:!0,get:function(){return EH.emptyResource}});var UCt=cH();Object.defineProperty(yn,"defaultServiceName",{enumerable:!0,get:function(){return UCt.defaultServiceName}})});var TIe=S(c$=>{"use strict";Object.defineProperty(c$,"__esModule",{value:!0});c$.ExceptionEventName=void 0;c$.ExceptionEventName="exception"});var bIe=S(u$=>{"use strict";Object.defineProperty(u$,"__esModule",{value:!0});u$.SpanImpl=void 0;var _a=(pe(),se(Pe)),ni=_r(),xm=(er(),se(Cr)),jCt=TIe(),bH=class{_spanContext;kind;parentSpanContext;attributes={};links=[];events=[];startTime;resource;instrumentationScope;_droppedAttributesCount=0;_droppedEventsCount=0;_droppedLinksCount=0;name;status={code:_a.SpanStatusCode.UNSET};endTime=[0,0];_ended=!1;_duration=[-1,-1];_spanProcessor;_spanLimits;_attributeValueLengthLimit;_performanceStartTime;_performanceOffset;_startTimeProvided;constructor(e){let r=Date.now();this._spanContext=e.spanContext,this._performanceStartTime=ni.otperformance.now(),this._performanceOffset=r-(this._performanceStartTime+(0,ni.getTimeOrigin)()),this._startTimeProvided=e.startTime!=null,this._spanLimits=e.spanLimits,this._attributeValueLengthLimit=this._spanLimits.attributeValueLengthLimit||0,this._spanProcessor=e.spanProcessor,this.name=e.name,this.parentSpanContext=e.parentSpanContext,this.kind=e.kind,this.links=e.links||[],this.startTime=this._getTime(e.startTime??r),this.resource=e.resource,this.instrumentationScope=e.scope,e.attributes!=null&&this.setAttributes(e.attributes),this._spanProcessor.onStart(this,e.context)}spanContext(){return this._spanContext}setAttribute(e,r){if(r==null||this._isSpanEnded())return this;if(e.length===0)return _a.diag.warn(`Invalid attribute key: ${e}`),this;if(!(0,ni.isAttributeValue)(r))return _a.diag.warn(`Invalid attribute value set for key: ${e}`),this;let{attributeCountLimit:n}=this._spanLimits;return n!==void 0&&Object.keys(this.attributes).length>=n&&!Object.prototype.hasOwnProperty.call(this.attributes,e)?(this._droppedAttributesCount++,this):(this.attributes[e]=this._truncateToSize(r),this)}setAttributes(e){for(let[r,n]of Object.entries(e))this.setAttribute(r,n);return this}addEvent(e,r,n){if(this._isSpanEnded())return this;let{eventCountLimit:o}=this._spanLimits;if(o===0)return _a.diag.warn("No events allowed."),this._droppedEventsCount++,this;o!==void 0&&this.events.length>=o&&(this._droppedEventsCount===0&&_a.diag.debug("Dropping extra events."),this.events.shift(),this._droppedEventsCount++),(0,ni.isTimeInput)(r)&&((0,ni.isTimeInput)(n)||(n=r),r=void 0);let i=(0,ni.sanitizeAttributes)(r);return this.events.push({name:e,attributes:i,time:this._getTime(n),droppedAttributesCount:0}),this}addLink(e){return this.links.push(e),this}addLinks(e){return this.links.push(...e),this}setStatus(e){return this._isSpanEnded()?this:(this.status={...e},this.status.message!=null&&typeof e.message!="string"&&(_a.diag.warn(`Dropping invalid status.message of type '${typeof e.message}', expected 'string'`),delete this.status.message),this)}updateName(e){return this._isSpanEnded()?this:(this.name=e,this)}end(e){if(this._isSpanEnded()){_a.diag.error(`${this.name} ${this._spanContext.traceId}-${this._spanContext.spanId} - You can only call end() on a span once.`);return}this._ended=!0,this.endTime=this._getTime(e),this._duration=(0,ni.hrTimeDuration)(this.startTime,this.endTime),this._duration[0]<0&&(_a.diag.warn("Inconsistent start and end time, startTime > endTime. Setting span duration to 0ms.",this.startTime,this.endTime),this.endTime=this.startTime.slice(),this._duration=[0,0]),this._droppedEventsCount>0&&_a.diag.warn(`Dropped ${this._droppedEventsCount} events because eventCountLimit reached`),this._spanProcessor.onEnd(this)}_getTime(e){if(typeof e=="number"&&e<=ni.otperformance.now())return(0,ni.hrTime)(e+this._performanceOffset);if(typeof e=="number")return(0,ni.millisToHrTime)(e);if(e instanceof Date)return(0,ni.millisToHrTime)(e.getTime());if((0,ni.isTimeInputHrTime)(e))return e;if(this._startTimeProvided)return(0,ni.millisToHrTime)(Date.now());let r=ni.otperformance.now()-this._performanceStartTime;return(0,ni.addHrTimes)(this.startTime,(0,ni.millisToHrTime)(r))}isRecording(){return this._ended===!1}recordException(e,r){let n={};typeof e=="string"?n[xm.ATTR_EXCEPTION_MESSAGE]=e:e&&(e.code?n[xm.ATTR_EXCEPTION_TYPE]=e.code.toString():e.name&&(n[xm.ATTR_EXCEPTION_TYPE]=e.name),e.message&&(n[xm.ATTR_EXCEPTION_MESSAGE]=e.message),e.stack&&(n[xm.ATTR_EXCEPTION_STACKTRACE]=e.stack)),n[xm.ATTR_EXCEPTION_TYPE]||n[xm.ATTR_EXCEPTION_MESSAGE]?this.addEvent(jCt.ExceptionEventName,n,r):_a.diag.warn(`Failed to record an exception ${e}`)}get duration(){return this._duration}get ended(){return this._ended}get droppedAttributesCount(){return this._droppedAttributesCount}get droppedEventsCount(){return this._droppedEventsCount}get droppedLinksCount(){return this._droppedLinksCount}_isSpanEnded(){if(this._ended){let e=new Error(`Operation attempted on ended Span {traceId: ${this._spanContext.traceId}, spanId: ${this._spanContext.spanId}}`);_a.diag.warn(`Cannot execute the operation on ended Span {traceId: ${this._spanContext.traceId}, spanId: ${this._spanContext.spanId}}`,e)}return this._ended}_truncateToLimitUtil(e,r){return e.length<=r?e:e.substring(0,r)}_truncateToSize(e){let r=this._attributeValueLengthLimit;return r<=0?(_a.diag.warn(`Attribute value limit must be positive, got ${r}`),e):typeof e=="string"?this._truncateToLimitUtil(e,r):Array.isArray(e)?e.map(n=>typeof n=="string"?this._truncateToLimitUtil(n,r):n):e}};u$.SpanImpl=bH});var wx=S(Ax=>{"use strict";Object.defineProperty(Ax,"__esModule",{value:!0});Ax.SamplingDecision=void 0;var zCt;(function(t){t[t.NOT_RECORD=0]="NOT_RECORD",t[t.RECORD=1]="RECORD",t[t.RECORD_AND_SAMPLED=2]="RECORD_AND_SAMPLED"})(zCt=Ax.SamplingDecision||(Ax.SamplingDecision={}))});var p$=S(l$=>{"use strict";Object.defineProperty(l$,"__esModule",{value:!0});l$.AlwaysOffSampler=void 0;var FCt=wx(),xH=class{shouldSample(){return{decision:FCt.SamplingDecision.NOT_RECORD}}toString(){return"AlwaysOffSampler"}};l$.AlwaysOffSampler=xH});var f$=S(d$=>{"use strict";Object.defineProperty(d$,"__esModule",{value:!0});d$.AlwaysOnSampler=void 0;var qCt=wx(),AH=class{shouldSample(){return{decision:qCt.SamplingDecision.RECORD_AND_SAMPLED}}toString(){return"AlwaysOnSampler"}};d$.AlwaysOnSampler=AH});var PH=S(h$=>{"use strict";Object.defineProperty(h$,"__esModule",{value:!0});h$.ParentBasedSampler=void 0;var m$=(pe(),se(Pe)),BCt=_r(),xIe=p$(),wH=f$(),RH=class{_root;_remoteParentSampled;_remoteParentNotSampled;_localParentSampled;_localParentNotSampled;constructor(e){this._root=e.root,this._root||((0,BCt.globalErrorHandler)(new Error("ParentBasedSampler must have a root sampler configured")),this._root=new wH.AlwaysOnSampler),this._remoteParentSampled=e.remoteParentSampled??new wH.AlwaysOnSampler,this._remoteParentNotSampled=e.remoteParentNotSampled??new xIe.AlwaysOffSampler,this._localParentSampled=e.localParentSampled??new wH.AlwaysOnSampler,this._localParentNotSampled=e.localParentNotSampled??new xIe.AlwaysOffSampler}shouldSample(e,r,n,o,i,s){let a=m$.trace.getSpanContext(e);return!a||!(0,m$.isSpanContextValid)(a)?this._root.shouldSample(e,r,n,o,i,s):a.isRemote?a.traceFlags&m$.TraceFlags.SAMPLED?this._remoteParentSampled.shouldSample(e,r,n,o,i,s):this._remoteParentNotSampled.shouldSample(e,r,n,o,i,s):a.traceFlags&m$.TraceFlags.SAMPLED?this._localParentSampled.shouldSample(e,r,n,o,i,s):this._localParentNotSampled.shouldSample(e,r,n,o,i,s)}toString(){return`ParentBased{root=${this._root.toString()}, remoteParentSampled=${this._remoteParentSampled.toString()}, remoteParentNotSampled=${this._remoteParentNotSampled.toString()}, localParentSampled=${this._localParentSampled.toString()}, localParentNotSampled=${this._localParentNotSampled.toString()}}`}};h$.ParentBasedSampler=RH});var OH=S(g$=>{"use strict";Object.defineProperty(g$,"__esModule",{value:!0});g$.TraceIdRatioBasedSampler=void 0;var GCt=(pe(),se(Pe)),AIe=wx(),IH=class{_ratio;_upperBound;constructor(e=0){this._ratio=e,this._ratio=this._normalize(e),this._upperBound=Math.floor(this._ratio*4294967295)}shouldSample(e,r){return{decision:(0,GCt.isValidTraceId)(r)&&this._accumulate(r)=1?1:e<=0?0:e}_accumulate(e){let r=0;for(let n=0;n>>0}return r}};g$.TraceIdRatioBasedSampler=IH});var $H=S(V_=>{"use strict";Object.defineProperty(V_,"__esModule",{value:!0});V_.buildSamplerFromEnv=V_.loadDefaultConfig=void 0;var CH=(pe(),se(Pe)),fu=_r(),wIe=p$(),NH=f$(),_$=PH(),RIe=OH(),v$=1;function VCt(){return{sampler:IIe(),forceFlushTimeoutMillis:3e4,generalLimits:{attributeValueLengthLimit:(0,fu.getNumberFromEnv)("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT")??1/0,attributeCountLimit:(0,fu.getNumberFromEnv)("OTEL_ATTRIBUTE_COUNT_LIMIT")??128},spanLimits:{attributeValueLengthLimit:(0,fu.getNumberFromEnv)("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT")??1/0,attributeCountLimit:(0,fu.getNumberFromEnv)("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT")??128,linkCountLimit:(0,fu.getNumberFromEnv)("OTEL_SPAN_LINK_COUNT_LIMIT")??128,eventCountLimit:(0,fu.getNumberFromEnv)("OTEL_SPAN_EVENT_COUNT_LIMIT")??128,attributePerEventCountLimit:(0,fu.getNumberFromEnv)("OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT")??128,attributePerLinkCountLimit:(0,fu.getNumberFromEnv)("OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT")??128}}}V_.loadDefaultConfig=VCt;function IIe(){let t=(0,fu.getStringFromEnv)("OTEL_TRACES_SAMPLER")??"parentbased_always_on";switch(t){case"always_on":return new NH.AlwaysOnSampler;case"always_off":return new wIe.AlwaysOffSampler;case"parentbased_always_on":return new _$.ParentBasedSampler({root:new NH.AlwaysOnSampler});case"parentbased_always_off":return new _$.ParentBasedSampler({root:new wIe.AlwaysOffSampler});case"traceidratio":return new RIe.TraceIdRatioBasedSampler(PIe());case"parentbased_traceidratio":return new _$.ParentBasedSampler({root:new RIe.TraceIdRatioBasedSampler(PIe())});default:return CH.diag.error(`OTEL_TRACES_SAMPLER value "${t}" invalid, defaulting to "parentbased_always_on".`),new _$.ParentBasedSampler({root:new NH.AlwaysOnSampler})}}V_.buildSamplerFromEnv=IIe;function PIe(){let t=(0,fu.getNumberFromEnv)("OTEL_TRACES_SAMPLER_ARG");return t==null?(CH.diag.error(`OTEL_TRACES_SAMPLER_ARG is blank, defaulting to ${v$}.`),v$):t<0||t>1?(CH.diag.error(`OTEL_TRACES_SAMPLER_ARG=${t} was given, but it is out of range ([0..1]), defaulting to ${v$}.`),v$):t}});var kH=S(va=>{"use strict";Object.defineProperty(va,"__esModule",{value:!0});va.reconfigureLimits=va.mergeConfig=va.DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT=va.DEFAULT_ATTRIBUTE_COUNT_LIMIT=void 0;var OIe=$H(),S$=_r();va.DEFAULT_ATTRIBUTE_COUNT_LIMIT=128;va.DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT=1/0;function HCt(t){let e={sampler:(0,OIe.buildSamplerFromEnv)()},r=(0,OIe.loadDefaultConfig)(),n=Object.assign({},r,e,t);return n.generalLimits=Object.assign({},r.generalLimits,t.generalLimits||{}),n.spanLimits=Object.assign({},r.spanLimits,t.spanLimits||{}),n}va.mergeConfig=HCt;function WCt(t){let e=Object.assign({},t.spanLimits);return e.attributeCountLimit=t.spanLimits?.attributeCountLimit??t.generalLimits?.attributeCountLimit??(0,S$.getNumberFromEnv)("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT")??(0,S$.getNumberFromEnv)("OTEL_ATTRIBUTE_COUNT_LIMIT")??va.DEFAULT_ATTRIBUTE_COUNT_LIMIT,e.attributeValueLengthLimit=t.spanLimits?.attributeValueLengthLimit??t.generalLimits?.attributeValueLengthLimit??(0,S$.getNumberFromEnv)("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT")??(0,S$.getNumberFromEnv)("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT")??va.DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT,Object.assign({},t,{spanLimits:e})}va.reconfigureLimits=WCt});var NIe=S(y$=>{"use strict";Object.defineProperty(y$,"__esModule",{value:!0});y$.BatchSpanProcessorBase=void 0;var H_=(pe(),se(Pe)),mu=_r(),MH=class{_exporter;_maxExportBatchSize;_maxQueueSize;_scheduledDelayMillis;_exportTimeoutMillis;_isExporting=!1;_finishedSpans=[];_timer;_shutdownOnce;_droppedSpansCount=0;constructor(e,r){this._exporter=e,this._maxExportBatchSize=typeof r?.maxExportBatchSize=="number"?r.maxExportBatchSize:(0,mu.getNumberFromEnv)("OTEL_BSP_MAX_EXPORT_BATCH_SIZE")??512,this._maxQueueSize=typeof r?.maxQueueSize=="number"?r.maxQueueSize:(0,mu.getNumberFromEnv)("OTEL_BSP_MAX_QUEUE_SIZE")??2048,this._scheduledDelayMillis=typeof r?.scheduledDelayMillis=="number"?r.scheduledDelayMillis:(0,mu.getNumberFromEnv)("OTEL_BSP_SCHEDULE_DELAY")??5e3,this._exportTimeoutMillis=typeof r?.exportTimeoutMillis=="number"?r.exportTimeoutMillis:(0,mu.getNumberFromEnv)("OTEL_BSP_EXPORT_TIMEOUT")??3e4,this._shutdownOnce=new mu.BindOnceFuture(this._shutdown,this),this._maxExportBatchSize>this._maxQueueSize&&(H_.diag.warn("BatchSpanProcessor: maxExportBatchSize must be smaller or equal to maxQueueSize, setting maxExportBatchSize to match maxQueueSize"),this._maxExportBatchSize=this._maxQueueSize)}forceFlush(){return this._shutdownOnce.isCalled?this._shutdownOnce.promise:this._flushAll()}onStart(e,r){}onEnd(e){this._shutdownOnce.isCalled||(e.spanContext().traceFlags&H_.TraceFlags.SAMPLED)!==0&&this._addToBuffer(e)}shutdown(){return this._shutdownOnce.call()}_shutdown(){return Promise.resolve().then(()=>this.onShutdown()).then(()=>this._flushAll()).then(()=>this._exporter.shutdown())}_addToBuffer(e){if(this._finishedSpans.length>=this._maxQueueSize){this._droppedSpansCount===0&&H_.diag.debug("maxQueueSize reached, dropping spans"),this._droppedSpansCount++;return}this._droppedSpansCount>0&&(H_.diag.warn(`Dropped ${this._droppedSpansCount} spans because maxQueueSize reached`),this._droppedSpansCount=0),this._finishedSpans.push(e),this._maybeStartTimer()}_flushAll(){return new Promise((e,r)=>{let n=[],o=Math.ceil(this._finishedSpans.length/this._maxExportBatchSize);for(let i=0,s=o;i{e()}).catch(r)})}_flushOneBatch(){return this._clearTimer(),this._finishedSpans.length===0?Promise.resolve():new Promise((e,r)=>{let n=setTimeout(()=>{r(new Error("Timeout"))},this._exportTimeoutMillis);H_.context.with((0,mu.suppressTracing)(H_.context.active()),()=>{let o;this._finishedSpans.length<=this._maxExportBatchSize?(o=this._finishedSpans,this._finishedSpans=[]):o=this._finishedSpans.splice(0,this._maxExportBatchSize);let i=()=>this._exporter.export(o,a=>{clearTimeout(n),a.code===mu.ExportResultCode.SUCCESS?e():r(a.error??new Error("BatchSpanProcessor: span export failed"))}),s=null;for(let a=0,c=o.length;a{(0,mu.globalErrorHandler)(a),r(a)})})})}_maybeStartTimer(){if(this._isExporting)return;let e=()=>{this._isExporting=!0,this._flushOneBatch().finally(()=>{this._isExporting=!1,this._finishedSpans.length>0&&(this._clearTimer(),this._maybeStartTimer())}).catch(r=>{this._isExporting=!1,(0,mu.globalErrorHandler)(r)})};if(this._finishedSpans.length>=this._maxExportBatchSize)return e();this._timer===void 0&&(this._timer=setTimeout(()=>e(),this._scheduledDelayMillis),(0,mu.unrefTimer)(this._timer))}_clearTimer(){this._timer!==void 0&&(clearTimeout(this._timer),this._timer=void 0)}};y$.BatchSpanProcessorBase=MH});var CIe=S(E$=>{"use strict";Object.defineProperty(E$,"__esModule",{value:!0});E$.BatchSpanProcessor=void 0;var KCt=NIe(),DH=class extends KCt.BatchSpanProcessorBase{onShutdown(){}};E$.BatchSpanProcessor=DH});var MIe=S(b$=>{"use strict";Object.defineProperty(b$,"__esModule",{value:!0});b$.RandomIdGenerator=void 0;var ZCt=8,kIe=16,LH=class{generateTraceId=$Ie(kIe);generateSpanId=$Ie(ZCt)};b$.RandomIdGenerator=LH;var T$=Buffer.allocUnsafe(kIe);function $Ie(t){return function(){for(let r=0;r>>0,r*4);for(let r=0;r0);r++)r===t-1&&(T$[t-1]=1);return T$.toString("hex",0,t)}}});var DIe=S(W_=>{"use strict";Object.defineProperty(W_,"__esModule",{value:!0});W_.RandomIdGenerator=W_.BatchSpanProcessor=void 0;var YCt=CIe();Object.defineProperty(W_,"BatchSpanProcessor",{enumerable:!0,get:function(){return YCt.BatchSpanProcessor}});var JCt=MIe();Object.defineProperty(W_,"RandomIdGenerator",{enumerable:!0,get:function(){return JCt.RandomIdGenerator}})});var UH=S(K_=>{"use strict";Object.defineProperty(K_,"__esModule",{value:!0});K_.RandomIdGenerator=K_.BatchSpanProcessor=void 0;var LIe=DIe();Object.defineProperty(K_,"BatchSpanProcessor",{enumerable:!0,get:function(){return LIe.BatchSpanProcessor}});Object.defineProperty(K_,"RandomIdGenerator",{enumerable:!0,get:function(){return LIe.RandomIdGenerator}})});var UIe=S(A$=>{"use strict";Object.defineProperty(A$,"__esModule",{value:!0});A$.Tracer=void 0;var $o=(pe(),se(Pe)),x$=_r(),XCt=bIe(),QCt=kH(),e1t=UH(),jH=class{_sampler;_generalLimits;_spanLimits;_idGenerator;instrumentationScope;_resource;_spanProcessor;constructor(e,r,n,o){let i=(0,QCt.mergeConfig)(r);this._sampler=i.sampler,this._generalLimits=i.generalLimits,this._spanLimits=i.spanLimits,this._idGenerator=r.idGenerator||new e1t.RandomIdGenerator,this._resource=n,this._spanProcessor=o,this.instrumentationScope=e}startSpan(e,r={},n=$o.context.active()){r.root&&(n=$o.trace.deleteSpan(n));let o=$o.trace.getSpan(n);if((0,x$.isTracingSuppressed)(n))return $o.diag.debug("Instrumentation suppressed, returning Noop Span"),$o.trace.wrapSpanContext($o.INVALID_SPAN_CONTEXT);let i=o?.spanContext(),s=this._idGenerator.generateSpanId(),a,c,u;!i||!$o.trace.isSpanContextValid(i)?c=this._idGenerator.generateTraceId():(c=i.traceId,u=i.traceState,a=i);let p=r.kind??$o.SpanKind.INTERNAL,f=(r.links??[]).map(w=>({context:w.context,attributes:(0,x$.sanitizeAttributes)(w.attributes)})),m=(0,x$.sanitizeAttributes)(r.attributes),h=this._sampler.shouldSample(n,c,e,p,m,f);u=h.traceState??u;let _=h.decision===$o.SamplingDecision.RECORD_AND_SAMPLED?$o.TraceFlags.SAMPLED:$o.TraceFlags.NONE,v={traceId:c,spanId:s,traceFlags:_,traceState:u};if(h.decision===$o.SamplingDecision.NOT_RECORD)return $o.diag.debug("Recording is off, propagating context in a non-recording span"),$o.trace.wrapSpanContext(v);let E=(0,x$.sanitizeAttributes)(Object.assign(m,h.attributes));return new XCt.SpanImpl({resource:this._resource,scope:this.instrumentationScope,context:n,spanContext:v,name:e,kind:p,links:f,parentSpanContext:a,attributes:E,startTime:r.startTime,spanProcessor:this._spanProcessor,spanLimits:this._spanLimits})}startActiveSpan(e,r,n,o){let i,s,a;if(arguments.length<2)return;arguments.length===2?a=r:arguments.length===3?(i=r,a=n):(i=r,s=n,a=o);let c=s??$o.context.active(),u=this.startSpan(e,i,c),p=$o.trace.setSpan(c,u);return $o.context.with(p,a,void 0,u)}getGeneralLimits(){return this._generalLimits}getSpanLimits(){return this._spanLimits}};A$.Tracer=jH});var jIe=S(w$=>{"use strict";Object.defineProperty(w$,"__esModule",{value:!0});w$.MultiSpanProcessor=void 0;var t1t=_r(),zH=class{_spanProcessors;constructor(e){this._spanProcessors=e}forceFlush(){let e=[];for(let r of this._spanProcessors)e.push(r.forceFlush());return new Promise(r=>{Promise.all(e).then(()=>{r()}).catch(n=>{(0,t1t.globalErrorHandler)(n||new Error("MultiSpanProcessor: forceFlush failed")),r()})})}onStart(e,r){for(let n of this._spanProcessors)n.onStart(e,r)}onEnd(e){for(let r of this._spanProcessors)r.onEnd(e)}shutdown(){let e=[];for(let r of this._spanProcessors)e.push(r.shutdown());return new Promise((r,n)=>{Promise.all(e).then(()=>{r()},n)})}};w$.MultiSpanProcessor=zH});var zIe=S(Am=>{"use strict";Object.defineProperty(Am,"__esModule",{value:!0});Am.BasicTracerProvider=Am.ForceFlushState=void 0;var r1t=_r(),n1t=TH(),o1t=UIe(),i1t=$H(),s1t=jIe(),a1t=kH(),Z_;(function(t){t[t.resolved=0]="resolved",t[t.timeout=1]="timeout",t[t.error=2]="error",t[t.unresolved=3]="unresolved"})(Z_=Am.ForceFlushState||(Am.ForceFlushState={}));var FH=class{_config;_tracers=new Map;_resource;_activeSpanProcessor;constructor(e={}){let r=(0,r1t.merge)({},(0,i1t.loadDefaultConfig)(),(0,a1t.reconfigureLimits)(e));this._resource=r.resource??(0,n1t.defaultResource)(),this._config=Object.assign({},r,{resource:this._resource});let n=[];e.spanProcessors?.length&&n.push(...e.spanProcessors),this._activeSpanProcessor=new s1t.MultiSpanProcessor(n)}getTracer(e,r,n){let o=`${e}@${r||""}:${n?.schemaUrl||""}`;return this._tracers.has(o)||this._tracers.set(o,new o1t.Tracer({name:e,version:r,schemaUrl:n?.schemaUrl},this._config,this._resource,this._activeSpanProcessor)),this._tracers.get(o)}forceFlush(){let e=this._config.forceFlushTimeoutMillis,r=this._activeSpanProcessor._spanProcessors.map(n=>new Promise(o=>{let i,s=setTimeout(()=>{o(new Error(`Span processor did not completed within timeout period of ${e} ms`)),i=Z_.timeout},e);n.forceFlush().then(()=>{clearTimeout(s),i!==Z_.timeout&&(i=Z_.resolved,o(i))}).catch(a=>{clearTimeout(s),i=Z_.error,o(a)})}));return new Promise((n,o)=>{Promise.all(r).then(i=>{let s=i.filter(a=>a!==Z_.resolved);s.length>0?o(s):n()}).catch(i=>o([i]))})}shutdown(){return this._activeSpanProcessor.shutdown()}};Am.BasicTracerProvider=FH});var FIe=S(R$=>{"use strict";Object.defineProperty(R$,"__esModule",{value:!0});R$.ConsoleSpanExporter=void 0;var qH=_r(),BH=class{export(e,r){return this._sendSpans(e,r)}shutdown(){return this._sendSpans([]),this.forceFlush()}forceFlush(){return Promise.resolve()}_exportInfo(e){return{resource:{attributes:e.resource.attributes},instrumentationScope:e.instrumentationScope,traceId:e.spanContext().traceId,parentSpanContext:e.parentSpanContext,traceState:e.spanContext().traceState?.serialize(),name:e.name,id:e.spanContext().spanId,kind:e.kind,timestamp:(0,qH.hrTimeToMicroseconds)(e.startTime),duration:(0,qH.hrTimeToMicroseconds)(e.duration),attributes:e.attributes,status:e.status,events:e.events,links:e.links}}_sendSpans(e,r){for(let n of e)console.dir(this._exportInfo(n),{depth:3});if(r)return r({code:qH.ExportResultCode.SUCCESS})}};R$.ConsoleSpanExporter=BH});var BIe=S(P$=>{"use strict";Object.defineProperty(P$,"__esModule",{value:!0});P$.InMemorySpanExporter=void 0;var qIe=_r(),GH=class{_finishedSpans=[];_stopped=!1;export(e,r){if(this._stopped)return r({code:qIe.ExportResultCode.FAILED,error:new Error("Exporter has been stopped")});this._finishedSpans.push(...e),setTimeout(()=>r({code:qIe.ExportResultCode.SUCCESS}),0)}shutdown(){return this._stopped=!0,this._finishedSpans=[],this.forceFlush()}forceFlush(){return Promise.resolve()}reset(){this._finishedSpans=[]}getFinishedSpans(){return this._finishedSpans}};P$.InMemorySpanExporter=GH});var GIe=S(O$=>{"use strict";Object.defineProperty(O$,"__esModule",{value:!0});O$.SimpleSpanProcessor=void 0;var c1t=(pe(),se(Pe)),I$=_r(),VH=class{_exporter;_shutdownOnce;_pendingExports;constructor(e){this._exporter=e,this._shutdownOnce=new I$.BindOnceFuture(this._shutdown,this),this._pendingExports=new Set}async forceFlush(){await Promise.all(Array.from(this._pendingExports)),this._exporter.forceFlush&&await this._exporter.forceFlush()}onStart(e,r){}onEnd(e){if(this._shutdownOnce.isCalled||(e.spanContext().traceFlags&c1t.TraceFlags.SAMPLED)===0)return;let r=this._doExport(e).catch(n=>(0,I$.globalErrorHandler)(n));this._pendingExports.add(r),r.finally(()=>this._pendingExports.delete(r))}async _doExport(e){e.resource.asyncAttributesPending&&await e.resource.waitForAsyncAttributes?.();let r=await I$.internal._export(this._exporter,[e]);if(r.code!==I$.ExportResultCode.SUCCESS)throw r.error??new Error(`SimpleSpanProcessor: span export failed (status ${r})`)}shutdown(){return this._shutdownOnce.call()}_shutdown(){return this._exporter.shutdown()}};O$.SimpleSpanProcessor=VH});var VIe=S(N$=>{"use strict";Object.defineProperty(N$,"__esModule",{value:!0});N$.NoopSpanProcessor=void 0;var HH=class{onStart(e,r){}onEnd(e){}shutdown(){return Promise.resolve()}forceFlush(){return Promise.resolve()}};N$.NoopSpanProcessor=HH});var WH=S(Gr=>{"use strict";Object.defineProperty(Gr,"__esModule",{value:!0});Gr.SamplingDecision=Gr.TraceIdRatioBasedSampler=Gr.ParentBasedSampler=Gr.AlwaysOnSampler=Gr.AlwaysOffSampler=Gr.NoopSpanProcessor=Gr.SimpleSpanProcessor=Gr.InMemorySpanExporter=Gr.ConsoleSpanExporter=Gr.RandomIdGenerator=Gr.BatchSpanProcessor=Gr.BasicTracerProvider=void 0;var u1t=zIe();Object.defineProperty(Gr,"BasicTracerProvider",{enumerable:!0,get:function(){return u1t.BasicTracerProvider}});var HIe=UH();Object.defineProperty(Gr,"BatchSpanProcessor",{enumerable:!0,get:function(){return HIe.BatchSpanProcessor}});Object.defineProperty(Gr,"RandomIdGenerator",{enumerable:!0,get:function(){return HIe.RandomIdGenerator}});var l1t=FIe();Object.defineProperty(Gr,"ConsoleSpanExporter",{enumerable:!0,get:function(){return l1t.ConsoleSpanExporter}});var p1t=BIe();Object.defineProperty(Gr,"InMemorySpanExporter",{enumerable:!0,get:function(){return p1t.InMemorySpanExporter}});var d1t=GIe();Object.defineProperty(Gr,"SimpleSpanProcessor",{enumerable:!0,get:function(){return d1t.SimpleSpanProcessor}});var f1t=VIe();Object.defineProperty(Gr,"NoopSpanProcessor",{enumerable:!0,get:function(){return f1t.NoopSpanProcessor}});var m1t=p$();Object.defineProperty(Gr,"AlwaysOffSampler",{enumerable:!0,get:function(){return m1t.AlwaysOffSampler}});var h1t=f$();Object.defineProperty(Gr,"AlwaysOnSampler",{enumerable:!0,get:function(){return h1t.AlwaysOnSampler}});var g1t=PH();Object.defineProperty(Gr,"ParentBasedSampler",{enumerable:!0,get:function(){return g1t.ParentBasedSampler}});var _1t=OH();Object.defineProperty(Gr,"TraceIdRatioBasedSampler",{enumerable:!0,get:function(){return _1t.TraceIdRatioBasedSampler}});var v1t=wx();Object.defineProperty(Gr,"SamplingDecision",{enumerable:!0,get:function(){return v1t.SamplingDecision}})});var y3=S(F$=>{"use strict";Object.defineProperty(F$,"__esModule",{value:!0});F$.AbstractAsyncHooksContextManager=void 0;var lkt=require("events"),pkt=["addListener","on","once","prependListener","prependOnceListener"],S3=class{bind(e,r){return r instanceof lkt.EventEmitter?this._bindEventEmitter(e,r):typeof r=="function"?this._bindFunction(e,r):r}_bindFunction(e,r){let n=this,o=function(...i){return n.with(e,()=>r.apply(this,i))};return Object.defineProperty(o,"length",{enumerable:!1,configurable:!0,writable:!1,value:r.length}),o}_bindEventEmitter(e,r){return this._getPatchMap(r)!==void 0||(this._createPatchMap(r),pkt.forEach(o=>{r[o]!==void 0&&(r[o]=this._patchAddListener(r,r[o],e))}),typeof r.removeListener=="function"&&(r.removeListener=this._patchRemoveListener(r,r.removeListener)),typeof r.off=="function"&&(r.off=this._patchRemoveListener(r,r.off)),typeof r.removeAllListeners=="function"&&(r.removeAllListeners=this._patchRemoveAllListeners(r,r.removeAllListeners))),r}_patchRemoveListener(e,r){let n=this;return function(o,i){let s=n._getPatchMap(e)?.[o];if(s===void 0)return r.call(this,o,i);let a=s.get(i);return r.call(this,o,a||i)}}_patchRemoveAllListeners(e,r){let n=this;return function(o){let i=n._getPatchMap(e);return i!==void 0&&(arguments.length===0?n._createPatchMap(e):i[o]!==void 0&&delete i[o]),r.apply(this,arguments)}}_patchAddListener(e,r,n){let o=this;return function(i,s){if(o._wrapped)return r.call(this,i,s);let a=o._getPatchMap(e);a===void 0&&(a=o._createPatchMap(e));let c=a[i];c===void 0&&(c=new WeakMap,a[i]=c);let u=o.bind(n,s);c.set(s,u),o._wrapped=!0;try{return r.call(this,i,u)}finally{o._wrapped=!1}}}_createPatchMap(e){let r=Object.create(null);return e[this._kOtListeners]=r,r}_getPatchMap(e){return e[this._kOtListeners]}_kOtListeners=Symbol("OtListeners");_wrapped=!1};F$.AbstractAsyncHooksContextManager=S3});var uNe=S(q$=>{"use strict";Object.defineProperty(q$,"__esModule",{value:!0});q$.AsyncHooksContextManager=void 0;var dkt=(pe(),se(Pe)),fkt=require("async_hooks"),mkt=y3(),E3=class extends mkt.AbstractAsyncHooksContextManager{_asyncHook;_contexts=new Map;_stack=[];constructor(){super(),this._asyncHook=fkt.createHook({init:this._init.bind(this),before:this._before.bind(this),after:this._after.bind(this),destroy:this._destroy.bind(this),promiseResolve:this._destroy.bind(this)})}active(){return this._stack[this._stack.length-1]??dkt.ROOT_CONTEXT}with(e,r,n,...o){this._enterContext(e);try{return r.call(n,...o)}finally{this._exitContext()}}enable(){return this._asyncHook.enable(),this}disable(){return this._asyncHook.disable(),this._contexts.clear(),this._stack=[],this}_init(e,r){if(r==="TIMERWRAP")return;let n=this._stack[this._stack.length-1];n!==void 0&&this._contexts.set(e,n)}_destroy(e){this._contexts.delete(e)}_before(e){let r=this._contexts.get(e);r!==void 0&&this._enterContext(r)}_after(){this._exitContext()}_enterContext(e){this._stack.push(e)}_exitContext(){this._stack.pop()}};q$.AsyncHooksContextManager=E3});var lNe=S(B$=>{"use strict";Object.defineProperty(B$,"__esModule",{value:!0});B$.AsyncLocalStorageContextManager=void 0;var hkt=(pe(),se(Pe)),gkt=require("async_hooks"),_kt=y3(),T3=class extends _kt.AbstractAsyncHooksContextManager{_asyncLocalStorage;constructor(){super(),this._asyncLocalStorage=new gkt.AsyncLocalStorage}active(){return this._asyncLocalStorage.getStore()??hkt.ROOT_CONTEXT}with(e,r,n,...o){let i=n==null?r:r.bind(n);return this._asyncLocalStorage.run(e,i,...o)}enable(){return this}disable(){return this._asyncLocalStorage.disable(),this}};B$.AsyncLocalStorageContextManager=T3});var pNe=S(rv=>{"use strict";Object.defineProperty(rv,"__esModule",{value:!0});rv.AsyncLocalStorageContextManager=rv.AsyncHooksContextManager=void 0;var vkt=uNe();Object.defineProperty(rv,"AsyncHooksContextManager",{enumerable:!0,get:function(){return vkt.AsyncHooksContextManager}});var Skt=lNe();Object.defineProperty(rv,"AsyncLocalStorageContextManager",{enumerable:!0,get:function(){return Skt.AsyncLocalStorageContextManager}})});var NNe=S(nv=>{"use strict";Object.defineProperty(nv,"__esModule",{value:!0});nv.PACKAGE_NAME=nv.PACKAGE_VERSION=void 0;nv.PACKAGE_VERSION="0.14.0";nv.PACKAGE_NAME="@opentelemetry/instrumentation-undici"});var CNe=S(J$=>{"use strict";Object.defineProperty(J$,"__esModule",{value:!0});J$.SemanticAttributes=void 0;J$.SemanticAttributes={HTTP_CONNECTION_STATE:"http.connection.state",ERROR_TYPE:"error.type",HTTP_REQUEST_BODY_SIZE:"http.request.body.size",HTTP_REQUEST_METHOD:"http.request.method",HTTP_REQUEST_METHOD_ORIGINAL:"http.request.method_original",HTTP_REQUEST_RESEND_COUNT:"http.request.resend_count",HTTP_RESPONSE_BODY_SIZE:"http.response.body.size",HTTP_RESPONSE_STATUS_CODE:"http.response.status_code",HTTP_ROUTE:"http.route",NETWORK_PEER_ADDRESS:"network.peer.address",NETWORK_PEER_PORT:"network.peer.port",NETWORK_PROTOCOL_NAME:"network.protocol.name",NETWORK_PROTOCOL_VERSION:"network.protocol.version",SERVER_ADDRESS:"server.address",SERVER_PORT:"server.port",URL_FULL:"url.full",URL_PATH:"url.path",URL_QUERY:"url.query",URL_SCHEME:"url.scheme",USER_AGENT_ORIGINAL:"user_agent.original"}});var kNe=S(Q$=>{"use strict";Object.defineProperty(Q$,"__esModule",{value:!0});Q$.UndiciInstrumentation=void 0;var O3=require("diagnostics_channel"),Gkt=require("url"),jx=Ft(),Ps=(pe(),se(Pe)),$Ne=NNe(),Xn=CNe(),X$=_r(),N3=class extends jx.InstrumentationBase{_recordFromReq=new WeakMap;constructor(e={}){super($Ne.PACKAGE_NAME,$Ne.PACKAGE_VERSION,e)}init(){}disable(){super.disable(),this._channelSubs.forEach(e=>e.unsubscribe()),this._channelSubs.length=0}enable(){super.enable(),this._channelSubs=this._channelSubs||[],!(this._channelSubs.length>0)&&(this.subscribeToChannel("undici:request:create",this.onRequestCreated.bind(this)),this.subscribeToChannel("undici:client:sendHeaders",this.onRequestHeaders.bind(this)),this.subscribeToChannel("undici:request:headers",this.onResponseHeaders.bind(this)),this.subscribeToChannel("undici:request:trailers",this.onDone.bind(this)),this.subscribeToChannel("undici:request:error",this.onError.bind(this)))}_updateMetricInstruments(){this._httpClientDurationHistogram=this.meter.createHistogram("http.client.request.duration",{description:"Measures the duration of outbound HTTP requests.",unit:"s",valueType:Ps.ValueType.DOUBLE,advice:{explicitBucketBoundaries:[.005,.01,.025,.05,.075,.1,.25,.5,.75,1,2.5,5,7.5,10]}})}subscribeToChannel(e,r){let[n,o]=process.version.replace("v","").split(".").map(a=>Number(a)),i=n>18||n===18&&o>=19,s;if(i)O3.subscribe?.(e,r),s=()=>O3.unsubscribe?.(e,r);else{let a=O3.channel(e);a.subscribe(r),s=()=>a.unsubscribe(r)}this._channelSubs.push({name:e,unsubscribe:s})}parseRequestHeaders(e){let r=new Map;if(Array.isArray(e.headers))for(let n=0;n!n||e.method==="CONNECT"||r.ignoreRequestHook?.(e),B=>B&&this._diag.error("caught ignoreRequestHook error: ",B),!0))return;let i=(0,X$.hrTime)(),s;try{s=new Gkt.URL(e.path,e.origin)}catch(B){this._diag.warn("could not determine url.full:",B);return}let a=s.protocol.replace(":",""),c=this.getRequestMethod(e.method),u={[Xn.SemanticAttributes.HTTP_REQUEST_METHOD]:c,[Xn.SemanticAttributes.HTTP_REQUEST_METHOD_ORIGINAL]:e.method,[Xn.SemanticAttributes.URL_FULL]:s.toString(),[Xn.SemanticAttributes.URL_PATH]:s.pathname,[Xn.SemanticAttributes.URL_QUERY]:s.search,[Xn.SemanticAttributes.URL_SCHEME]:a},p={https:"443",http:"80"},f=s.hostname,m=s.port||p[a];u[Xn.SemanticAttributes.SERVER_ADDRESS]=f,m&&!isNaN(Number(m))&&(u[Xn.SemanticAttributes.SERVER_PORT]=Number(m));let _=this.parseRequestHeaders(e).get("user-agent");if(_){let B=Array.isArray(_)?_[_.length-1]:_;u[Xn.SemanticAttributes.USER_AGENT_ORIGINAL]=B}let v=(0,jx.safeExecuteInTheMiddle)(()=>r.startSpanHook?.(e),B=>B&&this._diag.error("caught startSpanHook error: ",B),!0);v&&Object.entries(v).forEach(([B,G])=>{u[B]=G});let E=Ps.context.active(),x=Ps.trace.getSpan(E),w;r.requireParentforSpans&&(!x||!Ps.trace.isSpanContextValid(x.spanContext()))?w=Ps.trace.wrapSpanContext(Ps.INVALID_SPAN_CONTEXT):w=this.tracer.startSpan(c==="_OTHER"?"HTTP":c,{kind:Ps.SpanKind.CLIENT,attributes:u},E),(0,jx.safeExecuteInTheMiddle)(()=>r.requestHook?.(w,e),B=>B&&this._diag.error("caught requestHook error: ",B),!0);let I=Ps.trace.setSpan(Ps.context.active(),w),N={};Ps.propagation.inject(I,N);let $=Object.entries(N);for(let B=0;B<$.length;B++){let[G,he]=$[B];typeof e.addHeader=="function"?e.addHeader(G,he):typeof e.headers=="string"?e.headers+=`${G}: ${he}\r -`:Array.isArray(e.headers)&&e.headers.push(G,he)}this._recordFromReq.set(e,{span:w,attributes:u,startTime:i})}onRequestHeaders({request:e,socket:r}){let n=this._recordFromReq.get(e);if(!n)return;let o=this.getConfig(),{span:i}=n,{remoteAddress:s,remotePort:a}=r,c={[Xn.SemanticAttributes.NETWORK_PEER_ADDRESS]:s,[Xn.SemanticAttributes.NETWORK_PEER_PORT]:a};if(o.headersToSpanAttributes?.requestHeaders){let u=new Set(o.headersToSpanAttributes.requestHeaders.map(f=>f.toLowerCase())),p=this.parseRequestHeaders(e);for(let[f,m]of p.entries())if(u.has(f)){let h=Array.isArray(m)?m.join(", "):m;c[`http.request.header.${f}`]=h}}i.setAttributes(c)}onResponseHeaders({request:e,response:r}){let n=this._recordFromReq.get(e);if(!n)return;let{span:o,attributes:i}=n,s={[Xn.SemanticAttributes.HTTP_RESPONSE_STATUS_CODE]:r.statusCode},a=this.getConfig();(0,jx.safeExecuteInTheMiddle)(()=>a.responseHook?.(o,{request:e,response:r}),u=>u&&this._diag.error("caught responseHook error: ",u),!0);let c=new Set;a.headersToSpanAttributes?.responseHeaders&&a.headersToSpanAttributes?.responseHeaders.forEach(u=>c.add(u.toLowerCase()));for(let u=0;u=400?Ps.SpanStatusCode.ERROR:Ps.SpanStatusCode.UNSET}),n.attributes=Object.assign(i,s)}onDone({request:e}){let r=this._recordFromReq.get(e);if(!r)return;let{span:n,attributes:o,startTime:i}=r;n.end(),this._recordFromReq.delete(e),this.recordRequestDuration(o,i)}onError({request:e,error:r}){let n=this._recordFromReq.get(e);if(!n)return;let{span:o,attributes:i,startTime:s}=n;o.recordException(r),o.setStatus({code:Ps.SpanStatusCode.ERROR,message:r.message}),o.end(),this._recordFromReq.delete(e),i[Xn.SemanticAttributes.ERROR_TYPE]=r.message,this.recordRequestDuration(i,s)}recordRequestDuration(e,r){let n={};[Xn.SemanticAttributes.HTTP_RESPONSE_STATUS_CODE,Xn.SemanticAttributes.HTTP_REQUEST_METHOD,Xn.SemanticAttributes.SERVER_ADDRESS,Xn.SemanticAttributes.SERVER_PORT,Xn.SemanticAttributes.URL_SCHEME,Xn.SemanticAttributes.ERROR_TYPE].forEach(s=>{s in e&&(n[s]=e[s])});let i=(0,X$.hrTimeToMilliseconds)((0,X$.hrTimeDuration)(r,(0,X$.hrTime)()))/1e3;this._httpClientDurationHistogram.record(i,n)}getRequestMethod(e){let r={CONNECT:!0,OPTIONS:!0,HEAD:!0,GET:!0,POST:!0,PUT:!0,PATCH:!0,DELETE:!0,TRACE:!0};return e.toUpperCase()in r?e.toUpperCase():"_OTHER"}};Q$.UndiciInstrumentation=N3});var MNe=S(ek=>{"use strict";Object.defineProperty(ek,"__esModule",{value:!0});ek.UndiciInstrumentation=void 0;var Vkt=kNe();Object.defineProperty(ek,"UndiciInstrumentation",{enumerable:!0,get:function(){return Vkt.UndiciInstrumentation}})});var tk=S(zx=>{"use strict";Object.defineProperty(zx,"__esModule",{value:!0});zx.ExpressLayerType=void 0;var Xkt;(function(t){t.ROUTER="router",t.MIDDLEWARE="middleware",t.REQUEST_HANDLER="request_handler"})(Xkt=zx.ExpressLayerType||(zx.ExpressLayerType={}))});var rk=S(Fx=>{"use strict";Object.defineProperty(Fx,"__esModule",{value:!0});Fx.AttributeNames=void 0;var Qkt;(function(t){t.EXPRESS_TYPE="express.type",t.EXPRESS_NAME="express.name"})(Qkt=Fx.AttributeNames||(Fx.AttributeNames={}))});var C3=S(ov=>{"use strict";Object.defineProperty(ov,"__esModule",{value:!0});ov._LAYERS_STORE_PROPERTY=ov.kLayerPatched=void 0;ov.kLayerPatched=Symbol("express-layer-patched");ov._LAYERS_STORE_PROPERTY="__ot_middlewares"});var FNe=S(Fn=>{"use strict";Object.defineProperty(Fn,"__esModule",{value:!0});Fn.getActualMatchedRoute=Fn.getConstructedRoute=Fn.getLayerPath=Fn.asErrorAndMessage=Fn.isLayerIgnored=Fn.getLayerMetadata=Fn.getRouterPath=Fn.storeLayerPath=void 0;var $3=tk(),iv=rk(),Im=C3(),eMt=(t,e)=>(Array.isArray(t[Im._LAYERS_STORE_PROPERTY])===!1&&Object.defineProperty(t,Im._LAYERS_STORE_PROPERTY,{enumerable:!1,value:[]}),e===void 0?{isLayerPathStored:!1}:(t[Im._LAYERS_STORE_PROPERTY].push(e),{isLayerPathStored:!0}));Fn.storeLayerPath=eMt;var tMt=(t,e)=>{let r=e.handle?.stack?.[0];return r?.route?.path?`${t}${r.route.path}`:r?.handle?.stack?(0,Fn.getRouterPath)(t,r):t};Fn.getRouterPath=tMt;var rMt=(t,e,r)=>{if(e.name==="router"){let n=(0,Fn.getRouterPath)("",e),o=n||r||t||"/";return{attributes:{[iv.AttributeNames.EXPRESS_NAME]:o,[iv.AttributeNames.EXPRESS_TYPE]:$3.ExpressLayerType.ROUTER},name:`router - ${o}`}}else return e.name==="bound dispatch"||e.name==="handle"?{attributes:{[iv.AttributeNames.EXPRESS_NAME]:(t||r)??"request handler",[iv.AttributeNames.EXPRESS_TYPE]:$3.ExpressLayerType.REQUEST_HANDLER},name:`request handler${e.path?` - ${t||r}`:""}`}:{attributes:{[iv.AttributeNames.EXPRESS_NAME]:e.name,[iv.AttributeNames.EXPRESS_TYPE]:$3.ExpressLayerType.MIDDLEWARE},name:`middleware - ${e.name}`}};Fn.getLayerMetadata=rMt;var nMt=(t,e)=>{if(typeof e=="string")return e===t;if(e instanceof RegExp)return e.test(t);if(typeof e=="function")return e(t);throw new TypeError("Pattern is in unsupported datatype")},oMt=(t,e,r)=>{if(Array.isArray(r?.ignoreLayersType)&&r?.ignoreLayersType?.includes(e))return!0;if(Array.isArray(r?.ignoreLayers)===!1)return!1;try{for(let n of r.ignoreLayers)if(nMt(t,n))return!0}catch{}return!1};Fn.isLayerIgnored=oMt;var iMt=t=>t instanceof Error?[t,t.message]:[String(t),String(t)];Fn.asErrorAndMessage=iMt;var sMt=t=>{let e=t[0];return Array.isArray(e)?e.map(r=>jNe(r)||"").join(","):jNe(e)};Fn.getLayerPath=sMt;var jNe=t=>{if(typeof t=="string")return t;if(t instanceof RegExp||typeof t=="number")return t.toString()};function zNe(t){let r=(Array.isArray(t[Im._LAYERS_STORE_PROPERTY])?t[Im._LAYERS_STORE_PROPERTY]:[]).filter(n=>n!=="/"&&n!=="/*");return r.length===1&&r[0]==="*"?"*":r.join("").replace(/\/{2,}/g,"/")}Fn.getConstructedRoute=zNe;function aMt(t){let e=Array.isArray(t[Im._LAYERS_STORE_PROPERTY])?t[Im._LAYERS_STORE_PROPERTY]:[];if(e.length===0)return;if(e.every(i=>i==="/"))return t.originalUrl==="/"?"/":void 0;let r=zNe(t);if(r==="*"||r.includes("/")&&(r.includes(",")||r.includes("\\")||r.includes("*")||r.includes("[")))return r;let n=r.startsWith("/")?r:`/${r}`;return n.length>0&&(t.originalUrl===n||t.originalUrl.startsWith(n)||cMt(n))?n:void 0}Fn.getActualMatchedRoute=aMt;function cMt(t){return t.includes(":")||t.includes("*")}});var qNe=S(sv=>{"use strict";Object.defineProperty(sv,"__esModule",{value:!0});sv.PACKAGE_NAME=sv.PACKAGE_VERSION=void 0;sv.PACKAGE_VERSION="0.52.0";sv.PACKAGE_NAME="@opentelemetry/instrumentation-express"});var WNe=S(ok=>{"use strict";Object.defineProperty(ok,"__esModule",{value:!0});ok.ExpressInstrumentation=void 0;var BNe=_r(),bc=(pe(),se(Pe)),GNe=tk(),VNe=rk(),Su=FNe(),HNe=qNe(),av=Ft(),uMt=(er(),se(Cr)),nk=C3(),k3=class extends av.InstrumentationBase{constructor(e={}){super(HNe.PACKAGE_NAME,HNe.PACKAGE_VERSION,e)}init(){return[new av.InstrumentationNodeModuleDefinition("express",[">=4.0.0 <6"],e=>{let r=typeof e?.Router?.prototype?.route=="function",n=r?e.Router.prototype:e.Router;return(0,av.isWrapped)(n.route)&&this._unwrap(n,"route"),this._wrap(n,"route",this._getRoutePatch()),(0,av.isWrapped)(n.use)&&this._unwrap(n,"use"),this._wrap(n,"use",this._getRouterUsePatch()),(0,av.isWrapped)(e.application.use)&&this._unwrap(e.application,"use"),this._wrap(e.application,"use",this._getAppUsePatch(r)),e},e=>{if(e===void 0)return;let n=typeof e?.Router?.prototype?.route=="function"?e.Router.prototype:e.Router;this._unwrap(n,"route"),this._unwrap(n,"use"),this._unwrap(e.application,"use")})]}_getRoutePatch(){let e=this;return function(r){return function(...o){let i=r.apply(this,o),s=this.stack[this.stack.length-1];return e._applyPatch(s,(0,Su.getLayerPath)(o)),i}}}_getRouterUsePatch(){let e=this;return function(r){return function(...o){let i=r.apply(this,o),s=this.stack[this.stack.length-1];return e._applyPatch(s,(0,Su.getLayerPath)(o)),i}}}_getAppUsePatch(e){let r=this;return function(n){return function(...i){let s=e?this.router:this._router,a=n.apply(this,i);if(s){let c=s.stack[s.stack.length-1];r._applyPatch(c,(0,Su.getLayerPath)(i))}return a}}}_applyPatch(e,r){let n=this;e[nk.kLayerPatched]!==!0&&(e[nk.kLayerPatched]=!0,this._wrap(e,"handle",o=>{if(o.length===4)return o;let i=function(s,a){let{isLayerPathStored:c}=(0,Su.storeLayerPath)(s,r),u=(0,Su.getConstructedRoute)(s),p=(0,Su.getActualMatchedRoute)(s),f={[uMt.ATTR_HTTP_ROUTE]:p},m=(0,Su.getLayerMetadata)(u,e,r),h=m.attributes[VNe.AttributeNames.EXPRESS_TYPE],_=(0,BNe.getRPCMetadata)(bc.context.active());if(_?.type===BNe.RPCType.HTTP&&(_.route=p),(0,Su.isLayerIgnored)(m.name,h,n.getConfig()))return h===GNe.ExpressLayerType.MIDDLEWARE&&s[nk._LAYERS_STORE_PROPERTY].pop(),o.apply(this,arguments);if(bc.trace.getSpan(bc.context.active())===void 0)return o.apply(this,arguments);let v=n._getSpanName({request:s,layerType:h,route:u},m.name),E=n.tracer.startSpan(v,{attributes:Object.assign(f,m.attributes)}),x=bc.context.active(),w=bc.trace.setSpan(x,E),{requestHook:I}=n.getConfig();I&&(0,av.safeExecuteInTheMiddle)(()=>I(E,{request:s,layerType:h,route:u}),he=>{he&&bc.diag.error("express instrumentation: request hook failed",he)},!0);let N=!1;m.attributes[VNe.AttributeNames.EXPRESS_TYPE]===GNe.ExpressLayerType.ROUTER&&(E.end(),N=!0,w=x);let $=()=>{N===!1&&(N=!0,E.end())},B=Array.from(arguments),G=B.findIndex(he=>typeof he=="function");G>=0&&(arguments[G]=function(){let he=arguments[0],Q=![void 0,null,"route","router"].includes(he);if(!N&&Q){let[J,Oe]=(0,Su.asErrorAndMessage)(he);E.recordException(J),E.setStatus({code:bc.SpanStatusCode.ERROR,message:Oe})}N===!1&&(N=!0,s.res?.removeListener("finish",$),E.end()),!(s.route&&Q)&&c&&s[nk._LAYERS_STORE_PROPERTY].pop();let me=B[G];return bc.context.bind(x,me).apply(this,arguments)});try{return bc.context.bind(w,o).apply(this,arguments)}catch(he){let[Q,me]=(0,Su.asErrorAndMessage)(he);throw E.recordException(Q),E.setStatus({code:bc.SpanStatusCode.ERROR,message:me}),he}finally{N||a.once("finish",$)}};for(let s in o)Object.defineProperty(i,s,{get(){return o[s]},set(a){o[s]=a}});return i}))}_getSpanName(e,r){let{spanNameHook:n}=this.getConfig();if(!(n instanceof Function))return r;try{return n(e,r)??r}catch(o){return bc.diag.error("express instrumentation: error calling span name rewrite hook",o),r}}};ok.ExpressInstrumentation=k3});var KNe=S(yd=>{"use strict";Object.defineProperty(yd,"__esModule",{value:!0});yd.AttributeNames=yd.ExpressLayerType=yd.ExpressInstrumentation=void 0;var lMt=WNe();Object.defineProperty(yd,"ExpressInstrumentation",{enumerable:!0,get:function(){return lMt.ExpressInstrumentation}});var pMt=tk();Object.defineProperty(yd,"ExpressLayerType",{enumerable:!0,get:function(){return pMt.ExpressLayerType}});var dMt=rk();Object.defineProperty(yd,"AttributeNames",{enumerable:!0,get:function(){return dMt.AttributeNames}})});var nCe=S((NPr,rCe)=>{"use strict";rCe.exports=eCe;function eCe(t,e,r){t instanceof RegExp&&(t=QNe(t,r)),e instanceof RegExp&&(e=QNe(e,r));var n=tCe(t,e,r);return n&&{start:n[0],end:n[1],pre:r.slice(0,n[0]),body:r.slice(n[0]+t.length,n[1]),post:r.slice(n[1]+e.length)}}function QNe(t,e){var r=e.match(t);return r?r[0]:null}eCe.range=tCe;function tCe(t,e,r){var n,o,i,s,a,c=r.indexOf(t),u=r.indexOf(e,c+1),p=c;if(c>=0&&u>0){if(t===e)return[c,u];for(n=[],i=r.length;p>=0&&!a;)p==c?(n.push(p),c=r.indexOf(t,p+1)):n.length==1?a=[n.pop(),u]:(o=n.pop(),o=0?c:u;n.length&&(a=[i,s])}return a}});var pCe=S((CPr,lCe)=>{var oCe=nCe();lCe.exports=vMt;var iCe="\0SLASH"+Math.random()+"\0",sCe="\0OPEN"+Math.random()+"\0",L3="\0CLOSE"+Math.random()+"\0",aCe="\0COMMA"+Math.random()+"\0",cCe="\0PERIOD"+Math.random()+"\0";function D3(t){return parseInt(t,10)==t?parseInt(t,10):t.charCodeAt(0)}function gMt(t){return t.split("\\\\").join(iCe).split("\\{").join(sCe).split("\\}").join(L3).split("\\,").join(aCe).split("\\.").join(cCe)}function _Mt(t){return t.split(iCe).join("\\").split(sCe).join("{").split(L3).join("}").split(aCe).join(",").split(cCe).join(".")}function uCe(t){if(!t)return[""];var e=[],r=oCe("{","}",t);if(!r)return t.split(",");var n=r.pre,o=r.body,i=r.post,s=n.split(",");s[s.length-1]+="{"+o+"}";var a=uCe(i);return i.length&&(s[s.length-1]+=a.shift(),s.push.apply(s,a)),e.push.apply(e,s),e}function vMt(t){return t?(t.substr(0,2)==="{}"&&(t="\\{\\}"+t.substr(2)),qx(gMt(t),!0).map(_Mt)):[]}function SMt(t){return"{"+t+"}"}function yMt(t){return/^-?0\d/.test(t)}function EMt(t,e){return t<=e}function TMt(t,e){return t>=e}function qx(t,e){var r=[],n=oCe("{","}",t);if(!n)return[t];var o=n.pre,i=n.post.length?qx(n.post,!1):[""];if(/\$$/.test(n.pre))for(var s=0;s=0;if(!p&&!f)return n.post.match(/,(?!,).*\}/)?(t=n.pre+"{"+n.body+L3+n.post,qx(t)):[t];var m;if(p)m=n.body.split(/\.\./);else if(m=uCe(n.body),m.length===1&&(m=qx(m[0],!1).map(SMt),m.length===1))return i.map(function(me){return n.pre+m[0]+me});var h;if(p){var _=D3(m[0]),v=D3(m[1]),E=Math.max(m[0].length,m[1].length),x=m.length==3?Math.abs(D3(m[2])):1,w=EMt,I=v<_;I&&(x*=-1,w=TMt);var N=m.some(yMt);h=[];for(var $=_;w($,v);$+=x){var B;if(u)B=String.fromCharCode($),B==="\\"&&(B="");else if(B=String($),N){var G=E-B.length;if(G>0){var he=new Array(G+1).join("0");$<0?B="-"+he+B.slice(1):B=he+B}}h.push(B)}}else{h=[];for(var Q=0;Q{"use strict";Object.defineProperty(xc,"__esModule",{value:!0});xc.SpanNames=xc.TokenKind=xc.AllowedOperationTypes=void 0;var vDt;(function(t){t.QUERY="query",t.MUTATION="mutation",t.SUBSCRIPTION="subscription"})(vDt=xc.AllowedOperationTypes||(xc.AllowedOperationTypes={}));var SDt;(function(t){t.SOF="",t.EOF="",t.BANG="!",t.DOLLAR="$",t.AMP="&",t.PAREN_L="(",t.PAREN_R=")",t.SPREAD="...",t.COLON=":",t.EQUALS="=",t.AT="@",t.BRACKET_L="[",t.BRACKET_R="]",t.BRACE_L="{",t.PIPE="|",t.BRACE_R="}",t.NAME="Name",t.INT="Int",t.FLOAT="Float",t.STRING="String",t.BLOCK_STRING="BlockString",t.COMMENT="Comment"})(SDt=xc.TokenKind||(xc.TokenKind={}));var yDt;(function(t){t.EXECUTE="graphql.execute",t.PARSE="graphql.parse",t.RESOLVE="graphql.resolve",t.VALIDATE="graphql.validate",t.SCHEMA_VALIDATE="graphql.validateSchema",t.SCHEMA_PARSE="graphql.parseSchema"})(yDt=xc.SpanNames||(xc.SpanNames={}))});var G3=S(Zx=>{"use strict";Object.defineProperty(Zx,"__esModule",{value:!0});Zx.AttributeNames=void 0;var EDt;(function(t){t.SOURCE="graphql.source",t.FIELD_NAME="graphql.field.name",t.FIELD_PATH="graphql.field.path",t.FIELD_TYPE="graphql.field.type",t.OPERATION_TYPE="graphql.operation.type",t.OPERATION_NAME="graphql.operation.name",t.VARIABLES="graphql.variables.",t.ERROR_VALIDATION_NAME="graphql.validation.error"})(EDt=Zx.AttributeNames||(Zx.AttributeNames={}))});var dk=S(lv=>{"use strict";Object.defineProperty(lv,"__esModule",{value:!0});lv.OTEL_GRAPHQL_DATA_SYMBOL=lv.OTEL_PATCHED_SYMBOL=void 0;lv.OTEL_PATCHED_SYMBOL=Symbol.for("opentelemetry.patched");lv.OTEL_GRAPHQL_DATA_SYMBOL=Symbol.for("opentelemetry.graphql_data")});var zCe=S(fk=>{"use strict";Object.defineProperty(fk,"__esModule",{value:!0});fk.OPERATION_NOT_SUPPORTED=void 0;var vIr=dk();fk.OPERATION_NOT_SUPPORTED="Operation$operationName$not supported"});var XCe=S(Qn=>{"use strict";Object.defineProperty(Qn,"__esModule",{value:!0});Qn.wrapFieldResolver=Qn.wrapFields=Qn.getSourceFromLocation=Qn.getOperation=Qn.endSpan=Qn.addSpanSource=Qn.addInputVariableAttributes=Qn.isPromise=void 0;var dv=(pe(),se(Pe)),xd=B3(),pv=G3(),Pl=dk(),FCe=Object.values(xd.AllowedOperationTypes),TDt=t=>typeof t?.then=="function";Qn.isPromise=TDt;var bDt=t=>typeof t=="object"&&t!==null;function V3(t,e,r){Array.isArray(r)?r.forEach((n,o)=>{V3(t,`${e}.${o}`,n)}):r instanceof Object?Object.entries(r).forEach(([n,o])=>{V3(t,`${e}.${n}`,o)}):t.setAttribute(`${pv.AttributeNames.VARIABLES}${String(e)}`,r)}function xDt(t,e){Object.entries(e).forEach(([r,n])=>{V3(t,r,n)})}Qn.addInputVariableAttributes=xDt;function VCe(t,e,r,n,o){let i=ZCe(e,r,n,o);t.setAttribute(pv.AttributeNames.SOURCE,i)}Qn.addSpanSource=VCe;function ADt(t,e,r,n,o){let i=HCe(r,o),s=!1;if(!i){s=!0;let a=WCe(r,o);i={parent:a,span:wDt(t,e,r,n,o,a.span),error:null},IDt(r,o,i)}return{spanAdded:s,field:i}}function wDt(t,e,r,n,o,i){let s={[pv.AttributeNames.FIELD_NAME]:n.fieldName,[pv.AttributeNames.FIELD_PATH]:o.join("."),[pv.AttributeNames.FIELD_TYPE]:n.returnType.toString()},a=t.startSpan(`${xd.SpanNames.RESOLVE} ${s[pv.AttributeNames.FIELD_PATH]}`,{attributes:s},i?dv.trace.setSpan(dv.context.active(),i):void 0),c=r[Pl.OTEL_GRAPHQL_DATA_SYMBOL].source,u=n.fieldNodes.find(p=>p.kind==="Field");return u&&VCe(a,c.loc,e().allowValues,u.loc?.start,u.loc?.end),a}function RDt(t,e){e&&t.recordException(e),t.end()}Qn.endSpan=RDt;function PDt(t,e){if(!(!t||!Array.isArray(t.definitions)))return e?t.definitions.filter(r=>FCe.indexOf(r?.operation)!==-1).find(r=>e===r?.name?.value):t.definitions.find(r=>FCe.indexOf(r?.operation)!==-1)}Qn.getOperation=PDt;function IDt(t,e,r){return t[Pl.OTEL_GRAPHQL_DATA_SYMBOL].fields[e.join(".")]=r}function HCe(t,e){return t[Pl.OTEL_GRAPHQL_DATA_SYMBOL].fields[e.join(".")]}function WCe(t,e){for(let r=e.length-1;r>0;r--){let n=HCe(t,e.slice(0,r));if(n)return n}return{span:t[Pl.OTEL_GRAPHQL_DATA_SYMBOL].span}}function ODt(t,e){let r=[],n=e;for(;n;){let o=n.key;t&&typeof o=="number"&&(o="*"),r.push(String(o)),n=n.prev}return r.reverse()}function NDt(t){return KCe(` -`,t)}function qCe(t){return KCe(" ",t)}function KCe(t,e){let r="";for(let n=0;ns){a=a.next,c=a?.line;continue}let u=a.value||a.kind,p="";!e&&CDt.indexOf(a.kind)>=0&&(u="*"),a.kind===xd.TokenKind.STRING&&(u=`"${u}"`),a.kind===xd.TokenKind.EOF&&(u=""),a.line>c?(o+=NDt(a.line-c),c=a.line,p=qCe(a.column-1)):a.line===a.prev?.line&&(p=qCe(a.start-(a.prev?.end||0))),o+=p+u,a&&(a=a.next)}}return o}Qn.getSourceFromLocation=ZCe;function YCe(t,e,r){if(!t||typeof t.getFields!="function"||t[Pl.OTEL_PATCHED_SYMBOL])return;let n=t.getFields();t[Pl.OTEL_PATCHED_SYMBOL]=!0,Object.keys(n).forEach(o=>{let i=n[o];if(i&&(i.resolve&&(i.resolve=JCe(e,r,i.resolve)),i.type)){let s=i.type;for(;s.ofType;)s=s.ofType;YCe(s,e,r)}})}Qn.wrapFields=YCe;var BCe=(t,e,r)=>{r&&(t.recordException(e),t.setStatus({code:dv.SpanStatusCode.ERROR,message:e.message}),t.end())},GCe=(t,e)=>{e&&t.end()};function JCe(t,e,r,n=!1){if(o[Pl.OTEL_PATCHED_SYMBOL]||typeof r!="function")return r;function o(i,s,a,c){if(!r)return;let u=e();if(u.ignoreTrivialResolveSpans&&n&&(bDt(i)||typeof i=="function")&&typeof i[c.fieldName]!="function")return r.call(this,i,s,a,c);if(!a[Pl.OTEL_GRAPHQL_DATA_SYMBOL])return r.call(this,i,s,a,c);let p=ODt(u.mergeItems,c&&c.path),f=p.filter(_=>typeof _=="string").length,m,h=!1;if(u.depth>=0&&u.depth{try{let _=r.call(this,i,s,a,c);return(0,Qn.isPromise)(_)?_.then(v=>(GCe(m.span,h),v),v=>{throw BCe(m.span,v,h),v}):(GCe(m.span,h),_)}catch(_){throw BCe(m.span,_,h),_}})}return o[Pl.OTEL_PATCHED_SYMBOL]=!0,o}Qn.wrapFieldResolver=JCe});var QCe=S(fv=>{"use strict";Object.defineProperty(fv,"__esModule",{value:!0});fv.PACKAGE_NAME=fv.PACKAGE_VERSION=void 0;fv.PACKAGE_VERSION="0.51.0";fv.PACKAGE_NAME="@opentelemetry/instrumentation-graphql"});var r1e=S(gk=>{"use strict";Object.defineProperty(gk,"__esModule",{value:!0});gk.GraphQLInstrumentation=void 0;var Il=(pe(),se(Pe)),Ta=Ft(),Yx=B3(),mk=G3(),H3=dk(),$Dt=zCe(),oi=XCe(),e1e=QCe(),t1e={mergeItems:!1,depth:-1,allowValues:!1,ignoreResolveSpans:!1},hk=[">=14.0.0 <17"],W3=class extends Ta.InstrumentationBase{constructor(e={}){super(e1e.PACKAGE_NAME,e1e.PACKAGE_VERSION,{...t1e,...e})}setConfig(e={}){super.setConfig({...t1e,...e})}init(){let e=new Ta.InstrumentationNodeModuleDefinition("graphql",hk);return e.files.push(this._addPatchingExecute()),e.files.push(this._addPatchingParser()),e.files.push(this._addPatchingValidate()),e}_addPatchingExecute(){return new Ta.InstrumentationNodeModuleFile("graphql/execution/execute.js",hk,e=>((0,Ta.isWrapped)(e.execute)&&this._unwrap(e,"execute"),this._wrap(e,"execute",this._patchExecute(e.defaultFieldResolver)),e),e=>{e&&this._unwrap(e,"execute")})}_addPatchingParser(){return new Ta.InstrumentationNodeModuleFile("graphql/language/parser.js",hk,e=>((0,Ta.isWrapped)(e.parse)&&this._unwrap(e,"parse"),this._wrap(e,"parse",this._patchParse()),e),e=>{e&&this._unwrap(e,"parse")})}_addPatchingValidate(){return new Ta.InstrumentationNodeModuleFile("graphql/validation/validate.js",hk,e=>((0,Ta.isWrapped)(e.validate)&&this._unwrap(e,"validate"),this._wrap(e,"validate",this._patchValidate()),e),e=>{e&&this._unwrap(e,"validate")})}_patchExecute(e){let r=this;return function(o){return function(){let s;if(arguments.length>=2){let u=arguments;s=r._wrapExecuteArgs(u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],e)}else{let u=arguments[0];s=r._wrapExecuteArgs(u.schema,u.document,u.rootValue,u.contextValue,u.variableValues,u.operationName,u.fieldResolver,u.typeResolver,e)}let a=(0,oi.getOperation)(s.document,s.operationName),c=r._createExecuteSpan(a,s);return s.contextValue[H3.OTEL_GRAPHQL_DATA_SYMBOL]={source:s.document?s.document||s.document[H3.OTEL_GRAPHQL_DATA_SYMBOL]:void 0,span:c,fields:{}},Il.context.with(Il.trace.setSpan(Il.context.active(),c),()=>(0,Ta.safeExecuteInTheMiddle)(()=>o.apply(this,[s]),(u,p)=>{r._handleExecutionResult(c,u,p)}))}}}_handleExecutionResult(e,r,n){let o=this.getConfig();if(n===void 0||r){(0,oi.endSpan)(e,r);return}if((0,oi.isPromise)(n))n.then(i=>{if(typeof o.responseHook!="function"){(0,oi.endSpan)(e);return}this._executeResponseHook(e,i)},i=>{(0,oi.endSpan)(e,i)});else{if(typeof o.responseHook!="function"){(0,oi.endSpan)(e);return}this._executeResponseHook(e,n)}}_executeResponseHook(e,r){let{responseHook:n}=this.getConfig();n&&(0,Ta.safeExecuteInTheMiddle)(()=>{n(e,r)},o=>{o&&this._diag.error("Error running response hook",o),(0,oi.endSpan)(e,void 0)},!0)}_patchParse(){let e=this;return function(n){return function(i,s){return e._parse(this,n,i,s)}}}_patchValidate(){let e=this;return function(n){return function(i,s,a,c,u){return e._validate(this,n,i,s,a,u,c)}}}_parse(e,r,n,o){let i=this.getConfig(),s=this.tracer.startSpan(Yx.SpanNames.PARSE);return Il.context.with(Il.trace.setSpan(Il.context.active(),s),()=>(0,Ta.safeExecuteInTheMiddle)(()=>r.call(e,n,o),(a,c)=>{c&&((0,oi.getOperation)(c)?c.loc&&(0,oi.addSpanSource)(s,c.loc,i.allowValues):s.updateName(Yx.SpanNames.SCHEMA_PARSE)),(0,oi.endSpan)(s,a)}))}_validate(e,r,n,o,i,s,a){let c=this.tracer.startSpan(Yx.SpanNames.VALIDATE,{});return Il.context.with(Il.trace.setSpan(Il.context.active(),c),()=>(0,Ta.safeExecuteInTheMiddle)(()=>r.call(e,n,o,i,a,s),(u,p)=>{o.loc||c.updateName(Yx.SpanNames.SCHEMA_VALIDATE),p&&p.length&&c.recordException({name:mk.AttributeNames.ERROR_VALIDATION_NAME,message:JSON.stringify(p)}),(0,oi.endSpan)(c,u)}))}_createExecuteSpan(e,r){let n=this.getConfig(),o=this.tracer.startSpan(Yx.SpanNames.EXECUTE,{});if(e){let{operation:i,name:s}=e;o.setAttribute(mk.AttributeNames.OPERATION_TYPE,i);let a=s?.value;a?(o.setAttribute(mk.AttributeNames.OPERATION_NAME,a),o.updateName(`${i} ${a}`)):o.updateName(i)}else{let i=" ";r.operationName&&(i=` "${r.operationName}" `),i=$Dt.OPERATION_NOT_SUPPORTED.replace("$operationName$",i),o.setAttribute(mk.AttributeNames.OPERATION_NAME,i)}return r.document?.loc&&(0,oi.addSpanSource)(o,r.document.loc,n.allowValues),r.variableValues&&n.allowValues&&(0,oi.addInputVariableAttributes)(o,r.variableValues),o}_wrapExecuteArgs(e,r,n,o,i,s,a,c,u){if(o||(o={}),o[H3.OTEL_GRAPHQL_DATA_SYMBOL]||this.getConfig().ignoreResolveSpans)return{schema:e,document:r,rootValue:n,contextValue:o,variableValues:i,operationName:s,fieldResolver:a,typeResolver:c};let p=a==null,f=a??u;return a=(0,oi.wrapFieldResolver)(this.tracer,()=>this.getConfig(),f,p),e&&((0,oi.wrapFields)(e.getQueryType(),this.tracer,()=>this.getConfig()),(0,oi.wrapFields)(e.getMutationType(),this.tracer,()=>this.getConfig())),{schema:e,document:r,rootValue:n,contextValue:o,variableValues:i,operationName:s,fieldResolver:a,typeResolver:c}}};gk.GraphQLInstrumentation=W3});var n1e=S(_k=>{"use strict";Object.defineProperty(_k,"__esModule",{value:!0});_k.GraphQLInstrumentation=void 0;var kDt=r1e();Object.defineProperty(_k,"GraphQLInstrumentation",{enumerable:!0,get:function(){return kDt.GraphQLInstrumentation}})});var u1e=S(vk=>{"use strict";Object.defineProperty(vk,"__esModule",{value:!0});vk.EVENT_LISTENERS_SET=void 0;vk.EVENT_LISTENERS_SET=Symbol("opentelemetry.instrumentation.kafkajs.eventListenersSet")});var l1e=S(Sk=>{"use strict";Object.defineProperty(Sk,"__esModule",{value:!0});Sk.bufferTextMapGetter=void 0;Sk.bufferTextMapGetter={get(t,e){if(!t)return;let r=Object.keys(t);for(let n of r)if(n===e||n.toLowerCase()===e)return t[n]?.toString()},keys(t){return t?Object.keys(t):[]}}});var p1e=S(Zt=>{"use strict";Object.defineProperty(Zt,"__esModule",{value:!0});Zt.METRIC_MESSAGING_PROCESS_DURATION=Zt.METRIC_MESSAGING_CLIENT_SENT_MESSAGES=Zt.METRIC_MESSAGING_CLIENT_OPERATION_DURATION=Zt.METRIC_MESSAGING_CLIENT_CONSUMED_MESSAGES=Zt.MESSAGING_SYSTEM_VALUE_KAFKA=Zt.MESSAGING_OPERATION_TYPE_VALUE_SEND=Zt.MESSAGING_OPERATION_TYPE_VALUE_RECEIVE=Zt.MESSAGING_OPERATION_TYPE_VALUE_PROCESS=Zt.ATTR_MESSAGING_SYSTEM=Zt.ATTR_MESSAGING_OPERATION_TYPE=Zt.ATTR_MESSAGING_OPERATION_NAME=Zt.ATTR_MESSAGING_KAFKA_OFFSET=Zt.ATTR_MESSAGING_KAFKA_MESSAGE_TOMBSTONE=Zt.ATTR_MESSAGING_KAFKA_MESSAGE_KEY=Zt.ATTR_MESSAGING_DESTINATION_PARTITION_ID=Zt.ATTR_MESSAGING_DESTINATION_NAME=Zt.ATTR_MESSAGING_BATCH_MESSAGE_COUNT=void 0;Zt.ATTR_MESSAGING_BATCH_MESSAGE_COUNT="messaging.batch.message_count";Zt.ATTR_MESSAGING_DESTINATION_NAME="messaging.destination.name";Zt.ATTR_MESSAGING_DESTINATION_PARTITION_ID="messaging.destination.partition.id";Zt.ATTR_MESSAGING_KAFKA_MESSAGE_KEY="messaging.kafka.message.key";Zt.ATTR_MESSAGING_KAFKA_MESSAGE_TOMBSTONE="messaging.kafka.message.tombstone";Zt.ATTR_MESSAGING_KAFKA_OFFSET="messaging.kafka.offset";Zt.ATTR_MESSAGING_OPERATION_NAME="messaging.operation.name";Zt.ATTR_MESSAGING_OPERATION_TYPE="messaging.operation.type";Zt.ATTR_MESSAGING_SYSTEM="messaging.system";Zt.MESSAGING_OPERATION_TYPE_VALUE_PROCESS="process";Zt.MESSAGING_OPERATION_TYPE_VALUE_RECEIVE="receive";Zt.MESSAGING_OPERATION_TYPE_VALUE_SEND="send";Zt.MESSAGING_SYSTEM_VALUE_KAFKA="kafka";Zt.METRIC_MESSAGING_CLIENT_CONSUMED_MESSAGES="messaging.client.consumed.messages";Zt.METRIC_MESSAGING_CLIENT_OPERATION_DURATION="messaging.client.operation.duration";Zt.METRIC_MESSAGING_CLIENT_SENT_MESSAGES="messaging.client.sent.messages";Zt.METRIC_MESSAGING_PROCESS_DURATION="messaging.process.duration"});var d1e=S(mv=>{"use strict";Object.defineProperty(mv,"__esModule",{value:!0});mv.PACKAGE_NAME=mv.PACKAGE_VERSION=void 0;mv.PACKAGE_VERSION="0.12.0";mv.PACKAGE_NAME="@opentelemetry/instrumentation-kafkajs"});var v1e=S(Ek=>{"use strict";Object.defineProperty(Ek,"__esModule",{value:!0});Ek.KafkaJsInstrumentation=void 0;var ho=(pe(),se(Pe)),Ac=Ft(),hv=(er(),se(Cr)),f1e=u1e(),m1e=l1e(),Ne=p1e(),h1e=d1e();function yk(t,e,r){return n=>{t.add(e,{...r,...n?{[hv.ATTR_ERROR_TYPE]:n}:{}})}}function g1e(t,e,r){return n=>{t.record((Date.now()-e)/1e3,{...r,...n?{[hv.ATTR_ERROR_TYPE]:n}:{}})}}var _1e=[.005,.01,.025,.05,.075,.1,.25,.5,.75,1,2.5,5,7.5,10],K3=class extends Ac.InstrumentationBase{constructor(e={}){super(h1e.PACKAGE_NAME,h1e.PACKAGE_VERSION,e)}_updateMetricInstruments(){this._clientDuration=this.meter.createHistogram(Ne.METRIC_MESSAGING_CLIENT_OPERATION_DURATION,{advice:{explicitBucketBoundaries:_1e}}),this._sentMessages=this.meter.createCounter(Ne.METRIC_MESSAGING_CLIENT_SENT_MESSAGES),this._consumedMessages=this.meter.createCounter(Ne.METRIC_MESSAGING_CLIENT_CONSUMED_MESSAGES),this._processDuration=this.meter.createHistogram(Ne.METRIC_MESSAGING_PROCESS_DURATION,{advice:{explicitBucketBoundaries:_1e}})}init(){let e=n=>{(0,Ac.isWrapped)(n?.Kafka?.prototype.producer)&&this._unwrap(n.Kafka.prototype,"producer"),(0,Ac.isWrapped)(n?.Kafka?.prototype.consumer)&&this._unwrap(n.Kafka.prototype,"consumer")};return new Ac.InstrumentationNodeModuleDefinition("kafkajs",[">=0.3.0 <3"],n=>(e(n),this._wrap(n?.Kafka?.prototype,"producer",this._getProducerPatch()),this._wrap(n?.Kafka?.prototype,"consumer",this._getConsumerPatch()),n),e)}_getConsumerPatch(){let e=this;return r=>function(...o){let i=r.apply(this,o);return(0,Ac.isWrapped)(i.run)&&e._unwrap(i,"run"),e._wrap(i,"run",e._getConsumerRunPatch()),e._setKafkaEventListeners(i),i}}_setKafkaEventListeners(e){e[f1e.EVENT_LISTENERS_SET]||(e.events?.REQUEST&&e.on(e.events.REQUEST,this._recordClientDurationMetric.bind(this)),e[f1e.EVENT_LISTENERS_SET]=!0)}_recordClientDurationMetric(e){let[r,n]=e.payload.broker.split(":");this._clientDuration.record(e.payload.duration/1e3,{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_OPERATION_NAME]:`${e.payload.apiName}`,[hv.ATTR_SERVER_ADDRESS]:r,[hv.ATTR_SERVER_PORT]:Number.parseInt(n,10)})}_getProducerPatch(){let e=this;return r=>function(...o){let i=r.apply(this,o);return(0,Ac.isWrapped)(i.sendBatch)&&e._unwrap(i,"sendBatch"),e._wrap(i,"sendBatch",e._getProducerSendBatchPatch()),(0,Ac.isWrapped)(i.send)&&e._unwrap(i,"send"),e._wrap(i,"send",e._getProducerSendPatch()),e._setKafkaEventListeners(i),i}}_getConsumerRunPatch(){let e=this;return r=>function(...o){let i=o[0];return i?.eachMessage&&((0,Ac.isWrapped)(i.eachMessage)&&e._unwrap(i,"eachMessage"),e._wrap(i,"eachMessage",e._getConsumerEachMessagePatch())),i?.eachBatch&&((0,Ac.isWrapped)(i.eachBatch)&&e._unwrap(i,"eachBatch"),e._wrap(i,"eachBatch",e._getConsumerEachBatchPatch())),r.call(this,i)}}_getConsumerEachMessagePatch(){let e=this;return r=>function(...o){let i=o[0],s=ho.propagation.extract(ho.ROOT_CONTEXT,i.message.headers,m1e.bufferTextMapGetter),a=e._startConsumerSpan({topic:i.topic,message:i.message,operationType:Ne.MESSAGING_OPERATION_TYPE_VALUE_PROCESS,ctx:s,attributes:{[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(i.partition)}}),c=[g1e(e._processDuration,Date.now(),{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_OPERATION_NAME]:"process",[Ne.ATTR_MESSAGING_DESTINATION_NAME]:i.topic,[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(i.partition)}),yk(e._consumedMessages,1,{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_OPERATION_NAME]:"process",[Ne.ATTR_MESSAGING_DESTINATION_NAME]:i.topic,[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(i.partition)})],u=ho.context.with(ho.trace.setSpan(s,a),()=>r.apply(this,o));return e._endSpansOnPromise([a],c,u)}}_getConsumerEachBatchPatch(){return e=>{let r=this;return function(...o){let i=o[0],s=r._startConsumerSpan({topic:i.batch.topic,message:void 0,operationType:Ne.MESSAGING_OPERATION_TYPE_VALUE_RECEIVE,ctx:ho.ROOT_CONTEXT,attributes:{[Ne.ATTR_MESSAGING_BATCH_MESSAGE_COUNT]:i.batch.messages.length,[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(i.batch.partition)}});return ho.context.with(ho.trace.setSpan(ho.context.active(),s),()=>{let a=Date.now(),c=[],u=[yk(r._consumedMessages,i.batch.messages.length,{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_OPERATION_NAME]:"process",[Ne.ATTR_MESSAGING_DESTINATION_NAME]:i.batch.topic,[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(i.batch.partition)})];i.batch.messages.forEach(f=>{let m=ho.propagation.extract(ho.ROOT_CONTEXT,f.headers,m1e.bufferTextMapGetter),h=ho.trace.getSpan(m)?.spanContext(),_;h&&(_={context:h}),c.push(r._startConsumerSpan({topic:i.batch.topic,message:f,operationType:Ne.MESSAGING_OPERATION_TYPE_VALUE_PROCESS,link:_,attributes:{[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(i.batch.partition)}})),u.push(g1e(r._processDuration,a,{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_OPERATION_NAME]:"process",[Ne.ATTR_MESSAGING_DESTINATION_NAME]:i.batch.topic,[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(i.batch.partition)}))});let p=e.apply(this,o);return c.unshift(s),r._endSpansOnPromise(c,u,p)})}}}_getProducerSendBatchPatch(){let e=this;return r=>function(...o){let s=o[0].topicMessages||[],a=[],c=[];s.forEach(p=>{p.messages.forEach(f=>{a.push(e._startProducerSpan(p.topic,f)),c.push(yk(e._sentMessages,1,{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_OPERATION_NAME]:"send",[Ne.ATTR_MESSAGING_DESTINATION_NAME]:p.topic,...f.partition!==void 0?{[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(f.partition)}:{}}))})});let u=r.apply(this,o);return e._endSpansOnPromise(a,c,u)}}_getProducerSendPatch(){let e=this;return r=>function(...o){let i=o[0],s=i.messages.map(u=>e._startProducerSpan(i.topic,u)),a=i.messages.map(u=>yk(e._sentMessages,1,{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_OPERATION_NAME]:"send",[Ne.ATTR_MESSAGING_DESTINATION_NAME]:i.topic,...u.partition!==void 0?{[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:String(u.partition)}:{}})),c=r.apply(this,o);return e._endSpansOnPromise(s,a,c)}}_endSpansOnPromise(e,r,n){return Promise.resolve(n).then(o=>(r.forEach(i=>i()),o)).catch(o=>{let i,s=hv.ERROR_TYPE_VALUE_OTHER;throw typeof o=="string"||o===void 0?i=o:typeof o=="object"&&Object.prototype.hasOwnProperty.call(o,"message")&&(i=o.message,s=o.constructor.name),r.forEach(a=>a(s)),e.forEach(a=>{a.setAttribute(hv.ATTR_ERROR_TYPE,s),a.setStatus({code:ho.SpanStatusCode.ERROR,message:i})}),o}).finally(()=>{e.forEach(o=>o.end())})}_startConsumerSpan({topic:e,message:r,operationType:n,ctx:o,link:i,attributes:s}){let a=n===Ne.MESSAGING_OPERATION_TYPE_VALUE_RECEIVE?"poll":n,c=this.tracer.startSpan(`${a} ${e}`,{kind:n===Ne.MESSAGING_OPERATION_TYPE_VALUE_RECEIVE?ho.SpanKind.CLIENT:ho.SpanKind.CONSUMER,attributes:{...s,[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_DESTINATION_NAME]:e,[Ne.ATTR_MESSAGING_OPERATION_TYPE]:n,[Ne.ATTR_MESSAGING_OPERATION_NAME]:a,[Ne.ATTR_MESSAGING_KAFKA_MESSAGE_KEY]:r?.key?String(r.key):void 0,[Ne.ATTR_MESSAGING_KAFKA_MESSAGE_TOMBSTONE]:r?.key&&r.value===null?!0:void 0,[Ne.ATTR_MESSAGING_KAFKA_OFFSET]:r?.offset},links:i?[i]:[]},o),{consumerHook:u}=this.getConfig();return u&&r&&(0,Ac.safeExecuteInTheMiddle)(()=>u(c,{topic:e,message:r}),p=>{p&&this._diag.error("consumerHook error",p)},!0),c}_startProducerSpan(e,r){let n=this.tracer.startSpan(`send ${e}`,{kind:ho.SpanKind.PRODUCER,attributes:{[Ne.ATTR_MESSAGING_SYSTEM]:Ne.MESSAGING_SYSTEM_VALUE_KAFKA,[Ne.ATTR_MESSAGING_DESTINATION_NAME]:e,[Ne.ATTR_MESSAGING_KAFKA_MESSAGE_KEY]:r.key?String(r.key):void 0,[Ne.ATTR_MESSAGING_KAFKA_MESSAGE_TOMBSTONE]:r.key&&r.value===null?!0:void 0,[Ne.ATTR_MESSAGING_DESTINATION_PARTITION_ID]:r.partition!==void 0?String(r.partition):void 0,[Ne.ATTR_MESSAGING_OPERATION_NAME]:"send",[Ne.ATTR_MESSAGING_OPERATION_TYPE]:Ne.MESSAGING_OPERATION_TYPE_VALUE_SEND}});r.headers=r.headers??{},ho.propagation.inject(ho.trace.setSpan(ho.context.active(),n),r.headers);let{producerHook:o}=this.getConfig();return o&&(0,Ac.safeExecuteInTheMiddle)(()=>o(n,{topic:e,message:r}),i=>{i&&this._diag.error("producerHook error",i)},!0),n}};Ek.KafkaJsInstrumentation=K3});var S1e=S(Tk=>{"use strict";Object.defineProperty(Tk,"__esModule",{value:!0});Tk.KafkaJsInstrumentation=void 0;var LDt=v1e();Object.defineProperty(Tk,"KafkaJsInstrumentation",{enumerable:!0,get:function(){return LDt.KafkaJsInstrumentation}})});var x1e=S(gv=>{"use strict";Object.defineProperty(gv,"__esModule",{value:!0});gv.PACKAGE_NAME=gv.PACKAGE_VERSION=void 0;gv.PACKAGE_VERSION="0.48.0";gv.PACKAGE_NAME="@opentelemetry/instrumentation-lru-memoizer"});var P1e=S(bk=>{"use strict";Object.defineProperty(bk,"__esModule",{value:!0});bk.LruMemoizerInstrumentation=void 0;var A1e=(pe(),se(Pe)),w1e=Ft(),R1e=x1e(),Z3=class extends w1e.InstrumentationBase{constructor(e={}){super(R1e.PACKAGE_NAME,R1e.PACKAGE_VERSION,e)}init(){return[new w1e.InstrumentationNodeModuleDefinition("lru-memoizer",[">=1.3 <3"],e=>{let r=function(){let n=e.apply(this,arguments);return function(){let o=[...arguments],i=o.pop(),s=typeof i=="function"?A1e.context.bind(A1e.context.active(),i):i;return o.push(s),n.apply(this,o)}};return r.sync=e.sync,r},void 0)]}};bk.LruMemoizerInstrumentation=Z3});var I1e=S(xk=>{"use strict";Object.defineProperty(xk,"__esModule",{value:!0});xk.LruMemoizerInstrumentation=void 0;var jDt=P1e();Object.defineProperty(xk,"LruMemoizerInstrumentation",{enumerable:!0,get:function(){return jDt.LruMemoizerInstrumentation}})});var k1e=S(Jx=>{"use strict";Object.defineProperty(Jx,"__esModule",{value:!0});Jx.MongodbCommandType=void 0;var FDt;(function(t){t.CREATE_INDEXES="createIndexes",t.FIND_AND_MODIFY="findAndModify",t.IS_MASTER="isMaster",t.COUNT="count",t.AGGREGATE="aggregate",t.UNKNOWN="unknown"})(FDt=Jx.MongodbCommandType||(Jx.MongodbCommandType={}))});var M1e=S(_v=>{"use strict";Object.defineProperty(_v,"__esModule",{value:!0});_v.PACKAGE_NAME=_v.PACKAGE_VERSION=void 0;_v.PACKAGE_VERSION="0.56.0";_v.PACKAGE_NAME="@opentelemetry/instrumentation-mongodb"});var U1e=S(Ak=>{"use strict";Object.defineProperty(Ak,"__esModule",{value:!0});Ak.MongoDBInstrumentation=void 0;var ln=(pe(),se(Pe)),Xr=Ft(),Ol=(er(),se(Cr)),Ad=k1e(),D1e=M1e(),L1e={requireParentSpan:!0},Y3=class t extends Xr.InstrumentationBase{constructor(e={}){super(D1e.PACKAGE_NAME,D1e.PACKAGE_VERSION,{...L1e,...e})}setConfig(e={}){super.setConfig({...L1e,...e})}_updateMetricInstruments(){this._connectionsUsage=this.meter.createUpDownCounter("db.client.connections.usage",{description:"The number of connections that are currently in state described by the state attribute.",unit:"{connection}"})}init(){let{v3PatchConnection:e,v3UnpatchConnection:r}=this._getV3ConnectionPatches(),{v4PatchConnect:n,v4UnpatchConnect:o}=this._getV4ConnectPatches(),{v4PatchConnectionCallback:i,v4PatchConnectionPromise:s,v4UnpatchConnection:a}=this._getV4ConnectionPatches(),{v4PatchConnectionPool:c,v4UnpatchConnectionPool:u}=this._getV4ConnectionPoolPatches(),{v4PatchSessions:p,v4UnpatchSessions:f}=this._getV4SessionsPatches();return[new Xr.InstrumentationNodeModuleDefinition("mongodb",[">=3.3.0 <4"],void 0,void 0,[new Xr.InstrumentationNodeModuleFile("mongodb/lib/core/wireprotocol/index.js",[">=3.3.0 <4"],e,r)]),new Xr.InstrumentationNodeModuleDefinition("mongodb",[">=4.0.0 <7"],void 0,void 0,[new Xr.InstrumentationNodeModuleFile("mongodb/lib/cmap/connection.js",[">=4.0.0 <6.4"],i,a),new Xr.InstrumentationNodeModuleFile("mongodb/lib/cmap/connection.js",[">=6.4.0 <7"],s,a),new Xr.InstrumentationNodeModuleFile("mongodb/lib/cmap/connection_pool.js",[">=4.0.0 <6.4"],c,u),new Xr.InstrumentationNodeModuleFile("mongodb/lib/cmap/connect.js",[">=4.0.0 <7"],n,o),new Xr.InstrumentationNodeModuleFile("mongodb/lib/sessions.js",[">=4.0.0 <7"],p,f)])]}_getV3ConnectionPatches(){return{v3PatchConnection:e=>((0,Xr.isWrapped)(e.insert)&&this._unwrap(e,"insert"),this._wrap(e,"insert",this._getV3PatchOperation("insert")),(0,Xr.isWrapped)(e.remove)&&this._unwrap(e,"remove"),this._wrap(e,"remove",this._getV3PatchOperation("remove")),(0,Xr.isWrapped)(e.update)&&this._unwrap(e,"update"),this._wrap(e,"update",this._getV3PatchOperation("update")),(0,Xr.isWrapped)(e.command)&&this._unwrap(e,"command"),this._wrap(e,"command",this._getV3PatchCommand()),(0,Xr.isWrapped)(e.query)&&this._unwrap(e,"query"),this._wrap(e,"query",this._getV3PatchFind()),(0,Xr.isWrapped)(e.getMore)&&this._unwrap(e,"getMore"),this._wrap(e,"getMore",this._getV3PatchCursor()),e),v3UnpatchConnection:e=>{e!==void 0&&(this._unwrap(e,"insert"),this._unwrap(e,"remove"),this._unwrap(e,"update"),this._unwrap(e,"command"),this._unwrap(e,"query"),this._unwrap(e,"getMore"))}}}_getV4SessionsPatches(){return{v4PatchSessions:e=>((0,Xr.isWrapped)(e.acquire)&&this._unwrap(e,"acquire"),this._wrap(e.ServerSessionPool.prototype,"acquire",this._getV4AcquireCommand()),(0,Xr.isWrapped)(e.release)&&this._unwrap(e,"release"),this._wrap(e.ServerSessionPool.prototype,"release",this._getV4ReleaseCommand()),e),v4UnpatchSessions:e=>{e!==void 0&&((0,Xr.isWrapped)(e.acquire)&&this._unwrap(e,"acquire"),(0,Xr.isWrapped)(e.release)&&this._unwrap(e,"release"))}}}_getV4AcquireCommand(){let e=this;return r=>function(){let o=this.sessions.length,i=r.call(this),s=this.sessions.length;return o===s?e._connectionsUsage.add(1,{state:"used","pool.name":e._poolName}):o-1===s&&(e._connectionsUsage.add(-1,{state:"idle","pool.name":e._poolName}),e._connectionsUsage.add(1,{state:"used","pool.name":e._poolName})),i}}_getV4ReleaseCommand(){let e=this;return r=>function(o){let i=r.call(this,o);return e._connectionsUsage.add(-1,{state:"used","pool.name":e._poolName}),e._connectionsUsage.add(1,{state:"idle","pool.name":e._poolName}),i}}_getV4ConnectionPoolPatches(){return{v4PatchConnectionPool:e=>{let r=e.ConnectionPool.prototype;return(0,Xr.isWrapped)(r.checkOut)&&this._unwrap(r,"checkOut"),this._wrap(r,"checkOut",this._getV4ConnectionPoolCheckOut()),e},v4UnpatchConnectionPool:e=>{e!==void 0&&this._unwrap(e.ConnectionPool.prototype,"checkOut")}}}_getV4ConnectPatches(){return{v4PatchConnect:e=>((0,Xr.isWrapped)(e.connect)&&this._unwrap(e,"connect"),this._wrap(e,"connect",this._getV4ConnectCommand()),e),v4UnpatchConnect:e=>{e!==void 0&&this._unwrap(e,"connect")}}}_getV4ConnectionPoolCheckOut(){return e=>function(n){let o=ln.context.bind(ln.context.active(),n);return e.call(this,o)}}_getV4ConnectCommand(){let e=this;return r=>function(o,i){if(r.length===1){let a=r.call(this,o);return a&&typeof a.then=="function"&&a.then(()=>e.setPoolName(o),()=>{}),a}let s=function(a,c){if(a||!c){i(a,c);return}e.setPoolName(o),i(a,c)};return r.call(this,o,s)}}_getV4ConnectionPatches(){return{v4PatchConnectionCallback:e=>((0,Xr.isWrapped)(e.Connection.prototype.command)&&this._unwrap(e.Connection.prototype,"command"),this._wrap(e.Connection.prototype,"command",this._getV4PatchCommandCallback()),e),v4PatchConnectionPromise:e=>((0,Xr.isWrapped)(e.Connection.prototype.command)&&this._unwrap(e.Connection.prototype,"command"),this._wrap(e.Connection.prototype,"command",this._getV4PatchCommandPromise()),e),v4UnpatchConnection:e=>{e!==void 0&&this._unwrap(e.Connection.prototype,"command")}}}_getV3PatchOperation(e){let r=this;return n=>function(i,s,a,c,u){let p=ln.trace.getSpan(ln.context.active()),f=r._checkSkipInstrumentation(p),m=typeof c=="function"?c:u;if(f||typeof m!="function"||typeof a!="object")return typeof c=="function"?n.call(this,i,s,a,c):n.call(this,i,s,a,c,u);let h=r.tracer.startSpan(`mongodb.${e}`,{kind:ln.SpanKind.CLIENT});r._populateV3Attributes(h,s,i,a[0],e);let _=r._patchEnd(h,m);return typeof c=="function"?n.call(this,i,s,a,_):n.call(this,i,s,a,c,_)}}_getV3PatchCommand(){let e=this;return r=>function(o,i,s,a,c){let u=ln.trace.getSpan(ln.context.active()),p=e._checkSkipInstrumentation(u),f=typeof a=="function"?a:c;if(p||typeof f!="function"||typeof s!="object")return typeof a=="function"?r.call(this,o,i,s,a):r.call(this,o,i,s,a,c);let m=t._getCommandType(s),h=m===Ad.MongodbCommandType.UNKNOWN?"command":m,_=e.tracer.startSpan(`mongodb.${h}`,{kind:ln.SpanKind.CLIENT}),v=m===Ad.MongodbCommandType.UNKNOWN?void 0:m;e._populateV3Attributes(_,i,o,s,v);let E=e._patchEnd(_,f);return typeof a=="function"?r.call(this,o,i,s,E):r.call(this,o,i,s,a,E)}}_getV4PatchCommandCallback(){let e=this;return r=>function(o,i,s,a){let c=ln.trace.getSpan(ln.context.active()),u=e._checkSkipInstrumentation(c),p=a,f=Object.keys(i)[0];if(typeof i!="object"||i.ismaster||i.hello)return r.call(this,o,i,s,a);let m;u||(m=e.tracer.startSpan(`mongodb.${f}`,{kind:ln.SpanKind.CLIENT}),e._populateV4Attributes(m,this,o,i,f));let h=e._patchEnd(m,p,this.id,f);return r.call(this,o,i,s,h)}}_getV4PatchCommandPromise(){let e=this;return r=>function(...o){let[i,s]=o,a=ln.trace.getSpan(ln.context.active()),c=e._checkSkipInstrumentation(a),u=Object.keys(s)[0],p=()=>{};if(typeof s!="object"||s.ismaster||s.hello)return r.apply(this,o);let f;c||(f=e.tracer.startSpan(`mongodb.${u}`,{kind:ln.SpanKind.CLIENT}),e._populateV4Attributes(f,this,i,s,u));let m=e._patchEnd(f,p,this.id,u),h=r.apply(this,o);return h.then(_=>m(null,_),_=>m(_)),h}}_getV3PatchFind(){let e=this;return r=>function(o,i,s,a,c,u){let p=ln.trace.getSpan(ln.context.active()),f=e._checkSkipInstrumentation(p),m=typeof c=="function"?c:u;if(f||typeof m!="function"||typeof s!="object")return typeof c=="function"?r.call(this,o,i,s,a,c):r.call(this,o,i,s,a,c,u);let h=e.tracer.startSpan("mongodb.find",{kind:ln.SpanKind.CLIENT});e._populateV3Attributes(h,i,o,s,"find");let _=e._patchEnd(h,m);return typeof c=="function"?r.call(this,o,i,s,a,_):r.call(this,o,i,s,a,c,_)}}_getV3PatchCursor(){let e=this;return r=>function(o,i,s,a,c,u){let p=ln.trace.getSpan(ln.context.active()),f=e._checkSkipInstrumentation(p),m=typeof c=="function"?c:u;if(f||typeof m!="function")return typeof c=="function"?r.call(this,o,i,s,a,c):r.call(this,o,i,s,a,c,u);let h=e.tracer.startSpan("mongodb.getMore",{kind:ln.SpanKind.CLIENT});e._populateV3Attributes(h,i,o,s.cmd,"getMore");let _=e._patchEnd(h,m);return typeof c=="function"?r.call(this,o,i,s,a,_):r.call(this,o,i,s,a,c,_)}}static _getCommandType(e){return e.createIndexes!==void 0?Ad.MongodbCommandType.CREATE_INDEXES:e.findandmodify!==void 0?Ad.MongodbCommandType.FIND_AND_MODIFY:e.ismaster!==void 0?Ad.MongodbCommandType.IS_MASTER:e.count!==void 0?Ad.MongodbCommandType.COUNT:e.aggregate!==void 0?Ad.MongodbCommandType.AGGREGATE:Ad.MongodbCommandType.UNKNOWN}_populateV4Attributes(e,r,n,o,i){let s,a;if(r){let u=typeof r.address=="string"?r.address.split(":"):"";u.length===2&&(s=u[0],a=u[1])}let c;o?.documents&&o.documents[0]?c=o.documents[0]:o?.cursors?c=o.cursors:c=o,this._addAllSpanAttributes(e,n.db,n.collection,s,a,c,i)}_populateV3Attributes(e,r,n,o,i){let s,a;if(n&&n.s&&(s=n.s.options?.host??n.s.host,a=(n.s.options?.port??n.s.port)?.toString(),s==null||a==null)){let f=n.description?.address;if(f){let m=f.split(":");s=m[0],a=m[1]}}let[c,u]=r.toString().split("."),p=o?.query??o?.q??o;this._addAllSpanAttributes(e,c,u,s,a,p,i)}_addAllSpanAttributes(e,r,n,o,i,s,a){if(e.setAttributes({[Ol.SEMATTRS_DB_SYSTEM]:Ol.DBSYSTEMVALUES_MONGODB,[Ol.SEMATTRS_DB_NAME]:r,[Ol.SEMATTRS_DB_MONGODB_COLLECTION]:n,[Ol.SEMATTRS_DB_OPERATION]:a,[Ol.SEMATTRS_DB_CONNECTION_STRING]:`mongodb://${o}:${i}/${r}`}),o&&i){e.setAttribute(Ol.SEMATTRS_NET_PEER_NAME,o);let p=parseInt(i,10);isNaN(p)||e.setAttribute(Ol.SEMATTRS_NET_PEER_PORT,p)}if(!s)return;let{dbStatementSerializer:c}=this.getConfig(),u=typeof c=="function"?c:this._defaultDbStatementSerializer.bind(this);(0,Xr.safeExecuteInTheMiddle)(()=>{let p=u(s);e.setAttribute(Ol.SEMATTRS_DB_STATEMENT,p)},p=>{p&&this._diag.error("Error running dbStatementSerializer hook",p)},!0)}_defaultDbStatementSerializer(e){let{enhancedDatabaseReporting:r}=this.getConfig(),n=r?e:this._scrubStatement(e);return JSON.stringify(n)}_scrubStatement(e){return Array.isArray(e)?e.map(r=>this._scrubStatement(r)):typeof e=="object"&&e!==null?Object.fromEntries(Object.entries(e).map(([r,n])=>[r,this._scrubStatement(n)])):"?"}_handleExecutionResult(e,r){let{responseHook:n}=this.getConfig();typeof n=="function"&&(0,Xr.safeExecuteInTheMiddle)(()=>{n(e,{data:r})},o=>{o&&this._diag.error("Error running response hook",o)},!0)}_patchEnd(e,r,n,o){let i=ln.context.active(),s=this;return function(...c){let u=c[0];if(e){if(u instanceof Error)e?.setStatus({code:ln.SpanStatusCode.ERROR,message:u.message});else{let p=c[1];s._handleExecutionResult(e,p)}e.end()}return ln.context.with(i,()=>(o==="endSessions"&&s._connectionsUsage.add(-1,{state:"idle","pool.name":s._poolName}),r.apply(this,c)))}}setPoolName(e){let r=e.hostAddress?.host,n=e.hostAddress?.port,o=e.dbName,i=`mongodb://${r}:${n}/${o}`;this._poolName=i}_checkSkipInstrumentation(e){return this.getConfig().requireParentSpan===!0&&e===void 0}};Ak.MongoDBInstrumentation=Y3});var j1e=S(Xx=>{"use strict";Object.defineProperty(Xx,"__esModule",{value:!0});Xx.MongodbCommandType=void 0;var qDt;(function(t){t.CREATE_INDEXES="createIndexes",t.FIND_AND_MODIFY="findAndModify",t.IS_MASTER="isMaster",t.COUNT="count",t.UNKNOWN="unknown"})(qDt=Xx.MongodbCommandType||(Xx.MongodbCommandType={}))});var z1e=S(vv=>{"use strict";Object.defineProperty(vv,"__esModule",{value:!0});vv.MongodbCommandType=vv.MongoDBInstrumentation=void 0;var BDt=U1e();Object.defineProperty(vv,"MongoDBInstrumentation",{enumerable:!0,get:function(){return BDt.MongoDBInstrumentation}});var GDt=j1e();Object.defineProperty(vv,"MongodbCommandType",{enumerable:!0,get:function(){return GDt.MongodbCommandType}})});var W1e=S(wd=>{"use strict";Object.defineProperty(wd,"__esModule",{value:!0});wd.handleCallbackResponse=wd.handlePromiseResponse=wd.getAttributesFromCollection=void 0;var V1e=(pe(),se(Pe)),YDt=Ft(),Qx=(er(),se(Cr));function JDt(t){return{[Qx.SEMATTRS_DB_MONGODB_COLLECTION]:t.name,[Qx.SEMATTRS_DB_NAME]:t.conn.name,[Qx.SEMATTRS_DB_USER]:t.conn.user,[Qx.SEMATTRS_NET_PEER_NAME]:t.conn.host,[Qx.SEMATTRS_NET_PEER_PORT]:t.conn.port}}wd.getAttributesFromCollection=JDt;function H1e(t,e={}){t.recordException(e),t.setStatus({code:V1e.SpanStatusCode.ERROR,message:`${e.message} ${e.code?` -Mongoose Error Code: ${e.code}`:""}`})}function X3(t,e,r,n=void 0){r&&(0,YDt.safeExecuteInTheMiddle)(()=>r(t,{moduleVersion:n,response:e}),o=>{o&&V1e.diag.error("mongoose instrumentation: responseHook error",o)},!0)}function XDt(t,e,r,n=void 0){return t instanceof Promise?t.then(o=>(X3(e,o,r,n),o)).catch(o=>{throw H1e(e,o),o}).finally(()=>e.end()):(X3(e,t,r,n),e.end(),t)}wd.handlePromiseResponse=XDt;function QDt(t,e,r,n,o,i,s=void 0){let a=0;return o.length===2?a=1:o.length===3&&(a=2),o[a]=(c,u)=>(c?H1e(n,c):X3(n,u,i,s),n.end(),t(c,u)),e.apply(r,o)}wd.handleCallbackResponse=QDt});var K1e=S(Sv=>{"use strict";Object.defineProperty(Sv,"__esModule",{value:!0});Sv.PACKAGE_NAME=Sv.PACKAGE_VERSION=void 0;Sv.PACKAGE_VERSION="0.50.0";Sv.PACKAGE_NAME="@opentelemetry/instrumentation-mongoose"});var Q1e=S(Eu=>{"use strict";Object.defineProperty(Eu,"__esModule",{value:!0});Eu.MongooseInstrumentation=Eu._STORED_PARENT_SPAN=void 0;var Mo=(pe(),se(Pe)),eLt=_r(),Q3=W1e(),Z1e=Ft(),Y1e=K1e(),yv=(er(),se(Cr)),wk=["deleteOne","deleteMany","find","findOne","estimatedDocumentCount","countDocuments","distinct","where","$where","findOneAndUpdate","findOneAndDelete","findOneAndReplace"],tLt=["remove","count","findOneAndRemove",...wk],rLt=["count","findOneAndRemove",...wk],nLt=[...wk];function J1e(t){return t?t.startsWith("6.")||t.startsWith("5.")?tLt:t.startsWith("7.")?rLt:nLt:wk}function X1e(t){return t&&(t.startsWith("5.")||t.startsWith("6."))||!1}Eu._STORED_PARENT_SPAN=Symbol("stored-parent-span");var e9=class extends Z1e.InstrumentationBase{constructor(e={}){super(Y1e.PACKAGE_NAME,Y1e.PACKAGE_VERSION,e)}init(){return new Z1e.InstrumentationNodeModuleDefinition("mongoose",[">=5.9.7 <9"],this.patch.bind(this),this.unpatch.bind(this))}patch(e,r){let n=e[Symbol.toStringTag]==="Module"?e.default:e;return this._wrap(n.Model.prototype,"save",this.patchOnModelMethods("save",r)),n.Model.prototype.$save=n.Model.prototype.save,X1e(r)&&this._wrap(n.Model.prototype,"remove",this.patchOnModelMethods("remove",r)),this._wrap(n.Query.prototype,"exec",this.patchQueryExec(r)),this._wrap(n.Aggregate.prototype,"exec",this.patchAggregateExec(r)),J1e(r).forEach(i=>{this._wrap(n.Query.prototype,i,this.patchAndCaptureSpanContext(i))}),this._wrap(n.Model,"aggregate",this.patchModelAggregate()),this._wrap(n.Model,"insertMany",this.patchModelStatic("insertMany",r)),this._wrap(n.Model,"bulkWrite",this.patchModelStatic("bulkWrite",r)),n}unpatch(e,r){let n=e[Symbol.toStringTag]==="Module"?e.default:e,o=J1e(r);this._unwrap(n.Model.prototype,"save"),n.Model.prototype.$save=n.Model.prototype.save,X1e(r)&&this._unwrap(n.Model.prototype,"remove"),this._unwrap(n.Query.prototype,"exec"),this._unwrap(n.Aggregate.prototype,"exec"),o.forEach(i=>{this._unwrap(n.Query.prototype,i)}),this._unwrap(n.Model,"aggregate"),this._unwrap(n.Model,"insertMany"),this._unwrap(n.Model,"bulkWrite")}patchAggregateExec(e){let r=this;return n=>function(i){if(r.getConfig().requireParentSpan&&Mo.trace.getSpan(Mo.context.active())===void 0)return n.apply(this,arguments);let s=this[Eu._STORED_PARENT_SPAN],a={},{dbStatementSerializer:c}=r.getConfig();c&&(a[yv.SEMATTRS_DB_STATEMENT]=c("aggregate",{options:this.options,aggregatePipeline:this._pipeline}));let u=r._startSpan(this._model.collection,this._model?.modelName,"aggregate",a,s);return r._handleResponse(u,n,this,arguments,i,e)}}patchQueryExec(e){let r=this;return n=>function(i){if(r.getConfig().requireParentSpan&&Mo.trace.getSpan(Mo.context.active())===void 0)return n.apply(this,arguments);let s=this[Eu._STORED_PARENT_SPAN],a={},{dbStatementSerializer:c}=r.getConfig();c&&(a[yv.SEMATTRS_DB_STATEMENT]=c(this.op,{condition:this._conditions,updates:this._update,options:this.options,fields:this._fields}));let u=r._startSpan(this.mongooseCollection,this.model.modelName,this.op,a,s);return r._handleResponse(u,n,this,arguments,i,e)}}patchOnModelMethods(e,r){let n=this;return o=>function(s,a){if(n.getConfig().requireParentSpan&&Mo.trace.getSpan(Mo.context.active())===void 0)return o.apply(this,arguments);let c={document:this};s&&!(s instanceof Function)&&(c.options=s);let u={},{dbStatementSerializer:p}=n.getConfig();p&&(u[yv.SEMATTRS_DB_STATEMENT]=p(e,c));let f=n._startSpan(this.constructor.collection,this.constructor.modelName,e,u);return s instanceof Function&&(a=s,s=void 0),n._handleResponse(f,o,this,arguments,a,r)}}patchModelStatic(e,r){let n=this;return o=>function(s,a,c){if(n.getConfig().requireParentSpan&&Mo.trace.getSpan(Mo.context.active())===void 0)return o.apply(this,arguments);typeof a=="function"&&(c=a,a=void 0);let u={};switch(e){case"insertMany":u.documents=s;break;case"bulkWrite":u.operations=s;break;default:u.document=s;break}a!==void 0&&(u.options=a);let p={},{dbStatementSerializer:f}=n.getConfig();f&&(p[yv.SEMATTRS_DB_STATEMENT]=f(e,u));let m=n._startSpan(this.collection,this.modelName,e,p);return n._handleResponse(m,o,this,arguments,c,r)}}patchModelAggregate(){let e=this;return r=>function(){let o=Mo.trace.getSpan(Mo.context.active()),i=e._callOriginalFunction(()=>r.apply(this,arguments));return i&&(i[Eu._STORED_PARENT_SPAN]=o),i}}patchAndCaptureSpanContext(e){let r=this;return n=>function(){return this[Eu._STORED_PARENT_SPAN]=Mo.trace.getSpan(Mo.context.active()),r._callOriginalFunction(()=>n.apply(this,arguments))}}_startSpan(e,r,n,o,i){return this.tracer.startSpan(`mongoose.${r}.${n}`,{kind:Mo.SpanKind.CLIENT,attributes:{...o,...(0,Q3.getAttributesFromCollection)(e),[yv.SEMATTRS_DB_OPERATION]:n,[yv.SEMATTRS_DB_SYSTEM]:"mongoose"}},i?Mo.trace.setSpan(Mo.context.active(),i):void 0)}_handleResponse(e,r,n,o,i,s=void 0){let a=this;if(i instanceof Function)return a._callOriginalFunction(()=>(0,Q3.handleCallbackResponse)(i,r,n,e,o,a.getConfig().responseHook,s));{let c=a._callOriginalFunction(()=>r.apply(n,o));return(0,Q3.handlePromiseResponse)(c,e,a.getConfig().responseHook,s)}}_callOriginalFunction(e){return this.getConfig().suppressInternalInstrumentation?Mo.context.with((0,eLt.suppressTracing)(Mo.context.active()),e):e()}};Eu.MongooseInstrumentation=e9});var e$e=S(Rk=>{"use strict";Object.defineProperty(Rk,"__esModule",{value:!0});Rk.MongooseInstrumentation=void 0;var oLt=Q1e();Object.defineProperty(Rk,"MongooseInstrumentation",{enumerable:!0,get:function(){return oLt.MongooseInstrumentation}})});var i$e=S(e0=>{"use strict";Object.defineProperty(e0,"__esModule",{value:!0});e0.AttributeNames=void 0;var sLt;(function(t){t.MYSQL_VALUES="db.mysql.values"})(sLt=e0.AttributeNames||(e0.AttributeNames={}))});var a$e=S(ts=>{"use strict";Object.defineProperty(ts,"__esModule",{value:!0});ts.getPoolName=ts.arrayStringifyHelper=ts.getSpanName=ts.getDbValues=ts.getDbStatement=ts.getConnectionAttributes=void 0;var Nl=(er(),se(Cr));function aLt(t){let{host:e,port:r,database:n,user:o}=cLt(t),i=parseInt(r,10);return isNaN(i)?{[Nl.SEMATTRS_NET_PEER_NAME]:e,[Nl.SEMATTRS_DB_CONNECTION_STRING]:s$e(e,r,n),[Nl.SEMATTRS_DB_NAME]:n,[Nl.SEMATTRS_DB_USER]:o}:{[Nl.SEMATTRS_NET_PEER_NAME]:e,[Nl.SEMATTRS_NET_PEER_PORT]:i,[Nl.SEMATTRS_DB_CONNECTION_STRING]:s$e(e,r,n),[Nl.SEMATTRS_DB_NAME]:n,[Nl.SEMATTRS_DB_USER]:o}}ts.getConnectionAttributes=aLt;function cLt(t){let{host:e,port:r,database:n,user:o}=t&&t.connectionConfig||t||{};return{host:e,port:r,database:n,user:o}}function s$e(t,e,r){let n=`jdbc:mysql://${t||"localhost"}`;return typeof e=="number"&&(n+=`:${e}`),typeof r=="string"&&(n+=`/${r}`),n}function uLt(t){return typeof t=="string"?t:t.sql}ts.getDbStatement=uLt;function lLt(t,e){return t9(typeof t=="string"?e:e||t.values)}ts.getDbValues=lLt;function pLt(t){let e=typeof t=="object"?t.sql:t,r=e?.indexOf(" ");return typeof r=="number"&&r!==-1?e?.substring(0,r):e}ts.getSpanName=pLt;function t9(t){return t?`[${t.toString()}]`:""}ts.arrayStringifyHelper=t9;function dLt(t){let e=t.config.connectionConfig,r="";return r+=e.host?`host: '${e.host}', `:"",r+=e.port?`port: ${e.port}, `:"",r+=e.database?`database: '${e.database}', `:"",r+=e.user?`user: '${e.user}'`:"",e.user||(r=r.substring(0,r.length-2)),r.trim()}ts.getPoolName=dLt});var c$e=S(Ev=>{"use strict";Object.defineProperty(Ev,"__esModule",{value:!0});Ev.PACKAGE_NAME=Ev.PACKAGE_VERSION=void 0;Ev.PACKAGE_VERSION="0.49.0";Ev.PACKAGE_NAME="@opentelemetry/instrumentation-mysql"});var l$e=S(Pk=>{"use strict";Object.defineProperty(Pk,"__esModule",{value:!0});Pk.MySQLInstrumentation=void 0;var rs=(pe(),se(Pe)),Tv=Ft(),r9=(er(),se(Cr)),fLt=i$e(),bv=a$e(),u$e=c$e(),n9=class t extends Tv.InstrumentationBase{static COMMON_ATTRIBUTES={[r9.SEMATTRS_DB_SYSTEM]:r9.DBSYSTEMVALUES_MYSQL};constructor(e={}){super(u$e.PACKAGE_NAME,u$e.PACKAGE_VERSION,e),this._setMetricInstruments()}setMeterProvider(e){super.setMeterProvider(e),this._setMetricInstruments()}_setMetricInstruments(){this._connectionsUsage=this.meter.createUpDownCounter("db.client.connections.usage",{description:"The number of connections that are currently in state described by the state attribute.",unit:"{connection}"})}init(){return[new Tv.InstrumentationNodeModuleDefinition("mysql",[">=2.0.0 <3"],e=>((0,Tv.isWrapped)(e.createConnection)&&this._unwrap(e,"createConnection"),this._wrap(e,"createConnection",this._patchCreateConnection()),(0,Tv.isWrapped)(e.createPool)&&this._unwrap(e,"createPool"),this._wrap(e,"createPool",this._patchCreatePool()),(0,Tv.isWrapped)(e.createPoolCluster)&&this._unwrap(e,"createPoolCluster"),this._wrap(e,"createPoolCluster",this._patchCreatePoolCluster()),e),e=>{e!==void 0&&(this._unwrap(e,"createConnection"),this._unwrap(e,"createPool"),this._unwrap(e,"createPoolCluster"))})]}_patchCreateConnection(){return e=>{let r=this;return function(o){let i=e(...arguments);return r._wrap(i,"query",r._patchQuery(i)),i}}}_patchCreatePool(){return e=>{let r=this;return function(o){let i=e(...arguments);return r._wrap(i,"query",r._patchQuery(i)),r._wrap(i,"getConnection",r._patchGetConnection(i)),r._wrap(i,"end",r._patchPoolEnd(i)),r._setPoolcallbacks(i,r,""),i}}}_patchPoolEnd(e){return r=>{let n=this;return function(i){let s=e._allConnections.length,a=e._freeConnections.length,c=s-a,u=(0,bv.getPoolName)(e);n._connectionsUsage.add(-c,{state:"used",name:u}),n._connectionsUsage.add(-a,{state:"idle",name:u}),r.apply(e,arguments)}}}_patchCreatePoolCluster(){return e=>{let r=this;return function(o){let i=e(...arguments);return r._wrap(i,"getConnection",r._patchGetConnection(i)),r._wrap(i,"add",r._patchAdd(i)),i}}}_patchAdd(e){return r=>{let n=this;return function(i,s){if(!n._enabled)return n._unwrap(e,"add"),r.apply(e,arguments);r.apply(e,arguments);let a=e._nodes;if(a){let c=typeof i=="object"?"CLUSTER::"+e._lastId:String(i),u=a[c].pool;n._setPoolcallbacks(u,n,i)}}}}_patchGetConnection(e){return r=>{let n=this;return function(i,s,a){if(!n._enabled)return n._unwrap(e,"getConnection"),r.apply(e,arguments);if(arguments.length===1&&typeof i=="function"){let c=n._getConnectionCallbackPatchFn(i);return r.call(e,c)}if(arguments.length===2&&typeof s=="function"){let c=n._getConnectionCallbackPatchFn(s);return r.call(e,i,c)}if(arguments.length===3&&typeof a=="function"){let c=n._getConnectionCallbackPatchFn(a);return r.call(e,i,s,c)}return r.apply(e,arguments)}}}_getConnectionCallbackPatchFn(e){let r=this,n=rs.context.active();return function(o,i){i&&((0,Tv.isWrapped)(i.query)||r._wrap(i,"query",r._patchQuery(i))),typeof e=="function"&&rs.context.with(n,e,this,o,i)}}_patchQuery(e){return r=>{let n=this;return function(o,i,s){if(!n._enabled)return n._unwrap(e,"query"),r.apply(e,arguments);let a=n.tracer.startSpan((0,bv.getSpanName)(o),{kind:rs.SpanKind.CLIENT,attributes:{...t.COMMON_ATTRIBUTES,...(0,bv.getConnectionAttributes)(e.config)}});if(a.setAttribute(r9.SEMATTRS_DB_STATEMENT,(0,bv.getDbStatement)(o)),n.getConfig().enhancedDatabaseReporting){let p;Array.isArray(i)?p=i:arguments[2]&&(p=[i]),a.setAttribute(fLt.AttributeNames.MYSQL_VALUES,(0,bv.getDbValues)(o,p))}let c=Array.from(arguments).findIndex(p=>typeof p=="function"),u=rs.context.active();if(c===-1){let p=rs.context.with(rs.trace.setSpan(rs.context.active(),a),()=>r.apply(e,arguments));return rs.context.bind(u,p),p.on("error",f=>a.setStatus({code:rs.SpanStatusCode.ERROR,message:f.message})).on("end",()=>{a.end()})}else return n._wrap(arguments,c,n._patchCallbackQuery(a,u)),rs.context.with(rs.trace.setSpan(rs.context.active(),a),()=>r.apply(e,arguments))}}}_patchCallbackQuery(e,r){return n=>function(o,i,s){return o&&e.setStatus({code:rs.SpanStatusCode.ERROR,message:o.message}),e.end(),rs.context.with(r,()=>n(...arguments))}}_setPoolcallbacks(e,r,n){let o=n||(0,bv.getPoolName)(e);e.on("connection",i=>{r._connectionsUsage.add(1,{state:"idle",name:o})}),e.on("acquire",i=>{r._connectionsUsage.add(-1,{state:"idle",name:o}),r._connectionsUsage.add(1,{state:"used",name:o})}),e.on("release",i=>{r._connectionsUsage.add(-1,{state:"used",name:o}),r._connectionsUsage.add(1,{state:"idle",name:o})})}};Pk.MySQLInstrumentation=n9});var p$e=S(Ik=>{"use strict";Object.defineProperty(Ik,"__esModule",{value:!0});Ik.MySQLInstrumentation=void 0;var mLt=l$e();Object.defineProperty(Ik,"MySQLInstrumentation",{enumerable:!0,get:function(){return mLt.MySQLInstrumentation}})});var i9=S(Ok=>{"use strict";Object.defineProperty(Ok,"__esModule",{value:!0});Ok.addSqlCommenterComment=void 0;var o9=(pe(),se(Pe)),gLt=_r();function _Lt(t){let e=t.indexOf("--");if(e>=0)return!0;if(t.indexOf("/*")<0)return!1;let n=t.indexOf("*/");return e`%${e.charCodeAt(0).toString(16).toUpperCase()}`)}function SLt(t,e){if(typeof e!="string"||e.length===0||_Lt(e))return e;let r=new gLt.W3CTraceContextPropagator,n={};r.inject(o9.trace.setSpan(o9.ROOT_CONTEXT,t),n,o9.defaultTextMapSetter);let o=Object.keys(n).sort();if(o.length===0)return e;let i=o.map(s=>{let a=vLt(n[s]);return`${s}='${a}'`}).join(",");return`${e} /*${i}*/`}Ok.addSqlCommenterComment=SLt});var _$e=S(ba=>{"use strict";Object.defineProperty(ba,"__esModule",{value:!0});ba.getConnectionPrototypeToInstrument=ba.once=ba.getSpanName=ba.getDbStatement=ba.getConnectionAttributes=void 0;var Cl=(er(),se(Cr));function yLt(t){let{host:e,port:r,database:n,user:o}=ELt(t),i=parseInt(r,10);return isNaN(i)?{[Cl.SEMATTRS_NET_PEER_NAME]:e,[Cl.SEMATTRS_DB_CONNECTION_STRING]:g$e(e,r,n),[Cl.SEMATTRS_DB_NAME]:n,[Cl.SEMATTRS_DB_USER]:o}:{[Cl.SEMATTRS_NET_PEER_NAME]:e,[Cl.SEMATTRS_NET_PEER_PORT]:i,[Cl.SEMATTRS_DB_CONNECTION_STRING]:g$e(e,r,n),[Cl.SEMATTRS_DB_NAME]:n,[Cl.SEMATTRS_DB_USER]:o}}ba.getConnectionAttributes=yLt;function ELt(t){let{host:e,port:r,database:n,user:o}=t&&t.connectionConfig||t||{};return{host:e,port:r,database:n,user:o}}function g$e(t,e,r){let n=`jdbc:mysql://${t||"localhost"}`;return typeof e=="number"&&(n+=`:${e}`),typeof r=="string"&&(n+=`/${r}`),n}function TLt(t,e,r){return e?typeof t=="string"?r?e(t,r):t:r||t.values?e(t.sql,r||t.values):t.sql:typeof t=="string"?t:t.sql}ba.getDbStatement=TLt;function bLt(t){let e=typeof t=="object"?t.sql:t,r=e?.indexOf(" ");return typeof r=="number"&&r!==-1?e?.substring(0,r):e}ba.getSpanName=bLt;var xLt=t=>{let e=!1;return(...r)=>{if(!e)return e=!0,t(...r)}};ba.once=xLt;function ALt(t){let e=t.prototype,r=Object.getPrototypeOf(e);return typeof r?.query=="function"&&typeof r?.execute=="function"?r:e}ba.getConnectionPrototypeToInstrument=ALt});var v$e=S(xv=>{"use strict";Object.defineProperty(xv,"__esModule",{value:!0});xv.PACKAGE_NAME=xv.PACKAGE_VERSION=void 0;xv.PACKAGE_VERSION="0.49.0";xv.PACKAGE_NAME="@opentelemetry/instrumentation-mysql2"});var T$e=S(Nk=>{"use strict";Object.defineProperty(Nk,"__esModule",{value:!0});Nk.MySQL2Instrumentation=void 0;var S$e=(pe(),se(Pe)),Nm=Ft(),s9=(er(),se(Cr)),y$e=i9(),Av=_$e(),E$e=v$e(),a9=[">=1.4.2 <4"],c9=class t extends Nm.InstrumentationBase{static COMMON_ATTRIBUTES={[s9.SEMATTRS_DB_SYSTEM]:s9.DBSYSTEMVALUES_MYSQL};constructor(e={}){super(E$e.PACKAGE_NAME,E$e.PACKAGE_VERSION,e)}init(){let e;function r(i){!e&&i.format&&(e=i.format)}let n=i=>{(0,Nm.isWrapped)(i.query)&&this._unwrap(i,"query"),this._wrap(i,"query",this._patchQuery(e,!1)),(0,Nm.isWrapped)(i.execute)&&this._unwrap(i,"execute"),this._wrap(i,"execute",this._patchQuery(e,!0))},o=i=>{this._unwrap(i,"query"),this._unwrap(i,"execute")};return[new Nm.InstrumentationNodeModuleDefinition("mysql2",a9,i=>(r(i),i),()=>{},[new Nm.InstrumentationNodeModuleFile("mysql2/promise.js",a9,i=>(r(i),i),()=>{}),new Nm.InstrumentationNodeModuleFile("mysql2/lib/connection.js",a9,i=>{let s=(0,Av.getConnectionPrototypeToInstrument)(i);return n(s),i},i=>{if(i===void 0)return;let s=(0,Av.getConnectionPrototypeToInstrument)(i);o(s)})])]}_patchQuery(e,r){return n=>{let o=this;return function(i,s,a){let c;Array.isArray(s)?c=s:arguments[2]&&(c=[s]);let u=o.tracer.startSpan((0,Av.getSpanName)(i),{kind:S$e.SpanKind.CLIENT,attributes:{...t.COMMON_ATTRIBUTES,...(0,Av.getConnectionAttributes)(this.config),[s9.SEMATTRS_DB_STATEMENT]:(0,Av.getDbStatement)(i,e,c)}});!r&&o.getConfig().addSqlCommenterCommentToQueries&&(arguments[0]=i=typeof i=="string"?(0,y$e.addSqlCommenterComment)(u,i):Object.assign(i,{sql:(0,y$e.addSqlCommenterComment)(u,i.sql)}));let p=(0,Av.once)((f,m)=>{if(f)u.setStatus({code:S$e.SpanStatusCode.ERROR,message:f.message});else{let{responseHook:h}=o.getConfig();typeof h=="function"&&(0,Nm.safeExecuteInTheMiddle)(()=>{h(u,{queryResults:m})},_=>{_&&o._diag.warn("Failed executing responseHook",_)},!0)}u.end()});if(arguments.length===1){typeof i.onResult=="function"&&o._wrap(i,"onResult",o._patchCallbackQuery(p));let f=n.apply(this,arguments);return f.once("error",m=>{p(m)}).once("result",m=>{p(void 0,m)}),f}return typeof arguments[1]=="function"?o._wrap(arguments,1,o._patchCallbackQuery(p)):typeof arguments[2]=="function"&&o._wrap(arguments,2,o._patchCallbackQuery(p)),n.apply(this,arguments)}}}_patchCallbackQuery(e){return r=>function(n,o,i){return e(n,o),r(...arguments)}}};Nk.MySQL2Instrumentation=c9});var b$e=S(Ck=>{"use strict";Object.defineProperty(Ck,"__esModule",{value:!0});Ck.MySQL2Instrumentation=void 0;var wLt=T$e();Object.defineProperty(Ck,"MySQL2Instrumentation",{enumerable:!0,get:function(){return wLt.MySQL2Instrumentation}})});var P$e=S($k=>{"use strict";Object.defineProperty($k,"__esModule",{value:!0});$k.endSpan=void 0;var PLt=(pe(),se(Pe)),ILt=(t,e)=>{e&&(t.recordException(e),t.setStatus({code:PLt.SpanStatusCode.ERROR,message:e.message})),t.end()};$k.endSpan=ILt});var Mk=S(kk=>{"use strict";Object.defineProperty(kk,"__esModule",{value:!0});kk.defaultDbStatementSerializer=void 0;var OLt=[{regex:/^ECHO/i,args:0},{regex:/^(LPUSH|MSET|PFA|PUBLISH|RPUSH|SADD|SET|SPUBLISH|XADD|ZADD)/i,args:1},{regex:/^(HSET|HMSET|LSET|LINSERT)/i,args:2},{regex:/^(ACL|BIT|B[LRZ]|CLIENT|CLUSTER|CONFIG|COMMAND|DECR|DEL|EVAL|EX|FUNCTION|GEO|GET|HINCR|HMGET|HSCAN|INCR|L[TRLM]|MEMORY|P[EFISTU]|RPOP|S[CDIMORSU]|XACK|X[CDGILPRT]|Z[CDILMPRS])/i,args:-1}],NLt=(t,e)=>{if(Array.isArray(e)&&e.length){let r=OLt.find(({regex:o})=>o.test(t))?.args??0,n=r>=0?e.slice(0,r):e;return e.length>n.length&&n.push(`[${e.length-r} other arguments]`),`${t} ${n.join(" ")}`}return t};kk.defaultDbStatementSerializer=NLt});var I$e=S(wv=>{"use strict";Object.defineProperty(wv,"__esModule",{value:!0});wv.PACKAGE_NAME=wv.PACKAGE_VERSION=void 0;wv.PACKAGE_VERSION="0.51.0";wv.PACKAGE_NAME="@opentelemetry/instrumentation-ioredis"});var $$e=S(Lk=>{"use strict";Object.defineProperty(Lk,"__esModule",{value:!0});Lk.IORedisInstrumentation=void 0;var Rd=(pe(),se(Pe)),Dk=Ft(),xa=(er(),se(Cr)),O$e=Ft(),t0=P$e(),CLt=Mk(),N$e=I$e(),C$e={requireParentSpan:!0},u9=class extends Dk.InstrumentationBase{constructor(e={}){super(N$e.PACKAGE_NAME,N$e.PACKAGE_VERSION,{...C$e,...e})}setConfig(e={}){super.setConfig({...C$e,...e})}init(){return[new Dk.InstrumentationNodeModuleDefinition("ioredis",[">=2.0.0 <6"],(e,r)=>{let n=e[Symbol.toStringTag]==="Module"?e.default:e;return(0,Dk.isWrapped)(n.prototype.sendCommand)&&this._unwrap(n.prototype,"sendCommand"),this._wrap(n.prototype,"sendCommand",this._patchSendCommand(r)),(0,Dk.isWrapped)(n.prototype.connect)&&this._unwrap(n.prototype,"connect"),this._wrap(n.prototype,"connect",this._patchConnection()),e},e=>{if(e===void 0)return;let r=e[Symbol.toStringTag]==="Module"?e.default:e;this._unwrap(r.prototype,"sendCommand"),this._unwrap(r.prototype,"connect")})]}_patchSendCommand(e){return r=>this._traceSendCommand(r,e)}_patchConnection(){return e=>this._traceConnection(e)}_traceSendCommand(e,r){let n=this;return function(o){if(arguments.length<1||typeof o!="object")return e.apply(this,arguments);let i=n.getConfig(),s=i.dbStatementSerializer||CLt.defaultDbStatementSerializer,a=Rd.trace.getSpan(Rd.context.active())===void 0;if(i.requireParentSpan===!0&&a)return e.apply(this,arguments);let c=n.tracer.startSpan(o.name,{kind:Rd.SpanKind.CLIENT,attributes:{[xa.SEMATTRS_DB_SYSTEM]:xa.DBSYSTEMVALUES_REDIS,[xa.SEMATTRS_DB_STATEMENT]:s(o.name,o.args)}}),{requestHook:u}=i;u&&(0,O$e.safeExecuteInTheMiddle)(()=>u(c,{moduleVersion:r,cmdName:o.name,cmdArgs:o.args}),m=>{m&&Rd.diag.error("ioredis instrumentation: request hook failed",m)},!0);let{host:p,port:f}=this.options;c.setAttributes({[xa.SEMATTRS_NET_PEER_NAME]:p,[xa.SEMATTRS_NET_PEER_PORT]:f,[xa.SEMATTRS_DB_CONNECTION_STRING]:`redis://${p}:${f}`});try{let m=e.apply(this,arguments),h=o.resolve;o.resolve=function(v){(0,O$e.safeExecuteInTheMiddle)(()=>i.responseHook?.(c,o.name,o.args,v),E=>{E&&Rd.diag.error("ioredis instrumentation: response hook failed",E)},!0),(0,t0.endSpan)(c,null),h(v)};let _=o.reject;return o.reject=function(v){(0,t0.endSpan)(c,v),_(v)},m}catch(m){throw(0,t0.endSpan)(c,m),m}}}_traceConnection(e){let r=this;return function(){let n=Rd.trace.getSpan(Rd.context.active())===void 0;if(r.getConfig().requireParentSpan===!0&&n)return e.apply(this,arguments);let o=r.tracer.startSpan("connect",{kind:Rd.SpanKind.CLIENT,attributes:{[xa.SEMATTRS_DB_SYSTEM]:xa.DBSYSTEMVALUES_REDIS,[xa.SEMATTRS_DB_STATEMENT]:"connect"}}),{host:i,port:s}=this.options;o.setAttributes({[xa.SEMATTRS_NET_PEER_NAME]:i,[xa.SEMATTRS_NET_PEER_PORT]:s,[xa.SEMATTRS_DB_CONNECTION_STRING]:`redis://${i}:${s}`});try{let a=e.apply(this,arguments);return(0,t0.endSpan)(o,null),a}catch(a){throw(0,t0.endSpan)(o,a),a}}}};Lk.IORedisInstrumentation=u9});var k$e=S(Uk=>{"use strict";Object.defineProperty(Uk,"__esModule",{value:!0});Uk.IORedisInstrumentation=void 0;var $Lt=$$e();Object.defineProperty(Uk,"IORedisInstrumentation",{enumerable:!0,get:function(){return $Lt.IORedisInstrumentation}})});var jk=S(Rv=>{"use strict";Object.defineProperty(Rv,"__esModule",{value:!0});Rv.PACKAGE_NAME=Rv.PACKAGE_VERSION=void 0;Rv.PACKAGE_VERSION="0.51.0";Rv.PACKAGE_NAME="@opentelemetry/instrumentation-redis"});var M$e=S(Pd=>{"use strict";Object.defineProperty(Pd,"__esModule",{value:!0});Pd.getTracedCreateStreamTrace=Pd.getTracedCreateClient=Pd.endSpan=void 0;var r0=(pe(),se(Pe)),kLt=(t,e)=>{e&&t.setStatus({code:r0.SpanStatusCode.ERROR,message:e.message}),t.end()};Pd.endSpan=kLt;var MLt=t=>function(){let r=t.apply(this,arguments);return r0.context.bind(r0.context.active(),r)};Pd.getTracedCreateClient=MLt;var DLt=t=>function(){return Object.prototype.hasOwnProperty.call(this,"stream")||Object.defineProperty(this,"stream",{get(){return this._patched_redis_stream},set(r){r0.context.bind(r0.context.active(),r),this._patched_redis_stream=r}}),t.apply(this,arguments)};Pd.getTracedCreateStreamTrace=DLt});var L$e=S(Fk=>{"use strict";Object.defineProperty(Fk,"__esModule",{value:!0});Fk.RedisInstrumentationV2_V3=void 0;var Pv=Ft(),zk=M$e(),D$e=jk(),n0=(pe(),se(Pe)),Iv=(er(),se(Cr)),LLt=Mk(),l9=class t extends Pv.InstrumentationBase{static COMPONENT="redis";constructor(e={}){super(D$e.PACKAGE_NAME,D$e.PACKAGE_VERSION,e)}init(){return[new Pv.InstrumentationNodeModuleDefinition("redis",[">=2.6.0 <4"],e=>((0,Pv.isWrapped)(e.RedisClient.prototype.internal_send_command)&&this._unwrap(e.RedisClient.prototype,"internal_send_command"),this._wrap(e.RedisClient.prototype,"internal_send_command",this._getPatchInternalSendCommand()),(0,Pv.isWrapped)(e.RedisClient.prototype.create_stream)&&this._unwrap(e.RedisClient.prototype,"create_stream"),this._wrap(e.RedisClient.prototype,"create_stream",this._getPatchCreateStream()),(0,Pv.isWrapped)(e.createClient)&&this._unwrap(e,"createClient"),this._wrap(e,"createClient",this._getPatchCreateClient()),e),e=>{e!==void 0&&(this._unwrap(e.RedisClient.prototype,"internal_send_command"),this._unwrap(e.RedisClient.prototype,"create_stream"),this._unwrap(e,"createClient"))})]}_getPatchInternalSendCommand(){let e=this;return function(n){return function(i){if(arguments.length!==1||typeof i!="object")return n.apply(this,arguments);let s=e.getConfig(),a=n0.trace.getSpan(n0.context.active())===void 0;if(s.requireParentSpan===!0&&a)return n.apply(this,arguments);let c=s?.dbStatementSerializer||LLt.defaultDbStatementSerializer,u=e.tracer.startSpan(`${t.COMPONENT}-${i.command}`,{kind:n0.SpanKind.CLIENT,attributes:{[Iv.SEMATTRS_DB_SYSTEM]:Iv.DBSYSTEMVALUES_REDIS,[Iv.SEMATTRS_DB_STATEMENT]:c(i.command,i.args)}});this.connection_options&&u.setAttributes({[Iv.SEMATTRS_NET_PEER_NAME]:this.connection_options.host,[Iv.SEMATTRS_NET_PEER_PORT]:this.connection_options.port}),this.address&&u.setAttribute(Iv.SEMATTRS_DB_CONNECTION_STRING,`redis://${this.address}`);let p=arguments[0].callback;if(p){let f=n0.context.active();arguments[0].callback=function(h,_){if(s?.responseHook){let v=s.responseHook;(0,Pv.safeExecuteInTheMiddle)(()=>{v(u,i.command,i.args,_)},E=>{E&&e._diag.error("Error executing responseHook",E)},!0)}return(0,zk.endSpan)(u,h),n0.context.with(f,p,this,...arguments)}}try{return n.apply(this,arguments)}catch(f){throw(0,zk.endSpan)(u,f),f}}}}_getPatchCreateClient(){return function(r){return(0,zk.getTracedCreateClient)(r)}}_getPatchCreateStream(){return function(r){return(0,zk.getTracedCreateStreamTrace)(r)}}};Fk.RedisInstrumentationV2_V3=l9});var U$e=S(qk=>{"use strict";Object.defineProperty(qk,"__esModule",{value:!0});qk.getClientAttributes=void 0;var o0=(er(),se(Cr));function ULt(t,e){return{[o0.SEMATTRS_DB_SYSTEM]:o0.DBSYSTEMVALUES_REDIS,[o0.SEMATTRS_NET_PEER_NAME]:e?.socket?.host,[o0.SEMATTRS_NET_PEER_PORT]:e?.socket?.port,[o0.SEMATTRS_DB_CONNECTION_STRING]:jLt(t,e?.url)}}qk.getClientAttributes=ULt;function jLt(t,e){if(!(typeof e!="string"||!e))try{let r=new URL(e);return r.searchParams.delete("user_pwd"),r.username="",r.password="",r.href}catch(r){t.error("failed to sanitize redis connection url",r)}}});var q$e=S(Bk=>{"use strict";Object.defineProperty(Bk,"__esModule",{value:!0});Bk.RedisInstrumentationV4=void 0;var Aa=(pe(),se(Pe)),go=Ft(),j$e=U$e(),zLt=Mk(),z$e=jk(),FLt=(er(),se(Cr)),i0=Symbol("opentelemetry.instrumentation.redis.open_spans"),F$e=Symbol("opentelemetry.instrumentation.redis.multi_command_options"),p9=class t extends go.InstrumentationBase{static COMPONENT="redis";constructor(e={}){super(z$e.PACKAGE_NAME,z$e.PACKAGE_VERSION,e)}init(){return[this._getInstrumentationNodeModuleDefinition("@redis/client"),this._getInstrumentationNodeModuleDefinition("@node-redis/client")]}_getInstrumentationNodeModuleDefinition(e){let r=new go.InstrumentationNodeModuleFile(`${e}/dist/lib/commander.js`,["^1.0.0"],(i,s)=>{let a=i.transformCommandArguments;if(!a)return this._diag.error("internal instrumentation error, missing transformCommandArguments function"),i;let c=s?.startsWith("1.0.")?"extendWithCommands":"attachCommands";return(0,go.isWrapped)(i?.[c])&&this._unwrap(i,c),this._wrap(i,c,this._getPatchExtendWithCommands(a)),i},i=>{(0,go.isWrapped)(i?.extendWithCommands)&&this._unwrap(i,"extendWithCommands"),(0,go.isWrapped)(i?.attachCommands)&&this._unwrap(i,"attachCommands")}),n=new go.InstrumentationNodeModuleFile(`${e}/dist/lib/client/multi-command.js`,["^1.0.0"],i=>{let s=i?.default?.prototype;return(0,go.isWrapped)(s?.exec)&&this._unwrap(s,"exec"),this._wrap(s,"exec",this._getPatchMultiCommandsExec()),(0,go.isWrapped)(s?.addCommand)&&this._unwrap(s,"addCommand"),this._wrap(s,"addCommand",this._getPatchMultiCommandsAddCommand()),i},i=>{let s=i?.default?.prototype;(0,go.isWrapped)(s?.exec)&&this._unwrap(s,"exec"),(0,go.isWrapped)(s?.addCommand)&&this._unwrap(s,"addCommand")}),o=new go.InstrumentationNodeModuleFile(`${e}/dist/lib/client/index.js`,["^1.0.0"],i=>{let s=i?.default?.prototype;return s?.multi&&((0,go.isWrapped)(s?.multi)&&this._unwrap(s,"multi"),this._wrap(s,"multi",this._getPatchRedisClientMulti())),s?.MULTI&&((0,go.isWrapped)(s?.MULTI)&&this._unwrap(s,"MULTI"),this._wrap(s,"MULTI",this._getPatchRedisClientMulti())),(0,go.isWrapped)(s?.sendCommand)&&this._unwrap(s,"sendCommand"),this._wrap(s,"sendCommand",this._getPatchRedisClientSendCommand()),this._wrap(s,"connect",this._getPatchedClientConnect()),i},i=>{let s=i?.default?.prototype;(0,go.isWrapped)(s?.multi)&&this._unwrap(s,"multi"),(0,go.isWrapped)(s?.MULTI)&&this._unwrap(s,"MULTI"),(0,go.isWrapped)(s?.sendCommand)&&this._unwrap(s,"sendCommand")});return new go.InstrumentationNodeModuleDefinition(e,["^1.0.0"],i=>i,()=>{},[r,n,o])}_getPatchExtendWithCommands(e){let r=this;return function(o){return function(s){if(s?.BaseClass?.name!=="RedisClient")return o.apply(this,arguments);let a=s.executor;return s.executor=function(c,u){let p=e(c,u).args;return r._traceClientCommand(a,this,arguments,p)},o.apply(this,arguments)}}}_getPatchMultiCommandsExec(){let e=this;return function(n){return function(){let i=n.apply(this,arguments);return typeof i?.then!="function"?(e._diag.error("got non promise result when patching RedisClientMultiCommand.exec"),i):i.then(s=>{let a=this[i0];return e._endSpansWithRedisReplies(a,s),s}).catch(s=>{let a=this[i0];if(!a)e._diag.error("cannot find open spans to end for redis multi command");else{let c=s.constructor.name==="MultiErrorReply"?s.replies:new Array(a.length).fill(s);e._endSpansWithRedisReplies(a,c)}return Promise.reject(s)})}}}_getPatchMultiCommandsAddCommand(){let e=this;return function(n){return function(i){return e._traceClientCommand(n,this,arguments,i)}}}_getPatchRedisClientMulti(){return function(r){return function(){let o=r.apply(this,arguments);return o[F$e]=this.options,o}}}_getPatchRedisClientSendCommand(){let e=this;return function(n){return function(i){return e._traceClientCommand(n,this,arguments,i)}}}_getPatchedClientConnect(){let e=this;return function(n){return function(){let i=this.options,s=(0,j$e.getClientAttributes)(e._diag,i),a=e.tracer.startSpan(`${t.COMPONENT}-connect`,{kind:Aa.SpanKind.CLIENT,attributes:s});return Aa.context.with(Aa.trace.setSpan(Aa.context.active(),a),()=>n.apply(this)).then(u=>(a.end(),u)).catch(u=>(a.recordException(u),a.setStatus({code:Aa.SpanStatusCode.ERROR,message:u.message}),a.end(),Promise.reject(u)))}}}_traceClientCommand(e,r,n,o){if(Aa.trace.getSpan(Aa.context.active())===void 0&&this.getConfig().requireParentSpan)return e.apply(r,n);let s=r.options||r[F$e],a=o[0],c=o.slice(1),u=this.getConfig().dbStatementSerializer||zLt.defaultDbStatementSerializer,p=(0,j$e.getClientAttributes)(this._diag,s);try{let h=u(a,c);h!=null&&(p[FLt.SEMATTRS_DB_STATEMENT]=h)}catch(h){this._diag.error("dbStatementSerializer throw an exception",h,{commandName:a})}let f=this.tracer.startSpan(`${t.COMPONENT}-${a}`,{kind:Aa.SpanKind.CLIENT,attributes:p}),m=Aa.context.with(Aa.trace.setSpan(Aa.context.active(),f),()=>e.apply(r,n));if(typeof m?.then=="function")m.then(h=>{this._endSpanWithResponse(f,a,c,h,void 0)},h=>{this._endSpanWithResponse(f,a,c,null,h)});else{let h=m;h[i0]=h[i0]||[],h[i0].push({span:f,commandName:a,commandArgs:c})}return m}_endSpansWithRedisReplies(e,r){if(!e)return this._diag.error("cannot find open spans to end for redis multi command");if(r.length!==e.length)return this._diag.error("number of multi command spans does not match response from redis");for(let n=0;n{"use strict";Object.defineProperty(Gk,"__esModule",{value:!0});Gk.RedisInstrumentation=void 0;var qLt=Ft(),B$e=jk(),BLt=L$e(),GLt=q$e(),G$e={requireParentSpan:!1},d9=class extends qLt.InstrumentationBase{instrumentationV2_V3;instrumentationV4;initialized=!1;constructor(e={}){let r={...G$e,...e};super(B$e.PACKAGE_NAME,B$e.PACKAGE_VERSION,r),this.instrumentationV2_V3=new BLt.RedisInstrumentationV2_V3(this.getConfig()),this.instrumentationV4=new GLt.RedisInstrumentationV4(this.getConfig()),this.initialized=!0}setConfig(e={}){let r={...G$e,...e};super.setConfig(r),this.initialized&&(this.instrumentationV2_V3.setConfig(r),this.instrumentationV4.setConfig(r))}init(){}setTracerProvider(e){super.setTracerProvider(e),this.initialized&&(this.instrumentationV2_V3.setTracerProvider(e),this.instrumentationV4.setTracerProvider(e))}enable(){super.enable(),this.initialized&&(this.instrumentationV2_V3.enable(),this.instrumentationV4.enable())}disable(){super.disable(),this.initialized&&(this.instrumentationV2_V3.disable(),this.instrumentationV4.disable())}};Gk.RedisInstrumentation=d9});var H$e=S(Vk=>{"use strict";Object.defineProperty(Vk,"__esModule",{value:!0});Vk.RedisInstrumentation=void 0;var VLt=V$e();Object.defineProperty(Vk,"RedisInstrumentation",{enumerable:!0,get:function(){return VLt.RedisInstrumentation}})});var nke=S(Hk=>{"use strict";Object.defineProperty(Hk,"__esModule",{value:!0});Hk.EVENT_LISTENERS_SET=void 0;Hk.EVENT_LISTENERS_SET=Symbol("opentelemetry.instrumentation.pg.eventListenersSet")});var g9=S(a0=>{"use strict";Object.defineProperty(a0,"__esModule",{value:!0});a0.AttributeNames=void 0;var XLt;(function(t){t.PG_VALUES="db.postgresql.values",t.PG_PLAN="db.postgresql.plan",t.IDLE_TIMEOUT_MILLIS="db.postgresql.idle.timeout.millis",t.MAX_CLIENT="db.postgresql.max.client"})(XLt=a0.AttributeNames||(a0.AttributeNames={}))});var _9=S(qn=>{"use strict";Object.defineProperty(qn,"__esModule",{value:!0});qn.METRIC_DB_CLIENT_OPERATION_DURATION=qn.METRIC_DB_CLIENT_CONNECTION_PENDING_REQUESTS=qn.METRIC_DB_CLIENT_CONNECTION_COUNT=qn.DB_CLIENT_CONNECTION_STATE_VALUE_IDLE=qn.DB_CLIENT_CONNECTION_STATE_VALUE_USED=qn.ATTR_DB_OPERATION_NAME=qn.ATTR_DB_NAMESPACE=qn.ATTR_DB_CLIENT_CONNECTION_STATE=qn.ATTR_DB_CLIENT_CONNECTION_POOL_NAME=void 0;qn.ATTR_DB_CLIENT_CONNECTION_POOL_NAME="db.client.connection.pool.name";qn.ATTR_DB_CLIENT_CONNECTION_STATE="db.client.connection.state";qn.ATTR_DB_NAMESPACE="db.namespace";qn.ATTR_DB_OPERATION_NAME="db.operation.name";qn.DB_CLIENT_CONNECTION_STATE_VALUE_USED="used";qn.DB_CLIENT_CONNECTION_STATE_VALUE_IDLE="idle";qn.METRIC_DB_CLIENT_CONNECTION_COUNT="db.client.connection.count";qn.METRIC_DB_CLIENT_CONNECTION_PENDING_REQUESTS="db.client.connection.pending_requests";qn.METRIC_DB_CLIENT_OPERATION_DURATION="db.client.operation.duration"});var v9=S(c0=>{"use strict";Object.defineProperty(c0,"__esModule",{value:!0});c0.SpanNames=void 0;var QLt;(function(t){t.QUERY_PREFIX="pg.query",t.CONNECT="pg.connect",t.POOL_CONNECT="pg-pool.connect"})(QLt=c0.SpanNames||(c0.SpanNames={}))});var pke=S(or=>{"use strict";Object.defineProperty(or,"__esModule",{value:!0});or.isObjectWithTextString=or.getErrorMessage=or.patchClientConnectCallback=or.patchCallbackPGPool=or.updateCounter=or.getPoolName=or.patchCallback=or.handleExecutionResult=or.handleConfigQuery=or.shouldSkipInstrumentation=or.getSemanticAttributesFromPool=or.getSemanticAttributesFromConnection=or.getConnectionString=or.parseAndMaskConnectionString=or.parseNormalizedOperationName=or.getQuerySpanName=void 0;var Id=(pe(),se(Pe)),Wk=g9(),ii=(er(),se(Cr)),Cm=_9(),eUt=Ft(),oke=v9();function ike(t,e){if(!e)return oke.SpanNames.QUERY_PREFIX;let r=typeof e.name=="string"&&e.name?e.name:ske(e.text);return`${oke.SpanNames.QUERY_PREFIX}:${r}${t?` ${t}`:""}`}or.getQuerySpanName=ike;function ske(t){let e=t.indexOf(" "),r=e===-1?t:t.slice(0,e);return r=r.toUpperCase(),r.endsWith(";")?r.slice(0,-1):r}or.parseNormalizedOperationName=ske;function ake(t){try{let e=new URL(t);return e.username="",e.password="",e.toString()}catch{return"postgresql://localhost:5432/"}}or.parseAndMaskConnectionString=ake;function S9(t){if("connectionString"in t&&t.connectionString)return ake(t.connectionString);let e=t.host||"localhost",r=t.port||5432,n=t.database||"";return`postgresql://${e}:${r}/${n}`}or.getConnectionString=S9;function cke(t){if(Number.isInteger(t))return t}function uke(t){return{[ii.SEMATTRS_DB_SYSTEM]:ii.DBSYSTEMVALUES_POSTGRESQL,[ii.SEMATTRS_DB_NAME]:t.database,[ii.SEMATTRS_DB_CONNECTION_STRING]:S9(t),[ii.SEMATTRS_NET_PEER_NAME]:t.host,[ii.SEMATTRS_NET_PEER_PORT]:cke(t.port),[ii.SEMATTRS_DB_USER]:t.user}}or.getSemanticAttributesFromConnection=uke;function tUt(t){let e;try{e=t.connectionString?new URL(t.connectionString):void 0}catch{e=void 0}return{[ii.SEMATTRS_DB_SYSTEM]:ii.DBSYSTEMVALUES_POSTGRESQL,[ii.SEMATTRS_DB_NAME]:e?.pathname.slice(1)??t.database,[ii.SEMATTRS_DB_CONNECTION_STRING]:S9(t),[ii.SEMATTRS_NET_PEER_NAME]:e?.hostname??t.host,[ii.SEMATTRS_NET_PEER_PORT]:Number(e?.port)||cke(t.port),[ii.SEMATTRS_DB_USER]:e?.username??t.user,[Wk.AttributeNames.IDLE_TIMEOUT_MILLIS]:t.idleTimeoutMillis,[Wk.AttributeNames.MAX_CLIENT]:t.maxClient}}or.getSemanticAttributesFromPool=tUt;function rUt(t){return t.requireParentSpan===!0&&Id.trace.getSpan(Id.context.active())===void 0}or.shouldSkipInstrumentation=rUt;function nUt(t,e,r){let{connectionParameters:n}=this,o=n.database,i=ike(o,r),s=t.startSpan(i,{kind:Id.SpanKind.CLIENT,attributes:uke(n)});if(!r)return s;if(r.text&&s.setAttribute(ii.SEMATTRS_DB_STATEMENT,r.text),e.enhancedDatabaseReporting&&Array.isArray(r.values))try{let a=r.values.map(c=>c==null?"null":c instanceof Buffer?c.toString():typeof c=="object"?typeof c.toPostgres=="function"?c.toPostgres():JSON.stringify(c):c.toString());s.setAttribute(Wk.AttributeNames.PG_VALUES,a)}catch(a){Id.diag.error("failed to stringify ",r.values,a)}return typeof r.name=="string"&&s.setAttribute(Wk.AttributeNames.PG_PLAN,r.name),s}or.handleConfigQuery=nUt;function lke(t,e,r){typeof t.responseHook=="function"&&(0,eUt.safeExecuteInTheMiddle)(()=>{t.responseHook(e,{data:r})},n=>{n&&Id.diag.error("Error running response hook",n)},!0)}or.handleExecutionResult=lke;function oUt(t,e,r,n,o){return function(s,a){s?(Object.prototype.hasOwnProperty.call(s,"code")&&(n[ii.ATTR_ERROR_TYPE]=s.code),e.setStatus({code:Id.SpanStatusCode.ERROR,message:s.message})):lke(t,e,a),o(),e.end(),r.call(this,s,a)}}or.patchCallback=oUt;function iUt(t){let e="";return e+=(t?.host?`${t.host}`:"unknown_host")+":",e+=(t?.port?`${t.port}`:"unknown_port")+"/",e+=t?.database?`${t.database}`:"unknown_database",e.trim()}or.getPoolName=iUt;function sUt(t,e,r,n,o){let i=e.totalCount,s=e.waitingCount,a=e.idleCount,c=i-a;return r.add(c-o.used,{[Cm.ATTR_DB_CLIENT_CONNECTION_STATE]:Cm.DB_CLIENT_CONNECTION_STATE_VALUE_USED,[Cm.ATTR_DB_CLIENT_CONNECTION_POOL_NAME]:t}),r.add(a-o.idle,{[Cm.ATTR_DB_CLIENT_CONNECTION_STATE]:Cm.DB_CLIENT_CONNECTION_STATE_VALUE_IDLE,[Cm.ATTR_DB_CLIENT_CONNECTION_POOL_NAME]:t}),n.add(s-o.pending,{[Cm.ATTR_DB_CLIENT_CONNECTION_POOL_NAME]:t}),{used:c,idle:a,pending:s}}or.updateCounter=sUt;function aUt(t,e){return function(n,o,i){n&&t.setStatus({code:Id.SpanStatusCode.ERROR,message:n.message}),t.end(),e.call(this,n,o,i)}}or.patchCallbackPGPool=aUt;function cUt(t,e){return function(n){n&&t.setStatus({code:Id.SpanStatusCode.ERROR,message:n.message}),t.end(),e.apply(this,arguments)}}or.patchClientConnectCallback=cUt;function uUt(t){return typeof t=="object"&&t!==null&&"message"in t?String(t.message):void 0}or.getErrorMessage=uUt;function lUt(t){return typeof t=="object"&&typeof t?.text=="string"}or.isObjectWithTextString=lUt});var dke=S(Ov=>{"use strict";Object.defineProperty(Ov,"__esModule",{value:!0});Ov.PACKAGE_NAME=Ov.PACKAGE_VERSION=void 0;Ov.PACKAGE_VERSION="0.55.0";Ov.PACKAGE_NAME="@opentelemetry/instrumentation-pg"});var vke=S(Zk=>{"use strict";Object.defineProperty(Zk,"__esModule",{value:!0});Zk.PgInstrumentation=void 0;var wa=Ft(),$r=(pe(),se(Pe)),fke=nke(),Bn=pke(),mke=i9(),hke=dke(),gke=v9(),Kk=_r(),Od=(er(),se(Cr)),$m=_9();function Nv(t){return t[Symbol.toStringTag]==="Module"?t.default:t}var y9=class extends wa.InstrumentationBase{_connectionsCounter={used:0,idle:0,pending:0};constructor(e={}){super(hke.PACKAGE_NAME,hke.PACKAGE_VERSION,e)}_updateMetricInstruments(){this._operationDuration=this.meter.createHistogram($m.METRIC_DB_CLIENT_OPERATION_DURATION,{description:"Duration of database client operations.",unit:"s",valueType:$r.ValueType.DOUBLE,advice:{explicitBucketBoundaries:[.001,.005,.01,.05,.1,.5,1,5,10]}}),this._connectionsCounter={idle:0,pending:0,used:0},this._connectionsCount=this.meter.createUpDownCounter($m.METRIC_DB_CLIENT_CONNECTION_COUNT,{description:"The number of connections that are currently in state described by the state attribute.",unit:"{connection}"}),this._connectionPendingRequests=this.meter.createUpDownCounter($m.METRIC_DB_CLIENT_CONNECTION_PENDING_REQUESTS,{description:"The number of current pending requests for an open connection.",unit:"{connection}"})}init(){let e=[">=8.0.3 <9"],r=[">=2.0.0 <4"],n=new wa.InstrumentationNodeModuleFile("pg/lib/native/client.js",e,this._patchPgClient.bind(this),this._unpatchPgClient.bind(this)),o=new wa.InstrumentationNodeModuleFile("pg/lib/client.js",e,this._patchPgClient.bind(this),this._unpatchPgClient.bind(this)),i=new wa.InstrumentationNodeModuleDefinition("pg",e,a=>{let c=Nv(a);return this._patchPgClient(c.Client),a},a=>{let c=Nv(a);return this._unpatchPgClient(c.Client),a},[o,n]),s=new wa.InstrumentationNodeModuleDefinition("pg-pool",r,a=>{let c=Nv(a);return(0,wa.isWrapped)(c.prototype.connect)&&this._unwrap(c.prototype,"connect"),this._wrap(c.prototype,"connect",this._getPoolConnectPatch()),c},a=>{let c=Nv(a);(0,wa.isWrapped)(c.prototype.connect)&&this._unwrap(c.prototype,"connect")});return[i,s]}_patchPgClient(e){if(!e)return;let r=Nv(e);return(0,wa.isWrapped)(r.prototype.query)&&this._unwrap(r.prototype,"query"),(0,wa.isWrapped)(r.prototype.connect)&&this._unwrap(r.prototype,"connect"),this._wrap(r.prototype,"query",this._getClientQueryPatch()),this._wrap(r.prototype,"connect",this._getClientConnectPatch()),e}_unpatchPgClient(e){let r=Nv(e);return(0,wa.isWrapped)(r.prototype.query)&&this._unwrap(r.prototype,"query"),(0,wa.isWrapped)(r.prototype.connect)&&this._unwrap(r.prototype,"connect"),e}_getClientConnectPatch(){let e=this;return r=>function(o){if(Bn.shouldSkipInstrumentation(e.getConfig()))return r.call(this,o);let i=e.tracer.startSpan(gke.SpanNames.CONNECT,{kind:$r.SpanKind.CLIENT,attributes:Bn.getSemanticAttributesFromConnection(this)});if(o){let a=$r.trace.getSpan($r.context.active());o=Bn.patchClientConnectCallback(i,o),a&&(o=$r.context.bind($r.context.active(),o))}let s=$r.context.with($r.trace.setSpan($r.context.active(),i),()=>r.call(this,o));return _ke(i,s)}}recordOperationDuration(e,r){let n={};[Od.SEMATTRS_DB_SYSTEM,$m.ATTR_DB_NAMESPACE,Od.ATTR_ERROR_TYPE,Od.ATTR_SERVER_PORT,Od.ATTR_SERVER_ADDRESS,$m.ATTR_DB_OPERATION_NAME].forEach(s=>{s in e&&(n[s]=e[s])});let i=(0,Kk.hrTimeToMilliseconds)((0,Kk.hrTimeDuration)(r,(0,Kk.hrTime)()))/1e3;this._operationDuration.record(i,n)}_getClientQueryPatch(){let e=this;return r=>(this._diag.debug("Patching pg.Client.prototype.query"),function(...o){if(Bn.shouldSkipInstrumentation(e.getConfig()))return r.apply(this,o);let i=(0,Kk.hrTime)(),s=o[0],a=typeof s=="string",c=Bn.isObjectWithTextString(s),u=a?{text:s,values:Array.isArray(o[1])?o[1]:void 0}:c?s:void 0,p={[Od.SEMATTRS_DB_SYSTEM]:Od.DBSYSTEMVALUES_POSTGRESQL,[$m.ATTR_DB_NAMESPACE]:this.database,[Od.ATTR_SERVER_PORT]:this.connectionParameters.port,[Od.ATTR_SERVER_ADDRESS]:this.connectionParameters.host};u?.text&&(p[$m.ATTR_DB_OPERATION_NAME]=Bn.parseNormalizedOperationName(u?.text));let f=()=>{e.recordOperationDuration(p,i)},m=e.getConfig(),h=Bn.handleConfigQuery.call(this,e.tracer,m,u);if(m.addSqlCommenterCommentToQueries&&(a?o[0]=(0,mke.addSqlCommenterComment)(h,s):c&&!("name"in s)&&(o[0]={...s,text:(0,mke.addSqlCommenterComment)(h,s.text)})),o.length>0){let E=$r.trace.getSpan($r.context.active());if(typeof o[o.length-1]=="function")o[o.length-1]=Bn.patchCallback(m,h,o[o.length-1],p,f),E&&(o[o.length-1]=$r.context.bind($r.context.active(),o[o.length-1]));else if(typeof u?.callback=="function"){let x=Bn.patchCallback(e.getConfig(),h,u.callback,p,f);E&&(x=$r.context.bind($r.context.active(),x)),o[0].callback=x}}let{requestHook:_}=m;typeof _=="function"&&u&&(0,wa.safeExecuteInTheMiddle)(()=>{let{database:E,host:x,port:w,user:I}=this.connectionParameters;_(h,{connection:{database:E,host:x,port:w,user:I},query:{text:u.text,values:u.values,name:u.name}})},E=>{E&&e._diag.error("Error running query hook",E)},!0);let v;try{v=r.apply(this,o)}catch(E){throw h.setStatus({code:$r.SpanStatusCode.ERROR,message:Bn.getErrorMessage(E)}),h.end(),E}return v instanceof Promise?v.then(E=>new Promise(x=>{Bn.handleExecutionResult(e.getConfig(),h,E),f(),h.end(),x(E)})).catch(E=>new Promise((x,w)=>{h.setStatus({code:$r.SpanStatusCode.ERROR,message:E.message}),f(),h.end(),w(E)})):v})}_setPoolConnectEventListeners(e){if(e[fke.EVENT_LISTENERS_SET])return;let r=Bn.getPoolName(e.options);e.on("connect",()=>{this._connectionsCounter=Bn.updateCounter(r,e,this._connectionsCount,this._connectionPendingRequests,this._connectionsCounter)}),e.on("acquire",()=>{this._connectionsCounter=Bn.updateCounter(r,e,this._connectionsCount,this._connectionPendingRequests,this._connectionsCounter)}),e.on("remove",()=>{this._connectionsCounter=Bn.updateCounter(r,e,this._connectionsCount,this._connectionPendingRequests,this._connectionsCounter)}),e.on("release",()=>{this._connectionsCounter=Bn.updateCounter(r,e,this._connectionsCount,this._connectionPendingRequests,this._connectionsCounter)}),e[fke.EVENT_LISTENERS_SET]=!0}_getPoolConnectPatch(){let e=this;return r=>function(o){if(Bn.shouldSkipInstrumentation(e.getConfig()))return r.call(this,o);let i=e.tracer.startSpan(gke.SpanNames.POOL_CONNECT,{kind:$r.SpanKind.CLIENT,attributes:Bn.getSemanticAttributesFromPool(this.options)});if(e._setPoolConnectEventListeners(this),o){let a=$r.trace.getSpan($r.context.active());o=Bn.patchCallbackPGPool(i,o),a&&(o=$r.context.bind($r.context.active(),o))}let s=$r.context.with($r.trace.setSpan($r.context.active(),i),()=>r.call(this,o));return _ke(i,s)}}};Zk.PgInstrumentation=y9;function _ke(t,e){if(!(e instanceof Promise))return e;let r=e;return $r.context.bind($r.context.active(),r.then(n=>(t.end(),n)).catch(n=>(t.setStatus({code:$r.SpanStatusCode.ERROR,message:Bn.getErrorMessage(n)}),t.end(),Promise.reject(n))))}});var Ske=S(Cv=>{"use strict";Object.defineProperty(Cv,"__esModule",{value:!0});Cv.AttributeNames=Cv.PgInstrumentation=void 0;var pUt=vke();Object.defineProperty(Cv,"PgInstrumentation",{enumerable:!0,get:function(){return pUt.PgInstrumentation}});var dUt=g9();Object.defineProperty(Cv,"AttributeNames",{enumerable:!0,get:function(){return dUt.AttributeNames}})});var Rke=S(u0=>{"use strict";Object.defineProperty(u0,"__esModule",{value:!0});u0.SeverityNumber=void 0;var hUt;(function(t){t[t.UNSPECIFIED=0]="UNSPECIFIED",t[t.TRACE=1]="TRACE",t[t.TRACE2=2]="TRACE2",t[t.TRACE3=3]="TRACE3",t[t.TRACE4=4]="TRACE4",t[t.DEBUG=5]="DEBUG",t[t.DEBUG2=6]="DEBUG2",t[t.DEBUG3=7]="DEBUG3",t[t.DEBUG4=8]="DEBUG4",t[t.INFO=9]="INFO",t[t.INFO2=10]="INFO2",t[t.INFO3=11]="INFO3",t[t.INFO4=12]="INFO4",t[t.WARN=13]="WARN",t[t.WARN2=14]="WARN2",t[t.WARN3=15]="WARN3",t[t.WARN4=16]="WARN4",t[t.ERROR=17]="ERROR",t[t.ERROR2=18]="ERROR2",t[t.ERROR3=19]="ERROR3",t[t.ERROR4=20]="ERROR4",t[t.FATAL=21]="FATAL",t[t.FATAL2=22]="FATAL2",t[t.FATAL3=23]="FATAL3",t[t.FATAL4=24]="FATAL4"})(hUt=u0.SeverityNumber||(u0.SeverityNumber={}))});var Jk=S($v=>{"use strict";Object.defineProperty($v,"__esModule",{value:!0});$v.NOOP_LOGGER=$v.NoopLogger=void 0;var Yk=class{emit(e){}};$v.NoopLogger=Yk;$v.NOOP_LOGGER=new Yk});var Qk=S(kv=>{"use strict";Object.defineProperty(kv,"__esModule",{value:!0});kv.NOOP_LOGGER_PROVIDER=kv.NoopLoggerProvider=void 0;var gUt=Jk(),Xk=class{getLogger(e,r,n){return new gUt.NoopLogger}};kv.NoopLoggerProvider=Xk;kv.NOOP_LOGGER_PROVIDER=new Xk});var x9=S(eM=>{"use strict";Object.defineProperty(eM,"__esModule",{value:!0});eM.ProxyLogger=void 0;var _Ut=Jk(),b9=class{constructor(e,r,n,o){this._provider=e,this.name=r,this.version=n,this.options=o}emit(e){this._getLogger().emit(e)}_getLogger(){if(this._delegate)return this._delegate;let e=this._provider.getDelegateLogger(this.name,this.version,this.options);return e?(this._delegate=e,this._delegate):_Ut.NOOP_LOGGER}};eM.ProxyLogger=b9});var w9=S(tM=>{"use strict";Object.defineProperty(tM,"__esModule",{value:!0});tM.ProxyLoggerProvider=void 0;var vUt=Qk(),SUt=x9(),A9=class{getLogger(e,r,n){var o;return(o=this.getDelegateLogger(e,r,n))!==null&&o!==void 0?o:new SUt.ProxyLogger(this,e,r,n)}getDelegate(){var e;return(e=this._delegate)!==null&&e!==void 0?e:vUt.NOOP_LOGGER_PROVIDER}setDelegate(e){this._delegate=e}getDelegateLogger(e,r,n){var o;return(o=this._delegate)===null||o===void 0?void 0:o.getLogger(e,r,n)}};tM.ProxyLoggerProvider=A9});var Pke=S(rM=>{"use strict";Object.defineProperty(rM,"__esModule",{value:!0});rM._globalThis=void 0;rM._globalThis=typeof globalThis=="object"?globalThis:global});var Ike=S(nM=>{"use strict";Object.defineProperty(nM,"__esModule",{value:!0});nM._globalThis=void 0;var yUt=Pke();Object.defineProperty(nM,"_globalThis",{enumerable:!0,get:function(){return yUt._globalThis}})});var Oke=S(oM=>{"use strict";Object.defineProperty(oM,"__esModule",{value:!0});oM._globalThis=void 0;var EUt=Ike();Object.defineProperty(oM,"_globalThis",{enumerable:!0,get:function(){return EUt._globalThis}})});var Nke=S(Tu=>{"use strict";Object.defineProperty(Tu,"__esModule",{value:!0});Tu.API_BACKWARDS_COMPATIBILITY_VERSION=Tu.makeGetter=Tu._global=Tu.GLOBAL_LOGS_API_KEY=void 0;var TUt=Oke();Tu.GLOBAL_LOGS_API_KEY=Symbol.for("io.opentelemetry.js.api.logs");Tu._global=TUt._globalThis;function bUt(t,e,r){return n=>n===t?e:r}Tu.makeGetter=bUt;Tu.API_BACKWARDS_COMPATIBILITY_VERSION=1});var $ke=S(iM=>{"use strict";Object.defineProperty(iM,"__esModule",{value:!0});iM.LogsAPI=void 0;var Ra=Nke(),xUt=Qk(),Cke=w9(),R9=class t{constructor(){this._proxyLoggerProvider=new Cke.ProxyLoggerProvider}static getInstance(){return this._instance||(this._instance=new t),this._instance}setGlobalLoggerProvider(e){return Ra._global[Ra.GLOBAL_LOGS_API_KEY]?this.getLoggerProvider():(Ra._global[Ra.GLOBAL_LOGS_API_KEY]=(0,Ra.makeGetter)(Ra.API_BACKWARDS_COMPATIBILITY_VERSION,e,xUt.NOOP_LOGGER_PROVIDER),this._proxyLoggerProvider.setDelegate(e),e)}getLoggerProvider(){var e,r;return(r=(e=Ra._global[Ra.GLOBAL_LOGS_API_KEY])===null||e===void 0?void 0:e.call(Ra._global,Ra.API_BACKWARDS_COMPATIBILITY_VERSION))!==null&&r!==void 0?r:this._proxyLoggerProvider}getLogger(e,r,n){return this.getLoggerProvider().getLogger(e,r,n)}disable(){delete Ra._global[Ra.GLOBAL_LOGS_API_KEY],this._proxyLoggerProvider=new Cke.ProxyLoggerProvider}};iM.LogsAPI=R9});var P9=S(_o=>{"use strict";Object.defineProperty(_o,"__esModule",{value:!0});_o.logs=_o.ProxyLoggerProvider=_o.ProxyLogger=_o.NoopLoggerProvider=_o.NOOP_LOGGER_PROVIDER=_o.NoopLogger=_o.NOOP_LOGGER=_o.SeverityNumber=void 0;var AUt=Rke();Object.defineProperty(_o,"SeverityNumber",{enumerable:!0,get:function(){return AUt.SeverityNumber}});var kke=Jk();Object.defineProperty(_o,"NOOP_LOGGER",{enumerable:!0,get:function(){return kke.NOOP_LOGGER}});Object.defineProperty(_o,"NoopLogger",{enumerable:!0,get:function(){return kke.NoopLogger}});var Mke=Qk();Object.defineProperty(_o,"NOOP_LOGGER_PROVIDER",{enumerable:!0,get:function(){return Mke.NOOP_LOGGER_PROVIDER}});Object.defineProperty(_o,"NoopLoggerProvider",{enumerable:!0,get:function(){return Mke.NoopLoggerProvider}});var wUt=x9();Object.defineProperty(_o,"ProxyLogger",{enumerable:!0,get:function(){return wUt.ProxyLogger}});var RUt=w9();Object.defineProperty(_o,"ProxyLoggerProvider",{enumerable:!0,get:function(){return RUt.ProxyLoggerProvider}});var PUt=$ke();_o.logs=PUt.LogsAPI.getInstance()});var Dke=S(Mv=>{"use strict";Object.defineProperty(Mv,"__esModule",{value:!0});Mv.disableInstrumentations=Mv.enableInstrumentations=void 0;function IUt(t,e,r,n){for(let o=0,i=t.length;oe.disable())}Mv.disableInstrumentations=OUt});var jke=S(sM=>{"use strict";Object.defineProperty(sM,"__esModule",{value:!0});sM.registerInstrumentations=void 0;var Lke=(pe(),se(Pe)),NUt=P9(),Uke=Dke();function CUt(t){var e,r;let n=t.tracerProvider||Lke.trace.getTracerProvider(),o=t.meterProvider||Lke.metrics.getMeterProvider(),i=t.loggerProvider||NUt.logs.getLoggerProvider(),s=(r=(e=t.instrumentations)===null||e===void 0?void 0:e.flat())!==null&&r!==void 0?r:[];return(0,Uke.enableInstrumentations)(s,n,o,i),()=>{(0,Uke.disableInstrumentations)(s)}}sM.registerInstrumentations=CUt});var l0=S((sNr,zke)=>{"use strict";var $Ut="2.0.0",kUt=Number.MAX_SAFE_INTEGER||9007199254740991,MUt=16,DUt=250,LUt=["major","premajor","minor","preminor","patch","prepatch","prerelease"];zke.exports={MAX_LENGTH:256,MAX_SAFE_COMPONENT_LENGTH:MUt,MAX_SAFE_BUILD_LENGTH:DUt,MAX_SAFE_INTEGER:kUt,RELEASE_TYPES:LUt,SEMVER_SPEC_VERSION:$Ut,FLAG_INCLUDE_PRERELEASE:1,FLAG_LOOSE:2}});var p0=S((aNr,Fke)=>{"use strict";var UUt=typeof process=="object"&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?(...t)=>console.error("SEMVER",...t):()=>{};Fke.exports=UUt});var Dv=S((bu,qke)=>{"use strict";var{MAX_SAFE_COMPONENT_LENGTH:I9,MAX_SAFE_BUILD_LENGTH:jUt,MAX_LENGTH:zUt}=l0(),FUt=p0();bu=qke.exports={};var qUt=bu.re=[],BUt=bu.safeRe=[],xe=bu.src=[],GUt=bu.safeSrc=[],Ae=bu.t={},VUt=0,O9="[a-zA-Z0-9-]",HUt=[["\\s",1],["\\d",zUt],[O9,jUt]],WUt=t=>{for(let[e,r]of HUt)t=t.split(`${e}*`).join(`${e}{0,${r}}`).split(`${e}+`).join(`${e}{1,${r}}`);return t},_t=(t,e,r)=>{let n=WUt(e),o=VUt++;FUt(t,o,e),Ae[t]=o,xe[o]=e,GUt[o]=n,qUt[o]=new RegExp(e,r?"g":void 0),BUt[o]=new RegExp(n,r?"g":void 0)};_t("NUMERICIDENTIFIER","0|[1-9]\\d*");_t("NUMERICIDENTIFIERLOOSE","\\d+");_t("NONNUMERICIDENTIFIER",`\\d*[a-zA-Z-]${O9}*`);_t("MAINVERSION",`(${xe[Ae.NUMERICIDENTIFIER]})\\.(${xe[Ae.NUMERICIDENTIFIER]})\\.(${xe[Ae.NUMERICIDENTIFIER]})`);_t("MAINVERSIONLOOSE",`(${xe[Ae.NUMERICIDENTIFIERLOOSE]})\\.(${xe[Ae.NUMERICIDENTIFIERLOOSE]})\\.(${xe[Ae.NUMERICIDENTIFIERLOOSE]})`);_t("PRERELEASEIDENTIFIER",`(?:${xe[Ae.NONNUMERICIDENTIFIER]}|${xe[Ae.NUMERICIDENTIFIER]})`);_t("PRERELEASEIDENTIFIERLOOSE",`(?:${xe[Ae.NONNUMERICIDENTIFIER]}|${xe[Ae.NUMERICIDENTIFIERLOOSE]})`);_t("PRERELEASE",`(?:-(${xe[Ae.PRERELEASEIDENTIFIER]}(?:\\.${xe[Ae.PRERELEASEIDENTIFIER]})*))`);_t("PRERELEASELOOSE",`(?:-?(${xe[Ae.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${xe[Ae.PRERELEASEIDENTIFIERLOOSE]})*))`);_t("BUILDIDENTIFIER",`${O9}+`);_t("BUILD",`(?:\\+(${xe[Ae.BUILDIDENTIFIER]}(?:\\.${xe[Ae.BUILDIDENTIFIER]})*))`);_t("FULLPLAIN",`v?${xe[Ae.MAINVERSION]}${xe[Ae.PRERELEASE]}?${xe[Ae.BUILD]}?`);_t("FULL",`^${xe[Ae.FULLPLAIN]}$`);_t("LOOSEPLAIN",`[v=\\s]*${xe[Ae.MAINVERSIONLOOSE]}${xe[Ae.PRERELEASELOOSE]}?${xe[Ae.BUILD]}?`);_t("LOOSE",`^${xe[Ae.LOOSEPLAIN]}$`);_t("GTLT","((?:<|>)?=?)");_t("XRANGEIDENTIFIERLOOSE",`${xe[Ae.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);_t("XRANGEIDENTIFIER",`${xe[Ae.NUMERICIDENTIFIER]}|x|X|\\*`);_t("XRANGEPLAIN",`[v=\\s]*(${xe[Ae.XRANGEIDENTIFIER]})(?:\\.(${xe[Ae.XRANGEIDENTIFIER]})(?:\\.(${xe[Ae.XRANGEIDENTIFIER]})(?:${xe[Ae.PRERELEASE]})?${xe[Ae.BUILD]}?)?)?`);_t("XRANGEPLAINLOOSE",`[v=\\s]*(${xe[Ae.XRANGEIDENTIFIERLOOSE]})(?:\\.(${xe[Ae.XRANGEIDENTIFIERLOOSE]})(?:\\.(${xe[Ae.XRANGEIDENTIFIERLOOSE]})(?:${xe[Ae.PRERELEASELOOSE]})?${xe[Ae.BUILD]}?)?)?`);_t("XRANGE",`^${xe[Ae.GTLT]}\\s*${xe[Ae.XRANGEPLAIN]}$`);_t("XRANGELOOSE",`^${xe[Ae.GTLT]}\\s*${xe[Ae.XRANGEPLAINLOOSE]}$`);_t("COERCEPLAIN",`(^|[^\\d])(\\d{1,${I9}})(?:\\.(\\d{1,${I9}}))?(?:\\.(\\d{1,${I9}}))?`);_t("COERCE",`${xe[Ae.COERCEPLAIN]}(?:$|[^\\d])`);_t("COERCEFULL",xe[Ae.COERCEPLAIN]+`(?:${xe[Ae.PRERELEASE]})?(?:${xe[Ae.BUILD]})?(?:$|[^\\d])`);_t("COERCERTL",xe[Ae.COERCE],!0);_t("COERCERTLFULL",xe[Ae.COERCEFULL],!0);_t("LONETILDE","(?:~>?)");_t("TILDETRIM",`(\\s*)${xe[Ae.LONETILDE]}\\s+`,!0);bu.tildeTrimReplace="$1~";_t("TILDE",`^${xe[Ae.LONETILDE]}${xe[Ae.XRANGEPLAIN]}$`);_t("TILDELOOSE",`^${xe[Ae.LONETILDE]}${xe[Ae.XRANGEPLAINLOOSE]}$`);_t("LONECARET","(?:\\^)");_t("CARETTRIM",`(\\s*)${xe[Ae.LONECARET]}\\s+`,!0);bu.caretTrimReplace="$1^";_t("CARET",`^${xe[Ae.LONECARET]}${xe[Ae.XRANGEPLAIN]}$`);_t("CARETLOOSE",`^${xe[Ae.LONECARET]}${xe[Ae.XRANGEPLAINLOOSE]}$`);_t("COMPARATORLOOSE",`^${xe[Ae.GTLT]}\\s*(${xe[Ae.LOOSEPLAIN]})$|^$`);_t("COMPARATOR",`^${xe[Ae.GTLT]}\\s*(${xe[Ae.FULLPLAIN]})$|^$`);_t("COMPARATORTRIM",`(\\s*)${xe[Ae.GTLT]}\\s*(${xe[Ae.LOOSEPLAIN]}|${xe[Ae.XRANGEPLAIN]})`,!0);bu.comparatorTrimReplace="$1$2$3";_t("HYPHENRANGE",`^\\s*(${xe[Ae.XRANGEPLAIN]})\\s+-\\s+(${xe[Ae.XRANGEPLAIN]})\\s*$`);_t("HYPHENRANGELOOSE",`^\\s*(${xe[Ae.XRANGEPLAINLOOSE]})\\s+-\\s+(${xe[Ae.XRANGEPLAINLOOSE]})\\s*$`);_t("STAR","(<|>)?=?\\s*\\*");_t("GTE0","^\\s*>=\\s*0\\.0\\.0\\s*$");_t("GTE0PRE","^\\s*>=\\s*0\\.0\\.0-0\\s*$")});var aM=S((cNr,Bke)=>{"use strict";var KUt=Object.freeze({loose:!0}),ZUt=Object.freeze({}),YUt=t=>t?typeof t!="object"?KUt:t:ZUt;Bke.exports=YUt});var N9=S((uNr,Hke)=>{"use strict";var Gke=/^[0-9]+$/,Vke=(t,e)=>{let r=Gke.test(t),n=Gke.test(e);return r&&n&&(t=+t,e=+e),t===e?0:r&&!n?-1:n&&!r?1:tVke(e,t);Hke.exports={compareIdentifiers:Vke,rcompareIdentifiers:JUt}});var si=S((lNr,Kke)=>{"use strict";var cM=p0(),{MAX_LENGTH:Wke,MAX_SAFE_INTEGER:uM}=l0(),{safeRe:lM,t:pM}=Dv(),XUt=aM(),{compareIdentifiers:Lv}=N9(),C9=class t{constructor(e,r){if(r=XUt(r),e instanceof t){if(e.loose===!!r.loose&&e.includePrerelease===!!r.includePrerelease)return e;e=e.version}else if(typeof e!="string")throw new TypeError(`Invalid version. Must be a string. Got type "${typeof e}".`);if(e.length>Wke)throw new TypeError(`version is longer than ${Wke} characters`);cM("SemVer",e,r),this.options=r,this.loose=!!r.loose,this.includePrerelease=!!r.includePrerelease;let n=e.trim().match(r.loose?lM[pM.LOOSE]:lM[pM.FULL]);if(!n)throw new TypeError(`Invalid Version: ${e}`);if(this.raw=e,this.major=+n[1],this.minor=+n[2],this.patch=+n[3],this.major>uM||this.major<0)throw new TypeError("Invalid major version");if(this.minor>uM||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>uM||this.patch<0)throw new TypeError("Invalid patch version");n[4]?this.prerelease=n[4].split(".").map(o=>{if(/^[0-9]+$/.test(o)){let i=+o;if(i>=0&&i=0;)typeof this.prerelease[i]=="number"&&(this.prerelease[i]++,i=-2);if(i===-1){if(r===this.prerelease.join(".")&&n===!1)throw new Error("invalid increment argument: identifier already exists");this.prerelease.push(o)}}if(r){let i=[r,o];n===!1&&(i=[r]),Lv(this.prerelease[0],r)===0?isNaN(this.prerelease[1])&&(this.prerelease=i):this.prerelease=i}break}default:throw new Error(`invalid increment argument: ${e}`)}return this.raw=this.format(),this.build.length&&(this.raw+=`+${this.build.join(".")}`),this}};Kke.exports=C9});var km=S((pNr,Yke)=>{"use strict";var Zke=si(),QUt=(t,e,r=!1)=>{if(t instanceof Zke)return t;try{return new Zke(t,e)}catch(n){if(!r)return null;throw n}};Yke.exports=QUt});var Xke=S((dNr,Jke)=>{"use strict";var ejt=km(),tjt=(t,e)=>{let r=ejt(t,e);return r?r.version:null};Jke.exports=tjt});var eMe=S((fNr,Qke)=>{"use strict";var rjt=km(),njt=(t,e)=>{let r=rjt(t.trim().replace(/^[=v]+/,""),e);return r?r.version:null};Qke.exports=njt});var nMe=S((mNr,rMe)=>{"use strict";var tMe=si(),ojt=(t,e,r,n,o)=>{typeof r=="string"&&(o=n,n=r,r=void 0);try{return new tMe(t instanceof tMe?t.version:t,r).inc(e,n,o).version}catch{return null}};rMe.exports=ojt});var sMe=S((hNr,iMe)=>{"use strict";var oMe=km(),ijt=(t,e)=>{let r=oMe(t,null,!0),n=oMe(e,null,!0),o=r.compare(n);if(o===0)return null;let i=o>0,s=i?r:n,a=i?n:r,c=!!s.prerelease.length;if(!!a.prerelease.length&&!c){if(!a.patch&&!a.minor)return"major";if(a.compareMain(s)===0)return a.minor&&!a.patch?"minor":"patch"}let p=c?"pre":"";return r.major!==n.major?p+"major":r.minor!==n.minor?p+"minor":r.patch!==n.patch?p+"patch":"prerelease"};iMe.exports=ijt});var cMe=S((gNr,aMe)=>{"use strict";var sjt=si(),ajt=(t,e)=>new sjt(t,e).major;aMe.exports=ajt});var lMe=S((_Nr,uMe)=>{"use strict";var cjt=si(),ujt=(t,e)=>new cjt(t,e).minor;uMe.exports=ujt});var dMe=S((vNr,pMe)=>{"use strict";var ljt=si(),pjt=(t,e)=>new ljt(t,e).patch;pMe.exports=pjt});var mMe=S((SNr,fMe)=>{"use strict";var djt=km(),fjt=(t,e)=>{let r=djt(t,e);return r&&r.prerelease.length?r.prerelease:null};fMe.exports=fjt});var Pa=S((yNr,gMe)=>{"use strict";var hMe=si(),mjt=(t,e,r)=>new hMe(t,r).compare(new hMe(e,r));gMe.exports=mjt});var vMe=S((ENr,_Me)=>{"use strict";var hjt=Pa(),gjt=(t,e,r)=>hjt(e,t,r);_Me.exports=gjt});var yMe=S((TNr,SMe)=>{"use strict";var _jt=Pa(),vjt=(t,e)=>_jt(t,e,!0);SMe.exports=vjt});var dM=S((bNr,TMe)=>{"use strict";var EMe=si(),Sjt=(t,e,r)=>{let n=new EMe(t,r),o=new EMe(e,r);return n.compare(o)||n.compareBuild(o)};TMe.exports=Sjt});var xMe=S((xNr,bMe)=>{"use strict";var yjt=dM(),Ejt=(t,e)=>t.sort((r,n)=>yjt(r,n,e));bMe.exports=Ejt});var wMe=S((ANr,AMe)=>{"use strict";var Tjt=dM(),bjt=(t,e)=>t.sort((r,n)=>Tjt(n,r,e));AMe.exports=bjt});var d0=S((wNr,RMe)=>{"use strict";var xjt=Pa(),Ajt=(t,e,r)=>xjt(t,e,r)>0;RMe.exports=Ajt});var fM=S((RNr,PMe)=>{"use strict";var wjt=Pa(),Rjt=(t,e,r)=>wjt(t,e,r)<0;PMe.exports=Rjt});var $9=S((PNr,IMe)=>{"use strict";var Pjt=Pa(),Ijt=(t,e,r)=>Pjt(t,e,r)===0;IMe.exports=Ijt});var k9=S((INr,OMe)=>{"use strict";var Ojt=Pa(),Njt=(t,e,r)=>Ojt(t,e,r)!==0;OMe.exports=Njt});var mM=S((ONr,NMe)=>{"use strict";var Cjt=Pa(),$jt=(t,e,r)=>Cjt(t,e,r)>=0;NMe.exports=$jt});var hM=S((NNr,CMe)=>{"use strict";var kjt=Pa(),Mjt=(t,e,r)=>kjt(t,e,r)<=0;CMe.exports=Mjt});var M9=S((CNr,$Me)=>{"use strict";var Djt=$9(),Ljt=k9(),Ujt=d0(),jjt=mM(),zjt=fM(),Fjt=hM(),qjt=(t,e,r,n)=>{switch(e){case"===":return typeof t=="object"&&(t=t.version),typeof r=="object"&&(r=r.version),t===r;case"!==":return typeof t=="object"&&(t=t.version),typeof r=="object"&&(r=r.version),t!==r;case"":case"=":case"==":return Djt(t,r,n);case"!=":return Ljt(t,r,n);case">":return Ujt(t,r,n);case">=":return jjt(t,r,n);case"<":return zjt(t,r,n);case"<=":return Fjt(t,r,n);default:throw new TypeError(`Invalid operator: ${e}`)}};$Me.exports=qjt});var MMe=S(($Nr,kMe)=>{"use strict";var Bjt=si(),Gjt=km(),{safeRe:gM,t:_M}=Dv(),Vjt=(t,e)=>{if(t instanceof Bjt)return t;if(typeof t=="number"&&(t=String(t)),typeof t!="string")return null;e=e||{};let r=null;if(!e.rtl)r=t.match(e.includePrerelease?gM[_M.COERCEFULL]:gM[_M.COERCE]);else{let c=e.includePrerelease?gM[_M.COERCERTLFULL]:gM[_M.COERCERTL],u;for(;(u=c.exec(t))&&(!r||r.index+r[0].length!==t.length);)(!r||u.index+u[0].length!==r.index+r[0].length)&&(r=u),c.lastIndex=u.index+u[1].length+u[2].length;c.lastIndex=-1}if(r===null)return null;let n=r[2],o=r[3]||"0",i=r[4]||"0",s=e.includePrerelease&&r[5]?`-${r[5]}`:"",a=e.includePrerelease&&r[6]?`+${r[6]}`:"";return Gjt(`${n}.${o}.${i}${s}${a}`,e)};kMe.exports=Vjt});var LMe=S((kNr,DMe)=>{"use strict";var D9=class{constructor(){this.max=1e3,this.map=new Map}get(e){let r=this.map.get(e);if(r!==void 0)return this.map.delete(e),this.map.set(e,r),r}delete(e){return this.map.delete(e)}set(e,r){if(!this.delete(e)&&r!==void 0){if(this.map.size>=this.max){let o=this.map.keys().next().value;this.delete(o)}this.map.set(e,r)}return this}};DMe.exports=D9});var Ia=S((MNr,FMe)=>{"use strict";var Hjt=/\s+/g,L9=class t{constructor(e,r){if(r=Kjt(r),e instanceof t)return e.loose===!!r.loose&&e.includePrerelease===!!r.includePrerelease?e:new t(e.raw,r);if(e instanceof U9)return this.raw=e.value,this.set=[[e]],this.formatted=void 0,this;if(this.options=r,this.loose=!!r.loose,this.includePrerelease=!!r.includePrerelease,this.raw=e.trim().replace(Hjt," "),this.set=this.raw.split("||").map(n=>this.parseRange(n.trim())).filter(n=>n.length),!this.set.length)throw new TypeError(`Invalid SemVer Range: ${this.raw}`);if(this.set.length>1){let n=this.set[0];if(this.set=this.set.filter(o=>!jMe(o[0])),this.set.length===0)this.set=[n];else if(this.set.length>1){for(let o of this.set)if(o.length===1&&tzt(o[0])){this.set=[o];break}}}this.formatted=void 0}get range(){if(this.formatted===void 0){this.formatted="";for(let e=0;e0&&(this.formatted+="||");let r=this.set[e];for(let n=0;n0&&(this.formatted+=" "),this.formatted+=r[n].toString().trim()}}return this.formatted}format(){return this.range}toString(){return this.range}parseRange(e){let n=((this.options.includePrerelease&&Qjt)|(this.options.loose&&ezt))+":"+e,o=UMe.get(n);if(o)return o;let i=this.options.loose,s=i?ns[Ii.HYPHENRANGELOOSE]:ns[Ii.HYPHENRANGE];e=e.replace(s,pzt(this.options.includePrerelease)),Qr("hyphen replace",e),e=e.replace(ns[Ii.COMPARATORTRIM],Yjt),Qr("comparator trim",e),e=e.replace(ns[Ii.TILDETRIM],Jjt),Qr("tilde trim",e),e=e.replace(ns[Ii.CARETTRIM],Xjt),Qr("caret trim",e);let a=e.split(" ").map(f=>rzt(f,this.options)).join(" ").split(/\s+/).map(f=>lzt(f,this.options));i&&(a=a.filter(f=>(Qr("loose invalid filter",f,this.options),!!f.match(ns[Ii.COMPARATORLOOSE])))),Qr("range list",a);let c=new Map,u=a.map(f=>new U9(f,this.options));for(let f of u){if(jMe(f))return[f];c.set(f.value,f)}c.size>1&&c.has("")&&c.delete("");let p=[...c.values()];return UMe.set(n,p),p}intersects(e,r){if(!(e instanceof t))throw new TypeError("a Range is required");return this.set.some(n=>zMe(n,r)&&e.set.some(o=>zMe(o,r)&&n.every(i=>o.every(s=>i.intersects(s,r)))))}test(e){if(!e)return!1;if(typeof e=="string")try{e=new Zjt(e,this.options)}catch{return!1}for(let r=0;rt.value==="<0.0.0-0",tzt=t=>t.value==="",zMe=(t,e)=>{let r=!0,n=t.slice(),o=n.pop();for(;r&&n.length;)r=n.every(i=>o.intersects(i,e)),o=n.pop();return r},rzt=(t,e)=>(Qr("comp",t,e),t=izt(t,e),Qr("caret",t),t=nzt(t,e),Qr("tildes",t),t=azt(t,e),Qr("xrange",t),t=uzt(t,e),Qr("stars",t),t),Oi=t=>!t||t.toLowerCase()==="x"||t==="*",nzt=(t,e)=>t.trim().split(/\s+/).map(r=>ozt(r,e)).join(" "),ozt=(t,e)=>{let r=e.loose?ns[Ii.TILDELOOSE]:ns[Ii.TILDE];return t.replace(r,(n,o,i,s,a)=>{Qr("tilde",t,n,o,i,s,a);let c;return Oi(o)?c="":Oi(i)?c=`>=${o}.0.0 <${+o+1}.0.0-0`:Oi(s)?c=`>=${o}.${i}.0 <${o}.${+i+1}.0-0`:a?(Qr("replaceTilde pr",a),c=`>=${o}.${i}.${s}-${a} <${o}.${+i+1}.0-0`):c=`>=${o}.${i}.${s} <${o}.${+i+1}.0-0`,Qr("tilde return",c),c})},izt=(t,e)=>t.trim().split(/\s+/).map(r=>szt(r,e)).join(" "),szt=(t,e)=>{Qr("caret",t,e);let r=e.loose?ns[Ii.CARETLOOSE]:ns[Ii.CARET],n=e.includePrerelease?"-0":"";return t.replace(r,(o,i,s,a,c)=>{Qr("caret",t,o,i,s,a,c);let u;return Oi(i)?u="":Oi(s)?u=`>=${i}.0.0${n} <${+i+1}.0.0-0`:Oi(a)?i==="0"?u=`>=${i}.${s}.0${n} <${i}.${+s+1}.0-0`:u=`>=${i}.${s}.0${n} <${+i+1}.0.0-0`:c?(Qr("replaceCaret pr",c),i==="0"?s==="0"?u=`>=${i}.${s}.${a}-${c} <${i}.${s}.${+a+1}-0`:u=`>=${i}.${s}.${a}-${c} <${i}.${+s+1}.0-0`:u=`>=${i}.${s}.${a}-${c} <${+i+1}.0.0-0`):(Qr("no pr"),i==="0"?s==="0"?u=`>=${i}.${s}.${a}${n} <${i}.${s}.${+a+1}-0`:u=`>=${i}.${s}.${a}${n} <${i}.${+s+1}.0-0`:u=`>=${i}.${s}.${a} <${+i+1}.0.0-0`),Qr("caret return",u),u})},azt=(t,e)=>(Qr("replaceXRanges",t,e),t.split(/\s+/).map(r=>czt(r,e)).join(" ")),czt=(t,e)=>{t=t.trim();let r=e.loose?ns[Ii.XRANGELOOSE]:ns[Ii.XRANGE];return t.replace(r,(n,o,i,s,a,c)=>{Qr("xRange",t,n,o,i,s,a,c);let u=Oi(i),p=u||Oi(s),f=p||Oi(a),m=f;return o==="="&&m&&(o=""),c=e.includePrerelease?"-0":"",u?o===">"||o==="<"?n="<0.0.0-0":n="*":o&&m?(p&&(s=0),a=0,o===">"?(o=">=",p?(i=+i+1,s=0,a=0):(s=+s+1,a=0)):o==="<="&&(o="<",p?i=+i+1:s=+s+1),o==="<"&&(c="-0"),n=`${o+i}.${s}.${a}${c}`):p?n=`>=${i}.0.0${c} <${+i+1}.0.0-0`:f&&(n=`>=${i}.${s}.0${c} <${i}.${+s+1}.0-0`),Qr("xRange return",n),n})},uzt=(t,e)=>(Qr("replaceStars",t,e),t.trim().replace(ns[Ii.STAR],"")),lzt=(t,e)=>(Qr("replaceGTE0",t,e),t.trim().replace(ns[e.includePrerelease?Ii.GTE0PRE:Ii.GTE0],"")),pzt=t=>(e,r,n,o,i,s,a,c,u,p,f,m)=>(Oi(n)?r="":Oi(o)?r=`>=${n}.0.0${t?"-0":""}`:Oi(i)?r=`>=${n}.${o}.0${t?"-0":""}`:s?r=`>=${r}`:r=`>=${r}${t?"-0":""}`,Oi(u)?c="":Oi(p)?c=`<${+u+1}.0.0-0`:Oi(f)?c=`<${u}.${+p+1}.0-0`:m?c=`<=${u}.${p}.${f}-${m}`:t?c=`<${u}.${p}.${+f+1}-0`:c=`<=${c}`,`${r} ${c}`.trim()),dzt=(t,e,r)=>{for(let n=0;n0){let o=t[n].semver;if(o.major===e.major&&o.minor===e.minor&&o.patch===e.patch)return!0}return!1}return!0}});var f0=S((DNr,WMe)=>{"use strict";var m0=Symbol("SemVer ANY"),F9=class t{static get ANY(){return m0}constructor(e,r){if(r=qMe(r),e instanceof t){if(e.loose===!!r.loose)return e;e=e.value}e=e.trim().split(/\s+/).join(" "),z9("comparator",e,r),this.options=r,this.loose=!!r.loose,this.parse(e),this.semver===m0?this.value="":this.value=this.operator+this.semver.version,z9("comp",this)}parse(e){let r=this.options.loose?BMe[GMe.COMPARATORLOOSE]:BMe[GMe.COMPARATOR],n=e.match(r);if(!n)throw new TypeError(`Invalid comparator: ${e}`);this.operator=n[1]!==void 0?n[1]:"",this.operator==="="&&(this.operator=""),n[2]?this.semver=new VMe(n[2],this.options.loose):this.semver=m0}toString(){return this.value}test(e){if(z9("Comparator.test",e,this.options.loose),this.semver===m0||e===m0)return!0;if(typeof e=="string")try{e=new VMe(e,this.options)}catch{return!1}return j9(e,this.operator,this.semver,this.options)}intersects(e,r){if(!(e instanceof t))throw new TypeError("a Comparator is required");return this.operator===""?this.value===""?!0:new HMe(e.value,r).test(this.value):e.operator===""?e.value===""?!0:new HMe(this.value,r).test(e.semver):(r=qMe(r),r.includePrerelease&&(this.value==="<0.0.0-0"||e.value==="<0.0.0-0")||!r.includePrerelease&&(this.value.startsWith("<0.0.0")||e.value.startsWith("<0.0.0"))?!1:!!(this.operator.startsWith(">")&&e.operator.startsWith(">")||this.operator.startsWith("<")&&e.operator.startsWith("<")||this.semver.version===e.semver.version&&this.operator.includes("=")&&e.operator.includes("=")||j9(this.semver,"<",e.semver,r)&&this.operator.startsWith(">")&&e.operator.startsWith("<")||j9(this.semver,">",e.semver,r)&&this.operator.startsWith("<")&&e.operator.startsWith(">")))}};WMe.exports=F9;var qMe=aM(),{safeRe:BMe,t:GMe}=Dv(),j9=M9(),z9=p0(),VMe=si(),HMe=Ia()});var h0=S((LNr,KMe)=>{"use strict";var fzt=Ia(),mzt=(t,e,r)=>{try{e=new fzt(e,r)}catch{return!1}return e.test(t)};KMe.exports=mzt});var YMe=S((UNr,ZMe)=>{"use strict";var hzt=Ia(),gzt=(t,e)=>new hzt(t,e).set.map(r=>r.map(n=>n.value).join(" ").trim().split(" "));ZMe.exports=gzt});var XMe=S((jNr,JMe)=>{"use strict";var _zt=si(),vzt=Ia(),Szt=(t,e,r)=>{let n=null,o=null,i=null;try{i=new vzt(e,r)}catch{return null}return t.forEach(s=>{i.test(s)&&(!n||o.compare(s)===-1)&&(n=s,o=new _zt(n,r))}),n};JMe.exports=Szt});var eDe=S((zNr,QMe)=>{"use strict";var yzt=si(),Ezt=Ia(),Tzt=(t,e,r)=>{let n=null,o=null,i=null;try{i=new Ezt(e,r)}catch{return null}return t.forEach(s=>{i.test(s)&&(!n||o.compare(s)===1)&&(n=s,o=new yzt(n,r))}),n};QMe.exports=Tzt});var nDe=S((FNr,rDe)=>{"use strict";var q9=si(),bzt=Ia(),tDe=d0(),xzt=(t,e)=>{t=new bzt(t,e);let r=new q9("0.0.0");if(t.test(r)||(r=new q9("0.0.0-0"),t.test(r)))return r;r=null;for(let n=0;n{let a=new q9(s.semver.version);switch(s.operator){case">":a.prerelease.length===0?a.patch++:a.prerelease.push(0),a.raw=a.format();case"":case">=":(!i||tDe(a,i))&&(i=a);break;case"<":case"<=":break;default:throw new Error(`Unexpected operation: ${s.operator}`)}}),i&&(!r||tDe(r,i))&&(r=i)}return r&&t.test(r)?r:null};rDe.exports=xzt});var iDe=S((qNr,oDe)=>{"use strict";var Azt=Ia(),wzt=(t,e)=>{try{return new Azt(t,e).range||"*"}catch{return null}};oDe.exports=wzt});var vM=S((BNr,uDe)=>{"use strict";var Rzt=si(),cDe=f0(),{ANY:Pzt}=cDe,Izt=Ia(),Ozt=h0(),sDe=d0(),aDe=fM(),Nzt=hM(),Czt=mM(),$zt=(t,e,r,n)=>{t=new Rzt(t,n),e=new Izt(e,n);let o,i,s,a,c;switch(r){case">":o=sDe,i=Nzt,s=aDe,a=">",c=">=";break;case"<":o=aDe,i=Czt,s=sDe,a="<",c="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(Ozt(t,e,n))return!1;for(let u=0;u{h.semver===Pzt&&(h=new cDe(">=0.0.0")),f=f||h,m=m||h,o(h.semver,f.semver,n)?f=h:s(h.semver,m.semver,n)&&(m=h)}),f.operator===a||f.operator===c||(!m.operator||m.operator===a)&&i(t,m.semver))return!1;if(m.operator===c&&s(t,m.semver))return!1}return!0};uDe.exports=$zt});var pDe=S((GNr,lDe)=>{"use strict";var kzt=vM(),Mzt=(t,e,r)=>kzt(t,e,">",r);lDe.exports=Mzt});var fDe=S((VNr,dDe)=>{"use strict";var Dzt=vM(),Lzt=(t,e,r)=>Dzt(t,e,"<",r);dDe.exports=Lzt});var gDe=S((HNr,hDe)=>{"use strict";var mDe=Ia(),Uzt=(t,e,r)=>(t=new mDe(t,r),e=new mDe(e,r),t.intersects(e,r));hDe.exports=Uzt});var vDe=S((WNr,_De)=>{"use strict";var jzt=h0(),zzt=Pa();_De.exports=(t,e,r)=>{let n=[],o=null,i=null,s=t.sort((p,f)=>zzt(p,f,r));for(let p of s)jzt(p,e,r)?(i=p,o||(o=p)):(i&&n.push([o,i]),i=null,o=null);o&&n.push([o,null]);let a=[];for(let[p,f]of n)p===f?a.push(p):!f&&p===s[0]?a.push("*"):f?p===s[0]?a.push(`<=${f}`):a.push(`${p} - ${f}`):a.push(`>=${p}`);let c=a.join(" || "),u=typeof e.raw=="string"?e.raw:String(e);return c.length{"use strict";var SDe=Ia(),G9=f0(),{ANY:B9}=G9,g0=h0(),V9=Pa(),Fzt=(t,e,r={})=>{if(t===e)return!0;t=new SDe(t,r),e=new SDe(e,r);let n=!1;e:for(let o of t.set){for(let i of e.set){let s=Bzt(o,i,r);if(n=n||s!==null,s)continue e}if(n)return!1}return!0},qzt=[new G9(">=0.0.0-0")],yDe=[new G9(">=0.0.0")],Bzt=(t,e,r)=>{if(t===e)return!0;if(t.length===1&&t[0].semver===B9){if(e.length===1&&e[0].semver===B9)return!0;r.includePrerelease?t=qzt:t=yDe}if(e.length===1&&e[0].semver===B9){if(r.includePrerelease)return!0;e=yDe}let n=new Set,o,i;for(let h of t)h.operator===">"||h.operator===">="?o=EDe(o,h,r):h.operator==="<"||h.operator==="<="?i=TDe(i,h,r):n.add(h.semver);if(n.size>1)return null;let s;if(o&&i){if(s=V9(o.semver,i.semver,r),s>0)return null;if(s===0&&(o.operator!==">="||i.operator!=="<="))return null}for(let h of n){if(o&&!g0(h,String(o),r)||i&&!g0(h,String(i),r))return null;for(let _ of e)if(!g0(h,String(_),r))return!1;return!0}let a,c,u,p,f=i&&!r.includePrerelease&&i.semver.prerelease.length?i.semver:!1,m=o&&!r.includePrerelease&&o.semver.prerelease.length?o.semver:!1;f&&f.prerelease.length===1&&i.operator==="<"&&f.prerelease[0]===0&&(f=!1);for(let h of e){if(p=p||h.operator===">"||h.operator===">=",u=u||h.operator==="<"||h.operator==="<=",o){if(m&&h.semver.prerelease&&h.semver.prerelease.length&&h.semver.major===m.major&&h.semver.minor===m.minor&&h.semver.patch===m.patch&&(m=!1),h.operator===">"||h.operator===">="){if(a=EDe(o,h,r),a===h&&a!==o)return!1}else if(o.operator===">="&&!g0(o.semver,String(h),r))return!1}if(i){if(f&&h.semver.prerelease&&h.semver.prerelease.length&&h.semver.major===f.major&&h.semver.minor===f.minor&&h.semver.patch===f.patch&&(f=!1),h.operator==="<"||h.operator==="<="){if(c=TDe(i,h,r),c===h&&c!==i)return!1}else if(i.operator==="<="&&!g0(i.semver,String(h),r))return!1}if(!h.operator&&(i||o)&&s!==0)return!1}return!(o&&u&&!i&&s!==0||i&&p&&!o&&s!==0||m||f)},EDe=(t,e,r)=>{if(!t)return e;let n=V9(t.semver,e.semver,r);return n>0?t:n<0||e.operator===">"&&t.operator===">="?e:t},TDe=(t,e,r)=>{if(!t)return e;let n=V9(t.semver,e.semver,r);return n<0?t:n>0||e.operator==="<"&&t.operator==="<="?e:t};bDe.exports=Fzt});var PDe=S((ZNr,RDe)=>{"use strict";var H9=Dv(),ADe=l0(),Gzt=si(),wDe=N9(),Vzt=km(),Hzt=Xke(),Wzt=eMe(),Kzt=nMe(),Zzt=sMe(),Yzt=cMe(),Jzt=lMe(),Xzt=dMe(),Qzt=mMe(),e4t=Pa(),t4t=vMe(),r4t=yMe(),n4t=dM(),o4t=xMe(),i4t=wMe(),s4t=d0(),a4t=fM(),c4t=$9(),u4t=k9(),l4t=mM(),p4t=hM(),d4t=M9(),f4t=MMe(),m4t=f0(),h4t=Ia(),g4t=h0(),_4t=YMe(),v4t=XMe(),S4t=eDe(),y4t=nDe(),E4t=iDe(),T4t=vM(),b4t=pDe(),x4t=fDe(),A4t=gDe(),w4t=vDe(),R4t=xDe();RDe.exports={parse:Vzt,valid:Hzt,clean:Wzt,inc:Kzt,diff:Zzt,major:Yzt,minor:Jzt,patch:Xzt,prerelease:Qzt,compare:e4t,rcompare:t4t,compareLoose:r4t,compareBuild:n4t,sort:o4t,rsort:i4t,gt:s4t,lt:a4t,eq:c4t,neq:u4t,gte:l4t,lte:p4t,cmp:d4t,coerce:f4t,Comparator:m4t,Range:h4t,satisfies:g4t,toComparators:_4t,maxSatisfying:v4t,minSatisfying:S4t,minVersion:y4t,validRange:E4t,outside:T4t,gtr:b4t,ltr:x4t,intersects:A4t,simplifyRange:w4t,subset:R4t,SemVer:Gzt,re:H9.re,src:H9.src,tokens:H9.t,SEMVER_SPEC_VERSION:ADe.SEMVER_SPEC_VERSION,RELEASE_TYPES:ADe.RELEASE_TYPES,compareIdentifiers:wDe.compareIdentifiers,rcompareIdentifiers:wDe.rcompareIdentifiers}});var K9=S((YNr,NDe)=>{"use strict";function W9(t){return typeof t=="function"}var Ni=console.error.bind(console);function _0(t,e,r){var n=!!t[e]&&t.propertyIsEnumerable(e);Object.defineProperty(t,e,{configurable:!0,enumerable:n,writable:!0,value:r})}function v0(t){t&&t.logger&&(W9(t.logger)?Ni=t.logger:Ni("new logger isn't a function, not replacing"))}function IDe(t,e,r){if(!t||!t[e]){Ni("no original function "+e+" to wrap");return}if(!r){Ni("no wrapper function"),Ni(new Error().stack);return}if(!W9(t[e])||!W9(r)){Ni("original object and wrapper must be functions");return}var n=t[e],o=r(n,e);return _0(o,"__original",n),_0(o,"__unwrap",function(){t[e]===o&&_0(t,e,n)}),_0(o,"__wrapped",!0),_0(t,e,o),o}function P4t(t,e,r){if(t)Array.isArray(t)||(t=[t]);else{Ni("must provide one or more modules to patch"),Ni(new Error().stack);return}if(!(e&&Array.isArray(e))){Ni("must provide one or more functions to wrap on modules");return}t.forEach(function(n){e.forEach(function(o){IDe(n,o,r)})})}function ODe(t,e){if(!t||!t[e]){Ni("no function to unwrap."),Ni(new Error().stack);return}if(!t[e].__unwrap)Ni("no original to unwrap to -- has "+e+" already been unwrapped?");else return t[e].__unwrap()}function I4t(t,e){if(t)Array.isArray(t)||(t=[t]);else{Ni("must provide one or more modules to patch"),Ni(new Error().stack);return}if(!(e&&Array.isArray(e))){Ni("must provide one or more functions to unwrap on modules");return}t.forEach(function(r){e.forEach(function(n){ODe(r,n)})})}v0.wrap=IDe;v0.massWrap=P4t;v0.unwrap=ODe;v0.massUnwrap=I4t;NDe.exports=v0});var CDe=S(yM=>{"use strict";Object.defineProperty(yM,"__esModule",{value:!0});yM.InstrumentationAbstract=void 0;var Z9=(pe(),se(Pe)),O4t=P9(),SM=K9(),Y9=class{constructor(e,r,n){this.instrumentationName=e,this.instrumentationVersion=r,this._config={},this._wrap=SM.wrap,this._unwrap=SM.unwrap,this._massWrap=SM.massWrap,this._massUnwrap=SM.massUnwrap,this.setConfig(n),this._diag=Z9.diag.createComponentLogger({namespace:e}),this._tracer=Z9.trace.getTracer(e,r),this._meter=Z9.metrics.getMeter(e,r),this._logger=O4t.logs.getLogger(e,r),this._updateMetricInstruments()}get meter(){return this._meter}setMeterProvider(e){this._meter=e.getMeter(this.instrumentationName,this.instrumentationVersion),this._updateMetricInstruments()}get logger(){return this._logger}setLoggerProvider(e){this._logger=e.getLogger(this.instrumentationName,this.instrumentationVersion)}getModuleDefinitions(){var e;let r=(e=this.init())!==null&&e!==void 0?e:[];return Array.isArray(r)?r:[r]}_updateMetricInstruments(){}getConfig(){return this._config}setConfig(e){this._config=Object.assign({enabled:!0},e)}setTracerProvider(e){this._tracer=e.getTracer(this.instrumentationName,this.instrumentationVersion)}get tracer(){return this._tracer}_runSpanCustomizationHook(e,r,n,o){if(e)try{e(n,o)}catch(i){this._diag.error("Error running span customization hook due to exception in handler",{triggerName:r},i)}}};yM.InstrumentationAbstract=Y9});var $De=S(Nd=>{"use strict";Object.defineProperty(Nd,"__esModule",{value:!0});Nd.ModuleNameTrie=Nd.ModuleNameSeparator=void 0;Nd.ModuleNameSeparator="/";var EM=class{constructor(){this.hooks=[],this.children=new Map}},J9=class{constructor(){this._trie=new EM,this._counter=0}insert(e){let r=this._trie;for(let n of e.moduleName.split(Nd.ModuleNameSeparator)){let o=r.children.get(n);o||(o=new EM,r.children.set(n,o)),r=o}r.hooks.push({hook:e,insertedId:this._counter++})}search(e,{maintainInsertionOrder:r,fullOnly:n}={}){let o=this._trie,i=[],s=!0;for(let a of e.split(Nd.ModuleNameSeparator)){let c=o.children.get(a);if(!c){s=!1;break}n||i.push(...c.hooks),o=c}return n&&s&&i.push(...o.hooks),i.length===0?[]:i.length===1?[i[0].hook]:(r&&i.sort((a,c)=>a.insertedId-c.insertedId),i.map(({hook:a})=>a))}};Nd.ModuleNameTrie=J9});var MDe=S(TM=>{"use strict";Object.defineProperty(TM,"__esModule",{value:!0});TM.RequireInTheMiddleSingleton=void 0;var N4t=zb(),kDe=require("path"),X9=$De(),C4t=["afterEach","after","beforeEach","before","describe","it"].every(t=>typeof global[t]=="function"),Q9=class t{constructor(){this._moduleNameTrie=new X9.ModuleNameTrie,this._initialize()}_initialize(){new N4t.Hook(null,{internals:!0},(e,r,n)=>{let o=$4t(r),i=this._moduleNameTrie.search(o,{maintainInsertionOrder:!0,fullOnly:n===void 0});for(let{onRequire:s}of i)e=s(e,r,n);return e})}register(e,r){let n={moduleName:e,onRequire:r};return this._moduleNameTrie.insert(n),n}static getInstance(){var e;return C4t?new t:this._instance=(e=this._instance)!==null&&e!==void 0?e:new t}};TM.RequireInTheMiddleSingleton=Q9;function $4t(t){return kDe.sep!==X9.ModuleNameSeparator?t.split(kDe.sep).join(X9.ModuleNameSeparator):t}});var eW=S(Cd=>{"use strict";Object.defineProperty(Cd,"__esModule",{value:!0});Cd.isWrapped=Cd.safeExecuteInTheMiddleAsync=Cd.safeExecuteInTheMiddle=void 0;function k4t(t,e,r){let n,o;try{o=t()}catch(i){n=i}finally{if(e(n,o),n&&!r)throw n;return o}}Cd.safeExecuteInTheMiddle=k4t;async function M4t(t,e,r){let n,o;try{o=await t()}catch(i){n=i}finally{if(e(n,o),n&&!r)throw n;return o}}Cd.safeExecuteInTheMiddleAsync=M4t;function D4t(t){return typeof t=="function"&&typeof t.__original=="function"&&typeof t.__unwrap=="function"&&t.__wrapped===!0}Cd.isWrapped=D4t});var UDe=S(bM=>{"use strict";Object.defineProperty(bM,"__esModule",{value:!0});bM.InstrumentationBase=void 0;var S0=require("path"),DDe=require("util"),L4t=PDe(),tW=K9(),U4t=CDe(),j4t=MDe(),z4t=qb(),y0=(pe(),se(Pe)),F4t=zb(),q4t=require("fs"),B4t=eW(),rW=class extends U4t.InstrumentationAbstract{constructor(e,r,n){super(e,r,n),this._hooks=[],this._requireInTheMiddleSingleton=j4t.RequireInTheMiddleSingleton.getInstance(),this._enabled=!1,this._wrap=(i,s,a)=>{if((0,B4t.isWrapped)(i[s])&&this._unwrap(i,s),DDe.types.isProxy(i)){let c=(0,tW.wrap)(Object.assign({},i),s,a);return Object.defineProperty(i,s,{value:c}),c}else return(0,tW.wrap)(i,s,a)},this._unwrap=(i,s)=>DDe.types.isProxy(i)?Object.defineProperty(i,s,{value:i[s]}):(0,tW.unwrap)(i,s),this._massWrap=(i,s,a)=>{if(i)Array.isArray(i)||(i=[i]);else{y0.diag.error("must provide one or more modules to patch");return}if(!(s&&Array.isArray(s))){y0.diag.error("must provide one or more functions to wrap on modules");return}i.forEach(c=>{s.forEach(u=>{this._wrap(c,u,a)})})},this._massUnwrap=(i,s)=>{if(i)Array.isArray(i)||(i=[i]);else{y0.diag.error("must provide one or more modules to patch");return}if(!(s&&Array.isArray(s))){y0.diag.error("must provide one or more functions to wrap on modules");return}i.forEach(a=>{s.forEach(c=>{this._unwrap(a,c)})})};let o=this.init();o&&!Array.isArray(o)&&(o=[o]),this._modules=o||[],this._config.enabled&&this.enable()}_warnOnPreloadedModules(){this._modules.forEach(e=>{let{name:r}=e;try{let n=require.resolve(r);require.cache[n]&&this._diag.warn(`Module ${r} has been loaded before ${this.instrumentationName} so it might not work, please initialize it before requiring ${r}`)}catch{}})}_extractPackageVersion(e){try{let r=(0,q4t.readFileSync)(S0.join(e,"package.json"),{encoding:"utf8"}),n=JSON.parse(r).version;return typeof n=="string"?n:void 0}catch{y0.diag.warn("Failed extracting version",e)}}_onRequire(e,r,n,o){var i;if(!o)return typeof e.patch=="function"&&(e.moduleExports=r,this._enabled)?(this._diag.debug("Applying instrumentation patch for nodejs core module on require hook",{module:e.name}),e.patch(r)):r;let s=this._extractPackageVersion(o);if(e.moduleVersion=s,e.name===n)return LDe(e.supportedVersions,s,e.includePrerelease)&&typeof e.patch=="function"&&(e.moduleExports=r,this._enabled)?(this._diag.debug("Applying instrumentation patch for module on require hook",{module:e.name,version:e.moduleVersion,baseDir:o}),e.patch(r,e.moduleVersion)):r;let a=(i=e.files)!==null&&i!==void 0?i:[],c=S0.normalize(n);return a.filter(p=>p.name===c).filter(p=>LDe(p.supportedVersions,s,e.includePrerelease)).reduce((p,f)=>(f.moduleExports=p,this._enabled?(this._diag.debug("Applying instrumentation patch for nodejs module file on require hook",{module:e.name,version:e.moduleVersion,fileName:f.name,baseDir:o}),f.patch(p,e.moduleVersion)):p),r)}enable(){if(!this._enabled){if(this._enabled=!0,this._hooks.length>0){for(let e of this._modules){typeof e.patch=="function"&&e.moduleExports&&(this._diag.debug("Applying instrumentation patch for nodejs module on instrumentation enabled",{module:e.name,version:e.moduleVersion}),e.patch(e.moduleExports,e.moduleVersion));for(let r of e.files)r.moduleExports&&(this._diag.debug("Applying instrumentation patch for nodejs module file on instrumentation enabled",{module:e.name,version:e.moduleVersion,fileName:r.name}),r.patch(r.moduleExports,e.moduleVersion))}return}this._warnOnPreloadedModules();for(let e of this._modules){let r=(s,a,c)=>{if(!c&&S0.isAbsolute(a)){let u=S0.parse(a);a=u.name,c=u.dir}return this._onRequire(e,s,a,c)},n=(s,a,c)=>this._onRequire(e,s,a,c),o=S0.isAbsolute(e.name)?new F4t.Hook([e.name],{internals:!0},n):this._requireInTheMiddleSingleton.register(e.name,n);this._hooks.push(o);let i=new z4t.Hook([e.name],{internals:!1},r);this._hooks.push(i)}}}disable(){if(this._enabled){this._enabled=!1;for(let e of this._modules){typeof e.unpatch=="function"&&e.moduleExports&&(this._diag.debug("Removing instrumentation patch for nodejs module on instrumentation disabled",{module:e.name,version:e.moduleVersion}),e.unpatch(e.moduleExports,e.moduleVersion));for(let r of e.files)r.moduleExports&&(this._diag.debug("Removing instrumentation patch for nodejs module file on instrumentation disabled",{module:e.name,version:e.moduleVersion,fileName:r.name}),r.unpatch(r.moduleExports,e.moduleVersion))}}}isEnabled(){return this._enabled}};bM.InstrumentationBase=rW;function LDe(t,e,r){return typeof e>"u"?t.includes("*"):t.some(n=>(0,L4t.satisfies)(e,n,{includePrerelease:r}))}});var jDe=S(xM=>{"use strict";Object.defineProperty(xM,"__esModule",{value:!0});xM.normalize=void 0;var G4t=require("path");Object.defineProperty(xM,"normalize",{enumerable:!0,get:function(){return G4t.normalize}})});var zDe=S(Uv=>{"use strict";Object.defineProperty(Uv,"__esModule",{value:!0});Uv.normalize=Uv.InstrumentationBase=void 0;var V4t=UDe();Object.defineProperty(Uv,"InstrumentationBase",{enumerable:!0,get:function(){return V4t.InstrumentationBase}});var H4t=jDe();Object.defineProperty(Uv,"normalize",{enumerable:!0,get:function(){return H4t.normalize}})});var nW=S(jv=>{"use strict";Object.defineProperty(jv,"__esModule",{value:!0});jv.normalize=jv.InstrumentationBase=void 0;var FDe=zDe();Object.defineProperty(jv,"InstrumentationBase",{enumerable:!0,get:function(){return FDe.InstrumentationBase}});Object.defineProperty(jv,"normalize",{enumerable:!0,get:function(){return FDe.normalize}})});var qDe=S(AM=>{"use strict";Object.defineProperty(AM,"__esModule",{value:!0});AM.InstrumentationNodeModuleDefinition=void 0;var oW=class{constructor(e,r,n,o,i){this.name=e,this.supportedVersions=r,this.patch=n,this.unpatch=o,this.files=i||[]}};AM.InstrumentationNodeModuleDefinition=oW});var BDe=S(wM=>{"use strict";Object.defineProperty(wM,"__esModule",{value:!0});wM.InstrumentationNodeModuleFile=void 0;var W4t=nW(),iW=class{constructor(e,r,n,o){this.supportedVersions=r,this.patch=n,this.unpatch=o,this.name=(0,W4t.normalize)(e)}};wM.InstrumentationNodeModuleFile=iW});var GDe=S(ai=>{"use strict";Object.defineProperty(ai,"__esModule",{value:!0});ai.safeExecuteInTheMiddleAsync=ai.safeExecuteInTheMiddle=ai.isWrapped=ai.InstrumentationNodeModuleFile=ai.InstrumentationNodeModuleDefinition=ai.InstrumentationBase=ai.registerInstrumentations=void 0;var K4t=jke();Object.defineProperty(ai,"registerInstrumentations",{enumerable:!0,get:function(){return K4t.registerInstrumentations}});var Z4t=nW();Object.defineProperty(ai,"InstrumentationBase",{enumerable:!0,get:function(){return Z4t.InstrumentationBase}});var Y4t=qDe();Object.defineProperty(ai,"InstrumentationNodeModuleDefinition",{enumerable:!0,get:function(){return Y4t.InstrumentationNodeModuleDefinition}});var J4t=BDe();Object.defineProperty(ai,"InstrumentationNodeModuleFile",{enumerable:!0,get:function(){return J4t.InstrumentationNodeModuleFile}});var sW=eW();Object.defineProperty(ai,"isWrapped",{enumerable:!0,get:function(){return sW.isWrapped}});Object.defineProperty(ai,"safeExecuteInTheMiddle",{enumerable:!0,get:function(){return sW.safeExecuteInTheMiddle}});Object.defineProperty(ai,"safeExecuteInTheMiddleAsync",{enumerable:!0,get:function(){return sW.safeExecuteInTheMiddleAsync}})});var eLe=S(zv=>{"use strict";Object.defineProperty(zv,"__esModule",{value:!0});zv.PACKAGE_NAME=zv.PACKAGE_VERSION=void 0;zv.PACKAGE_VERSION="0.50.0";zv.PACKAGE_NAME="@opentelemetry/instrumentation-hapi"});var lW=S(xu=>{"use strict";Object.defineProperty(xu,"__esModule",{value:!0});xu.HapiLifecycleMethodNames=xu.HapiLayerType=xu.handlerPatched=xu.HapiComponentName=void 0;xu.HapiComponentName="@hapi/hapi";xu.handlerPatched=Symbol("hapi-handler-patched");xu.HapiLayerType={ROUTER:"router",PLUGIN:"plugin",EXT:"server.ext"};xu.HapiLifecycleMethodNames=new Set(["onPreAuth","onCredentials","onPostAuth","onPreHandler","onPostHandler","onPreResponse","onRequest"])});var tLe=S(PM=>{"use strict";Object.defineProperty(PM,"__esModule",{value:!0});PM.ATTR_HTTP_METHOD=void 0;PM.ATTR_HTTP_METHOD="http.method"});var pW=S(E0=>{"use strict";Object.defineProperty(E0,"__esModule",{value:!0});E0.AttributeNames=void 0;var c2t;(function(t){t.HAPI_TYPE="hapi.type",t.PLUGIN_NAME="hapi.plugin.name",t.EXT_TYPE="server.ext.type"})(c2t=E0.AttributeNames||(E0.AttributeNames={}))});var oLe=S(Gn=>{"use strict";Object.defineProperty(Gn,"__esModule",{value:!0});Gn.getPluginFromInput=Gn.getExtMetadata=Gn.getRouteMetadata=Gn.isPatchableExtMethod=Gn.isDirectExtInput=Gn.isLifecycleExtEventObj=Gn.isLifecycleExtType=Gn.getPluginName=void 0;var rLe=(er(),se(Cr)),u2t=tLe(),T0=lW(),$d=pW(),nLe=Ft();function l2t(t){return t.name?t.name:t.pkg.name}Gn.getPluginName=l2t;var p2t=t=>typeof t=="string"&&T0.HapiLifecycleMethodNames.has(t);Gn.isLifecycleExtType=p2t;var d2t=t=>{let e=t?.type;return e!==void 0&&(0,Gn.isLifecycleExtType)(e)};Gn.isLifecycleExtEventObj=d2t;var f2t=t=>Array.isArray(t)&&t.length<=3&&(0,Gn.isLifecycleExtType)(t[0])&&typeof t[1]=="function";Gn.isDirectExtInput=f2t;var m2t=t=>!Array.isArray(t);Gn.isPatchableExtMethod=m2t;var h2t=(t,e,r)=>{let n={[rLe.ATTR_HTTP_ROUTE]:t.path};e&nLe.SemconvStability.OLD&&(n[u2t.ATTR_HTTP_METHOD]=t.method),e&nLe.SemconvStability.STABLE&&(n[rLe.ATTR_HTTP_REQUEST_METHOD]=t.method);let o;return r?(n[$d.AttributeNames.HAPI_TYPE]=T0.HapiLayerType.PLUGIN,n[$d.AttributeNames.PLUGIN_NAME]=r,o=`${r}: route - ${t.path}`):(n[$d.AttributeNames.HAPI_TYPE]=T0.HapiLayerType.ROUTER,o=`route - ${t.path}`),{attributes:n,name:o}};Gn.getRouteMetadata=h2t;var g2t=(t,e)=>e?{attributes:{[$d.AttributeNames.EXT_TYPE]:t,[$d.AttributeNames.HAPI_TYPE]:T0.HapiLayerType.EXT,[$d.AttributeNames.PLUGIN_NAME]:e},name:`${e}: ext - ${t}`}:{attributes:{[$d.AttributeNames.EXT_TYPE]:t,[$d.AttributeNames.HAPI_TYPE]:T0.HapiLayerType.EXT},name:`ext - ${t}`};Gn.getExtMetadata=g2t;var _2t=t=>"plugin"in t?"plugin"in t.plugin?t.plugin.plugin:t.plugin:t;Gn.getPluginFromInput=_2t});var aLe=S(IM=>{"use strict";Object.defineProperty(IM,"__esModule",{value:!0});IM.HapiInstrumentation=void 0;var Is=(pe(),se(Pe)),iLe=_r(),b0=Ft(),sLe=eLe(),x0=lW(),kl=oLe(),dW=class extends b0.InstrumentationBase{_semconvStability;constructor(e={}){super(sLe.PACKAGE_NAME,sLe.PACKAGE_VERSION,e),this._semconvStability=(0,b0.semconvStabilityFromStr)("http",process.env.OTEL_SEMCONV_STABILITY_OPT_IN)}init(){return new b0.InstrumentationNodeModuleDefinition(x0.HapiComponentName,[">=17.0.0 <22"],e=>{let r=e[Symbol.toStringTag]==="Module"?e.default:e;return(0,b0.isWrapped)(r.server)||this._wrap(r,"server",this._getServerPatch.bind(this)),(0,b0.isWrapped)(r.Server)||this._wrap(r,"Server",this._getServerPatch.bind(this)),r},e=>{let r=e[Symbol.toStringTag]==="Module"?e.default:e;this._massUnwrap([r],["server","Server"])})}_getServerPatch(e){let r=this,n=this;return function(i){let s=e.apply(this,[i]);return n._wrap(s,"route",a=>r._getServerRoutePatch.bind(r)(a)),n._wrap(s,"ext",a=>r._getServerExtPatch.bind(r)(a)),n._wrap(s,"register",r._getServerRegisterPatch.bind(r)),s}}_getServerRegisterPatch(e){let r=this;return function(o,i){if(Array.isArray(o))for(let s of o){let a=(0,kl.getPluginFromInput)(s);r._wrapRegisterHandler(a)}else{let s=(0,kl.getPluginFromInput)(o);r._wrapRegisterHandler(s)}return e.apply(this,[o,i])}}_getServerExtPatch(e,r){let n=this;return function(...i){if(Array.isArray(i[0])){let s=i[0];for(let a=0;ar._getServerRoutePatch.bind(r)(u,n)),i._wrap(a,"ext",u=>r._getServerExtPatch.bind(r)(u,n)),o.call(this,a,c)};e.register=s}_wrapExtMethods(e,r,n){let o=this;if(e instanceof Array){for(let i=0;iasync function(...s){if(Is.trace.getSpan(Is.context.active())===void 0)return await i.call(this,...s);let a=(0,iLe.getRPCMetadata)(Is.context.active());a?.type===iLe.RPCType.HTTP&&(a.route=e.path);let c=(0,kl.getRouteMetadata)(e,n._semconvStability,r),u=n.tracer.startSpan(c.name,{attributes:c.attributes});try{return await Is.context.with(Is.trace.setSpan(Is.context.active(),u),()=>i.call(this,...s))}catch(p){throw u.recordException(p),u.setStatus({code:Is.SpanStatusCode.ERROR,message:p.message}),p}finally{u.end()}};if(typeof e.handler=="function")e.handler=o(e.handler);else if(typeof e.options=="function"){let i=e.options;e.options=function(s){let a=i(s);return typeof a.handler=="function"&&(a.handler=o(a.handler)),a}}else typeof e.options?.handler=="function"&&(e.options.handler=o(e.options.handler));return e}};IM.HapiInstrumentation=dW});var cLe=S(Fv=>{"use strict";Object.defineProperty(Fv,"__esModule",{value:!0});Fv.AttributeNames=Fv.HapiInstrumentation=void 0;var v2t=aLe();Object.defineProperty(Fv,"HapiInstrumentation",{enumerable:!0,get:function(){return v2t.HapiInstrumentation}});var S2t=pW();Object.defineProperty(Fv,"AttributeNames",{enumerable:!0,get:function(){return S2t.AttributeNames}})});var OM=S(A0=>{"use strict";Object.defineProperty(A0,"__esModule",{value:!0});A0.KoaLayerType=void 0;var E2t;(function(t){t.ROUTER="router",t.MIDDLEWARE="middleware"})(E2t=A0.KoaLayerType||(A0.KoaLayerType={}))});var fLe=S(qv=>{"use strict";Object.defineProperty(qv,"__esModule",{value:!0});qv.PACKAGE_NAME=qv.PACKAGE_VERSION=void 0;qv.PACKAGE_VERSION="0.51.0";qv.PACKAGE_NAME="@opentelemetry/instrumentation-koa"});var fW=S(w0=>{"use strict";Object.defineProperty(w0,"__esModule",{value:!0});w0.AttributeNames=void 0;var T2t;(function(t){t.KOA_TYPE="koa.type",t.KOA_NAME="koa.name"})(T2t=w0.AttributeNames||(w0.AttributeNames={}))});var hLe=S(Bv=>{"use strict";Object.defineProperty(Bv,"__esModule",{value:!0});Bv.isLayerIgnored=Bv.getMiddlewareMetadata=void 0;var mLe=OM(),NM=fW(),b2t=(er(),se(Cr)),x2t=(t,e,r,n)=>r?{attributes:{[NM.AttributeNames.KOA_NAME]:n?.toString(),[NM.AttributeNames.KOA_TYPE]:mLe.KoaLayerType.ROUTER,[b2t.SEMATTRS_HTTP_ROUTE]:n?.toString()},name:t._matchedRouteName||`router - ${n}`}:{attributes:{[NM.AttributeNames.KOA_NAME]:e.name??"middleware",[NM.AttributeNames.KOA_TYPE]:mLe.KoaLayerType.MIDDLEWARE},name:`middleware - ${e.name}`};Bv.getMiddlewareMetadata=x2t;var A2t=(t,e)=>!!(Array.isArray(e?.ignoreLayersType)&&e?.ignoreLayersType?.includes(t));Bv.isLayerIgnored=A2t});var gLe=S(CM=>{"use strict";Object.defineProperty(CM,"__esModule",{value:!0});CM.kLayerPatched=void 0;CM.kLayerPatched=Symbol("koa-layer-patched")});var TLe=S($M=>{"use strict";Object.defineProperty($M,"__esModule",{value:!0});$M.KoaInstrumentation=void 0;var Au=(pe(),se(Pe)),R0=Ft(),_Le=OM(),vLe=fLe(),SLe=hLe(),yLe=_r(),ELe=gLe(),mW=class extends R0.InstrumentationBase{constructor(e={}){super(vLe.PACKAGE_NAME,vLe.PACKAGE_VERSION,e)}init(){return new R0.InstrumentationNodeModuleDefinition("koa",[">=2.0.0 <3"],e=>{let r=e[Symbol.toStringTag]==="Module"?e.default:e;return r==null?r:((0,R0.isWrapped)(r.prototype.use)&&this._unwrap(r.prototype,"use"),this._wrap(r.prototype,"use",this._getKoaUsePatch.bind(this)),e)},e=>{let r=e[Symbol.toStringTag]==="Module"?e.default:e;(0,R0.isWrapped)(r.prototype.use)&&this._unwrap(r.prototype,"use")})}_getKoaUsePatch(e){let r=this;return function(o){let i;return o.router?i=r._patchRouterDispatch(o):i=r._patchLayer(o,!1),e.apply(this,[i])}}_patchRouterDispatch(e){Au.diag.debug("Patching @koa/router dispatch");let n=e.router?.stack??[];for(let o of n){let i=o.path,s=o.stack;for(let a=0;a{if(Au.trace.getSpan(Au.context.active())===void 0)return e(i,s);let c=(0,SLe.getMiddlewareMetadata)(i,e,r,n),u=this.tracer.startSpan(c.name,{attributes:c.attributes}),p=(0,yLe.getRPCMetadata)(Au.context.active());p?.type===yLe.RPCType.HTTP&&i._matchedRoute&&(p.route=i._matchedRoute.toString());let{requestHook:f}=this.getConfig();f&&(0,R0.safeExecuteInTheMiddle)(()=>f(u,{context:i,middlewareLayer:e,layerType:o}),h=>{h&&Au.diag.error("koa instrumentation: request hook failed",h)},!0);let m=Au.trace.setSpan(Au.context.active(),u);return Au.context.with(m,async()=>{try{return await e(i,s)}catch(h){throw u.recordException(h),h}finally{u.end()}})})}};$M.KoaInstrumentation=mW});var bLe=S(kd=>{"use strict";Object.defineProperty(kd,"__esModule",{value:!0});kd.KoaLayerType=kd.AttributeNames=kd.KoaInstrumentation=void 0;var w2t=TLe();Object.defineProperty(kd,"KoaInstrumentation",{enumerable:!0,get:function(){return w2t.KoaInstrumentation}});var R2t=fW();Object.defineProperty(kd,"AttributeNames",{enumerable:!0,get:function(){return R2t.AttributeNames}});var P2t=OM();Object.defineProperty(kd,"KoaLayerType",{enumerable:!0,get:function(){return P2t.KoaLayerType}})});var hW=S(wc=>{"use strict";Object.defineProperty(wc,"__esModule",{value:!0});wc.ConnectNames=wc.ConnectTypes=wc.AttributeNames=void 0;var O2t;(function(t){t.CONNECT_TYPE="connect.type",t.CONNECT_NAME="connect.name"})(O2t=wc.AttributeNames||(wc.AttributeNames={}));var N2t;(function(t){t.MIDDLEWARE="middleware",t.REQUEST_HANDLER="request_handler"})(N2t=wc.ConnectTypes||(wc.ConnectTypes={}));var C2t;(function(t){t.MIDDLEWARE="middleware",t.REQUEST_HANDLER="request handler"})(C2t=wc.ConnectNames||(wc.ConnectNames={}))});var PLe=S(Gv=>{"use strict";Object.defineProperty(Gv,"__esModule",{value:!0});Gv.PACKAGE_NAME=Gv.PACKAGE_VERSION=void 0;Gv.PACKAGE_VERSION="0.47.0";Gv.PACKAGE_NAME="@opentelemetry/instrumentation-connect"});var ILe=S(kM=>{"use strict";Object.defineProperty(kM,"__esModule",{value:!0});kM._LAYERS_STORE_PROPERTY=void 0;kM._LAYERS_STORE_PROPERTY=Symbol("opentelemetry.instrumentation-connect.request-route-stack")});var OLe=S(Dd=>{"use strict";Object.defineProperty(Dd,"__esModule",{value:!0});Dd.generateRoute=Dd.replaceCurrentStackRoute=Dd.addNewStackLayer=void 0;var $2t=(pe(),se(Pe)),Md=ILe(),k2t=t=>{Array.isArray(t[Md._LAYERS_STORE_PROPERTY])===!1&&Object.defineProperty(t,Md._LAYERS_STORE_PROPERTY,{enumerable:!1,value:[]}),t[Md._LAYERS_STORE_PROPERTY].push("/");let e=t[Md._LAYERS_STORE_PROPERTY].length;return()=>{e===t[Md._LAYERS_STORE_PROPERTY].length?t[Md._LAYERS_STORE_PROPERTY].pop():$2t.diag.warn("Connect: Trying to pop the stack multiple time")}};Dd.addNewStackLayer=k2t;var M2t=(t,e)=>{e&&t[Md._LAYERS_STORE_PROPERTY].splice(-1,1,e)};Dd.replaceCurrentStackRoute=M2t;var D2t=t=>t[Md._LAYERS_STORE_PROPERTY].reduce((e,r)=>e.replace(/\/+$/,"")+r);Dd.generateRoute=D2t});var $Le=S(Ld=>{"use strict";Object.defineProperty(Ld,"__esModule",{value:!0});Ld.ConnectInstrumentation=Ld.ANONYMOUS_NAME=void 0;var L2t=(pe(),se(Pe)),NLe=_r(),Vv=hW(),CLe=PLe(),MM=Ft(),U2t=(er(),se(Cr)),gW=OLe();Ld.ANONYMOUS_NAME="anonymous";var _W=class extends MM.InstrumentationBase{constructor(e={}){super(CLe.PACKAGE_NAME,CLe.PACKAGE_VERSION,e)}init(){return[new MM.InstrumentationNodeModuleDefinition("connect",[">=3.0.0 <4"],e=>this._patchConstructor(e))]}_patchApp(e){(0,MM.isWrapped)(e.use)||this._wrap(e,"use",this._patchUse.bind(this)),(0,MM.isWrapped)(e.handle)||this._wrap(e,"handle",this._patchHandle.bind(this))}_patchConstructor(e){let r=this;return function(...n){let o=e.apply(this,n);return r._patchApp(o),o}}_patchNext(e,r){return function(o){let i=e.apply(this,[o]);return r(),i}}_startSpan(e,r){let n,o,i;e?(n=Vv.ConnectTypes.REQUEST_HANDLER,i=Vv.ConnectNames.REQUEST_HANDLER,o=e):(n=Vv.ConnectTypes.MIDDLEWARE,i=Vv.ConnectNames.MIDDLEWARE,o=r.name||Ld.ANONYMOUS_NAME);let s=`${i} - ${o}`,a={attributes:{[U2t.ATTR_HTTP_ROUTE]:e.length>0?e:"/",[Vv.AttributeNames.CONNECT_TYPE]:n,[Vv.AttributeNames.CONNECT_NAME]:o}};return this.tracer.startSpan(s,a)}_patchMiddleware(e,r){let n=this,o=r.length===4;function i(){if(!n.isEnabled())return r.apply(this,arguments);let[s,a,c]=o?[1,2,3]:[0,1,2],u=arguments[s],p=arguments[a],f=arguments[c];(0,gW.replaceCurrentStackRoute)(u,e);let m=(0,NLe.getRPCMetadata)(L2t.context.active());e&&m?.type===NLe.RPCType.HTTP&&(m.route=(0,gW.generateRoute)(u));let h="";e?h=`request handler - ${e}`:h=`middleware - ${r.name||Ld.ANONYMOUS_NAME}`;let _=n._startSpan(e,r);n._diag.debug("start span",h);let v=!1;function E(){v?n._diag.debug(`span ${_.name} - already finished`):(v=!0,n._diag.debug(`finishing span ${_.name}`),_.end()),p.removeListener("close",E)}return p.addListener("close",E),arguments[c]=n._patchNext(f,E),r.apply(this,arguments)}return Object.defineProperty(i,"length",{value:r.length,writable:!1,configurable:!0}),i}_patchUse(e){let r=this;return function(...n){let o=n[n.length-1],i=n[n.length-2]||"";return n[n.length-1]=r._patchMiddleware(i,o),e.apply(this,n)}}_patchHandle(e){let r=this;return function(){let[n,o]=[0,2],i=arguments[n],s=arguments[o],a=(0,gW.addNewStackLayer)(i);return typeof s=="function"&&(arguments[o]=r._patchOut(s,a)),e.apply(this,arguments)}}_patchOut(e,r){return function(...o){return r(),Reflect.apply(e,this,o)}}};Ld.ConnectInstrumentation=_W});var MLe=S(Oa=>{"use strict";Object.defineProperty(Oa,"__esModule",{value:!0});Oa.ConnectTypes=Oa.ConnectNames=Oa.AttributeNames=Oa.ANONYMOUS_NAME=Oa.ConnectInstrumentation=void 0;var kLe=$Le();Object.defineProperty(Oa,"ConnectInstrumentation",{enumerable:!0,get:function(){return kLe.ConnectInstrumentation}});Object.defineProperty(Oa,"ANONYMOUS_NAME",{enumerable:!0,get:function(){return kLe.ANONYMOUS_NAME}});var vW=hW();Object.defineProperty(Oa,"AttributeNames",{enumerable:!0,get:function(){return vW.AttributeNames}});Object.defineProperty(Oa,"ConnectNames",{enumerable:!0,get:function(){return vW.ConnectNames}});Object.defineProperty(Oa,"ConnectTypes",{enumerable:!0,get:function(){return vW.ConnectTypes}})});var zLe=S(Hv=>{"use strict";Object.defineProperty(Hv,"__esModule",{value:!0});Hv.once=Hv.getSpanName=void 0;function z2t(t,e,r,n){return t==="execBulkLoad"&&n&&e?`${t} ${n} ${e}`:t==="callProcedure"?e?`${t} ${r} ${e}`:`${t} ${r}`:e?`${t} ${e}`:`${t}`}Hv.getSpanName=z2t;var F2t=t=>{let e=!1;return(...r)=>{if(!e)return e=!0,t(...r)}};Hv.once=F2t});var FLe=S(Wv=>{"use strict";Object.defineProperty(Wv,"__esModule",{value:!0});Wv.PACKAGE_NAME=Wv.PACKAGE_VERSION=void 0;Wv.PACKAGE_VERSION="0.22.0";Wv.PACKAGE_NAME="@opentelemetry/instrumentation-tedious"});var HLe=S(UM=>{"use strict";Object.defineProperty(UM,"__esModule",{value:!0});UM.TediousInstrumentation=void 0;var P0=(pe(),se(Pe)),q2t=require("events"),DM=Ft(),Ud=(er(),se(Cr)),qLe=zLe(),BLe=FLe(),VLe=Symbol("opentelemetry.instrumentation-tedious.current-database"),GLe=["callProcedure","execSql","execSqlBatch","execBulkLoad","prepare","execute"];function LM(t){Object.defineProperty(this,VLe,{value:t,writable:!0})}var SW=class t extends DM.InstrumentationBase{static COMPONENT="tedious";constructor(e={}){super(BLe.PACKAGE_NAME,BLe.PACKAGE_VERSION,e)}init(){return[new DM.InstrumentationNodeModuleDefinition(t.COMPONENT,[">=1.11.0 <20"],e=>{let r=e.Connection.prototype;for(let n of GLe)(0,DM.isWrapped)(r[n])&&this._unwrap(r,n),this._wrap(r,n,this._patchQuery(n));return(0,DM.isWrapped)(r.connect)&&this._unwrap(r,"connect"),this._wrap(r,"connect",this._patchConnect),e},e=>{if(e===void 0)return;let r=e.Connection.prototype;for(let n of GLe)this._unwrap(r,n);this._unwrap(r,"connect")})]}_patchConnect(e){return function(){return LM.call(this,this.config?.options?.database),this.removeListener("databaseChange",LM),this.on("databaseChange",LM),this.once("end",()=>{this.removeListener("databaseChange",LM)}),e.apply(this,arguments)}}_patchQuery(e){return r=>{let n=this;function o(i){if(!(i instanceof q2t.EventEmitter))return n._diag.warn(`Unexpected invocation of patched ${e} method. Span not recorded`),r.apply(this,arguments);let s=0,a=0,c=()=>a++,u=()=>s++,p=this[VLe],f=(_=>_.sqlTextOrProcedure==="sp_prepare"&&_.parametersByName?.stmt?.value?_.parametersByName.stmt.value:_.sqlTextOrProcedure)(i),m=n.tracer.startSpan((0,qLe.getSpanName)(e,p,f,i.table),{kind:P0.SpanKind.CLIENT,attributes:{[Ud.SEMATTRS_DB_SYSTEM]:Ud.DBSYSTEMVALUES_MSSQL,[Ud.SEMATTRS_DB_NAME]:p,[Ud.SEMATTRS_NET_PEER_PORT]:this.config?.options?.port,[Ud.SEMATTRS_NET_PEER_NAME]:this.config?.server,[Ud.SEMATTRS_DB_USER]:this.config?.userName??this.config?.authentication?.options?.userName,[Ud.SEMATTRS_DB_STATEMENT]:f,[Ud.SEMATTRS_DB_SQL_TABLE]:i.table}}),h=(0,qLe.once)(_=>{i.removeListener("done",c),i.removeListener("doneInProc",c),i.removeListener("doneProc",u),i.removeListener("error",h),this.removeListener("end",h),m.setAttribute("tedious.procedure_count",s),m.setAttribute("tedious.statement_count",a),_&&m.setStatus({code:P0.SpanStatusCode.ERROR,message:_.message}),m.end()});return i.on("done",c),i.on("doneInProc",c),i.on("doneProc",u),i.once("error",h),this.on("end",h),typeof i.callback=="function"?n._wrap(i,"callback",n._patchCallbackQuery(h)):n._diag.error("Expected request.callback to be a function"),P0.context.with(P0.trace.setSpan(P0.context.active(),m),r,this,...arguments)}return Object.defineProperty(o,"length",{value:r.length,writable:!1}),o}}_patchCallbackQuery(e){return r=>function(n,o,i){return e(n),r.apply(this,arguments)}}};UM.TediousInstrumentation=SW});var WLe=S(jM=>{"use strict";Object.defineProperty(jM,"__esModule",{value:!0});jM.TediousInstrumentation=void 0;var B2t=HLe();Object.defineProperty(jM,"TediousInstrumentation",{enumerable:!0,get:function(){return B2t.TediousInstrumentation}})});var XLe=S(Kv=>{"use strict";Object.defineProperty(Kv,"__esModule",{value:!0});Kv.PACKAGE_NAME=Kv.PACKAGE_VERSION=void 0;Kv.PACKAGE_VERSION="0.47.0";Kv.PACKAGE_NAME="@opentelemetry/instrumentation-generic-pool"});var eUe=S(zM=>{"use strict";Object.defineProperty(zM,"__esModule",{value:!0});zM.GenericPoolInstrumentation=void 0;var Zv=(pe(),se(Pe)),Mm=Ft(),QLe=XLe(),yW="generic-pool",EW=class extends Mm.InstrumentationBase{_isDisabled=!1;constructor(e={}){super(QLe.PACKAGE_NAME,QLe.PACKAGE_VERSION,e)}init(){return[new Mm.InstrumentationNodeModuleDefinition(yW,[">=3.0.0 <4"],e=>{let r=e.Pool;return(0,Mm.isWrapped)(r.prototype.acquire)&&this._unwrap(r.prototype,"acquire"),this._wrap(r.prototype,"acquire",this._acquirePatcher.bind(this)),e},e=>{let r=e.Pool;return this._unwrap(r.prototype,"acquire"),e}),new Mm.InstrumentationNodeModuleDefinition(yW,[">=2.4.0 <3"],e=>{let r=e.Pool;return(0,Mm.isWrapped)(r.prototype.acquire)&&this._unwrap(r.prototype,"acquire"),this._wrap(r.prototype,"acquire",this._acquireWithCallbacksPatcher.bind(this)),e},e=>{let r=e.Pool;return this._unwrap(r.prototype,"acquire"),e}),new Mm.InstrumentationNodeModuleDefinition(yW,[">=2.0.0 <2.4"],e=>(this._isDisabled=!1,(0,Mm.isWrapped)(e.Pool)&&this._unwrap(e,"Pool"),this._wrap(e,"Pool",this._poolWrapper.bind(this)),e),e=>(this._isDisabled=!0,e))]}_acquirePatcher(e){let r=this;return function(...o){let i=Zv.context.active(),s=r.tracer.startSpan("generic-pool.acquire",{},i);return Zv.context.with(Zv.trace.setSpan(i,s),()=>e.call(this,...o).then(a=>(s.end(),a),a=>{throw s.recordException(a),s.end(),a}))}}_poolWrapper(e){let r=this;return function(){let o=e.apply(this,arguments);return r._wrap(o,"acquire",r._acquireWithCallbacksPatcher.bind(r)),o}}_acquireWithCallbacksPatcher(e){let r=this;return function(o,i){if(r._isDisabled)return e.call(this,o,i);let s=Zv.context.active(),a=r.tracer.startSpan("generic-pool.acquire",{},s);return Zv.context.with(Zv.trace.setSpan(s,a),()=>{e.call(this,(c,u)=>{if(a.end(),o)return o(c,u)},i)})}}};zM.GenericPoolInstrumentation=EW});var tUe=S(FM=>{"use strict";Object.defineProperty(FM,"__esModule",{value:!0});FM.GenericPoolInstrumentation=void 0;var H2t=eUe();Object.defineProperty(FM,"GenericPoolInstrumentation",{enumerable:!0,get:function(){return H2t.GenericPoolInstrumentation}})});var TW=S(Dm=>{"use strict";Object.defineProperty(Dm,"__esModule",{value:!0});Dm.DEFAULT_CONFIG=Dm.EndOperation=void 0;var K2t;(function(t){t.AutoAck="auto ack",t.Ack="ack",t.AckAll="ackAll",t.Reject="reject",t.Nack="nack",t.NackAll="nackAll",t.ChannelClosed="channel closed",t.ChannelError="channel error",t.InstrumentationTimeout="instrumentation timeout"})(K2t=Dm.EndOperation||(Dm.EndOperation={}));Dm.DEFAULT_CONFIG={consumeTimeoutMs:1e3*60,useLinksForConsume:!1}});var uUe=S(En=>{"use strict";Object.defineProperty(En,"__esModule",{value:!0});En.isConfirmChannelTracing=En.unmarkConfirmChannelTracing=En.markConfirmChannelTracing=En.getConnectionAttributesFromUrl=En.getConnectionAttributesFromServer=En.normalizeExchange=En.CONNECTION_ATTRIBUTES=En.CHANNEL_CONSUME_TIMEOUT_TIMER=En.CHANNEL_SPANS_NOT_ENDED=En.MESSAGE_STORED_SPAN=void 0;var bW=(pe(),se(Pe)),Ml=(er(),se(Cr));En.MESSAGE_STORED_SPAN=Symbol("opentelemetry.amqplib.message.stored-span");En.CHANNEL_SPANS_NOT_ENDED=Symbol("opentelemetry.amqplib.channel.spans-not-ended");En.CHANNEL_CONSUME_TIMEOUT_TIMER=Symbol("opentelemetry.amqplib.channel.consumer-timeout-timer");En.CONNECTION_ATTRIBUTES=Symbol("opentelemetry.amqplib.connection.attributes");var xW=(0,bW.createContextKey)("opentelemetry.amqplib.channel.is-confirm-channel"),Z2t=t=>t!==""?t:"";En.normalizeExchange=Z2t;var Y2t=t=>t.replace(/:[^:@/]*@/,":***@"),sUe=(t,e)=>t||(e==="AMQP"?5672:5671),aUe=t=>{let e=t||"amqp";return(e.endsWith(":")?e.substring(0,e.length-1):e).toUpperCase()},cUe=t=>t||"localhost",Yv=(t,e,r,n)=>r?{[e]:r}:(bW.diag.error(`amqplib instrumentation: could not extract connection attribute ${n} from user supplied url`,{url:t}),{}),J2t=t=>{let e=t.serverProperties.product?.toLowerCase?.();return e?{[Ml.SEMATTRS_MESSAGING_SYSTEM]:e}:{}};En.getConnectionAttributesFromServer=J2t;var X2t=t=>{let e={[Ml.SEMATTRS_MESSAGING_PROTOCOL_VERSION]:"0.9.1"};if(t=t||"amqp://localhost",typeof t=="object"){let r=t,n=aUe(r?.protocol);Object.assign(e,{...Yv(t,Ml.SEMATTRS_MESSAGING_PROTOCOL,n,"protocol")});let o=cUe(r?.hostname);Object.assign(e,{...Yv(t,Ml.SEMATTRS_NET_PEER_NAME,o,"hostname")});let i=sUe(r.port,n);Object.assign(e,{...Yv(t,Ml.SEMATTRS_NET_PEER_PORT,i,"port")})}else{let r=Y2t(t);e[Ml.SEMATTRS_MESSAGING_URL]=r;try{let n=new URL(r),o=aUe(n.protocol);Object.assign(e,{...Yv(r,Ml.SEMATTRS_MESSAGING_PROTOCOL,o,"protocol")});let i=cUe(n.hostname);Object.assign(e,{...Yv(r,Ml.SEMATTRS_NET_PEER_NAME,i,"hostname")});let s=sUe(n.port?parseInt(n.port):void 0,o);Object.assign(e,{...Yv(r,Ml.SEMATTRS_NET_PEER_PORT,s,"port")})}catch(n){bW.diag.error("amqplib instrumentation: error while extracting connection details from connection url",{censoredUrl:r,err:n})}}return e};En.getConnectionAttributesFromUrl=X2t;var Q2t=t=>t.setValue(xW,!0);En.markConfirmChannelTracing=Q2t;var eFt=t=>t.deleteValue(xW);En.unmarkConfirmChannelTracing=eFt;var tFt=t=>t.getValue(xW)===!0;En.isConfirmChannelTracing=tFt});var lUe=S(Jv=>{"use strict";Object.defineProperty(Jv,"__esModule",{value:!0});Jv.PACKAGE_NAME=Jv.PACKAGE_VERSION=void 0;Jv.PACKAGE_VERSION="0.50.0";Jv.PACKAGE_NAME="@opentelemetry/instrumentation-amqplib"});var dUe=S(GM=>{"use strict";Object.defineProperty(GM,"__esModule",{value:!0});GM.AmqplibInstrumentation=void 0;var pn=(pe(),se(Pe)),qM=_r(),vr=Ft(),os=(er(),se(Cr)),Ci=TW(),Tn=uUe(),pUe=lUe(),BM=[">=0.5.5 <1"],AW=class extends vr.InstrumentationBase{constructor(e={}){super(pUe.PACKAGE_NAME,pUe.PACKAGE_VERSION,{...Ci.DEFAULT_CONFIG,...e})}setConfig(e={}){super.setConfig({...Ci.DEFAULT_CONFIG,...e})}init(){let e=new vr.InstrumentationNodeModuleFile("amqplib/lib/channel_model.js",BM,this.patchChannelModel.bind(this),this.unpatchChannelModel.bind(this)),r=new vr.InstrumentationNodeModuleFile("amqplib/lib/callback_model.js",BM,this.patchChannelModel.bind(this),this.unpatchChannelModel.bind(this)),n=new vr.InstrumentationNodeModuleFile("amqplib/lib/connect.js",BM,this.patchConnect.bind(this),this.unpatchConnect.bind(this));return new vr.InstrumentationNodeModuleDefinition("amqplib",BM,void 0,void 0,[e,n,r])}patchConnect(e){return e=this.unpatchConnect(e),(0,vr.isWrapped)(e.connect)||this._wrap(e,"connect",this.getConnectPatch.bind(this)),e}unpatchConnect(e){return(0,vr.isWrapped)(e.connect)&&this._unwrap(e,"connect"),e}patchChannelModel(e,r){return(0,vr.isWrapped)(e.Channel.prototype.publish)||this._wrap(e.Channel.prototype,"publish",this.getPublishPatch.bind(this,r)),(0,vr.isWrapped)(e.Channel.prototype.consume)||this._wrap(e.Channel.prototype,"consume",this.getConsumePatch.bind(this,r)),(0,vr.isWrapped)(e.Channel.prototype.ack)||this._wrap(e.Channel.prototype,"ack",this.getAckPatch.bind(this,!1,Ci.EndOperation.Ack)),(0,vr.isWrapped)(e.Channel.prototype.nack)||this._wrap(e.Channel.prototype,"nack",this.getAckPatch.bind(this,!0,Ci.EndOperation.Nack)),(0,vr.isWrapped)(e.Channel.prototype.reject)||this._wrap(e.Channel.prototype,"reject",this.getAckPatch.bind(this,!0,Ci.EndOperation.Reject)),(0,vr.isWrapped)(e.Channel.prototype.ackAll)||this._wrap(e.Channel.prototype,"ackAll",this.getAckAllPatch.bind(this,!1,Ci.EndOperation.AckAll)),(0,vr.isWrapped)(e.Channel.prototype.nackAll)||this._wrap(e.Channel.prototype,"nackAll",this.getAckAllPatch.bind(this,!0,Ci.EndOperation.NackAll)),(0,vr.isWrapped)(e.Channel.prototype.emit)||this._wrap(e.Channel.prototype,"emit",this.getChannelEmitPatch.bind(this)),(0,vr.isWrapped)(e.ConfirmChannel.prototype.publish)||this._wrap(e.ConfirmChannel.prototype,"publish",this.getConfirmedPublishPatch.bind(this,r)),e}unpatchChannelModel(e){return(0,vr.isWrapped)(e.Channel.prototype.publish)&&this._unwrap(e.Channel.prototype,"publish"),(0,vr.isWrapped)(e.Channel.prototype.consume)&&this._unwrap(e.Channel.prototype,"consume"),(0,vr.isWrapped)(e.Channel.prototype.ack)&&this._unwrap(e.Channel.prototype,"ack"),(0,vr.isWrapped)(e.Channel.prototype.nack)&&this._unwrap(e.Channel.prototype,"nack"),(0,vr.isWrapped)(e.Channel.prototype.reject)&&this._unwrap(e.Channel.prototype,"reject"),(0,vr.isWrapped)(e.Channel.prototype.ackAll)&&this._unwrap(e.Channel.prototype,"ackAll"),(0,vr.isWrapped)(e.Channel.prototype.nackAll)&&this._unwrap(e.Channel.prototype,"nackAll"),(0,vr.isWrapped)(e.Channel.prototype.emit)&&this._unwrap(e.Channel.prototype,"emit"),(0,vr.isWrapped)(e.ConfirmChannel.prototype.publish)&&this._unwrap(e.ConfirmChannel.prototype,"publish"),e}getConnectPatch(e){return function(n,o,i){return e.call(this,n,o,function(s,a){if(s==null){let c=(0,Tn.getConnectionAttributesFromUrl)(n),u=(0,Tn.getConnectionAttributesFromServer)(a);a[Tn.CONNECTION_ATTRIBUTES]={...c,...u}}i.apply(this,arguments)})}}getChannelEmitPatch(e){let r=this;return function(o){if(o==="close"){r.endAllSpansOnChannel(this,!0,Ci.EndOperation.ChannelClosed,void 0);let i=this[Tn.CHANNEL_CONSUME_TIMEOUT_TIMER];i&&clearInterval(i),this[Tn.CHANNEL_CONSUME_TIMEOUT_TIMER]=void 0}else o==="error"&&r.endAllSpansOnChannel(this,!0,Ci.EndOperation.ChannelError,void 0);return e.apply(this,arguments)}}getAckAllPatch(e,r,n){let o=this;return function(s){return o.endAllSpansOnChannel(this,e,r,s),n.apply(this,arguments)}}getAckPatch(e,r,n){let o=this;return function(s,a,c){let u=this,p=r===Ci.EndOperation.Reject?a:c,f=u[Tn.CHANNEL_SPANS_NOT_ENDED]??[],m=f.findIndex(h=>h.msg===s);if(m<0)o.endConsumerSpan(s,e,r,p);else if(r!==Ci.EndOperation.Reject&&a){for(let h=0;h<=m;h++)o.endConsumerSpan(f[h].msg,e,r,p);f.splice(0,m+1)}else o.endConsumerSpan(s,e,r,p),f.splice(m,1);return n.apply(this,arguments)}}getConsumePatch(e,r){let n=this;return function(i,s,a){let c=this;if(!Object.prototype.hasOwnProperty.call(c,Tn.CHANNEL_SPANS_NOT_ENDED)){let{consumeTimeoutMs:p}=n.getConfig();if(p){let f=setInterval(()=>{n.checkConsumeTimeoutOnChannel(c)},p);f.unref(),c[Tn.CHANNEL_CONSUME_TIMEOUT_TIMER]=f}c[Tn.CHANNEL_SPANS_NOT_ENDED]=[]}let u=function(p){if(!p)return s.call(this,p);let f=p.properties.headers??{},m=pn.propagation.extract(pn.ROOT_CONTEXT,f),h=p.fields?.exchange,_;if(n._config.useLinksForConsume){let w=m?pn.trace.getSpan(m)?.spanContext():void 0;m=void 0,w&&(_=[{context:w}])}let v=n.tracer.startSpan(`${i} process`,{kind:pn.SpanKind.CONSUMER,attributes:{...c?.connection?.[Tn.CONNECTION_ATTRIBUTES],[os.SEMATTRS_MESSAGING_DESTINATION]:h,[os.SEMATTRS_MESSAGING_DESTINATION_KIND]:os.MESSAGINGDESTINATIONKINDVALUES_TOPIC,[os.SEMATTRS_MESSAGING_RABBITMQ_ROUTING_KEY]:p.fields?.routingKey,[os.SEMATTRS_MESSAGING_OPERATION]:os.MESSAGINGOPERATIONVALUES_PROCESS,[os.SEMATTRS_MESSAGING_MESSAGE_ID]:p?.properties.messageId,[os.SEMATTRS_MESSAGING_CONVERSATION_ID]:p?.properties.correlationId},links:_},m),{consumeHook:E}=n.getConfig();E&&(0,vr.safeExecuteInTheMiddle)(()=>E(v,{moduleVersion:e,msg:p}),w=>{w&&pn.diag.error("amqplib instrumentation: consumerHook error",w)},!0),a?.noAck||(c[Tn.CHANNEL_SPANS_NOT_ENDED].push({msg:p,timeOfConsume:(0,qM.hrTime)()}),p[Tn.MESSAGE_STORED_SPAN]=v);let x=m||pn.ROOT_CONTEXT;pn.context.with(pn.trace.setSpan(x,v),()=>{s.call(this,p)}),a?.noAck&&(n.callConsumeEndHook(v,p,!1,Ci.EndOperation.AutoAck),v.end())};return arguments[1]=u,r.apply(this,arguments)}}getConfirmedPublishPatch(e,r){let n=this;return function(i,s,a,c,u){let p=this,{span:f,modifiedOptions:m}=n.createPublishSpan(n,i,s,p,c),{publishHook:h}=n.getConfig();h&&(0,vr.safeExecuteInTheMiddle)(()=>h(f,{moduleVersion:e,exchange:i,routingKey:s,content:a,options:m,isConfirmChannel:!0}),x=>{x&&pn.diag.error("amqplib instrumentation: publishHook error",x)},!0);let _=function(x,w){try{u?.call(this,x,w)}finally{let{publishConfirmHook:I}=n.getConfig();I&&(0,vr.safeExecuteInTheMiddle)(()=>I(f,{moduleVersion:e,exchange:i,routingKey:s,content:a,options:c,isConfirmChannel:!0,confirmError:x}),N=>{N&&pn.diag.error("amqplib instrumentation: publishConfirmHook error",N)},!0),x&&f.setStatus({code:pn.SpanStatusCode.ERROR,message:"message confirmation has been nack'ed"}),f.end()}},v=(0,Tn.markConfirmChannelTracing)(pn.context.active()),E=[...arguments];return E[3]=m,E[4]=pn.context.bind((0,Tn.unmarkConfirmChannelTracing)(pn.trace.setSpan(v,f)),_),pn.context.with(v,r.bind(this,...E))}}getPublishPatch(e,r){let n=this;return function(i,s,a,c){if((0,Tn.isConfirmChannelTracing)(pn.context.active()))return r.apply(this,arguments);{let u=this,{span:p,modifiedOptions:f}=n.createPublishSpan(n,i,s,u,c),{publishHook:m}=n.getConfig();m&&(0,vr.safeExecuteInTheMiddle)(()=>m(p,{moduleVersion:e,exchange:i,routingKey:s,content:a,options:f,isConfirmChannel:!1}),v=>{v&&pn.diag.error("amqplib instrumentation: publishHook error",v)},!0);let h=[...arguments];h[3]=f;let _=r.apply(this,h);return p.end(),_}}}createPublishSpan(e,r,n,o,i){let s=(0,Tn.normalizeExchange)(r),a=e.tracer.startSpan(`publish ${s}`,{kind:pn.SpanKind.PRODUCER,attributes:{...o.connection[Tn.CONNECTION_ATTRIBUTES],[os.SEMATTRS_MESSAGING_DESTINATION]:r,[os.SEMATTRS_MESSAGING_DESTINATION_KIND]:os.MESSAGINGDESTINATIONKINDVALUES_TOPIC,[os.SEMATTRS_MESSAGING_RABBITMQ_ROUTING_KEY]:n,[os.SEMATTRS_MESSAGING_MESSAGE_ID]:i?.messageId,[os.SEMATTRS_MESSAGING_CONVERSATION_ID]:i?.correlationId}}),c=i??{};return c.headers=c.headers??{},pn.propagation.inject(pn.trace.setSpan(pn.context.active(),a),c.headers),{span:a,modifiedOptions:c}}endConsumerSpan(e,r,n,o){let i=e[Tn.MESSAGE_STORED_SPAN];i&&(r!==!1&&i.setStatus({code:pn.SpanStatusCode.ERROR,message:n!==Ci.EndOperation.ChannelClosed&&n!==Ci.EndOperation.ChannelError?`${n} called on message${o===!0?" with requeue":o===!1?" without requeue":""}`:n}),this.callConsumeEndHook(i,e,r,n),i.end(),e[Tn.MESSAGE_STORED_SPAN]=void 0)}endAllSpansOnChannel(e,r,n,o){(e[Tn.CHANNEL_SPANS_NOT_ENDED]??[]).forEach(s=>{this.endConsumerSpan(s.msg,r,n,o)}),e[Tn.CHANNEL_SPANS_NOT_ENDED]=[]}callConsumeEndHook(e,r,n,o){let{consumeEndHook:i}=this.getConfig();i&&(0,vr.safeExecuteInTheMiddle)(()=>i(e,{msg:r,rejected:n,endOperation:o}),s=>{s&&pn.diag.error("amqplib instrumentation: consumerEndHook error",s)},!0)}checkConsumeTimeoutOnChannel(e){let r=(0,qM.hrTime)(),n=e[Tn.CHANNEL_SPANS_NOT_ENDED]??[],o,{consumeTimeoutMs:i}=this.getConfig();for(o=0;o{"use strict";Object.defineProperty(jd,"__esModule",{value:!0});jd.EndOperation=jd.DEFAULT_CONFIG=jd.AmqplibInstrumentation=void 0;var rFt=dUe();Object.defineProperty(jd,"AmqplibInstrumentation",{enumerable:!0,get:function(){return rFt.AmqplibInstrumentation}});var fUe=TW();Object.defineProperty(jd,"DEFAULT_CONFIG",{enumerable:!0,get:function(){return fUe.DEFAULT_CONFIG}});Object.defineProperty(jd,"EndOperation",{enumerable:!0,get:function(){return fUe.EndOperation}})});var GUe=S((Qv,O0)=>{(function(){var t,e="4.17.21",r=200,n="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",o="Expected a function",i="Invalid `variable` option passed into `_.template`",s="__lodash_hash_undefined__",a=500,c="__lodash_placeholder__",u=1,p=2,f=4,m=1,h=2,_=1,v=2,E=4,x=8,w=16,I=32,N=64,$=128,B=256,G=512,he=30,Q="...",me=800,J=16,Oe=1,pt=2,Ke=3,it=1/0,Mr=9007199254740991,Dr=17976931348623157e292,Sr=NaN,Ar=4294967295,ui=Ar-1,ku=Ar>>>1,Ms=[["ary",$],["bind",_],["bindKey",v],["curry",x],["curryRight",w],["flip",G],["partial",I],["partialRight",N],["rearg",B]],sn="[object Arguments]",Mu="[object Array]",Nc="[object AsyncFunction]",Ds="[object Boolean]",li="[object Date]",Qm="[object DOMException]",Du="[object Error]",Cc="[object Function]",LS="[object GeneratorFunction]",Ua="[object Map]",US="[object Number]",yGe="[object Null]",Lu="[object Object]",kY="[object Promise]",EGe="[object Proxy]",jS="[object RegExp]",ja="[object Set]",zS="[object String]",gA="[object Symbol]",TGe="[object Undefined]",FS="[object WeakMap]",bGe="[object WeakSet]",qS="[object ArrayBuffer]",eh="[object DataView]",RL="[object Float32Array]",PL="[object Float64Array]",IL="[object Int8Array]",OL="[object Int16Array]",NL="[object Int32Array]",CL="[object Uint8Array]",$L="[object Uint8ClampedArray]",kL="[object Uint16Array]",ML="[object Uint32Array]",xGe=/\b__p \+= '';/g,AGe=/\b(__p \+=) '' \+/g,wGe=/(__e\(.*?\)|\b__t\)) \+\n'';/g,MY=/&(?:amp|lt|gt|quot|#39);/g,DY=/[&<>"']/g,RGe=RegExp(MY.source),PGe=RegExp(DY.source),IGe=/<%-([\s\S]+?)%>/g,OGe=/<%([\s\S]+?)%>/g,LY=/<%=([\s\S]+?)%>/g,NGe=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,CGe=/^\w*$/,$Ge=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,DL=/[\\^$.*+?()[\]{}|]/g,kGe=RegExp(DL.source),LL=/^\s+/,MGe=/\s/,DGe=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,LGe=/\{\n\/\* \[wrapped with (.+)\] \*/,UGe=/,? & /,jGe=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,zGe=/[()=,{}\[\]\/\s]/,FGe=/\\(\\)?/g,qGe=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,UY=/\w*$/,BGe=/^[-+]0x[0-9a-f]+$/i,GGe=/^0b[01]+$/i,VGe=/^\[object .+?Constructor\]$/,HGe=/^0o[0-7]+$/i,WGe=/^(?:0|[1-9]\d*)$/,KGe=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,_A=/($^)/,ZGe=/['\n\r\u2028\u2029\\]/g,vA="\\ud800-\\udfff",YGe="\\u0300-\\u036f",JGe="\\ufe20-\\ufe2f",XGe="\\u20d0-\\u20ff",jY=YGe+JGe+XGe,zY="\\u2700-\\u27bf",FY="a-z\\xdf-\\xf6\\xf8-\\xff",QGe="\\xac\\xb1\\xd7\\xf7",e5e="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",t5e="\\u2000-\\u206f",r5e=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",qY="A-Z\\xc0-\\xd6\\xd8-\\xde",BY="\\ufe0e\\ufe0f",GY=QGe+e5e+t5e+r5e,UL="['\u2019]",n5e="["+vA+"]",VY="["+GY+"]",SA="["+jY+"]",HY="\\d+",o5e="["+zY+"]",WY="["+FY+"]",KY="[^"+vA+GY+HY+zY+FY+qY+"]",jL="\\ud83c[\\udffb-\\udfff]",i5e="(?:"+SA+"|"+jL+")",ZY="[^"+vA+"]",zL="(?:\\ud83c[\\udde6-\\uddff]){2}",FL="[\\ud800-\\udbff][\\udc00-\\udfff]",th="["+qY+"]",YY="\\u200d",JY="(?:"+WY+"|"+KY+")",s5e="(?:"+th+"|"+KY+")",XY="(?:"+UL+"(?:d|ll|m|re|s|t|ve))?",QY="(?:"+UL+"(?:D|LL|M|RE|S|T|VE))?",e7=i5e+"?",t7="["+BY+"]?",a5e="(?:"+YY+"(?:"+[ZY,zL,FL].join("|")+")"+t7+e7+")*",c5e="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",u5e="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",r7=t7+e7+a5e,l5e="(?:"+[o5e,zL,FL].join("|")+")"+r7,p5e="(?:"+[ZY+SA+"?",SA,zL,FL,n5e].join("|")+")",d5e=RegExp(UL,"g"),f5e=RegExp(SA,"g"),qL=RegExp(jL+"(?="+jL+")|"+p5e+r7,"g"),m5e=RegExp([th+"?"+WY+"+"+XY+"(?="+[VY,th,"$"].join("|")+")",s5e+"+"+QY+"(?="+[VY,th+JY,"$"].join("|")+")",th+"?"+JY+"+"+XY,th+"+"+QY,u5e,c5e,HY,l5e].join("|"),"g"),h5e=RegExp("["+YY+vA+jY+BY+"]"),g5e=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,_5e=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],v5e=-1,Hr={};Hr[RL]=Hr[PL]=Hr[IL]=Hr[OL]=Hr[NL]=Hr[CL]=Hr[$L]=Hr[kL]=Hr[ML]=!0,Hr[sn]=Hr[Mu]=Hr[qS]=Hr[Ds]=Hr[eh]=Hr[li]=Hr[Du]=Hr[Cc]=Hr[Ua]=Hr[US]=Hr[Lu]=Hr[jS]=Hr[ja]=Hr[zS]=Hr[FS]=!1;var Fr={};Fr[sn]=Fr[Mu]=Fr[qS]=Fr[eh]=Fr[Ds]=Fr[li]=Fr[RL]=Fr[PL]=Fr[IL]=Fr[OL]=Fr[NL]=Fr[Ua]=Fr[US]=Fr[Lu]=Fr[jS]=Fr[ja]=Fr[zS]=Fr[gA]=Fr[CL]=Fr[$L]=Fr[kL]=Fr[ML]=!0,Fr[Du]=Fr[Cc]=Fr[FS]=!1;var S5e={\u00C0:"A",\u00C1:"A",\u00C2:"A",\u00C3:"A",\u00C4:"A",\u00C5:"A",\u00E0:"a",\u00E1:"a",\u00E2:"a",\u00E3:"a",\u00E4:"a",\u00E5:"a",\u00C7:"C",\u00E7:"c",\u00D0:"D",\u00F0:"d",\u00C8:"E",\u00C9:"E",\u00CA:"E",\u00CB:"E",\u00E8:"e",\u00E9:"e",\u00EA:"e",\u00EB:"e",\u00CC:"I",\u00CD:"I",\u00CE:"I",\u00CF:"I",\u00EC:"i",\u00ED:"i",\u00EE:"i",\u00EF:"i",\u00D1:"N",\u00F1:"n",\u00D2:"O",\u00D3:"O",\u00D4:"O",\u00D5:"O",\u00D6:"O",\u00D8:"O",\u00F2:"o",\u00F3:"o",\u00F4:"o",\u00F5:"o",\u00F6:"o",\u00F8:"o",\u00D9:"U",\u00DA:"U",\u00DB:"U",\u00DC:"U",\u00F9:"u",\u00FA:"u",\u00FB:"u",\u00FC:"u",\u00DD:"Y",\u00FD:"y",\u00FF:"y",\u00C6:"Ae",\u00E6:"ae",\u00DE:"Th",\u00FE:"th",\u00DF:"ss",\u0100:"A",\u0102:"A",\u0104:"A",\u0101:"a",\u0103:"a",\u0105:"a",\u0106:"C",\u0108:"C",\u010A:"C",\u010C:"C",\u0107:"c",\u0109:"c",\u010B:"c",\u010D:"c",\u010E:"D",\u0110:"D",\u010F:"d",\u0111:"d",\u0112:"E",\u0114:"E",\u0116:"E",\u0118:"E",\u011A:"E",\u0113:"e",\u0115:"e",\u0117:"e",\u0119:"e",\u011B:"e",\u011C:"G",\u011E:"G",\u0120:"G",\u0122:"G",\u011D:"g",\u011F:"g",\u0121:"g",\u0123:"g",\u0124:"H",\u0126:"H",\u0125:"h",\u0127:"h",\u0128:"I",\u012A:"I",\u012C:"I",\u012E:"I",\u0130:"I",\u0129:"i",\u012B:"i",\u012D:"i",\u012F:"i",\u0131:"i",\u0134:"J",\u0135:"j",\u0136:"K",\u0137:"k",\u0138:"k",\u0139:"L",\u013B:"L",\u013D:"L",\u013F:"L",\u0141:"L",\u013A:"l",\u013C:"l",\u013E:"l",\u0140:"l",\u0142:"l",\u0143:"N",\u0145:"N",\u0147:"N",\u014A:"N",\u0144:"n",\u0146:"n",\u0148:"n",\u014B:"n",\u014C:"O",\u014E:"O",\u0150:"O",\u014D:"o",\u014F:"o",\u0151:"o",\u0154:"R",\u0156:"R",\u0158:"R",\u0155:"r",\u0157:"r",\u0159:"r",\u015A:"S",\u015C:"S",\u015E:"S",\u0160:"S",\u015B:"s",\u015D:"s",\u015F:"s",\u0161:"s",\u0162:"T",\u0164:"T",\u0166:"T",\u0163:"t",\u0165:"t",\u0167:"t",\u0168:"U",\u016A:"U",\u016C:"U",\u016E:"U",\u0170:"U",\u0172:"U",\u0169:"u",\u016B:"u",\u016D:"u",\u016F:"u",\u0171:"u",\u0173:"u",\u0174:"W",\u0175:"w",\u0176:"Y",\u0177:"y",\u0178:"Y",\u0179:"Z",\u017B:"Z",\u017D:"Z",\u017A:"z",\u017C:"z",\u017E:"z",\u0132:"IJ",\u0133:"ij",\u0152:"Oe",\u0153:"oe",\u0149:"'n",\u017F:"s"},y5e={"&":"&","<":"<",">":">",'"':""","'":"'"},E5e={"&":"&","<":"<",">":">",""":'"',"'":"'"},T5e={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},b5e=parseFloat,x5e=parseInt,n7=typeof global=="object"&&global&&global.Object===Object&&global,A5e=typeof self=="object"&&self&&self.Object===Object&&self,ro=n7||A5e||Function("return this")(),BL=typeof Qv=="object"&&Qv&&!Qv.nodeType&&Qv,Yd=BL&&typeof O0=="object"&&O0&&!O0.nodeType&&O0,o7=Yd&&Yd.exports===BL,GL=o7&&n7.process,Ls=(function(){try{var L=Yd&&Yd.require&&Yd.require("util").types;return L||GL&&GL.binding&&GL.binding("util")}catch{}})(),i7=Ls&&Ls.isArrayBuffer,s7=Ls&&Ls.isDate,a7=Ls&&Ls.isMap,c7=Ls&&Ls.isRegExp,u7=Ls&&Ls.isSet,l7=Ls&&Ls.isTypedArray;function ss(L,V,F){switch(F.length){case 0:return L.call(V);case 1:return L.call(V,F[0]);case 2:return L.call(V,F[0],F[1]);case 3:return L.call(V,F[0],F[1],F[2])}return L.apply(V,F)}function w5e(L,V,F,we){for(var dt=-1,mr=L==null?0:L.length;++dt-1}function VL(L,V,F){for(var we=-1,dt=L==null?0:L.length;++we-1;);return F}function v7(L,V){for(var F=L.length;F--&&rh(V,L[F],0)>-1;);return F}function M5e(L,V){for(var F=L.length,we=0;F--;)L[F]===V&&++we;return we}var D5e=ZL(S5e),L5e=ZL(y5e);function U5e(L){return"\\"+T5e[L]}function j5e(L,V){return L==null?t:L[V]}function nh(L){return h5e.test(L)}function z5e(L){return g5e.test(L)}function F5e(L){for(var V,F=[];!(V=L.next()).done;)F.push(V.value);return F}function QL(L){var V=-1,F=Array(L.size);return L.forEach(function(we,dt){F[++V]=[dt,we]}),F}function S7(L,V){return function(F){return L(V(F))}}function Wl(L,V){for(var F=-1,we=L.length,dt=0,mr=[];++F-1}function P8e(l,d){var g=this.__data__,y=LA(g,l);return y<0?(++this.size,g.push([l,d])):g[y][1]=d,this}Uu.prototype.clear=x8e,Uu.prototype.delete=A8e,Uu.prototype.get=w8e,Uu.prototype.has=R8e,Uu.prototype.set=P8e;function ju(l){var d=-1,g=l==null?0:l.length;for(this.clear();++d=d?l:d)),l}function Fs(l,d,g,y,b,P){var C,M=d&u,U=d&p,K=d&f;if(g&&(C=b?g(l,y,b,P):g(l)),C!==t)return C;if(!an(l))return l;var Z=ht(l);if(Z){if(C=CVe(l),!M)return ki(l,C)}else{var X=qo(l),_e=X==Cc||X==LS;if(ep(l))return tJ(l,M);if(X==Lu||X==sn||_e&&!b){if(C=U||_e?{}:yJ(l),!M)return U?EVe(l,G8e(C,l)):yVe(l,N7(C,l))}else{if(!Fr[X])return b?l:{};C=$Ve(l,X,M)}}P||(P=new Fa);var Me=P.get(l);if(Me)return Me;P.set(l,C),ZJ(l)?l.forEach(function(et){C.add(Fs(et,d,g,et,l,P))}):WJ(l)&&l.forEach(function(et,qt){C.set(qt,Fs(et,d,g,qt,l,P))});var Qe=K?U?AU:xU:U?Di:no,wt=Z?t:Qe(l);return Us(wt||l,function(et,qt){wt&&(qt=et,et=l[qt]),ZS(C,qt,Fs(et,d,g,qt,l,P))}),C}function V8e(l){var d=no(l);return function(g){return C7(g,l,d)}}function C7(l,d,g){var y=g.length;if(l==null)return!y;for(l=Lr(l);y--;){var b=g[y],P=d[b],C=l[b];if(C===t&&!(b in l)||!P(C))return!1}return!0}function $7(l,d,g){if(typeof l!="function")throw new js(o);return ry(function(){l.apply(t,g)},d)}function YS(l,d,g,y){var b=-1,P=yA,C=!0,M=l.length,U=[],K=d.length;if(!M)return U;g&&(d=en(d,as(g))),y?(P=VL,C=!1):d.length>=r&&(P=BS,C=!1,d=new Qd(d));e:for(;++bb?0:b+g),y=y===t||y>b?b:Et(y),y<0&&(y+=b),y=g>y?0:JJ(y);g0&&g(M)?d>1?Eo(M,d-1,g,y,b):Hl(b,M):y||(b[b.length]=M)}return b}var sU=aJ(),D7=aJ(!0);function $c(l,d){return l&&sU(l,d,no)}function aU(l,d){return l&&D7(l,d,no)}function jA(l,d){return Vl(d,function(g){return Gu(l[g])})}function tf(l,d){d=Xl(d,l);for(var g=0,y=d.length;l!=null&&gd}function K8e(l,d){return l!=null&&yr.call(l,d)}function Z8e(l,d){return l!=null&&d in Lr(l)}function Y8e(l,d,g){return l>=Fo(d,g)&&l=120&&Z.length>=120)?new Qd(C&&Z):t}Z=l[0];var X=-1,_e=M[0];e:for(;++X-1;)M!==l&&OA.call(M,U,1),OA.call(l,U,1);return l}function W7(l,d){for(var g=l?d.length:0,y=g-1;g--;){var b=d[g];if(g==y||b!==P){var P=b;Bu(b)?OA.call(l,b,1):_U(l,b)}}return l}function mU(l,d){return l+$A(R7()*(d-l+1))}function uVe(l,d,g,y){for(var b=-1,P=Hn(CA((d-l)/(g||1)),0),C=F(P);P--;)C[y?P:++b]=l,l+=g;return C}function hU(l,d){var g="";if(!l||d<1||d>Mr)return g;do d%2&&(g+=l),d=$A(d/2),d&&(l+=l);while(d);return g}function Ot(l,d){return CU(bJ(l,d,Li),l+"")}function lVe(l){return O7(fh(l))}function pVe(l,d){var g=fh(l);return YA(g,ef(d,0,g.length))}function QS(l,d,g,y){if(!an(l))return l;d=Xl(d,l);for(var b=-1,P=d.length,C=P-1,M=l;M!=null&&++bb?0:b+d),g=g>b?b:g,g<0&&(g+=b),b=d>g?0:g-d>>>0,d>>>=0;for(var P=F(b);++y>>1,C=l[P];C!==null&&!us(C)&&(g?C<=d:C=r){var K=d?null:AVe(l);if(K)return TA(K);C=!1,b=BS,U=new Qd}else U=d?[]:M;e:for(;++y=y?l:qs(l,d,g)}var eJ=r8e||function(l){return ro.clearTimeout(l)};function tJ(l,d){if(d)return l.slice();var g=l.length,y=T7?T7(g):new l.constructor(g);return l.copy(y),y}function EU(l){var d=new l.constructor(l.byteLength);return new PA(d).set(new PA(l)),d}function gVe(l,d){var g=d?EU(l.buffer):l.buffer;return new l.constructor(g,l.byteOffset,l.byteLength)}function _Ve(l){var d=new l.constructor(l.source,UY.exec(l));return d.lastIndex=l.lastIndex,d}function vVe(l){return KS?Lr(KS.call(l)):{}}function rJ(l,d){var g=d?EU(l.buffer):l.buffer;return new l.constructor(g,l.byteOffset,l.length)}function nJ(l,d){if(l!==d){var g=l!==t,y=l===null,b=l===l,P=us(l),C=d!==t,M=d===null,U=d===d,K=us(d);if(!M&&!K&&!P&&l>d||P&&C&&U&&!M&&!K||y&&C&&U||!g&&U||!b)return 1;if(!y&&!P&&!K&&l=M)return U;var K=g[y];return U*(K=="desc"?-1:1)}}return l.index-d.index}function oJ(l,d,g,y){for(var b=-1,P=l.length,C=g.length,M=-1,U=d.length,K=Hn(P-C,0),Z=F(U+K),X=!y;++M1?g[b-1]:t,C=b>2?g[2]:t;for(P=l.length>3&&typeof P=="function"?(b--,P):t,C&&di(g[0],g[1],C)&&(P=b<3?t:P,b=1),d=Lr(d);++y-1?b[P?d[C]:C]:t}}function lJ(l){return qu(function(d){var g=d.length,y=g,b=zs.prototype.thru;for(l&&d.reverse();y--;){var P=d[y];if(typeof P!="function")throw new js(o);if(b&&!C&&KA(P)=="wrapper")var C=new zs([],!0)}for(y=C?y:g;++y1&&Yt.reverse(),Z&&UM))return!1;var K=P.get(l),Z=P.get(d);if(K&&Z)return K==d&&Z==l;var X=-1,_e=!0,Me=g&h?new Qd:t;for(P.set(l,d),P.set(d,l);++X1?"& ":"")+d[y],d=d.join(g>2?", ":" "),l.replace(DGe,`{ -/* [wrapped with `+d+`] */ -`)}function MVe(l){return ht(l)||of(l)||!!(A7&&l&&l[A7])}function Bu(l,d){var g=typeof l;return d=d??Mr,!!d&&(g=="number"||g!="symbol"&&WGe.test(l))&&l>-1&&l%1==0&&l0){if(++d>=me)return arguments[0]}else d=0;return l.apply(t,arguments)}}function YA(l,d){var g=-1,y=l.length,b=y-1;for(d=d===t?y:d;++g1?l[d-1]:t;return g=typeof g=="function"?(l.pop(),g):t,MJ(l,g)});function DJ(l){var d=R(l);return d.__chain__=!0,d}function HHe(l,d){return d(l),l}function JA(l,d){return d(l)}var WHe=qu(function(l){var d=l.length,g=d?l[0]:0,y=this.__wrapped__,b=function(P){return iU(P,l)};return d>1||this.__actions__.length||!(y instanceof Vt)||!Bu(g)?this.thru(b):(y=y.slice(g,+g+(d?1:0)),y.__actions__.push({func:JA,args:[b],thisArg:t}),new zs(y,this.__chain__).thru(function(P){return d&&!P.length&&P.push(t),P}))});function KHe(){return DJ(this)}function ZHe(){return new zs(this.value(),this.__chain__)}function YHe(){this.__values__===t&&(this.__values__=YJ(this.value()));var l=this.__index__>=this.__values__.length,d=l?t:this.__values__[this.__index__++];return{done:l,value:d}}function JHe(){return this}function XHe(l){for(var d,g=this;g instanceof DA;){var y=IJ(g);y.__index__=0,y.__values__=t,d?b.__wrapped__=y:d=y;var b=y;g=g.__wrapped__}return b.__wrapped__=l,d}function QHe(){var l=this.__wrapped__;if(l instanceof Vt){var d=l;return this.__actions__.length&&(d=new Vt(this)),d=d.reverse(),d.__actions__.push({func:JA,args:[$U],thisArg:t}),new zs(d,this.__chain__)}return this.thru($U)}function e3e(){return X7(this.__wrapped__,this.__actions__)}var t3e=BA(function(l,d,g){yr.call(l,g)?++l[g]:zu(l,g,1)});function r3e(l,d,g){var y=ht(l)?p7:H8e;return g&&di(l,d,g)&&(d=t),y(l,Xe(d,3))}function n3e(l,d){var g=ht(l)?Vl:M7;return g(l,Xe(d,3))}var o3e=uJ(OJ),i3e=uJ(NJ);function s3e(l,d){return Eo(XA(l,d),1)}function a3e(l,d){return Eo(XA(l,d),it)}function c3e(l,d,g){return g=g===t?1:Et(g),Eo(XA(l,d),g)}function LJ(l,d){var g=ht(l)?Us:Yl;return g(l,Xe(d,3))}function UJ(l,d){var g=ht(l)?R5e:k7;return g(l,Xe(d,3))}var u3e=BA(function(l,d,g){yr.call(l,g)?l[g].push(d):zu(l,g,[d])});function l3e(l,d,g,y){l=Mi(l)?l:fh(l),g=g&&!y?Et(g):0;var b=l.length;return g<0&&(g=Hn(b+g,0)),nw(l)?g<=b&&l.indexOf(d,g)>-1:!!b&&rh(l,d,g)>-1}var p3e=Ot(function(l,d,g){var y=-1,b=typeof d=="function",P=Mi(l)?F(l.length):[];return Yl(l,function(C){P[++y]=b?ss(d,C,g):JS(C,d,g)}),P}),d3e=BA(function(l,d,g){zu(l,g,d)});function XA(l,d){var g=ht(l)?en:F7;return g(l,Xe(d,3))}function f3e(l,d,g,y){return l==null?[]:(ht(d)||(d=d==null?[]:[d]),g=y?t:g,ht(g)||(g=g==null?[]:[g]),V7(l,d,g))}var m3e=BA(function(l,d,g){l[g?0:1].push(d)},function(){return[[],[]]});function h3e(l,d,g){var y=ht(l)?HL:h7,b=arguments.length<3;return y(l,Xe(d,4),g,b,Yl)}function g3e(l,d,g){var y=ht(l)?P5e:h7,b=arguments.length<3;return y(l,Xe(d,4),g,b,k7)}function _3e(l,d){var g=ht(l)?Vl:M7;return g(l,tw(Xe(d,3)))}function v3e(l){var d=ht(l)?O7:lVe;return d(l)}function S3e(l,d,g){(g?di(l,d,g):d===t)?d=1:d=Et(d);var y=ht(l)?F8e:pVe;return y(l,d)}function y3e(l){var d=ht(l)?q8e:fVe;return d(l)}function E3e(l){if(l==null)return 0;if(Mi(l))return nw(l)?oh(l):l.length;var d=qo(l);return d==Ua||d==ja?l.size:pU(l).length}function T3e(l,d,g){var y=ht(l)?WL:mVe;return g&&di(l,d,g)&&(d=t),y(l,Xe(d,3))}var b3e=Ot(function(l,d){if(l==null)return[];var g=d.length;return g>1&&di(l,d[0],d[1])?d=[]:g>2&&di(d[0],d[1],d[2])&&(d=[d[0]]),V7(l,Eo(d,1),[])}),QA=n8e||function(){return ro.Date.now()};function x3e(l,d){if(typeof d!="function")throw new js(o);return l=Et(l),function(){if(--l<1)return d.apply(this,arguments)}}function jJ(l,d,g){return d=g?t:d,d=l&&d==null?l.length:d,Fu(l,$,t,t,t,t,d)}function zJ(l,d){var g;if(typeof d!="function")throw new js(o);return l=Et(l),function(){return--l>0&&(g=d.apply(this,arguments)),l<=1&&(d=t),g}}var MU=Ot(function(l,d,g){var y=_;if(g.length){var b=Wl(g,ph(MU));y|=I}return Fu(l,y,d,g,b)}),FJ=Ot(function(l,d,g){var y=_|v;if(g.length){var b=Wl(g,ph(FJ));y|=I}return Fu(d,y,l,g,b)});function qJ(l,d,g){d=g?t:d;var y=Fu(l,x,t,t,t,t,t,d);return y.placeholder=qJ.placeholder,y}function BJ(l,d,g){d=g?t:d;var y=Fu(l,w,t,t,t,t,t,d);return y.placeholder=BJ.placeholder,y}function GJ(l,d,g){var y,b,P,C,M,U,K=0,Z=!1,X=!1,_e=!0;if(typeof l!="function")throw new js(o);d=Gs(d)||0,an(g)&&(Z=!!g.leading,X="maxWait"in g,P=X?Hn(Gs(g.maxWait)||0,d):P,_e="trailing"in g?!!g.trailing:_e);function Me(An){var Ba=y,Hu=b;return y=b=t,K=An,C=l.apply(Hu,Ba),C}function Qe(An){return K=An,M=ry(qt,d),Z?Me(An):C}function wt(An){var Ba=An-U,Hu=An-K,uX=d-Ba;return X?Fo(uX,P-Hu):uX}function et(An){var Ba=An-U,Hu=An-K;return U===t||Ba>=d||Ba<0||X&&Hu>=P}function qt(){var An=QA();if(et(An))return Yt(An);M=ry(qt,wt(An))}function Yt(An){return M=t,_e&&y?Me(An):(y=b=t,C)}function ls(){M!==t&&eJ(M),K=0,y=U=b=M=t}function fi(){return M===t?C:Yt(QA())}function ps(){var An=QA(),Ba=et(An);if(y=arguments,b=this,U=An,Ba){if(M===t)return Qe(U);if(X)return eJ(M),M=ry(qt,d),Me(U)}return M===t&&(M=ry(qt,d)),C}return ps.cancel=ls,ps.flush=fi,ps}var A3e=Ot(function(l,d){return $7(l,1,d)}),w3e=Ot(function(l,d,g){return $7(l,Gs(d)||0,g)});function R3e(l){return Fu(l,G)}function ew(l,d){if(typeof l!="function"||d!=null&&typeof d!="function")throw new js(o);var g=function(){var y=arguments,b=d?d.apply(this,y):y[0],P=g.cache;if(P.has(b))return P.get(b);var C=l.apply(this,y);return g.cache=P.set(b,C)||P,C};return g.cache=new(ew.Cache||ju),g}ew.Cache=ju;function tw(l){if(typeof l!="function")throw new js(o);return function(){var d=arguments;switch(d.length){case 0:return!l.call(this);case 1:return!l.call(this,d[0]);case 2:return!l.call(this,d[0],d[1]);case 3:return!l.call(this,d[0],d[1],d[2])}return!l.apply(this,d)}}function P3e(l){return zJ(2,l)}var I3e=hVe(function(l,d){d=d.length==1&&ht(d[0])?en(d[0],as(Xe())):en(Eo(d,1),as(Xe()));var g=d.length;return Ot(function(y){for(var b=-1,P=Fo(y.length,g);++b=d}),of=U7((function(){return arguments})())?U7:function(l){return fn(l)&&yr.call(l,"callee")&&!x7.call(l,"callee")},ht=F.isArray,G3e=i7?as(i7):X8e;function Mi(l){return l!=null&&rw(l.length)&&!Gu(l)}function xn(l){return fn(l)&&Mi(l)}function V3e(l){return l===!0||l===!1||fn(l)&&pi(l)==Ds}var ep=i8e||WU,H3e=s7?as(s7):Q8e;function W3e(l){return fn(l)&&l.nodeType===1&&!ny(l)}function K3e(l){if(l==null)return!0;if(Mi(l)&&(ht(l)||typeof l=="string"||typeof l.splice=="function"||ep(l)||dh(l)||of(l)))return!l.length;var d=qo(l);if(d==Ua||d==ja)return!l.size;if(ty(l))return!pU(l).length;for(var g in l)if(yr.call(l,g))return!1;return!0}function Z3e(l,d){return XS(l,d)}function Y3e(l,d,g){g=typeof g=="function"?g:t;var y=g?g(l,d):t;return y===t?XS(l,d,t,g):!!y}function LU(l){if(!fn(l))return!1;var d=pi(l);return d==Du||d==Qm||typeof l.message=="string"&&typeof l.name=="string"&&!ny(l)}function J3e(l){return typeof l=="number"&&w7(l)}function Gu(l){if(!an(l))return!1;var d=pi(l);return d==Cc||d==LS||d==Nc||d==EGe}function HJ(l){return typeof l=="number"&&l==Et(l)}function rw(l){return typeof l=="number"&&l>-1&&l%1==0&&l<=Mr}function an(l){var d=typeof l;return l!=null&&(d=="object"||d=="function")}function fn(l){return l!=null&&typeof l=="object"}var WJ=a7?as(a7):tVe;function X3e(l,d){return l===d||lU(l,d,RU(d))}function Q3e(l,d,g){return g=typeof g=="function"?g:t,lU(l,d,RU(d),g)}function e9e(l){return KJ(l)&&l!=+l}function t9e(l){if(UVe(l))throw new dt(n);return j7(l)}function r9e(l){return l===null}function n9e(l){return l==null}function KJ(l){return typeof l=="number"||fn(l)&&pi(l)==US}function ny(l){if(!fn(l)||pi(l)!=Lu)return!1;var d=IA(l);if(d===null)return!0;var g=yr.call(d,"constructor")&&d.constructor;return typeof g=="function"&&g instanceof g&&AA.call(g)==Q5e}var UU=c7?as(c7):rVe;function o9e(l){return HJ(l)&&l>=-Mr&&l<=Mr}var ZJ=u7?as(u7):nVe;function nw(l){return typeof l=="string"||!ht(l)&&fn(l)&&pi(l)==zS}function us(l){return typeof l=="symbol"||fn(l)&&pi(l)==gA}var dh=l7?as(l7):oVe;function i9e(l){return l===t}function s9e(l){return fn(l)&&qo(l)==FS}function a9e(l){return fn(l)&&pi(l)==bGe}var c9e=WA(dU),u9e=WA(function(l,d){return l<=d});function YJ(l){if(!l)return[];if(Mi(l))return nw(l)?za(l):ki(l);if(GS&&l[GS])return F5e(l[GS]());var d=qo(l),g=d==Ua?QL:d==ja?TA:fh;return g(l)}function Vu(l){if(!l)return l===0?l:0;if(l=Gs(l),l===it||l===-it){var d=l<0?-1:1;return d*Dr}return l===l?l:0}function Et(l){var d=Vu(l),g=d%1;return d===d?g?d-g:d:0}function JJ(l){return l?ef(Et(l),0,Ar):0}function Gs(l){if(typeof l=="number")return l;if(us(l))return Sr;if(an(l)){var d=typeof l.valueOf=="function"?l.valueOf():l;l=an(d)?d+"":d}if(typeof l!="string")return l===0?l:+l;l=g7(l);var g=GGe.test(l);return g||HGe.test(l)?x5e(l.slice(2),g?2:8):BGe.test(l)?Sr:+l}function XJ(l){return kc(l,Di(l))}function l9e(l){return l?ef(Et(l),-Mr,Mr):l===0?l:0}function gr(l){return l==null?"":cs(l)}var p9e=uh(function(l,d){if(ty(d)||Mi(d)){kc(d,no(d),l);return}for(var g in d)yr.call(d,g)&&ZS(l,g,d[g])}),QJ=uh(function(l,d){kc(d,Di(d),l)}),ow=uh(function(l,d,g,y){kc(d,Di(d),l,y)}),d9e=uh(function(l,d,g,y){kc(d,no(d),l,y)}),f9e=qu(iU);function m9e(l,d){var g=ch(l);return d==null?g:N7(g,d)}var h9e=Ot(function(l,d){l=Lr(l);var g=-1,y=d.length,b=y>2?d[2]:t;for(b&&di(d[0],d[1],b)&&(y=1);++g1),P}),kc(l,AU(l),g),y&&(g=Fs(g,u|p|f,wVe));for(var b=d.length;b--;)_U(g,d[b]);return g});function $9e(l,d){return tX(l,tw(Xe(d)))}var k9e=qu(function(l,d){return l==null?{}:aVe(l,d)});function tX(l,d){if(l==null)return{};var g=en(AU(l),function(y){return[y]});return d=Xe(d),H7(l,g,function(y,b){return d(y,b[0])})}function M9e(l,d,g){d=Xl(d,l);var y=-1,b=d.length;for(b||(b=1,l=t);++yd){var y=l;l=d,d=y}if(g||l%1||d%1){var b=R7();return Fo(l+b*(d-l+b5e("1e-"+((b+"").length-1))),d)}return mU(l,d)}var H9e=lh(function(l,d,g){return d=d.toLowerCase(),l+(g?oX(d):d)});function oX(l){return FU(gr(l).toLowerCase())}function iX(l){return l=gr(l),l&&l.replace(KGe,D5e).replace(f5e,"")}function W9e(l,d,g){l=gr(l),d=cs(d);var y=l.length;g=g===t?y:ef(Et(g),0,y);var b=g;return g-=d.length,g>=0&&l.slice(g,b)==d}function K9e(l){return l=gr(l),l&&PGe.test(l)?l.replace(DY,L5e):l}function Z9e(l){return l=gr(l),l&&kGe.test(l)?l.replace(DL,"\\$&"):l}var Y9e=lh(function(l,d,g){return l+(g?"-":"")+d.toLowerCase()}),J9e=lh(function(l,d,g){return l+(g?" ":"")+d.toLowerCase()}),X9e=cJ("toLowerCase");function Q9e(l,d,g){l=gr(l),d=Et(d);var y=d?oh(l):0;if(!d||y>=d)return l;var b=(d-y)/2;return HA($A(b),g)+l+HA(CA(b),g)}function eWe(l,d,g){l=gr(l),d=Et(d);var y=d?oh(l):0;return d&&y>>0,g?(l=gr(l),l&&(typeof d=="string"||d!=null&&!UU(d))&&(d=cs(d),!d&&nh(l))?Ql(za(l),0,g):l.split(d,g)):[]}var aWe=lh(function(l,d,g){return l+(g?" ":"")+FU(d)});function cWe(l,d,g){return l=gr(l),g=g==null?0:ef(Et(g),0,l.length),d=cs(d),l.slice(g,g+d.length)==d}function uWe(l,d,g){var y=R.templateSettings;g&&di(l,d,g)&&(d=t),l=gr(l),d=ow({},d,y,hJ);var b=ow({},d.imports,y.imports,hJ),P=no(b),C=XL(b,P),M,U,K=0,Z=d.interpolate||_A,X="__p += '",_e=eU((d.escape||_A).source+"|"+Z.source+"|"+(Z===LY?qGe:_A).source+"|"+(d.evaluate||_A).source+"|$","g"),Me="//# sourceURL="+(yr.call(d,"sourceURL")?(d.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++v5e+"]")+` -`;l.replace(_e,function(et,qt,Yt,ls,fi,ps){return Yt||(Yt=ls),X+=l.slice(K,ps).replace(ZGe,U5e),qt&&(M=!0,X+=`' + -__e(`+qt+`) + -'`),fi&&(U=!0,X+=`'; -`+fi+`; -__p += '`),Yt&&(X+=`' + -((__t = (`+Yt+`)) == null ? '' : __t) + -'`),K=ps+et.length,et}),X+=`'; -`;var Qe=yr.call(d,"variable")&&d.variable;if(!Qe)X=`with (obj) { -`+X+` -} -`;else if(zGe.test(Qe))throw new dt(i);X=(U?X.replace(xGe,""):X).replace(AGe,"$1").replace(wGe,"$1;"),X="function("+(Qe||"obj")+`) { -`+(Qe?"":`obj || (obj = {}); -`)+"var __t, __p = ''"+(M?", __e = _.escape":"")+(U?`, __j = Array.prototype.join; -function print() { __p += __j.call(arguments, '') } -`:`; -`)+X+`return __p -}`;var wt=aX(function(){return mr(P,Me+"return "+X).apply(t,C)});if(wt.source=X,LU(wt))throw wt;return wt}function lWe(l){return gr(l).toLowerCase()}function pWe(l){return gr(l).toUpperCase()}function dWe(l,d,g){if(l=gr(l),l&&(g||d===t))return g7(l);if(!l||!(d=cs(d)))return l;var y=za(l),b=za(d),P=_7(y,b),C=v7(y,b)+1;return Ql(y,P,C).join("")}function fWe(l,d,g){if(l=gr(l),l&&(g||d===t))return l.slice(0,y7(l)+1);if(!l||!(d=cs(d)))return l;var y=za(l),b=v7(y,za(d))+1;return Ql(y,0,b).join("")}function mWe(l,d,g){if(l=gr(l),l&&(g||d===t))return l.replace(LL,"");if(!l||!(d=cs(d)))return l;var y=za(l),b=_7(y,za(d));return Ql(y,b).join("")}function hWe(l,d){var g=he,y=Q;if(an(d)){var b="separator"in d?d.separator:b;g="length"in d?Et(d.length):g,y="omission"in d?cs(d.omission):y}l=gr(l);var P=l.length;if(nh(l)){var C=za(l);P=C.length}if(g>=P)return l;var M=g-oh(y);if(M<1)return y;var U=C?Ql(C,0,M).join(""):l.slice(0,M);if(b===t)return U+y;if(C&&(M+=U.length-M),UU(b)){if(l.slice(M).search(b)){var K,Z=U;for(b.global||(b=eU(b.source,gr(UY.exec(b))+"g")),b.lastIndex=0;K=b.exec(Z);)var X=K.index;U=U.slice(0,X===t?M:X)}}else if(l.indexOf(cs(b),M)!=M){var _e=U.lastIndexOf(b);_e>-1&&(U=U.slice(0,_e))}return U+y}function gWe(l){return l=gr(l),l&&RGe.test(l)?l.replace(MY,V5e):l}var _We=lh(function(l,d,g){return l+(g?" ":"")+d.toUpperCase()}),FU=cJ("toUpperCase");function sX(l,d,g){return l=gr(l),d=g?t:d,d===t?z5e(l)?K5e(l):N5e(l):l.match(d)||[]}var aX=Ot(function(l,d){try{return ss(l,t,d)}catch(g){return LU(g)?g:new dt(g)}}),vWe=qu(function(l,d){return Us(d,function(g){g=Mc(g),zu(l,g,MU(l[g],l))}),l});function SWe(l){var d=l==null?0:l.length,g=Xe();return l=d?en(l,function(y){if(typeof y[1]!="function")throw new js(o);return[g(y[0]),y[1]]}):[],Ot(function(y){for(var b=-1;++bMr)return[];var g=Ar,y=Fo(l,Ar);d=Xe(d),l-=Ar;for(var b=JL(y,d);++g0||d<0)?new Vt(g):(l<0?g=g.takeRight(-l):l&&(g=g.drop(l)),d!==t&&(d=Et(d),g=d<0?g.dropRight(-d):g.take(d-l)),g)},Vt.prototype.takeRightWhile=function(l){return this.reverse().takeWhile(l).reverse()},Vt.prototype.toArray=function(){return this.take(Ar)},$c(Vt.prototype,function(l,d){var g=/^(?:filter|find|map|reject)|While$/.test(d),y=/^(?:head|last)$/.test(d),b=R[y?"take"+(d=="last"?"Right":""):d],P=y||/^find/.test(d);b&&(R.prototype[d]=function(){var C=this.__wrapped__,M=y?[1]:arguments,U=C instanceof Vt,K=M[0],Z=U||ht(C),X=function(qt){var Yt=b.apply(R,Hl([qt],M));return y&&_e?Yt[0]:Yt};Z&&g&&typeof K=="function"&&K.length!=1&&(U=Z=!1);var _e=this.__chain__,Me=!!this.__actions__.length,Qe=P&&!_e,wt=U&&!Me;if(!P&&Z){C=wt?C:new Vt(this);var et=l.apply(C,M);return et.__actions__.push({func:JA,args:[X],thisArg:t}),new zs(et,_e)}return Qe&&wt?l.apply(this,M):(et=this.thru(X),Qe?y?et.value()[0]:et.value():et)})}),Us(["pop","push","shift","sort","splice","unshift"],function(l){var d=bA[l],g=/^(?:push|sort|unshift)$/.test(l)?"tap":"thru",y=/^(?:pop|shift)$/.test(l);R.prototype[l]=function(){var b=arguments;if(y&&!this.__chain__){var P=this.value();return d.apply(ht(P)?P:[],b)}return this[g](function(C){return d.apply(ht(C)?C:[],b)})}}),$c(Vt.prototype,function(l,d){var g=R[d];if(g){var y=g.name+"";yr.call(ah,y)||(ah[y]=[]),ah[y].push({name:d,func:g})}}),ah[GA(t,v).name]=[{name:"wrapper",func:t}],Vt.prototype.clone=g8e,Vt.prototype.reverse=_8e,Vt.prototype.value=v8e,R.prototype.at=WHe,R.prototype.chain=KHe,R.prototype.commit=ZHe,R.prototype.next=YHe,R.prototype.plant=XHe,R.prototype.reverse=QHe,R.prototype.toJSON=R.prototype.valueOf=R.prototype.value=e3e,R.prototype.first=R.prototype.head,GS&&(R.prototype[GS]=JHe),R}),Kl=Z5e();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(ro._=Kl,define(function(){return Kl})):Yd?((Yd.exports=Kl)._=Kl,BL._=Kl):ro._=Kl}).call(Qv)});var $0=S((ukr,sje)=>{var HFt=require("path").relative;sje.exports=JFt;var WFt=process.cwd();function oje(t,e){for(var r=t.split(/[ ,]+/),n=String(e).toLowerCase(),o=0;o0}function QFt(t){if(process.noDeprecation)return!0;var e=process.env.NO_DEPRECATION||"";return oje(e,t)}function eqt(t){if(process.traceDeprecation)return!0;var e=process.env.TRACE_DEPRECATION||"";return oje(e,t)}function iD(t,e){var r=XFt(process,"deprecation");if(!(!r&&this._ignored)){var n,o,i,s,a=0,c=!1,u=sD(),p=this._file;for(e?(s=e,i=oS(u[1]),i.name=s.name,p=i[0]):(a=2,s=oS(u[a]),i=s);a",r=t.getLineNumber(),n=t.getColumnNumber();t.isEval()&&(e=t.getEvalOrigin()+", "+e);var o=[e,r,n];return o.callSite=t,o.name=t.getFunctionName(),o}function nje(t){var e=t.callSite,r=t.name;r||(r="");var n=e.getThis(),o=n&&e.getTypeName();return o==="Object"&&(o=void 0),o==="Function"&&(o=n.name||o),o&&e.getMethodName()?o+"."+r:r}function tqt(t,e,r){var n=new Date().toUTCString(),o=n+" "+this._namespace+" deprecated "+t;if(this._traced){for(var i=0;i{"use strict";aje.exports=Object.setPrototypeOf||({__proto__:[]}instanceof Array?sqt:aqt);function sqt(t,e){return t.__proto__=e,t}function aqt(t,e){for(var r in e)Object.prototype.hasOwnProperty.call(t,r)||(t[r]=e[r]);return t}});var uje=S((pkr,cqt)=>{cqt.exports={"100":"Continue","101":"Switching Protocols","102":"Processing","103":"Early Hints","200":"OK","201":"Created","202":"Accepted","203":"Non-Authoritative Information","204":"No Content","205":"Reset Content","206":"Partial Content","207":"Multi-Status","208":"Already Reported","226":"IM Used","300":"Multiple Choices","301":"Moved Permanently","302":"Found","303":"See Other","304":"Not Modified","305":"Use Proxy","307":"Temporary Redirect","308":"Permanent Redirect","400":"Bad Request","401":"Unauthorized","402":"Payment Required","403":"Forbidden","404":"Not Found","405":"Method Not Allowed","406":"Not Acceptable","407":"Proxy Authentication Required","408":"Request Timeout","409":"Conflict","410":"Gone","411":"Length Required","412":"Precondition Failed","413":"Payload Too Large","414":"URI Too Long","415":"Unsupported Media Type","416":"Range Not Satisfiable","417":"Expectation Failed","418":"I'm a Teapot","421":"Misdirected Request","422":"Unprocessable Entity","423":"Locked","424":"Failed Dependency","425":"Too Early","426":"Upgrade Required","428":"Precondition Required","429":"Too Many Requests","431":"Request Header Fields Too Large","451":"Unavailable For Legal Reasons","500":"Internal Server Error","501":"Not Implemented","502":"Bad Gateway","503":"Service Unavailable","504":"Gateway Timeout","505":"HTTP Version Not Supported","506":"Variant Also Negotiates","507":"Insufficient Storage","508":"Loop Detected","509":"Bandwidth Limit Exceeded","510":"Not Extended","511":"Network Authentication Required"}});var k0=S((dkr,pje)=>{"use strict";var DW=uje();pje.exports=Rc;Rc.message=DW;Rc.code=uqt(DW);Rc.codes=lqt(DW);Rc.redirect={300:!0,301:!0,302:!0,303:!0,305:!0,307:!0,308:!0};Rc.empty={204:!0,205:!0,304:!0};Rc.retry={502:!0,503:!0,504:!0};function uqt(t){var e={};return Object.keys(t).forEach(function(n){var o=t[n],i=Number(n);e[o.toLowerCase()]=i}),e}function lqt(t){return Object.keys(t).map(function(r){return Number(r)})}function pqt(t){var e=t.toLowerCase();if(!Object.prototype.hasOwnProperty.call(Rc.code,e))throw new Error('invalid status message: "'+t+'"');return Rc.code[e]}function lje(t){if(!Object.prototype.hasOwnProperty.call(Rc.message,t))throw new Error("invalid status code: "+t);return Rc.message[t]}function Rc(t){if(typeof t=="number")return lje(t);if(typeof t!="string")throw new TypeError("code must be a number or string");var e=parseInt(t,10);return isNaN(e)?pqt(t):lje(e)}});var dje=S((fkr,LW)=>{typeof Object.create=="function"?LW.exports=function(e,r){r&&(e.super_=r,e.prototype=Object.create(r.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:LW.exports=function(e,r){if(r){e.super_=r;var n=function(){};n.prototype=r.prototype,e.prototype=new n,e.prototype.constructor=e}}});var fje=S((mkr,jW)=>{try{if(UW=require("util"),typeof UW.inherits!="function")throw"";jW.exports=UW.inherits}catch{jW.exports=dje()}var UW});var hje=S((hkr,mje)=>{"use strict";mje.exports=dqt;function dqt(t){return t.split(" ").map(function(e){return e.slice(0,1).toUpperCase()+e.slice(1)}).join("").replace(/[^ _0-9a-z]/gi,"")}});var sS=S((gkr,jm)=>{"use strict";var fqt=$0()("http-errors"),gje=cje(),iS=k0(),zW=fje(),mqt=hje();jm.exports=aD;jm.exports.HttpError=hqt();jm.exports.isHttpError=_qt(jm.exports.HttpError);Sqt(jm.exports,iS.codes,jm.exports.HttpError);function _je(t){return+(String(t).charAt(0)+"00")}function aD(){for(var t,e,r=500,n={},o=0;o=600)&&fqt("non-error status code; use only 4xx or 5xx status codes"),(typeof r!="number"||!iS.message[r]&&(r<400||r>=600))&&(r=500);var a=aD[r]||aD[_je(r)];t||(t=a?new a(e):new Error(e||iS.message[r]),Error.captureStackTrace(t,aD)),(!a||!(t instanceof a)||t.status!==r)&&(t.expose=r<500,t.status=t.statusCode=r);for(var c in n)c!=="status"&&c!=="statusCode"&&(t[c]=n[c]);return t}function hqt(){function t(){throw new TypeError("cannot construct abstract class")}return zW(t,Error),t}function gqt(t,e,r){var n=Sje(e);function o(i){var s=i??iS.message[r],a=new Error(s);return Error.captureStackTrace(a,o),gje(a,o.prototype),Object.defineProperty(a,"message",{enumerable:!0,configurable:!0,value:s,writable:!0}),Object.defineProperty(a,"name",{enumerable:!1,configurable:!0,value:n,writable:!0}),a}return zW(o,t),vje(o,n),o.prototype.status=r,o.prototype.statusCode=r,o.prototype.expose=!0,o}function _qt(t){return function(r){return!r||typeof r!="object"?!1:r instanceof t?!0:r instanceof Error&&typeof r.expose=="boolean"&&typeof r.statusCode=="number"&&r.status===r.statusCode}}function vqt(t,e,r){var n=Sje(e);function o(i){var s=i??iS.message[r],a=new Error(s);return Error.captureStackTrace(a,o),gje(a,o.prototype),Object.defineProperty(a,"message",{enumerable:!0,configurable:!0,value:s,writable:!0}),Object.defineProperty(a,"name",{enumerable:!1,configurable:!0,value:n,writable:!0}),a}return zW(o,t),vje(o,n),o.prototype.status=r,o.prototype.statusCode=r,o.prototype.expose=!1,o}function vje(t,e){var r=Object.getOwnPropertyDescriptor(t,"name");r&&r.configurable&&(r.value=e,Object.defineProperty(t,"name",r))}function Sqt(t,e,r){e.forEach(function(o){var i,s=mqt(iS.message[o]);switch(_je(o)){case 400:i=gqt(r,s,o);break;case 500:i=vqt(r,s,o);break}i&&(t[o]=i,t[s]=i)})}function Sje(t){return t.slice(-5)==="Error"?t:t+"Error"}});var FW=S((_kr,cD)=>{"use strict";cD.exports=bqt;cD.exports.format=yje;cD.exports.parse=Eje;var yqt=/\B(?=(\d{3})+(?!\d))/g,Eqt=/(?:\.0*|(\.[^0]+)0+)$/,zd={b:1,kb:1024,mb:1<<20,gb:1<<30,tb:Math.pow(1024,4),pb:Math.pow(1024,5)},Tqt=/^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i;function bqt(t,e){return typeof t=="string"?Eje(t):typeof t=="number"?yje(t,e):null}function yje(t,e){if(!Number.isFinite(t))return null;var r=Math.abs(t),n=e&&e.thousandsSeparator||"",o=e&&e.unitSeparator||"",i=e&&e.decimalPlaces!==void 0?e.decimalPlaces:2,s=!!(e&&e.fixedDecimals),a=e&&e.unit||"";(!a||!zd[a.toLowerCase()])&&(r>=zd.pb?a="PB":r>=zd.tb?a="TB":r>=zd.gb?a="GB":r>=zd.mb?a="MB":r>=zd.kb?a="KB":a="B");var c=t/zd[a.toLowerCase()],u=c.toFixed(i);return s||(u=u.replace(Eqt,"$1")),n&&(u=u.split(".").map(function(p,f){return f===0?p.replace(yqt,n):p}).join(".")),u+o+a}function Eje(t){if(typeof t=="number"&&!isNaN(t))return t;if(typeof t!="string")return null;var e=Tqt.exec(t),r,n="b";return e?(r=parseFloat(e[1]),n=e[4].toLowerCase()):(r=parseInt(t,10),n="b"),isNaN(r)?null:Math.floor(zd[n]*r)}});var Ll=S((vkr,Tje)=>{"use strict";var uD=require("buffer"),aS=uD.Buffer,Na={},Ca;for(Ca in uD)uD.hasOwnProperty(Ca)&&(Ca==="SlowBuffer"||Ca==="Buffer"||(Na[Ca]=uD[Ca]));var cS=Na.Buffer={};for(Ca in aS)aS.hasOwnProperty(Ca)&&(Ca==="allocUnsafe"||Ca==="allocUnsafeSlow"||(cS[Ca]=aS[Ca]));Na.Buffer.prototype=aS.prototype;(!cS.from||cS.from===Uint8Array.from)&&(cS.from=function(t,e,r){if(typeof t=="number")throw new TypeError('The "value" argument must not be of type number. Received type '+typeof t);if(t&&typeof t.length>"u")throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);return aS(t,e,r)});cS.alloc||(cS.alloc=function(t,e,r){if(typeof t!="number")throw new TypeError('The "size" argument must be of type number. Received type '+typeof t);if(t<0||t>=2*(1<<30))throw new RangeError('The value "'+t+'" is invalid for option "size"');var n=aS(t);return!e||e.length===0?n.fill(0):typeof r=="string"?n.fill(e,r):n.fill(e),n});if(!Na.kStringMaxLength)try{Na.kStringMaxLength=process.binding("buffer").kStringMaxLength}catch{}Na.constants||(Na.constants={MAX_LENGTH:Na.kMaxLength},Na.kStringMaxLength&&(Na.constants.MAX_STRING_LENGTH=Na.kStringMaxLength));Tje.exports=Na});var xje=S(GW=>{"use strict";var bje="\uFEFF";GW.PrependBOM=qW;function qW(t,e){this.encoder=t,this.addBOM=!0}qW.prototype.write=function(t){return this.addBOM&&(t=bje+t,this.addBOM=!1),this.encoder.write(t)};qW.prototype.end=function(){return this.encoder.end()};GW.StripBOM=BW;function BW(t,e){this.decoder=t,this.pass=!1,this.options=e||{}}BW.prototype.write=function(t){var e=this.decoder.write(t);return this.pass||!e||(e[0]===bje&&(e=e.slice(1),typeof this.options.stripBOM=="function"&&this.options.stripBOM()),this.pass=!0),e};BW.prototype.end=function(){return this.decoder.end()}});var VW=S((ykr,Aje)=>{"use strict";var xqt=typeof Object.hasOwn>"u"?Function.call.bind(Object.prototype.hasOwnProperty):Object.hasOwn;function Aqt(t,e){for(var r in e)xqt(e,r)&&(t[r]=e[r])}Aje.exports=Aqt});var Rje=S((Ekr,wje)=>{"use strict";var Ul=Ll().Buffer;wje.exports={utf8:{type:"_internal",bomAware:!0},cesu8:{type:"_internal",bomAware:!0},unicode11utf8:"utf8",ucs2:{type:"_internal",bomAware:!0},utf16le:"ucs2",binary:{type:"_internal"},base64:{type:"_internal"},hex:{type:"_internal"},_internal:HW};function HW(t,e){this.enc=t.encodingName,this.bomAware=t.bomAware,this.enc==="base64"?this.encoder=ZW:this.enc==="utf8"?this.encoder=XW:this.enc==="cesu8"&&(this.enc="utf8",this.encoder=YW,Ul.from("eda0bdedb2a9","hex").toString()!=="\u{1F4A9}"&&(this.decoder=JW,this.defaultCharUnicode=e.defaultCharUnicode))}HW.prototype.encoder=KW;HW.prototype.decoder=WW;var wqt=require("string_decoder").StringDecoder;function WW(t,e){this.decoder=new wqt(e.enc)}WW.prototype.write=function(t){return Ul.isBuffer(t)||(t=Ul.from(t)),this.decoder.write(t)};WW.prototype.end=function(){return this.decoder.end()};function KW(t,e){this.enc=e.enc}KW.prototype.write=function(t){return Ul.from(t,this.enc)};KW.prototype.end=function(){};function ZW(t,e){this.prevStr=""}ZW.prototype.write=function(t){t=this.prevStr+t;var e=t.length-t.length%4;return this.prevStr=t.slice(e),t=t.slice(0,e),Ul.from(t,"base64")};ZW.prototype.end=function(){return Ul.from(this.prevStr,"base64")};function YW(t,e){}YW.prototype.write=function(t){for(var e=Ul.alloc(t.length*3),r=0,n=0;n>>6),e[r++]=128+(o&63)):(e[r++]=224+(o>>>12),e[r++]=128+(o>>>6&63),e[r++]=128+(o&63))}return e.slice(0,r)};YW.prototype.end=function(){};function JW(t,e){this.acc=0,this.contBytes=0,this.accBytes=0,this.defaultCharUnicode=e.defaultCharUnicode}JW.prototype.write=function(t){for(var e=this.acc,r=this.contBytes,n=this.accBytes,o="",i=0;i0&&(o+=this.defaultCharUnicode,r=0),s<128?o+=String.fromCharCode(s):s<224?(e=s&31,r=1,n=1):s<240?(e=s&15,r=2,n=1):o+=this.defaultCharUnicode):r>0?(e=e<<6|s&63,r--,n++,r===0&&(n===2&&e<128&&e>0?o+=this.defaultCharUnicode:n===3&&e<2048?o+=this.defaultCharUnicode:o+=String.fromCharCode(e))):o+=this.defaultCharUnicode}return this.acc=e,this.contBytes=r,this.accBytes=n,o};JW.prototype.end=function(){var t=0;return this.contBytes>0&&(t+=this.defaultCharUnicode),t};function XW(t,e){this.highSurrogate=""}XW.prototype.write=function(t){if(this.highSurrogate&&(t=this.highSurrogate+t,this.highSurrogate=""),t.length>0){var e=t.charCodeAt(t.length-1);e>=55296&&e<56320&&(this.highSurrogate=t[t.length-1],t=t.slice(0,t.length-1))}return Ul.from(t,this.enc)};XW.prototype.end=function(){if(this.highSurrogate){var t=this.highSurrogate;return this.highSurrogate="",Ul.from(t,this.enc)}}});var Oje=S(Fd=>{"use strict";var lD=Ll().Buffer;Fd._utf32=QW;function QW(t,e){this.iconv=e,this.bomAware=!0,this.isLE=t.isLE}Fd.utf32le={type:"_utf32",isLE:!0};Fd.utf32be={type:"_utf32",isLE:!1};Fd.ucs4le="utf32le";Fd.ucs4be="utf32be";QW.prototype.encoder=eK;QW.prototype.decoder=tK;function eK(t,e){this.isLE=e.isLE,this.highSurrogate=0}eK.prototype.write=function(t){for(var e=lD.from(t,"ucs2"),r=lD.alloc(e.length*2),n=this.isLE?r.writeUInt32LE:r.writeUInt32BE,o=0,i=0;i=55296&&s<56320,c=s>=56320&&s<57344;if(this.highSurrogate)if(a||!c)n.call(r,this.highSurrogate,o),o+=4;else{var u=(this.highSurrogate-55296<<10|s-56320)+65536;n.call(r,u,o),o+=4,this.highSurrogate=0;continue}a?this.highSurrogate=s:(n.call(r,s,o),o+=4,this.highSurrogate=0)}return o0){for(;e1114111)&&(r=n),r>=65536){r-=65536;var o=55296|r>>10;t[e++]=o&255,t[e++]=o>>8;var r=56320|r&1023}return t[e++]=r&255,t[e++]=r>>8,e}tK.prototype.end=function(){this.overflow.length=0};Fd.utf32=rK;Fd.ucs4="utf32";function rK(t,e){this.iconv=e}rK.prototype.encoder=nK;rK.prototype.decoder=oK;function nK(t,e){t=t||{},t.addBOM===void 0&&(t.addBOM=!0),this.encoder=e.iconv.getEncoder(t.defaultEncoding||"utf-32le",t)}nK.prototype.write=function(t){return this.encoder.write(t)};nK.prototype.end=function(){return this.encoder.end()};function oK(t,e){this.decoder=null,this.initialBufs=[],this.initialBufsLen=0,this.options=t||{},this.iconv=e.iconv}oK.prototype.write=function(t){if(!this.decoder){if(this.initialBufs.push(t),this.initialBufsLen+=t.length,this.initialBufsLen<32)return"";var e=Ije(this.initialBufs,this.options.defaultEncoding);this.decoder=this.iconv.getDecoder(e,this.options);for(var r="",n=0;n16)&&i++,(r[3]!==0||r[2]>16)&&o++,r[0]===0&&r[1]===0&&(r[2]!==0||r[3]!==0)&&a++,(r[0]!==0||r[1]!==0)&&r[2]===0&&r[3]===0&&s++,r.length=0,n++,n>=100)break e}return a-i>s-o?"utf-32be":a-i{"use strict";var Nje=Ll().Buffer;lK.utf16be=pD;function pD(){}pD.prototype.encoder=iK;pD.prototype.decoder=sK;pD.prototype.bomAware=!0;function iK(){}iK.prototype.write=function(t){for(var e=Nje.from(t,"ucs2"),r=0;r=100)break e}return i>o?"utf-16be":i{"use strict";var wu=Ll().Buffer;mD.utf7=dD;mD.unicode11utf7="utf7";function dD(t,e){this.iconv=e}dD.prototype.encoder=dK;dD.prototype.decoder=fK;dD.prototype.bomAware=!0;var Rqt=/[^A-Za-z0-9'\(\),-\.\/:\? \n\r\t]+/g;function dK(t,e){this.iconv=e.iconv}dK.prototype.write=function(t){return wu.from(t.replace(Rqt,function(e){return"+"+(e==="+"?"":this.iconv.encode(e,"utf16-be").toString("base64").replace(/=+$/,""))+"-"}.bind(this)))};dK.prototype.end=function(){};function fK(t,e){this.iconv=e.iconv,this.inBase64=!1,this.base64Accum=""}var Pqt=/[A-Za-z0-9\/+]/,mK=[];for(M0=0;M0<256;M0++)mK[M0]=Pqt.test(String.fromCharCode(M0));var M0,Iqt=43,zm=45,pK=38;fK.prototype.write=function(t){for(var e="",r=0,n=this.inBase64,o=this.base64Accum,i=0;i0&&(t=this.iconv.decode(wu.from(this.base64Accum,"base64"),"utf16-be")),this.inBase64=!1,this.base64Accum="",t};mD.utf7imap=fD;function fD(t,e){this.iconv=e}fD.prototype.encoder=hK;fD.prototype.decoder=gK;fD.prototype.bomAware=!0;function hK(t,e){this.iconv=e.iconv,this.inBase64=!1,this.base64Accum=wu.alloc(6),this.base64AccumIdx=0}hK.prototype.write=function(t){for(var e=this.inBase64,r=this.base64Accum,n=this.base64AccumIdx,o=wu.alloc(t.length*5+10),i=0,s=0;s=32&&a<=126?(e&&(n>0&&(i+=o.write(r.slice(0,n).toString("base64").replace(/\//g,",").replace(/=+$/,""),i),n=0),o[i++]=zm,e=!1),e||(o[i++]=a,a===pK&&(o[i++]=zm))):(e||(o[i++]=pK,e=!0),e&&(r[n++]=a>>8,r[n++]=a&255,n==r.length&&(i+=o.write(r.toString("base64").replace(/\//g,","),i),n=0)))}return this.inBase64=e,this.base64AccumIdx=n,o.slice(0,i)};hK.prototype.end=function(){var t=wu.alloc(10),e=0;return this.inBase64&&(this.base64AccumIdx>0&&(e+=t.write(this.base64Accum.slice(0,this.base64AccumIdx).toString("base64").replace(/\//g,",").replace(/=+$/,""),e),this.base64AccumIdx=0),t[e++]=zm,this.inBase64=!1),t.slice(0,e)};function gK(t,e){this.iconv=e.iconv,this.inBase64=!1,this.base64Accum=""}var kje=mK.slice();kje[44]=!0;gK.prototype.write=function(t){for(var e="",r=0,n=this.inBase64,o=this.base64Accum,i=0;i0&&(t=this.iconv.decode(wu.from(this.base64Accum,"base64"),"utf16-be")),this.inBase64=!1,this.base64Accum="",t}});var Lje=S(Dje=>{"use strict";var hD=Ll().Buffer;Dje._sbcs=_K;function _K(t,e){if(!t)throw new Error("SBCS codec is called without the data.");if(!t.chars||t.chars.length!==128&&t.chars.length!==256)throw new Error("Encoding '"+t.type+"' has incorrect 'chars' (must be of len 128 or 256)");if(t.chars.length===128){for(var r="",n=0;n<128;n++)r+=String.fromCharCode(n);t.chars=r+t.chars}this.decodeBuf=hD.from(t.chars,"ucs2");for(var o=hD.alloc(65536,e.defaultCharSingleByte.charCodeAt(0)),n=0;n{"use strict";Uje.exports={10029:"maccenteuro",maccenteuro:{type:"_sbcs",chars:"\xC4\u0100\u0101\xC9\u0104\xD6\xDC\xE1\u0105\u010C\xE4\u010D\u0106\u0107\xE9\u0179\u017A\u010E\xED\u010F\u0112\u0113\u0116\xF3\u0117\xF4\xF6\xF5\xFA\u011A\u011B\xFC\u2020\xB0\u0118\xA3\xA7\u2022\xB6\xDF\xAE\xA9\u2122\u0119\xA8\u2260\u0123\u012E\u012F\u012A\u2264\u2265\u012B\u0136\u2202\u2211\u0142\u013B\u013C\u013D\u013E\u0139\u013A\u0145\u0146\u0143\xAC\u221A\u0144\u0147\u2206\xAB\xBB\u2026\xA0\u0148\u0150\xD5\u0151\u014C\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u25CA\u014D\u0154\u0155\u0158\u2039\u203A\u0159\u0156\u0157\u0160\u201A\u201E\u0161\u015A\u015B\xC1\u0164\u0165\xCD\u017D\u017E\u016A\xD3\xD4\u016B\u016E\xDA\u016F\u0170\u0171\u0172\u0173\xDD\xFD\u0137\u017B\u0141\u017C\u0122\u02C7"},808:"cp808",ibm808:"cp808",cp808:{type:"_sbcs",chars:"\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F\u0401\u0451\u0404\u0454\u0407\u0457\u040E\u045E\xB0\u2219\xB7\u221A\u2116\u20AC\u25A0\xA0"},mik:{type:"_sbcs",chars:"\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F\u2514\u2534\u252C\u251C\u2500\u253C\u2563\u2551\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2510\u2591\u2592\u2593\u2502\u2524\u2116\xA7\u2557\u255D\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},cp720:{type:"_sbcs",chars:"\x80\x81\xE9\xE2\x84\xE0\x86\xE7\xEA\xEB\xE8\xEF\xEE\x8D\x8E\x8F\x90\u0651\u0652\xF4\xA4\u0640\xFB\xF9\u0621\u0622\u0623\u0624\xA3\u0625\u0626\u0627\u0628\u0629\u062A\u062B\u062C\u062D\u062E\u062F\u0630\u0631\u0632\u0633\u0634\u0635\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u0636\u0637\u0638\u0639\u063A\u0641\xB5\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064A\u2261\u064B\u064C\u064D\u064E\u064F\u0650\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ascii8bit:"ascii",usascii:"ascii",ansix34:"ascii",ansix341968:"ascii",ansix341986:"ascii",csascii:"ascii",cp367:"ascii",ibm367:"ascii",isoir6:"ascii",iso646us:"ascii",iso646irv:"ascii",us:"ascii",latin1:"iso88591",latin2:"iso88592",latin3:"iso88593",latin4:"iso88594",latin5:"iso88599",latin6:"iso885910",latin7:"iso885913",latin8:"iso885914",latin9:"iso885915",latin10:"iso885916",csisolatin1:"iso88591",csisolatin2:"iso88592",csisolatin3:"iso88593",csisolatin4:"iso88594",csisolatincyrillic:"iso88595",csisolatinarabic:"iso88596",csisolatingreek:"iso88597",csisolatinhebrew:"iso88598",csisolatin5:"iso88599",csisolatin6:"iso885910",l1:"iso88591",l2:"iso88592",l3:"iso88593",l4:"iso88594",l5:"iso88599",l6:"iso885910",l7:"iso885913",l8:"iso885914",l9:"iso885915",l10:"iso885916",isoir14:"iso646jp",isoir57:"iso646cn",isoir100:"iso88591",isoir101:"iso88592",isoir109:"iso88593",isoir110:"iso88594",isoir144:"iso88595",isoir127:"iso88596",isoir126:"iso88597",isoir138:"iso88598",isoir148:"iso88599",isoir157:"iso885910",isoir166:"tis620",isoir179:"iso885913",isoir199:"iso885914",isoir203:"iso885915",isoir226:"iso885916",cp819:"iso88591",ibm819:"iso88591",cyrillic:"iso88595",arabic:"iso88596",arabic8:"iso88596",ecma114:"iso88596",asmo708:"iso88596",greek:"iso88597",greek8:"iso88597",ecma118:"iso88597",elot928:"iso88597",hebrew:"iso88598",hebrew8:"iso88598",turkish:"iso88599",turkish8:"iso88599",thai:"iso885911",thai8:"iso885911",celtic:"iso885914",celtic8:"iso885914",isoceltic:"iso885914",tis6200:"tis620",tis62025291:"tis620",tis62025330:"tis620",1e4:"macroman",10006:"macgreek",10007:"maccyrillic",10079:"maciceland",10081:"macturkish",cspc8codepage437:"cp437",cspc775baltic:"cp775",cspc850multilingual:"cp850",cspcp852:"cp852",cspc862latinhebrew:"cp862",cpgr:"cp869",msee:"cp1250",mscyrl:"cp1251",msansi:"cp1252",msgreek:"cp1253",msturk:"cp1254",mshebr:"cp1255",msarab:"cp1256",winbaltrim:"cp1257",cp20866:"koi8r",20866:"koi8r",ibm878:"koi8r",cskoi8r:"koi8r",cp21866:"koi8u",21866:"koi8u",ibm1168:"koi8u",strk10482002:"rk1048",tcvn5712:"tcvn",tcvn57121:"tcvn",gb198880:"iso646cn",cn:"iso646cn",csiso14jisc6220ro:"iso646jp",jisc62201969ro:"iso646jp",jp:"iso646jp",cshproman8:"hproman8",r8:"hproman8",roman8:"hproman8",xroman8:"hproman8",ibm1051:"hproman8",mac:"macintosh",csmacintosh:"macintosh"}});var Fje=S((Rkr,zje)=>{"use strict";zje.exports={437:"cp437",737:"cp737",775:"cp775",850:"cp850",852:"cp852",855:"cp855",856:"cp856",857:"cp857",858:"cp858",860:"cp860",861:"cp861",862:"cp862",863:"cp863",864:"cp864",865:"cp865",866:"cp866",869:"cp869",874:"windows874",922:"cp922",1046:"cp1046",1124:"cp1124",1125:"cp1125",1129:"cp1129",1133:"cp1133",1161:"cp1161",1162:"cp1162",1163:"cp1163",1250:"windows1250",1251:"windows1251",1252:"windows1252",1253:"windows1253",1254:"windows1254",1255:"windows1255",1256:"windows1256",1257:"windows1257",1258:"windows1258",28591:"iso88591",28592:"iso88592",28593:"iso88593",28594:"iso88594",28595:"iso88595",28596:"iso88596",28597:"iso88597",28598:"iso88598",28599:"iso88599",28600:"iso885910",28601:"iso885911",28603:"iso885913",28604:"iso885914",28605:"iso885915",28606:"iso885916",windows874:{type:"_sbcs",chars:"\u20AC\uFFFD\uFFFD\uFFFD\uFFFD\u2026\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\xA0\u0E01\u0E02\u0E03\u0E04\u0E05\u0E06\u0E07\u0E08\u0E09\u0E0A\u0E0B\u0E0C\u0E0D\u0E0E\u0E0F\u0E10\u0E11\u0E12\u0E13\u0E14\u0E15\u0E16\u0E17\u0E18\u0E19\u0E1A\u0E1B\u0E1C\u0E1D\u0E1E\u0E1F\u0E20\u0E21\u0E22\u0E23\u0E24\u0E25\u0E26\u0E27\u0E28\u0E29\u0E2A\u0E2B\u0E2C\u0E2D\u0E2E\u0E2F\u0E30\u0E31\u0E32\u0E33\u0E34\u0E35\u0E36\u0E37\u0E38\u0E39\u0E3A\uFFFD\uFFFD\uFFFD\uFFFD\u0E3F\u0E40\u0E41\u0E42\u0E43\u0E44\u0E45\u0E46\u0E47\u0E48\u0E49\u0E4A\u0E4B\u0E4C\u0E4D\u0E4E\u0E4F\u0E50\u0E51\u0E52\u0E53\u0E54\u0E55\u0E56\u0E57\u0E58\u0E59\u0E5A\u0E5B\uFFFD\uFFFD\uFFFD\uFFFD"},win874:"windows874",cp874:"windows874",windows1250:{type:"_sbcs",chars:"\u20AC\uFFFD\u201A\uFFFD\u201E\u2026\u2020\u2021\uFFFD\u2030\u0160\u2039\u015A\u0164\u017D\u0179\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\u2122\u0161\u203A\u015B\u0165\u017E\u017A\xA0\u02C7\u02D8\u0141\xA4\u0104\xA6\xA7\xA8\xA9\u015E\xAB\xAC\xAD\xAE\u017B\xB0\xB1\u02DB\u0142\xB4\xB5\xB6\xB7\xB8\u0105\u015F\xBB\u013D\u02DD\u013E\u017C\u0154\xC1\xC2\u0102\xC4\u0139\u0106\xC7\u010C\xC9\u0118\xCB\u011A\xCD\xCE\u010E\u0110\u0143\u0147\xD3\xD4\u0150\xD6\xD7\u0158\u016E\xDA\u0170\xDC\xDD\u0162\xDF\u0155\xE1\xE2\u0103\xE4\u013A\u0107\xE7\u010D\xE9\u0119\xEB\u011B\xED\xEE\u010F\u0111\u0144\u0148\xF3\xF4\u0151\xF6\xF7\u0159\u016F\xFA\u0171\xFC\xFD\u0163\u02D9"},win1250:"windows1250",cp1250:"windows1250",windows1251:{type:"_sbcs",chars:"\u0402\u0403\u201A\u0453\u201E\u2026\u2020\u2021\u20AC\u2030\u0409\u2039\u040A\u040C\u040B\u040F\u0452\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\u2122\u0459\u203A\u045A\u045C\u045B\u045F\xA0\u040E\u045E\u0408\xA4\u0490\xA6\xA7\u0401\xA9\u0404\xAB\xAC\xAD\xAE\u0407\xB0\xB1\u0406\u0456\u0491\xB5\xB6\xB7\u0451\u2116\u0454\xBB\u0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F"},win1251:"windows1251",cp1251:"windows1251",windows1252:{type:"_sbcs",chars:"\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\u0160\u2039\u0152\uFFFD\u017D\uFFFD\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u02DC\u2122\u0161\u203A\u0153\uFFFD\u017E\u0178\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"},win1252:"windows1252",cp1252:"windows1252",windows1253:{type:"_sbcs",chars:"\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021\uFFFD\u2030\uFFFD\u2039\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\u2122\uFFFD\u203A\uFFFD\uFFFD\uFFFD\uFFFD\xA0\u0385\u0386\xA3\xA4\xA5\xA6\xA7\xA8\xA9\uFFFD\xAB\xAC\xAD\xAE\u2015\xB0\xB1\xB2\xB3\u0384\xB5\xB6\xB7\u0388\u0389\u038A\xBB\u038C\xBD\u038E\u038F\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039A\u039B\u039C\u039D\u039E\u039F\u03A0\u03A1\uFFFD\u03A3\u03A4\u03A5\u03A6\u03A7\u03A8\u03A9\u03AA\u03AB\u03AC\u03AD\u03AE\u03AF\u03B0\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C2\u03C3\u03C4\u03C5\u03C6\u03C7\u03C8\u03C9\u03CA\u03CB\u03CC\u03CD\u03CE\uFFFD"},win1253:"windows1253",cp1253:"windows1253",windows1254:{type:"_sbcs",chars:"\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\u0160\u2039\u0152\uFFFD\uFFFD\uFFFD\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u02DC\u2122\u0161\u203A\u0153\uFFFD\uFFFD\u0178\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\u011E\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\u0130\u015E\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\u011F\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\u0131\u015F\xFF"},win1254:"windows1254",cp1254:"windows1254",windows1255:{type:"_sbcs",chars:"\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\uFFFD\u2039\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u02DC\u2122\uFFFD\u203A\uFFFD\uFFFD\uFFFD\uFFFD\xA0\xA1\xA2\xA3\u20AA\xA5\xA6\xA7\xA8\xA9\xD7\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xF7\xBB\xBC\xBD\xBE\xBF\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BE\u05BF\u05C0\u05C1\u05C2\u05C3\u05F0\u05F1\u05F2\u05F3\u05F4\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC\u05DD\u05DE\u05DF\u05E0\u05E1\u05E2\u05E3\u05E4\u05E5\u05E6\u05E7\u05E8\u05E9\u05EA\uFFFD\uFFFD\u200E\u200F\uFFFD"},win1255:"windows1255",cp1255:"windows1255",windows1256:{type:"_sbcs",chars:"\u20AC\u067E\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06AF\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u06A9\u2122\u0691\u203A\u0153\u200C\u200D\u06BA\xA0\u060C\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\u06BE\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\u061B\xBB\xBC\xBD\xBE\u061F\u06C1\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062A\u062B\u062C\u062D\u062E\u062F\u0630\u0631\u0632\u0633\u0634\u0635\u0636\xD7\u0637\u0638\u0639\u063A\u0640\u0641\u0642\u0643\xE0\u0644\xE2\u0645\u0646\u0647\u0648\xE7\xE8\xE9\xEA\xEB\u0649\u064A\xEE\xEF\u064B\u064C\u064D\u064E\xF4\u064F\u0650\xF7\u0651\xF9\u0652\xFB\xFC\u200E\u200F\u06D2"},win1256:"windows1256",cp1256:"windows1256",windows1257:{type:"_sbcs",chars:"\u20AC\uFFFD\u201A\uFFFD\u201E\u2026\u2020\u2021\uFFFD\u2030\uFFFD\u2039\uFFFD\xA8\u02C7\xB8\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\u2122\uFFFD\u203A\uFFFD\xAF\u02DB\uFFFD\xA0\uFFFD\xA2\xA3\xA4\uFFFD\xA6\xA7\xD8\xA9\u0156\xAB\xAC\xAD\xAE\xC6\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xF8\xB9\u0157\xBB\xBC\xBD\xBE\xE6\u0104\u012E\u0100\u0106\xC4\xC5\u0118\u0112\u010C\xC9\u0179\u0116\u0122\u0136\u012A\u013B\u0160\u0143\u0145\xD3\u014C\xD5\xD6\xD7\u0172\u0141\u015A\u016A\xDC\u017B\u017D\xDF\u0105\u012F\u0101\u0107\xE4\xE5\u0119\u0113\u010D\xE9\u017A\u0117\u0123\u0137\u012B\u013C\u0161\u0144\u0146\xF3\u014D\xF5\xF6\xF7\u0173\u0142\u015B\u016B\xFC\u017C\u017E\u02D9"},win1257:"windows1257",cp1257:"windows1257",windows1258:{type:"_sbcs",chars:"\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\uFFFD\u2039\u0152\uFFFD\uFFFD\uFFFD\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u02DC\u2122\uFFFD\u203A\u0153\uFFFD\uFFFD\u0178\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\u0102\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\u0300\xCD\xCE\xCF\u0110\xD1\u0309\xD3\xD4\u01A0\xD6\xD7\xD8\xD9\xDA\xDB\xDC\u01AF\u0303\xDF\xE0\xE1\xE2\u0103\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\u0301\xED\xEE\xEF\u0111\xF1\u0323\xF3\xF4\u01A1\xF6\xF7\xF8\xF9\xFA\xFB\xFC\u01B0\u20AB\xFF"},win1258:"windows1258",cp1258:"windows1258",iso88591:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"},cp28591:"iso88591",iso88592:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0104\u02D8\u0141\xA4\u013D\u015A\xA7\xA8\u0160\u015E\u0164\u0179\xAD\u017D\u017B\xB0\u0105\u02DB\u0142\xB4\u013E\u015B\u02C7\xB8\u0161\u015F\u0165\u017A\u02DD\u017E\u017C\u0154\xC1\xC2\u0102\xC4\u0139\u0106\xC7\u010C\xC9\u0118\xCB\u011A\xCD\xCE\u010E\u0110\u0143\u0147\xD3\xD4\u0150\xD6\xD7\u0158\u016E\xDA\u0170\xDC\xDD\u0162\xDF\u0155\xE1\xE2\u0103\xE4\u013A\u0107\xE7\u010D\xE9\u0119\xEB\u011B\xED\xEE\u010F\u0111\u0144\u0148\xF3\xF4\u0151\xF6\xF7\u0159\u016F\xFA\u0171\xFC\xFD\u0163\u02D9"},cp28592:"iso88592",iso88593:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0126\u02D8\xA3\xA4\uFFFD\u0124\xA7\xA8\u0130\u015E\u011E\u0134\xAD\uFFFD\u017B\xB0\u0127\xB2\xB3\xB4\xB5\u0125\xB7\xB8\u0131\u015F\u011F\u0135\xBD\uFFFD\u017C\xC0\xC1\xC2\uFFFD\xC4\u010A\u0108\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\uFFFD\xD1\xD2\xD3\xD4\u0120\xD6\xD7\u011C\xD9\xDA\xDB\xDC\u016C\u015C\xDF\xE0\xE1\xE2\uFFFD\xE4\u010B\u0109\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\uFFFD\xF1\xF2\xF3\xF4\u0121\xF6\xF7\u011D\xF9\xFA\xFB\xFC\u016D\u015D\u02D9"},cp28593:"iso88593",iso88594:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0104\u0138\u0156\xA4\u0128\u013B\xA7\xA8\u0160\u0112\u0122\u0166\xAD\u017D\xAF\xB0\u0105\u02DB\u0157\xB4\u0129\u013C\u02C7\xB8\u0161\u0113\u0123\u0167\u014A\u017E\u014B\u0100\xC1\xC2\xC3\xC4\xC5\xC6\u012E\u010C\xC9\u0118\xCB\u0116\xCD\xCE\u012A\u0110\u0145\u014C\u0136\xD4\xD5\xD6\xD7\xD8\u0172\xDA\xDB\xDC\u0168\u016A\xDF\u0101\xE1\xE2\xE3\xE4\xE5\xE6\u012F\u010D\xE9\u0119\xEB\u0117\xED\xEE\u012B\u0111\u0146\u014D\u0137\xF4\xF5\xF6\xF7\xF8\u0173\xFA\xFB\xFC\u0169\u016B\u02D9"},cp28594:"iso88594",iso88595:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040A\u040B\u040C\xAD\u040E\u040F\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F\u2116\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045A\u045B\u045C\xA7\u045E\u045F"},cp28595:"iso88595",iso88596:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\uFFFD\uFFFD\uFFFD\xA4\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u060C\xAD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u061B\uFFFD\uFFFD\uFFFD\u061F\uFFFD\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062A\u062B\u062C\u062D\u062E\u062F\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0640\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064A\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"},cp28596:"iso88596",iso88597:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u2018\u2019\xA3\u20AC\u20AF\xA6\xA7\xA8\xA9\u037A\xAB\xAC\xAD\uFFFD\u2015\xB0\xB1\xB2\xB3\u0384\u0385\u0386\xB7\u0388\u0389\u038A\xBB\u038C\xBD\u038E\u038F\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039A\u039B\u039C\u039D\u039E\u039F\u03A0\u03A1\uFFFD\u03A3\u03A4\u03A5\u03A6\u03A7\u03A8\u03A9\u03AA\u03AB\u03AC\u03AD\u03AE\u03AF\u03B0\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C2\u03C3\u03C4\u03C5\u03C6\u03C7\u03C8\u03C9\u03CA\u03CB\u03CC\u03CD\u03CE\uFFFD"},cp28597:"iso88597",iso88598:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\uFFFD\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xD7\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xF7\xBB\xBC\xBD\xBE\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2017\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC\u05DD\u05DE\u05DF\u05E0\u05E1\u05E2\u05E3\u05E4\u05E5\u05E6\u05E7\u05E8\u05E9\u05EA\uFFFD\uFFFD\u200E\u200F\uFFFD"},cp28598:"iso88598",iso88599:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\u011E\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\u0130\u015E\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\u011F\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\u0131\u015F\xFF"},cp28599:"iso88599",iso885910:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0104\u0112\u0122\u012A\u0128\u0136\xA7\u013B\u0110\u0160\u0166\u017D\xAD\u016A\u014A\xB0\u0105\u0113\u0123\u012B\u0129\u0137\xB7\u013C\u0111\u0161\u0167\u017E\u2015\u016B\u014B\u0100\xC1\xC2\xC3\xC4\xC5\xC6\u012E\u010C\xC9\u0118\xCB\u0116\xCD\xCE\xCF\xD0\u0145\u014C\xD3\xD4\xD5\xD6\u0168\xD8\u0172\xDA\xDB\xDC\xDD\xDE\xDF\u0101\xE1\xE2\xE3\xE4\xE5\xE6\u012F\u010D\xE9\u0119\xEB\u0117\xED\xEE\xEF\xF0\u0146\u014D\xF3\xF4\xF5\xF6\u0169\xF8\u0173\xFA\xFB\xFC\xFD\xFE\u0138"},cp28600:"iso885910",iso885911:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0E01\u0E02\u0E03\u0E04\u0E05\u0E06\u0E07\u0E08\u0E09\u0E0A\u0E0B\u0E0C\u0E0D\u0E0E\u0E0F\u0E10\u0E11\u0E12\u0E13\u0E14\u0E15\u0E16\u0E17\u0E18\u0E19\u0E1A\u0E1B\u0E1C\u0E1D\u0E1E\u0E1F\u0E20\u0E21\u0E22\u0E23\u0E24\u0E25\u0E26\u0E27\u0E28\u0E29\u0E2A\u0E2B\u0E2C\u0E2D\u0E2E\u0E2F\u0E30\u0E31\u0E32\u0E33\u0E34\u0E35\u0E36\u0E37\u0E38\u0E39\u0E3A\uFFFD\uFFFD\uFFFD\uFFFD\u0E3F\u0E40\u0E41\u0E42\u0E43\u0E44\u0E45\u0E46\u0E47\u0E48\u0E49\u0E4A\u0E4B\u0E4C\u0E4D\u0E4E\u0E4F\u0E50\u0E51\u0E52\u0E53\u0E54\u0E55\u0E56\u0E57\u0E58\u0E59\u0E5A\u0E5B\uFFFD\uFFFD\uFFFD\uFFFD"},cp28601:"iso885911",iso885913:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u201D\xA2\xA3\xA4\u201E\xA6\xA7\xD8\xA9\u0156\xAB\xAC\xAD\xAE\xC6\xB0\xB1\xB2\xB3\u201C\xB5\xB6\xB7\xF8\xB9\u0157\xBB\xBC\xBD\xBE\xE6\u0104\u012E\u0100\u0106\xC4\xC5\u0118\u0112\u010C\xC9\u0179\u0116\u0122\u0136\u012A\u013B\u0160\u0143\u0145\xD3\u014C\xD5\xD6\xD7\u0172\u0141\u015A\u016A\xDC\u017B\u017D\xDF\u0105\u012F\u0101\u0107\xE4\xE5\u0119\u0113\u010D\xE9\u017A\u0117\u0123\u0137\u012B\u013C\u0161\u0144\u0146\xF3\u014D\xF5\xF6\xF7\u0173\u0142\u015B\u016B\xFC\u017C\u017E\u2019"},cp28603:"iso885913",iso885914:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u1E02\u1E03\xA3\u010A\u010B\u1E0A\xA7\u1E80\xA9\u1E82\u1E0B\u1EF2\xAD\xAE\u0178\u1E1E\u1E1F\u0120\u0121\u1E40\u1E41\xB6\u1E56\u1E81\u1E57\u1E83\u1E60\u1EF3\u1E84\u1E85\u1E61\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\u0174\xD1\xD2\xD3\xD4\xD5\xD6\u1E6A\xD8\xD9\xDA\xDB\xDC\xDD\u0176\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\u0175\xF1\xF2\xF3\xF4\xF5\xF6\u1E6B\xF8\xF9\xFA\xFB\xFC\xFD\u0177\xFF"},cp28604:"iso885914",iso885915:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\u20AC\xA5\u0160\xA7\u0161\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\u017D\xB5\xB6\xB7\u017E\xB9\xBA\xBB\u0152\u0153\u0178\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"},cp28605:"iso885915",iso885916:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0104\u0105\u0141\u20AC\u201E\u0160\xA7\u0161\xA9\u0218\xAB\u0179\xAD\u017A\u017B\xB0\xB1\u010C\u0142\u017D\u201D\xB6\xB7\u017E\u010D\u0219\xBB\u0152\u0153\u0178\u017C\xC0\xC1\xC2\u0102\xC4\u0106\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\u0110\u0143\xD2\xD3\xD4\u0150\xD6\u015A\u0170\xD9\xDA\xDB\xDC\u0118\u021A\xDF\xE0\xE1\xE2\u0103\xE4\u0107\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\u0111\u0144\xF2\xF3\xF4\u0151\xF6\u015B\u0171\xF9\xFA\xFB\xFC\u0119\u021B\xFF"},cp28606:"iso885916",cp437:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ibm437:"cp437",csibm437:"cp437",cp737:{type:"_sbcs",chars:"\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039A\u039B\u039C\u039D\u039E\u039F\u03A0\u03A1\u03A3\u03A4\u03A5\u03A6\u03A7\u03A8\u03A9\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C3\u03C2\u03C4\u03C5\u03C6\u03C7\u03C8\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03C9\u03AC\u03AD\u03AE\u03CA\u03AF\u03CC\u03CD\u03CB\u03CE\u0386\u0388\u0389\u038A\u038C\u038E\u038F\xB1\u2265\u2264\u03AA\u03AB\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ibm737:"cp737",csibm737:"cp737",cp775:{type:"_sbcs",chars:"\u0106\xFC\xE9\u0101\xE4\u0123\xE5\u0107\u0142\u0113\u0156\u0157\u012B\u0179\xC4\xC5\xC9\xE6\xC6\u014D\xF6\u0122\xA2\u015A\u015B\xD6\xDC\xF8\xA3\xD8\xD7\xA4\u0100\u012A\xF3\u017B\u017C\u017A\u201D\xA6\xA9\xAE\xAC\xBD\xBC\u0141\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u0104\u010C\u0118\u0116\u2563\u2551\u2557\u255D\u012E\u0160\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u0172\u016A\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u017D\u0105\u010D\u0119\u0117\u012F\u0161\u0173\u016B\u017E\u2518\u250C\u2588\u2584\u258C\u2590\u2580\xD3\xDF\u014C\u0143\xF5\xD5\xB5\u0144\u0136\u0137\u013B\u013C\u0146\u0112\u0145\u2019\xAD\xB1\u201C\xBE\xB6\xA7\xF7\u201E\xB0\u2219\xB7\xB9\xB3\xB2\u25A0\xA0"},ibm775:"cp775",csibm775:"cp775",cp850:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xF8\xA3\xD8\xD7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\xAE\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\xC1\xC2\xC0\xA9\u2563\u2551\u2557\u255D\xA2\xA5\u2510\u2514\u2534\u252C\u251C\u2500\u253C\xE3\xC3\u255A\u2554\u2569\u2566\u2560\u2550\u256C\xA4\xF0\xD0\xCA\xCB\xC8\u0131\xCD\xCE\xCF\u2518\u250C\u2588\u2584\xA6\xCC\u2580\xD3\xDF\xD4\xD2\xF5\xD5\xB5\xFE\xDE\xDA\xDB\xD9\xFD\xDD\xAF\xB4\xAD\xB1\u2017\xBE\xB6\xA7\xF7\xB8\xB0\xA8\xB7\xB9\xB3\xB2\u25A0\xA0"},ibm850:"cp850",csibm850:"cp850",cp852:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE4\u016F\u0107\xE7\u0142\xEB\u0150\u0151\xEE\u0179\xC4\u0106\xC9\u0139\u013A\xF4\xF6\u013D\u013E\u015A\u015B\xD6\xDC\u0164\u0165\u0141\xD7\u010D\xE1\xED\xF3\xFA\u0104\u0105\u017D\u017E\u0118\u0119\xAC\u017A\u010C\u015F\xAB\xBB\u2591\u2592\u2593\u2502\u2524\xC1\xC2\u011A\u015E\u2563\u2551\u2557\u255D\u017B\u017C\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u0102\u0103\u255A\u2554\u2569\u2566\u2560\u2550\u256C\xA4\u0111\u0110\u010E\xCB\u010F\u0147\xCD\xCE\u011B\u2518\u250C\u2588\u2584\u0162\u016E\u2580\xD3\xDF\xD4\u0143\u0144\u0148\u0160\u0161\u0154\xDA\u0155\u0170\xFD\xDD\u0163\xB4\xAD\u02DD\u02DB\u02C7\u02D8\xA7\xF7\xB8\xB0\xA8\u02D9\u0171\u0158\u0159\u25A0\xA0"},ibm852:"cp852",csibm852:"cp852",cp855:{type:"_sbcs",chars:"\u0452\u0402\u0453\u0403\u0451\u0401\u0454\u0404\u0455\u0405\u0456\u0406\u0457\u0407\u0458\u0408\u0459\u0409\u045A\u040A\u045B\u040B\u045C\u040C\u045E\u040E\u045F\u040F\u044E\u042E\u044A\u042A\u0430\u0410\u0431\u0411\u0446\u0426\u0434\u0414\u0435\u0415\u0444\u0424\u0433\u0413\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u0445\u0425\u0438\u0418\u2563\u2551\u2557\u255D\u0439\u0419\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u043A\u041A\u255A\u2554\u2569\u2566\u2560\u2550\u256C\xA4\u043B\u041B\u043C\u041C\u043D\u041D\u043E\u041E\u043F\u2518\u250C\u2588\u2584\u041F\u044F\u2580\u042F\u0440\u0420\u0441\u0421\u0442\u0422\u0443\u0423\u0436\u0416\u0432\u0412\u044C\u042C\u2116\xAD\u044B\u042B\u0437\u0417\u0448\u0428\u044D\u042D\u0449\u0429\u0447\u0427\xA7\u25A0\xA0"},ibm855:"cp855",csibm855:"cp855",cp856:{type:"_sbcs",chars:"\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC\u05DD\u05DE\u05DF\u05E0\u05E1\u05E2\u05E3\u05E4\u05E5\u05E6\u05E7\u05E8\u05E9\u05EA\uFFFD\xA3\uFFFD\xD7\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\xAE\xAC\xBD\xBC\uFFFD\xAB\xBB\u2591\u2592\u2593\u2502\u2524\uFFFD\uFFFD\uFFFD\xA9\u2563\u2551\u2557\u255D\xA2\xA5\u2510\u2514\u2534\u252C\u251C\u2500\u253C\uFFFD\uFFFD\u255A\u2554\u2569\u2566\u2560\u2550\u256C\xA4\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2518\u250C\u2588\u2584\xA6\uFFFD\u2580\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\xB5\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\xAF\xB4\xAD\xB1\u2017\xBE\xB6\xA7\xF7\xB8\xB0\xA8\xB7\xB9\xB3\xB2\u25A0\xA0"},ibm856:"cp856",csibm856:"cp856",cp857:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\u0131\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\u0130\xD6\xDC\xF8\xA3\xD8\u015E\u015F\xE1\xED\xF3\xFA\xF1\xD1\u011E\u011F\xBF\xAE\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\xC1\xC2\xC0\xA9\u2563\u2551\u2557\u255D\xA2\xA5\u2510\u2514\u2534\u252C\u251C\u2500\u253C\xE3\xC3\u255A\u2554\u2569\u2566\u2560\u2550\u256C\xA4\xBA\xAA\xCA\xCB\xC8\uFFFD\xCD\xCE\xCF\u2518\u250C\u2588\u2584\xA6\xCC\u2580\xD3\xDF\xD4\xD2\xF5\xD5\xB5\uFFFD\xD7\xDA\xDB\xD9\xEC\xFF\xAF\xB4\xAD\xB1\uFFFD\xBE\xB6\xA7\xF7\xB8\xB0\xA8\xB7\xB9\xB3\xB2\u25A0\xA0"},ibm857:"cp857",csibm857:"cp857",cp858:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xF8\xA3\xD8\xD7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\xAE\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\xC1\xC2\xC0\xA9\u2563\u2551\u2557\u255D\xA2\xA5\u2510\u2514\u2534\u252C\u251C\u2500\u253C\xE3\xC3\u255A\u2554\u2569\u2566\u2560\u2550\u256C\xA4\xF0\xD0\xCA\xCB\xC8\u20AC\xCD\xCE\xCF\u2518\u250C\u2588\u2584\xA6\xCC\u2580\xD3\xDF\xD4\xD2\xF5\xD5\xB5\xFE\xDE\xDA\xDB\xD9\xFD\xDD\xAF\xB4\xAD\xB1\u2017\xBE\xB6\xA7\xF7\xB8\xB0\xA8\xB7\xB9\xB3\xB2\u25A0\xA0"},ibm858:"cp858",csibm858:"cp858",cp860:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE3\xE0\xC1\xE7\xEA\xCA\xE8\xCD\xD4\xEC\xC3\xC2\xC9\xC0\xC8\xF4\xF5\xF2\xDA\xF9\xCC\xD5\xDC\xA2\xA3\xD9\u20A7\xD3\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\xD2\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ibm860:"cp860",csibm860:"cp860",cp861:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xD0\xF0\xDE\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xFE\xFB\xDD\xFD\xD6\xDC\xF8\xA3\xD8\u20A7\u0192\xE1\xED\xF3\xFA\xC1\xCD\xD3\xDA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ibm861:"cp861",csibm861:"cp861",cp862:{type:"_sbcs",chars:"\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC\u05DD\u05DE\u05DF\u05E0\u05E1\u05E2\u05E3\u05E4\u05E5\u05E6\u05E7\u05E8\u05E9\u05EA\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ibm862:"cp862",csibm862:"cp862",cp863:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xC2\xE0\xB6\xE7\xEA\xEB\xE8\xEF\xEE\u2017\xC0\xA7\xC9\xC8\xCA\xF4\xCB\xCF\xFB\xF9\xA4\xD4\xDC\xA2\xA3\xD9\xDB\u0192\xA6\xB4\xF3\xFA\xA8\xB8\xB3\xAF\xCE\u2310\xAC\xBD\xBC\xBE\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ibm863:"cp863",csibm863:"cp863",cp864:{type:"_sbcs",chars:`\0\x07\b -\v\f\r\x1B !"#$\u066A&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~\x7F\xB0\xB7\u2219\u221A\u2592\u2500\u2502\u253C\u2524\u252C\u251C\u2534\u2510\u250C\u2514\u2518\u03B2\u221E\u03C6\xB1\xBD\xBC\u2248\xAB\xBB\uFEF7\uFEF8\uFFFD\uFFFD\uFEFB\uFEFC\uFFFD\xA0\xAD\uFE82\xA3\xA4\uFE84\uFFFD\uFFFD\uFE8E\uFE8F\uFE95\uFE99\u060C\uFE9D\uFEA1\uFEA5\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\uFED1\u061B\uFEB1\uFEB5\uFEB9\u061F\xA2\uFE80\uFE81\uFE83\uFE85\uFECA\uFE8B\uFE8D\uFE91\uFE93\uFE97\uFE9B\uFE9F\uFEA3\uFEA7\uFEA9\uFEAB\uFEAD\uFEAF\uFEB3\uFEB7\uFEBB\uFEBF\uFEC1\uFEC5\uFECB\uFECF\xA6\xAC\xF7\xD7\uFEC9\u0640\uFED3\uFED7\uFEDB\uFEDF\uFEE3\uFEE7\uFEEB\uFEED\uFEEF\uFEF3\uFEBD\uFECC\uFECE\uFECD\uFEE1\uFE7D\u0651\uFEE5\uFEE9\uFEEC\uFEF0\uFEF2\uFED0\uFED5\uFEF5\uFEF6\uFEDD\uFED9\uFEF1\u25A0\uFFFD`},ibm864:"cp864",csibm864:"cp864",cp865:{type:"_sbcs",chars:"\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xF8\xA3\xD8\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xA4\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"},ibm865:"cp865",csibm865:"cp865",cp866:{type:"_sbcs",chars:"\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F\u0401\u0451\u0404\u0454\u0407\u0457\u040E\u045E\xB0\u2219\xB7\u221A\u2116\xA4\u25A0\xA0"},ibm866:"cp866",csibm866:"cp866",cp869:{type:"_sbcs",chars:"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0386\uFFFD\xB7\xAC\xA6\u2018\u2019\u0388\u2015\u0389\u038A\u03AA\u038C\uFFFD\uFFFD\u038E\u03AB\xA9\u038F\xB2\xB3\u03AC\xA3\u03AD\u03AE\u03AF\u03CA\u0390\u03CC\u03CD\u0391\u0392\u0393\u0394\u0395\u0396\u0397\xBD\u0398\u0399\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u039A\u039B\u039C\u039D\u2563\u2551\u2557\u255D\u039E\u039F\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u03A0\u03A1\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u03A3\u03A4\u03A5\u03A6\u03A7\u03A8\u03A9\u03B1\u03B2\u03B3\u2518\u250C\u2588\u2584\u03B4\u03B5\u2580\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C3\u03C2\u03C4\u0384\xAD\xB1\u03C5\u03C6\u03C7\xA7\u03C8\u0385\xB0\xA8\u03C9\u03CB\u03B0\u03CE\u25A0\xA0"},ibm869:"cp869",csibm869:"cp869",cp922:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\u203E\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\u0160\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\u017D\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\u0161\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\u017E\xFF"},ibm922:"cp922",csibm922:"cp922",cp1046:{type:"_sbcs",chars:"\uFE88\xD7\xF7\uF8F6\uF8F5\uF8F4\uF8F7\uFE71\x88\u25A0\u2502\u2500\u2510\u250C\u2514\u2518\uFE79\uFE7B\uFE7D\uFE7F\uFE77\uFE8A\uFEF0\uFEF3\uFEF2\uFECE\uFECF\uFED0\uFEF6\uFEF8\uFEFA\uFEFC\xA0\uF8FA\uF8F9\uF8F8\xA4\uF8FB\uFE8B\uFE91\uFE97\uFE9B\uFE9F\uFEA3\u060C\xAD\uFEA7\uFEB3\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\uFEB7\u061B\uFEBB\uFEBF\uFECA\u061F\uFECB\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062A\u062B\u062C\u062D\u062E\u062F\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\uFEC7\u0639\u063A\uFECC\uFE82\uFE84\uFE8E\uFED3\u0640\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064A\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652\uFED7\uFEDB\uFEDF\uF8FC\uFEF5\uFEF7\uFEF9\uFEFB\uFEE3\uFEE7\uFEEC\uFEE9\uFFFD"},ibm1046:"cp1046",csibm1046:"cp1046",cp1124:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0401\u0402\u0490\u0404\u0405\u0406\u0407\u0408\u0409\u040A\u040B\u040C\xAD\u040E\u040F\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F\u2116\u0451\u0452\u0491\u0454\u0455\u0456\u0457\u0458\u0459\u045A\u045B\u045C\xA7\u045E\u045F"},ibm1124:"cp1124",csibm1124:"cp1124",cp1125:{type:"_sbcs",chars:"\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F\u0401\u0451\u0490\u0491\u0404\u0454\u0406\u0456\u0407\u0457\xB7\u221A\u2116\xA4\u25A0\xA0"},ibm1125:"cp1125",csibm1125:"cp1125",cp1129:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\u0153\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\u0178\xB5\xB6\xB7\u0152\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\u0102\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\u0300\xCD\xCE\xCF\u0110\xD1\u0309\xD3\xD4\u01A0\xD6\xD7\xD8\xD9\xDA\xDB\xDC\u01AF\u0303\xDF\xE0\xE1\xE2\u0103\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\u0301\xED\xEE\xEF\u0111\xF1\u0323\xF3\xF4\u01A1\xF6\xF7\xF8\xF9\xFA\xFB\xFC\u01B0\u20AB\xFF"},ibm1129:"cp1129",csibm1129:"cp1129",cp1133:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0E81\u0E82\u0E84\u0E87\u0E88\u0EAA\u0E8A\u0E8D\u0E94\u0E95\u0E96\u0E97\u0E99\u0E9A\u0E9B\u0E9C\u0E9D\u0E9E\u0E9F\u0EA1\u0EA2\u0EA3\u0EA5\u0EA7\u0EAB\u0EAD\u0EAE\uFFFD\uFFFD\uFFFD\u0EAF\u0EB0\u0EB2\u0EB3\u0EB4\u0EB5\u0EB6\u0EB7\u0EB8\u0EB9\u0EBC\u0EB1\u0EBB\u0EBD\uFFFD\uFFFD\uFFFD\u0EC0\u0EC1\u0EC2\u0EC3\u0EC4\u0EC8\u0EC9\u0ECA\u0ECB\u0ECC\u0ECD\u0EC6\uFFFD\u0EDC\u0EDD\u20AD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0ED0\u0ED1\u0ED2\u0ED3\u0ED4\u0ED5\u0ED6\u0ED7\u0ED8\u0ED9\uFFFD\uFFFD\xA2\xAC\xA6\uFFFD"},ibm1133:"cp1133",csibm1133:"cp1133",cp1161:{type:"_sbcs",chars:"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0E48\u0E01\u0E02\u0E03\u0E04\u0E05\u0E06\u0E07\u0E08\u0E09\u0E0A\u0E0B\u0E0C\u0E0D\u0E0E\u0E0F\u0E10\u0E11\u0E12\u0E13\u0E14\u0E15\u0E16\u0E17\u0E18\u0E19\u0E1A\u0E1B\u0E1C\u0E1D\u0E1E\u0E1F\u0E20\u0E21\u0E22\u0E23\u0E24\u0E25\u0E26\u0E27\u0E28\u0E29\u0E2A\u0E2B\u0E2C\u0E2D\u0E2E\u0E2F\u0E30\u0E31\u0E32\u0E33\u0E34\u0E35\u0E36\u0E37\u0E38\u0E39\u0E3A\u0E49\u0E4A\u0E4B\u20AC\u0E3F\u0E40\u0E41\u0E42\u0E43\u0E44\u0E45\u0E46\u0E47\u0E48\u0E49\u0E4A\u0E4B\u0E4C\u0E4D\u0E4E\u0E4F\u0E50\u0E51\u0E52\u0E53\u0E54\u0E55\u0E56\u0E57\u0E58\u0E59\u0E5A\u0E5B\xA2\xAC\xA6\xA0"},ibm1161:"cp1161",csibm1161:"cp1161",cp1162:{type:"_sbcs",chars:"\u20AC\x81\x82\x83\x84\u2026\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\u2018\u2019\u201C\u201D\u2022\u2013\u2014\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\u0E01\u0E02\u0E03\u0E04\u0E05\u0E06\u0E07\u0E08\u0E09\u0E0A\u0E0B\u0E0C\u0E0D\u0E0E\u0E0F\u0E10\u0E11\u0E12\u0E13\u0E14\u0E15\u0E16\u0E17\u0E18\u0E19\u0E1A\u0E1B\u0E1C\u0E1D\u0E1E\u0E1F\u0E20\u0E21\u0E22\u0E23\u0E24\u0E25\u0E26\u0E27\u0E28\u0E29\u0E2A\u0E2B\u0E2C\u0E2D\u0E2E\u0E2F\u0E30\u0E31\u0E32\u0E33\u0E34\u0E35\u0E36\u0E37\u0E38\u0E39\u0E3A\uFFFD\uFFFD\uFFFD\uFFFD\u0E3F\u0E40\u0E41\u0E42\u0E43\u0E44\u0E45\u0E46\u0E47\u0E48\u0E49\u0E4A\u0E4B\u0E4C\u0E4D\u0E4E\u0E4F\u0E50\u0E51\u0E52\u0E53\u0E54\u0E55\u0E56\u0E57\u0E58\u0E59\u0E5A\u0E5B\uFFFD\uFFFD\uFFFD\uFFFD"},ibm1162:"cp1162",csibm1162:"cp1162",cp1163:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\u20AC\xA5\xA6\xA7\u0153\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\u0178\xB5\xB6\xB7\u0152\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\u0102\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\u0300\xCD\xCE\xCF\u0110\xD1\u0309\xD3\xD4\u01A0\xD6\xD7\xD8\xD9\xDA\xDB\xDC\u01AF\u0303\xDF\xE0\xE1\xE2\u0103\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\u0301\xED\xEE\xEF\u0111\xF1\u0323\xF3\xF4\u01A1\xF6\xF7\xF8\xF9\xFA\xFB\xFC\u01B0\u20AB\xFF"},ibm1163:"cp1163",csibm1163:"cp1163",maccroatian:{type:"_sbcs",chars:"\xC4\xC5\xC7\xC9\xD1\xD6\xDC\xE1\xE0\xE2\xE4\xE3\xE5\xE7\xE9\xE8\xEA\xEB\xED\xEC\xEE\xEF\xF1\xF3\xF2\xF4\xF6\xF5\xFA\xF9\xFB\xFC\u2020\xB0\xA2\xA3\xA7\u2022\xB6\xDF\xAE\u0160\u2122\xB4\xA8\u2260\u017D\xD8\u221E\xB1\u2264\u2265\u2206\xB5\u2202\u2211\u220F\u0161\u222B\xAA\xBA\u2126\u017E\xF8\xBF\xA1\xAC\u221A\u0192\u2248\u0106\xAB\u010C\u2026\xA0\xC0\xC3\xD5\u0152\u0153\u0110\u2014\u201C\u201D\u2018\u2019\xF7\u25CA\uFFFD\xA9\u2044\xA4\u2039\u203A\xC6\xBB\u2013\xB7\u201A\u201E\u2030\xC2\u0107\xC1\u010D\xC8\xCD\xCE\xCF\xCC\xD3\xD4\u0111\xD2\xDA\xDB\xD9\u0131\u02C6\u02DC\xAF\u03C0\xCB\u02DA\xB8\xCA\xE6\u02C7"},maccyrillic:{type:"_sbcs",chars:"\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u2020\xB0\xA2\xA3\xA7\u2022\xB6\u0406\xAE\xA9\u2122\u0402\u0452\u2260\u0403\u0453\u221E\xB1\u2264\u2265\u0456\xB5\u2202\u0408\u0404\u0454\u0407\u0457\u0409\u0459\u040A\u045A\u0458\u0405\xAC\u221A\u0192\u2248\u2206\xAB\xBB\u2026\xA0\u040B\u045B\u040C\u045C\u0455\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u201E\u040E\u045E\u040F\u045F\u2116\u0401\u0451\u044F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\xA4"},macgreek:{type:"_sbcs",chars:"\xC4\xB9\xB2\xC9\xB3\xD6\xDC\u0385\xE0\xE2\xE4\u0384\xA8\xE7\xE9\xE8\xEA\xEB\xA3\u2122\xEE\xEF\u2022\xBD\u2030\xF4\xF6\xA6\xAD\xF9\xFB\xFC\u2020\u0393\u0394\u0398\u039B\u039E\u03A0\xDF\xAE\xA9\u03A3\u03AA\xA7\u2260\xB0\u0387\u0391\xB1\u2264\u2265\xA5\u0392\u0395\u0396\u0397\u0399\u039A\u039C\u03A6\u03AB\u03A8\u03A9\u03AC\u039D\xAC\u039F\u03A1\u2248\u03A4\xAB\xBB\u2026\xA0\u03A5\u03A7\u0386\u0388\u0153\u2013\u2015\u201C\u201D\u2018\u2019\xF7\u0389\u038A\u038C\u038E\u03AD\u03AE\u03AF\u03CC\u038F\u03CD\u03B1\u03B2\u03C8\u03B4\u03B5\u03C6\u03B3\u03B7\u03B9\u03BE\u03BA\u03BB\u03BC\u03BD\u03BF\u03C0\u03CE\u03C1\u03C3\u03C4\u03B8\u03C9\u03C2\u03C7\u03C5\u03B6\u03CA\u03CB\u0390\u03B0\uFFFD"},maciceland:{type:"_sbcs",chars:"\xC4\xC5\xC7\xC9\xD1\xD6\xDC\xE1\xE0\xE2\xE4\xE3\xE5\xE7\xE9\xE8\xEA\xEB\xED\xEC\xEE\xEF\xF1\xF3\xF2\xF4\xF6\xF5\xFA\xF9\xFB\xFC\xDD\xB0\xA2\xA3\xA7\u2022\xB6\xDF\xAE\xA9\u2122\xB4\xA8\u2260\xC6\xD8\u221E\xB1\u2264\u2265\xA5\xB5\u2202\u2211\u220F\u03C0\u222B\xAA\xBA\u2126\xE6\xF8\xBF\xA1\xAC\u221A\u0192\u2248\u2206\xAB\xBB\u2026\xA0\xC0\xC3\xD5\u0152\u0153\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u25CA\xFF\u0178\u2044\xA4\xD0\xF0\xDE\xFE\xFD\xB7\u201A\u201E\u2030\xC2\xCA\xC1\xCB\xC8\xCD\xCE\xCF\xCC\xD3\xD4\uFFFD\xD2\xDA\xDB\xD9\u0131\u02C6\u02DC\xAF\u02D8\u02D9\u02DA\xB8\u02DD\u02DB\u02C7"},macroman:{type:"_sbcs",chars:"\xC4\xC5\xC7\xC9\xD1\xD6\xDC\xE1\xE0\xE2\xE4\xE3\xE5\xE7\xE9\xE8\xEA\xEB\xED\xEC\xEE\xEF\xF1\xF3\xF2\xF4\xF6\xF5\xFA\xF9\xFB\xFC\u2020\xB0\xA2\xA3\xA7\u2022\xB6\xDF\xAE\xA9\u2122\xB4\xA8\u2260\xC6\xD8\u221E\xB1\u2264\u2265\xA5\xB5\u2202\u2211\u220F\u03C0\u222B\xAA\xBA\u2126\xE6\xF8\xBF\xA1\xAC\u221A\u0192\u2248\u2206\xAB\xBB\u2026\xA0\xC0\xC3\xD5\u0152\u0153\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u25CA\xFF\u0178\u2044\xA4\u2039\u203A\uFB01\uFB02\u2021\xB7\u201A\u201E\u2030\xC2\xCA\xC1\xCB\xC8\xCD\xCE\xCF\xCC\xD3\xD4\uFFFD\xD2\xDA\xDB\xD9\u0131\u02C6\u02DC\xAF\u02D8\u02D9\u02DA\xB8\u02DD\u02DB\u02C7"},macromania:{type:"_sbcs",chars:"\xC4\xC5\xC7\xC9\xD1\xD6\xDC\xE1\xE0\xE2\xE4\xE3\xE5\xE7\xE9\xE8\xEA\xEB\xED\xEC\xEE\xEF\xF1\xF3\xF2\xF4\xF6\xF5\xFA\xF9\xFB\xFC\u2020\xB0\xA2\xA3\xA7\u2022\xB6\xDF\xAE\xA9\u2122\xB4\xA8\u2260\u0102\u015E\u221E\xB1\u2264\u2265\xA5\xB5\u2202\u2211\u220F\u03C0\u222B\xAA\xBA\u2126\u0103\u015F\xBF\xA1\xAC\u221A\u0192\u2248\u2206\xAB\xBB\u2026\xA0\xC0\xC3\xD5\u0152\u0153\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u25CA\xFF\u0178\u2044\xA4\u2039\u203A\u0162\u0163\u2021\xB7\u201A\u201E\u2030\xC2\xCA\xC1\xCB\xC8\xCD\xCE\xCF\xCC\xD3\xD4\uFFFD\xD2\xDA\xDB\xD9\u0131\u02C6\u02DC\xAF\u02D8\u02D9\u02DA\xB8\u02DD\u02DB\u02C7"},macthai:{type:"_sbcs",chars:"\xAB\xBB\u2026\uF88C\uF88F\uF892\uF895\uF898\uF88B\uF88E\uF891\uF894\uF897\u201C\u201D\uF899\uFFFD\u2022\uF884\uF889\uF885\uF886\uF887\uF888\uF88A\uF88D\uF890\uF893\uF896\u2018\u2019\uFFFD\xA0\u0E01\u0E02\u0E03\u0E04\u0E05\u0E06\u0E07\u0E08\u0E09\u0E0A\u0E0B\u0E0C\u0E0D\u0E0E\u0E0F\u0E10\u0E11\u0E12\u0E13\u0E14\u0E15\u0E16\u0E17\u0E18\u0E19\u0E1A\u0E1B\u0E1C\u0E1D\u0E1E\u0E1F\u0E20\u0E21\u0E22\u0E23\u0E24\u0E25\u0E26\u0E27\u0E28\u0E29\u0E2A\u0E2B\u0E2C\u0E2D\u0E2E\u0E2F\u0E30\u0E31\u0E32\u0E33\u0E34\u0E35\u0E36\u0E37\u0E38\u0E39\u0E3A\uFEFF\u200B\u2013\u2014\u0E3F\u0E40\u0E41\u0E42\u0E43\u0E44\u0E45\u0E46\u0E47\u0E48\u0E49\u0E4A\u0E4B\u0E4C\u0E4D\u2122\u0E4F\u0E50\u0E51\u0E52\u0E53\u0E54\u0E55\u0E56\u0E57\u0E58\u0E59\xAE\xA9\uFFFD\uFFFD\uFFFD\uFFFD"},macturkish:{type:"_sbcs",chars:"\xC4\xC5\xC7\xC9\xD1\xD6\xDC\xE1\xE0\xE2\xE4\xE3\xE5\xE7\xE9\xE8\xEA\xEB\xED\xEC\xEE\xEF\xF1\xF3\xF2\xF4\xF6\xF5\xFA\xF9\xFB\xFC\u2020\xB0\xA2\xA3\xA7\u2022\xB6\xDF\xAE\xA9\u2122\xB4\xA8\u2260\xC6\xD8\u221E\xB1\u2264\u2265\xA5\xB5\u2202\u2211\u220F\u03C0\u222B\xAA\xBA\u2126\xE6\xF8\xBF\xA1\xAC\u221A\u0192\u2248\u2206\xAB\xBB\u2026\xA0\xC0\xC3\xD5\u0152\u0153\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u25CA\xFF\u0178\u011E\u011F\u0130\u0131\u015E\u015F\u2021\xB7\u201A\u201E\u2030\xC2\xCA\xC1\xCB\xC8\xCD\xCE\xCF\xCC\xD3\xD4\uFFFD\xD2\xDA\xDB\xD9\uFFFD\u02C6\u02DC\xAF\u02D8\u02D9\u02DA\xB8\u02DD\u02DB\u02C7"},macukraine:{type:"_sbcs",chars:"\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u2020\xB0\u0490\xA3\xA7\u2022\xB6\u0406\xAE\xA9\u2122\u0402\u0452\u2260\u0403\u0453\u221E\xB1\u2264\u2265\u0456\xB5\u0491\u0408\u0404\u0454\u0407\u0457\u0409\u0459\u040A\u045A\u0458\u0405\xAC\u221A\u0192\u2248\u2206\xAB\xBB\u2026\xA0\u040B\u045B\u040C\u045C\u0455\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u201E\u040E\u045E\u040F\u045F\u2116\u0401\u0451\u044F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\xA4"},koi8r:{type:"_sbcs",chars:"\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2580\u2584\u2588\u258C\u2590\u2591\u2592\u2593\u2320\u25A0\u2219\u221A\u2248\u2264\u2265\xA0\u2321\xB0\xB2\xB7\xF7\u2550\u2551\u2552\u0451\u2553\u2554\u2555\u2556\u2557\u2558\u2559\u255A\u255B\u255C\u255D\u255E\u255F\u2560\u2561\u0401\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569\u256A\u256B\u256C\xA9\u044E\u0430\u0431\u0446\u0434\u0435\u0444\u0433\u0445\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u044F\u0440\u0441\u0442\u0443\u0436\u0432\u044C\u044B\u0437\u0448\u044D\u0449\u0447\u044A\u042E\u0410\u0411\u0426\u0414\u0415\u0424\u0413\u0425\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u042F\u0420\u0421\u0422\u0423\u0416\u0412\u042C\u042B\u0417\u0428\u042D\u0429\u0427\u042A"},koi8u:{type:"_sbcs",chars:"\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2580\u2584\u2588\u258C\u2590\u2591\u2592\u2593\u2320\u25A0\u2219\u221A\u2248\u2264\u2265\xA0\u2321\xB0\xB2\xB7\xF7\u2550\u2551\u2552\u0451\u0454\u2554\u0456\u0457\u2557\u2558\u2559\u255A\u255B\u0491\u255D\u255E\u255F\u2560\u2561\u0401\u0404\u2563\u0406\u0407\u2566\u2567\u2568\u2569\u256A\u0490\u256C\xA9\u044E\u0430\u0431\u0446\u0434\u0435\u0444\u0433\u0445\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u044F\u0440\u0441\u0442\u0443\u0436\u0432\u044C\u044B\u0437\u0448\u044D\u0449\u0447\u044A\u042E\u0410\u0411\u0426\u0414\u0415\u0424\u0413\u0425\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u042F\u0420\u0421\u0422\u0423\u0416\u0412\u042C\u042B\u0417\u0428\u042D\u0429\u0427\u042A"},koi8ru:{type:"_sbcs",chars:"\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2580\u2584\u2588\u258C\u2590\u2591\u2592\u2593\u2320\u25A0\u2219\u221A\u2248\u2264\u2265\xA0\u2321\xB0\xB2\xB7\xF7\u2550\u2551\u2552\u0451\u0454\u2554\u0456\u0457\u2557\u2558\u2559\u255A\u255B\u0491\u045E\u255E\u255F\u2560\u2561\u0401\u0404\u2563\u0406\u0407\u2566\u2567\u2568\u2569\u256A\u0490\u040E\xA9\u044E\u0430\u0431\u0446\u0434\u0435\u0444\u0433\u0445\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u044F\u0440\u0441\u0442\u0443\u0436\u0432\u044C\u044B\u0437\u0448\u044D\u0449\u0447\u044A\u042E\u0410\u0411\u0426\u0414\u0415\u0424\u0413\u0425\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u042F\u0420\u0421\u0422\u0423\u0416\u0412\u042C\u042B\u0417\u0428\u042D\u0429\u0427\u042A"},koi8t:{type:"_sbcs",chars:"\u049B\u0493\u201A\u0492\u201E\u2026\u2020\u2021\uFFFD\u2030\u04B3\u2039\u04B2\u04B7\u04B6\uFFFD\u049A\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\u2122\uFFFD\u203A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04EF\u04EE\u0451\xA4\u04E3\xA6\xA7\uFFFD\uFFFD\uFFFD\xAB\xAC\xAD\xAE\uFFFD\xB0\xB1\xB2\u0401\uFFFD\u04E2\xB6\xB7\uFFFD\u2116\uFFFD\xBB\uFFFD\uFFFD\uFFFD\xA9\u044E\u0430\u0431\u0446\u0434\u0435\u0444\u0433\u0445\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u044F\u0440\u0441\u0442\u0443\u0436\u0432\u044C\u044B\u0437\u0448\u044D\u0449\u0447\u044A\u042E\u0410\u0411\u0426\u0414\u0415\u0424\u0413\u0425\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u042F\u0420\u0421\u0422\u0423\u0416\u0412\u042C\u042B\u0417\u0428\u042D\u0429\u0427\u042A"},armscii8:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\uFFFD\u0587\u0589)(\xBB\xAB\u2014.\u055D,-\u058A\u2026\u055C\u055B\u055E\u0531\u0561\u0532\u0562\u0533\u0563\u0534\u0564\u0535\u0565\u0536\u0566\u0537\u0567\u0538\u0568\u0539\u0569\u053A\u056A\u053B\u056B\u053C\u056C\u053D\u056D\u053E\u056E\u053F\u056F\u0540\u0570\u0541\u0571\u0542\u0572\u0543\u0573\u0544\u0574\u0545\u0575\u0546\u0576\u0547\u0577\u0548\u0578\u0549\u0579\u054A\u057A\u054B\u057B\u054C\u057C\u054D\u057D\u054E\u057E\u054F\u057F\u0550\u0580\u0551\u0581\u0552\u0582\u0553\u0583\u0554\u0584\u0555\u0585\u0556\u0586\u055A\uFFFD"},rk1048:{type:"_sbcs",chars:"\u0402\u0403\u201A\u0453\u201E\u2026\u2020\u2021\u20AC\u2030\u0409\u2039\u040A\u049A\u04BA\u040F\u0452\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\u2122\u0459\u203A\u045A\u049B\u04BB\u045F\xA0\u04B0\u04B1\u04D8\xA4\u04E8\xA6\xA7\u0401\xA9\u0492\xAB\xAC\xAD\xAE\u04AE\xB0\xB1\u0406\u0456\u04E9\xB5\xB6\xB7\u0451\u2116\u0493\xBB\u04D9\u04A2\u04A3\u04AF\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F"},tcvn:{type:"_sbcs",chars:`\0\xDA\u1EE4\u1EEA\u1EEC\u1EEE\x07\b -\v\f\r\u1EE8\u1EF0\u1EF2\u1EF6\u1EF8\xDD\u1EF4\x1B !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~\x7F\xC0\u1EA2\xC3\xC1\u1EA0\u1EB6\u1EAC\xC8\u1EBA\u1EBC\xC9\u1EB8\u1EC6\xCC\u1EC8\u0128\xCD\u1ECA\xD2\u1ECE\xD5\xD3\u1ECC\u1ED8\u1EDC\u1EDE\u1EE0\u1EDA\u1EE2\xD9\u1EE6\u0168\xA0\u0102\xC2\xCA\xD4\u01A0\u01AF\u0110\u0103\xE2\xEA\xF4\u01A1\u01B0\u0111\u1EB0\u0300\u0309\u0303\u0301\u0323\xE0\u1EA3\xE3\xE1\u1EA1\u1EB2\u1EB1\u1EB3\u1EB5\u1EAF\u1EB4\u1EAE\u1EA6\u1EA8\u1EAA\u1EA4\u1EC0\u1EB7\u1EA7\u1EA9\u1EAB\u1EA5\u1EAD\xE8\u1EC2\u1EBB\u1EBD\xE9\u1EB9\u1EC1\u1EC3\u1EC5\u1EBF\u1EC7\xEC\u1EC9\u1EC4\u1EBE\u1ED2\u0129\xED\u1ECB\xF2\u1ED4\u1ECF\xF5\xF3\u1ECD\u1ED3\u1ED5\u1ED7\u1ED1\u1ED9\u1EDD\u1EDF\u1EE1\u1EDB\u1EE3\xF9\u1ED6\u1EE7\u0169\xFA\u1EE5\u1EEB\u1EED\u1EEF\u1EE9\u1EF1\u1EF3\u1EF7\u1EF9\xFD\u1EF5\u1ED0`},georgianacademy:{type:"_sbcs",chars:"\x80\x81\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\u0160\u2039\u0152\x8D\x8E\x8F\x90\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u02DC\u2122\u0161\u203A\u0153\x9D\x9E\u0178\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\u10D0\u10D1\u10D2\u10D3\u10D4\u10D5\u10D6\u10D7\u10D8\u10D9\u10DA\u10DB\u10DC\u10DD\u10DE\u10DF\u10E0\u10E1\u10E2\u10E3\u10E4\u10E5\u10E6\u10E7\u10E8\u10E9\u10EA\u10EB\u10EC\u10ED\u10EE\u10EF\u10F0\u10F1\u10F2\u10F3\u10F4\u10F5\u10F6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"},georgianps:{type:"_sbcs",chars:"\x80\x81\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\u0160\u2039\u0152\x8D\x8E\x8F\x90\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u02DC\u2122\u0161\u203A\u0153\x9D\x9E\u0178\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\u10D0\u10D1\u10D2\u10D3\u10D4\u10D5\u10D6\u10F1\u10D7\u10D8\u10D9\u10DA\u10DB\u10DC\u10F2\u10DD\u10DE\u10DF\u10E0\u10E1\u10E2\u10F3\u10E3\u10E4\u10E5\u10E6\u10E7\u10E8\u10E9\u10EA\u10EB\u10EC\u10ED\u10EE\u10F4\u10EF\u10F0\u10F5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"},pt154:{type:"_sbcs",chars:"\u0496\u0492\u04EE\u0493\u201E\u2026\u04B6\u04AE\u04B2\u04AF\u04A0\u04E2\u04A2\u049A\u04BA\u04B8\u0497\u2018\u2019\u201C\u201D\u2022\u2013\u2014\u04B3\u04B7\u04A1\u04E3\u04A3\u049B\u04BB\u04B9\xA0\u040E\u045E\u0408\u04E8\u0498\u04B0\xA7\u0401\xA9\u04D8\xAB\xAC\u04EF\xAE\u049C\xB0\u04B1\u0406\u0456\u0499\u04E9\xB6\xB7\u0451\u2116\u04D9\xBB\u0458\u04AA\u04AB\u049D\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F"},viscii:{type:"_sbcs",chars:`\0\u1EB2\u1EB4\u1EAA\x07\b -\v\f\r\u1EF6\u1EF8\x1B\u1EF4 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~\x7F\u1EA0\u1EAE\u1EB0\u1EB6\u1EA4\u1EA6\u1EA8\u1EAC\u1EBC\u1EB8\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EE2\u1EDA\u1EDC\u1EDE\u1ECA\u1ECE\u1ECC\u1EC8\u1EE6\u0168\u1EE4\u1EF2\xD5\u1EAF\u1EB1\u1EB7\u1EA5\u1EA7\u1EA9\u1EAD\u1EBD\u1EB9\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1ED1\u1ED3\u1ED5\u1ED7\u1EE0\u01A0\u1ED9\u1EDD\u1EDF\u1ECB\u1EF0\u1EE8\u1EEA\u1EEC\u01A1\u1EDB\u01AF\xC0\xC1\xC2\xC3\u1EA2\u0102\u1EB3\u1EB5\xC8\xC9\xCA\u1EBA\xCC\xCD\u0128\u1EF3\u0110\u1EE9\xD2\xD3\xD4\u1EA1\u1EF7\u1EEB\u1EED\xD9\xDA\u1EF9\u1EF5\xDD\u1EE1\u01B0\xE0\xE1\xE2\xE3\u1EA3\u0103\u1EEF\u1EAB\xE8\xE9\xEA\u1EBB\xEC\xED\u0129\u1EC9\u0111\u1EF1\xF2\xF3\xF4\xF5\u1ECF\u1ECD\u1EE5\xF9\xFA\u0169\u1EE7\xFD\u1EE3\u1EEE`},iso646cn:{type:"_sbcs",chars:`\0\x07\b -\v\f\r\x1B !"#\xA5%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}\u203E\x7F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD`},iso646jp:{type:"_sbcs",chars:`\0\x07\b -\v\f\r\x1B !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\xA5]^_\`abcdefghijklmnopqrstuvwxyz{|}\u203E\x7F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD`},hproman8:{type:"_sbcs",chars:"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xC0\xC2\xC8\xCA\xCB\xCE\xCF\xB4\u02CB\u02C6\xA8\u02DC\xD9\xDB\u20A4\xAF\xDD\xFD\xB0\xC7\xE7\xD1\xF1\xA1\xBF\xA4\xA3\xA5\xA7\u0192\xA2\xE2\xEA\xF4\xFB\xE1\xE9\xF3\xFA\xE0\xE8\xF2\xF9\xE4\xEB\xF6\xFC\xC5\xEE\xD8\xC6\xE5\xED\xF8\xE6\xC4\xEC\xD6\xDC\xC9\xEF\xDF\xD4\xC1\xC3\xE3\xD0\xF0\xCD\xCC\xD3\xD2\xD5\xF5\u0160\u0161\xDA\u0178\xFF\xDE\xFE\xB7\xB5\xB6\xBE\u2014\xBC\xBD\xAA\xBA\xAB\u25A0\xBB\xB1\uFFFD"},macintosh:{type:"_sbcs",chars:"\xC4\xC5\xC7\xC9\xD1\xD6\xDC\xE1\xE0\xE2\xE4\xE3\xE5\xE7\xE9\xE8\xEA\xEB\xED\xEC\xEE\xEF\xF1\xF3\xF2\xF4\xF6\xF5\xFA\xF9\xFB\xFC\u2020\xB0\xA2\xA3\xA7\u2022\xB6\xDF\xAE\xA9\u2122\xB4\xA8\u2260\xC6\xD8\u221E\xB1\u2264\u2265\xA5\xB5\u2202\u2211\u220F\u03C0\u222B\xAA\xBA\u2126\xE6\xF8\xBF\xA1\xAC\u221A\u0192\u2248\u2206\xAB\xBB\u2026\xA0\xC0\xC3\xD5\u0152\u0153\u2013\u2014\u201C\u201D\u2018\u2019\xF7\u25CA\xFF\u0178\u2044\xA4\u2039\u203A\uFB01\uFB02\u2021\xB7\u201A\u201E\u2030\xC2\xCA\xC1\xCB\xC8\xCD\xCE\xCF\xCC\xD3\xD4\uFFFD\xD2\xDA\xDB\xD9\u0131\u02C6\u02DC\xAF\u02D8\u02D9\u02DA\xB8\u02DD\u02DB\u02C7"},ascii:{type:"_sbcs",chars:"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"},tis620:{type:"_sbcs",chars:"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0E01\u0E02\u0E03\u0E04\u0E05\u0E06\u0E07\u0E08\u0E09\u0E0A\u0E0B\u0E0C\u0E0D\u0E0E\u0E0F\u0E10\u0E11\u0E12\u0E13\u0E14\u0E15\u0E16\u0E17\u0E18\u0E19\u0E1A\u0E1B\u0E1C\u0E1D\u0E1E\u0E1F\u0E20\u0E21\u0E22\u0E23\u0E24\u0E25\u0E26\u0E27\u0E28\u0E29\u0E2A\u0E2B\u0E2C\u0E2D\u0E2E\u0E2F\u0E30\u0E31\u0E32\u0E33\u0E34\u0E35\u0E36\u0E37\u0E38\u0E39\u0E3A\uFFFD\uFFFD\uFFFD\uFFFD\u0E3F\u0E40\u0E41\u0E42\u0E43\u0E44\u0E45\u0E46\u0E47\u0E48\u0E49\u0E4A\u0E4B\u0E4C\u0E4D\u0E4E\u0E4F\u0E50\u0E51\u0E52\u0E53\u0E54\u0E55\u0E56\u0E57\u0E58\u0E59\u0E5A\u0E5B\uFFFD\uFFFD\uFFFD\uFFFD"}}});var Gje=S(Bje=>{"use strict";var yK=Ll().Buffer;Bje._dbcs=jl;var Do=-1,qje=-2,$a=-10,$i=-1e3,uS=new Array(256),D0=-1;for(gD=0;gD<256;gD++)uS[gD]=Do;var gD;function jl(t,e){if(this.encodingName=t.encodingName,!t)throw new Error("DBCS codec is called without the data.");if(!t.table)throw new Error("Encoding '"+this.encodingName+"' has no data.");var r=t.table();this.decodeTables=[],this.decodeTables[0]=uS.slice(0),this.decodeTableSeq=[];for(var n=0;n$i)throw new Error("gb18030 decode tables conflict at byte 2");for(var u=this.decodeTables[$i-a[c]],p=129;p<=254;p++){if(u[p]===Do)u[p]=$i-i;else{if(u[p]===$i-i)continue;if(u[p]>$i)throw new Error("gb18030 decode tables conflict at byte 3")}for(var f=this.decodeTables[$i-u[p]],m=48;m<=57;m++)f[m]===Do&&(f[m]=qje)}}}this.defaultCharUnicode=e.defaultCharUnicode,this.encodeTable=[],this.encodeTableSeq=[];var h={};if(t.encodeSkipVals)for(var n=0;n0;t>>>=8)e.push(t&255);e.length==0&&e.push(0);for(var r=this.decodeTables[0],n=e.length-1;n>0;n--){var o=r[e[n]];if(o==Do)r[e[n]]=$i-this.decodeTables.length,this.decodeTables.push(r=uS.slice(0));else if(o<=$i)r=this.decodeTables[$i-o];else throw new Error("Overwrite byte in "+this.encodingName+", addr: "+t.toString(16))}return r};jl.prototype._addDecodeChunk=function(t){var e=parseInt(t[0],16),r=this._getDecodeTrieNode(e);e=e&255;for(var n=1;n=55296&&s<56320){var a=o.charCodeAt(i++);if(a>=56320&&a<57344)r[e++]=65536+(s-55296)*1024+(a-56320);else throw new Error("Incorrect surrogate pair in "+this.encodingName+" at chunk "+t[0])}else if(s>4080&&s<=4095){for(var c=4095-s+2,u=[],p=0;p255)throw new Error("Incorrect chunk in "+this.encodingName+" at addr "+t[0]+": too long"+e)};jl.prototype._getEncodeBucket=function(t){var e=t>>8;return this.encodeTable[e]===void 0&&(this.encodeTable[e]=uS.slice(0)),this.encodeTable[e]};jl.prototype._setEncodeChar=function(t,e){var r=this._getEncodeBucket(t),n=t&255;r[n]<=$a?this.encodeTableSeq[$a-r[n]][D0]=e:r[n]==Do&&(r[n]=e)};jl.prototype._setEncodeSequence=function(t,e){var r=t[0],n=this._getEncodeBucket(r),o=r&255,i;n[o]<=$a?i=this.encodeTableSeq[$a-n[o]]:(i={},n[o]!==Do&&(i[D0]=n[o]),n[o]=$a-this.encodeTableSeq.length,this.encodeTableSeq.push(i));for(var s=1;s=0)this._setEncodeChar(a,c),o=!0;else if(a<=$i){var u=$i-a;if(!i[u]){var p=c<<8>>>0;this._fillEncodeTable(u,p,r)?o=!0:i[u]=!0}}else a<=$a&&(this._setEncodeSequence(this.decodeTableSeq[$a-a],c),o=!0)}return o};function _D(t,e){this.leadSurrogate=-1,this.seqObj=void 0,this.encodeTable=e.encodeTable,this.encodeTableSeq=e.encodeTableSeq,this.defaultCharSingleByte=e.defCharSB,this.gb18030=e.gb18030}_D.prototype.write=function(t){for(var e=yK.alloc(t.length*(this.gb18030?4:3)),r=this.leadSurrogate,n=this.seqObj,o=-1,i=0,s=0;;){if(o===-1){if(i==t.length)break;var a=t.charCodeAt(i++)}else{var a=o;o=-1}if(a>=55296&&a<57344)if(a<56320)if(r===-1){r=a;continue}else r=a,a=Do;else r!==-1?(a=65536+(r-55296)*1024+(a-56320),r=-1):a=Do;else r!==-1&&(o=a,a=Do,r=-1);var c=Do;if(n!==void 0&&a!=Do){var u=n[a];if(typeof u=="object"){n=u;continue}else typeof u=="number"?c=u:u==null&&(u=n[D0],u!==void 0&&(c=u,o=a));n=void 0}else if(a>=0){var p=this.encodeTable[a>>8];if(p!==void 0&&(c=p[a&255]),c<=$a){n=this.encodeTableSeq[$a-c];continue}if(c==Do&&this.gb18030){var f=TK(this.gb18030.uChars,a);if(f!=-1){var c=this.gb18030.gbChars[f]+(a-this.gb18030.uChars[f]);e[s++]=129+Math.floor(c/12600),c=c%12600,e[s++]=48+Math.floor(c/1260),c=c%1260,e[s++]=129+Math.floor(c/10),c=c%10,e[s++]=48+c;continue}}}c===Do&&(c=this.defaultCharSingleByte),c<256?e[s++]=c:c<65536?(e[s++]=c>>8,e[s++]=c&255):c<16777216?(e[s++]=c>>16,e[s++]=c>>8&255,e[s++]=c&255):(e[s++]=c>>>24,e[s++]=c>>>16&255,e[s++]=c>>>8&255,e[s++]=c&255)}return this.seqObj=n,this.leadSurrogate=r,e.slice(0,s)};_D.prototype.end=function(){if(!(this.leadSurrogate===-1&&this.seqObj===void 0)){var t=yK.alloc(10),e=0;if(this.seqObj){var r=this.seqObj[D0];r!==void 0&&(r<256?t[e++]=r:(t[e++]=r>>8,t[e++]=r&255)),this.seqObj=void 0}return this.leadSurrogate!==-1&&(t[e++]=this.defaultCharSingleByte,this.leadSurrogate=-1),t.slice(0,e)}};_D.prototype.findIdx=TK;function EK(t,e){this.nodeIdx=0,this.prevBytes=[],this.decodeTables=e.decodeTables,this.decodeTableSeq=e.decodeTableSeq,this.defaultCharUnicode=e.defaultCharUnicode,this.gb18030=e.gb18030}EK.prototype.write=function(t){for(var e=yK.alloc(t.length*2),r=this.nodeIdx,n=this.prevBytes,o=this.prevBytes.length,i=-this.prevBytes.length,s,a=0,c=0;a=0?t[a]:n[a+o],s=this.decodeTables[r][u];if(!(s>=0))if(s===Do)s=this.defaultCharUnicode.charCodeAt(0),a=i;else if(s===qje){if(a>=3)var p=(t[a-3]-129)*12600+(t[a-2]-48)*1260+(t[a-1]-129)*10+(u-48);else var p=(n[a-3+o]-129)*12600+((a-2>=0?t[a-2]:n[a-2+o])-48)*1260+((a-1>=0?t[a-1]:n[a-1+o])-129)*10+(u-48);var f=TK(this.gb18030.gbChars,p);s=this.gb18030.uChars[f]+p-this.gb18030.gbChars[f]}else if(s<=$i){r=$i-s;continue}else if(s<=$a){for(var m=this.decodeTableSeq[$a-s],h=0;h>8;s=m[m.length-1]}else throw new Error("iconv-lite internal error: invalid decoding table value "+s+" at "+r+"/"+u);if(s>=65536){s-=65536;var _=55296|s>>10;e[c++]=_&255,e[c++]=_>>8,s=56320|s&1023}e[c++]=s&255,e[c++]=s>>8,r=0,i=a+1}return this.nodeIdx=r,this.prevBytes=i>=0?Array.prototype.slice.call(t,i):n.slice(i+o).concat(Array.prototype.slice.call(t)),e.slice(0,c).toString("ucs2")};EK.prototype.end=function(){for(var t="";this.prevBytes.length>0;){t+=this.defaultCharUnicode;var e=this.prevBytes.slice(1);this.prevBytes=[],this.nodeIdx=0,e.length>0&&(t+=this.write(e))}return this.prevBytes=[],this.nodeIdx=0,t};function TK(t,e){if(t[0]>e)return-1;for(var r=0,n=t.length;r>1);t[o]<=e?r=o:n=o}return r}});var Vje=S((Ikr,Oqt)=>{Oqt.exports=[["0","\0",128],["a1","\uFF61",62],["8140","\u3000\u3001\u3002\uFF0C\uFF0E\u30FB\uFF1A\uFF1B\uFF1F\uFF01\u309B\u309C\xB4\uFF40\xA8\uFF3E\uFFE3\uFF3F\u30FD\u30FE\u309D\u309E\u3003\u4EDD\u3005\u3006\u3007\u30FC\u2015\u2010\uFF0F\uFF3C\uFF5E\u2225\uFF5C\u2026\u2025\u2018\u2019\u201C\u201D\uFF08\uFF09\u3014\u3015\uFF3B\uFF3D\uFF5B\uFF5D\u3008",9,"\uFF0B\uFF0D\xB1\xD7"],["8180","\xF7\uFF1D\u2260\uFF1C\uFF1E\u2266\u2267\u221E\u2234\u2642\u2640\xB0\u2032\u2033\u2103\uFFE5\uFF04\uFFE0\uFFE1\uFF05\uFF03\uFF06\uFF0A\uFF20\xA7\u2606\u2605\u25CB\u25CF\u25CE\u25C7\u25C6\u25A1\u25A0\u25B3\u25B2\u25BD\u25BC\u203B\u3012\u2192\u2190\u2191\u2193\u3013"],["81b8","\u2208\u220B\u2286\u2287\u2282\u2283\u222A\u2229"],["81c8","\u2227\u2228\uFFE2\u21D2\u21D4\u2200\u2203"],["81da","\u2220\u22A5\u2312\u2202\u2207\u2261\u2252\u226A\u226B\u221A\u223D\u221D\u2235\u222B\u222C"],["81f0","\u212B\u2030\u266F\u266D\u266A\u2020\u2021\xB6"],["81fc","\u25EF"],["824f","\uFF10",9],["8260","\uFF21",25],["8281","\uFF41",25],["829f","\u3041",82],["8340","\u30A1",62],["8380","\u30E0",22],["839f","\u0391",16,"\u03A3",6],["83bf","\u03B1",16,"\u03C3",6],["8440","\u0410",5,"\u0401\u0416",25],["8470","\u0430",5,"\u0451\u0436",7],["8480","\u043E",17],["849f","\u2500\u2502\u250C\u2510\u2518\u2514\u251C\u252C\u2524\u2534\u253C\u2501\u2503\u250F\u2513\u251B\u2517\u2523\u2533\u252B\u253B\u254B\u2520\u252F\u2528\u2537\u253F\u251D\u2530\u2525\u2538\u2542"],["8740","\u2460",19,"\u2160",9],["875f","\u3349\u3314\u3322\u334D\u3318\u3327\u3303\u3336\u3351\u3357\u330D\u3326\u3323\u332B\u334A\u333B\u339C\u339D\u339E\u338E\u338F\u33C4\u33A1"],["877e","\u337B"],["8780","\u301D\u301F\u2116\u33CD\u2121\u32A4",4,"\u3231\u3232\u3239\u337E\u337D\u337C\u2252\u2261\u222B\u222E\u2211\u221A\u22A5\u2220\u221F\u22BF\u2235\u2229\u222A"],["889f","\u4E9C\u5516\u5A03\u963F\u54C0\u611B\u6328\u59F6\u9022\u8475\u831C\u7A50\u60AA\u63E1\u6E25\u65ED\u8466\u82A6\u9BF5\u6893\u5727\u65A1\u6271\u5B9B\u59D0\u867B\u98F4\u7D62\u7DBE\u9B8E\u6216\u7C9F\u88B7\u5B89\u5EB5\u6309\u6697\u6848\u95C7\u978D\u674F\u4EE5\u4F0A\u4F4D\u4F9D\u5049\u56F2\u5937\u59D4\u5A01\u5C09\u60DF\u610F\u6170\u6613\u6905\u70BA\u754F\u7570\u79FB\u7DAD\u7DEF\u80C3\u840E\u8863\u8B02\u9055\u907A\u533B\u4E95\u4EA5\u57DF\u80B2\u90C1\u78EF\u4E00\u58F1\u6EA2\u9038\u7A32\u8328\u828B\u9C2F\u5141\u5370\u54BD\u54E1\u56E0\u59FB\u5F15\u98F2\u6DEB\u80E4\u852D"],["8940","\u9662\u9670\u96A0\u97FB\u540B\u53F3\u5B87\u70CF\u7FBD\u8FC2\u96E8\u536F\u9D5C\u7ABA\u4E11\u7893\u81FC\u6E26\u5618\u5504\u6B1D\u851A\u9C3B\u59E5\u53A9\u6D66\u74DC\u958F\u5642\u4E91\u904B\u96F2\u834F\u990C\u53E1\u55B6\u5B30\u5F71\u6620\u66F3\u6804\u6C38\u6CF3\u6D29\u745B\u76C8\u7A4E\u9834\u82F1\u885B\u8A60\u92ED\u6DB2\u75AB\u76CA\u99C5\u60A6\u8B01\u8D8A\u95B2\u698E\u53AD\u5186"],["8980","\u5712\u5830\u5944\u5BB4\u5EF6\u6028\u63A9\u63F4\u6CBF\u6F14\u708E\u7114\u7159\u71D5\u733F\u7E01\u8276\u82D1\u8597\u9060\u925B\u9D1B\u5869\u65BC\u6C5A\u7525\u51F9\u592E\u5965\u5F80\u5FDC\u62BC\u65FA\u6A2A\u6B27\u6BB4\u738B\u7FC1\u8956\u9D2C\u9D0E\u9EC4\u5CA1\u6C96\u837B\u5104\u5C4B\u61B6\u81C6\u6876\u7261\u4E59\u4FFA\u5378\u6069\u6E29\u7A4F\u97F3\u4E0B\u5316\u4EEE\u4F55\u4F3D\u4FA1\u4F73\u52A0\u53EF\u5609\u590F\u5AC1\u5BB6\u5BE1\u79D1\u6687\u679C\u67B6\u6B4C\u6CB3\u706B\u73C2\u798D\u79BE\u7A3C\u7B87\u82B1\u82DB\u8304\u8377\u83EF\u83D3\u8766\u8AB2\u5629\u8CA8\u8FE6\u904E\u971E\u868A\u4FC4\u5CE8\u6211\u7259\u753B\u81E5\u82BD\u86FE\u8CC0\u96C5\u9913\u99D5\u4ECB\u4F1A\u89E3\u56DE\u584A\u58CA\u5EFB\u5FEB\u602A\u6094\u6062\u61D0\u6212\u62D0\u6539"],["8a40","\u9B41\u6666\u68B0\u6D77\u7070\u754C\u7686\u7D75\u82A5\u87F9\u958B\u968E\u8C9D\u51F1\u52BE\u5916\u54B3\u5BB3\u5D16\u6168\u6982\u6DAF\u788D\u84CB\u8857\u8A72\u93A7\u9AB8\u6D6C\u99A8\u86D9\u57A3\u67FF\u86CE\u920E\u5283\u5687\u5404\u5ED3\u62E1\u64B9\u683C\u6838\u6BBB\u7372\u78BA\u7A6B\u899A\u89D2\u8D6B\u8F03\u90ED\u95A3\u9694\u9769\u5B66\u5CB3\u697D\u984D\u984E\u639B\u7B20\u6A2B"],["8a80","\u6A7F\u68B6\u9C0D\u6F5F\u5272\u559D\u6070\u62EC\u6D3B\u6E07\u6ED1\u845B\u8910\u8F44\u4E14\u9C39\u53F6\u691B\u6A3A\u9784\u682A\u515C\u7AC3\u84B2\u91DC\u938C\u565B\u9D28\u6822\u8305\u8431\u7CA5\u5208\u82C5\u74E6\u4E7E\u4F83\u51A0\u5BD2\u520A\u52D8\u52E7\u5DFB\u559A\u582A\u59E6\u5B8C\u5B98\u5BDB\u5E72\u5E79\u60A3\u611F\u6163\u61BE\u63DB\u6562\u67D1\u6853\u68FA\u6B3E\u6B53\u6C57\u6F22\u6F97\u6F45\u74B0\u7518\u76E3\u770B\u7AFF\u7BA1\u7C21\u7DE9\u7F36\u7FF0\u809D\u8266\u839E\u89B3\u8ACC\u8CAB\u9084\u9451\u9593\u9591\u95A2\u9665\u97D3\u9928\u8218\u4E38\u542B\u5CB8\u5DCC\u73A9\u764C\u773C\u5CA9\u7FEB\u8D0B\u96C1\u9811\u9854\u9858\u4F01\u4F0E\u5371\u559C\u5668\u57FA\u5947\u5B09\u5BC4\u5C90\u5E0C\u5E7E\u5FCC\u63EE\u673A\u65D7\u65E2\u671F\u68CB\u68C4"],["8b40","\u6A5F\u5E30\u6BC5\u6C17\u6C7D\u757F\u7948\u5B63\u7A00\u7D00\u5FBD\u898F\u8A18\u8CB4\u8D77\u8ECC\u8F1D\u98E2\u9A0E\u9B3C\u4E80\u507D\u5100\u5993\u5B9C\u622F\u6280\u64EC\u6B3A\u72A0\u7591\u7947\u7FA9\u87FB\u8ABC\u8B70\u63AC\u83CA\u97A0\u5409\u5403\u55AB\u6854\u6A58\u8A70\u7827\u6775\u9ECD\u5374\u5BA2\u811A\u8650\u9006\u4E18\u4E45\u4EC7\u4F11\u53CA\u5438\u5BAE\u5F13\u6025\u6551"],["8b80","\u673D\u6C42\u6C72\u6CE3\u7078\u7403\u7A76\u7AAE\u7B08\u7D1A\u7CFE\u7D66\u65E7\u725B\u53BB\u5C45\u5DE8\u62D2\u62E0\u6319\u6E20\u865A\u8A31\u8DDD\u92F8\u6F01\u79A6\u9B5A\u4EA8\u4EAB\u4EAC\u4F9B\u4FA0\u50D1\u5147\u7AF6\u5171\u51F6\u5354\u5321\u537F\u53EB\u55AC\u5883\u5CE1\u5F37\u5F4A\u602F\u6050\u606D\u631F\u6559\u6A4B\u6CC1\u72C2\u72ED\u77EF\u80F8\u8105\u8208\u854E\u90F7\u93E1\u97FF\u9957\u9A5A\u4EF0\u51DD\u5C2D\u6681\u696D\u5C40\u66F2\u6975\u7389\u6850\u7C81\u50C5\u52E4\u5747\u5DFE\u9326\u65A4\u6B23\u6B3D\u7434\u7981\u79BD\u7B4B\u7DCA\u82B9\u83CC\u887F\u895F\u8B39\u8FD1\u91D1\u541F\u9280\u4E5D\u5036\u53E5\u533A\u72D7\u7396\u77E9\u82E6\u8EAF\u99C6\u99C8\u99D2\u5177\u611A\u865E\u55B0\u7A7A\u5076\u5BD3\u9047\u9685\u4E32\u6ADB\u91E7\u5C51\u5C48"],["8c40","\u6398\u7A9F\u6C93\u9774\u8F61\u7AAA\u718A\u9688\u7C82\u6817\u7E70\u6851\u936C\u52F2\u541B\u85AB\u8A13\u7FA4\u8ECD\u90E1\u5366\u8888\u7941\u4FC2\u50BE\u5211\u5144\u5553\u572D\u73EA\u578B\u5951\u5F62\u5F84\u6075\u6176\u6167\u61A9\u63B2\u643A\u656C\u666F\u6842\u6E13\u7566\u7A3D\u7CFB\u7D4C\u7D99\u7E4B\u7F6B\u830E\u834A\u86CD\u8A08\u8A63\u8B66\u8EFD\u981A\u9D8F\u82B8\u8FCE\u9BE8"],["8c80","\u5287\u621F\u6483\u6FC0\u9699\u6841\u5091\u6B20\u6C7A\u6F54\u7A74\u7D50\u8840\u8A23\u6708\u4EF6\u5039\u5026\u5065\u517C\u5238\u5263\u55A7\u570F\u5805\u5ACC\u5EFA\u61B2\u61F8\u62F3\u6372\u691C\u6A29\u727D\u72AC\u732E\u7814\u786F\u7D79\u770C\u80A9\u898B\u8B19\u8CE2\u8ED2\u9063\u9375\u967A\u9855\u9A13\u9E78\u5143\u539F\u53B3\u5E7B\u5F26\u6E1B\u6E90\u7384\u73FE\u7D43\u8237\u8A00\u8AFA\u9650\u4E4E\u500B\u53E4\u547C\u56FA\u59D1\u5B64\u5DF1\u5EAB\u5F27\u6238\u6545\u67AF\u6E56\u72D0\u7CCA\u88B4\u80A1\u80E1\u83F0\u864E\u8A87\u8DE8\u9237\u96C7\u9867\u9F13\u4E94\u4E92\u4F0D\u5348\u5449\u543E\u5A2F\u5F8C\u5FA1\u609F\u68A7\u6A8E\u745A\u7881\u8A9E\u8AA4\u8B77\u9190\u4E5E\u9BC9\u4EA4\u4F7C\u4FAF\u5019\u5016\u5149\u516C\u529F\u52B9\u52FE\u539A\u53E3\u5411"],["8d40","\u540E\u5589\u5751\u57A2\u597D\u5B54\u5B5D\u5B8F\u5DE5\u5DE7\u5DF7\u5E78\u5E83\u5E9A\u5EB7\u5F18\u6052\u614C\u6297\u62D8\u63A7\u653B\u6602\u6643\u66F4\u676D\u6821\u6897\u69CB\u6C5F\u6D2A\u6D69\u6E2F\u6E9D\u7532\u7687\u786C\u7A3F\u7CE0\u7D05\u7D18\u7D5E\u7DB1\u8015\u8003\u80AF\u80B1\u8154\u818F\u822A\u8352\u884C\u8861\u8B1B\u8CA2\u8CFC\u90CA\u9175\u9271\u783F\u92FC\u95A4\u964D"],["8d80","\u9805\u9999\u9AD8\u9D3B\u525B\u52AB\u53F7\u5408\u58D5\u62F7\u6FE0\u8C6A\u8F5F\u9EB9\u514B\u523B\u544A\u56FD\u7A40\u9177\u9D60\u9ED2\u7344\u6F09\u8170\u7511\u5FFD\u60DA\u9AA8\u72DB\u8FBC\u6B64\u9803\u4ECA\u56F0\u5764\u58BE\u5A5A\u6068\u61C7\u660F\u6606\u6839\u68B1\u6DF7\u75D5\u7D3A\u826E\u9B42\u4E9B\u4F50\u53C9\u5506\u5D6F\u5DE6\u5DEE\u67FB\u6C99\u7473\u7802\u8A50\u9396\u88DF\u5750\u5EA7\u632B\u50B5\u50AC\u518D\u6700\u54C9\u585E\u59BB\u5BB0\u5F69\u624D\u63A1\u683D\u6B73\u6E08\u707D\u91C7\u7280\u7815\u7826\u796D\u658E\u7D30\u83DC\u88C1\u8F09\u969B\u5264\u5728\u6750\u7F6A\u8CA1\u51B4\u5742\u962A\u583A\u698A\u80B4\u54B2\u5D0E\u57FC\u7895\u9DFA\u4F5C\u524A\u548B\u643E\u6628\u6714\u67F5\u7A84\u7B56\u7D22\u932F\u685C\u9BAD\u7B39\u5319\u518A\u5237"],["8e40","\u5BDF\u62F6\u64AE\u64E6\u672D\u6BBA\u85A9\u96D1\u7690\u9BD6\u634C\u9306\u9BAB\u76BF\u6652\u4E09\u5098\u53C2\u5C71\u60E8\u6492\u6563\u685F\u71E6\u73CA\u7523\u7B97\u7E82\u8695\u8B83\u8CDB\u9178\u9910\u65AC\u66AB\u6B8B\u4ED5\u4ED4\u4F3A\u4F7F\u523A\u53F8\u53F2\u55E3\u56DB\u58EB\u59CB\u59C9\u59FF\u5B50\u5C4D\u5E02\u5E2B\u5FD7\u601D\u6307\u652F\u5B5C\u65AF\u65BD\u65E8\u679D\u6B62"],["8e80","\u6B7B\u6C0F\u7345\u7949\u79C1\u7CF8\u7D19\u7D2B\u80A2\u8102\u81F3\u8996\u8A5E\u8A69\u8A66\u8A8C\u8AEE\u8CC7\u8CDC\u96CC\u98FC\u6B6F\u4E8B\u4F3C\u4F8D\u5150\u5B57\u5BFA\u6148\u6301\u6642\u6B21\u6ECB\u6CBB\u723E\u74BD\u75D4\u78C1\u793A\u800C\u8033\u81EA\u8494\u8F9E\u6C50\u9E7F\u5F0F\u8B58\u9D2B\u7AFA\u8EF8\u5B8D\u96EB\u4E03\u53F1\u57F7\u5931\u5AC9\u5BA4\u6089\u6E7F\u6F06\u75BE\u8CEA\u5B9F\u8500\u7BE0\u5072\u67F4\u829D\u5C61\u854A\u7E1E\u820E\u5199\u5C04\u6368\u8D66\u659C\u716E\u793E\u7D17\u8005\u8B1D\u8ECA\u906E\u86C7\u90AA\u501F\u52FA\u5C3A\u6753\u707C\u7235\u914C\u91C8\u932B\u82E5\u5BC2\u5F31\u60F9\u4E3B\u53D6\u5B88\u624B\u6731\u6B8A\u72E9\u73E0\u7A2E\u816B\u8DA3\u9152\u9996\u5112\u53D7\u546A\u5BFF\u6388\u6A39\u7DAC\u9700\u56DA\u53CE\u5468"],["8f40","\u5B97\u5C31\u5DDE\u4FEE\u6101\u62FE\u6D32\u79C0\u79CB\u7D42\u7E4D\u7FD2\u81ED\u821F\u8490\u8846\u8972\u8B90\u8E74\u8F2F\u9031\u914B\u916C\u96C6\u919C\u4EC0\u4F4F\u5145\u5341\u5F93\u620E\u67D4\u6C41\u6E0B\u7363\u7E26\u91CD\u9283\u53D4\u5919\u5BBF\u6DD1\u795D\u7E2E\u7C9B\u587E\u719F\u51FA\u8853\u8FF0\u4FCA\u5CFB\u6625\u77AC\u7AE3\u821C\u99FF\u51C6\u5FAA\u65EC\u696F\u6B89\u6DF3"],["8f80","\u6E96\u6F64\u76FE\u7D14\u5DE1\u9075\u9187\u9806\u51E6\u521D\u6240\u6691\u66D9\u6E1A\u5EB6\u7DD2\u7F72\u66F8\u85AF\u85F7\u8AF8\u52A9\u53D9\u5973\u5E8F\u5F90\u6055\u92E4\u9664\u50B7\u511F\u52DD\u5320\u5347\u53EC\u54E8\u5546\u5531\u5617\u5968\u59BE\u5A3C\u5BB5\u5C06\u5C0F\u5C11\u5C1A\u5E84\u5E8A\u5EE0\u5F70\u627F\u6284\u62DB\u638C\u6377\u6607\u660C\u662D\u6676\u677E\u68A2\u6A1F\u6A35\u6CBC\u6D88\u6E09\u6E58\u713C\u7126\u7167\u75C7\u7701\u785D\u7901\u7965\u79F0\u7AE0\u7B11\u7CA7\u7D39\u8096\u83D6\u848B\u8549\u885D\u88F3\u8A1F\u8A3C\u8A54\u8A73\u8C61\u8CDE\u91A4\u9266\u937E\u9418\u969C\u9798\u4E0A\u4E08\u4E1E\u4E57\u5197\u5270\u57CE\u5834\u58CC\u5B22\u5E38\u60C5\u64FE\u6761\u6756\u6D44\u72B6\u7573\u7A63\u84B8\u8B72\u91B8\u9320\u5631\u57F4\u98FE"],["9040","\u62ED\u690D\u6B96\u71ED\u7E54\u8077\u8272\u89E6\u98DF\u8755\u8FB1\u5C3B\u4F38\u4FE1\u4FB5\u5507\u5A20\u5BDD\u5BE9\u5FC3\u614E\u632F\u65B0\u664B\u68EE\u699B\u6D78\u6DF1\u7533\u75B9\u771F\u795E\u79E6\u7D33\u81E3\u82AF\u85AA\u89AA\u8A3A\u8EAB\u8F9B\u9032\u91DD\u9707\u4EBA\u4EC1\u5203\u5875\u58EC\u5C0B\u751A\u5C3D\u814E\u8A0A\u8FC5\u9663\u976D\u7B25\u8ACF\u9808\u9162\u56F3\u53A8"],["9080","\u9017\u5439\u5782\u5E25\u63A8\u6C34\u708A\u7761\u7C8B\u7FE0\u8870\u9042\u9154\u9310\u9318\u968F\u745E\u9AC4\u5D07\u5D69\u6570\u67A2\u8DA8\u96DB\u636E\u6749\u6919\u83C5\u9817\u96C0\u88FE\u6F84\u647A\u5BF8\u4E16\u702C\u755D\u662F\u51C4\u5236\u52E2\u59D3\u5F81\u6027\u6210\u653F\u6574\u661F\u6674\u68F2\u6816\u6B63\u6E05\u7272\u751F\u76DB\u7CBE\u8056\u58F0\u88FD\u897F\u8AA0\u8A93\u8ACB\u901D\u9192\u9752\u9759\u6589\u7A0E\u8106\u96BB\u5E2D\u60DC\u621A\u65A5\u6614\u6790\u77F3\u7A4D\u7C4D\u7E3E\u810A\u8CAC\u8D64\u8DE1\u8E5F\u78A9\u5207\u62D9\u63A5\u6442\u6298\u8A2D\u7A83\u7BC0\u8AAC\u96EA\u7D76\u820C\u8749\u4ED9\u5148\u5343\u5360\u5BA3\u5C02\u5C16\u5DDD\u6226\u6247\u64B0\u6813\u6834\u6CC9\u6D45\u6D17\u67D3\u6F5C\u714E\u717D\u65CB\u7A7F\u7BAD\u7DDA"],["9140","\u7E4A\u7FA8\u817A\u821B\u8239\u85A6\u8A6E\u8CCE\u8DF5\u9078\u9077\u92AD\u9291\u9583\u9BAE\u524D\u5584\u6F38\u7136\u5168\u7985\u7E55\u81B3\u7CCE\u564C\u5851\u5CA8\u63AA\u66FE\u66FD\u695A\u72D9\u758F\u758E\u790E\u7956\u79DF\u7C97\u7D20\u7D44\u8607\u8A34\u963B\u9061\u9F20\u50E7\u5275\u53CC\u53E2\u5009\u55AA\u58EE\u594F\u723D\u5B8B\u5C64\u531D\u60E3\u60F3\u635C\u6383\u633F\u63BB"],["9180","\u64CD\u65E9\u66F9\u5DE3\u69CD\u69FD\u6F15\u71E5\u4E89\u75E9\u76F8\u7A93\u7CDF\u7DCF\u7D9C\u8061\u8349\u8358\u846C\u84BC\u85FB\u88C5\u8D70\u9001\u906D\u9397\u971C\u9A12\u50CF\u5897\u618E\u81D3\u8535\u8D08\u9020\u4FC3\u5074\u5247\u5373\u606F\u6349\u675F\u6E2C\u8DB3\u901F\u4FD7\u5C5E\u8CCA\u65CF\u7D9A\u5352\u8896\u5176\u63C3\u5B58\u5B6B\u5C0A\u640D\u6751\u905C\u4ED6\u591A\u592A\u6C70\u8A51\u553E\u5815\u59A5\u60F0\u6253\u67C1\u8235\u6955\u9640\u99C4\u9A28\u4F53\u5806\u5BFE\u8010\u5CB1\u5E2F\u5F85\u6020\u614B\u6234\u66FF\u6CF0\u6EDE\u80CE\u817F\u82D4\u888B\u8CB8\u9000\u902E\u968A\u9EDB\u9BDB\u4EE3\u53F0\u5927\u7B2C\u918D\u984C\u9DF9\u6EDD\u7027\u5353\u5544\u5B85\u6258\u629E\u62D3\u6CA2\u6FEF\u7422\u8A17\u9438\u6FC1\u8AFE\u8338\u51E7\u86F8\u53EA"],["9240","\u53E9\u4F46\u9054\u8FB0\u596A\u8131\u5DFD\u7AEA\u8FBF\u68DA\u8C37\u72F8\u9C48\u6A3D\u8AB0\u4E39\u5358\u5606\u5766\u62C5\u63A2\u65E6\u6B4E\u6DE1\u6E5B\u70AD\u77ED\u7AEF\u7BAA\u7DBB\u803D\u80C6\u86CB\u8A95\u935B\u56E3\u58C7\u5F3E\u65AD\u6696\u6A80\u6BB5\u7537\u8AC7\u5024\u77E5\u5730\u5F1B\u6065\u667A\u6C60\u75F4\u7A1A\u7F6E\u81F4\u8718\u9045\u99B3\u7BC9\u755C\u7AF9\u7B51\u84C4"],["9280","\u9010\u79E9\u7A92\u8336\u5AE1\u7740\u4E2D\u4EF2\u5B99\u5FE0\u62BD\u663C\u67F1\u6CE8\u866B\u8877\u8A3B\u914E\u92F3\u99D0\u6A17\u7026\u732A\u82E7\u8457\u8CAF\u4E01\u5146\u51CB\u558B\u5BF5\u5E16\u5E33\u5E81\u5F14\u5F35\u5F6B\u5FB4\u61F2\u6311\u66A2\u671D\u6F6E\u7252\u753A\u773A\u8074\u8139\u8178\u8776\u8ABF\u8ADC\u8D85\u8DF3\u929A\u9577\u9802\u9CE5\u52C5\u6357\u76F4\u6715\u6C88\u73CD\u8CC3\u93AE\u9673\u6D25\u589C\u690E\u69CC\u8FFD\u939A\u75DB\u901A\u585A\u6802\u63B4\u69FB\u4F43\u6F2C\u67D8\u8FBB\u8526\u7DB4\u9354\u693F\u6F70\u576A\u58F7\u5B2C\u7D2C\u722A\u540A\u91E3\u9DB4\u4EAD\u4F4E\u505C\u5075\u5243\u8C9E\u5448\u5824\u5B9A\u5E1D\u5E95\u5EAD\u5EF7\u5F1F\u608C\u62B5\u633A\u63D0\u68AF\u6C40\u7887\u798E\u7A0B\u7DE0\u8247\u8A02\u8AE6\u8E44\u9013"],["9340","\u90B8\u912D\u91D8\u9F0E\u6CE5\u6458\u64E2\u6575\u6EF4\u7684\u7B1B\u9069\u93D1\u6EBA\u54F2\u5FB9\u64A4\u8F4D\u8FED\u9244\u5178\u586B\u5929\u5C55\u5E97\u6DFB\u7E8F\u751C\u8CBC\u8EE2\u985B\u70B9\u4F1D\u6BBF\u6FB1\u7530\u96FB\u514E\u5410\u5835\u5857\u59AC\u5C60\u5F92\u6597\u675C\u6E21\u767B\u83DF\u8CED\u9014\u90FD\u934D\u7825\u783A\u52AA\u5EA6\u571F\u5974\u6012\u5012\u515A\u51AC"],["9380","\u51CD\u5200\u5510\u5854\u5858\u5957\u5B95\u5CF6\u5D8B\u60BC\u6295\u642D\u6771\u6843\u68BC\u68DF\u76D7\u6DD8\u6E6F\u6D9B\u706F\u71C8\u5F53\u75D8\u7977\u7B49\u7B54\u7B52\u7CD6\u7D71\u5230\u8463\u8569\u85E4\u8A0E\u8B04\u8C46\u8E0F\u9003\u900F\u9419\u9676\u982D\u9A30\u95D8\u50CD\u52D5\u540C\u5802\u5C0E\u61A7\u649E\u6D1E\u77B3\u7AE5\u80F4\u8404\u9053\u9285\u5CE0\u9D07\u533F\u5F97\u5FB3\u6D9C\u7279\u7763\u79BF\u7BE4\u6BD2\u72EC\u8AAD\u6803\u6A61\u51F8\u7A81\u6934\u5C4A\u9CF6\u82EB\u5BC5\u9149\u701E\u5678\u5C6F\u60C7\u6566\u6C8C\u8C5A\u9041\u9813\u5451\u66C7\u920D\u5948\u90A3\u5185\u4E4D\u51EA\u8599\u8B0E\u7058\u637A\u934B\u6962\u99B4\u7E04\u7577\u5357\u6960\u8EDF\u96E3\u6C5D\u4E8C\u5C3C\u5F10\u8FE9\u5302\u8CD1\u8089\u8679\u5EFF\u65E5\u4E73\u5165"],["9440","\u5982\u5C3F\u97EE\u4EFB\u598A\u5FCD\u8A8D\u6FE1\u79B0\u7962\u5BE7\u8471\u732B\u71B1\u5E74\u5FF5\u637B\u649A\u71C3\u7C98\u4E43\u5EFC\u4E4B\u57DC\u56A2\u60A9\u6FC3\u7D0D\u80FD\u8133\u81BF\u8FB2\u8997\u86A4\u5DF4\u628A\u64AD\u8987\u6777\u6CE2\u6D3E\u7436\u7834\u5A46\u7F75\u82AD\u99AC\u4FF3\u5EC3\u62DD\u6392\u6557\u676F\u76C3\u724C\u80CC\u80BA\u8F29\u914D\u500D\u57F9\u5A92\u6885"],["9480","\u6973\u7164\u72FD\u8CB7\u58F2\u8CE0\u966A\u9019\u877F\u79E4\u77E7\u8429\u4F2F\u5265\u535A\u62CD\u67CF\u6CCA\u767D\u7B94\u7C95\u8236\u8584\u8FEB\u66DD\u6F20\u7206\u7E1B\u83AB\u99C1\u9EA6\u51FD\u7BB1\u7872\u7BB8\u8087\u7B48\u6AE8\u5E61\u808C\u7551\u7560\u516B\u9262\u6E8C\u767A\u9197\u9AEA\u4F10\u7F70\u629C\u7B4F\u95A5\u9CE9\u567A\u5859\u86E4\u96BC\u4F34\u5224\u534A\u53CD\u53DB\u5E06\u642C\u6591\u677F\u6C3E\u6C4E\u7248\u72AF\u73ED\u7554\u7E41\u822C\u85E9\u8CA9\u7BC4\u91C6\u7169\u9812\u98EF\u633D\u6669\u756A\u76E4\u78D0\u8543\u86EE\u532A\u5351\u5426\u5983\u5E87\u5F7C\u60B2\u6249\u6279\u62AB\u6590\u6BD4\u6CCC\u75B2\u76AE\u7891\u79D8\u7DCB\u7F77\u80A5\u88AB\u8AB9\u8CBB\u907F\u975E\u98DB\u6A0B\u7C38\u5099\u5C3E\u5FAE\u6787\u6BD8\u7435\u7709\u7F8E"],["9540","\u9F3B\u67CA\u7A17\u5339\u758B\u9AED\u5F66\u819D\u83F1\u8098\u5F3C\u5FC5\u7562\u7B46\u903C\u6867\u59EB\u5A9B\u7D10\u767E\u8B2C\u4FF5\u5F6A\u6A19\u6C37\u6F02\u74E2\u7968\u8868\u8A55\u8C79\u5EDF\u63CF\u75C5\u79D2\u82D7\u9328\u92F2\u849C\u86ED\u9C2D\u54C1\u5F6C\u658C\u6D5C\u7015\u8CA7\u8CD3\u983B\u654F\u74F6\u4E0D\u4ED8\u57E0\u592B\u5A66\u5BCC\u51A8\u5E03\u5E9C\u6016\u6276\u6577"],["9580","\u65A7\u666E\u6D6E\u7236\u7B26\u8150\u819A\u8299\u8B5C\u8CA0\u8CE6\u8D74\u961C\u9644\u4FAE\u64AB\u6B66\u821E\u8461\u856A\u90E8\u5C01\u6953\u98A8\u847A\u8557\u4F0F\u526F\u5FA9\u5E45\u670D\u798F\u8179\u8907\u8986\u6DF5\u5F17\u6255\u6CB8\u4ECF\u7269\u9B92\u5206\u543B\u5674\u58B3\u61A4\u626E\u711A\u596E\u7C89\u7CDE\u7D1B\u96F0\u6587\u805E\u4E19\u4F75\u5175\u5840\u5E63\u5E73\u5F0A\u67C4\u4E26\u853D\u9589\u965B\u7C73\u9801\u50FB\u58C1\u7656\u78A7\u5225\u77A5\u8511\u7B86\u504F\u5909\u7247\u7BC7\u7DE8\u8FBA\u8FD4\u904D\u4FBF\u52C9\u5A29\u5F01\u97AD\u4FDD\u8217\u92EA\u5703\u6355\u6B69\u752B\u88DC\u8F14\u7A42\u52DF\u5893\u6155\u620A\u66AE\u6BCD\u7C3F\u83E9\u5023\u4FF8\u5305\u5446\u5831\u5949\u5B9D\u5CF0\u5CEF\u5D29\u5E96\u62B1\u6367\u653E\u65B9\u670B"],["9640","\u6CD5\u6CE1\u70F9\u7832\u7E2B\u80DE\u82B3\u840C\u84EC\u8702\u8912\u8A2A\u8C4A\u90A6\u92D2\u98FD\u9CF3\u9D6C\u4E4F\u4EA1\u508D\u5256\u574A\u59A8\u5E3D\u5FD8\u5FD9\u623F\u66B4\u671B\u67D0\u68D2\u5192\u7D21\u80AA\u81A8\u8B00\u8C8C\u8CBF\u927E\u9632\u5420\u982C\u5317\u50D5\u535C\u58A8\u64B2\u6734\u7267\u7766\u7A46\u91E6\u52C3\u6CA1\u6B86\u5800\u5E4C\u5954\u672C\u7FFB\u51E1\u76C6"],["9680","\u6469\u78E8\u9B54\u9EBB\u57CB\u59B9\u6627\u679A\u6BCE\u54E9\u69D9\u5E55\u819C\u6795\u9BAA\u67FE\u9C52\u685D\u4EA6\u4FE3\u53C8\u62B9\u672B\u6CAB\u8FC4\u4FAD\u7E6D\u9EBF\u4E07\u6162\u6E80\u6F2B\u8513\u5473\u672A\u9B45\u5DF3\u7B95\u5CAC\u5BC6\u871C\u6E4A\u84D1\u7A14\u8108\u5999\u7C8D\u6C11\u7720\u52D9\u5922\u7121\u725F\u77DB\u9727\u9D61\u690B\u5A7F\u5A18\u51A5\u540D\u547D\u660E\u76DF\u8FF7\u9298\u9CF4\u59EA\u725D\u6EC5\u514D\u68C9\u7DBF\u7DEC\u9762\u9EBA\u6478\u6A21\u8302\u5984\u5B5F\u6BDB\u731B\u76F2\u7DB2\u8017\u8499\u5132\u6728\u9ED9\u76EE\u6762\u52FF\u9905\u5C24\u623B\u7C7E\u8CB0\u554F\u60B6\u7D0B\u9580\u5301\u4E5F\u51B6\u591C\u723A\u8036\u91CE\u5F25\u77E2\u5384\u5F79\u7D04\u85AC\u8A33\u8E8D\u9756\u67F3\u85AE\u9453\u6109\u6108\u6CB9\u7652"],["9740","\u8AED\u8F38\u552F\u4F51\u512A\u52C7\u53CB\u5BA5\u5E7D\u60A0\u6182\u63D6\u6709\u67DA\u6E67\u6D8C\u7336\u7337\u7531\u7950\u88D5\u8A98\u904A\u9091\u90F5\u96C4\u878D\u5915\u4E88\u4F59\u4E0E\u8A89\u8F3F\u9810\u50AD\u5E7C\u5996\u5BB9\u5EB8\u63DA\u63FA\u64C1\u66DC\u694A\u69D8\u6D0B\u6EB6\u7194\u7528\u7AAF\u7F8A\u8000\u8449\u84C9\u8981\u8B21\u8E0A\u9065\u967D\u990A\u617E\u6291\u6B32"],["9780","\u6C83\u6D74\u7FCC\u7FFC\u6DC0\u7F85\u87BA\u88F8\u6765\u83B1\u983C\u96F7\u6D1B\u7D61\u843D\u916A\u4E71\u5375\u5D50\u6B04\u6FEB\u85CD\u862D\u89A7\u5229\u540F\u5C65\u674E\u68A8\u7406\u7483\u75E2\u88CF\u88E1\u91CC\u96E2\u9678\u5F8B\u7387\u7ACB\u844E\u63A0\u7565\u5289\u6D41\u6E9C\u7409\u7559\u786B\u7C92\u9686\u7ADC\u9F8D\u4FB6\u616E\u65C5\u865C\u4E86\u4EAE\u50DA\u4E21\u51CC\u5BEE\u6599\u6881\u6DBC\u731F\u7642\u77AD\u7A1C\u7CE7\u826F\u8AD2\u907C\u91CF\u9675\u9818\u529B\u7DD1\u502B\u5398\u6797\u6DCB\u71D0\u7433\u81E8\u8F2A\u96A3\u9C57\u9E9F\u7460\u5841\u6D99\u7D2F\u985E\u4EE4\u4F36\u4F8B\u51B7\u52B1\u5DBA\u601C\u73B2\u793C\u82D3\u9234\u96B7\u96F6\u970A\u9E97\u9F62\u66A6\u6B74\u5217\u52A3\u70C8\u88C2\u5EC9\u604B\u6190\u6F23\u7149\u7C3E\u7DF4\u806F"],["9840","\u84EE\u9023\u932C\u5442\u9B6F\u6AD3\u7089\u8CC2\u8DEF\u9732\u52B4\u5A41\u5ECA\u5F04\u6717\u697C\u6994\u6D6A\u6F0F\u7262\u72FC\u7BED\u8001\u807E\u874B\u90CE\u516D\u9E93\u7984\u808B\u9332\u8AD6\u502D\u548C\u8A71\u6B6A\u8CC4\u8107\u60D1\u67A0\u9DF2\u4E99\u4E98\u9C10\u8A6B\u85C1\u8568\u6900\u6E7E\u7897\u8155"],["989f","\u5F0C\u4E10\u4E15\u4E2A\u4E31\u4E36\u4E3C\u4E3F\u4E42\u4E56\u4E58\u4E82\u4E85\u8C6B\u4E8A\u8212\u5F0D\u4E8E\u4E9E\u4E9F\u4EA0\u4EA2\u4EB0\u4EB3\u4EB6\u4ECE\u4ECD\u4EC4\u4EC6\u4EC2\u4ED7\u4EDE\u4EED\u4EDF\u4EF7\u4F09\u4F5A\u4F30\u4F5B\u4F5D\u4F57\u4F47\u4F76\u4F88\u4F8F\u4F98\u4F7B\u4F69\u4F70\u4F91\u4F6F\u4F86\u4F96\u5118\u4FD4\u4FDF\u4FCE\u4FD8\u4FDB\u4FD1\u4FDA\u4FD0\u4FE4\u4FE5\u501A\u5028\u5014\u502A\u5025\u5005\u4F1C\u4FF6\u5021\u5029\u502C\u4FFE\u4FEF\u5011\u5006\u5043\u5047\u6703\u5055\u5050\u5048\u505A\u5056\u506C\u5078\u5080\u509A\u5085\u50B4\u50B2"],["9940","\u50C9\u50CA\u50B3\u50C2\u50D6\u50DE\u50E5\u50ED\u50E3\u50EE\u50F9\u50F5\u5109\u5101\u5102\u5116\u5115\u5114\u511A\u5121\u513A\u5137\u513C\u513B\u513F\u5140\u5152\u514C\u5154\u5162\u7AF8\u5169\u516A\u516E\u5180\u5182\u56D8\u518C\u5189\u518F\u5191\u5193\u5195\u5196\u51A4\u51A6\u51A2\u51A9\u51AA\u51AB\u51B3\u51B1\u51B2\u51B0\u51B5\u51BD\u51C5\u51C9\u51DB\u51E0\u8655\u51E9\u51ED"],["9980","\u51F0\u51F5\u51FE\u5204\u520B\u5214\u520E\u5227\u522A\u522E\u5233\u5239\u524F\u5244\u524B\u524C\u525E\u5254\u526A\u5274\u5269\u5273\u527F\u527D\u528D\u5294\u5292\u5271\u5288\u5291\u8FA8\u8FA7\u52AC\u52AD\u52BC\u52B5\u52C1\u52CD\u52D7\u52DE\u52E3\u52E6\u98ED\u52E0\u52F3\u52F5\u52F8\u52F9\u5306\u5308\u7538\u530D\u5310\u530F\u5315\u531A\u5323\u532F\u5331\u5333\u5338\u5340\u5346\u5345\u4E17\u5349\u534D\u51D6\u535E\u5369\u536E\u5918\u537B\u5377\u5382\u5396\u53A0\u53A6\u53A5\u53AE\u53B0\u53B6\u53C3\u7C12\u96D9\u53DF\u66FC\u71EE\u53EE\u53E8\u53ED\u53FA\u5401\u543D\u5440\u542C\u542D\u543C\u542E\u5436\u5429\u541D\u544E\u548F\u5475\u548E\u545F\u5471\u5477\u5470\u5492\u547B\u5480\u5476\u5484\u5490\u5486\u54C7\u54A2\u54B8\u54A5\u54AC\u54C4\u54C8\u54A8"],["9a40","\u54AB\u54C2\u54A4\u54BE\u54BC\u54D8\u54E5\u54E6\u550F\u5514\u54FD\u54EE\u54ED\u54FA\u54E2\u5539\u5540\u5563\u554C\u552E\u555C\u5545\u5556\u5557\u5538\u5533\u555D\u5599\u5580\u54AF\u558A\u559F\u557B\u557E\u5598\u559E\u55AE\u557C\u5583\u55A9\u5587\u55A8\u55DA\u55C5\u55DF\u55C4\u55DC\u55E4\u55D4\u5614\u55F7\u5616\u55FE\u55FD\u561B\u55F9\u564E\u5650\u71DF\u5634\u5636\u5632\u5638"],["9a80","\u566B\u5664\u562F\u566C\u566A\u5686\u5680\u568A\u56A0\u5694\u568F\u56A5\u56AE\u56B6\u56B4\u56C2\u56BC\u56C1\u56C3\u56C0\u56C8\u56CE\u56D1\u56D3\u56D7\u56EE\u56F9\u5700\u56FF\u5704\u5709\u5708\u570B\u570D\u5713\u5718\u5716\u55C7\u571C\u5726\u5737\u5738\u574E\u573B\u5740\u574F\u5769\u57C0\u5788\u5761\u577F\u5789\u5793\u57A0\u57B3\u57A4\u57AA\u57B0\u57C3\u57C6\u57D4\u57D2\u57D3\u580A\u57D6\u57E3\u580B\u5819\u581D\u5872\u5821\u5862\u584B\u5870\u6BC0\u5852\u583D\u5879\u5885\u58B9\u589F\u58AB\u58BA\u58DE\u58BB\u58B8\u58AE\u58C5\u58D3\u58D1\u58D7\u58D9\u58D8\u58E5\u58DC\u58E4\u58DF\u58EF\u58FA\u58F9\u58FB\u58FC\u58FD\u5902\u590A\u5910\u591B\u68A6\u5925\u592C\u592D\u5932\u5938\u593E\u7AD2\u5955\u5950\u594E\u595A\u5958\u5962\u5960\u5967\u596C\u5969"],["9b40","\u5978\u5981\u599D\u4F5E\u4FAB\u59A3\u59B2\u59C6\u59E8\u59DC\u598D\u59D9\u59DA\u5A25\u5A1F\u5A11\u5A1C\u5A09\u5A1A\u5A40\u5A6C\u5A49\u5A35\u5A36\u5A62\u5A6A\u5A9A\u5ABC\u5ABE\u5ACB\u5AC2\u5ABD\u5AE3\u5AD7\u5AE6\u5AE9\u5AD6\u5AFA\u5AFB\u5B0C\u5B0B\u5B16\u5B32\u5AD0\u5B2A\u5B36\u5B3E\u5B43\u5B45\u5B40\u5B51\u5B55\u5B5A\u5B5B\u5B65\u5B69\u5B70\u5B73\u5B75\u5B78\u6588\u5B7A\u5B80"],["9b80","\u5B83\u5BA6\u5BB8\u5BC3\u5BC7\u5BC9\u5BD4\u5BD0\u5BE4\u5BE6\u5BE2\u5BDE\u5BE5\u5BEB\u5BF0\u5BF6\u5BF3\u5C05\u5C07\u5C08\u5C0D\u5C13\u5C20\u5C22\u5C28\u5C38\u5C39\u5C41\u5C46\u5C4E\u5C53\u5C50\u5C4F\u5B71\u5C6C\u5C6E\u4E62\u5C76\u5C79\u5C8C\u5C91\u5C94\u599B\u5CAB\u5CBB\u5CB6\u5CBC\u5CB7\u5CC5\u5CBE\u5CC7\u5CD9\u5CE9\u5CFD\u5CFA\u5CED\u5D8C\u5CEA\u5D0B\u5D15\u5D17\u5D5C\u5D1F\u5D1B\u5D11\u5D14\u5D22\u5D1A\u5D19\u5D18\u5D4C\u5D52\u5D4E\u5D4B\u5D6C\u5D73\u5D76\u5D87\u5D84\u5D82\u5DA2\u5D9D\u5DAC\u5DAE\u5DBD\u5D90\u5DB7\u5DBC\u5DC9\u5DCD\u5DD3\u5DD2\u5DD6\u5DDB\u5DEB\u5DF2\u5DF5\u5E0B\u5E1A\u5E19\u5E11\u5E1B\u5E36\u5E37\u5E44\u5E43\u5E40\u5E4E\u5E57\u5E54\u5E5F\u5E62\u5E64\u5E47\u5E75\u5E76\u5E7A\u9EBC\u5E7F\u5EA0\u5EC1\u5EC2\u5EC8\u5ED0\u5ECF"],["9c40","\u5ED6\u5EE3\u5EDD\u5EDA\u5EDB\u5EE2\u5EE1\u5EE8\u5EE9\u5EEC\u5EF1\u5EF3\u5EF0\u5EF4\u5EF8\u5EFE\u5F03\u5F09\u5F5D\u5F5C\u5F0B\u5F11\u5F16\u5F29\u5F2D\u5F38\u5F41\u5F48\u5F4C\u5F4E\u5F2F\u5F51\u5F56\u5F57\u5F59\u5F61\u5F6D\u5F73\u5F77\u5F83\u5F82\u5F7F\u5F8A\u5F88\u5F91\u5F87\u5F9E\u5F99\u5F98\u5FA0\u5FA8\u5FAD\u5FBC\u5FD6\u5FFB\u5FE4\u5FF8\u5FF1\u5FDD\u60B3\u5FFF\u6021\u6060"],["9c80","\u6019\u6010\u6029\u600E\u6031\u601B\u6015\u602B\u6026\u600F\u603A\u605A\u6041\u606A\u6077\u605F\u604A\u6046\u604D\u6063\u6043\u6064\u6042\u606C\u606B\u6059\u6081\u608D\u60E7\u6083\u609A\u6084\u609B\u6096\u6097\u6092\u60A7\u608B\u60E1\u60B8\u60E0\u60D3\u60B4\u5FF0\u60BD\u60C6\u60B5\u60D8\u614D\u6115\u6106\u60F6\u60F7\u6100\u60F4\u60FA\u6103\u6121\u60FB\u60F1\u610D\u610E\u6147\u613E\u6128\u6127\u614A\u613F\u613C\u612C\u6134\u613D\u6142\u6144\u6173\u6177\u6158\u6159\u615A\u616B\u6174\u616F\u6165\u6171\u615F\u615D\u6153\u6175\u6199\u6196\u6187\u61AC\u6194\u619A\u618A\u6191\u61AB\u61AE\u61CC\u61CA\u61C9\u61F7\u61C8\u61C3\u61C6\u61BA\u61CB\u7F79\u61CD\u61E6\u61E3\u61F6\u61FA\u61F4\u61FF\u61FD\u61FC\u61FE\u6200\u6208\u6209\u620D\u620C\u6214\u621B"],["9d40","\u621E\u6221\u622A\u622E\u6230\u6232\u6233\u6241\u624E\u625E\u6263\u625B\u6260\u6268\u627C\u6282\u6289\u627E\u6292\u6293\u6296\u62D4\u6283\u6294\u62D7\u62D1\u62BB\u62CF\u62FF\u62C6\u64D4\u62C8\u62DC\u62CC\u62CA\u62C2\u62C7\u629B\u62C9\u630C\u62EE\u62F1\u6327\u6302\u6308\u62EF\u62F5\u6350\u633E\u634D\u641C\u634F\u6396\u638E\u6380\u63AB\u6376\u63A3\u638F\u6389\u639F\u63B5\u636B"],["9d80","\u6369\u63BE\u63E9\u63C0\u63C6\u63E3\u63C9\u63D2\u63F6\u63C4\u6416\u6434\u6406\u6413\u6426\u6436\u651D\u6417\u6428\u640F\u6467\u646F\u6476\u644E\u652A\u6495\u6493\u64A5\u64A9\u6488\u64BC\u64DA\u64D2\u64C5\u64C7\u64BB\u64D8\u64C2\u64F1\u64E7\u8209\u64E0\u64E1\u62AC\u64E3\u64EF\u652C\u64F6\u64F4\u64F2\u64FA\u6500\u64FD\u6518\u651C\u6505\u6524\u6523\u652B\u6534\u6535\u6537\u6536\u6538\u754B\u6548\u6556\u6555\u654D\u6558\u655E\u655D\u6572\u6578\u6582\u6583\u8B8A\u659B\u659F\u65AB\u65B7\u65C3\u65C6\u65C1\u65C4\u65CC\u65D2\u65DB\u65D9\u65E0\u65E1\u65F1\u6772\u660A\u6603\u65FB\u6773\u6635\u6636\u6634\u661C\u664F\u6644\u6649\u6641\u665E\u665D\u6664\u6667\u6668\u665F\u6662\u6670\u6683\u6688\u668E\u6689\u6684\u6698\u669D\u66C1\u66B9\u66C9\u66BE\u66BC"],["9e40","\u66C4\u66B8\u66D6\u66DA\u66E0\u663F\u66E6\u66E9\u66F0\u66F5\u66F7\u670F\u6716\u671E\u6726\u6727\u9738\u672E\u673F\u6736\u6741\u6738\u6737\u6746\u675E\u6760\u6759\u6763\u6764\u6789\u6770\u67A9\u677C\u676A\u678C\u678B\u67A6\u67A1\u6785\u67B7\u67EF\u67B4\u67EC\u67B3\u67E9\u67B8\u67E4\u67DE\u67DD\u67E2\u67EE\u67B9\u67CE\u67C6\u67E7\u6A9C\u681E\u6846\u6829\u6840\u684D\u6832\u684E"],["9e80","\u68B3\u682B\u6859\u6863\u6877\u687F\u689F\u688F\u68AD\u6894\u689D\u689B\u6883\u6AAE\u68B9\u6874\u68B5\u68A0\u68BA\u690F\u688D\u687E\u6901\u68CA\u6908\u68D8\u6922\u6926\u68E1\u690C\u68CD\u68D4\u68E7\u68D5\u6936\u6912\u6904\u68D7\u68E3\u6925\u68F9\u68E0\u68EF\u6928\u692A\u691A\u6923\u6921\u68C6\u6979\u6977\u695C\u6978\u696B\u6954\u697E\u696E\u6939\u6974\u693D\u6959\u6930\u6961\u695E\u695D\u6981\u696A\u69B2\u69AE\u69D0\u69BF\u69C1\u69D3\u69BE\u69CE\u5BE8\u69CA\u69DD\u69BB\u69C3\u69A7\u6A2E\u6991\u69A0\u699C\u6995\u69B4\u69DE\u69E8\u6A02\u6A1B\u69FF\u6B0A\u69F9\u69F2\u69E7\u6A05\u69B1\u6A1E\u69ED\u6A14\u69EB\u6A0A\u6A12\u6AC1\u6A23\u6A13\u6A44\u6A0C\u6A72\u6A36\u6A78\u6A47\u6A62\u6A59\u6A66\u6A48\u6A38\u6A22\u6A90\u6A8D\u6AA0\u6A84\u6AA2\u6AA3"],["9f40","\u6A97\u8617\u6ABB\u6AC3\u6AC2\u6AB8\u6AB3\u6AAC\u6ADE\u6AD1\u6ADF\u6AAA\u6ADA\u6AEA\u6AFB\u6B05\u8616\u6AFA\u6B12\u6B16\u9B31\u6B1F\u6B38\u6B37\u76DC\u6B39\u98EE\u6B47\u6B43\u6B49\u6B50\u6B59\u6B54\u6B5B\u6B5F\u6B61\u6B78\u6B79\u6B7F\u6B80\u6B84\u6B83\u6B8D\u6B98\u6B95\u6B9E\u6BA4\u6BAA\u6BAB\u6BAF\u6BB2\u6BB1\u6BB3\u6BB7\u6BBC\u6BC6\u6BCB\u6BD3\u6BDF\u6BEC\u6BEB\u6BF3\u6BEF"],["9f80","\u9EBE\u6C08\u6C13\u6C14\u6C1B\u6C24\u6C23\u6C5E\u6C55\u6C62\u6C6A\u6C82\u6C8D\u6C9A\u6C81\u6C9B\u6C7E\u6C68\u6C73\u6C92\u6C90\u6CC4\u6CF1\u6CD3\u6CBD\u6CD7\u6CC5\u6CDD\u6CAE\u6CB1\u6CBE\u6CBA\u6CDB\u6CEF\u6CD9\u6CEA\u6D1F\u884D\u6D36\u6D2B\u6D3D\u6D38\u6D19\u6D35\u6D33\u6D12\u6D0C\u6D63\u6D93\u6D64\u6D5A\u6D79\u6D59\u6D8E\u6D95\u6FE4\u6D85\u6DF9\u6E15\u6E0A\u6DB5\u6DC7\u6DE6\u6DB8\u6DC6\u6DEC\u6DDE\u6DCC\u6DE8\u6DD2\u6DC5\u6DFA\u6DD9\u6DE4\u6DD5\u6DEA\u6DEE\u6E2D\u6E6E\u6E2E\u6E19\u6E72\u6E5F\u6E3E\u6E23\u6E6B\u6E2B\u6E76\u6E4D\u6E1F\u6E43\u6E3A\u6E4E\u6E24\u6EFF\u6E1D\u6E38\u6E82\u6EAA\u6E98\u6EC9\u6EB7\u6ED3\u6EBD\u6EAF\u6EC4\u6EB2\u6ED4\u6ED5\u6E8F\u6EA5\u6EC2\u6E9F\u6F41\u6F11\u704C\u6EEC\u6EF8\u6EFE\u6F3F\u6EF2\u6F31\u6EEF\u6F32\u6ECC"],["e040","\u6F3E\u6F13\u6EF7\u6F86\u6F7A\u6F78\u6F81\u6F80\u6F6F\u6F5B\u6FF3\u6F6D\u6F82\u6F7C\u6F58\u6F8E\u6F91\u6FC2\u6F66\u6FB3\u6FA3\u6FA1\u6FA4\u6FB9\u6FC6\u6FAA\u6FDF\u6FD5\u6FEC\u6FD4\u6FD8\u6FF1\u6FEE\u6FDB\u7009\u700B\u6FFA\u7011\u7001\u700F\u6FFE\u701B\u701A\u6F74\u701D\u7018\u701F\u7030\u703E\u7032\u7051\u7063\u7099\u7092\u70AF\u70F1\u70AC\u70B8\u70B3\u70AE\u70DF\u70CB\u70DD"],["e080","\u70D9\u7109\u70FD\u711C\u7119\u7165\u7155\u7188\u7166\u7162\u714C\u7156\u716C\u718F\u71FB\u7184\u7195\u71A8\u71AC\u71D7\u71B9\u71BE\u71D2\u71C9\u71D4\u71CE\u71E0\u71EC\u71E7\u71F5\u71FC\u71F9\u71FF\u720D\u7210\u721B\u7228\u722D\u722C\u7230\u7232\u723B\u723C\u723F\u7240\u7246\u724B\u7258\u7274\u727E\u7282\u7281\u7287\u7292\u7296\u72A2\u72A7\u72B9\u72B2\u72C3\u72C6\u72C4\u72CE\u72D2\u72E2\u72E0\u72E1\u72F9\u72F7\u500F\u7317\u730A\u731C\u7316\u731D\u7334\u732F\u7329\u7325\u733E\u734E\u734F\u9ED8\u7357\u736A\u7368\u7370\u7378\u7375\u737B\u737A\u73C8\u73B3\u73CE\u73BB\u73C0\u73E5\u73EE\u73DE\u74A2\u7405\u746F\u7425\u73F8\u7432\u743A\u7455\u743F\u745F\u7459\u7441\u745C\u7469\u7470\u7463\u746A\u7476\u747E\u748B\u749E\u74A7\u74CA\u74CF\u74D4\u73F1"],["e140","\u74E0\u74E3\u74E7\u74E9\u74EE\u74F2\u74F0\u74F1\u74F8\u74F7\u7504\u7503\u7505\u750C\u750E\u750D\u7515\u7513\u751E\u7526\u752C\u753C\u7544\u754D\u754A\u7549\u755B\u7546\u755A\u7569\u7564\u7567\u756B\u756D\u7578\u7576\u7586\u7587\u7574\u758A\u7589\u7582\u7594\u759A\u759D\u75A5\u75A3\u75C2\u75B3\u75C3\u75B5\u75BD\u75B8\u75BC\u75B1\u75CD\u75CA\u75D2\u75D9\u75E3\u75DE\u75FE\u75FF"],["e180","\u75FC\u7601\u75F0\u75FA\u75F2\u75F3\u760B\u760D\u7609\u761F\u7627\u7620\u7621\u7622\u7624\u7634\u7630\u763B\u7647\u7648\u7646\u765C\u7658\u7661\u7662\u7668\u7669\u766A\u7667\u766C\u7670\u7672\u7676\u7678\u767C\u7680\u7683\u7688\u768B\u768E\u7696\u7693\u7699\u769A\u76B0\u76B4\u76B8\u76B9\u76BA\u76C2\u76CD\u76D6\u76D2\u76DE\u76E1\u76E5\u76E7\u76EA\u862F\u76FB\u7708\u7707\u7704\u7729\u7724\u771E\u7725\u7726\u771B\u7737\u7738\u7747\u775A\u7768\u776B\u775B\u7765\u777F\u777E\u7779\u778E\u778B\u7791\u77A0\u779E\u77B0\u77B6\u77B9\u77BF\u77BC\u77BD\u77BB\u77C7\u77CD\u77D7\u77DA\u77DC\u77E3\u77EE\u77FC\u780C\u7812\u7926\u7820\u792A\u7845\u788E\u7874\u7886\u787C\u789A\u788C\u78A3\u78B5\u78AA\u78AF\u78D1\u78C6\u78CB\u78D4\u78BE\u78BC\u78C5\u78CA\u78EC"],["e240","\u78E7\u78DA\u78FD\u78F4\u7907\u7912\u7911\u7919\u792C\u792B\u7940\u7960\u7957\u795F\u795A\u7955\u7953\u797A\u797F\u798A\u799D\u79A7\u9F4B\u79AA\u79AE\u79B3\u79B9\u79BA\u79C9\u79D5\u79E7\u79EC\u79E1\u79E3\u7A08\u7A0D\u7A18\u7A19\u7A20\u7A1F\u7980\u7A31\u7A3B\u7A3E\u7A37\u7A43\u7A57\u7A49\u7A61\u7A62\u7A69\u9F9D\u7A70\u7A79\u7A7D\u7A88\u7A97\u7A95\u7A98\u7A96\u7AA9\u7AC8\u7AB0"],["e280","\u7AB6\u7AC5\u7AC4\u7ABF\u9083\u7AC7\u7ACA\u7ACD\u7ACF\u7AD5\u7AD3\u7AD9\u7ADA\u7ADD\u7AE1\u7AE2\u7AE6\u7AED\u7AF0\u7B02\u7B0F\u7B0A\u7B06\u7B33\u7B18\u7B19\u7B1E\u7B35\u7B28\u7B36\u7B50\u7B7A\u7B04\u7B4D\u7B0B\u7B4C\u7B45\u7B75\u7B65\u7B74\u7B67\u7B70\u7B71\u7B6C\u7B6E\u7B9D\u7B98\u7B9F\u7B8D\u7B9C\u7B9A\u7B8B\u7B92\u7B8F\u7B5D\u7B99\u7BCB\u7BC1\u7BCC\u7BCF\u7BB4\u7BC6\u7BDD\u7BE9\u7C11\u7C14\u7BE6\u7BE5\u7C60\u7C00\u7C07\u7C13\u7BF3\u7BF7\u7C17\u7C0D\u7BF6\u7C23\u7C27\u7C2A\u7C1F\u7C37\u7C2B\u7C3D\u7C4C\u7C43\u7C54\u7C4F\u7C40\u7C50\u7C58\u7C5F\u7C64\u7C56\u7C65\u7C6C\u7C75\u7C83\u7C90\u7CA4\u7CAD\u7CA2\u7CAB\u7CA1\u7CA8\u7CB3\u7CB2\u7CB1\u7CAE\u7CB9\u7CBD\u7CC0\u7CC5\u7CC2\u7CD8\u7CD2\u7CDC\u7CE2\u9B3B\u7CEF\u7CF2\u7CF4\u7CF6\u7CFA\u7D06"],["e340","\u7D02\u7D1C\u7D15\u7D0A\u7D45\u7D4B\u7D2E\u7D32\u7D3F\u7D35\u7D46\u7D73\u7D56\u7D4E\u7D72\u7D68\u7D6E\u7D4F\u7D63\u7D93\u7D89\u7D5B\u7D8F\u7D7D\u7D9B\u7DBA\u7DAE\u7DA3\u7DB5\u7DC7\u7DBD\u7DAB\u7E3D\u7DA2\u7DAF\u7DDC\u7DB8\u7D9F\u7DB0\u7DD8\u7DDD\u7DE4\u7DDE\u7DFB\u7DF2\u7DE1\u7E05\u7E0A\u7E23\u7E21\u7E12\u7E31\u7E1F\u7E09\u7E0B\u7E22\u7E46\u7E66\u7E3B\u7E35\u7E39\u7E43\u7E37"],["e380","\u7E32\u7E3A\u7E67\u7E5D\u7E56\u7E5E\u7E59\u7E5A\u7E79\u7E6A\u7E69\u7E7C\u7E7B\u7E83\u7DD5\u7E7D\u8FAE\u7E7F\u7E88\u7E89\u7E8C\u7E92\u7E90\u7E93\u7E94\u7E96\u7E8E\u7E9B\u7E9C\u7F38\u7F3A\u7F45\u7F4C\u7F4D\u7F4E\u7F50\u7F51\u7F55\u7F54\u7F58\u7F5F\u7F60\u7F68\u7F69\u7F67\u7F78\u7F82\u7F86\u7F83\u7F88\u7F87\u7F8C\u7F94\u7F9E\u7F9D\u7F9A\u7FA3\u7FAF\u7FB2\u7FB9\u7FAE\u7FB6\u7FB8\u8B71\u7FC5\u7FC6\u7FCA\u7FD5\u7FD4\u7FE1\u7FE6\u7FE9\u7FF3\u7FF9\u98DC\u8006\u8004\u800B\u8012\u8018\u8019\u801C\u8021\u8028\u803F\u803B\u804A\u8046\u8052\u8058\u805A\u805F\u8062\u8068\u8073\u8072\u8070\u8076\u8079\u807D\u807F\u8084\u8086\u8085\u809B\u8093\u809A\u80AD\u5190\u80AC\u80DB\u80E5\u80D9\u80DD\u80C4\u80DA\u80D6\u8109\u80EF\u80F1\u811B\u8129\u8123\u812F\u814B"],["e440","\u968B\u8146\u813E\u8153\u8151\u80FC\u8171\u816E\u8165\u8166\u8174\u8183\u8188\u818A\u8180\u8182\u81A0\u8195\u81A4\u81A3\u815F\u8193\u81A9\u81B0\u81B5\u81BE\u81B8\u81BD\u81C0\u81C2\u81BA\u81C9\u81CD\u81D1\u81D9\u81D8\u81C8\u81DA\u81DF\u81E0\u81E7\u81FA\u81FB\u81FE\u8201\u8202\u8205\u8207\u820A\u820D\u8210\u8216\u8229\u822B\u8238\u8233\u8240\u8259\u8258\u825D\u825A\u825F\u8264"],["e480","\u8262\u8268\u826A\u826B\u822E\u8271\u8277\u8278\u827E\u828D\u8292\u82AB\u829F\u82BB\u82AC\u82E1\u82E3\u82DF\u82D2\u82F4\u82F3\u82FA\u8393\u8303\u82FB\u82F9\u82DE\u8306\u82DC\u8309\u82D9\u8335\u8334\u8316\u8332\u8331\u8340\u8339\u8350\u8345\u832F\u832B\u8317\u8318\u8385\u839A\u83AA\u839F\u83A2\u8396\u8323\u838E\u8387\u838A\u837C\u83B5\u8373\u8375\u83A0\u8389\u83A8\u83F4\u8413\u83EB\u83CE\u83FD\u8403\u83D8\u840B\u83C1\u83F7\u8407\u83E0\u83F2\u840D\u8422\u8420\u83BD\u8438\u8506\u83FB\u846D\u842A\u843C\u855A\u8484\u8477\u846B\u84AD\u846E\u8482\u8469\u8446\u842C\u846F\u8479\u8435\u84CA\u8462\u84B9\u84BF\u849F\u84D9\u84CD\u84BB\u84DA\u84D0\u84C1\u84C6\u84D6\u84A1\u8521\u84FF\u84F4\u8517\u8518\u852C\u851F\u8515\u8514\u84FC\u8540\u8563\u8558\u8548"],["e540","\u8541\u8602\u854B\u8555\u8580\u85A4\u8588\u8591\u858A\u85A8\u856D\u8594\u859B\u85EA\u8587\u859C\u8577\u857E\u8590\u85C9\u85BA\u85CF\u85B9\u85D0\u85D5\u85DD\u85E5\u85DC\u85F9\u860A\u8613\u860B\u85FE\u85FA\u8606\u8622\u861A\u8630\u863F\u864D\u4E55\u8654\u865F\u8667\u8671\u8693\u86A3\u86A9\u86AA\u868B\u868C\u86B6\u86AF\u86C4\u86C6\u86B0\u86C9\u8823\u86AB\u86D4\u86DE\u86E9\u86EC"],["e580","\u86DF\u86DB\u86EF\u8712\u8706\u8708\u8700\u8703\u86FB\u8711\u8709\u870D\u86F9\u870A\u8734\u873F\u8737\u873B\u8725\u8729\u871A\u8760\u875F\u8778\u874C\u874E\u8774\u8757\u8768\u876E\u8759\u8753\u8763\u876A\u8805\u87A2\u879F\u8782\u87AF\u87CB\u87BD\u87C0\u87D0\u96D6\u87AB\u87C4\u87B3\u87C7\u87C6\u87BB\u87EF\u87F2\u87E0\u880F\u880D\u87FE\u87F6\u87F7\u880E\u87D2\u8811\u8816\u8815\u8822\u8821\u8831\u8836\u8839\u8827\u883B\u8844\u8842\u8852\u8859\u885E\u8862\u886B\u8881\u887E\u889E\u8875\u887D\u88B5\u8872\u8882\u8897\u8892\u88AE\u8899\u88A2\u888D\u88A4\u88B0\u88BF\u88B1\u88C3\u88C4\u88D4\u88D8\u88D9\u88DD\u88F9\u8902\u88FC\u88F4\u88E8\u88F2\u8904\u890C\u890A\u8913\u8943\u891E\u8925\u892A\u892B\u8941\u8944\u893B\u8936\u8938\u894C\u891D\u8960\u895E"],["e640","\u8966\u8964\u896D\u896A\u896F\u8974\u8977\u897E\u8983\u8988\u898A\u8993\u8998\u89A1\u89A9\u89A6\u89AC\u89AF\u89B2\u89BA\u89BD\u89BF\u89C0\u89DA\u89DC\u89DD\u89E7\u89F4\u89F8\u8A03\u8A16\u8A10\u8A0C\u8A1B\u8A1D\u8A25\u8A36\u8A41\u8A5B\u8A52\u8A46\u8A48\u8A7C\u8A6D\u8A6C\u8A62\u8A85\u8A82\u8A84\u8AA8\u8AA1\u8A91\u8AA5\u8AA6\u8A9A\u8AA3\u8AC4\u8ACD\u8AC2\u8ADA\u8AEB\u8AF3\u8AE7"],["e680","\u8AE4\u8AF1\u8B14\u8AE0\u8AE2\u8AF7\u8ADE\u8ADB\u8B0C\u8B07\u8B1A\u8AE1\u8B16\u8B10\u8B17\u8B20\u8B33\u97AB\u8B26\u8B2B\u8B3E\u8B28\u8B41\u8B4C\u8B4F\u8B4E\u8B49\u8B56\u8B5B\u8B5A\u8B6B\u8B5F\u8B6C\u8B6F\u8B74\u8B7D\u8B80\u8B8C\u8B8E\u8B92\u8B93\u8B96\u8B99\u8B9A\u8C3A\u8C41\u8C3F\u8C48\u8C4C\u8C4E\u8C50\u8C55\u8C62\u8C6C\u8C78\u8C7A\u8C82\u8C89\u8C85\u8C8A\u8C8D\u8C8E\u8C94\u8C7C\u8C98\u621D\u8CAD\u8CAA\u8CBD\u8CB2\u8CB3\u8CAE\u8CB6\u8CC8\u8CC1\u8CE4\u8CE3\u8CDA\u8CFD\u8CFA\u8CFB\u8D04\u8D05\u8D0A\u8D07\u8D0F\u8D0D\u8D10\u9F4E\u8D13\u8CCD\u8D14\u8D16\u8D67\u8D6D\u8D71\u8D73\u8D81\u8D99\u8DC2\u8DBE\u8DBA\u8DCF\u8DDA\u8DD6\u8DCC\u8DDB\u8DCB\u8DEA\u8DEB\u8DDF\u8DE3\u8DFC\u8E08\u8E09\u8DFF\u8E1D\u8E1E\u8E10\u8E1F\u8E42\u8E35\u8E30\u8E34\u8E4A"],["e740","\u8E47\u8E49\u8E4C\u8E50\u8E48\u8E59\u8E64\u8E60\u8E2A\u8E63\u8E55\u8E76\u8E72\u8E7C\u8E81\u8E87\u8E85\u8E84\u8E8B\u8E8A\u8E93\u8E91\u8E94\u8E99\u8EAA\u8EA1\u8EAC\u8EB0\u8EC6\u8EB1\u8EBE\u8EC5\u8EC8\u8ECB\u8EDB\u8EE3\u8EFC\u8EFB\u8EEB\u8EFE\u8F0A\u8F05\u8F15\u8F12\u8F19\u8F13\u8F1C\u8F1F\u8F1B\u8F0C\u8F26\u8F33\u8F3B\u8F39\u8F45\u8F42\u8F3E\u8F4C\u8F49\u8F46\u8F4E\u8F57\u8F5C"],["e780","\u8F62\u8F63\u8F64\u8F9C\u8F9F\u8FA3\u8FAD\u8FAF\u8FB7\u8FDA\u8FE5\u8FE2\u8FEA\u8FEF\u9087\u8FF4\u9005\u8FF9\u8FFA\u9011\u9015\u9021\u900D\u901E\u9016\u900B\u9027\u9036\u9035\u9039\u8FF8\u904F\u9050\u9051\u9052\u900E\u9049\u903E\u9056\u9058\u905E\u9068\u906F\u9076\u96A8\u9072\u9082\u907D\u9081\u9080\u908A\u9089\u908F\u90A8\u90AF\u90B1\u90B5\u90E2\u90E4\u6248\u90DB\u9102\u9112\u9119\u9132\u9130\u914A\u9156\u9158\u9163\u9165\u9169\u9173\u9172\u918B\u9189\u9182\u91A2\u91AB\u91AF\u91AA\u91B5\u91B4\u91BA\u91C0\u91C1\u91C9\u91CB\u91D0\u91D6\u91DF\u91E1\u91DB\u91FC\u91F5\u91F6\u921E\u91FF\u9214\u922C\u9215\u9211\u925E\u9257\u9245\u9249\u9264\u9248\u9295\u923F\u924B\u9250\u929C\u9296\u9293\u929B\u925A\u92CF\u92B9\u92B7\u92E9\u930F\u92FA\u9344\u932E"],["e840","\u9319\u9322\u931A\u9323\u933A\u9335\u933B\u935C\u9360\u937C\u936E\u9356\u93B0\u93AC\u93AD\u9394\u93B9\u93D6\u93D7\u93E8\u93E5\u93D8\u93C3\u93DD\u93D0\u93C8\u93E4\u941A\u9414\u9413\u9403\u9407\u9410\u9436\u942B\u9435\u9421\u943A\u9441\u9452\u9444\u945B\u9460\u9462\u945E\u946A\u9229\u9470\u9475\u9477\u947D\u945A\u947C\u947E\u9481\u947F\u9582\u9587\u958A\u9594\u9596\u9598\u9599"],["e880","\u95A0\u95A8\u95A7\u95AD\u95BC\u95BB\u95B9\u95BE\u95CA\u6FF6\u95C3\u95CD\u95CC\u95D5\u95D4\u95D6\u95DC\u95E1\u95E5\u95E2\u9621\u9628\u962E\u962F\u9642\u964C\u964F\u964B\u9677\u965C\u965E\u965D\u965F\u9666\u9672\u966C\u968D\u9698\u9695\u9697\u96AA\u96A7\u96B1\u96B2\u96B0\u96B4\u96B6\u96B8\u96B9\u96CE\u96CB\u96C9\u96CD\u894D\u96DC\u970D\u96D5\u96F9\u9704\u9706\u9708\u9713\u970E\u9711\u970F\u9716\u9719\u9724\u972A\u9730\u9739\u973D\u973E\u9744\u9746\u9748\u9742\u9749\u975C\u9760\u9764\u9766\u9768\u52D2\u976B\u9771\u9779\u9785\u977C\u9781\u977A\u9786\u978B\u978F\u9790\u979C\u97A8\u97A6\u97A3\u97B3\u97B4\u97C3\u97C6\u97C8\u97CB\u97DC\u97ED\u9F4F\u97F2\u7ADF\u97F6\u97F5\u980F\u980C\u9838\u9824\u9821\u9837\u983D\u9846\u984F\u984B\u986B\u986F\u9870"],["e940","\u9871\u9874\u9873\u98AA\u98AF\u98B1\u98B6\u98C4\u98C3\u98C6\u98E9\u98EB\u9903\u9909\u9912\u9914\u9918\u9921\u991D\u991E\u9924\u9920\u992C\u992E\u993D\u993E\u9942\u9949\u9945\u9950\u994B\u9951\u9952\u994C\u9955\u9997\u9998\u99A5\u99AD\u99AE\u99BC\u99DF\u99DB\u99DD\u99D8\u99D1\u99ED\u99EE\u99F1\u99F2\u99FB\u99F8\u9A01\u9A0F\u9A05\u99E2\u9A19\u9A2B\u9A37\u9A45\u9A42\u9A40\u9A43"],["e980","\u9A3E\u9A55\u9A4D\u9A5B\u9A57\u9A5F\u9A62\u9A65\u9A64\u9A69\u9A6B\u9A6A\u9AAD\u9AB0\u9ABC\u9AC0\u9ACF\u9AD1\u9AD3\u9AD4\u9ADE\u9ADF\u9AE2\u9AE3\u9AE6\u9AEF\u9AEB\u9AEE\u9AF4\u9AF1\u9AF7\u9AFB\u9B06\u9B18\u9B1A\u9B1F\u9B22\u9B23\u9B25\u9B27\u9B28\u9B29\u9B2A\u9B2E\u9B2F\u9B32\u9B44\u9B43\u9B4F\u9B4D\u9B4E\u9B51\u9B58\u9B74\u9B93\u9B83\u9B91\u9B96\u9B97\u9B9F\u9BA0\u9BA8\u9BB4\u9BC0\u9BCA\u9BB9\u9BC6\u9BCF\u9BD1\u9BD2\u9BE3\u9BE2\u9BE4\u9BD4\u9BE1\u9C3A\u9BF2\u9BF1\u9BF0\u9C15\u9C14\u9C09\u9C13\u9C0C\u9C06\u9C08\u9C12\u9C0A\u9C04\u9C2E\u9C1B\u9C25\u9C24\u9C21\u9C30\u9C47\u9C32\u9C46\u9C3E\u9C5A\u9C60\u9C67\u9C76\u9C78\u9CE7\u9CEC\u9CF0\u9D09\u9D08\u9CEB\u9D03\u9D06\u9D2A\u9D26\u9DAF\u9D23\u9D1F\u9D44\u9D15\u9D12\u9D41\u9D3F\u9D3E\u9D46\u9D48"],["ea40","\u9D5D\u9D5E\u9D64\u9D51\u9D50\u9D59\u9D72\u9D89\u9D87\u9DAB\u9D6F\u9D7A\u9D9A\u9DA4\u9DA9\u9DB2\u9DC4\u9DC1\u9DBB\u9DB8\u9DBA\u9DC6\u9DCF\u9DC2\u9DD9\u9DD3\u9DF8\u9DE6\u9DED\u9DEF\u9DFD\u9E1A\u9E1B\u9E1E\u9E75\u9E79\u9E7D\u9E81\u9E88\u9E8B\u9E8C\u9E92\u9E95\u9E91\u9E9D\u9EA5\u9EA9\u9EB8\u9EAA\u9EAD\u9761\u9ECC\u9ECE\u9ECF\u9ED0\u9ED4\u9EDC\u9EDE\u9EDD\u9EE0\u9EE5\u9EE8\u9EEF"],["ea80","\u9EF4\u9EF6\u9EF7\u9EF9\u9EFB\u9EFC\u9EFD\u9F07\u9F08\u76B7\u9F15\u9F21\u9F2C\u9F3E\u9F4A\u9F52\u9F54\u9F63\u9F5F\u9F60\u9F61\u9F66\u9F67\u9F6C\u9F6A\u9F77\u9F72\u9F76\u9F95\u9F9C\u9FA0\u582F\u69C7\u9059\u7464\u51DC\u7199"],["ed40","\u7E8A\u891C\u9348\u9288\u84DC\u4FC9\u70BB\u6631\u68C8\u92F9\u66FB\u5F45\u4E28\u4EE1\u4EFC\u4F00\u4F03\u4F39\u4F56\u4F92\u4F8A\u4F9A\u4F94\u4FCD\u5040\u5022\u4FFF\u501E\u5046\u5070\u5042\u5094\u50F4\u50D8\u514A\u5164\u519D\u51BE\u51EC\u5215\u529C\u52A6\u52C0\u52DB\u5300\u5307\u5324\u5372\u5393\u53B2\u53DD\uFA0E\u549C\u548A\u54A9\u54FF\u5586\u5759\u5765\u57AC\u57C8\u57C7\uFA0F"],["ed80","\uFA10\u589E\u58B2\u590B\u5953\u595B\u595D\u5963\u59A4\u59BA\u5B56\u5BC0\u752F\u5BD8\u5BEC\u5C1E\u5CA6\u5CBA\u5CF5\u5D27\u5D53\uFA11\u5D42\u5D6D\u5DB8\u5DB9\u5DD0\u5F21\u5F34\u5F67\u5FB7\u5FDE\u605D\u6085\u608A\u60DE\u60D5\u6120\u60F2\u6111\u6137\u6130\u6198\u6213\u62A6\u63F5\u6460\u649D\u64CE\u654E\u6600\u6615\u663B\u6609\u662E\u661E\u6624\u6665\u6657\u6659\uFA12\u6673\u6699\u66A0\u66B2\u66BF\u66FA\u670E\uF929\u6766\u67BB\u6852\u67C0\u6801\u6844\u68CF\uFA13\u6968\uFA14\u6998\u69E2\u6A30\u6A6B\u6A46\u6A73\u6A7E\u6AE2\u6AE4\u6BD6\u6C3F\u6C5C\u6C86\u6C6F\u6CDA\u6D04\u6D87\u6D6F\u6D96\u6DAC\u6DCF\u6DF8\u6DF2\u6DFC\u6E39\u6E5C\u6E27\u6E3C\u6EBF\u6F88\u6FB5\u6FF5\u7005\u7007\u7028\u7085\u70AB\u710F\u7104\u715C\u7146\u7147\uFA15\u71C1\u71FE\u72B1"],["ee40","\u72BE\u7324\uFA16\u7377\u73BD\u73C9\u73D6\u73E3\u73D2\u7407\u73F5\u7426\u742A\u7429\u742E\u7462\u7489\u749F\u7501\u756F\u7682\u769C\u769E\u769B\u76A6\uFA17\u7746\u52AF\u7821\u784E\u7864\u787A\u7930\uFA18\uFA19\uFA1A\u7994\uFA1B\u799B\u7AD1\u7AE7\uFA1C\u7AEB\u7B9E\uFA1D\u7D48\u7D5C\u7DB7\u7DA0\u7DD6\u7E52\u7F47\u7FA1\uFA1E\u8301\u8362\u837F\u83C7\u83F6\u8448\u84B4\u8553\u8559"],["ee80","\u856B\uFA1F\u85B0\uFA20\uFA21\u8807\u88F5\u8A12\u8A37\u8A79\u8AA7\u8ABE\u8ADF\uFA22\u8AF6\u8B53\u8B7F\u8CF0\u8CF4\u8D12\u8D76\uFA23\u8ECF\uFA24\uFA25\u9067\u90DE\uFA26\u9115\u9127\u91DA\u91D7\u91DE\u91ED\u91EE\u91E4\u91E5\u9206\u9210\u920A\u923A\u9240\u923C\u924E\u9259\u9251\u9239\u9267\u92A7\u9277\u9278\u92E7\u92D7\u92D9\u92D0\uFA27\u92D5\u92E0\u92D3\u9325\u9321\u92FB\uFA28\u931E\u92FF\u931D\u9302\u9370\u9357\u93A4\u93C6\u93DE\u93F8\u9431\u9445\u9448\u9592\uF9DC\uFA29\u969D\u96AF\u9733\u973B\u9743\u974D\u974F\u9751\u9755\u9857\u9865\uFA2A\uFA2B\u9927\uFA2C\u999E\u9A4E\u9AD9\u9ADC\u9B75\u9B72\u9B8F\u9BB1\u9BBB\u9C00\u9D70\u9D6B\uFA2D\u9E19\u9ED1"],["eeef","\u2170",9,"\uFFE2\uFFE4\uFF07\uFF02"],["f040","\uE000",62],["f080","\uE03F",124],["f140","\uE0BC",62],["f180","\uE0FB",124],["f240","\uE178",62],["f280","\uE1B7",124],["f340","\uE234",62],["f380","\uE273",124],["f440","\uE2F0",62],["f480","\uE32F",124],["f540","\uE3AC",62],["f580","\uE3EB",124],["f640","\uE468",62],["f680","\uE4A7",124],["f740","\uE524",62],["f780","\uE563",124],["f840","\uE5E0",62],["f880","\uE61F",124],["f940","\uE69C"],["fa40","\u2170",9,"\u2160",9,"\uFFE2\uFFE4\uFF07\uFF02\u3231\u2116\u2121\u2235\u7E8A\u891C\u9348\u9288\u84DC\u4FC9\u70BB\u6631\u68C8\u92F9\u66FB\u5F45\u4E28\u4EE1\u4EFC\u4F00\u4F03\u4F39\u4F56\u4F92\u4F8A\u4F9A\u4F94\u4FCD\u5040\u5022\u4FFF\u501E\u5046\u5070\u5042\u5094\u50F4\u50D8\u514A"],["fa80","\u5164\u519D\u51BE\u51EC\u5215\u529C\u52A6\u52C0\u52DB\u5300\u5307\u5324\u5372\u5393\u53B2\u53DD\uFA0E\u549C\u548A\u54A9\u54FF\u5586\u5759\u5765\u57AC\u57C8\u57C7\uFA0F\uFA10\u589E\u58B2\u590B\u5953\u595B\u595D\u5963\u59A4\u59BA\u5B56\u5BC0\u752F\u5BD8\u5BEC\u5C1E\u5CA6\u5CBA\u5CF5\u5D27\u5D53\uFA11\u5D42\u5D6D\u5DB8\u5DB9\u5DD0\u5F21\u5F34\u5F67\u5FB7\u5FDE\u605D\u6085\u608A\u60DE\u60D5\u6120\u60F2\u6111\u6137\u6130\u6198\u6213\u62A6\u63F5\u6460\u649D\u64CE\u654E\u6600\u6615\u663B\u6609\u662E\u661E\u6624\u6665\u6657\u6659\uFA12\u6673\u6699\u66A0\u66B2\u66BF\u66FA\u670E\uF929\u6766\u67BB\u6852\u67C0\u6801\u6844\u68CF\uFA13\u6968\uFA14\u6998\u69E2\u6A30\u6A6B\u6A46\u6A73\u6A7E\u6AE2\u6AE4\u6BD6\u6C3F\u6C5C\u6C86\u6C6F\u6CDA\u6D04\u6D87\u6D6F"],["fb40","\u6D96\u6DAC\u6DCF\u6DF8\u6DF2\u6DFC\u6E39\u6E5C\u6E27\u6E3C\u6EBF\u6F88\u6FB5\u6FF5\u7005\u7007\u7028\u7085\u70AB\u710F\u7104\u715C\u7146\u7147\uFA15\u71C1\u71FE\u72B1\u72BE\u7324\uFA16\u7377\u73BD\u73C9\u73D6\u73E3\u73D2\u7407\u73F5\u7426\u742A\u7429\u742E\u7462\u7489\u749F\u7501\u756F\u7682\u769C\u769E\u769B\u76A6\uFA17\u7746\u52AF\u7821\u784E\u7864\u787A\u7930\uFA18\uFA19"],["fb80","\uFA1A\u7994\uFA1B\u799B\u7AD1\u7AE7\uFA1C\u7AEB\u7B9E\uFA1D\u7D48\u7D5C\u7DB7\u7DA0\u7DD6\u7E52\u7F47\u7FA1\uFA1E\u8301\u8362\u837F\u83C7\u83F6\u8448\u84B4\u8553\u8559\u856B\uFA1F\u85B0\uFA20\uFA21\u8807\u88F5\u8A12\u8A37\u8A79\u8AA7\u8ABE\u8ADF\uFA22\u8AF6\u8B53\u8B7F\u8CF0\u8CF4\u8D12\u8D76\uFA23\u8ECF\uFA24\uFA25\u9067\u90DE\uFA26\u9115\u9127\u91DA\u91D7\u91DE\u91ED\u91EE\u91E4\u91E5\u9206\u9210\u920A\u923A\u9240\u923C\u924E\u9259\u9251\u9239\u9267\u92A7\u9277\u9278\u92E7\u92D7\u92D9\u92D0\uFA27\u92D5\u92E0\u92D3\u9325\u9321\u92FB\uFA28\u931E\u92FF\u931D\u9302\u9370\u9357\u93A4\u93C6\u93DE\u93F8\u9431\u9445\u9448\u9592\uF9DC\uFA29\u969D\u96AF\u9733\u973B\u9743\u974D\u974F\u9751\u9755\u9857\u9865\uFA2A\uFA2B\u9927\uFA2C\u999E\u9A4E\u9AD9"],["fc40","\u9ADC\u9B75\u9B72\u9B8F\u9BB1\u9BBB\u9C00\u9D70\u9D6B\uFA2D\u9E19\u9ED1"]]});var Hje=S((Okr,Nqt)=>{Nqt.exports=[["0","\0",127],["8ea1","\uFF61",62],["a1a1","\u3000\u3001\u3002\uFF0C\uFF0E\u30FB\uFF1A\uFF1B\uFF1F\uFF01\u309B\u309C\xB4\uFF40\xA8\uFF3E\uFFE3\uFF3F\u30FD\u30FE\u309D\u309E\u3003\u4EDD\u3005\u3006\u3007\u30FC\u2015\u2010\uFF0F\uFF3C\uFF5E\u2225\uFF5C\u2026\u2025\u2018\u2019\u201C\u201D\uFF08\uFF09\u3014\u3015\uFF3B\uFF3D\uFF5B\uFF5D\u3008",9,"\uFF0B\uFF0D\xB1\xD7\xF7\uFF1D\u2260\uFF1C\uFF1E\u2266\u2267\u221E\u2234\u2642\u2640\xB0\u2032\u2033\u2103\uFFE5\uFF04\uFFE0\uFFE1\uFF05\uFF03\uFF06\uFF0A\uFF20\xA7\u2606\u2605\u25CB\u25CF\u25CE\u25C7"],["a2a1","\u25C6\u25A1\u25A0\u25B3\u25B2\u25BD\u25BC\u203B\u3012\u2192\u2190\u2191\u2193\u3013"],["a2ba","\u2208\u220B\u2286\u2287\u2282\u2283\u222A\u2229"],["a2ca","\u2227\u2228\uFFE2\u21D2\u21D4\u2200\u2203"],["a2dc","\u2220\u22A5\u2312\u2202\u2207\u2261\u2252\u226A\u226B\u221A\u223D\u221D\u2235\u222B\u222C"],["a2f2","\u212B\u2030\u266F\u266D\u266A\u2020\u2021\xB6"],["a2fe","\u25EF"],["a3b0","\uFF10",9],["a3c1","\uFF21",25],["a3e1","\uFF41",25],["a4a1","\u3041",82],["a5a1","\u30A1",85],["a6a1","\u0391",16,"\u03A3",6],["a6c1","\u03B1",16,"\u03C3",6],["a7a1","\u0410",5,"\u0401\u0416",25],["a7d1","\u0430",5,"\u0451\u0436",25],["a8a1","\u2500\u2502\u250C\u2510\u2518\u2514\u251C\u252C\u2524\u2534\u253C\u2501\u2503\u250F\u2513\u251B\u2517\u2523\u2533\u252B\u253B\u254B\u2520\u252F\u2528\u2537\u253F\u251D\u2530\u2525\u2538\u2542"],["ada1","\u2460",19,"\u2160",9],["adc0","\u3349\u3314\u3322\u334D\u3318\u3327\u3303\u3336\u3351\u3357\u330D\u3326\u3323\u332B\u334A\u333B\u339C\u339D\u339E\u338E\u338F\u33C4\u33A1"],["addf","\u337B\u301D\u301F\u2116\u33CD\u2121\u32A4",4,"\u3231\u3232\u3239\u337E\u337D\u337C\u2252\u2261\u222B\u222E\u2211\u221A\u22A5\u2220\u221F\u22BF\u2235\u2229\u222A"],["b0a1","\u4E9C\u5516\u5A03\u963F\u54C0\u611B\u6328\u59F6\u9022\u8475\u831C\u7A50\u60AA\u63E1\u6E25\u65ED\u8466\u82A6\u9BF5\u6893\u5727\u65A1\u6271\u5B9B\u59D0\u867B\u98F4\u7D62\u7DBE\u9B8E\u6216\u7C9F\u88B7\u5B89\u5EB5\u6309\u6697\u6848\u95C7\u978D\u674F\u4EE5\u4F0A\u4F4D\u4F9D\u5049\u56F2\u5937\u59D4\u5A01\u5C09\u60DF\u610F\u6170\u6613\u6905\u70BA\u754F\u7570\u79FB\u7DAD\u7DEF\u80C3\u840E\u8863\u8B02\u9055\u907A\u533B\u4E95\u4EA5\u57DF\u80B2\u90C1\u78EF\u4E00\u58F1\u6EA2\u9038\u7A32\u8328\u828B\u9C2F\u5141\u5370\u54BD\u54E1\u56E0\u59FB\u5F15\u98F2\u6DEB\u80E4\u852D"],["b1a1","\u9662\u9670\u96A0\u97FB\u540B\u53F3\u5B87\u70CF\u7FBD\u8FC2\u96E8\u536F\u9D5C\u7ABA\u4E11\u7893\u81FC\u6E26\u5618\u5504\u6B1D\u851A\u9C3B\u59E5\u53A9\u6D66\u74DC\u958F\u5642\u4E91\u904B\u96F2\u834F\u990C\u53E1\u55B6\u5B30\u5F71\u6620\u66F3\u6804\u6C38\u6CF3\u6D29\u745B\u76C8\u7A4E\u9834\u82F1\u885B\u8A60\u92ED\u6DB2\u75AB\u76CA\u99C5\u60A6\u8B01\u8D8A\u95B2\u698E\u53AD\u5186\u5712\u5830\u5944\u5BB4\u5EF6\u6028\u63A9\u63F4\u6CBF\u6F14\u708E\u7114\u7159\u71D5\u733F\u7E01\u8276\u82D1\u8597\u9060\u925B\u9D1B\u5869\u65BC\u6C5A\u7525\u51F9\u592E\u5965\u5F80\u5FDC"],["b2a1","\u62BC\u65FA\u6A2A\u6B27\u6BB4\u738B\u7FC1\u8956\u9D2C\u9D0E\u9EC4\u5CA1\u6C96\u837B\u5104\u5C4B\u61B6\u81C6\u6876\u7261\u4E59\u4FFA\u5378\u6069\u6E29\u7A4F\u97F3\u4E0B\u5316\u4EEE\u4F55\u4F3D\u4FA1\u4F73\u52A0\u53EF\u5609\u590F\u5AC1\u5BB6\u5BE1\u79D1\u6687\u679C\u67B6\u6B4C\u6CB3\u706B\u73C2\u798D\u79BE\u7A3C\u7B87\u82B1\u82DB\u8304\u8377\u83EF\u83D3\u8766\u8AB2\u5629\u8CA8\u8FE6\u904E\u971E\u868A\u4FC4\u5CE8\u6211\u7259\u753B\u81E5\u82BD\u86FE\u8CC0\u96C5\u9913\u99D5\u4ECB\u4F1A\u89E3\u56DE\u584A\u58CA\u5EFB\u5FEB\u602A\u6094\u6062\u61D0\u6212\u62D0\u6539"],["b3a1","\u9B41\u6666\u68B0\u6D77\u7070\u754C\u7686\u7D75\u82A5\u87F9\u958B\u968E\u8C9D\u51F1\u52BE\u5916\u54B3\u5BB3\u5D16\u6168\u6982\u6DAF\u788D\u84CB\u8857\u8A72\u93A7\u9AB8\u6D6C\u99A8\u86D9\u57A3\u67FF\u86CE\u920E\u5283\u5687\u5404\u5ED3\u62E1\u64B9\u683C\u6838\u6BBB\u7372\u78BA\u7A6B\u899A\u89D2\u8D6B\u8F03\u90ED\u95A3\u9694\u9769\u5B66\u5CB3\u697D\u984D\u984E\u639B\u7B20\u6A2B\u6A7F\u68B6\u9C0D\u6F5F\u5272\u559D\u6070\u62EC\u6D3B\u6E07\u6ED1\u845B\u8910\u8F44\u4E14\u9C39\u53F6\u691B\u6A3A\u9784\u682A\u515C\u7AC3\u84B2\u91DC\u938C\u565B\u9D28\u6822\u8305\u8431"],["b4a1","\u7CA5\u5208\u82C5\u74E6\u4E7E\u4F83\u51A0\u5BD2\u520A\u52D8\u52E7\u5DFB\u559A\u582A\u59E6\u5B8C\u5B98\u5BDB\u5E72\u5E79\u60A3\u611F\u6163\u61BE\u63DB\u6562\u67D1\u6853\u68FA\u6B3E\u6B53\u6C57\u6F22\u6F97\u6F45\u74B0\u7518\u76E3\u770B\u7AFF\u7BA1\u7C21\u7DE9\u7F36\u7FF0\u809D\u8266\u839E\u89B3\u8ACC\u8CAB\u9084\u9451\u9593\u9591\u95A2\u9665\u97D3\u9928\u8218\u4E38\u542B\u5CB8\u5DCC\u73A9\u764C\u773C\u5CA9\u7FEB\u8D0B\u96C1\u9811\u9854\u9858\u4F01\u4F0E\u5371\u559C\u5668\u57FA\u5947\u5B09\u5BC4\u5C90\u5E0C\u5E7E\u5FCC\u63EE\u673A\u65D7\u65E2\u671F\u68CB\u68C4"],["b5a1","\u6A5F\u5E30\u6BC5\u6C17\u6C7D\u757F\u7948\u5B63\u7A00\u7D00\u5FBD\u898F\u8A18\u8CB4\u8D77\u8ECC\u8F1D\u98E2\u9A0E\u9B3C\u4E80\u507D\u5100\u5993\u5B9C\u622F\u6280\u64EC\u6B3A\u72A0\u7591\u7947\u7FA9\u87FB\u8ABC\u8B70\u63AC\u83CA\u97A0\u5409\u5403\u55AB\u6854\u6A58\u8A70\u7827\u6775\u9ECD\u5374\u5BA2\u811A\u8650\u9006\u4E18\u4E45\u4EC7\u4F11\u53CA\u5438\u5BAE\u5F13\u6025\u6551\u673D\u6C42\u6C72\u6CE3\u7078\u7403\u7A76\u7AAE\u7B08\u7D1A\u7CFE\u7D66\u65E7\u725B\u53BB\u5C45\u5DE8\u62D2\u62E0\u6319\u6E20\u865A\u8A31\u8DDD\u92F8\u6F01\u79A6\u9B5A\u4EA8\u4EAB\u4EAC"],["b6a1","\u4F9B\u4FA0\u50D1\u5147\u7AF6\u5171\u51F6\u5354\u5321\u537F\u53EB\u55AC\u5883\u5CE1\u5F37\u5F4A\u602F\u6050\u606D\u631F\u6559\u6A4B\u6CC1\u72C2\u72ED\u77EF\u80F8\u8105\u8208\u854E\u90F7\u93E1\u97FF\u9957\u9A5A\u4EF0\u51DD\u5C2D\u6681\u696D\u5C40\u66F2\u6975\u7389\u6850\u7C81\u50C5\u52E4\u5747\u5DFE\u9326\u65A4\u6B23\u6B3D\u7434\u7981\u79BD\u7B4B\u7DCA\u82B9\u83CC\u887F\u895F\u8B39\u8FD1\u91D1\u541F\u9280\u4E5D\u5036\u53E5\u533A\u72D7\u7396\u77E9\u82E6\u8EAF\u99C6\u99C8\u99D2\u5177\u611A\u865E\u55B0\u7A7A\u5076\u5BD3\u9047\u9685\u4E32\u6ADB\u91E7\u5C51\u5C48"],["b7a1","\u6398\u7A9F\u6C93\u9774\u8F61\u7AAA\u718A\u9688\u7C82\u6817\u7E70\u6851\u936C\u52F2\u541B\u85AB\u8A13\u7FA4\u8ECD\u90E1\u5366\u8888\u7941\u4FC2\u50BE\u5211\u5144\u5553\u572D\u73EA\u578B\u5951\u5F62\u5F84\u6075\u6176\u6167\u61A9\u63B2\u643A\u656C\u666F\u6842\u6E13\u7566\u7A3D\u7CFB\u7D4C\u7D99\u7E4B\u7F6B\u830E\u834A\u86CD\u8A08\u8A63\u8B66\u8EFD\u981A\u9D8F\u82B8\u8FCE\u9BE8\u5287\u621F\u6483\u6FC0\u9699\u6841\u5091\u6B20\u6C7A\u6F54\u7A74\u7D50\u8840\u8A23\u6708\u4EF6\u5039\u5026\u5065\u517C\u5238\u5263\u55A7\u570F\u5805\u5ACC\u5EFA\u61B2\u61F8\u62F3\u6372"],["b8a1","\u691C\u6A29\u727D\u72AC\u732E\u7814\u786F\u7D79\u770C\u80A9\u898B\u8B19\u8CE2\u8ED2\u9063\u9375\u967A\u9855\u9A13\u9E78\u5143\u539F\u53B3\u5E7B\u5F26\u6E1B\u6E90\u7384\u73FE\u7D43\u8237\u8A00\u8AFA\u9650\u4E4E\u500B\u53E4\u547C\u56FA\u59D1\u5B64\u5DF1\u5EAB\u5F27\u6238\u6545\u67AF\u6E56\u72D0\u7CCA\u88B4\u80A1\u80E1\u83F0\u864E\u8A87\u8DE8\u9237\u96C7\u9867\u9F13\u4E94\u4E92\u4F0D\u5348\u5449\u543E\u5A2F\u5F8C\u5FA1\u609F\u68A7\u6A8E\u745A\u7881\u8A9E\u8AA4\u8B77\u9190\u4E5E\u9BC9\u4EA4\u4F7C\u4FAF\u5019\u5016\u5149\u516C\u529F\u52B9\u52FE\u539A\u53E3\u5411"],["b9a1","\u540E\u5589\u5751\u57A2\u597D\u5B54\u5B5D\u5B8F\u5DE5\u5DE7\u5DF7\u5E78\u5E83\u5E9A\u5EB7\u5F18\u6052\u614C\u6297\u62D8\u63A7\u653B\u6602\u6643\u66F4\u676D\u6821\u6897\u69CB\u6C5F\u6D2A\u6D69\u6E2F\u6E9D\u7532\u7687\u786C\u7A3F\u7CE0\u7D05\u7D18\u7D5E\u7DB1\u8015\u8003\u80AF\u80B1\u8154\u818F\u822A\u8352\u884C\u8861\u8B1B\u8CA2\u8CFC\u90CA\u9175\u9271\u783F\u92FC\u95A4\u964D\u9805\u9999\u9AD8\u9D3B\u525B\u52AB\u53F7\u5408\u58D5\u62F7\u6FE0\u8C6A\u8F5F\u9EB9\u514B\u523B\u544A\u56FD\u7A40\u9177\u9D60\u9ED2\u7344\u6F09\u8170\u7511\u5FFD\u60DA\u9AA8\u72DB\u8FBC"],["baa1","\u6B64\u9803\u4ECA\u56F0\u5764\u58BE\u5A5A\u6068\u61C7\u660F\u6606\u6839\u68B1\u6DF7\u75D5\u7D3A\u826E\u9B42\u4E9B\u4F50\u53C9\u5506\u5D6F\u5DE6\u5DEE\u67FB\u6C99\u7473\u7802\u8A50\u9396\u88DF\u5750\u5EA7\u632B\u50B5\u50AC\u518D\u6700\u54C9\u585E\u59BB\u5BB0\u5F69\u624D\u63A1\u683D\u6B73\u6E08\u707D\u91C7\u7280\u7815\u7826\u796D\u658E\u7D30\u83DC\u88C1\u8F09\u969B\u5264\u5728\u6750\u7F6A\u8CA1\u51B4\u5742\u962A\u583A\u698A\u80B4\u54B2\u5D0E\u57FC\u7895\u9DFA\u4F5C\u524A\u548B\u643E\u6628\u6714\u67F5\u7A84\u7B56\u7D22\u932F\u685C\u9BAD\u7B39\u5319\u518A\u5237"],["bba1","\u5BDF\u62F6\u64AE\u64E6\u672D\u6BBA\u85A9\u96D1\u7690\u9BD6\u634C\u9306\u9BAB\u76BF\u6652\u4E09\u5098\u53C2\u5C71\u60E8\u6492\u6563\u685F\u71E6\u73CA\u7523\u7B97\u7E82\u8695\u8B83\u8CDB\u9178\u9910\u65AC\u66AB\u6B8B\u4ED5\u4ED4\u4F3A\u4F7F\u523A\u53F8\u53F2\u55E3\u56DB\u58EB\u59CB\u59C9\u59FF\u5B50\u5C4D\u5E02\u5E2B\u5FD7\u601D\u6307\u652F\u5B5C\u65AF\u65BD\u65E8\u679D\u6B62\u6B7B\u6C0F\u7345\u7949\u79C1\u7CF8\u7D19\u7D2B\u80A2\u8102\u81F3\u8996\u8A5E\u8A69\u8A66\u8A8C\u8AEE\u8CC7\u8CDC\u96CC\u98FC\u6B6F\u4E8B\u4F3C\u4F8D\u5150\u5B57\u5BFA\u6148\u6301\u6642"],["bca1","\u6B21\u6ECB\u6CBB\u723E\u74BD\u75D4\u78C1\u793A\u800C\u8033\u81EA\u8494\u8F9E\u6C50\u9E7F\u5F0F\u8B58\u9D2B\u7AFA\u8EF8\u5B8D\u96EB\u4E03\u53F1\u57F7\u5931\u5AC9\u5BA4\u6089\u6E7F\u6F06\u75BE\u8CEA\u5B9F\u8500\u7BE0\u5072\u67F4\u829D\u5C61\u854A\u7E1E\u820E\u5199\u5C04\u6368\u8D66\u659C\u716E\u793E\u7D17\u8005\u8B1D\u8ECA\u906E\u86C7\u90AA\u501F\u52FA\u5C3A\u6753\u707C\u7235\u914C\u91C8\u932B\u82E5\u5BC2\u5F31\u60F9\u4E3B\u53D6\u5B88\u624B\u6731\u6B8A\u72E9\u73E0\u7A2E\u816B\u8DA3\u9152\u9996\u5112\u53D7\u546A\u5BFF\u6388\u6A39\u7DAC\u9700\u56DA\u53CE\u5468"],["bda1","\u5B97\u5C31\u5DDE\u4FEE\u6101\u62FE\u6D32\u79C0\u79CB\u7D42\u7E4D\u7FD2\u81ED\u821F\u8490\u8846\u8972\u8B90\u8E74\u8F2F\u9031\u914B\u916C\u96C6\u919C\u4EC0\u4F4F\u5145\u5341\u5F93\u620E\u67D4\u6C41\u6E0B\u7363\u7E26\u91CD\u9283\u53D4\u5919\u5BBF\u6DD1\u795D\u7E2E\u7C9B\u587E\u719F\u51FA\u8853\u8FF0\u4FCA\u5CFB\u6625\u77AC\u7AE3\u821C\u99FF\u51C6\u5FAA\u65EC\u696F\u6B89\u6DF3\u6E96\u6F64\u76FE\u7D14\u5DE1\u9075\u9187\u9806\u51E6\u521D\u6240\u6691\u66D9\u6E1A\u5EB6\u7DD2\u7F72\u66F8\u85AF\u85F7\u8AF8\u52A9\u53D9\u5973\u5E8F\u5F90\u6055\u92E4\u9664\u50B7\u511F"],["bea1","\u52DD\u5320\u5347\u53EC\u54E8\u5546\u5531\u5617\u5968\u59BE\u5A3C\u5BB5\u5C06\u5C0F\u5C11\u5C1A\u5E84\u5E8A\u5EE0\u5F70\u627F\u6284\u62DB\u638C\u6377\u6607\u660C\u662D\u6676\u677E\u68A2\u6A1F\u6A35\u6CBC\u6D88\u6E09\u6E58\u713C\u7126\u7167\u75C7\u7701\u785D\u7901\u7965\u79F0\u7AE0\u7B11\u7CA7\u7D39\u8096\u83D6\u848B\u8549\u885D\u88F3\u8A1F\u8A3C\u8A54\u8A73\u8C61\u8CDE\u91A4\u9266\u937E\u9418\u969C\u9798\u4E0A\u4E08\u4E1E\u4E57\u5197\u5270\u57CE\u5834\u58CC\u5B22\u5E38\u60C5\u64FE\u6761\u6756\u6D44\u72B6\u7573\u7A63\u84B8\u8B72\u91B8\u9320\u5631\u57F4\u98FE"],["bfa1","\u62ED\u690D\u6B96\u71ED\u7E54\u8077\u8272\u89E6\u98DF\u8755\u8FB1\u5C3B\u4F38\u4FE1\u4FB5\u5507\u5A20\u5BDD\u5BE9\u5FC3\u614E\u632F\u65B0\u664B\u68EE\u699B\u6D78\u6DF1\u7533\u75B9\u771F\u795E\u79E6\u7D33\u81E3\u82AF\u85AA\u89AA\u8A3A\u8EAB\u8F9B\u9032\u91DD\u9707\u4EBA\u4EC1\u5203\u5875\u58EC\u5C0B\u751A\u5C3D\u814E\u8A0A\u8FC5\u9663\u976D\u7B25\u8ACF\u9808\u9162\u56F3\u53A8\u9017\u5439\u5782\u5E25\u63A8\u6C34\u708A\u7761\u7C8B\u7FE0\u8870\u9042\u9154\u9310\u9318\u968F\u745E\u9AC4\u5D07\u5D69\u6570\u67A2\u8DA8\u96DB\u636E\u6749\u6919\u83C5\u9817\u96C0\u88FE"],["c0a1","\u6F84\u647A\u5BF8\u4E16\u702C\u755D\u662F\u51C4\u5236\u52E2\u59D3\u5F81\u6027\u6210\u653F\u6574\u661F\u6674\u68F2\u6816\u6B63\u6E05\u7272\u751F\u76DB\u7CBE\u8056\u58F0\u88FD\u897F\u8AA0\u8A93\u8ACB\u901D\u9192\u9752\u9759\u6589\u7A0E\u8106\u96BB\u5E2D\u60DC\u621A\u65A5\u6614\u6790\u77F3\u7A4D\u7C4D\u7E3E\u810A\u8CAC\u8D64\u8DE1\u8E5F\u78A9\u5207\u62D9\u63A5\u6442\u6298\u8A2D\u7A83\u7BC0\u8AAC\u96EA\u7D76\u820C\u8749\u4ED9\u5148\u5343\u5360\u5BA3\u5C02\u5C16\u5DDD\u6226\u6247\u64B0\u6813\u6834\u6CC9\u6D45\u6D17\u67D3\u6F5C\u714E\u717D\u65CB\u7A7F\u7BAD\u7DDA"],["c1a1","\u7E4A\u7FA8\u817A\u821B\u8239\u85A6\u8A6E\u8CCE\u8DF5\u9078\u9077\u92AD\u9291\u9583\u9BAE\u524D\u5584\u6F38\u7136\u5168\u7985\u7E55\u81B3\u7CCE\u564C\u5851\u5CA8\u63AA\u66FE\u66FD\u695A\u72D9\u758F\u758E\u790E\u7956\u79DF\u7C97\u7D20\u7D44\u8607\u8A34\u963B\u9061\u9F20\u50E7\u5275\u53CC\u53E2\u5009\u55AA\u58EE\u594F\u723D\u5B8B\u5C64\u531D\u60E3\u60F3\u635C\u6383\u633F\u63BB\u64CD\u65E9\u66F9\u5DE3\u69CD\u69FD\u6F15\u71E5\u4E89\u75E9\u76F8\u7A93\u7CDF\u7DCF\u7D9C\u8061\u8349\u8358\u846C\u84BC\u85FB\u88C5\u8D70\u9001\u906D\u9397\u971C\u9A12\u50CF\u5897\u618E"],["c2a1","\u81D3\u8535\u8D08\u9020\u4FC3\u5074\u5247\u5373\u606F\u6349\u675F\u6E2C\u8DB3\u901F\u4FD7\u5C5E\u8CCA\u65CF\u7D9A\u5352\u8896\u5176\u63C3\u5B58\u5B6B\u5C0A\u640D\u6751\u905C\u4ED6\u591A\u592A\u6C70\u8A51\u553E\u5815\u59A5\u60F0\u6253\u67C1\u8235\u6955\u9640\u99C4\u9A28\u4F53\u5806\u5BFE\u8010\u5CB1\u5E2F\u5F85\u6020\u614B\u6234\u66FF\u6CF0\u6EDE\u80CE\u817F\u82D4\u888B\u8CB8\u9000\u902E\u968A\u9EDB\u9BDB\u4EE3\u53F0\u5927\u7B2C\u918D\u984C\u9DF9\u6EDD\u7027\u5353\u5544\u5B85\u6258\u629E\u62D3\u6CA2\u6FEF\u7422\u8A17\u9438\u6FC1\u8AFE\u8338\u51E7\u86F8\u53EA"],["c3a1","\u53E9\u4F46\u9054\u8FB0\u596A\u8131\u5DFD\u7AEA\u8FBF\u68DA\u8C37\u72F8\u9C48\u6A3D\u8AB0\u4E39\u5358\u5606\u5766\u62C5\u63A2\u65E6\u6B4E\u6DE1\u6E5B\u70AD\u77ED\u7AEF\u7BAA\u7DBB\u803D\u80C6\u86CB\u8A95\u935B\u56E3\u58C7\u5F3E\u65AD\u6696\u6A80\u6BB5\u7537\u8AC7\u5024\u77E5\u5730\u5F1B\u6065\u667A\u6C60\u75F4\u7A1A\u7F6E\u81F4\u8718\u9045\u99B3\u7BC9\u755C\u7AF9\u7B51\u84C4\u9010\u79E9\u7A92\u8336\u5AE1\u7740\u4E2D\u4EF2\u5B99\u5FE0\u62BD\u663C\u67F1\u6CE8\u866B\u8877\u8A3B\u914E\u92F3\u99D0\u6A17\u7026\u732A\u82E7\u8457\u8CAF\u4E01\u5146\u51CB\u558B\u5BF5"],["c4a1","\u5E16\u5E33\u5E81\u5F14\u5F35\u5F6B\u5FB4\u61F2\u6311\u66A2\u671D\u6F6E\u7252\u753A\u773A\u8074\u8139\u8178\u8776\u8ABF\u8ADC\u8D85\u8DF3\u929A\u9577\u9802\u9CE5\u52C5\u6357\u76F4\u6715\u6C88\u73CD\u8CC3\u93AE\u9673\u6D25\u589C\u690E\u69CC\u8FFD\u939A\u75DB\u901A\u585A\u6802\u63B4\u69FB\u4F43\u6F2C\u67D8\u8FBB\u8526\u7DB4\u9354\u693F\u6F70\u576A\u58F7\u5B2C\u7D2C\u722A\u540A\u91E3\u9DB4\u4EAD\u4F4E\u505C\u5075\u5243\u8C9E\u5448\u5824\u5B9A\u5E1D\u5E95\u5EAD\u5EF7\u5F1F\u608C\u62B5\u633A\u63D0\u68AF\u6C40\u7887\u798E\u7A0B\u7DE0\u8247\u8A02\u8AE6\u8E44\u9013"],["c5a1","\u90B8\u912D\u91D8\u9F0E\u6CE5\u6458\u64E2\u6575\u6EF4\u7684\u7B1B\u9069\u93D1\u6EBA\u54F2\u5FB9\u64A4\u8F4D\u8FED\u9244\u5178\u586B\u5929\u5C55\u5E97\u6DFB\u7E8F\u751C\u8CBC\u8EE2\u985B\u70B9\u4F1D\u6BBF\u6FB1\u7530\u96FB\u514E\u5410\u5835\u5857\u59AC\u5C60\u5F92\u6597\u675C\u6E21\u767B\u83DF\u8CED\u9014\u90FD\u934D\u7825\u783A\u52AA\u5EA6\u571F\u5974\u6012\u5012\u515A\u51AC\u51CD\u5200\u5510\u5854\u5858\u5957\u5B95\u5CF6\u5D8B\u60BC\u6295\u642D\u6771\u6843\u68BC\u68DF\u76D7\u6DD8\u6E6F\u6D9B\u706F\u71C8\u5F53\u75D8\u7977\u7B49\u7B54\u7B52\u7CD6\u7D71\u5230"],["c6a1","\u8463\u8569\u85E4\u8A0E\u8B04\u8C46\u8E0F\u9003\u900F\u9419\u9676\u982D\u9A30\u95D8\u50CD\u52D5\u540C\u5802\u5C0E\u61A7\u649E\u6D1E\u77B3\u7AE5\u80F4\u8404\u9053\u9285\u5CE0\u9D07\u533F\u5F97\u5FB3\u6D9C\u7279\u7763\u79BF\u7BE4\u6BD2\u72EC\u8AAD\u6803\u6A61\u51F8\u7A81\u6934\u5C4A\u9CF6\u82EB\u5BC5\u9149\u701E\u5678\u5C6F\u60C7\u6566\u6C8C\u8C5A\u9041\u9813\u5451\u66C7\u920D\u5948\u90A3\u5185\u4E4D\u51EA\u8599\u8B0E\u7058\u637A\u934B\u6962\u99B4\u7E04\u7577\u5357\u6960\u8EDF\u96E3\u6C5D\u4E8C\u5C3C\u5F10\u8FE9\u5302\u8CD1\u8089\u8679\u5EFF\u65E5\u4E73\u5165"],["c7a1","\u5982\u5C3F\u97EE\u4EFB\u598A\u5FCD\u8A8D\u6FE1\u79B0\u7962\u5BE7\u8471\u732B\u71B1\u5E74\u5FF5\u637B\u649A\u71C3\u7C98\u4E43\u5EFC\u4E4B\u57DC\u56A2\u60A9\u6FC3\u7D0D\u80FD\u8133\u81BF\u8FB2\u8997\u86A4\u5DF4\u628A\u64AD\u8987\u6777\u6CE2\u6D3E\u7436\u7834\u5A46\u7F75\u82AD\u99AC\u4FF3\u5EC3\u62DD\u6392\u6557\u676F\u76C3\u724C\u80CC\u80BA\u8F29\u914D\u500D\u57F9\u5A92\u6885\u6973\u7164\u72FD\u8CB7\u58F2\u8CE0\u966A\u9019\u877F\u79E4\u77E7\u8429\u4F2F\u5265\u535A\u62CD\u67CF\u6CCA\u767D\u7B94\u7C95\u8236\u8584\u8FEB\u66DD\u6F20\u7206\u7E1B\u83AB\u99C1\u9EA6"],["c8a1","\u51FD\u7BB1\u7872\u7BB8\u8087\u7B48\u6AE8\u5E61\u808C\u7551\u7560\u516B\u9262\u6E8C\u767A\u9197\u9AEA\u4F10\u7F70\u629C\u7B4F\u95A5\u9CE9\u567A\u5859\u86E4\u96BC\u4F34\u5224\u534A\u53CD\u53DB\u5E06\u642C\u6591\u677F\u6C3E\u6C4E\u7248\u72AF\u73ED\u7554\u7E41\u822C\u85E9\u8CA9\u7BC4\u91C6\u7169\u9812\u98EF\u633D\u6669\u756A\u76E4\u78D0\u8543\u86EE\u532A\u5351\u5426\u5983\u5E87\u5F7C\u60B2\u6249\u6279\u62AB\u6590\u6BD4\u6CCC\u75B2\u76AE\u7891\u79D8\u7DCB\u7F77\u80A5\u88AB\u8AB9\u8CBB\u907F\u975E\u98DB\u6A0B\u7C38\u5099\u5C3E\u5FAE\u6787\u6BD8\u7435\u7709\u7F8E"],["c9a1","\u9F3B\u67CA\u7A17\u5339\u758B\u9AED\u5F66\u819D\u83F1\u8098\u5F3C\u5FC5\u7562\u7B46\u903C\u6867\u59EB\u5A9B\u7D10\u767E\u8B2C\u4FF5\u5F6A\u6A19\u6C37\u6F02\u74E2\u7968\u8868\u8A55\u8C79\u5EDF\u63CF\u75C5\u79D2\u82D7\u9328\u92F2\u849C\u86ED\u9C2D\u54C1\u5F6C\u658C\u6D5C\u7015\u8CA7\u8CD3\u983B\u654F\u74F6\u4E0D\u4ED8\u57E0\u592B\u5A66\u5BCC\u51A8\u5E03\u5E9C\u6016\u6276\u6577\u65A7\u666E\u6D6E\u7236\u7B26\u8150\u819A\u8299\u8B5C\u8CA0\u8CE6\u8D74\u961C\u9644\u4FAE\u64AB\u6B66\u821E\u8461\u856A\u90E8\u5C01\u6953\u98A8\u847A\u8557\u4F0F\u526F\u5FA9\u5E45\u670D"],["caa1","\u798F\u8179\u8907\u8986\u6DF5\u5F17\u6255\u6CB8\u4ECF\u7269\u9B92\u5206\u543B\u5674\u58B3\u61A4\u626E\u711A\u596E\u7C89\u7CDE\u7D1B\u96F0\u6587\u805E\u4E19\u4F75\u5175\u5840\u5E63\u5E73\u5F0A\u67C4\u4E26\u853D\u9589\u965B\u7C73\u9801\u50FB\u58C1\u7656\u78A7\u5225\u77A5\u8511\u7B86\u504F\u5909\u7247\u7BC7\u7DE8\u8FBA\u8FD4\u904D\u4FBF\u52C9\u5A29\u5F01\u97AD\u4FDD\u8217\u92EA\u5703\u6355\u6B69\u752B\u88DC\u8F14\u7A42\u52DF\u5893\u6155\u620A\u66AE\u6BCD\u7C3F\u83E9\u5023\u4FF8\u5305\u5446\u5831\u5949\u5B9D\u5CF0\u5CEF\u5D29\u5E96\u62B1\u6367\u653E\u65B9\u670B"],["cba1","\u6CD5\u6CE1\u70F9\u7832\u7E2B\u80DE\u82B3\u840C\u84EC\u8702\u8912\u8A2A\u8C4A\u90A6\u92D2\u98FD\u9CF3\u9D6C\u4E4F\u4EA1\u508D\u5256\u574A\u59A8\u5E3D\u5FD8\u5FD9\u623F\u66B4\u671B\u67D0\u68D2\u5192\u7D21\u80AA\u81A8\u8B00\u8C8C\u8CBF\u927E\u9632\u5420\u982C\u5317\u50D5\u535C\u58A8\u64B2\u6734\u7267\u7766\u7A46\u91E6\u52C3\u6CA1\u6B86\u5800\u5E4C\u5954\u672C\u7FFB\u51E1\u76C6\u6469\u78E8\u9B54\u9EBB\u57CB\u59B9\u6627\u679A\u6BCE\u54E9\u69D9\u5E55\u819C\u6795\u9BAA\u67FE\u9C52\u685D\u4EA6\u4FE3\u53C8\u62B9\u672B\u6CAB\u8FC4\u4FAD\u7E6D\u9EBF\u4E07\u6162\u6E80"],["cca1","\u6F2B\u8513\u5473\u672A\u9B45\u5DF3\u7B95\u5CAC\u5BC6\u871C\u6E4A\u84D1\u7A14\u8108\u5999\u7C8D\u6C11\u7720\u52D9\u5922\u7121\u725F\u77DB\u9727\u9D61\u690B\u5A7F\u5A18\u51A5\u540D\u547D\u660E\u76DF\u8FF7\u9298\u9CF4\u59EA\u725D\u6EC5\u514D\u68C9\u7DBF\u7DEC\u9762\u9EBA\u6478\u6A21\u8302\u5984\u5B5F\u6BDB\u731B\u76F2\u7DB2\u8017\u8499\u5132\u6728\u9ED9\u76EE\u6762\u52FF\u9905\u5C24\u623B\u7C7E\u8CB0\u554F\u60B6\u7D0B\u9580\u5301\u4E5F\u51B6\u591C\u723A\u8036\u91CE\u5F25\u77E2\u5384\u5F79\u7D04\u85AC\u8A33\u8E8D\u9756\u67F3\u85AE\u9453\u6109\u6108\u6CB9\u7652"],["cda1","\u8AED\u8F38\u552F\u4F51\u512A\u52C7\u53CB\u5BA5\u5E7D\u60A0\u6182\u63D6\u6709\u67DA\u6E67\u6D8C\u7336\u7337\u7531\u7950\u88D5\u8A98\u904A\u9091\u90F5\u96C4\u878D\u5915\u4E88\u4F59\u4E0E\u8A89\u8F3F\u9810\u50AD\u5E7C\u5996\u5BB9\u5EB8\u63DA\u63FA\u64C1\u66DC\u694A\u69D8\u6D0B\u6EB6\u7194\u7528\u7AAF\u7F8A\u8000\u8449\u84C9\u8981\u8B21\u8E0A\u9065\u967D\u990A\u617E\u6291\u6B32\u6C83\u6D74\u7FCC\u7FFC\u6DC0\u7F85\u87BA\u88F8\u6765\u83B1\u983C\u96F7\u6D1B\u7D61\u843D\u916A\u4E71\u5375\u5D50\u6B04\u6FEB\u85CD\u862D\u89A7\u5229\u540F\u5C65\u674E\u68A8\u7406\u7483"],["cea1","\u75E2\u88CF\u88E1\u91CC\u96E2\u9678\u5F8B\u7387\u7ACB\u844E\u63A0\u7565\u5289\u6D41\u6E9C\u7409\u7559\u786B\u7C92\u9686\u7ADC\u9F8D\u4FB6\u616E\u65C5\u865C\u4E86\u4EAE\u50DA\u4E21\u51CC\u5BEE\u6599\u6881\u6DBC\u731F\u7642\u77AD\u7A1C\u7CE7\u826F\u8AD2\u907C\u91CF\u9675\u9818\u529B\u7DD1\u502B\u5398\u6797\u6DCB\u71D0\u7433\u81E8\u8F2A\u96A3\u9C57\u9E9F\u7460\u5841\u6D99\u7D2F\u985E\u4EE4\u4F36\u4F8B\u51B7\u52B1\u5DBA\u601C\u73B2\u793C\u82D3\u9234\u96B7\u96F6\u970A\u9E97\u9F62\u66A6\u6B74\u5217\u52A3\u70C8\u88C2\u5EC9\u604B\u6190\u6F23\u7149\u7C3E\u7DF4\u806F"],["cfa1","\u84EE\u9023\u932C\u5442\u9B6F\u6AD3\u7089\u8CC2\u8DEF\u9732\u52B4\u5A41\u5ECA\u5F04\u6717\u697C\u6994\u6D6A\u6F0F\u7262\u72FC\u7BED\u8001\u807E\u874B\u90CE\u516D\u9E93\u7984\u808B\u9332\u8AD6\u502D\u548C\u8A71\u6B6A\u8CC4\u8107\u60D1\u67A0\u9DF2\u4E99\u4E98\u9C10\u8A6B\u85C1\u8568\u6900\u6E7E\u7897\u8155"],["d0a1","\u5F0C\u4E10\u4E15\u4E2A\u4E31\u4E36\u4E3C\u4E3F\u4E42\u4E56\u4E58\u4E82\u4E85\u8C6B\u4E8A\u8212\u5F0D\u4E8E\u4E9E\u4E9F\u4EA0\u4EA2\u4EB0\u4EB3\u4EB6\u4ECE\u4ECD\u4EC4\u4EC6\u4EC2\u4ED7\u4EDE\u4EED\u4EDF\u4EF7\u4F09\u4F5A\u4F30\u4F5B\u4F5D\u4F57\u4F47\u4F76\u4F88\u4F8F\u4F98\u4F7B\u4F69\u4F70\u4F91\u4F6F\u4F86\u4F96\u5118\u4FD4\u4FDF\u4FCE\u4FD8\u4FDB\u4FD1\u4FDA\u4FD0\u4FE4\u4FE5\u501A\u5028\u5014\u502A\u5025\u5005\u4F1C\u4FF6\u5021\u5029\u502C\u4FFE\u4FEF\u5011\u5006\u5043\u5047\u6703\u5055\u5050\u5048\u505A\u5056\u506C\u5078\u5080\u509A\u5085\u50B4\u50B2"],["d1a1","\u50C9\u50CA\u50B3\u50C2\u50D6\u50DE\u50E5\u50ED\u50E3\u50EE\u50F9\u50F5\u5109\u5101\u5102\u5116\u5115\u5114\u511A\u5121\u513A\u5137\u513C\u513B\u513F\u5140\u5152\u514C\u5154\u5162\u7AF8\u5169\u516A\u516E\u5180\u5182\u56D8\u518C\u5189\u518F\u5191\u5193\u5195\u5196\u51A4\u51A6\u51A2\u51A9\u51AA\u51AB\u51B3\u51B1\u51B2\u51B0\u51B5\u51BD\u51C5\u51C9\u51DB\u51E0\u8655\u51E9\u51ED\u51F0\u51F5\u51FE\u5204\u520B\u5214\u520E\u5227\u522A\u522E\u5233\u5239\u524F\u5244\u524B\u524C\u525E\u5254\u526A\u5274\u5269\u5273\u527F\u527D\u528D\u5294\u5292\u5271\u5288\u5291\u8FA8"],["d2a1","\u8FA7\u52AC\u52AD\u52BC\u52B5\u52C1\u52CD\u52D7\u52DE\u52E3\u52E6\u98ED\u52E0\u52F3\u52F5\u52F8\u52F9\u5306\u5308\u7538\u530D\u5310\u530F\u5315\u531A\u5323\u532F\u5331\u5333\u5338\u5340\u5346\u5345\u4E17\u5349\u534D\u51D6\u535E\u5369\u536E\u5918\u537B\u5377\u5382\u5396\u53A0\u53A6\u53A5\u53AE\u53B0\u53B6\u53C3\u7C12\u96D9\u53DF\u66FC\u71EE\u53EE\u53E8\u53ED\u53FA\u5401\u543D\u5440\u542C\u542D\u543C\u542E\u5436\u5429\u541D\u544E\u548F\u5475\u548E\u545F\u5471\u5477\u5470\u5492\u547B\u5480\u5476\u5484\u5490\u5486\u54C7\u54A2\u54B8\u54A5\u54AC\u54C4\u54C8\u54A8"],["d3a1","\u54AB\u54C2\u54A4\u54BE\u54BC\u54D8\u54E5\u54E6\u550F\u5514\u54FD\u54EE\u54ED\u54FA\u54E2\u5539\u5540\u5563\u554C\u552E\u555C\u5545\u5556\u5557\u5538\u5533\u555D\u5599\u5580\u54AF\u558A\u559F\u557B\u557E\u5598\u559E\u55AE\u557C\u5583\u55A9\u5587\u55A8\u55DA\u55C5\u55DF\u55C4\u55DC\u55E4\u55D4\u5614\u55F7\u5616\u55FE\u55FD\u561B\u55F9\u564E\u5650\u71DF\u5634\u5636\u5632\u5638\u566B\u5664\u562F\u566C\u566A\u5686\u5680\u568A\u56A0\u5694\u568F\u56A5\u56AE\u56B6\u56B4\u56C2\u56BC\u56C1\u56C3\u56C0\u56C8\u56CE\u56D1\u56D3\u56D7\u56EE\u56F9\u5700\u56FF\u5704\u5709"],["d4a1","\u5708\u570B\u570D\u5713\u5718\u5716\u55C7\u571C\u5726\u5737\u5738\u574E\u573B\u5740\u574F\u5769\u57C0\u5788\u5761\u577F\u5789\u5793\u57A0\u57B3\u57A4\u57AA\u57B0\u57C3\u57C6\u57D4\u57D2\u57D3\u580A\u57D6\u57E3\u580B\u5819\u581D\u5872\u5821\u5862\u584B\u5870\u6BC0\u5852\u583D\u5879\u5885\u58B9\u589F\u58AB\u58BA\u58DE\u58BB\u58B8\u58AE\u58C5\u58D3\u58D1\u58D7\u58D9\u58D8\u58E5\u58DC\u58E4\u58DF\u58EF\u58FA\u58F9\u58FB\u58FC\u58FD\u5902\u590A\u5910\u591B\u68A6\u5925\u592C\u592D\u5932\u5938\u593E\u7AD2\u5955\u5950\u594E\u595A\u5958\u5962\u5960\u5967\u596C\u5969"],["d5a1","\u5978\u5981\u599D\u4F5E\u4FAB\u59A3\u59B2\u59C6\u59E8\u59DC\u598D\u59D9\u59DA\u5A25\u5A1F\u5A11\u5A1C\u5A09\u5A1A\u5A40\u5A6C\u5A49\u5A35\u5A36\u5A62\u5A6A\u5A9A\u5ABC\u5ABE\u5ACB\u5AC2\u5ABD\u5AE3\u5AD7\u5AE6\u5AE9\u5AD6\u5AFA\u5AFB\u5B0C\u5B0B\u5B16\u5B32\u5AD0\u5B2A\u5B36\u5B3E\u5B43\u5B45\u5B40\u5B51\u5B55\u5B5A\u5B5B\u5B65\u5B69\u5B70\u5B73\u5B75\u5B78\u6588\u5B7A\u5B80\u5B83\u5BA6\u5BB8\u5BC3\u5BC7\u5BC9\u5BD4\u5BD0\u5BE4\u5BE6\u5BE2\u5BDE\u5BE5\u5BEB\u5BF0\u5BF6\u5BF3\u5C05\u5C07\u5C08\u5C0D\u5C13\u5C20\u5C22\u5C28\u5C38\u5C39\u5C41\u5C46\u5C4E\u5C53"],["d6a1","\u5C50\u5C4F\u5B71\u5C6C\u5C6E\u4E62\u5C76\u5C79\u5C8C\u5C91\u5C94\u599B\u5CAB\u5CBB\u5CB6\u5CBC\u5CB7\u5CC5\u5CBE\u5CC7\u5CD9\u5CE9\u5CFD\u5CFA\u5CED\u5D8C\u5CEA\u5D0B\u5D15\u5D17\u5D5C\u5D1F\u5D1B\u5D11\u5D14\u5D22\u5D1A\u5D19\u5D18\u5D4C\u5D52\u5D4E\u5D4B\u5D6C\u5D73\u5D76\u5D87\u5D84\u5D82\u5DA2\u5D9D\u5DAC\u5DAE\u5DBD\u5D90\u5DB7\u5DBC\u5DC9\u5DCD\u5DD3\u5DD2\u5DD6\u5DDB\u5DEB\u5DF2\u5DF5\u5E0B\u5E1A\u5E19\u5E11\u5E1B\u5E36\u5E37\u5E44\u5E43\u5E40\u5E4E\u5E57\u5E54\u5E5F\u5E62\u5E64\u5E47\u5E75\u5E76\u5E7A\u9EBC\u5E7F\u5EA0\u5EC1\u5EC2\u5EC8\u5ED0\u5ECF"],["d7a1","\u5ED6\u5EE3\u5EDD\u5EDA\u5EDB\u5EE2\u5EE1\u5EE8\u5EE9\u5EEC\u5EF1\u5EF3\u5EF0\u5EF4\u5EF8\u5EFE\u5F03\u5F09\u5F5D\u5F5C\u5F0B\u5F11\u5F16\u5F29\u5F2D\u5F38\u5F41\u5F48\u5F4C\u5F4E\u5F2F\u5F51\u5F56\u5F57\u5F59\u5F61\u5F6D\u5F73\u5F77\u5F83\u5F82\u5F7F\u5F8A\u5F88\u5F91\u5F87\u5F9E\u5F99\u5F98\u5FA0\u5FA8\u5FAD\u5FBC\u5FD6\u5FFB\u5FE4\u5FF8\u5FF1\u5FDD\u60B3\u5FFF\u6021\u6060\u6019\u6010\u6029\u600E\u6031\u601B\u6015\u602B\u6026\u600F\u603A\u605A\u6041\u606A\u6077\u605F\u604A\u6046\u604D\u6063\u6043\u6064\u6042\u606C\u606B\u6059\u6081\u608D\u60E7\u6083\u609A"],["d8a1","\u6084\u609B\u6096\u6097\u6092\u60A7\u608B\u60E1\u60B8\u60E0\u60D3\u60B4\u5FF0\u60BD\u60C6\u60B5\u60D8\u614D\u6115\u6106\u60F6\u60F7\u6100\u60F4\u60FA\u6103\u6121\u60FB\u60F1\u610D\u610E\u6147\u613E\u6128\u6127\u614A\u613F\u613C\u612C\u6134\u613D\u6142\u6144\u6173\u6177\u6158\u6159\u615A\u616B\u6174\u616F\u6165\u6171\u615F\u615D\u6153\u6175\u6199\u6196\u6187\u61AC\u6194\u619A\u618A\u6191\u61AB\u61AE\u61CC\u61CA\u61C9\u61F7\u61C8\u61C3\u61C6\u61BA\u61CB\u7F79\u61CD\u61E6\u61E3\u61F6\u61FA\u61F4\u61FF\u61FD\u61FC\u61FE\u6200\u6208\u6209\u620D\u620C\u6214\u621B"],["d9a1","\u621E\u6221\u622A\u622E\u6230\u6232\u6233\u6241\u624E\u625E\u6263\u625B\u6260\u6268\u627C\u6282\u6289\u627E\u6292\u6293\u6296\u62D4\u6283\u6294\u62D7\u62D1\u62BB\u62CF\u62FF\u62C6\u64D4\u62C8\u62DC\u62CC\u62CA\u62C2\u62C7\u629B\u62C9\u630C\u62EE\u62F1\u6327\u6302\u6308\u62EF\u62F5\u6350\u633E\u634D\u641C\u634F\u6396\u638E\u6380\u63AB\u6376\u63A3\u638F\u6389\u639F\u63B5\u636B\u6369\u63BE\u63E9\u63C0\u63C6\u63E3\u63C9\u63D2\u63F6\u63C4\u6416\u6434\u6406\u6413\u6426\u6436\u651D\u6417\u6428\u640F\u6467\u646F\u6476\u644E\u652A\u6495\u6493\u64A5\u64A9\u6488\u64BC"],["daa1","\u64DA\u64D2\u64C5\u64C7\u64BB\u64D8\u64C2\u64F1\u64E7\u8209\u64E0\u64E1\u62AC\u64E3\u64EF\u652C\u64F6\u64F4\u64F2\u64FA\u6500\u64FD\u6518\u651C\u6505\u6524\u6523\u652B\u6534\u6535\u6537\u6536\u6538\u754B\u6548\u6556\u6555\u654D\u6558\u655E\u655D\u6572\u6578\u6582\u6583\u8B8A\u659B\u659F\u65AB\u65B7\u65C3\u65C6\u65C1\u65C4\u65CC\u65D2\u65DB\u65D9\u65E0\u65E1\u65F1\u6772\u660A\u6603\u65FB\u6773\u6635\u6636\u6634\u661C\u664F\u6644\u6649\u6641\u665E\u665D\u6664\u6667\u6668\u665F\u6662\u6670\u6683\u6688\u668E\u6689\u6684\u6698\u669D\u66C1\u66B9\u66C9\u66BE\u66BC"],["dba1","\u66C4\u66B8\u66D6\u66DA\u66E0\u663F\u66E6\u66E9\u66F0\u66F5\u66F7\u670F\u6716\u671E\u6726\u6727\u9738\u672E\u673F\u6736\u6741\u6738\u6737\u6746\u675E\u6760\u6759\u6763\u6764\u6789\u6770\u67A9\u677C\u676A\u678C\u678B\u67A6\u67A1\u6785\u67B7\u67EF\u67B4\u67EC\u67B3\u67E9\u67B8\u67E4\u67DE\u67DD\u67E2\u67EE\u67B9\u67CE\u67C6\u67E7\u6A9C\u681E\u6846\u6829\u6840\u684D\u6832\u684E\u68B3\u682B\u6859\u6863\u6877\u687F\u689F\u688F\u68AD\u6894\u689D\u689B\u6883\u6AAE\u68B9\u6874\u68B5\u68A0\u68BA\u690F\u688D\u687E\u6901\u68CA\u6908\u68D8\u6922\u6926\u68E1\u690C\u68CD"],["dca1","\u68D4\u68E7\u68D5\u6936\u6912\u6904\u68D7\u68E3\u6925\u68F9\u68E0\u68EF\u6928\u692A\u691A\u6923\u6921\u68C6\u6979\u6977\u695C\u6978\u696B\u6954\u697E\u696E\u6939\u6974\u693D\u6959\u6930\u6961\u695E\u695D\u6981\u696A\u69B2\u69AE\u69D0\u69BF\u69C1\u69D3\u69BE\u69CE\u5BE8\u69CA\u69DD\u69BB\u69C3\u69A7\u6A2E\u6991\u69A0\u699C\u6995\u69B4\u69DE\u69E8\u6A02\u6A1B\u69FF\u6B0A\u69F9\u69F2\u69E7\u6A05\u69B1\u6A1E\u69ED\u6A14\u69EB\u6A0A\u6A12\u6AC1\u6A23\u6A13\u6A44\u6A0C\u6A72\u6A36\u6A78\u6A47\u6A62\u6A59\u6A66\u6A48\u6A38\u6A22\u6A90\u6A8D\u6AA0\u6A84\u6AA2\u6AA3"],["dda1","\u6A97\u8617\u6ABB\u6AC3\u6AC2\u6AB8\u6AB3\u6AAC\u6ADE\u6AD1\u6ADF\u6AAA\u6ADA\u6AEA\u6AFB\u6B05\u8616\u6AFA\u6B12\u6B16\u9B31\u6B1F\u6B38\u6B37\u76DC\u6B39\u98EE\u6B47\u6B43\u6B49\u6B50\u6B59\u6B54\u6B5B\u6B5F\u6B61\u6B78\u6B79\u6B7F\u6B80\u6B84\u6B83\u6B8D\u6B98\u6B95\u6B9E\u6BA4\u6BAA\u6BAB\u6BAF\u6BB2\u6BB1\u6BB3\u6BB7\u6BBC\u6BC6\u6BCB\u6BD3\u6BDF\u6BEC\u6BEB\u6BF3\u6BEF\u9EBE\u6C08\u6C13\u6C14\u6C1B\u6C24\u6C23\u6C5E\u6C55\u6C62\u6C6A\u6C82\u6C8D\u6C9A\u6C81\u6C9B\u6C7E\u6C68\u6C73\u6C92\u6C90\u6CC4\u6CF1\u6CD3\u6CBD\u6CD7\u6CC5\u6CDD\u6CAE\u6CB1\u6CBE"],["dea1","\u6CBA\u6CDB\u6CEF\u6CD9\u6CEA\u6D1F\u884D\u6D36\u6D2B\u6D3D\u6D38\u6D19\u6D35\u6D33\u6D12\u6D0C\u6D63\u6D93\u6D64\u6D5A\u6D79\u6D59\u6D8E\u6D95\u6FE4\u6D85\u6DF9\u6E15\u6E0A\u6DB5\u6DC7\u6DE6\u6DB8\u6DC6\u6DEC\u6DDE\u6DCC\u6DE8\u6DD2\u6DC5\u6DFA\u6DD9\u6DE4\u6DD5\u6DEA\u6DEE\u6E2D\u6E6E\u6E2E\u6E19\u6E72\u6E5F\u6E3E\u6E23\u6E6B\u6E2B\u6E76\u6E4D\u6E1F\u6E43\u6E3A\u6E4E\u6E24\u6EFF\u6E1D\u6E38\u6E82\u6EAA\u6E98\u6EC9\u6EB7\u6ED3\u6EBD\u6EAF\u6EC4\u6EB2\u6ED4\u6ED5\u6E8F\u6EA5\u6EC2\u6E9F\u6F41\u6F11\u704C\u6EEC\u6EF8\u6EFE\u6F3F\u6EF2\u6F31\u6EEF\u6F32\u6ECC"],["dfa1","\u6F3E\u6F13\u6EF7\u6F86\u6F7A\u6F78\u6F81\u6F80\u6F6F\u6F5B\u6FF3\u6F6D\u6F82\u6F7C\u6F58\u6F8E\u6F91\u6FC2\u6F66\u6FB3\u6FA3\u6FA1\u6FA4\u6FB9\u6FC6\u6FAA\u6FDF\u6FD5\u6FEC\u6FD4\u6FD8\u6FF1\u6FEE\u6FDB\u7009\u700B\u6FFA\u7011\u7001\u700F\u6FFE\u701B\u701A\u6F74\u701D\u7018\u701F\u7030\u703E\u7032\u7051\u7063\u7099\u7092\u70AF\u70F1\u70AC\u70B8\u70B3\u70AE\u70DF\u70CB\u70DD\u70D9\u7109\u70FD\u711C\u7119\u7165\u7155\u7188\u7166\u7162\u714C\u7156\u716C\u718F\u71FB\u7184\u7195\u71A8\u71AC\u71D7\u71B9\u71BE\u71D2\u71C9\u71D4\u71CE\u71E0\u71EC\u71E7\u71F5\u71FC"],["e0a1","\u71F9\u71FF\u720D\u7210\u721B\u7228\u722D\u722C\u7230\u7232\u723B\u723C\u723F\u7240\u7246\u724B\u7258\u7274\u727E\u7282\u7281\u7287\u7292\u7296\u72A2\u72A7\u72B9\u72B2\u72C3\u72C6\u72C4\u72CE\u72D2\u72E2\u72E0\u72E1\u72F9\u72F7\u500F\u7317\u730A\u731C\u7316\u731D\u7334\u732F\u7329\u7325\u733E\u734E\u734F\u9ED8\u7357\u736A\u7368\u7370\u7378\u7375\u737B\u737A\u73C8\u73B3\u73CE\u73BB\u73C0\u73E5\u73EE\u73DE\u74A2\u7405\u746F\u7425\u73F8\u7432\u743A\u7455\u743F\u745F\u7459\u7441\u745C\u7469\u7470\u7463\u746A\u7476\u747E\u748B\u749E\u74A7\u74CA\u74CF\u74D4\u73F1"],["e1a1","\u74E0\u74E3\u74E7\u74E9\u74EE\u74F2\u74F0\u74F1\u74F8\u74F7\u7504\u7503\u7505\u750C\u750E\u750D\u7515\u7513\u751E\u7526\u752C\u753C\u7544\u754D\u754A\u7549\u755B\u7546\u755A\u7569\u7564\u7567\u756B\u756D\u7578\u7576\u7586\u7587\u7574\u758A\u7589\u7582\u7594\u759A\u759D\u75A5\u75A3\u75C2\u75B3\u75C3\u75B5\u75BD\u75B8\u75BC\u75B1\u75CD\u75CA\u75D2\u75D9\u75E3\u75DE\u75FE\u75FF\u75FC\u7601\u75F0\u75FA\u75F2\u75F3\u760B\u760D\u7609\u761F\u7627\u7620\u7621\u7622\u7624\u7634\u7630\u763B\u7647\u7648\u7646\u765C\u7658\u7661\u7662\u7668\u7669\u766A\u7667\u766C\u7670"],["e2a1","\u7672\u7676\u7678\u767C\u7680\u7683\u7688\u768B\u768E\u7696\u7693\u7699\u769A\u76B0\u76B4\u76B8\u76B9\u76BA\u76C2\u76CD\u76D6\u76D2\u76DE\u76E1\u76E5\u76E7\u76EA\u862F\u76FB\u7708\u7707\u7704\u7729\u7724\u771E\u7725\u7726\u771B\u7737\u7738\u7747\u775A\u7768\u776B\u775B\u7765\u777F\u777E\u7779\u778E\u778B\u7791\u77A0\u779E\u77B0\u77B6\u77B9\u77BF\u77BC\u77BD\u77BB\u77C7\u77CD\u77D7\u77DA\u77DC\u77E3\u77EE\u77FC\u780C\u7812\u7926\u7820\u792A\u7845\u788E\u7874\u7886\u787C\u789A\u788C\u78A3\u78B5\u78AA\u78AF\u78D1\u78C6\u78CB\u78D4\u78BE\u78BC\u78C5\u78CA\u78EC"],["e3a1","\u78E7\u78DA\u78FD\u78F4\u7907\u7912\u7911\u7919\u792C\u792B\u7940\u7960\u7957\u795F\u795A\u7955\u7953\u797A\u797F\u798A\u799D\u79A7\u9F4B\u79AA\u79AE\u79B3\u79B9\u79BA\u79C9\u79D5\u79E7\u79EC\u79E1\u79E3\u7A08\u7A0D\u7A18\u7A19\u7A20\u7A1F\u7980\u7A31\u7A3B\u7A3E\u7A37\u7A43\u7A57\u7A49\u7A61\u7A62\u7A69\u9F9D\u7A70\u7A79\u7A7D\u7A88\u7A97\u7A95\u7A98\u7A96\u7AA9\u7AC8\u7AB0\u7AB6\u7AC5\u7AC4\u7ABF\u9083\u7AC7\u7ACA\u7ACD\u7ACF\u7AD5\u7AD3\u7AD9\u7ADA\u7ADD\u7AE1\u7AE2\u7AE6\u7AED\u7AF0\u7B02\u7B0F\u7B0A\u7B06\u7B33\u7B18\u7B19\u7B1E\u7B35\u7B28\u7B36\u7B50"],["e4a1","\u7B7A\u7B04\u7B4D\u7B0B\u7B4C\u7B45\u7B75\u7B65\u7B74\u7B67\u7B70\u7B71\u7B6C\u7B6E\u7B9D\u7B98\u7B9F\u7B8D\u7B9C\u7B9A\u7B8B\u7B92\u7B8F\u7B5D\u7B99\u7BCB\u7BC1\u7BCC\u7BCF\u7BB4\u7BC6\u7BDD\u7BE9\u7C11\u7C14\u7BE6\u7BE5\u7C60\u7C00\u7C07\u7C13\u7BF3\u7BF7\u7C17\u7C0D\u7BF6\u7C23\u7C27\u7C2A\u7C1F\u7C37\u7C2B\u7C3D\u7C4C\u7C43\u7C54\u7C4F\u7C40\u7C50\u7C58\u7C5F\u7C64\u7C56\u7C65\u7C6C\u7C75\u7C83\u7C90\u7CA4\u7CAD\u7CA2\u7CAB\u7CA1\u7CA8\u7CB3\u7CB2\u7CB1\u7CAE\u7CB9\u7CBD\u7CC0\u7CC5\u7CC2\u7CD8\u7CD2\u7CDC\u7CE2\u9B3B\u7CEF\u7CF2\u7CF4\u7CF6\u7CFA\u7D06"],["e5a1","\u7D02\u7D1C\u7D15\u7D0A\u7D45\u7D4B\u7D2E\u7D32\u7D3F\u7D35\u7D46\u7D73\u7D56\u7D4E\u7D72\u7D68\u7D6E\u7D4F\u7D63\u7D93\u7D89\u7D5B\u7D8F\u7D7D\u7D9B\u7DBA\u7DAE\u7DA3\u7DB5\u7DC7\u7DBD\u7DAB\u7E3D\u7DA2\u7DAF\u7DDC\u7DB8\u7D9F\u7DB0\u7DD8\u7DDD\u7DE4\u7DDE\u7DFB\u7DF2\u7DE1\u7E05\u7E0A\u7E23\u7E21\u7E12\u7E31\u7E1F\u7E09\u7E0B\u7E22\u7E46\u7E66\u7E3B\u7E35\u7E39\u7E43\u7E37\u7E32\u7E3A\u7E67\u7E5D\u7E56\u7E5E\u7E59\u7E5A\u7E79\u7E6A\u7E69\u7E7C\u7E7B\u7E83\u7DD5\u7E7D\u8FAE\u7E7F\u7E88\u7E89\u7E8C\u7E92\u7E90\u7E93\u7E94\u7E96\u7E8E\u7E9B\u7E9C\u7F38\u7F3A"],["e6a1","\u7F45\u7F4C\u7F4D\u7F4E\u7F50\u7F51\u7F55\u7F54\u7F58\u7F5F\u7F60\u7F68\u7F69\u7F67\u7F78\u7F82\u7F86\u7F83\u7F88\u7F87\u7F8C\u7F94\u7F9E\u7F9D\u7F9A\u7FA3\u7FAF\u7FB2\u7FB9\u7FAE\u7FB6\u7FB8\u8B71\u7FC5\u7FC6\u7FCA\u7FD5\u7FD4\u7FE1\u7FE6\u7FE9\u7FF3\u7FF9\u98DC\u8006\u8004\u800B\u8012\u8018\u8019\u801C\u8021\u8028\u803F\u803B\u804A\u8046\u8052\u8058\u805A\u805F\u8062\u8068\u8073\u8072\u8070\u8076\u8079\u807D\u807F\u8084\u8086\u8085\u809B\u8093\u809A\u80AD\u5190\u80AC\u80DB\u80E5\u80D9\u80DD\u80C4\u80DA\u80D6\u8109\u80EF\u80F1\u811B\u8129\u8123\u812F\u814B"],["e7a1","\u968B\u8146\u813E\u8153\u8151\u80FC\u8171\u816E\u8165\u8166\u8174\u8183\u8188\u818A\u8180\u8182\u81A0\u8195\u81A4\u81A3\u815F\u8193\u81A9\u81B0\u81B5\u81BE\u81B8\u81BD\u81C0\u81C2\u81BA\u81C9\u81CD\u81D1\u81D9\u81D8\u81C8\u81DA\u81DF\u81E0\u81E7\u81FA\u81FB\u81FE\u8201\u8202\u8205\u8207\u820A\u820D\u8210\u8216\u8229\u822B\u8238\u8233\u8240\u8259\u8258\u825D\u825A\u825F\u8264\u8262\u8268\u826A\u826B\u822E\u8271\u8277\u8278\u827E\u828D\u8292\u82AB\u829F\u82BB\u82AC\u82E1\u82E3\u82DF\u82D2\u82F4\u82F3\u82FA\u8393\u8303\u82FB\u82F9\u82DE\u8306\u82DC\u8309\u82D9"],["e8a1","\u8335\u8334\u8316\u8332\u8331\u8340\u8339\u8350\u8345\u832F\u832B\u8317\u8318\u8385\u839A\u83AA\u839F\u83A2\u8396\u8323\u838E\u8387\u838A\u837C\u83B5\u8373\u8375\u83A0\u8389\u83A8\u83F4\u8413\u83EB\u83CE\u83FD\u8403\u83D8\u840B\u83C1\u83F7\u8407\u83E0\u83F2\u840D\u8422\u8420\u83BD\u8438\u8506\u83FB\u846D\u842A\u843C\u855A\u8484\u8477\u846B\u84AD\u846E\u8482\u8469\u8446\u842C\u846F\u8479\u8435\u84CA\u8462\u84B9\u84BF\u849F\u84D9\u84CD\u84BB\u84DA\u84D0\u84C1\u84C6\u84D6\u84A1\u8521\u84FF\u84F4\u8517\u8518\u852C\u851F\u8515\u8514\u84FC\u8540\u8563\u8558\u8548"],["e9a1","\u8541\u8602\u854B\u8555\u8580\u85A4\u8588\u8591\u858A\u85A8\u856D\u8594\u859B\u85EA\u8587\u859C\u8577\u857E\u8590\u85C9\u85BA\u85CF\u85B9\u85D0\u85D5\u85DD\u85E5\u85DC\u85F9\u860A\u8613\u860B\u85FE\u85FA\u8606\u8622\u861A\u8630\u863F\u864D\u4E55\u8654\u865F\u8667\u8671\u8693\u86A3\u86A9\u86AA\u868B\u868C\u86B6\u86AF\u86C4\u86C6\u86B0\u86C9\u8823\u86AB\u86D4\u86DE\u86E9\u86EC\u86DF\u86DB\u86EF\u8712\u8706\u8708\u8700\u8703\u86FB\u8711\u8709\u870D\u86F9\u870A\u8734\u873F\u8737\u873B\u8725\u8729\u871A\u8760\u875F\u8778\u874C\u874E\u8774\u8757\u8768\u876E\u8759"],["eaa1","\u8753\u8763\u876A\u8805\u87A2\u879F\u8782\u87AF\u87CB\u87BD\u87C0\u87D0\u96D6\u87AB\u87C4\u87B3\u87C7\u87C6\u87BB\u87EF\u87F2\u87E0\u880F\u880D\u87FE\u87F6\u87F7\u880E\u87D2\u8811\u8816\u8815\u8822\u8821\u8831\u8836\u8839\u8827\u883B\u8844\u8842\u8852\u8859\u885E\u8862\u886B\u8881\u887E\u889E\u8875\u887D\u88B5\u8872\u8882\u8897\u8892\u88AE\u8899\u88A2\u888D\u88A4\u88B0\u88BF\u88B1\u88C3\u88C4\u88D4\u88D8\u88D9\u88DD\u88F9\u8902\u88FC\u88F4\u88E8\u88F2\u8904\u890C\u890A\u8913\u8943\u891E\u8925\u892A\u892B\u8941\u8944\u893B\u8936\u8938\u894C\u891D\u8960\u895E"],["eba1","\u8966\u8964\u896D\u896A\u896F\u8974\u8977\u897E\u8983\u8988\u898A\u8993\u8998\u89A1\u89A9\u89A6\u89AC\u89AF\u89B2\u89BA\u89BD\u89BF\u89C0\u89DA\u89DC\u89DD\u89E7\u89F4\u89F8\u8A03\u8A16\u8A10\u8A0C\u8A1B\u8A1D\u8A25\u8A36\u8A41\u8A5B\u8A52\u8A46\u8A48\u8A7C\u8A6D\u8A6C\u8A62\u8A85\u8A82\u8A84\u8AA8\u8AA1\u8A91\u8AA5\u8AA6\u8A9A\u8AA3\u8AC4\u8ACD\u8AC2\u8ADA\u8AEB\u8AF3\u8AE7\u8AE4\u8AF1\u8B14\u8AE0\u8AE2\u8AF7\u8ADE\u8ADB\u8B0C\u8B07\u8B1A\u8AE1\u8B16\u8B10\u8B17\u8B20\u8B33\u97AB\u8B26\u8B2B\u8B3E\u8B28\u8B41\u8B4C\u8B4F\u8B4E\u8B49\u8B56\u8B5B\u8B5A\u8B6B"],["eca1","\u8B5F\u8B6C\u8B6F\u8B74\u8B7D\u8B80\u8B8C\u8B8E\u8B92\u8B93\u8B96\u8B99\u8B9A\u8C3A\u8C41\u8C3F\u8C48\u8C4C\u8C4E\u8C50\u8C55\u8C62\u8C6C\u8C78\u8C7A\u8C82\u8C89\u8C85\u8C8A\u8C8D\u8C8E\u8C94\u8C7C\u8C98\u621D\u8CAD\u8CAA\u8CBD\u8CB2\u8CB3\u8CAE\u8CB6\u8CC8\u8CC1\u8CE4\u8CE3\u8CDA\u8CFD\u8CFA\u8CFB\u8D04\u8D05\u8D0A\u8D07\u8D0F\u8D0D\u8D10\u9F4E\u8D13\u8CCD\u8D14\u8D16\u8D67\u8D6D\u8D71\u8D73\u8D81\u8D99\u8DC2\u8DBE\u8DBA\u8DCF\u8DDA\u8DD6\u8DCC\u8DDB\u8DCB\u8DEA\u8DEB\u8DDF\u8DE3\u8DFC\u8E08\u8E09\u8DFF\u8E1D\u8E1E\u8E10\u8E1F\u8E42\u8E35\u8E30\u8E34\u8E4A"],["eda1","\u8E47\u8E49\u8E4C\u8E50\u8E48\u8E59\u8E64\u8E60\u8E2A\u8E63\u8E55\u8E76\u8E72\u8E7C\u8E81\u8E87\u8E85\u8E84\u8E8B\u8E8A\u8E93\u8E91\u8E94\u8E99\u8EAA\u8EA1\u8EAC\u8EB0\u8EC6\u8EB1\u8EBE\u8EC5\u8EC8\u8ECB\u8EDB\u8EE3\u8EFC\u8EFB\u8EEB\u8EFE\u8F0A\u8F05\u8F15\u8F12\u8F19\u8F13\u8F1C\u8F1F\u8F1B\u8F0C\u8F26\u8F33\u8F3B\u8F39\u8F45\u8F42\u8F3E\u8F4C\u8F49\u8F46\u8F4E\u8F57\u8F5C\u8F62\u8F63\u8F64\u8F9C\u8F9F\u8FA3\u8FAD\u8FAF\u8FB7\u8FDA\u8FE5\u8FE2\u8FEA\u8FEF\u9087\u8FF4\u9005\u8FF9\u8FFA\u9011\u9015\u9021\u900D\u901E\u9016\u900B\u9027\u9036\u9035\u9039\u8FF8"],["eea1","\u904F\u9050\u9051\u9052\u900E\u9049\u903E\u9056\u9058\u905E\u9068\u906F\u9076\u96A8\u9072\u9082\u907D\u9081\u9080\u908A\u9089\u908F\u90A8\u90AF\u90B1\u90B5\u90E2\u90E4\u6248\u90DB\u9102\u9112\u9119\u9132\u9130\u914A\u9156\u9158\u9163\u9165\u9169\u9173\u9172\u918B\u9189\u9182\u91A2\u91AB\u91AF\u91AA\u91B5\u91B4\u91BA\u91C0\u91C1\u91C9\u91CB\u91D0\u91D6\u91DF\u91E1\u91DB\u91FC\u91F5\u91F6\u921E\u91FF\u9214\u922C\u9215\u9211\u925E\u9257\u9245\u9249\u9264\u9248\u9295\u923F\u924B\u9250\u929C\u9296\u9293\u929B\u925A\u92CF\u92B9\u92B7\u92E9\u930F\u92FA\u9344\u932E"],["efa1","\u9319\u9322\u931A\u9323\u933A\u9335\u933B\u935C\u9360\u937C\u936E\u9356\u93B0\u93AC\u93AD\u9394\u93B9\u93D6\u93D7\u93E8\u93E5\u93D8\u93C3\u93DD\u93D0\u93C8\u93E4\u941A\u9414\u9413\u9403\u9407\u9410\u9436\u942B\u9435\u9421\u943A\u9441\u9452\u9444\u945B\u9460\u9462\u945E\u946A\u9229\u9470\u9475\u9477\u947D\u945A\u947C\u947E\u9481\u947F\u9582\u9587\u958A\u9594\u9596\u9598\u9599\u95A0\u95A8\u95A7\u95AD\u95BC\u95BB\u95B9\u95BE\u95CA\u6FF6\u95C3\u95CD\u95CC\u95D5\u95D4\u95D6\u95DC\u95E1\u95E5\u95E2\u9621\u9628\u962E\u962F\u9642\u964C\u964F\u964B\u9677\u965C\u965E"],["f0a1","\u965D\u965F\u9666\u9672\u966C\u968D\u9698\u9695\u9697\u96AA\u96A7\u96B1\u96B2\u96B0\u96B4\u96B6\u96B8\u96B9\u96CE\u96CB\u96C9\u96CD\u894D\u96DC\u970D\u96D5\u96F9\u9704\u9706\u9708\u9713\u970E\u9711\u970F\u9716\u9719\u9724\u972A\u9730\u9739\u973D\u973E\u9744\u9746\u9748\u9742\u9749\u975C\u9760\u9764\u9766\u9768\u52D2\u976B\u9771\u9779\u9785\u977C\u9781\u977A\u9786\u978B\u978F\u9790\u979C\u97A8\u97A6\u97A3\u97B3\u97B4\u97C3\u97C6\u97C8\u97CB\u97DC\u97ED\u9F4F\u97F2\u7ADF\u97F6\u97F5\u980F\u980C\u9838\u9824\u9821\u9837\u983D\u9846\u984F\u984B\u986B\u986F\u9870"],["f1a1","\u9871\u9874\u9873\u98AA\u98AF\u98B1\u98B6\u98C4\u98C3\u98C6\u98E9\u98EB\u9903\u9909\u9912\u9914\u9918\u9921\u991D\u991E\u9924\u9920\u992C\u992E\u993D\u993E\u9942\u9949\u9945\u9950\u994B\u9951\u9952\u994C\u9955\u9997\u9998\u99A5\u99AD\u99AE\u99BC\u99DF\u99DB\u99DD\u99D8\u99D1\u99ED\u99EE\u99F1\u99F2\u99FB\u99F8\u9A01\u9A0F\u9A05\u99E2\u9A19\u9A2B\u9A37\u9A45\u9A42\u9A40\u9A43\u9A3E\u9A55\u9A4D\u9A5B\u9A57\u9A5F\u9A62\u9A65\u9A64\u9A69\u9A6B\u9A6A\u9AAD\u9AB0\u9ABC\u9AC0\u9ACF\u9AD1\u9AD3\u9AD4\u9ADE\u9ADF\u9AE2\u9AE3\u9AE6\u9AEF\u9AEB\u9AEE\u9AF4\u9AF1\u9AF7"],["f2a1","\u9AFB\u9B06\u9B18\u9B1A\u9B1F\u9B22\u9B23\u9B25\u9B27\u9B28\u9B29\u9B2A\u9B2E\u9B2F\u9B32\u9B44\u9B43\u9B4F\u9B4D\u9B4E\u9B51\u9B58\u9B74\u9B93\u9B83\u9B91\u9B96\u9B97\u9B9F\u9BA0\u9BA8\u9BB4\u9BC0\u9BCA\u9BB9\u9BC6\u9BCF\u9BD1\u9BD2\u9BE3\u9BE2\u9BE4\u9BD4\u9BE1\u9C3A\u9BF2\u9BF1\u9BF0\u9C15\u9C14\u9C09\u9C13\u9C0C\u9C06\u9C08\u9C12\u9C0A\u9C04\u9C2E\u9C1B\u9C25\u9C24\u9C21\u9C30\u9C47\u9C32\u9C46\u9C3E\u9C5A\u9C60\u9C67\u9C76\u9C78\u9CE7\u9CEC\u9CF0\u9D09\u9D08\u9CEB\u9D03\u9D06\u9D2A\u9D26\u9DAF\u9D23\u9D1F\u9D44\u9D15\u9D12\u9D41\u9D3F\u9D3E\u9D46\u9D48"],["f3a1","\u9D5D\u9D5E\u9D64\u9D51\u9D50\u9D59\u9D72\u9D89\u9D87\u9DAB\u9D6F\u9D7A\u9D9A\u9DA4\u9DA9\u9DB2\u9DC4\u9DC1\u9DBB\u9DB8\u9DBA\u9DC6\u9DCF\u9DC2\u9DD9\u9DD3\u9DF8\u9DE6\u9DED\u9DEF\u9DFD\u9E1A\u9E1B\u9E1E\u9E75\u9E79\u9E7D\u9E81\u9E88\u9E8B\u9E8C\u9E92\u9E95\u9E91\u9E9D\u9EA5\u9EA9\u9EB8\u9EAA\u9EAD\u9761\u9ECC\u9ECE\u9ECF\u9ED0\u9ED4\u9EDC\u9EDE\u9EDD\u9EE0\u9EE5\u9EE8\u9EEF\u9EF4\u9EF6\u9EF7\u9EF9\u9EFB\u9EFC\u9EFD\u9F07\u9F08\u76B7\u9F15\u9F21\u9F2C\u9F3E\u9F4A\u9F52\u9F54\u9F63\u9F5F\u9F60\u9F61\u9F66\u9F67\u9F6C\u9F6A\u9F77\u9F72\u9F76\u9F95\u9F9C\u9FA0"],["f4a1","\u582F\u69C7\u9059\u7464\u51DC\u7199"],["f9a1","\u7E8A\u891C\u9348\u9288\u84DC\u4FC9\u70BB\u6631\u68C8\u92F9\u66FB\u5F45\u4E28\u4EE1\u4EFC\u4F00\u4F03\u4F39\u4F56\u4F92\u4F8A\u4F9A\u4F94\u4FCD\u5040\u5022\u4FFF\u501E\u5046\u5070\u5042\u5094\u50F4\u50D8\u514A\u5164\u519D\u51BE\u51EC\u5215\u529C\u52A6\u52C0\u52DB\u5300\u5307\u5324\u5372\u5393\u53B2\u53DD\uFA0E\u549C\u548A\u54A9\u54FF\u5586\u5759\u5765\u57AC\u57C8\u57C7\uFA0F\uFA10\u589E\u58B2\u590B\u5953\u595B\u595D\u5963\u59A4\u59BA\u5B56\u5BC0\u752F\u5BD8\u5BEC\u5C1E\u5CA6\u5CBA\u5CF5\u5D27\u5D53\uFA11\u5D42\u5D6D\u5DB8\u5DB9\u5DD0\u5F21\u5F34\u5F67\u5FB7"],["faa1","\u5FDE\u605D\u6085\u608A\u60DE\u60D5\u6120\u60F2\u6111\u6137\u6130\u6198\u6213\u62A6\u63F5\u6460\u649D\u64CE\u654E\u6600\u6615\u663B\u6609\u662E\u661E\u6624\u6665\u6657\u6659\uFA12\u6673\u6699\u66A0\u66B2\u66BF\u66FA\u670E\uF929\u6766\u67BB\u6852\u67C0\u6801\u6844\u68CF\uFA13\u6968\uFA14\u6998\u69E2\u6A30\u6A6B\u6A46\u6A73\u6A7E\u6AE2\u6AE4\u6BD6\u6C3F\u6C5C\u6C86\u6C6F\u6CDA\u6D04\u6D87\u6D6F\u6D96\u6DAC\u6DCF\u6DF8\u6DF2\u6DFC\u6E39\u6E5C\u6E27\u6E3C\u6EBF\u6F88\u6FB5\u6FF5\u7005\u7007\u7028\u7085\u70AB\u710F\u7104\u715C\u7146\u7147\uFA15\u71C1\u71FE\u72B1"],["fba1","\u72BE\u7324\uFA16\u7377\u73BD\u73C9\u73D6\u73E3\u73D2\u7407\u73F5\u7426\u742A\u7429\u742E\u7462\u7489\u749F\u7501\u756F\u7682\u769C\u769E\u769B\u76A6\uFA17\u7746\u52AF\u7821\u784E\u7864\u787A\u7930\uFA18\uFA19\uFA1A\u7994\uFA1B\u799B\u7AD1\u7AE7\uFA1C\u7AEB\u7B9E\uFA1D\u7D48\u7D5C\u7DB7\u7DA0\u7DD6\u7E52\u7F47\u7FA1\uFA1E\u8301\u8362\u837F\u83C7\u83F6\u8448\u84B4\u8553\u8559\u856B\uFA1F\u85B0\uFA20\uFA21\u8807\u88F5\u8A12\u8A37\u8A79\u8AA7\u8ABE\u8ADF\uFA22\u8AF6\u8B53\u8B7F\u8CF0\u8CF4\u8D12\u8D76\uFA23\u8ECF\uFA24\uFA25\u9067\u90DE\uFA26\u9115\u9127\u91DA"],["fca1","\u91D7\u91DE\u91ED\u91EE\u91E4\u91E5\u9206\u9210\u920A\u923A\u9240\u923C\u924E\u9259\u9251\u9239\u9267\u92A7\u9277\u9278\u92E7\u92D7\u92D9\u92D0\uFA27\u92D5\u92E0\u92D3\u9325\u9321\u92FB\uFA28\u931E\u92FF\u931D\u9302\u9370\u9357\u93A4\u93C6\u93DE\u93F8\u9431\u9445\u9448\u9592\uF9DC\uFA29\u969D\u96AF\u9733\u973B\u9743\u974D\u974F\u9751\u9755\u9857\u9865\uFA2A\uFA2B\u9927\uFA2C\u999E\u9A4E\u9AD9\u9ADC\u9B75\u9B72\u9B8F\u9BB1\u9BBB\u9C00\u9D70\u9D6B\uFA2D\u9E19\u9ED1"],["fcf1","\u2170",9,"\uFFE2\uFFE4\uFF07\uFF02"],["8fa2af","\u02D8\u02C7\xB8\u02D9\u02DD\xAF\u02DB\u02DA\uFF5E\u0384\u0385"],["8fa2c2","\xA1\xA6\xBF"],["8fa2eb","\xBA\xAA\xA9\xAE\u2122\xA4\u2116"],["8fa6e1","\u0386\u0388\u0389\u038A\u03AA"],["8fa6e7","\u038C"],["8fa6e9","\u038E\u03AB"],["8fa6ec","\u038F"],["8fa6f1","\u03AC\u03AD\u03AE\u03AF\u03CA\u0390\u03CC\u03C2\u03CD\u03CB\u03B0\u03CE"],["8fa7c2","\u0402",10,"\u040E\u040F"],["8fa7f2","\u0452",10,"\u045E\u045F"],["8fa9a1","\xC6\u0110"],["8fa9a4","\u0126"],["8fa9a6","\u0132"],["8fa9a8","\u0141\u013F"],["8fa9ab","\u014A\xD8\u0152"],["8fa9af","\u0166\xDE"],["8fa9c1","\xE6\u0111\xF0\u0127\u0131\u0133\u0138\u0142\u0140\u0149\u014B\xF8\u0153\xDF\u0167\xFE"],["8faaa1","\xC1\xC0\xC4\xC2\u0102\u01CD\u0100\u0104\xC5\xC3\u0106\u0108\u010C\xC7\u010A\u010E\xC9\xC8\xCB\xCA\u011A\u0116\u0112\u0118"],["8faaba","\u011C\u011E\u0122\u0120\u0124\xCD\xCC\xCF\xCE\u01CF\u0130\u012A\u012E\u0128\u0134\u0136\u0139\u013D\u013B\u0143\u0147\u0145\xD1\xD3\xD2\xD6\xD4\u01D1\u0150\u014C\xD5\u0154\u0158\u0156\u015A\u015C\u0160\u015E\u0164\u0162\xDA\xD9\xDC\xDB\u016C\u01D3\u0170\u016A\u0172\u016E\u0168\u01D7\u01DB\u01D9\u01D5\u0174\xDD\u0178\u0176\u0179\u017D\u017B"],["8faba1","\xE1\xE0\xE4\xE2\u0103\u01CE\u0101\u0105\xE5\xE3\u0107\u0109\u010D\xE7\u010B\u010F\xE9\xE8\xEB\xEA\u011B\u0117\u0113\u0119\u01F5\u011D\u011F"],["8fabbd","\u0121\u0125\xED\xEC\xEF\xEE\u01D0"],["8fabc5","\u012B\u012F\u0129\u0135\u0137\u013A\u013E\u013C\u0144\u0148\u0146\xF1\xF3\xF2\xF6\xF4\u01D2\u0151\u014D\xF5\u0155\u0159\u0157\u015B\u015D\u0161\u015F\u0165\u0163\xFA\xF9\xFC\xFB\u016D\u01D4\u0171\u016B\u0173\u016F\u0169\u01D8\u01DC\u01DA\u01D6\u0175\xFD\xFF\u0177\u017A\u017E\u017C"],["8fb0a1","\u4E02\u4E04\u4E05\u4E0C\u4E12\u4E1F\u4E23\u4E24\u4E28\u4E2B\u4E2E\u4E2F\u4E30\u4E35\u4E40\u4E41\u4E44\u4E47\u4E51\u4E5A\u4E5C\u4E63\u4E68\u4E69\u4E74\u4E75\u4E79\u4E7F\u4E8D\u4E96\u4E97\u4E9D\u4EAF\u4EB9\u4EC3\u4ED0\u4EDA\u4EDB\u4EE0\u4EE1\u4EE2\u4EE8\u4EEF\u4EF1\u4EF3\u4EF5\u4EFD\u4EFE\u4EFF\u4F00\u4F02\u4F03\u4F08\u4F0B\u4F0C\u4F12\u4F15\u4F16\u4F17\u4F19\u4F2E\u4F31\u4F60\u4F33\u4F35\u4F37\u4F39\u4F3B\u4F3E\u4F40\u4F42\u4F48\u4F49\u4F4B\u4F4C\u4F52\u4F54\u4F56\u4F58\u4F5F\u4F63\u4F6A\u4F6C\u4F6E\u4F71\u4F77\u4F78\u4F79\u4F7A\u4F7D\u4F7E\u4F81\u4F82\u4F84"],["8fb1a1","\u4F85\u4F89\u4F8A\u4F8C\u4F8E\u4F90\u4F92\u4F93\u4F94\u4F97\u4F99\u4F9A\u4F9E\u4F9F\u4FB2\u4FB7\u4FB9\u4FBB\u4FBC\u4FBD\u4FBE\u4FC0\u4FC1\u4FC5\u4FC6\u4FC8\u4FC9\u4FCB\u4FCC\u4FCD\u4FCF\u4FD2\u4FDC\u4FE0\u4FE2\u4FF0\u4FF2\u4FFC\u4FFD\u4FFF\u5000\u5001\u5004\u5007\u500A\u500C\u500E\u5010\u5013\u5017\u5018\u501B\u501C\u501D\u501E\u5022\u5027\u502E\u5030\u5032\u5033\u5035\u5040\u5041\u5042\u5045\u5046\u504A\u504C\u504E\u5051\u5052\u5053\u5057\u5059\u505F\u5060\u5062\u5063\u5066\u5067\u506A\u506D\u5070\u5071\u503B\u5081\u5083\u5084\u5086\u508A\u508E\u508F\u5090"],["8fb2a1","\u5092\u5093\u5094\u5096\u509B\u509C\u509E",4,"\u50AA\u50AF\u50B0\u50B9\u50BA\u50BD\u50C0\u50C3\u50C4\u50C7\u50CC\u50CE\u50D0\u50D3\u50D4\u50D8\u50DC\u50DD\u50DF\u50E2\u50E4\u50E6\u50E8\u50E9\u50EF\u50F1\u50F6\u50FA\u50FE\u5103\u5106\u5107\u5108\u510B\u510C\u510D\u510E\u50F2\u5110\u5117\u5119\u511B\u511C\u511D\u511E\u5123\u5127\u5128\u512C\u512D\u512F\u5131\u5133\u5134\u5135\u5138\u5139\u5142\u514A\u514F\u5153\u5155\u5157\u5158\u515F\u5164\u5166\u517E\u5183\u5184\u518B\u518E\u5198\u519D\u51A1\u51A3\u51AD\u51B8\u51BA\u51BC\u51BE\u51BF\u51C2"],["8fb3a1","\u51C8\u51CF\u51D1\u51D2\u51D3\u51D5\u51D8\u51DE\u51E2\u51E5\u51EE\u51F2\u51F3\u51F4\u51F7\u5201\u5202\u5205\u5212\u5213\u5215\u5216\u5218\u5222\u5228\u5231\u5232\u5235\u523C\u5245\u5249\u5255\u5257\u5258\u525A\u525C\u525F\u5260\u5261\u5266\u526E\u5277\u5278\u5279\u5280\u5282\u5285\u528A\u528C\u5293\u5295\u5296\u5297\u5298\u529A\u529C\u52A4\u52A5\u52A6\u52A7\u52AF\u52B0\u52B6\u52B7\u52B8\u52BA\u52BB\u52BD\u52C0\u52C4\u52C6\u52C8\u52CC\u52CF\u52D1\u52D4\u52D6\u52DB\u52DC\u52E1\u52E5\u52E8\u52E9\u52EA\u52EC\u52F0\u52F1\u52F4\u52F6\u52F7\u5300\u5303\u530A\u530B"],["8fb4a1","\u530C\u5311\u5313\u5318\u531B\u531C\u531E\u531F\u5325\u5327\u5328\u5329\u532B\u532C\u532D\u5330\u5332\u5335\u533C\u533D\u533E\u5342\u534C\u534B\u5359\u535B\u5361\u5363\u5365\u536C\u536D\u5372\u5379\u537E\u5383\u5387\u5388\u538E\u5393\u5394\u5399\u539D\u53A1\u53A4\u53AA\u53AB\u53AF\u53B2\u53B4\u53B5\u53B7\u53B8\u53BA\u53BD\u53C0\u53C5\u53CF\u53D2\u53D3\u53D5\u53DA\u53DD\u53DE\u53E0\u53E6\u53E7\u53F5\u5402\u5413\u541A\u5421\u5427\u5428\u542A\u542F\u5431\u5434\u5435\u5443\u5444\u5447\u544D\u544F\u545E\u5462\u5464\u5466\u5467\u5469\u546B\u546D\u546E\u5474\u547F"],["8fb5a1","\u5481\u5483\u5485\u5488\u5489\u548D\u5491\u5495\u5496\u549C\u549F\u54A1\u54A6\u54A7\u54A9\u54AA\u54AD\u54AE\u54B1\u54B7\u54B9\u54BA\u54BB\u54BF\u54C6\u54CA\u54CD\u54CE\u54E0\u54EA\u54EC\u54EF\u54F6\u54FC\u54FE\u54FF\u5500\u5501\u5505\u5508\u5509\u550C\u550D\u550E\u5515\u552A\u552B\u5532\u5535\u5536\u553B\u553C\u553D\u5541\u5547\u5549\u554A\u554D\u5550\u5551\u5558\u555A\u555B\u555E\u5560\u5561\u5564\u5566\u557F\u5581\u5582\u5586\u5588\u558E\u558F\u5591\u5592\u5593\u5594\u5597\u55A3\u55A4\u55AD\u55B2\u55BF\u55C1\u55C3\u55C6\u55C9\u55CB\u55CC\u55CE\u55D1\u55D2"],["8fb6a1","\u55D3\u55D7\u55D8\u55DB\u55DE\u55E2\u55E9\u55F6\u55FF\u5605\u5608\u560A\u560D",5,"\u5619\u562C\u5630\u5633\u5635\u5637\u5639\u563B\u563C\u563D\u563F\u5640\u5641\u5643\u5644\u5646\u5649\u564B\u564D\u564F\u5654\u565E\u5660\u5661\u5662\u5663\u5666\u5669\u566D\u566F\u5671\u5672\u5675\u5684\u5685\u5688\u568B\u568C\u5695\u5699\u569A\u569D\u569E\u569F\u56A6\u56A7\u56A8\u56A9\u56AB\u56AC\u56AD\u56B1\u56B3\u56B7\u56BE\u56C5\u56C9\u56CA\u56CB\u56CF\u56D0\u56CC\u56CD\u56D9\u56DC\u56DD\u56DF\u56E1\u56E4",4,"\u56F1\u56EB\u56ED"],["8fb7a1","\u56F6\u56F7\u5701\u5702\u5707\u570A\u570C\u5711\u5715\u571A\u571B\u571D\u5720\u5722\u5723\u5724\u5725\u5729\u572A\u572C\u572E\u572F\u5733\u5734\u573D\u573E\u573F\u5745\u5746\u574C\u574D\u5752\u5762\u5765\u5767\u5768\u576B\u576D",4,"\u5773\u5774\u5775\u5777\u5779\u577A\u577B\u577C\u577E\u5781\u5783\u578C\u5794\u5797\u5799\u579A\u579C\u579D\u579E\u579F\u57A1\u5795\u57A7\u57A8\u57A9\u57AC\u57B8\u57BD\u57C7\u57C8\u57CC\u57CF\u57D5\u57DD\u57DE\u57E4\u57E6\u57E7\u57E9\u57ED\u57F0\u57F5\u57F6\u57F8\u57FD\u57FE\u57FF\u5803\u5804\u5808\u5809\u57E1"],["8fb8a1","\u580C\u580D\u581B\u581E\u581F\u5820\u5826\u5827\u582D\u5832\u5839\u583F\u5849\u584C\u584D\u584F\u5850\u5855\u585F\u5861\u5864\u5867\u5868\u5878\u587C\u587F\u5880\u5881\u5887\u5888\u5889\u588A\u588C\u588D\u588F\u5890\u5894\u5896\u589D\u58A0\u58A1\u58A2\u58A6\u58A9\u58B1\u58B2\u58C4\u58BC\u58C2\u58C8\u58CD\u58CE\u58D0\u58D2\u58D4\u58D6\u58DA\u58DD\u58E1\u58E2\u58E9\u58F3\u5905\u5906\u590B\u590C\u5912\u5913\u5914\u8641\u591D\u5921\u5923\u5924\u5928\u592F\u5930\u5933\u5935\u5936\u593F\u5943\u5946\u5952\u5953\u5959\u595B\u595D\u595E\u595F\u5961\u5963\u596B\u596D"],["8fb9a1","\u596F\u5972\u5975\u5976\u5979\u597B\u597C\u598B\u598C\u598E\u5992\u5995\u5997\u599F\u59A4\u59A7\u59AD\u59AE\u59AF\u59B0\u59B3\u59B7\u59BA\u59BC\u59C1\u59C3\u59C4\u59C8\u59CA\u59CD\u59D2\u59DD\u59DE\u59DF\u59E3\u59E4\u59E7\u59EE\u59EF\u59F1\u59F2\u59F4\u59F7\u5A00\u5A04\u5A0C\u5A0D\u5A0E\u5A12\u5A13\u5A1E\u5A23\u5A24\u5A27\u5A28\u5A2A\u5A2D\u5A30\u5A44\u5A45\u5A47\u5A48\u5A4C\u5A50\u5A55\u5A5E\u5A63\u5A65\u5A67\u5A6D\u5A77\u5A7A\u5A7B\u5A7E\u5A8B\u5A90\u5A93\u5A96\u5A99\u5A9C\u5A9E\u5A9F\u5AA0\u5AA2\u5AA7\u5AAC\u5AB1\u5AB2\u5AB3\u5AB5\u5AB8\u5ABA\u5ABB\u5ABF"],["8fbaa1","\u5AC4\u5AC6\u5AC8\u5ACF\u5ADA\u5ADC\u5AE0\u5AE5\u5AEA\u5AEE\u5AF5\u5AF6\u5AFD\u5B00\u5B01\u5B08\u5B17\u5B34\u5B19\u5B1B\u5B1D\u5B21\u5B25\u5B2D\u5B38\u5B41\u5B4B\u5B4C\u5B52\u5B56\u5B5E\u5B68\u5B6E\u5B6F\u5B7C\u5B7D\u5B7E\u5B7F\u5B81\u5B84\u5B86\u5B8A\u5B8E\u5B90\u5B91\u5B93\u5B94\u5B96\u5BA8\u5BA9\u5BAC\u5BAD\u5BAF\u5BB1\u5BB2\u5BB7\u5BBA\u5BBC\u5BC0\u5BC1\u5BCD\u5BCF\u5BD6",4,"\u5BE0\u5BEF\u5BF1\u5BF4\u5BFD\u5C0C\u5C17\u5C1E\u5C1F\u5C23\u5C26\u5C29\u5C2B\u5C2C\u5C2E\u5C30\u5C32\u5C35\u5C36\u5C59\u5C5A\u5C5C\u5C62\u5C63\u5C67\u5C68\u5C69"],["8fbba1","\u5C6D\u5C70\u5C74\u5C75\u5C7A\u5C7B\u5C7C\u5C7D\u5C87\u5C88\u5C8A\u5C8F\u5C92\u5C9D\u5C9F\u5CA0\u5CA2\u5CA3\u5CA6\u5CAA\u5CB2\u5CB4\u5CB5\u5CBA\u5CC9\u5CCB\u5CD2\u5CDD\u5CD7\u5CEE\u5CF1\u5CF2\u5CF4\u5D01\u5D06\u5D0D\u5D12\u5D2B\u5D23\u5D24\u5D26\u5D27\u5D31\u5D34\u5D39\u5D3D\u5D3F\u5D42\u5D43\u5D46\u5D48\u5D55\u5D51\u5D59\u5D4A\u5D5F\u5D60\u5D61\u5D62\u5D64\u5D6A\u5D6D\u5D70\u5D79\u5D7A\u5D7E\u5D7F\u5D81\u5D83\u5D88\u5D8A\u5D92\u5D93\u5D94\u5D95\u5D99\u5D9B\u5D9F\u5DA0\u5DA7\u5DAB\u5DB0\u5DB4\u5DB8\u5DB9\u5DC3\u5DC7\u5DCB\u5DD0\u5DCE\u5DD8\u5DD9\u5DE0\u5DE4"],["8fbca1","\u5DE9\u5DF8\u5DF9\u5E00\u5E07\u5E0D\u5E12\u5E14\u5E15\u5E18\u5E1F\u5E20\u5E2E\u5E28\u5E32\u5E35\u5E3E\u5E4B\u5E50\u5E49\u5E51\u5E56\u5E58\u5E5B\u5E5C\u5E5E\u5E68\u5E6A",4,"\u5E70\u5E80\u5E8B\u5E8E\u5EA2\u5EA4\u5EA5\u5EA8\u5EAA\u5EAC\u5EB1\u5EB3\u5EBD\u5EBE\u5EBF\u5EC6\u5ECC\u5ECB\u5ECE\u5ED1\u5ED2\u5ED4\u5ED5\u5EDC\u5EDE\u5EE5\u5EEB\u5F02\u5F06\u5F07\u5F08\u5F0E\u5F19\u5F1C\u5F1D\u5F21\u5F22\u5F23\u5F24\u5F28\u5F2B\u5F2C\u5F2E\u5F30\u5F34\u5F36\u5F3B\u5F3D\u5F3F\u5F40\u5F44\u5F45\u5F47\u5F4D\u5F50\u5F54\u5F58\u5F5B\u5F60\u5F63\u5F64\u5F67"],["8fbda1","\u5F6F\u5F72\u5F74\u5F75\u5F78\u5F7A\u5F7D\u5F7E\u5F89\u5F8D\u5F8F\u5F96\u5F9C\u5F9D\u5FA2\u5FA7\u5FAB\u5FA4\u5FAC\u5FAF\u5FB0\u5FB1\u5FB8\u5FC4\u5FC7\u5FC8\u5FC9\u5FCB\u5FD0",4,"\u5FDE\u5FE1\u5FE2\u5FE8\u5FE9\u5FEA\u5FEC\u5FED\u5FEE\u5FEF\u5FF2\u5FF3\u5FF6\u5FFA\u5FFC\u6007\u600A\u600D\u6013\u6014\u6017\u6018\u601A\u601F\u6024\u602D\u6033\u6035\u6040\u6047\u6048\u6049\u604C\u6051\u6054\u6056\u6057\u605D\u6061\u6067\u6071\u607E\u607F\u6082\u6086\u6088\u608A\u608E\u6091\u6093\u6095\u6098\u609D\u609E\u60A2\u60A4\u60A5\u60A8\u60B0\u60B1\u60B7"],["8fbea1","\u60BB\u60BE\u60C2\u60C4\u60C8\u60C9\u60CA\u60CB\u60CE\u60CF\u60D4\u60D5\u60D9\u60DB\u60DD\u60DE\u60E2\u60E5\u60F2\u60F5\u60F8\u60FC\u60FD\u6102\u6107\u610A\u610C\u6110",4,"\u6116\u6117\u6119\u611C\u611E\u6122\u612A\u612B\u6130\u6131\u6135\u6136\u6137\u6139\u6141\u6145\u6146\u6149\u615E\u6160\u616C\u6172\u6178\u617B\u617C\u617F\u6180\u6181\u6183\u6184\u618B\u618D\u6192\u6193\u6197\u6198\u619C\u619D\u619F\u61A0\u61A5\u61A8\u61AA\u61AD\u61B8\u61B9\u61BC\u61C0\u61C1\u61C2\u61CE\u61CF\u61D5\u61DC\u61DD\u61DE\u61DF\u61E1\u61E2\u61E7\u61E9\u61E5"],["8fbfa1","\u61EC\u61ED\u61EF\u6201\u6203\u6204\u6207\u6213\u6215\u621C\u6220\u6222\u6223\u6227\u6229\u622B\u6239\u623D\u6242\u6243\u6244\u6246\u624C\u6250\u6251\u6252\u6254\u6256\u625A\u625C\u6264\u626D\u626F\u6273\u627A\u627D\u628D\u628E\u628F\u6290\u62A6\u62A8\u62B3\u62B6\u62B7\u62BA\u62BE\u62BF\u62C4\u62CE\u62D5\u62D6\u62DA\u62EA\u62F2\u62F4\u62FC\u62FD\u6303\u6304\u630A\u630B\u630D\u6310\u6313\u6316\u6318\u6329\u632A\u632D\u6335\u6336\u6339\u633C\u6341\u6342\u6343\u6344\u6346\u634A\u634B\u634E\u6352\u6353\u6354\u6358\u635B\u6365\u6366\u636C\u636D\u6371\u6374\u6375"],["8fc0a1","\u6378\u637C\u637D\u637F\u6382\u6384\u6387\u638A\u6390\u6394\u6395\u6399\u639A\u639E\u63A4\u63A6\u63AD\u63AE\u63AF\u63BD\u63C1\u63C5\u63C8\u63CE\u63D1\u63D3\u63D4\u63D5\u63DC\u63E0\u63E5\u63EA\u63EC\u63F2\u63F3\u63F5\u63F8\u63F9\u6409\u640A\u6410\u6412\u6414\u6418\u641E\u6420\u6422\u6424\u6425\u6429\u642A\u642F\u6430\u6435\u643D\u643F\u644B\u644F\u6451\u6452\u6453\u6454\u645A\u645B\u645C\u645D\u645F\u6460\u6461\u6463\u646D\u6473\u6474\u647B\u647D\u6485\u6487\u648F\u6490\u6491\u6498\u6499\u649B\u649D\u649F\u64A1\u64A3\u64A6\u64A8\u64AC\u64B3\u64BD\u64BE\u64BF"],["8fc1a1","\u64C4\u64C9\u64CA\u64CB\u64CC\u64CE\u64D0\u64D1\u64D5\u64D7\u64E4\u64E5\u64E9\u64EA\u64ED\u64F0\u64F5\u64F7\u64FB\u64FF\u6501\u6504\u6508\u6509\u650A\u650F\u6513\u6514\u6516\u6519\u651B\u651E\u651F\u6522\u6526\u6529\u652E\u6531\u653A\u653C\u653D\u6543\u6547\u6549\u6550\u6552\u6554\u655F\u6560\u6567\u656B\u657A\u657D\u6581\u6585\u658A\u6592\u6595\u6598\u659D\u65A0\u65A3\u65A6\u65AE\u65B2\u65B3\u65B4\u65BF\u65C2\u65C8\u65C9\u65CE\u65D0\u65D4\u65D6\u65D8\u65DF\u65F0\u65F2\u65F4\u65F5\u65F9\u65FE\u65FF\u6600\u6604\u6608\u6609\u660D\u6611\u6612\u6615\u6616\u661D"],["8fc2a1","\u661E\u6621\u6622\u6623\u6624\u6626\u6629\u662A\u662B\u662C\u662E\u6630\u6631\u6633\u6639\u6637\u6640\u6645\u6646\u664A\u664C\u6651\u664E\u6657\u6658\u6659\u665B\u665C\u6660\u6661\u66FB\u666A\u666B\u666C\u667E\u6673\u6675\u667F\u6677\u6678\u6679\u667B\u6680\u667C\u668B\u668C\u668D\u6690\u6692\u6699\u669A\u669B\u669C\u669F\u66A0\u66A4\u66AD\u66B1\u66B2\u66B5\u66BB\u66BF\u66C0\u66C2\u66C3\u66C8\u66CC\u66CE\u66CF\u66D4\u66DB\u66DF\u66E8\u66EB\u66EC\u66EE\u66FA\u6705\u6707\u670E\u6713\u6719\u671C\u6720\u6722\u6733\u673E\u6745\u6747\u6748\u674C\u6754\u6755\u675D"],["8fc3a1","\u6766\u676C\u676E\u6774\u6776\u677B\u6781\u6784\u678E\u678F\u6791\u6793\u6796\u6798\u6799\u679B\u67B0\u67B1\u67B2\u67B5\u67BB\u67BC\u67BD\u67F9\u67C0\u67C2\u67C3\u67C5\u67C8\u67C9\u67D2\u67D7\u67D9\u67DC\u67E1\u67E6\u67F0\u67F2\u67F6\u67F7\u6852\u6814\u6819\u681D\u681F\u6828\u6827\u682C\u682D\u682F\u6830\u6831\u6833\u683B\u683F\u6844\u6845\u684A\u684C\u6855\u6857\u6858\u685B\u686B\u686E",4,"\u6875\u6879\u687A\u687B\u687C\u6882\u6884\u6886\u6888\u6896\u6898\u689A\u689C\u68A1\u68A3\u68A5\u68A9\u68AA\u68AE\u68B2\u68BB\u68C5\u68C8\u68CC\u68CF"],["8fc4a1","\u68D0\u68D1\u68D3\u68D6\u68D9\u68DC\u68DD\u68E5\u68E8\u68EA\u68EB\u68EC\u68ED\u68F0\u68F1\u68F5\u68F6\u68FB\u68FC\u68FD\u6906\u6909\u690A\u6910\u6911\u6913\u6916\u6917\u6931\u6933\u6935\u6938\u693B\u6942\u6945\u6949\u694E\u6957\u695B\u6963\u6964\u6965\u6966\u6968\u6969\u696C\u6970\u6971\u6972\u697A\u697B\u697F\u6980\u698D\u6992\u6996\u6998\u69A1\u69A5\u69A6\u69A8\u69AB\u69AD\u69AF\u69B7\u69B8\u69BA\u69BC\u69C5\u69C8\u69D1\u69D6\u69D7\u69E2\u69E5\u69EE\u69EF\u69F1\u69F3\u69F5\u69FE\u6A00\u6A01\u6A03\u6A0F\u6A11\u6A15\u6A1A\u6A1D\u6A20\u6A24\u6A28\u6A30\u6A32"],["8fc5a1","\u6A34\u6A37\u6A3B\u6A3E\u6A3F\u6A45\u6A46\u6A49\u6A4A\u6A4E\u6A50\u6A51\u6A52\u6A55\u6A56\u6A5B\u6A64\u6A67\u6A6A\u6A71\u6A73\u6A7E\u6A81\u6A83\u6A86\u6A87\u6A89\u6A8B\u6A91\u6A9B\u6A9D\u6A9E\u6A9F\u6AA5\u6AAB\u6AAF\u6AB0\u6AB1\u6AB4\u6ABD\u6ABE\u6ABF\u6AC6\u6AC9\u6AC8\u6ACC\u6AD0\u6AD4\u6AD5\u6AD6\u6ADC\u6ADD\u6AE4\u6AE7\u6AEC\u6AF0\u6AF1\u6AF2\u6AFC\u6AFD\u6B02\u6B03\u6B06\u6B07\u6B09\u6B0F\u6B10\u6B11\u6B17\u6B1B\u6B1E\u6B24\u6B28\u6B2B\u6B2C\u6B2F\u6B35\u6B36\u6B3B\u6B3F\u6B46\u6B4A\u6B4D\u6B52\u6B56\u6B58\u6B5D\u6B60\u6B67\u6B6B\u6B6E\u6B70\u6B75\u6B7D"],["8fc6a1","\u6B7E\u6B82\u6B85\u6B97\u6B9B\u6B9F\u6BA0\u6BA2\u6BA3\u6BA8\u6BA9\u6BAC\u6BAD\u6BAE\u6BB0\u6BB8\u6BB9\u6BBD\u6BBE\u6BC3\u6BC4\u6BC9\u6BCC\u6BD6\u6BDA\u6BE1\u6BE3\u6BE6\u6BE7\u6BEE\u6BF1\u6BF7\u6BF9\u6BFF\u6C02\u6C04\u6C05\u6C09\u6C0D\u6C0E\u6C10\u6C12\u6C19\u6C1F\u6C26\u6C27\u6C28\u6C2C\u6C2E\u6C33\u6C35\u6C36\u6C3A\u6C3B\u6C3F\u6C4A\u6C4B\u6C4D\u6C4F\u6C52\u6C54\u6C59\u6C5B\u6C5C\u6C6B\u6C6D\u6C6F\u6C74\u6C76\u6C78\u6C79\u6C7B\u6C85\u6C86\u6C87\u6C89\u6C94\u6C95\u6C97\u6C98\u6C9C\u6C9F\u6CB0\u6CB2\u6CB4\u6CC2\u6CC6\u6CCD\u6CCF\u6CD0\u6CD1\u6CD2\u6CD4\u6CD6"],["8fc7a1","\u6CDA\u6CDC\u6CE0\u6CE7\u6CE9\u6CEB\u6CEC\u6CEE\u6CF2\u6CF4\u6D04\u6D07\u6D0A\u6D0E\u6D0F\u6D11\u6D13\u6D1A\u6D26\u6D27\u6D28\u6C67\u6D2E\u6D2F\u6D31\u6D39\u6D3C\u6D3F\u6D57\u6D5E\u6D5F\u6D61\u6D65\u6D67\u6D6F\u6D70\u6D7C\u6D82\u6D87\u6D91\u6D92\u6D94\u6D96\u6D97\u6D98\u6DAA\u6DAC\u6DB4\u6DB7\u6DB9\u6DBD\u6DBF\u6DC4\u6DC8\u6DCA\u6DCE\u6DCF\u6DD6\u6DDB\u6DDD\u6DDF\u6DE0\u6DE2\u6DE5\u6DE9\u6DEF\u6DF0\u6DF4\u6DF6\u6DFC\u6E00\u6E04\u6E1E\u6E22\u6E27\u6E32\u6E36\u6E39\u6E3B\u6E3C\u6E44\u6E45\u6E48\u6E49\u6E4B\u6E4F\u6E51\u6E52\u6E53\u6E54\u6E57\u6E5C\u6E5D\u6E5E"],["8fc8a1","\u6E62\u6E63\u6E68\u6E73\u6E7B\u6E7D\u6E8D\u6E93\u6E99\u6EA0\u6EA7\u6EAD\u6EAE\u6EB1\u6EB3\u6EBB\u6EBF\u6EC0\u6EC1\u6EC3\u6EC7\u6EC8\u6ECA\u6ECD\u6ECE\u6ECF\u6EEB\u6EED\u6EEE\u6EF9\u6EFB\u6EFD\u6F04\u6F08\u6F0A\u6F0C\u6F0D\u6F16\u6F18\u6F1A\u6F1B\u6F26\u6F29\u6F2A\u6F2F\u6F30\u6F33\u6F36\u6F3B\u6F3C\u6F2D\u6F4F\u6F51\u6F52\u6F53\u6F57\u6F59\u6F5A\u6F5D\u6F5E\u6F61\u6F62\u6F68\u6F6C\u6F7D\u6F7E\u6F83\u6F87\u6F88\u6F8B\u6F8C\u6F8D\u6F90\u6F92\u6F93\u6F94\u6F96\u6F9A\u6F9F\u6FA0\u6FA5\u6FA6\u6FA7\u6FA8\u6FAE\u6FAF\u6FB0\u6FB5\u6FB6\u6FBC\u6FC5\u6FC7\u6FC8\u6FCA"],["8fc9a1","\u6FDA\u6FDE\u6FE8\u6FE9\u6FF0\u6FF5\u6FF9\u6FFC\u6FFD\u7000\u7005\u7006\u7007\u700D\u7017\u7020\u7023\u702F\u7034\u7037\u7039\u703C\u7043\u7044\u7048\u7049\u704A\u704B\u7054\u7055\u705D\u705E\u704E\u7064\u7065\u706C\u706E\u7075\u7076\u707E\u7081\u7085\u7086\u7094",4,"\u709B\u70A4\u70AB\u70B0\u70B1\u70B4\u70B7\u70CA\u70D1\u70D3\u70D4\u70D5\u70D6\u70D8\u70DC\u70E4\u70FA\u7103",4,"\u710B\u710C\u710F\u711E\u7120\u712B\u712D\u712F\u7130\u7131\u7138\u7141\u7145\u7146\u7147\u714A\u714B\u7150\u7152\u7157\u715A\u715C\u715E\u7160"],["8fcaa1","\u7168\u7179\u7180\u7185\u7187\u718C\u7192\u719A\u719B\u71A0\u71A2\u71AF\u71B0\u71B2\u71B3\u71BA\u71BF\u71C0\u71C1\u71C4\u71CB\u71CC\u71D3\u71D6\u71D9\u71DA\u71DC\u71F8\u71FE\u7200\u7207\u7208\u7209\u7213\u7217\u721A\u721D\u721F\u7224\u722B\u722F\u7234\u7238\u7239\u7241\u7242\u7243\u7245\u724E\u724F\u7250\u7253\u7255\u7256\u725A\u725C\u725E\u7260\u7263\u7268\u726B\u726E\u726F\u7271\u7277\u7278\u727B\u727C\u727F\u7284\u7289\u728D\u728E\u7293\u729B\u72A8\u72AD\u72AE\u72B1\u72B4\u72BE\u72C1\u72C7\u72C9\u72CC\u72D5\u72D6\u72D8\u72DF\u72E5\u72F3\u72F4\u72FA\u72FB"],["8fcba1","\u72FE\u7302\u7304\u7305\u7307\u730B\u730D\u7312\u7313\u7318\u7319\u731E\u7322\u7324\u7327\u7328\u732C\u7331\u7332\u7335\u733A\u733B\u733D\u7343\u734D\u7350\u7352\u7356\u7358\u735D\u735E\u735F\u7360\u7366\u7367\u7369\u736B\u736C\u736E\u736F\u7371\u7377\u7379\u737C\u7380\u7381\u7383\u7385\u7386\u738E\u7390\u7393\u7395\u7397\u7398\u739C\u739E\u739F\u73A0\u73A2\u73A5\u73A6\u73AA\u73AB\u73AD\u73B5\u73B7\u73B9\u73BC\u73BD\u73BF\u73C5\u73C6\u73C9\u73CB\u73CC\u73CF\u73D2\u73D3\u73D6\u73D9\u73DD\u73E1\u73E3\u73E6\u73E7\u73E9\u73F4\u73F5\u73F7\u73F9\u73FA\u73FB\u73FD"],["8fcca1","\u73FF\u7400\u7401\u7404\u7407\u740A\u7411\u741A\u741B\u7424\u7426\u7428",9,"\u7439\u7440\u7443\u7444\u7446\u7447\u744B\u744D\u7451\u7452\u7457\u745D\u7462\u7466\u7467\u7468\u746B\u746D\u746E\u7471\u7472\u7480\u7481\u7485\u7486\u7487\u7489\u748F\u7490\u7491\u7492\u7498\u7499\u749A\u749C\u749F\u74A0\u74A1\u74A3\u74A6\u74A8\u74A9\u74AA\u74AB\u74AE\u74AF\u74B1\u74B2\u74B5\u74B9\u74BB\u74BF\u74C8\u74C9\u74CC\u74D0\u74D3\u74D8\u74DA\u74DB\u74DE\u74DF\u74E4\u74E8\u74EA\u74EB\u74EF\u74F4\u74FA\u74FB\u74FC\u74FF\u7506"],["8fcda1","\u7512\u7516\u7517\u7520\u7521\u7524\u7527\u7529\u752A\u752F\u7536\u7539\u753D\u753E\u753F\u7540\u7543\u7547\u7548\u754E\u7550\u7552\u7557\u755E\u755F\u7561\u756F\u7571\u7579",5,"\u7581\u7585\u7590\u7592\u7593\u7595\u7599\u759C\u75A2\u75A4\u75B4\u75BA\u75BF\u75C0\u75C1\u75C4\u75C6\u75CC\u75CE\u75CF\u75D7\u75DC\u75DF\u75E0\u75E1\u75E4\u75E7\u75EC\u75EE\u75EF\u75F1\u75F9\u7600\u7602\u7603\u7604\u7607\u7608\u760A\u760C\u760F\u7612\u7613\u7615\u7616\u7619\u761B\u761C\u761D\u761E\u7623\u7625\u7626\u7629\u762D\u7632\u7633\u7635\u7638\u7639"],["8fcea1","\u763A\u763C\u764A\u7640\u7641\u7643\u7644\u7645\u7649\u764B\u7655\u7659\u765F\u7664\u7665\u766D\u766E\u766F\u7671\u7674\u7681\u7685\u768C\u768D\u7695\u769B\u769C\u769D\u769F\u76A0\u76A2",6,"\u76AA\u76AD\u76BD\u76C1\u76C5\u76C9\u76CB\u76CC\u76CE\u76D4\u76D9\u76E0\u76E6\u76E8\u76EC\u76F0\u76F1\u76F6\u76F9\u76FC\u7700\u7706\u770A\u770E\u7712\u7714\u7715\u7717\u7719\u771A\u771C\u7722\u7728\u772D\u772E\u772F\u7734\u7735\u7736\u7739\u773D\u773E\u7742\u7745\u7746\u774A\u774D\u774E\u774F\u7752\u7756\u7757\u775C\u775E\u775F\u7760\u7762"],["8fcfa1","\u7764\u7767\u776A\u776C\u7770\u7772\u7773\u7774\u777A\u777D\u7780\u7784\u778C\u778D\u7794\u7795\u7796\u779A\u779F\u77A2\u77A7\u77AA\u77AE\u77AF\u77B1\u77B5\u77BE\u77C3\u77C9\u77D1\u77D2\u77D5\u77D9\u77DE\u77DF\u77E0\u77E4\u77E6\u77EA\u77EC\u77F0\u77F1\u77F4\u77F8\u77FB\u7805\u7806\u7809\u780D\u780E\u7811\u781D\u7821\u7822\u7823\u782D\u782E\u7830\u7835\u7837\u7843\u7844\u7847\u7848\u784C\u784E\u7852\u785C\u785E\u7860\u7861\u7863\u7864\u7868\u786A\u786E\u787A\u787E\u788A\u788F\u7894\u7898\u78A1\u789D\u789E\u789F\u78A4\u78A8\u78AC\u78AD\u78B0\u78B1\u78B2\u78B3"],["8fd0a1","\u78BB\u78BD\u78BF\u78C7\u78C8\u78C9\u78CC\u78CE\u78D2\u78D3\u78D5\u78D6\u78E4\u78DB\u78DF\u78E0\u78E1\u78E6\u78EA\u78F2\u78F3\u7900\u78F6\u78F7\u78FA\u78FB\u78FF\u7906\u790C\u7910\u791A\u791C\u791E\u791F\u7920\u7925\u7927\u7929\u792D\u7931\u7934\u7935\u793B\u793D\u793F\u7944\u7945\u7946\u794A\u794B\u794F\u7951\u7954\u7958\u795B\u795C\u7967\u7969\u796B\u7972\u7979\u797B\u797C\u797E\u798B\u798C\u7991\u7993\u7994\u7995\u7996\u7998\u799B\u799C\u79A1\u79A8\u79A9\u79AB\u79AF\u79B1\u79B4\u79B8\u79BB\u79C2\u79C4\u79C7\u79C8\u79CA\u79CF\u79D4\u79D6\u79DA\u79DD\u79DE"],["8fd1a1","\u79E0\u79E2\u79E5\u79EA\u79EB\u79ED\u79F1\u79F8\u79FC\u7A02\u7A03\u7A07\u7A09\u7A0A\u7A0C\u7A11\u7A15\u7A1B\u7A1E\u7A21\u7A27\u7A2B\u7A2D\u7A2F\u7A30\u7A34\u7A35\u7A38\u7A39\u7A3A\u7A44\u7A45\u7A47\u7A48\u7A4C\u7A55\u7A56\u7A59\u7A5C\u7A5D\u7A5F\u7A60\u7A65\u7A67\u7A6A\u7A6D\u7A75\u7A78\u7A7E\u7A80\u7A82\u7A85\u7A86\u7A8A\u7A8B\u7A90\u7A91\u7A94\u7A9E\u7AA0\u7AA3\u7AAC\u7AB3\u7AB5\u7AB9\u7ABB\u7ABC\u7AC6\u7AC9\u7ACC\u7ACE\u7AD1\u7ADB\u7AE8\u7AE9\u7AEB\u7AEC\u7AF1\u7AF4\u7AFB\u7AFD\u7AFE\u7B07\u7B14\u7B1F\u7B23\u7B27\u7B29\u7B2A\u7B2B\u7B2D\u7B2E\u7B2F\u7B30"],["8fd2a1","\u7B31\u7B34\u7B3D\u7B3F\u7B40\u7B41\u7B47\u7B4E\u7B55\u7B60\u7B64\u7B66\u7B69\u7B6A\u7B6D\u7B6F\u7B72\u7B73\u7B77\u7B84\u7B89\u7B8E\u7B90\u7B91\u7B96\u7B9B\u7B9E\u7BA0\u7BA5\u7BAC\u7BAF\u7BB0\u7BB2\u7BB5\u7BB6\u7BBA\u7BBB\u7BBC\u7BBD\u7BC2\u7BC5\u7BC8\u7BCA\u7BD4\u7BD6\u7BD7\u7BD9\u7BDA\u7BDB\u7BE8\u7BEA\u7BF2\u7BF4\u7BF5\u7BF8\u7BF9\u7BFA\u7BFC\u7BFE\u7C01\u7C02\u7C03\u7C04\u7C06\u7C09\u7C0B\u7C0C\u7C0E\u7C0F\u7C19\u7C1B\u7C20\u7C25\u7C26\u7C28\u7C2C\u7C31\u7C33\u7C34\u7C36\u7C39\u7C3A\u7C46\u7C4A\u7C55\u7C51\u7C52\u7C53\u7C59",5],["8fd3a1","\u7C61\u7C63\u7C67\u7C69\u7C6D\u7C6E\u7C70\u7C72\u7C79\u7C7C\u7C7D\u7C86\u7C87\u7C8F\u7C94\u7C9E\u7CA0\u7CA6\u7CB0\u7CB6\u7CB7\u7CBA\u7CBB\u7CBC\u7CBF\u7CC4\u7CC7\u7CC8\u7CC9\u7CCD\u7CCF\u7CD3\u7CD4\u7CD5\u7CD7\u7CD9\u7CDA\u7CDD\u7CE6\u7CE9\u7CEB\u7CF5\u7D03\u7D07\u7D08\u7D09\u7D0F\u7D11\u7D12\u7D13\u7D16\u7D1D\u7D1E\u7D23\u7D26\u7D2A\u7D2D\u7D31\u7D3C\u7D3D\u7D3E\u7D40\u7D41\u7D47\u7D48\u7D4D\u7D51\u7D53\u7D57\u7D59\u7D5A\u7D5C\u7D5D\u7D65\u7D67\u7D6A\u7D70\u7D78\u7D7A\u7D7B\u7D7F\u7D81\u7D82\u7D83\u7D85\u7D86\u7D88\u7D8B\u7D8C\u7D8D\u7D91\u7D96\u7D97\u7D9D"],["8fd4a1","\u7D9E\u7DA6\u7DA7\u7DAA\u7DB3\u7DB6\u7DB7\u7DB9\u7DC2",4,"\u7DCC\u7DCD\u7DCE\u7DD7\u7DD9\u7E00\u7DE2\u7DE5\u7DE6\u7DEA\u7DEB\u7DED\u7DF1\u7DF5\u7DF6\u7DF9\u7DFA\u7E08\u7E10\u7E11\u7E15\u7E17\u7E1C\u7E1D\u7E20\u7E27\u7E28\u7E2C\u7E2D\u7E2F\u7E33\u7E36\u7E3F\u7E44\u7E45\u7E47\u7E4E\u7E50\u7E52\u7E58\u7E5F\u7E61\u7E62\u7E65\u7E6B\u7E6E\u7E6F\u7E73\u7E78\u7E7E\u7E81\u7E86\u7E87\u7E8A\u7E8D\u7E91\u7E95\u7E98\u7E9A\u7E9D\u7E9E\u7F3C\u7F3B\u7F3D\u7F3E\u7F3F\u7F43\u7F44\u7F47\u7F4F\u7F52\u7F53\u7F5B\u7F5C\u7F5D\u7F61\u7F63\u7F64\u7F65\u7F66\u7F6D"],["8fd5a1","\u7F71\u7F7D\u7F7E\u7F7F\u7F80\u7F8B\u7F8D\u7F8F\u7F90\u7F91\u7F96\u7F97\u7F9C\u7FA1\u7FA2\u7FA6\u7FAA\u7FAD\u7FB4\u7FBC\u7FBF\u7FC0\u7FC3\u7FC8\u7FCE\u7FCF\u7FDB\u7FDF\u7FE3\u7FE5\u7FE8\u7FEC\u7FEE\u7FEF\u7FF2\u7FFA\u7FFD\u7FFE\u7FFF\u8007\u8008\u800A\u800D\u800E\u800F\u8011\u8013\u8014\u8016\u801D\u801E\u801F\u8020\u8024\u8026\u802C\u802E\u8030\u8034\u8035\u8037\u8039\u803A\u803C\u803E\u8040\u8044\u8060\u8064\u8066\u806D\u8071\u8075\u8081\u8088\u808E\u809C\u809E\u80A6\u80A7\u80AB\u80B8\u80B9\u80C8\u80CD\u80CF\u80D2\u80D4\u80D5\u80D7\u80D8\u80E0\u80ED\u80EE"],["8fd6a1","\u80F0\u80F2\u80F3\u80F6\u80F9\u80FA\u80FE\u8103\u810B\u8116\u8117\u8118\u811C\u811E\u8120\u8124\u8127\u812C\u8130\u8135\u813A\u813C\u8145\u8147\u814A\u814C\u8152\u8157\u8160\u8161\u8167\u8168\u8169\u816D\u816F\u8177\u8181\u8190\u8184\u8185\u8186\u818B\u818E\u8196\u8198\u819B\u819E\u81A2\u81AE\u81B2\u81B4\u81BB\u81CB\u81C3\u81C5\u81CA\u81CE\u81CF\u81D5\u81D7\u81DB\u81DD\u81DE\u81E1\u81E4\u81EB\u81EC\u81F0\u81F1\u81F2\u81F5\u81F6\u81F8\u81F9\u81FD\u81FF\u8200\u8203\u820F\u8213\u8214\u8219\u821A\u821D\u8221\u8222\u8228\u8232\u8234\u823A\u8243\u8244\u8245\u8246"],["8fd7a1","\u824B\u824E\u824F\u8251\u8256\u825C\u8260\u8263\u8267\u826D\u8274\u827B\u827D\u827F\u8280\u8281\u8283\u8284\u8287\u8289\u828A\u828E\u8291\u8294\u8296\u8298\u829A\u829B\u82A0\u82A1\u82A3\u82A4\u82A7\u82A8\u82A9\u82AA\u82AE\u82B0\u82B2\u82B4\u82B7\u82BA\u82BC\u82BE\u82BF\u82C6\u82D0\u82D5\u82DA\u82E0\u82E2\u82E4\u82E8\u82EA\u82ED\u82EF\u82F6\u82F7\u82FD\u82FE\u8300\u8301\u8307\u8308\u830A\u830B\u8354\u831B\u831D\u831E\u831F\u8321\u8322\u832C\u832D\u832E\u8330\u8333\u8337\u833A\u833C\u833D\u8342\u8343\u8344\u8347\u834D\u834E\u8351\u8355\u8356\u8357\u8370\u8378"],["8fd8a1","\u837D\u837F\u8380\u8382\u8384\u8386\u838D\u8392\u8394\u8395\u8398\u8399\u839B\u839C\u839D\u83A6\u83A7\u83A9\u83AC\u83BE\u83BF\u83C0\u83C7\u83C9\u83CF\u83D0\u83D1\u83D4\u83DD\u8353\u83E8\u83EA\u83F6\u83F8\u83F9\u83FC\u8401\u8406\u840A\u840F\u8411\u8415\u8419\u83AD\u842F\u8439\u8445\u8447\u8448\u844A\u844D\u844F\u8451\u8452\u8456\u8458\u8459\u845A\u845C\u8460\u8464\u8465\u8467\u846A\u8470\u8473\u8474\u8476\u8478\u847C\u847D\u8481\u8485\u8492\u8493\u8495\u849E\u84A6\u84A8\u84A9\u84AA\u84AF\u84B1\u84B4\u84BA\u84BD\u84BE\u84C0\u84C2\u84C7\u84C8\u84CC\u84CF\u84D3"],["8fd9a1","\u84DC\u84E7\u84EA\u84EF\u84F0\u84F1\u84F2\u84F7\u8532\u84FA\u84FB\u84FD\u8502\u8503\u8507\u850C\u850E\u8510\u851C\u851E\u8522\u8523\u8524\u8525\u8527\u852A\u852B\u852F\u8533\u8534\u8536\u853F\u8546\u854F",4,"\u8556\u8559\u855C",6,"\u8564\u856B\u856F\u8579\u857A\u857B\u857D\u857F\u8581\u8585\u8586\u8589\u858B\u858C\u858F\u8593\u8598\u859D\u859F\u85A0\u85A2\u85A5\u85A7\u85B4\u85B6\u85B7\u85B8\u85BC\u85BD\u85BE\u85BF\u85C2\u85C7\u85CA\u85CB\u85CE\u85AD\u85D8\u85DA\u85DF\u85E0\u85E6\u85E8\u85ED\u85F3\u85F6\u85FC"],["8fdaa1","\u85FF\u8600\u8604\u8605\u860D\u860E\u8610\u8611\u8612\u8618\u8619\u861B\u861E\u8621\u8627\u8629\u8636\u8638\u863A\u863C\u863D\u8640\u8642\u8646\u8652\u8653\u8656\u8657\u8658\u8659\u865D\u8660",4,"\u8669\u866C\u866F\u8675\u8676\u8677\u867A\u868D\u8691\u8696\u8698\u869A\u869C\u86A1\u86A6\u86A7\u86A8\u86AD\u86B1\u86B3\u86B4\u86B5\u86B7\u86B8\u86B9\u86BF\u86C0\u86C1\u86C3\u86C5\u86D1\u86D2\u86D5\u86D7\u86DA\u86DC\u86E0\u86E3\u86E5\u86E7\u8688\u86FA\u86FC\u86FD\u8704\u8705\u8707\u870B\u870E\u870F\u8710\u8713\u8714\u8719\u871E\u871F\u8721\u8723"],["8fdba1","\u8728\u872E\u872F\u8731\u8732\u8739\u873A\u873C\u873D\u873E\u8740\u8743\u8745\u874D\u8758\u875D\u8761\u8764\u8765\u876F\u8771\u8772\u877B\u8783",6,"\u878B\u878C\u8790\u8793\u8795\u8797\u8798\u8799\u879E\u87A0\u87A3\u87A7\u87AC\u87AD\u87AE\u87B1\u87B5\u87BE\u87BF\u87C1\u87C8\u87C9\u87CA\u87CE\u87D5\u87D6\u87D9\u87DA\u87DC\u87DF\u87E2\u87E3\u87E4\u87EA\u87EB\u87ED\u87F1\u87F3\u87F8\u87FA\u87FF\u8801\u8803\u8806\u8809\u880A\u880B\u8810\u8819\u8812\u8813\u8814\u8818\u881A\u881B\u881C\u881E\u881F\u8828\u882D\u882E\u8830\u8832\u8835"],["8fdca1","\u883A\u883C\u8841\u8843\u8845\u8848\u8849\u884A\u884B\u884E\u8851\u8855\u8856\u8858\u885A\u885C\u885F\u8860\u8864\u8869\u8871\u8879\u887B\u8880\u8898\u889A\u889B\u889C\u889F\u88A0\u88A8\u88AA\u88BA\u88BD\u88BE\u88C0\u88CA",4,"\u88D1\u88D2\u88D3\u88DB\u88DE\u88E7\u88EF\u88F0\u88F1\u88F5\u88F7\u8901\u8906\u890D\u890E\u890F\u8915\u8916\u8918\u8919\u891A\u891C\u8920\u8926\u8927\u8928\u8930\u8931\u8932\u8935\u8939\u893A\u893E\u8940\u8942\u8945\u8946\u8949\u894F\u8952\u8957\u895A\u895B\u895C\u8961\u8962\u8963\u896B\u896E\u8970\u8973\u8975\u897A"],["8fdda1","\u897B\u897C\u897D\u8989\u898D\u8990\u8994\u8995\u899B\u899C\u899F\u89A0\u89A5\u89B0\u89B4\u89B5\u89B6\u89B7\u89BC\u89D4",4,"\u89E5\u89E9\u89EB\u89ED\u89F1\u89F3\u89F6\u89F9\u89FD\u89FF\u8A04\u8A05\u8A07\u8A0F\u8A11\u8A12\u8A14\u8A15\u8A1E\u8A20\u8A22\u8A24\u8A26\u8A2B\u8A2C\u8A2F\u8A35\u8A37\u8A3D\u8A3E\u8A40\u8A43\u8A45\u8A47\u8A49\u8A4D\u8A4E\u8A53\u8A56\u8A57\u8A58\u8A5C\u8A5D\u8A61\u8A65\u8A67\u8A75\u8A76\u8A77\u8A79\u8A7A\u8A7B\u8A7E\u8A7F\u8A80\u8A83\u8A86\u8A8B\u8A8F\u8A90\u8A92\u8A96\u8A97\u8A99\u8A9F\u8AA7\u8AA9\u8AAE\u8AAF\u8AB3"],["8fdea1","\u8AB6\u8AB7\u8ABB\u8ABE\u8AC3\u8AC6\u8AC8\u8AC9\u8ACA\u8AD1\u8AD3\u8AD4\u8AD5\u8AD7\u8ADD\u8ADF\u8AEC\u8AF0\u8AF4\u8AF5\u8AF6\u8AFC\u8AFF\u8B05\u8B06\u8B0B\u8B11\u8B1C\u8B1E\u8B1F\u8B0A\u8B2D\u8B30\u8B37\u8B3C\u8B42",4,"\u8B48\u8B52\u8B53\u8B54\u8B59\u8B4D\u8B5E\u8B63\u8B6D\u8B76\u8B78\u8B79\u8B7C\u8B7E\u8B81\u8B84\u8B85\u8B8B\u8B8D\u8B8F\u8B94\u8B95\u8B9C\u8B9E\u8B9F\u8C38\u8C39\u8C3D\u8C3E\u8C45\u8C47\u8C49\u8C4B\u8C4F\u8C51\u8C53\u8C54\u8C57\u8C58\u8C5B\u8C5D\u8C59\u8C63\u8C64\u8C66\u8C68\u8C69\u8C6D\u8C73\u8C75\u8C76\u8C7B\u8C7E\u8C86"],["8fdfa1","\u8C87\u8C8B\u8C90\u8C92\u8C93\u8C99\u8C9B\u8C9C\u8CA4\u8CB9\u8CBA\u8CC5\u8CC6\u8CC9\u8CCB\u8CCF\u8CD6\u8CD5\u8CD9\u8CDD\u8CE1\u8CE8\u8CEC\u8CEF\u8CF0\u8CF2\u8CF5\u8CF7\u8CF8\u8CFE\u8CFF\u8D01\u8D03\u8D09\u8D12\u8D17\u8D1B\u8D65\u8D69\u8D6C\u8D6E\u8D7F\u8D82\u8D84\u8D88\u8D8D\u8D90\u8D91\u8D95\u8D9E\u8D9F\u8DA0\u8DA6\u8DAB\u8DAC\u8DAF\u8DB2\u8DB5\u8DB7\u8DB9\u8DBB\u8DC0\u8DC5\u8DC6\u8DC7\u8DC8\u8DCA\u8DCE\u8DD1\u8DD4\u8DD5\u8DD7\u8DD9\u8DE4\u8DE5\u8DE7\u8DEC\u8DF0\u8DBC\u8DF1\u8DF2\u8DF4\u8DFD\u8E01\u8E04\u8E05\u8E06\u8E0B\u8E11\u8E14\u8E16\u8E20\u8E21\u8E22"],["8fe0a1","\u8E23\u8E26\u8E27\u8E31\u8E33\u8E36\u8E37\u8E38\u8E39\u8E3D\u8E40\u8E41\u8E4B\u8E4D\u8E4E\u8E4F\u8E54\u8E5B\u8E5C\u8E5D\u8E5E\u8E61\u8E62\u8E69\u8E6C\u8E6D\u8E6F\u8E70\u8E71\u8E79\u8E7A\u8E7B\u8E82\u8E83\u8E89\u8E90\u8E92\u8E95\u8E9A\u8E9B\u8E9D\u8E9E\u8EA2\u8EA7\u8EA9\u8EAD\u8EAE\u8EB3\u8EB5\u8EBA\u8EBB\u8EC0\u8EC1\u8EC3\u8EC4\u8EC7\u8ECF\u8ED1\u8ED4\u8EDC\u8EE8\u8EEE\u8EF0\u8EF1\u8EF7\u8EF9\u8EFA\u8EED\u8F00\u8F02\u8F07\u8F08\u8F0F\u8F10\u8F16\u8F17\u8F18\u8F1E\u8F20\u8F21\u8F23\u8F25\u8F27\u8F28\u8F2C\u8F2D\u8F2E\u8F34\u8F35\u8F36\u8F37\u8F3A\u8F40\u8F41"],["8fe1a1","\u8F43\u8F47\u8F4F\u8F51",4,"\u8F58\u8F5D\u8F5E\u8F65\u8F9D\u8FA0\u8FA1\u8FA4\u8FA5\u8FA6\u8FB5\u8FB6\u8FB8\u8FBE\u8FC0\u8FC1\u8FC6\u8FCA\u8FCB\u8FCD\u8FD0\u8FD2\u8FD3\u8FD5\u8FE0\u8FE3\u8FE4\u8FE8\u8FEE\u8FF1\u8FF5\u8FF6\u8FFB\u8FFE\u9002\u9004\u9008\u900C\u9018\u901B\u9028\u9029\u902F\u902A\u902C\u902D\u9033\u9034\u9037\u903F\u9043\u9044\u904C\u905B\u905D\u9062\u9066\u9067\u906C\u9070\u9074\u9079\u9085\u9088\u908B\u908C\u908E\u9090\u9095\u9097\u9098\u9099\u909B\u90A0\u90A1\u90A2\u90A5\u90B0\u90B2\u90B3\u90B4\u90B6\u90BD\u90CC\u90BE\u90C3"],["8fe2a1","\u90C4\u90C5\u90C7\u90C8\u90D5\u90D7\u90D8\u90D9\u90DC\u90DD\u90DF\u90E5\u90D2\u90F6\u90EB\u90EF\u90F0\u90F4\u90FE\u90FF\u9100\u9104\u9105\u9106\u9108\u910D\u9110\u9114\u9116\u9117\u9118\u911A\u911C\u911E\u9120\u9125\u9122\u9123\u9127\u9129\u912E\u912F\u9131\u9134\u9136\u9137\u9139\u913A\u913C\u913D\u9143\u9147\u9148\u914F\u9153\u9157\u9159\u915A\u915B\u9161\u9164\u9167\u916D\u9174\u9179\u917A\u917B\u9181\u9183\u9185\u9186\u918A\u918E\u9191\u9193\u9194\u9195\u9198\u919E\u91A1\u91A6\u91A8\u91AC\u91AD\u91AE\u91B0\u91B1\u91B2\u91B3\u91B6\u91BB\u91BC\u91BD\u91BF"],["8fe3a1","\u91C2\u91C3\u91C5\u91D3\u91D4\u91D7\u91D9\u91DA\u91DE\u91E4\u91E5\u91E9\u91EA\u91EC",5,"\u91F7\u91F9\u91FB\u91FD\u9200\u9201\u9204\u9205\u9206\u9207\u9209\u920A\u920C\u9210\u9212\u9213\u9216\u9218\u921C\u921D\u9223\u9224\u9225\u9226\u9228\u922E\u922F\u9230\u9233\u9235\u9236\u9238\u9239\u923A\u923C\u923E\u9240\u9242\u9243\u9246\u9247\u924A\u924D\u924E\u924F\u9251\u9258\u9259\u925C\u925D\u9260\u9261\u9265\u9267\u9268\u9269\u926E\u926F\u9270\u9275",4,"\u927B\u927C\u927D\u927F\u9288\u9289\u928A\u928D\u928E\u9292\u9297"],["8fe4a1","\u9299\u929F\u92A0\u92A4\u92A5\u92A7\u92A8\u92AB\u92AF\u92B2\u92B6\u92B8\u92BA\u92BB\u92BC\u92BD\u92BF",4,"\u92C5\u92C6\u92C7\u92C8\u92CB\u92CC\u92CD\u92CE\u92D0\u92D3\u92D5\u92D7\u92D8\u92D9\u92DC\u92DD\u92DF\u92E0\u92E1\u92E3\u92E5\u92E7\u92E8\u92EC\u92EE\u92F0\u92F9\u92FB\u92FF\u9300\u9302\u9308\u930D\u9311\u9314\u9315\u931C\u931D\u931E\u931F\u9321\u9324\u9325\u9327\u9329\u932A\u9333\u9334\u9336\u9337\u9347\u9348\u9349\u9350\u9351\u9352\u9355\u9357\u9358\u935A\u935E\u9364\u9365\u9367\u9369\u936A\u936D\u936F\u9370\u9371\u9373\u9374\u9376"],["8fe5a1","\u937A\u937D\u937F\u9380\u9381\u9382\u9388\u938A\u938B\u938D\u938F\u9392\u9395\u9398\u939B\u939E\u93A1\u93A3\u93A4\u93A6\u93A8\u93AB\u93B4\u93B5\u93B6\u93BA\u93A9\u93C1\u93C4\u93C5\u93C6\u93C7\u93C9",4,"\u93D3\u93D9\u93DC\u93DE\u93DF\u93E2\u93E6\u93E7\u93F9\u93F7\u93F8\u93FA\u93FB\u93FD\u9401\u9402\u9404\u9408\u9409\u940D\u940E\u940F\u9415\u9416\u9417\u941F\u942E\u942F\u9431\u9432\u9433\u9434\u943B\u943F\u943D\u9443\u9445\u9448\u944A\u944C\u9455\u9459\u945C\u945F\u9461\u9463\u9468\u946B\u946D\u946E\u946F\u9471\u9472\u9484\u9483\u9578\u9579"],["8fe6a1","\u957E\u9584\u9588\u958C\u958D\u958E\u959D\u959E\u959F\u95A1\u95A6\u95A9\u95AB\u95AC\u95B4\u95B6\u95BA\u95BD\u95BF\u95C6\u95C8\u95C9\u95CB\u95D0\u95D1\u95D2\u95D3\u95D9\u95DA\u95DD\u95DE\u95DF\u95E0\u95E4\u95E6\u961D\u961E\u9622\u9624\u9625\u9626\u962C\u9631\u9633\u9637\u9638\u9639\u963A\u963C\u963D\u9641\u9652\u9654\u9656\u9657\u9658\u9661\u966E\u9674\u967B\u967C\u967E\u967F\u9681\u9682\u9683\u9684\u9689\u9691\u9696\u969A\u969D\u969F\u96A4\u96A5\u96A6\u96A9\u96AE\u96AF\u96B3\u96BA\u96CA\u96D2\u5DB2\u96D8\u96DA\u96DD\u96DE\u96DF\u96E9\u96EF\u96F1\u96FA\u9702"],["8fe7a1","\u9703\u9705\u9709\u971A\u971B\u971D\u9721\u9722\u9723\u9728\u9731\u9733\u9741\u9743\u974A\u974E\u974F\u9755\u9757\u9758\u975A\u975B\u9763\u9767\u976A\u976E\u9773\u9776\u9777\u9778\u977B\u977D\u977F\u9780\u9789\u9795\u9796\u9797\u9799\u979A\u979E\u979F\u97A2\u97AC\u97AE\u97B1\u97B2\u97B5\u97B6\u97B8\u97B9\u97BA\u97BC\u97BE\u97BF\u97C1\u97C4\u97C5\u97C7\u97C9\u97CA\u97CC\u97CD\u97CE\u97D0\u97D1\u97D4\u97D7\u97D8\u97D9\u97DD\u97DE\u97E0\u97DB\u97E1\u97E4\u97EF\u97F1\u97F4\u97F7\u97F8\u97FA\u9807\u980A\u9819\u980D\u980E\u9814\u9816\u981C\u981E\u9820\u9823\u9826"],["8fe8a1","\u982B\u982E\u982F\u9830\u9832\u9833\u9835\u9825\u983E\u9844\u9847\u984A\u9851\u9852\u9853\u9856\u9857\u9859\u985A\u9862\u9863\u9865\u9866\u986A\u986C\u98AB\u98AD\u98AE\u98B0\u98B4\u98B7\u98B8\u98BA\u98BB\u98BF\u98C2\u98C5\u98C8\u98CC\u98E1\u98E3\u98E5\u98E6\u98E7\u98EA\u98F3\u98F6\u9902\u9907\u9908\u9911\u9915\u9916\u9917\u991A\u991B\u991C\u991F\u9922\u9926\u9927\u992B\u9931",4,"\u9939\u993A\u993B\u993C\u9940\u9941\u9946\u9947\u9948\u994D\u994E\u9954\u9958\u9959\u995B\u995C\u995E\u995F\u9960\u999B\u999D\u999F\u99A6\u99B0\u99B1\u99B2\u99B5"],["8fe9a1","\u99B9\u99BA\u99BD\u99BF\u99C3\u99C9\u99D3\u99D4\u99D9\u99DA\u99DC\u99DE\u99E7\u99EA\u99EB\u99EC\u99F0\u99F4\u99F5\u99F9\u99FD\u99FE\u9A02\u9A03\u9A04\u9A0B\u9A0C\u9A10\u9A11\u9A16\u9A1E\u9A20\u9A22\u9A23\u9A24\u9A27\u9A2D\u9A2E\u9A33\u9A35\u9A36\u9A38\u9A47\u9A41\u9A44\u9A4A\u9A4B\u9A4C\u9A4E\u9A51\u9A54\u9A56\u9A5D\u9AAA\u9AAC\u9AAE\u9AAF\u9AB2\u9AB4\u9AB5\u9AB6\u9AB9\u9ABB\u9ABE\u9ABF\u9AC1\u9AC3\u9AC6\u9AC8\u9ACE\u9AD0\u9AD2\u9AD5\u9AD6\u9AD7\u9ADB\u9ADC\u9AE0\u9AE4\u9AE5\u9AE7\u9AE9\u9AEC\u9AF2\u9AF3\u9AF5\u9AF9\u9AFA\u9AFD\u9AFF",4],["8feaa1","\u9B04\u9B05\u9B08\u9B09\u9B0B\u9B0C\u9B0D\u9B0E\u9B10\u9B12\u9B16\u9B19\u9B1B\u9B1C\u9B20\u9B26\u9B2B\u9B2D\u9B33\u9B34\u9B35\u9B37\u9B39\u9B3A\u9B3D\u9B48\u9B4B\u9B4C\u9B55\u9B56\u9B57\u9B5B\u9B5E\u9B61\u9B63\u9B65\u9B66\u9B68\u9B6A",4,"\u9B73\u9B75\u9B77\u9B78\u9B79\u9B7F\u9B80\u9B84\u9B85\u9B86\u9B87\u9B89\u9B8A\u9B8B\u9B8D\u9B8F\u9B90\u9B94\u9B9A\u9B9D\u9B9E\u9BA6\u9BA7\u9BA9\u9BAC\u9BB0\u9BB1\u9BB2\u9BB7\u9BB8\u9BBB\u9BBC\u9BBE\u9BBF\u9BC1\u9BC7\u9BC8\u9BCE\u9BD0\u9BD7\u9BD8\u9BDD\u9BDF\u9BE5\u9BE7\u9BEA\u9BEB\u9BEF\u9BF3\u9BF7\u9BF8"],["8feba1","\u9BF9\u9BFA\u9BFD\u9BFF\u9C00\u9C02\u9C0B\u9C0F\u9C11\u9C16\u9C18\u9C19\u9C1A\u9C1C\u9C1E\u9C22\u9C23\u9C26",4,"\u9C31\u9C35\u9C36\u9C37\u9C3D\u9C41\u9C43\u9C44\u9C45\u9C49\u9C4A\u9C4E\u9C4F\u9C50\u9C53\u9C54\u9C56\u9C58\u9C5B\u9C5D\u9C5E\u9C5F\u9C63\u9C69\u9C6A\u9C5C\u9C6B\u9C68\u9C6E\u9C70\u9C72\u9C75\u9C77\u9C7B\u9CE6\u9CF2\u9CF7\u9CF9\u9D0B\u9D02\u9D11\u9D17\u9D18\u9D1C\u9D1D\u9D1E\u9D2F\u9D30\u9D32\u9D33\u9D34\u9D3A\u9D3C\u9D45\u9D3D\u9D42\u9D43\u9D47\u9D4A\u9D53\u9D54\u9D5F\u9D63\u9D62\u9D65\u9D69\u9D6A\u9D6B\u9D70\u9D76\u9D77\u9D7B"],["8feca1","\u9D7C\u9D7E\u9D83\u9D84\u9D86\u9D8A\u9D8D\u9D8E\u9D92\u9D93\u9D95\u9D96\u9D97\u9D98\u9DA1\u9DAA\u9DAC\u9DAE\u9DB1\u9DB5\u9DB9\u9DBC\u9DBF\u9DC3\u9DC7\u9DC9\u9DCA\u9DD4\u9DD5\u9DD6\u9DD7\u9DDA\u9DDE\u9DDF\u9DE0\u9DE5\u9DE7\u9DE9\u9DEB\u9DEE\u9DF0\u9DF3\u9DF4\u9DFE\u9E0A\u9E02\u9E07\u9E0E\u9E10\u9E11\u9E12\u9E15\u9E16\u9E19\u9E1C\u9E1D\u9E7A\u9E7B\u9E7C\u9E80\u9E82\u9E83\u9E84\u9E85\u9E87\u9E8E\u9E8F\u9E96\u9E98\u9E9B\u9E9E\u9EA4\u9EA8\u9EAC\u9EAE\u9EAF\u9EB0\u9EB3\u9EB4\u9EB5\u9EC6\u9EC8\u9ECB\u9ED5\u9EDF\u9EE4\u9EE7\u9EEC\u9EED\u9EEE\u9EF0\u9EF1\u9EF2\u9EF5"],["8feda1","\u9EF8\u9EFF\u9F02\u9F03\u9F09\u9F0F\u9F10\u9F11\u9F12\u9F14\u9F16\u9F17\u9F19\u9F1A\u9F1B\u9F1F\u9F22\u9F26\u9F2A\u9F2B\u9F2F\u9F31\u9F32\u9F34\u9F37\u9F39\u9F3A\u9F3C\u9F3D\u9F3F\u9F41\u9F43",4,"\u9F53\u9F55\u9F56\u9F57\u9F58\u9F5A\u9F5D\u9F5E\u9F68\u9F69\u9F6D",4,"\u9F73\u9F75\u9F7A\u9F7D\u9F8F\u9F90\u9F91\u9F92\u9F94\u9F96\u9F97\u9F9E\u9FA1\u9FA2\u9FA3\u9FA5"]]});var vD=S((Nkr,Cqt)=>{Cqt.exports=[["0","\0",127,"\u20AC"],["8140","\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A",5,"\u4E72\u4E74",9,"\u4E7F",6,"\u4E87\u4E8A"],["8180","\u4E90\u4E96\u4E97\u4E99\u4E9C\u4E9D\u4E9E\u4EA3\u4EAA\u4EAF\u4EB0\u4EB1\u4EB4\u4EB6\u4EB7\u4EB8\u4EB9\u4EBC\u4EBD\u4EBE\u4EC8\u4ECC\u4ECF\u4ED0\u4ED2\u4EDA\u4EDB\u4EDC\u4EE0\u4EE2\u4EE6\u4EE7\u4EE9\u4EED\u4EEE\u4EEF\u4EF1\u4EF4\u4EF8\u4EF9\u4EFA\u4EFC\u4EFE\u4F00\u4F02",6,"\u4F0B\u4F0C\u4F12",4,"\u4F1C\u4F1D\u4F21\u4F23\u4F28\u4F29\u4F2C\u4F2D\u4F2E\u4F31\u4F33\u4F35\u4F37\u4F39\u4F3B\u4F3E",4,"\u4F44\u4F45\u4F47",5,"\u4F52\u4F54\u4F56\u4F61\u4F62\u4F66\u4F68\u4F6A\u4F6B\u4F6D\u4F6E\u4F71\u4F72\u4F75\u4F77\u4F78\u4F79\u4F7A\u4F7D\u4F80\u4F81\u4F82\u4F85\u4F86\u4F87\u4F8A\u4F8C\u4F8E\u4F90\u4F92\u4F93\u4F95\u4F96\u4F98\u4F99\u4F9A\u4F9C\u4F9E\u4F9F\u4FA1\u4FA2"],["8240","\u4FA4\u4FAB\u4FAD\u4FB0",4,"\u4FB6",8,"\u4FC0\u4FC1\u4FC2\u4FC6\u4FC7\u4FC8\u4FC9\u4FCB\u4FCC\u4FCD\u4FD2",4,"\u4FD9\u4FDB\u4FE0\u4FE2\u4FE4\u4FE5\u4FE7\u4FEB\u4FEC\u4FF0\u4FF2\u4FF4\u4FF5\u4FF6\u4FF7\u4FF9\u4FFB\u4FFC\u4FFD\u4FFF",11],["8280","\u500B\u500E\u5010\u5011\u5013\u5015\u5016\u5017\u501B\u501D\u501E\u5020\u5022\u5023\u5024\u5027\u502B\u502F",10,"\u503B\u503D\u503F\u5040\u5041\u5042\u5044\u5045\u5046\u5049\u504A\u504B\u504D\u5050",4,"\u5056\u5057\u5058\u5059\u505B\u505D",7,"\u5066",5,"\u506D",8,"\u5078\u5079\u507A\u507C\u507D\u5081\u5082\u5083\u5084\u5086\u5087\u5089\u508A\u508B\u508C\u508E",20,"\u50A4\u50A6\u50AA\u50AB\u50AD",4,"\u50B3",6,"\u50BC"],["8340","\u50BD",17,"\u50D0",5,"\u50D7\u50D8\u50D9\u50DB",10,"\u50E8\u50E9\u50EA\u50EB\u50EF\u50F0\u50F1\u50F2\u50F4\u50F6",4,"\u50FC",9,"\u5108"],["8380","\u5109\u510A\u510C",5,"\u5113",13,"\u5122",28,"\u5142\u5147\u514A\u514C\u514E\u514F\u5150\u5152\u5153\u5157\u5158\u5159\u515B\u515D",4,"\u5163\u5164\u5166\u5167\u5169\u516A\u516F\u5172\u517A\u517E\u517F\u5183\u5184\u5186\u5187\u518A\u518B\u518E\u518F\u5190\u5191\u5193\u5194\u5198\u519A\u519D\u519E\u519F\u51A1\u51A3\u51A6",4,"\u51AD\u51AE\u51B4\u51B8\u51B9\u51BA\u51BE\u51BF\u51C1\u51C2\u51C3\u51C5\u51C8\u51CA\u51CD\u51CE\u51D0\u51D2",5],["8440","\u51D8\u51D9\u51DA\u51DC\u51DE\u51DF\u51E2\u51E3\u51E5",5,"\u51EC\u51EE\u51F1\u51F2\u51F4\u51F7\u51FE\u5204\u5205\u5209\u520B\u520C\u520F\u5210\u5213\u5214\u5215\u521C\u521E\u521F\u5221\u5222\u5223\u5225\u5226\u5227\u522A\u522C\u522F\u5231\u5232\u5234\u5235\u523C\u523E\u5244",5,"\u524B\u524E\u524F\u5252\u5253\u5255\u5257\u5258"],["8480","\u5259\u525A\u525B\u525D\u525F\u5260\u5262\u5263\u5264\u5266\u5268\u526B\u526C\u526D\u526E\u5270\u5271\u5273",9,"\u527E\u5280\u5283",4,"\u5289",6,"\u5291\u5292\u5294",6,"\u529C\u52A4\u52A5\u52A6\u52A7\u52AE\u52AF\u52B0\u52B4",9,"\u52C0\u52C1\u52C2\u52C4\u52C5\u52C6\u52C8\u52CA\u52CC\u52CD\u52CE\u52CF\u52D1\u52D3\u52D4\u52D5\u52D7\u52D9",5,"\u52E0\u52E1\u52E2\u52E3\u52E5",10,"\u52F1",7,"\u52FB\u52FC\u52FD\u5301\u5302\u5303\u5304\u5307\u5309\u530A\u530B\u530C\u530E"],["8540","\u5311\u5312\u5313\u5314\u5318\u531B\u531C\u531E\u531F\u5322\u5324\u5325\u5327\u5328\u5329\u532B\u532C\u532D\u532F",9,"\u533C\u533D\u5340\u5342\u5344\u5346\u534B\u534C\u534D\u5350\u5354\u5358\u5359\u535B\u535D\u5365\u5368\u536A\u536C\u536D\u5372\u5376\u5379\u537B\u537C\u537D\u537E\u5380\u5381\u5383\u5387\u5388\u538A\u538E\u538F"],["8580","\u5390",4,"\u5396\u5397\u5399\u539B\u539C\u539E\u53A0\u53A1\u53A4\u53A7\u53AA\u53AB\u53AC\u53AD\u53AF",6,"\u53B7\u53B8\u53B9\u53BA\u53BC\u53BD\u53BE\u53C0\u53C3",4,"\u53CE\u53CF\u53D0\u53D2\u53D3\u53D5\u53DA\u53DC\u53DD\u53DE\u53E1\u53E2\u53E7\u53F4\u53FA\u53FE\u53FF\u5400\u5402\u5405\u5407\u540B\u5414\u5418\u5419\u541A\u541C\u5422\u5424\u5425\u542A\u5430\u5433\u5436\u5437\u543A\u543D\u543F\u5441\u5442\u5444\u5445\u5447\u5449\u544C\u544D\u544E\u544F\u5451\u545A\u545D",4,"\u5463\u5465\u5467\u5469",7,"\u5474\u5479\u547A\u547E\u547F\u5481\u5483\u5485\u5487\u5488\u5489\u548A\u548D\u5491\u5493\u5497\u5498\u549C\u549E\u549F\u54A0\u54A1"],["8640","\u54A2\u54A5\u54AE\u54B0\u54B2\u54B5\u54B6\u54B7\u54B9\u54BA\u54BC\u54BE\u54C3\u54C5\u54CA\u54CB\u54D6\u54D8\u54DB\u54E0",4,"\u54EB\u54EC\u54EF\u54F0\u54F1\u54F4",5,"\u54FB\u54FE\u5500\u5502\u5503\u5504\u5505\u5508\u550A",4,"\u5512\u5513\u5515",5,"\u551C\u551D\u551E\u551F\u5521\u5525\u5526"],["8680","\u5528\u5529\u552B\u552D\u5532\u5534\u5535\u5536\u5538\u5539\u553A\u553B\u553D\u5540\u5542\u5545\u5547\u5548\u554B",4,"\u5551\u5552\u5553\u5554\u5557",4,"\u555D\u555E\u555F\u5560\u5562\u5563\u5568\u5569\u556B\u556F",5,"\u5579\u557A\u557D\u557F\u5585\u5586\u558C\u558D\u558E\u5590\u5592\u5593\u5595\u5596\u5597\u559A\u559B\u559E\u55A0",6,"\u55A8",8,"\u55B2\u55B4\u55B6\u55B8\u55BA\u55BC\u55BF",4,"\u55C6\u55C7\u55C8\u55CA\u55CB\u55CE\u55CF\u55D0\u55D5\u55D7",4,"\u55DE\u55E0\u55E2\u55E7\u55E9\u55ED\u55EE\u55F0\u55F1\u55F4\u55F6\u55F8",4,"\u55FF\u5602\u5603\u5604\u5605"],["8740","\u5606\u5607\u560A\u560B\u560D\u5610",7,"\u5619\u561A\u561C\u561D\u5620\u5621\u5622\u5625\u5626\u5628\u5629\u562A\u562B\u562E\u562F\u5630\u5633\u5635\u5637\u5638\u563A\u563C\u563D\u563E\u5640",11,"\u564F",4,"\u5655\u5656\u565A\u565B\u565D",4],["8780","\u5663\u5665\u5666\u5667\u566D\u566E\u566F\u5670\u5672\u5673\u5674\u5675\u5677\u5678\u5679\u567A\u567D",7,"\u5687",6,"\u5690\u5691\u5692\u5694",14,"\u56A4",10,"\u56B0",6,"\u56B8\u56B9\u56BA\u56BB\u56BD",12,"\u56CB",8,"\u56D5\u56D6\u56D8\u56D9\u56DC\u56E3\u56E5",5,"\u56EC\u56EE\u56EF\u56F2\u56F3\u56F6\u56F7\u56F8\u56FB\u56FC\u5700\u5701\u5702\u5705\u5707\u570B",6],["8840","\u5712",9,"\u571D\u571E\u5720\u5721\u5722\u5724\u5725\u5726\u5727\u572B\u5731\u5732\u5734",4,"\u573C\u573D\u573F\u5741\u5743\u5744\u5745\u5746\u5748\u5749\u574B\u5752",4,"\u5758\u5759\u5762\u5763\u5765\u5767\u576C\u576E\u5770\u5771\u5772\u5774\u5775\u5778\u5779\u577A\u577D\u577E\u577F\u5780"],["8880","\u5781\u5787\u5788\u5789\u578A\u578D",4,"\u5794",6,"\u579C\u579D\u579E\u579F\u57A5\u57A8\u57AA\u57AC\u57AF\u57B0\u57B1\u57B3\u57B5\u57B6\u57B7\u57B9",8,"\u57C4",6,"\u57CC\u57CD\u57D0\u57D1\u57D3\u57D6\u57D7\u57DB\u57DC\u57DE\u57E1\u57E2\u57E3\u57E5",7,"\u57EE\u57F0\u57F1\u57F2\u57F3\u57F5\u57F6\u57F7\u57FB\u57FC\u57FE\u57FF\u5801\u5803\u5804\u5805\u5808\u5809\u580A\u580C\u580E\u580F\u5810\u5812\u5813\u5814\u5816\u5817\u5818\u581A\u581B\u581C\u581D\u581F\u5822\u5823\u5825",4,"\u582B",4,"\u5831\u5832\u5833\u5834\u5836",7],["8940","\u583E",5,"\u5845",6,"\u584E\u584F\u5850\u5852\u5853\u5855\u5856\u5857\u5859",4,"\u585F",5,"\u5866",4,"\u586D",16,"\u587F\u5882\u5884\u5886\u5887\u5888\u588A\u588B\u588C"],["8980","\u588D",4,"\u5894",4,"\u589B\u589C\u589D\u58A0",7,"\u58AA",17,"\u58BD\u58BE\u58BF\u58C0\u58C2\u58C3\u58C4\u58C6",10,"\u58D2\u58D3\u58D4\u58D6",13,"\u58E5",5,"\u58ED\u58EF\u58F1\u58F2\u58F4\u58F5\u58F7\u58F8\u58FA",7,"\u5903\u5905\u5906\u5908",4,"\u590E\u5910\u5911\u5912\u5913\u5917\u5918\u591B\u591D\u591E\u5920\u5921\u5922\u5923\u5926\u5928\u592C\u5930\u5932\u5933\u5935\u5936\u593B"],["8a40","\u593D\u593E\u593F\u5940\u5943\u5945\u5946\u594A\u594C\u594D\u5950\u5952\u5953\u5959\u595B",4,"\u5961\u5963\u5964\u5966",12,"\u5975\u5977\u597A\u597B\u597C\u597E\u597F\u5980\u5985\u5989\u598B\u598C\u598E\u598F\u5990\u5991\u5994\u5995\u5998\u599A\u599B\u599C\u599D\u599F\u59A0\u59A1\u59A2\u59A6"],["8a80","\u59A7\u59AC\u59AD\u59B0\u59B1\u59B3",5,"\u59BA\u59BC\u59BD\u59BF",6,"\u59C7\u59C8\u59C9\u59CC\u59CD\u59CE\u59CF\u59D5\u59D6\u59D9\u59DB\u59DE",4,"\u59E4\u59E6\u59E7\u59E9\u59EA\u59EB\u59ED",11,"\u59FA\u59FC\u59FD\u59FE\u5A00\u5A02\u5A0A\u5A0B\u5A0D\u5A0E\u5A0F\u5A10\u5A12\u5A14\u5A15\u5A16\u5A17\u5A19\u5A1A\u5A1B\u5A1D\u5A1E\u5A21\u5A22\u5A24\u5A26\u5A27\u5A28\u5A2A",6,"\u5A33\u5A35\u5A37",4,"\u5A3D\u5A3E\u5A3F\u5A41",4,"\u5A47\u5A48\u5A4B",9,"\u5A56\u5A57\u5A58\u5A59\u5A5B",5],["8b40","\u5A61\u5A63\u5A64\u5A65\u5A66\u5A68\u5A69\u5A6B",8,"\u5A78\u5A79\u5A7B\u5A7C\u5A7D\u5A7E\u5A80",17,"\u5A93",6,"\u5A9C",13,"\u5AAB\u5AAC"],["8b80","\u5AAD",4,"\u5AB4\u5AB6\u5AB7\u5AB9",4,"\u5ABF\u5AC0\u5AC3",5,"\u5ACA\u5ACB\u5ACD",4,"\u5AD3\u5AD5\u5AD7\u5AD9\u5ADA\u5ADB\u5ADD\u5ADE\u5ADF\u5AE2\u5AE4\u5AE5\u5AE7\u5AE8\u5AEA\u5AEC",4,"\u5AF2",22,"\u5B0A",11,"\u5B18",25,"\u5B33\u5B35\u5B36\u5B38",7,"\u5B41",6],["8c40","\u5B48",7,"\u5B52\u5B56\u5B5E\u5B60\u5B61\u5B67\u5B68\u5B6B\u5B6D\u5B6E\u5B6F\u5B72\u5B74\u5B76\u5B77\u5B78\u5B79\u5B7B\u5B7C\u5B7E\u5B7F\u5B82\u5B86\u5B8A\u5B8D\u5B8E\u5B90\u5B91\u5B92\u5B94\u5B96\u5B9F\u5BA7\u5BA8\u5BA9\u5BAC\u5BAD\u5BAE\u5BAF\u5BB1\u5BB2\u5BB7\u5BBA\u5BBB\u5BBC\u5BC0\u5BC1\u5BC3\u5BC8\u5BC9\u5BCA\u5BCB\u5BCD\u5BCE\u5BCF"],["8c80","\u5BD1\u5BD4",8,"\u5BE0\u5BE2\u5BE3\u5BE6\u5BE7\u5BE9",4,"\u5BEF\u5BF1",6,"\u5BFD\u5BFE\u5C00\u5C02\u5C03\u5C05\u5C07\u5C08\u5C0B\u5C0C\u5C0D\u5C0E\u5C10\u5C12\u5C13\u5C17\u5C19\u5C1B\u5C1E\u5C1F\u5C20\u5C21\u5C23\u5C26\u5C28\u5C29\u5C2A\u5C2B\u5C2D\u5C2E\u5C2F\u5C30\u5C32\u5C33\u5C35\u5C36\u5C37\u5C43\u5C44\u5C46\u5C47\u5C4C\u5C4D\u5C52\u5C53\u5C54\u5C56\u5C57\u5C58\u5C5A\u5C5B\u5C5C\u5C5D\u5C5F\u5C62\u5C64\u5C67",6,"\u5C70\u5C72",6,"\u5C7B\u5C7C\u5C7D\u5C7E\u5C80\u5C83",4,"\u5C89\u5C8A\u5C8B\u5C8E\u5C8F\u5C92\u5C93\u5C95\u5C9D",4,"\u5CA4",4],["8d40","\u5CAA\u5CAE\u5CAF\u5CB0\u5CB2\u5CB4\u5CB6\u5CB9\u5CBA\u5CBB\u5CBC\u5CBE\u5CC0\u5CC2\u5CC3\u5CC5",5,"\u5CCC",5,"\u5CD3",5,"\u5CDA",6,"\u5CE2\u5CE3\u5CE7\u5CE9\u5CEB\u5CEC\u5CEE\u5CEF\u5CF1",9,"\u5CFC",4],["8d80","\u5D01\u5D04\u5D05\u5D08",5,"\u5D0F",4,"\u5D15\u5D17\u5D18\u5D19\u5D1A\u5D1C\u5D1D\u5D1F",4,"\u5D25\u5D28\u5D2A\u5D2B\u5D2C\u5D2F",4,"\u5D35",7,"\u5D3F",7,"\u5D48\u5D49\u5D4D",10,"\u5D59\u5D5A\u5D5C\u5D5E",10,"\u5D6A\u5D6D\u5D6E\u5D70\u5D71\u5D72\u5D73\u5D75",12,"\u5D83",21,"\u5D9A\u5D9B\u5D9C\u5D9E\u5D9F\u5DA0"],["8e40","\u5DA1",21,"\u5DB8",12,"\u5DC6",6,"\u5DCE",12,"\u5DDC\u5DDF\u5DE0\u5DE3\u5DE4\u5DEA\u5DEC\u5DED"],["8e80","\u5DF0\u5DF5\u5DF6\u5DF8",4,"\u5DFF\u5E00\u5E04\u5E07\u5E09\u5E0A\u5E0B\u5E0D\u5E0E\u5E12\u5E13\u5E17\u5E1E",7,"\u5E28",4,"\u5E2F\u5E30\u5E32",4,"\u5E39\u5E3A\u5E3E\u5E3F\u5E40\u5E41\u5E43\u5E46",5,"\u5E4D",6,"\u5E56",4,"\u5E5C\u5E5D\u5E5F\u5E60\u5E63",14,"\u5E75\u5E77\u5E79\u5E7E\u5E81\u5E82\u5E83\u5E85\u5E88\u5E89\u5E8C\u5E8D\u5E8E\u5E92\u5E98\u5E9B\u5E9D\u5EA1\u5EA2\u5EA3\u5EA4\u5EA8",4,"\u5EAE",4,"\u5EB4\u5EBA\u5EBB\u5EBC\u5EBD\u5EBF",6],["8f40","\u5EC6\u5EC7\u5EC8\u5ECB",5,"\u5ED4\u5ED5\u5ED7\u5ED8\u5ED9\u5EDA\u5EDC",11,"\u5EE9\u5EEB",8,"\u5EF5\u5EF8\u5EF9\u5EFB\u5EFC\u5EFD\u5F05\u5F06\u5F07\u5F09\u5F0C\u5F0D\u5F0E\u5F10\u5F12\u5F14\u5F16\u5F19\u5F1A\u5F1C\u5F1D\u5F1E\u5F21\u5F22\u5F23\u5F24"],["8f80","\u5F28\u5F2B\u5F2C\u5F2E\u5F30\u5F32",6,"\u5F3B\u5F3D\u5F3E\u5F3F\u5F41",14,"\u5F51\u5F54\u5F59\u5F5A\u5F5B\u5F5C\u5F5E\u5F5F\u5F60\u5F63\u5F65\u5F67\u5F68\u5F6B\u5F6E\u5F6F\u5F72\u5F74\u5F75\u5F76\u5F78\u5F7A\u5F7D\u5F7E\u5F7F\u5F83\u5F86\u5F8D\u5F8E\u5F8F\u5F91\u5F93\u5F94\u5F96\u5F9A\u5F9B\u5F9D\u5F9E\u5F9F\u5FA0\u5FA2",5,"\u5FA9\u5FAB\u5FAC\u5FAF",5,"\u5FB6\u5FB8\u5FB9\u5FBA\u5FBB\u5FBE",4,"\u5FC7\u5FC8\u5FCA\u5FCB\u5FCE\u5FD3\u5FD4\u5FD5\u5FDA\u5FDB\u5FDC\u5FDE\u5FDF\u5FE2\u5FE3\u5FE5\u5FE6\u5FE8\u5FE9\u5FEC\u5FEF\u5FF0\u5FF2\u5FF3\u5FF4\u5FF6\u5FF7\u5FF9\u5FFA\u5FFC\u6007"],["9040","\u6008\u6009\u600B\u600C\u6010\u6011\u6013\u6017\u6018\u601A\u601E\u601F\u6022\u6023\u6024\u602C\u602D\u602E\u6030",4,"\u6036",4,"\u603D\u603E\u6040\u6044",6,"\u604C\u604E\u604F\u6051\u6053\u6054\u6056\u6057\u6058\u605B\u605C\u605E\u605F\u6060\u6061\u6065\u6066\u606E\u6071\u6072\u6074\u6075\u6077\u607E\u6080"],["9080","\u6081\u6082\u6085\u6086\u6087\u6088\u608A\u608B\u608E\u608F\u6090\u6091\u6093\u6095\u6097\u6098\u6099\u609C\u609E\u60A1\u60A2\u60A4\u60A5\u60A7\u60A9\u60AA\u60AE\u60B0\u60B3\u60B5\u60B6\u60B7\u60B9\u60BA\u60BD",7,"\u60C7\u60C8\u60C9\u60CC",4,"\u60D2\u60D3\u60D4\u60D6\u60D7\u60D9\u60DB\u60DE\u60E1",4,"\u60EA\u60F1\u60F2\u60F5\u60F7\u60F8\u60FB",4,"\u6102\u6103\u6104\u6105\u6107\u610A\u610B\u610C\u6110",4,"\u6116\u6117\u6118\u6119\u611B\u611C\u611D\u611E\u6121\u6122\u6125\u6128\u6129\u612A\u612C",18,"\u6140",6],["9140","\u6147\u6149\u614B\u614D\u614F\u6150\u6152\u6153\u6154\u6156",6,"\u615E\u615F\u6160\u6161\u6163\u6164\u6165\u6166\u6169",6,"\u6171\u6172\u6173\u6174\u6176\u6178",18,"\u618C\u618D\u618F",4,"\u6195"],["9180","\u6196",6,"\u619E",8,"\u61AA\u61AB\u61AD",9,"\u61B8",5,"\u61BF\u61C0\u61C1\u61C3",4,"\u61C9\u61CC",4,"\u61D3\u61D5",16,"\u61E7",13,"\u61F6",8,"\u6200",5,"\u6207\u6209\u6213\u6214\u6219\u621C\u621D\u621E\u6220\u6223\u6226\u6227\u6228\u6229\u622B\u622D\u622F\u6230\u6231\u6232\u6235\u6236\u6238",4,"\u6242\u6244\u6245\u6246\u624A"],["9240","\u624F\u6250\u6255\u6256\u6257\u6259\u625A\u625C",6,"\u6264\u6265\u6268\u6271\u6272\u6274\u6275\u6277\u6278\u627A\u627B\u627D\u6281\u6282\u6283\u6285\u6286\u6287\u6288\u628B",5,"\u6294\u6299\u629C\u629D\u629E\u62A3\u62A6\u62A7\u62A9\u62AA\u62AD\u62AE\u62AF\u62B0\u62B2\u62B3\u62B4\u62B6\u62B7\u62B8\u62BA\u62BE\u62C0\u62C1"],["9280","\u62C3\u62CB\u62CF\u62D1\u62D5\u62DD\u62DE\u62E0\u62E1\u62E4\u62EA\u62EB\u62F0\u62F2\u62F5\u62F8\u62F9\u62FA\u62FB\u6300\u6303\u6304\u6305\u6306\u630A\u630B\u630C\u630D\u630F\u6310\u6312\u6313\u6314\u6315\u6317\u6318\u6319\u631C\u6326\u6327\u6329\u632C\u632D\u632E\u6330\u6331\u6333",5,"\u633B\u633C\u633E\u633F\u6340\u6341\u6344\u6347\u6348\u634A\u6351\u6352\u6353\u6354\u6356",7,"\u6360\u6364\u6365\u6366\u6368\u636A\u636B\u636C\u636F\u6370\u6372\u6373\u6374\u6375\u6378\u6379\u637C\u637D\u637E\u637F\u6381\u6383\u6384\u6385\u6386\u638B\u638D\u6391\u6393\u6394\u6395\u6397\u6399",6,"\u63A1\u63A4\u63A6\u63AB\u63AF\u63B1\u63B2\u63B5\u63B6\u63B9\u63BB\u63BD\u63BF\u63C0"],["9340","\u63C1\u63C2\u63C3\u63C5\u63C7\u63C8\u63CA\u63CB\u63CC\u63D1\u63D3\u63D4\u63D5\u63D7",6,"\u63DF\u63E2\u63E4",4,"\u63EB\u63EC\u63EE\u63EF\u63F0\u63F1\u63F3\u63F5\u63F7\u63F9\u63FA\u63FB\u63FC\u63FE\u6403\u6404\u6406",4,"\u640D\u640E\u6411\u6412\u6415",5,"\u641D\u641F\u6422\u6423\u6424"],["9380","\u6425\u6427\u6428\u6429\u642B\u642E",5,"\u6435",4,"\u643B\u643C\u643E\u6440\u6442\u6443\u6449\u644B",6,"\u6453\u6455\u6456\u6457\u6459",4,"\u645F",7,"\u6468\u646A\u646B\u646C\u646E",9,"\u647B",6,"\u6483\u6486\u6488",8,"\u6493\u6494\u6497\u6498\u649A\u649B\u649C\u649D\u649F",4,"\u64A5\u64A6\u64A7\u64A8\u64AA\u64AB\u64AF\u64B1\u64B2\u64B3\u64B4\u64B6\u64B9\u64BB\u64BD\u64BE\u64BF\u64C1\u64C3\u64C4\u64C6",6,"\u64CF\u64D1\u64D3\u64D4\u64D5\u64D6\u64D9\u64DA"],["9440","\u64DB\u64DC\u64DD\u64DF\u64E0\u64E1\u64E3\u64E5\u64E7",24,"\u6501",7,"\u650A",7,"\u6513",4,"\u6519",8],["9480","\u6522\u6523\u6524\u6526",4,"\u652C\u652D\u6530\u6531\u6532\u6533\u6537\u653A\u653C\u653D\u6540",4,"\u6546\u6547\u654A\u654B\u654D\u654E\u6550\u6552\u6553\u6554\u6557\u6558\u655A\u655C\u655F\u6560\u6561\u6564\u6565\u6567\u6568\u6569\u656A\u656D\u656E\u656F\u6571\u6573\u6575\u6576\u6578",14,"\u6588\u6589\u658A\u658D\u658E\u658F\u6592\u6594\u6595\u6596\u6598\u659A\u659D\u659E\u65A0\u65A2\u65A3\u65A6\u65A8\u65AA\u65AC\u65AE\u65B1",7,"\u65BA\u65BB\u65BE\u65BF\u65C0\u65C2\u65C7\u65C8\u65C9\u65CA\u65CD\u65D0\u65D1\u65D3\u65D4\u65D5\u65D8",7,"\u65E1\u65E3\u65E4\u65EA\u65EB"],["9540","\u65F2\u65F3\u65F4\u65F5\u65F8\u65F9\u65FB",4,"\u6601\u6604\u6605\u6607\u6608\u6609\u660B\u660D\u6610\u6611\u6612\u6616\u6617\u6618\u661A\u661B\u661C\u661E\u6621\u6622\u6623\u6624\u6626\u6629\u662A\u662B\u662C\u662E\u6630\u6632\u6633\u6637",4,"\u663D\u663F\u6640\u6642\u6644",6,"\u664D\u664E\u6650\u6651\u6658"],["9580","\u6659\u665B\u665C\u665D\u665E\u6660\u6662\u6663\u6665\u6667\u6669",4,"\u6671\u6672\u6673\u6675\u6678\u6679\u667B\u667C\u667D\u667F\u6680\u6681\u6683\u6685\u6686\u6688\u6689\u668A\u668B\u668D\u668E\u668F\u6690\u6692\u6693\u6694\u6695\u6698",4,"\u669E",8,"\u66A9",4,"\u66AF",4,"\u66B5\u66B6\u66B7\u66B8\u66BA\u66BB\u66BC\u66BD\u66BF",25,"\u66DA\u66DE",7,"\u66E7\u66E8\u66EA",5,"\u66F1\u66F5\u66F6\u66F8\u66FA\u66FB\u66FD\u6701\u6702\u6703"],["9640","\u6704\u6705\u6706\u6707\u670C\u670E\u670F\u6711\u6712\u6713\u6716\u6718\u6719\u671A\u671C\u671E\u6720",5,"\u6727\u6729\u672E\u6730\u6732\u6733\u6736\u6737\u6738\u6739\u673B\u673C\u673E\u673F\u6741\u6744\u6745\u6747\u674A\u674B\u674D\u6752\u6754\u6755\u6757",4,"\u675D\u6762\u6763\u6764\u6766\u6767\u676B\u676C\u676E\u6771\u6774\u6776"],["9680","\u6778\u6779\u677A\u677B\u677D\u6780\u6782\u6783\u6785\u6786\u6788\u678A\u678C\u678D\u678E\u678F\u6791\u6792\u6793\u6794\u6796\u6799\u679B\u679F\u67A0\u67A1\u67A4\u67A6\u67A9\u67AC\u67AE\u67B1\u67B2\u67B4\u67B9",7,"\u67C2\u67C5",9,"\u67D5\u67D6\u67D7\u67DB\u67DF\u67E1\u67E3\u67E4\u67E6\u67E7\u67E8\u67EA\u67EB\u67ED\u67EE\u67F2\u67F5",7,"\u67FE\u6801\u6802\u6803\u6804\u6806\u680D\u6810\u6812\u6814\u6815\u6818",4,"\u681E\u681F\u6820\u6822",6,"\u682B",6,"\u6834\u6835\u6836\u683A\u683B\u683F\u6847\u684B\u684D\u684F\u6852\u6856",5],["9740","\u685C\u685D\u685E\u685F\u686A\u686C",7,"\u6875\u6878",8,"\u6882\u6884\u6887",7,"\u6890\u6891\u6892\u6894\u6895\u6896\u6898",9,"\u68A3\u68A4\u68A5\u68A9\u68AA\u68AB\u68AC\u68AE\u68B1\u68B2\u68B4\u68B6\u68B7\u68B8"],["9780","\u68B9",6,"\u68C1\u68C3",5,"\u68CA\u68CC\u68CE\u68CF\u68D0\u68D1\u68D3\u68D4\u68D6\u68D7\u68D9\u68DB",4,"\u68E1\u68E2\u68E4",9,"\u68EF\u68F2\u68F3\u68F4\u68F6\u68F7\u68F8\u68FB\u68FD\u68FE\u68FF\u6900\u6902\u6903\u6904\u6906",4,"\u690C\u690F\u6911\u6913",11,"\u6921\u6922\u6923\u6925",7,"\u692E\u692F\u6931\u6932\u6933\u6935\u6936\u6937\u6938\u693A\u693B\u693C\u693E\u6940\u6941\u6943",16,"\u6955\u6956\u6958\u6959\u695B\u695C\u695F"],["9840","\u6961\u6962\u6964\u6965\u6967\u6968\u6969\u696A\u696C\u696D\u696F\u6970\u6972",4,"\u697A\u697B\u697D\u697E\u697F\u6981\u6983\u6985\u698A\u698B\u698C\u698E",5,"\u6996\u6997\u6999\u699A\u699D",9,"\u69A9\u69AA\u69AC\u69AE\u69AF\u69B0\u69B2\u69B3\u69B5\u69B6\u69B8\u69B9\u69BA\u69BC\u69BD"],["9880","\u69BE\u69BF\u69C0\u69C2",7,"\u69CB\u69CD\u69CF\u69D1\u69D2\u69D3\u69D5",5,"\u69DC\u69DD\u69DE\u69E1",11,"\u69EE\u69EF\u69F0\u69F1\u69F3",9,"\u69FE\u6A00",9,"\u6A0B",11,"\u6A19",5,"\u6A20\u6A22",5,"\u6A29\u6A2B\u6A2C\u6A2D\u6A2E\u6A30\u6A32\u6A33\u6A34\u6A36",6,"\u6A3F",4,"\u6A45\u6A46\u6A48",7,"\u6A51",6,"\u6A5A"],["9940","\u6A5C",4,"\u6A62\u6A63\u6A64\u6A66",10,"\u6A72",6,"\u6A7A\u6A7B\u6A7D\u6A7E\u6A7F\u6A81\u6A82\u6A83\u6A85",8,"\u6A8F\u6A92",4,"\u6A98",7,"\u6AA1",5],["9980","\u6AA7\u6AA8\u6AAA\u6AAD",114,"\u6B25\u6B26\u6B28",6],["9a40","\u6B2F\u6B30\u6B31\u6B33\u6B34\u6B35\u6B36\u6B38\u6B3B\u6B3C\u6B3D\u6B3F\u6B40\u6B41\u6B42\u6B44\u6B45\u6B48\u6B4A\u6B4B\u6B4D",11,"\u6B5A",7,"\u6B68\u6B69\u6B6B",13,"\u6B7A\u6B7D\u6B7E\u6B7F\u6B80\u6B85\u6B88"],["9a80","\u6B8C\u6B8E\u6B8F\u6B90\u6B91\u6B94\u6B95\u6B97\u6B98\u6B99\u6B9C",4,"\u6BA2",7,"\u6BAB",7,"\u6BB6\u6BB8",6,"\u6BC0\u6BC3\u6BC4\u6BC6",4,"\u6BCC\u6BCE\u6BD0\u6BD1\u6BD8\u6BDA\u6BDC",4,"\u6BE2",7,"\u6BEC\u6BED\u6BEE\u6BF0\u6BF1\u6BF2\u6BF4\u6BF6\u6BF7\u6BF8\u6BFA\u6BFB\u6BFC\u6BFE",6,"\u6C08",4,"\u6C0E\u6C12\u6C17\u6C1C\u6C1D\u6C1E\u6C20\u6C23\u6C25\u6C2B\u6C2C\u6C2D\u6C31\u6C33\u6C36\u6C37\u6C39\u6C3A\u6C3B\u6C3C\u6C3E\u6C3F\u6C43\u6C44\u6C45\u6C48\u6C4B",4,"\u6C51\u6C52\u6C53\u6C56\u6C58"],["9b40","\u6C59\u6C5A\u6C62\u6C63\u6C65\u6C66\u6C67\u6C6B",4,"\u6C71\u6C73\u6C75\u6C77\u6C78\u6C7A\u6C7B\u6C7C\u6C7F\u6C80\u6C84\u6C87\u6C8A\u6C8B\u6C8D\u6C8E\u6C91\u6C92\u6C95\u6C96\u6C97\u6C98\u6C9A\u6C9C\u6C9D\u6C9E\u6CA0\u6CA2\u6CA8\u6CAC\u6CAF\u6CB0\u6CB4\u6CB5\u6CB6\u6CB7\u6CBA\u6CC0\u6CC1\u6CC2\u6CC3\u6CC6\u6CC7\u6CC8\u6CCB\u6CCD\u6CCE\u6CCF\u6CD1\u6CD2\u6CD8"],["9b80","\u6CD9\u6CDA\u6CDC\u6CDD\u6CDF\u6CE4\u6CE6\u6CE7\u6CE9\u6CEC\u6CED\u6CF2\u6CF4\u6CF9\u6CFF\u6D00\u6D02\u6D03\u6D05\u6D06\u6D08\u6D09\u6D0A\u6D0D\u6D0F\u6D10\u6D11\u6D13\u6D14\u6D15\u6D16\u6D18\u6D1C\u6D1D\u6D1F",5,"\u6D26\u6D28\u6D29\u6D2C\u6D2D\u6D2F\u6D30\u6D34\u6D36\u6D37\u6D38\u6D3A\u6D3F\u6D40\u6D42\u6D44\u6D49\u6D4C\u6D50\u6D55\u6D56\u6D57\u6D58\u6D5B\u6D5D\u6D5F\u6D61\u6D62\u6D64\u6D65\u6D67\u6D68\u6D6B\u6D6C\u6D6D\u6D70\u6D71\u6D72\u6D73\u6D75\u6D76\u6D79\u6D7A\u6D7B\u6D7D",4,"\u6D83\u6D84\u6D86\u6D87\u6D8A\u6D8B\u6D8D\u6D8F\u6D90\u6D92\u6D96",4,"\u6D9C\u6DA2\u6DA5\u6DAC\u6DAD\u6DB0\u6DB1\u6DB3\u6DB4\u6DB6\u6DB7\u6DB9",5,"\u6DC1\u6DC2\u6DC3\u6DC8\u6DC9\u6DCA"],["9c40","\u6DCD\u6DCE\u6DCF\u6DD0\u6DD2\u6DD3\u6DD4\u6DD5\u6DD7\u6DDA\u6DDB\u6DDC\u6DDF\u6DE2\u6DE3\u6DE5\u6DE7\u6DE8\u6DE9\u6DEA\u6DED\u6DEF\u6DF0\u6DF2\u6DF4\u6DF5\u6DF6\u6DF8\u6DFA\u6DFD",7,"\u6E06\u6E07\u6E08\u6E09\u6E0B\u6E0F\u6E12\u6E13\u6E15\u6E18\u6E19\u6E1B\u6E1C\u6E1E\u6E1F\u6E22\u6E26\u6E27\u6E28\u6E2A\u6E2C\u6E2E\u6E30\u6E31\u6E33\u6E35"],["9c80","\u6E36\u6E37\u6E39\u6E3B",7,"\u6E45",7,"\u6E4F\u6E50\u6E51\u6E52\u6E55\u6E57\u6E59\u6E5A\u6E5C\u6E5D\u6E5E\u6E60",10,"\u6E6C\u6E6D\u6E6F",14,"\u6E80\u6E81\u6E82\u6E84\u6E87\u6E88\u6E8A",4,"\u6E91",6,"\u6E99\u6E9A\u6E9B\u6E9D\u6E9E\u6EA0\u6EA1\u6EA3\u6EA4\u6EA6\u6EA8\u6EA9\u6EAB\u6EAC\u6EAD\u6EAE\u6EB0\u6EB3\u6EB5\u6EB8\u6EB9\u6EBC\u6EBE\u6EBF\u6EC0\u6EC3\u6EC4\u6EC5\u6EC6\u6EC8\u6EC9\u6ECA\u6ECC\u6ECD\u6ECE\u6ED0\u6ED2\u6ED6\u6ED8\u6ED9\u6EDB\u6EDC\u6EDD\u6EE3\u6EE7\u6EEA",5],["9d40","\u6EF0\u6EF1\u6EF2\u6EF3\u6EF5\u6EF6\u6EF7\u6EF8\u6EFA",7,"\u6F03\u6F04\u6F05\u6F07\u6F08\u6F0A",4,"\u6F10\u6F11\u6F12\u6F16",9,"\u6F21\u6F22\u6F23\u6F25\u6F26\u6F27\u6F28\u6F2C\u6F2E\u6F30\u6F32\u6F34\u6F35\u6F37",6,"\u6F3F\u6F40\u6F41\u6F42"],["9d80","\u6F43\u6F44\u6F45\u6F48\u6F49\u6F4A\u6F4C\u6F4E",9,"\u6F59\u6F5A\u6F5B\u6F5D\u6F5F\u6F60\u6F61\u6F63\u6F64\u6F65\u6F67",5,"\u6F6F\u6F70\u6F71\u6F73\u6F75\u6F76\u6F77\u6F79\u6F7B\u6F7D",6,"\u6F85\u6F86\u6F87\u6F8A\u6F8B\u6F8F",12,"\u6F9D\u6F9E\u6F9F\u6FA0\u6FA2",4,"\u6FA8",10,"\u6FB4\u6FB5\u6FB7\u6FB8\u6FBA",5,"\u6FC1\u6FC3",5,"\u6FCA",6,"\u6FD3",10,"\u6FDF\u6FE2\u6FE3\u6FE4\u6FE5"],["9e40","\u6FE6",7,"\u6FF0",32,"\u7012",7,"\u701C",6,"\u7024",6],["9e80","\u702B",9,"\u7036\u7037\u7038\u703A",17,"\u704D\u704E\u7050",13,"\u705F",11,"\u706E\u7071\u7072\u7073\u7074\u7077\u7079\u707A\u707B\u707D\u7081\u7082\u7083\u7084\u7086\u7087\u7088\u708B\u708C\u708D\u708F\u7090\u7091\u7093\u7097\u7098\u709A\u709B\u709E",12,"\u70B0\u70B2\u70B4\u70B5\u70B6\u70BA\u70BE\u70BF\u70C4\u70C5\u70C6\u70C7\u70C9\u70CB",12,"\u70DA"],["9f40","\u70DC\u70DD\u70DE\u70E0\u70E1\u70E2\u70E3\u70E5\u70EA\u70EE\u70F0",6,"\u70F8\u70FA\u70FB\u70FC\u70FE",10,"\u710B",4,"\u7111\u7112\u7114\u7117\u711B",10,"\u7127",7,"\u7132\u7133\u7134"],["9f80","\u7135\u7137",13,"\u7146\u7147\u7148\u7149\u714B\u714D\u714F",12,"\u715D\u715F",4,"\u7165\u7169",4,"\u716F\u7170\u7171\u7174\u7175\u7176\u7177\u7179\u717B\u717C\u717E",5,"\u7185",4,"\u718B\u718C\u718D\u718E\u7190\u7191\u7192\u7193\u7195\u7196\u7197\u719A",4,"\u71A1",6,"\u71A9\u71AA\u71AB\u71AD",5,"\u71B4\u71B6\u71B7\u71B8\u71BA",8,"\u71C4",9,"\u71CF",4],["a040","\u71D6",9,"\u71E1\u71E2\u71E3\u71E4\u71E6\u71E8",5,"\u71EF",9,"\u71FA",11,"\u7207",19],["a080","\u721B\u721C\u721E",9,"\u7229\u722B\u722D\u722E\u722F\u7232\u7233\u7234\u723A\u723C\u723E\u7240",6,"\u7249\u724A\u724B\u724E\u724F\u7250\u7251\u7253\u7254\u7255\u7257\u7258\u725A\u725C\u725E\u7260\u7263\u7264\u7265\u7268\u726A\u726B\u726C\u726D\u7270\u7271\u7273\u7274\u7276\u7277\u7278\u727B\u727C\u727D\u7282\u7283\u7285",4,"\u728C\u728E\u7290\u7291\u7293",11,"\u72A0",11,"\u72AE\u72B1\u72B2\u72B3\u72B5\u72BA",6,"\u72C5\u72C6\u72C7\u72C9\u72CA\u72CB\u72CC\u72CF\u72D1\u72D3\u72D4\u72D5\u72D6\u72D8\u72DA\u72DB"],["a1a1","\u3000\u3001\u3002\xB7\u02C9\u02C7\xA8\u3003\u3005\u2014\uFF5E\u2016\u2026\u2018\u2019\u201C\u201D\u3014\u3015\u3008",7,"\u3016\u3017\u3010\u3011\xB1\xD7\xF7\u2236\u2227\u2228\u2211\u220F\u222A\u2229\u2208\u2237\u221A\u22A5\u2225\u2220\u2312\u2299\u222B\u222E\u2261\u224C\u2248\u223D\u221D\u2260\u226E\u226F\u2264\u2265\u221E\u2235\u2234\u2642\u2640\xB0\u2032\u2033\u2103\uFF04\xA4\uFFE0\uFFE1\u2030\xA7\u2116\u2606\u2605\u25CB\u25CF\u25CE\u25C7\u25C6\u25A1\u25A0\u25B3\u25B2\u203B\u2192\u2190\u2191\u2193\u3013"],["a2a1","\u2170",9],["a2b1","\u2488",19,"\u2474",19,"\u2460",9],["a2e5","\u3220",9],["a2f1","\u2160",11],["a3a1","\uFF01\uFF02\uFF03\uFFE5\uFF05",88,"\uFFE3"],["a4a1","\u3041",82],["a5a1","\u30A1",85],["a6a1","\u0391",16,"\u03A3",6],["a6c1","\u03B1",16,"\u03C3",6],["a6e0","\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44"],["a6ee","\uFE3B\uFE3C\uFE37\uFE38\uFE31"],["a6f4","\uFE33\uFE34"],["a7a1","\u0410",5,"\u0401\u0416",25],["a7d1","\u0430",5,"\u0451\u0436",25],["a840","\u02CA\u02CB\u02D9\u2013\u2015\u2025\u2035\u2105\u2109\u2196\u2197\u2198\u2199\u2215\u221F\u2223\u2252\u2266\u2267\u22BF\u2550",35,"\u2581",6],["a880","\u2588",7,"\u2593\u2594\u2595\u25BC\u25BD\u25E2\u25E3\u25E4\u25E5\u2609\u2295\u3012\u301D\u301E"],["a8a1","\u0101\xE1\u01CE\xE0\u0113\xE9\u011B\xE8\u012B\xED\u01D0\xEC\u014D\xF3\u01D2\xF2\u016B\xFA\u01D4\xF9\u01D6\u01D8\u01DA\u01DC\xFC\xEA\u0251"],["a8bd","\u0144\u0148"],["a8c0","\u0261"],["a8c5","\u3105",36],["a940","\u3021",8,"\u32A3\u338E\u338F\u339C\u339D\u339E\u33A1\u33C4\u33CE\u33D1\u33D2\u33D5\uFE30\uFFE2\uFFE4"],["a959","\u2121\u3231"],["a95c","\u2010"],["a960","\u30FC\u309B\u309C\u30FD\u30FE\u3006\u309D\u309E\uFE49",9,"\uFE54\uFE55\uFE56\uFE57\uFE59",8],["a980","\uFE62",4,"\uFE68\uFE69\uFE6A\uFE6B"],["a996","\u3007"],["a9a4","\u2500",75],["aa40","\u72DC\u72DD\u72DF\u72E2",5,"\u72EA\u72EB\u72F5\u72F6\u72F9\u72FD\u72FE\u72FF\u7300\u7302\u7304",5,"\u730B\u730C\u730D\u730F\u7310\u7311\u7312\u7314\u7318\u7319\u731A\u731F\u7320\u7323\u7324\u7326\u7327\u7328\u732D\u732F\u7330\u7332\u7333\u7335\u7336\u733A\u733B\u733C\u733D\u7340",8],["aa80","\u7349\u734A\u734B\u734C\u734E\u734F\u7351\u7353\u7354\u7355\u7356\u7358",7,"\u7361",10,"\u736E\u7370\u7371"],["ab40","\u7372",11,"\u737F",4,"\u7385\u7386\u7388\u738A\u738C\u738D\u738F\u7390\u7392\u7393\u7394\u7395\u7397\u7398\u7399\u739A\u739C\u739D\u739E\u73A0\u73A1\u73A3",5,"\u73AA\u73AC\u73AD\u73B1\u73B4\u73B5\u73B6\u73B8\u73B9\u73BC\u73BD\u73BE\u73BF\u73C1\u73C3",4],["ab80","\u73CB\u73CC\u73CE\u73D2",6,"\u73DA\u73DB\u73DC\u73DD\u73DF\u73E1\u73E2\u73E3\u73E4\u73E6\u73E8\u73EA\u73EB\u73EC\u73EE\u73EF\u73F0\u73F1\u73F3",4],["ac40","\u73F8",10,"\u7404\u7407\u7408\u740B\u740C\u740D\u740E\u7411",8,"\u741C",5,"\u7423\u7424\u7427\u7429\u742B\u742D\u742F\u7431\u7432\u7437",4,"\u743D\u743E\u743F\u7440\u7442",11],["ac80","\u744E",6,"\u7456\u7458\u745D\u7460",12,"\u746E\u746F\u7471",4,"\u7478\u7479\u747A"],["ad40","\u747B\u747C\u747D\u747F\u7482\u7484\u7485\u7486\u7488\u7489\u748A\u748C\u748D\u748F\u7491",10,"\u749D\u749F",7,"\u74AA",15,"\u74BB",12],["ad80","\u74C8",9,"\u74D3",8,"\u74DD\u74DF\u74E1\u74E5\u74E7",6,"\u74F0\u74F1\u74F2"],["ae40","\u74F3\u74F5\u74F8",6,"\u7500\u7501\u7502\u7503\u7505",7,"\u750E\u7510\u7512\u7514\u7515\u7516\u7517\u751B\u751D\u751E\u7520",4,"\u7526\u7527\u752A\u752E\u7534\u7536\u7539\u753C\u753D\u753F\u7541\u7542\u7543\u7544\u7546\u7547\u7549\u754A\u754D\u7550\u7551\u7552\u7553\u7555\u7556\u7557\u7558"],["ae80","\u755D",7,"\u7567\u7568\u7569\u756B",6,"\u7573\u7575\u7576\u7577\u757A",4,"\u7580\u7581\u7582\u7584\u7585\u7587"],["af40","\u7588\u7589\u758A\u758C\u758D\u758E\u7590\u7593\u7595\u7598\u759B\u759C\u759E\u75A2\u75A6",4,"\u75AD\u75B6\u75B7\u75BA\u75BB\u75BF\u75C0\u75C1\u75C6\u75CB\u75CC\u75CE\u75CF\u75D0\u75D1\u75D3\u75D7\u75D9\u75DA\u75DC\u75DD\u75DF\u75E0\u75E1\u75E5\u75E9\u75EC\u75ED\u75EE\u75EF\u75F2\u75F3\u75F5\u75F6\u75F7\u75F8\u75FA\u75FB\u75FD\u75FE\u7602\u7604\u7606\u7607"],["af80","\u7608\u7609\u760B\u760D\u760E\u760F\u7611\u7612\u7613\u7614\u7616\u761A\u761C\u761D\u761E\u7621\u7623\u7627\u7628\u762C\u762E\u762F\u7631\u7632\u7636\u7637\u7639\u763A\u763B\u763D\u7641\u7642\u7644"],["b040","\u7645",6,"\u764E",5,"\u7655\u7657",4,"\u765D\u765F\u7660\u7661\u7662\u7664",6,"\u766C\u766D\u766E\u7670",7,"\u7679\u767A\u767C\u767F\u7680\u7681\u7683\u7685\u7689\u768A\u768C\u768D\u768F\u7690\u7692\u7694\u7695\u7697\u7698\u769A\u769B"],["b080","\u769C",7,"\u76A5",8,"\u76AF\u76B0\u76B3\u76B5",9,"\u76C0\u76C1\u76C3\u554A\u963F\u57C3\u6328\u54CE\u5509\u54C0\u7691\u764C\u853C\u77EE\u827E\u788D\u7231\u9698\u978D\u6C28\u5B89\u4FFA\u6309\u6697\u5CB8\u80FA\u6848\u80AE\u6602\u76CE\u51F9\u6556\u71AC\u7FF1\u8884\u50B2\u5965\u61CA\u6FB3\u82AD\u634C\u6252\u53ED\u5427\u7B06\u516B\u75A4\u5DF4\u62D4\u8DCB\u9776\u628A\u8019\u575D\u9738\u7F62\u7238\u767D\u67CF\u767E\u6446\u4F70\u8D25\u62DC\u7A17\u6591\u73ED\u642C\u6273\u822C\u9881\u677F\u7248\u626E\u62CC\u4F34\u74E3\u534A\u529E\u7ECA\u90A6\u5E2E\u6886\u699C\u8180\u7ED1\u68D2\u78C5\u868C\u9551\u508D\u8C24\u82DE\u80DE\u5305\u8912\u5265"],["b140","\u76C4\u76C7\u76C9\u76CB\u76CC\u76D3\u76D5\u76D9\u76DA\u76DC\u76DD\u76DE\u76E0",4,"\u76E6",7,"\u76F0\u76F3\u76F5\u76F6\u76F7\u76FA\u76FB\u76FD\u76FF\u7700\u7702\u7703\u7705\u7706\u770A\u770C\u770E",10,"\u771B\u771C\u771D\u771E\u7721\u7723\u7724\u7725\u7727\u772A\u772B"],["b180","\u772C\u772E\u7730",4,"\u7739\u773B\u773D\u773E\u773F\u7742\u7744\u7745\u7746\u7748",7,"\u7752",7,"\u775C\u8584\u96F9\u4FDD\u5821\u9971\u5B9D\u62B1\u62A5\u66B4\u8C79\u9C8D\u7206\u676F\u7891\u60B2\u5351\u5317\u8F88\u80CC\u8D1D\u94A1\u500D\u72C8\u5907\u60EB\u7119\u88AB\u5954\u82EF\u672C\u7B28\u5D29\u7EF7\u752D\u6CF5\u8E66\u8FF8\u903C\u9F3B\u6BD4\u9119\u7B14\u5F7C\u78A7\u84D6\u853D\u6BD5\u6BD9\u6BD6\u5E01\u5E87\u75F9\u95ED\u655D\u5F0A\u5FC5\u8F9F\u58C1\u81C2\u907F\u965B\u97AD\u8FB9\u7F16\u8D2C\u6241\u4FBF\u53D8\u535E\u8FA8\u8FA9\u8FAB\u904D\u6807\u5F6A\u8198\u8868\u9CD6\u618B\u522B\u762A\u5F6C\u658C\u6FD2\u6EE8\u5BBE\u6448\u5175\u51B0\u67C4\u4E19\u79C9\u997C\u70B3"],["b240","\u775D\u775E\u775F\u7760\u7764\u7767\u7769\u776A\u776D",11,"\u777A\u777B\u777C\u7781\u7782\u7783\u7786",5,"\u778F\u7790\u7793",11,"\u77A1\u77A3\u77A4\u77A6\u77A8\u77AB\u77AD\u77AE\u77AF\u77B1\u77B2\u77B4\u77B6",4],["b280","\u77BC\u77BE\u77C0",12,"\u77CE",8,"\u77D8\u77D9\u77DA\u77DD",4,"\u77E4\u75C5\u5E76\u73BB\u83E0\u64AD\u62E8\u94B5\u6CE2\u535A\u52C3\u640F\u94C2\u7B94\u4F2F\u5E1B\u8236\u8116\u818A\u6E24\u6CCA\u9A73\u6355\u535C\u54FA\u8865\u57E0\u4E0D\u5E03\u6B65\u7C3F\u90E8\u6016\u64E6\u731C\u88C1\u6750\u624D\u8D22\u776C\u8E29\u91C7\u5F69\u83DC\u8521\u9910\u53C2\u8695\u6B8B\u60ED\u60E8\u707F\u82CD\u8231\u4ED3\u6CA7\u85CF\u64CD\u7CD9\u69FD\u66F9\u8349\u5395\u7B56\u4FA7\u518C\u6D4B\u5C42\u8E6D\u63D2\u53C9\u832C\u8336\u67E5\u78B4\u643D\u5BDF\u5C94\u5DEE\u8BE7\u62C6\u67F4\u8C7A\u6400\u63BA\u8749\u998B\u8C17\u7F20\u94F2\u4EA7\u9610\u98A4\u660C\u7316"],["b340","\u77E6\u77E8\u77EA\u77EF\u77F0\u77F1\u77F2\u77F4\u77F5\u77F7\u77F9\u77FA\u77FB\u77FC\u7803",5,"\u780A\u780B\u780E\u780F\u7810\u7813\u7815\u7819\u781B\u781E\u7820\u7821\u7822\u7824\u7828\u782A\u782B\u782E\u782F\u7831\u7832\u7833\u7835\u7836\u783D\u783F\u7841\u7842\u7843\u7844\u7846\u7848\u7849\u784A\u784B\u784D\u784F\u7851\u7853\u7854\u7858\u7859\u785A"],["b380","\u785B\u785C\u785E",11,"\u786F",7,"\u7878\u7879\u787A\u787B\u787D",6,"\u573A\u5C1D\u5E38\u957F\u507F\u80A0\u5382\u655E\u7545\u5531\u5021\u8D85\u6284\u949E\u671D\u5632\u6F6E\u5DE2\u5435\u7092\u8F66\u626F\u64A4\u63A3\u5F7B\u6F88\u90F4\u81E3\u8FB0\u5C18\u6668\u5FF1\u6C89\u9648\u8D81\u886C\u6491\u79F0\u57CE\u6A59\u6210\u5448\u4E58\u7A0B\u60E9\u6F84\u8BDA\u627F\u901E\u9A8B\u79E4\u5403\u75F4\u6301\u5319\u6C60\u8FDF\u5F1B\u9A70\u803B\u9F7F\u4F88\u5C3A\u8D64\u7FC5\u65A5\u70BD\u5145\u51B2\u866B\u5D07\u5BA0\u62BD\u916C\u7574\u8E0C\u7A20\u6101\u7B79\u4EC7\u7EF8\u7785\u4E11\u81ED\u521D\u51FA\u6A71\u53A8\u8E87\u9504\u96CF\u6EC1\u9664\u695A"],["b440","\u7884\u7885\u7886\u7888\u788A\u788B\u788F\u7890\u7892\u7894\u7895\u7896\u7899\u789D\u789E\u78A0\u78A2\u78A4\u78A6\u78A8",7,"\u78B5\u78B6\u78B7\u78B8\u78BA\u78BB\u78BC\u78BD\u78BF\u78C0\u78C2\u78C3\u78C4\u78C6\u78C7\u78C8\u78CC\u78CD\u78CE\u78CF\u78D1\u78D2\u78D3\u78D6\u78D7\u78D8\u78DA",9],["b480","\u78E4\u78E5\u78E6\u78E7\u78E9\u78EA\u78EB\u78ED",4,"\u78F3\u78F5\u78F6\u78F8\u78F9\u78FB",5,"\u7902\u7903\u7904\u7906",6,"\u7840\u50A8\u77D7\u6410\u89E6\u5904\u63E3\u5DDD\u7A7F\u693D\u4F20\u8239\u5598\u4E32\u75AE\u7A97\u5E62\u5E8A\u95EF\u521B\u5439\u708A\u6376\u9524\u5782\u6625\u693F\u9187\u5507\u6DF3\u7EAF\u8822\u6233\u7EF0\u75B5\u8328\u78C1\u96CC\u8F9E\u6148\u74F7\u8BCD\u6B64\u523A\u8D50\u6B21\u806A\u8471\u56F1\u5306\u4ECE\u4E1B\u51D1\u7C97\u918B\u7C07\u4FC3\u8E7F\u7BE1\u7A9C\u6467\u5D14\u50AC\u8106\u7601\u7CB9\u6DEC\u7FE0\u6751\u5B58\u5BF8\u78CB\u64AE\u6413\u63AA\u632B\u9519\u642D\u8FBE\u7B54\u7629\u6253\u5927\u5446\u6B79\u50A3\u6234\u5E26\u6B86\u4EE3\u8D37\u888B\u5F85\u902E"],["b540","\u790D",5,"\u7914",9,"\u791F",4,"\u7925",14,"\u7935",4,"\u793D\u793F\u7942\u7943\u7944\u7945\u7947\u794A",8,"\u7954\u7955\u7958\u7959\u7961\u7963"],["b580","\u7964\u7966\u7969\u796A\u796B\u796C\u796E\u7970",6,"\u7979\u797B",4,"\u7982\u7983\u7986\u7987\u7988\u7989\u798B\u798C\u798D\u798E\u7990\u7991\u7992\u6020\u803D\u62C5\u4E39\u5355\u90F8\u63B8\u80C6\u65E6\u6C2E\u4F46\u60EE\u6DE1\u8BDE\u5F39\u86CB\u5F53\u6321\u515A\u8361\u6863\u5200\u6363\u8E48\u5012\u5C9B\u7977\u5BFC\u5230\u7A3B\u60BC\u9053\u76D7\u5FB7\u5F97\u7684\u8E6C\u706F\u767B\u7B49\u77AA\u51F3\u9093\u5824\u4F4E\u6EF4\u8FEA\u654C\u7B1B\u72C4\u6DA4\u7FDF\u5AE1\u62B5\u5E95\u5730\u8482\u7B2C\u5E1D\u5F1F\u9012\u7F14\u98A0\u6382\u6EC7\u7898\u70B9\u5178\u975B\u57AB\u7535\u4F43\u7538\u5E97\u60E6\u5960\u6DC0\u6BBF\u7889\u53FC\u96D5\u51CB\u5201\u6389\u540A\u9493\u8C03\u8DCC\u7239\u789F\u8776\u8FED\u8C0D\u53E0"],["b640","\u7993",6,"\u799B",11,"\u79A8",10,"\u79B4",4,"\u79BC\u79BF\u79C2\u79C4\u79C5\u79C7\u79C8\u79CA\u79CC\u79CE\u79CF\u79D0\u79D3\u79D4\u79D6\u79D7\u79D9",5,"\u79E0\u79E1\u79E2\u79E5\u79E8\u79EA"],["b680","\u79EC\u79EE\u79F1",6,"\u79F9\u79FA\u79FC\u79FE\u79FF\u7A01\u7A04\u7A05\u7A07\u7A08\u7A09\u7A0A\u7A0C\u7A0F",4,"\u7A15\u7A16\u7A18\u7A19\u7A1B\u7A1C\u4E01\u76EF\u53EE\u9489\u9876\u9F0E\u952D\u5B9A\u8BA2\u4E22\u4E1C\u51AC\u8463\u61C2\u52A8\u680B\u4F97\u606B\u51BB\u6D1E\u515C\u6296\u6597\u9661\u8C46\u9017\u75D8\u90FD\u7763\u6BD2\u728A\u72EC\u8BFB\u5835\u7779\u8D4C\u675C\u9540\u809A\u5EA6\u6E21\u5992\u7AEF\u77ED\u953B\u6BB5\u65AD\u7F0E\u5806\u5151\u961F\u5BF9\u58A9\u5428\u8E72\u6566\u987F\u56E4\u949D\u76FE\u9041\u6387\u54C6\u591A\u593A\u579B\u8EB2\u6735\u8DFA\u8235\u5241\u60F0\u5815\u86FE\u5CE8\u9E45\u4FC4\u989D\u8BB9\u5A25\u6076\u5384\u627C\u904F\u9102\u997F\u6069\u800C\u513F\u8033\u5C14\u9975\u6D31\u4E8C"],["b740","\u7A1D\u7A1F\u7A21\u7A22\u7A24",14,"\u7A34\u7A35\u7A36\u7A38\u7A3A\u7A3E\u7A40",5,"\u7A47",9,"\u7A52",4,"\u7A58",16],["b780","\u7A69",6,"\u7A71\u7A72\u7A73\u7A75\u7A7B\u7A7C\u7A7D\u7A7E\u7A82\u7A85\u7A87\u7A89\u7A8A\u7A8B\u7A8C\u7A8E\u7A8F\u7A90\u7A93\u7A94\u7A99\u7A9A\u7A9B\u7A9E\u7AA1\u7AA2\u8D30\u53D1\u7F5A\u7B4F\u4F10\u4E4F\u9600\u6CD5\u73D0\u85E9\u5E06\u756A\u7FFB\u6A0A\u77FE\u9492\u7E41\u51E1\u70E6\u53CD\u8FD4\u8303\u8D29\u72AF\u996D\u6CDB\u574A\u82B3\u65B9\u80AA\u623F\u9632\u59A8\u4EFF\u8BBF\u7EBA\u653E\u83F2\u975E\u5561\u98DE\u80A5\u532A\u8BFD\u5420\u80BA\u5E9F\u6CB8\u8D39\u82AC\u915A\u5429\u6C1B\u5206\u7EB7\u575F\u711A\u6C7E\u7C89\u594B\u4EFD\u5FFF\u6124\u7CAA\u4E30\u5C01\u67AB\u8702\u5CF0\u950B\u98CE\u75AF\u70FD\u9022\u51AF\u7F1D\u8BBD\u5949\u51E4\u4F5B\u5426\u592B\u6577\u80A4\u5B75\u6276\u62C2\u8F90\u5E45\u6C1F\u7B26\u4F0F\u4FD8\u670D"],["b840","\u7AA3\u7AA4\u7AA7\u7AA9\u7AAA\u7AAB\u7AAE",4,"\u7AB4",10,"\u7AC0",10,"\u7ACC",9,"\u7AD7\u7AD8\u7ADA\u7ADB\u7ADC\u7ADD\u7AE1\u7AE2\u7AE4\u7AE7",5,"\u7AEE\u7AF0\u7AF1\u7AF2\u7AF3"],["b880","\u7AF4",4,"\u7AFB\u7AFC\u7AFE\u7B00\u7B01\u7B02\u7B05\u7B07\u7B09\u7B0C\u7B0D\u7B0E\u7B10\u7B12\u7B13\u7B16\u7B17\u7B18\u7B1A\u7B1C\u7B1D\u7B1F\u7B21\u7B22\u7B23\u7B27\u7B29\u7B2D\u6D6E\u6DAA\u798F\u88B1\u5F17\u752B\u629A\u8F85\u4FEF\u91DC\u65A7\u812F\u8151\u5E9C\u8150\u8D74\u526F\u8986\u8D4B\u590D\u5085\u4ED8\u961C\u7236\u8179\u8D1F\u5BCC\u8BA3\u9644\u5987\u7F1A\u5490\u5676\u560E\u8BE5\u6539\u6982\u9499\u76D6\u6E89\u5E72\u7518\u6746\u67D1\u7AFF\u809D\u8D76\u611F\u79C6\u6562\u8D63\u5188\u521A\u94A2\u7F38\u809B\u7EB2\u5C97\u6E2F\u6760\u7BD9\u768B\u9AD8\u818F\u7F94\u7CD5\u641E\u9550\u7A3F\u544A\u54E5\u6B4C\u6401\u6208\u9E3D\u80F3\u7599\u5272\u9769\u845B\u683C\u86E4\u9601\u9694\u94EC\u4E2A\u5404\u7ED9\u6839\u8DDF\u8015\u66F4\u5E9A\u7FB9"],["b940","\u7B2F\u7B30\u7B32\u7B34\u7B35\u7B36\u7B37\u7B39\u7B3B\u7B3D\u7B3F",5,"\u7B46\u7B48\u7B4A\u7B4D\u7B4E\u7B53\u7B55\u7B57\u7B59\u7B5C\u7B5E\u7B5F\u7B61\u7B63",10,"\u7B6F\u7B70\u7B73\u7B74\u7B76\u7B78\u7B7A\u7B7C\u7B7D\u7B7F\u7B81\u7B82\u7B83\u7B84\u7B86",6,"\u7B8E\u7B8F"],["b980","\u7B91\u7B92\u7B93\u7B96\u7B98\u7B99\u7B9A\u7B9B\u7B9E\u7B9F\u7BA0\u7BA3\u7BA4\u7BA5\u7BAE\u7BAF\u7BB0\u7BB2\u7BB3\u7BB5\u7BB6\u7BB7\u7BB9",7,"\u7BC2\u7BC3\u7BC4\u57C2\u803F\u6897\u5DE5\u653B\u529F\u606D\u9F9A\u4F9B\u8EAC\u516C\u5BAB\u5F13\u5DE9\u6C5E\u62F1\u8D21\u5171\u94A9\u52FE\u6C9F\u82DF\u72D7\u57A2\u6784\u8D2D\u591F\u8F9C\u83C7\u5495\u7B8D\u4F30\u6CBD\u5B64\u59D1\u9F13\u53E4\u86CA\u9AA8\u8C37\u80A1\u6545\u987E\u56FA\u96C7\u522E\u74DC\u5250\u5BE1\u6302\u8902\u4E56\u62D0\u602A\u68FA\u5173\u5B98\u51A0\u89C2\u7BA1\u9986\u7F50\u60EF\u704C\u8D2F\u5149\u5E7F\u901B\u7470\u89C4\u572D\u7845\u5F52\u9F9F\u95FA\u8F68\u9B3C\u8BE1\u7678\u6842\u67DC\u8DEA\u8D35\u523D\u8F8A\u6EDA\u68CD\u9505\u90ED\u56FD\u679C\u88F9\u8FC7\u54C8"],["ba40","\u7BC5\u7BC8\u7BC9\u7BCA\u7BCB\u7BCD\u7BCE\u7BCF\u7BD0\u7BD2\u7BD4",4,"\u7BDB\u7BDC\u7BDE\u7BDF\u7BE0\u7BE2\u7BE3\u7BE4\u7BE7\u7BE8\u7BE9\u7BEB\u7BEC\u7BED\u7BEF\u7BF0\u7BF2",4,"\u7BF8\u7BF9\u7BFA\u7BFB\u7BFD\u7BFF",7,"\u7C08\u7C09\u7C0A\u7C0D\u7C0E\u7C10",5,"\u7C17\u7C18\u7C19"],["ba80","\u7C1A",4,"\u7C20",5,"\u7C28\u7C29\u7C2B",12,"\u7C39",5,"\u7C42\u9AB8\u5B69\u6D77\u6C26\u4EA5\u5BB3\u9A87\u9163\u61A8\u90AF\u97E9\u542B\u6DB5\u5BD2\u51FD\u558A\u7F55\u7FF0\u64BC\u634D\u65F1\u61BE\u608D\u710A\u6C57\u6C49\u592F\u676D\u822A\u58D5\u568E\u8C6A\u6BEB\u90DD\u597D\u8017\u53F7\u6D69\u5475\u559D\u8377\u83CF\u6838\u79BE\u548C\u4F55\u5408\u76D2\u8C89\u9602\u6CB3\u6DB8\u8D6B\u8910\u9E64\u8D3A\u563F\u9ED1\u75D5\u5F88\u72E0\u6068\u54FC\u4EA8\u6A2A\u8861\u6052\u8F70\u54C4\u70D8\u8679\u9E3F\u6D2A\u5B8F\u5F18\u7EA2\u5589\u4FAF\u7334\u543C\u539A\u5019\u540E\u547C\u4E4E\u5FFD\u745A\u58F6\u846B\u80E1\u8774\u72D0\u7CCA\u6E56"],["bb40","\u7C43",9,"\u7C4E",36,"\u7C75",5,"\u7C7E",9],["bb80","\u7C88\u7C8A",6,"\u7C93\u7C94\u7C96\u7C99\u7C9A\u7C9B\u7CA0\u7CA1\u7CA3\u7CA6\u7CA7\u7CA8\u7CA9\u7CAB\u7CAC\u7CAD\u7CAF\u7CB0\u7CB4",4,"\u7CBA\u7CBB\u5F27\u864E\u552C\u62A4\u4E92\u6CAA\u6237\u82B1\u54D7\u534E\u733E\u6ED1\u753B\u5212\u5316\u8BDD\u69D0\u5F8A\u6000\u6DEE\u574F\u6B22\u73AF\u6853\u8FD8\u7F13\u6362\u60A3\u5524\u75EA\u8C62\u7115\u6DA3\u5BA6\u5E7B\u8352\u614C\u9EC4\u78FA\u8757\u7C27\u7687\u51F0\u60F6\u714C\u6643\u5E4C\u604D\u8C0E\u7070\u6325\u8F89\u5FBD\u6062\u86D4\u56DE\u6BC1\u6094\u6167\u5349\u60E0\u6666\u8D3F\u79FD\u4F1A\u70E9\u6C47\u8BB3\u8BF2\u7ED8\u8364\u660F\u5A5A\u9B42\u6D51\u6DF7\u8C41\u6D3B\u4F19\u706B\u83B7\u6216\u60D1\u970D\u8D27\u7978\u51FB\u573E\u57FA\u673A\u7578\u7A3D\u79EF\u7B95"],["bc40","\u7CBF\u7CC0\u7CC2\u7CC3\u7CC4\u7CC6\u7CC9\u7CCB\u7CCE",6,"\u7CD8\u7CDA\u7CDB\u7CDD\u7CDE\u7CE1",6,"\u7CE9",5,"\u7CF0",7,"\u7CF9\u7CFA\u7CFC",13,"\u7D0B",5],["bc80","\u7D11",14,"\u7D21\u7D23\u7D24\u7D25\u7D26\u7D28\u7D29\u7D2A\u7D2C\u7D2D\u7D2E\u7D30",6,"\u808C\u9965\u8FF9\u6FC0\u8BA5\u9E21\u59EC\u7EE9\u7F09\u5409\u6781\u68D8\u8F91\u7C4D\u96C6\u53CA\u6025\u75BE\u6C72\u5373\u5AC9\u7EA7\u6324\u51E0\u810A\u5DF1\u84DF\u6280\u5180\u5B63\u4F0E\u796D\u5242\u60B8\u6D4E\u5BC4\u5BC2\u8BA1\u8BB0\u65E2\u5FCC\u9645\u5993\u7EE7\u7EAA\u5609\u67B7\u5939\u4F73\u5BB6\u52A0\u835A\u988A\u8D3E\u7532\u94BE\u5047\u7A3C\u4EF7\u67B6\u9A7E\u5AC1\u6B7C\u76D1\u575A\u5C16\u7B3A\u95F4\u714E\u517C\u80A9\u8270\u5978\u7F04\u8327\u68C0\u67EC\u78B1\u7877\u62E3\u6361\u7B80\u4FED\u526A\u51CF\u8350\u69DB\u9274\u8DF5\u8D31\u89C1\u952E\u7BAD\u4EF6"],["bd40","\u7D37",54,"\u7D6F",7],["bd80","\u7D78",32,"\u5065\u8230\u5251\u996F\u6E10\u6E85\u6DA7\u5EFA\u50F5\u59DC\u5C06\u6D46\u6C5F\u7586\u848B\u6868\u5956\u8BB2\u5320\u9171\u964D\u8549\u6912\u7901\u7126\u80F6\u4EA4\u90CA\u6D47\u9A84\u5A07\u56BC\u6405\u94F0\u77EB\u4FA5\u811A\u72E1\u89D2\u997A\u7F34\u7EDE\u527F\u6559\u9175\u8F7F\u8F83\u53EB\u7A96\u63ED\u63A5\u7686\u79F8\u8857\u9636\u622A\u52AB\u8282\u6854\u6770\u6377\u776B\u7AED\u6D01\u7ED3\u89E3\u59D0\u6212\u85C9\u82A5\u754C\u501F\u4ECB\u75A5\u8BEB\u5C4A\u5DFE\u7B4B\u65A4\u91D1\u4ECA\u6D25\u895F\u7D27\u9526\u4EC5\u8C28\u8FDB\u9773\u664B\u7981\u8FD1\u70EC\u6D78"],["be40","\u7D99",12,"\u7DA7",6,"\u7DAF",42],["be80","\u7DDA",32,"\u5C3D\u52B2\u8346\u5162\u830E\u775B\u6676\u9CB8\u4EAC\u60CA\u7CBE\u7CB3\u7ECF\u4E95\u8B66\u666F\u9888\u9759\u5883\u656C\u955C\u5F84\u75C9\u9756\u7ADF\u7ADE\u51C0\u70AF\u7A98\u63EA\u7A76\u7EA0\u7396\u97ED\u4E45\u7078\u4E5D\u9152\u53A9\u6551\u65E7\u81FC\u8205\u548E\u5C31\u759A\u97A0\u62D8\u72D9\u75BD\u5C45\u9A79\u83CA\u5C40\u5480\u77E9\u4E3E\u6CAE\u805A\u62D2\u636E\u5DE8\u5177\u8DDD\u8E1E\u952F\u4FF1\u53E5\u60E7\u70AC\u5267\u6350\u9E43\u5A1F\u5026\u7737\u5377\u7EE2\u6485\u652B\u6289\u6398\u5014\u7235\u89C9\u51B3\u8BC0\u7EDD\u5747\u83CC\u94A7\u519B\u541B\u5CFB"],["bf40","\u7DFB",62],["bf80","\u7E3A\u7E3C",4,"\u7E42",4,"\u7E48",21,"\u4FCA\u7AE3\u6D5A\u90E1\u9A8F\u5580\u5496\u5361\u54AF\u5F00\u63E9\u6977\u51EF\u6168\u520A\u582A\u52D8\u574E\u780D\u770B\u5EB7\u6177\u7CE0\u625B\u6297\u4EA2\u7095\u8003\u62F7\u70E4\u9760\u5777\u82DB\u67EF\u68F5\u78D5\u9897\u79D1\u58F3\u54B3\u53EF\u6E34\u514B\u523B\u5BA2\u8BFE\u80AF\u5543\u57A6\u6073\u5751\u542D\u7A7A\u6050\u5B54\u63A7\u62A0\u53E3\u6263\u5BC7\u67AF\u54ED\u7A9F\u82E6\u9177\u5E93\u88E4\u5938\u57AE\u630E\u8DE8\u80EF\u5757\u7B77\u4FA9\u5FEB\u5BBD\u6B3E\u5321\u7B50\u72C2\u6846\u77FF\u7736\u65F7\u51B5\u4E8F\u76D4\u5CBF\u7AA5\u8475\u594E\u9B41\u5080"],["c040","\u7E5E",35,"\u7E83",23,"\u7E9C\u7E9D\u7E9E"],["c080","\u7EAE\u7EB4\u7EBB\u7EBC\u7ED6\u7EE4\u7EEC\u7EF9\u7F0A\u7F10\u7F1E\u7F37\u7F39\u7F3B",6,"\u7F43\u7F46",9,"\u7F52\u7F53\u9988\u6127\u6E83\u5764\u6606\u6346\u56F0\u62EC\u6269\u5ED3\u9614\u5783\u62C9\u5587\u8721\u814A\u8FA3\u5566\u83B1\u6765\u8D56\u84DD\u5A6A\u680F\u62E6\u7BEE\u9611\u5170\u6F9C\u8C30\u63FD\u89C8\u61D2\u7F06\u70C2\u6EE5\u7405\u6994\u72FC\u5ECA\u90CE\u6717\u6D6A\u635E\u52B3\u7262\u8001\u4F6C\u59E5\u916A\u70D9\u6D9D\u52D2\u4E50\u96F7\u956D\u857E\u78CA\u7D2F\u5121\u5792\u64C2\u808B\u7C7B\u6CEA\u68F1\u695E\u51B7\u5398\u68A8\u7281\u9ECE\u7BF1\u72F8\u79BB\u6F13\u7406\u674E\u91CC\u9CA4\u793C\u8389\u8354\u540F\u6817\u4E3D\u5389\u52B1\u783E\u5386\u5229\u5088\u4F8B\u4FD0"],["c140","\u7F56\u7F59\u7F5B\u7F5C\u7F5D\u7F5E\u7F60\u7F63",4,"\u7F6B\u7F6C\u7F6D\u7F6F\u7F70\u7F73\u7F75\u7F76\u7F77\u7F78\u7F7A\u7F7B\u7F7C\u7F7D\u7F7F\u7F80\u7F82",7,"\u7F8B\u7F8D\u7F8F",4,"\u7F95",4,"\u7F9B\u7F9C\u7FA0\u7FA2\u7FA3\u7FA5\u7FA6\u7FA8",6,"\u7FB1"],["c180","\u7FB3",4,"\u7FBA\u7FBB\u7FBE\u7FC0\u7FC2\u7FC3\u7FC4\u7FC6\u7FC7\u7FC8\u7FC9\u7FCB\u7FCD\u7FCF",4,"\u7FD6\u7FD7\u7FD9",5,"\u7FE2\u7FE3\u75E2\u7ACB\u7C92\u6CA5\u96B6\u529B\u7483\u54E9\u4FE9\u8054\u83B2\u8FDE\u9570\u5EC9\u601C\u6D9F\u5E18\u655B\u8138\u94FE\u604B\u70BC\u7EC3\u7CAE\u51C9\u6881\u7CB1\u826F\u4E24\u8F86\u91CF\u667E\u4EAE\u8C05\u64A9\u804A\u50DA\u7597\u71CE\u5BE5\u8FBD\u6F66\u4E86\u6482\u9563\u5ED6\u6599\u5217\u88C2\u70C8\u52A3\u730E\u7433\u6797\u78F7\u9716\u4E34\u90BB\u9CDE\u6DCB\u51DB\u8D41\u541D\u62CE\u73B2\u83F1\u96F6\u9F84\u94C3\u4F36\u7F9A\u51CC\u7075\u9675\u5CAD\u9886\u53E6\u4EE4\u6E9C\u7409\u69B4\u786B\u998F\u7559\u5218\u7624\u6D41\u67F3\u516D\u9F99\u804B\u5499\u7B3C\u7ABF"],["c240","\u7FE4\u7FE7\u7FE8\u7FEA\u7FEB\u7FEC\u7FED\u7FEF\u7FF2\u7FF4",6,"\u7FFD\u7FFE\u7FFF\u8002\u8007\u8008\u8009\u800A\u800E\u800F\u8011\u8013\u801A\u801B\u801D\u801E\u801F\u8021\u8023\u8024\u802B",5,"\u8032\u8034\u8039\u803A\u803C\u803E\u8040\u8041\u8044\u8045\u8047\u8048\u8049\u804E\u804F\u8050\u8051\u8053\u8055\u8056\u8057"],["c280","\u8059\u805B",13,"\u806B",5,"\u8072",11,"\u9686\u5784\u62E2\u9647\u697C\u5A04\u6402\u7BD3\u6F0F\u964B\u82A6\u5362\u9885\u5E90\u7089\u63B3\u5364\u864F\u9C81\u9E93\u788C\u9732\u8DEF\u8D42\u9E7F\u6F5E\u7984\u5F55\u9646\u622E\u9A74\u5415\u94DD\u4FA3\u65C5\u5C65\u5C61\u7F15\u8651\u6C2F\u5F8B\u7387\u6EE4\u7EFF\u5CE6\u631B\u5B6A\u6EE6\u5375\u4E71\u63A0\u7565\u62A1\u8F6E\u4F26\u4ED1\u6CA6\u7EB6\u8BBA\u841D\u87BA\u7F57\u903B\u9523\u7BA9\u9AA1\u88F8\u843D\u6D1B\u9A86\u7EDC\u5988\u9EBB\u739B\u7801\u8682\u9A6C\u9A82\u561B\u5417\u57CB\u4E70\u9EA6\u5356\u8FC8\u8109\u7792\u9992\u86EE\u6EE1\u8513\u66FC\u6162\u6F2B"],["c340","\u807E\u8081\u8082\u8085\u8088\u808A\u808D",5,"\u8094\u8095\u8097\u8099\u809E\u80A3\u80A6\u80A7\u80A8\u80AC\u80B0\u80B3\u80B5\u80B6\u80B8\u80B9\u80BB\u80C5\u80C7",4,"\u80CF",6,"\u80D8\u80DF\u80E0\u80E2\u80E3\u80E6\u80EE\u80F5\u80F7\u80F9\u80FB\u80FE\u80FF\u8100\u8101\u8103\u8104\u8105\u8107\u8108\u810B"],["c380","\u810C\u8115\u8117\u8119\u811B\u811C\u811D\u811F",12,"\u812D\u812E\u8130\u8133\u8134\u8135\u8137\u8139",4,"\u813F\u8C29\u8292\u832B\u76F2\u6C13\u5FD9\u83BD\u732B\u8305\u951A\u6BDB\u77DB\u94C6\u536F\u8302\u5192\u5E3D\u8C8C\u8D38\u4E48\u73AB\u679A\u6885\u9176\u9709\u7164\u6CA1\u7709\u5A92\u9541\u6BCF\u7F8E\u6627\u5BD0\u59B9\u5A9A\u95E8\u95F7\u4EEC\u840C\u8499\u6AAC\u76DF\u9530\u731B\u68A6\u5B5F\u772F\u919A\u9761\u7CDC\u8FF7\u8C1C\u5F25\u7C73\u79D8\u89C5\u6CCC\u871C\u5BC6\u5E42\u68C9\u7720\u7EF5\u5195\u514D\u52C9\u5A29\u7F05\u9762\u82D7\u63CF\u7784\u85D0\u79D2\u6E3A\u5E99\u5999\u8511\u706D\u6C11\u62BF\u76BF\u654F\u60AF\u95FD\u660E\u879F\u9E23\u94ED\u540D\u547D\u8C2C\u6478"],["c440","\u8140",5,"\u8147\u8149\u814D\u814E\u814F\u8152\u8156\u8157\u8158\u815B",4,"\u8161\u8162\u8163\u8164\u8166\u8168\u816A\u816B\u816C\u816F\u8172\u8173\u8175\u8176\u8177\u8178\u8181\u8183",4,"\u8189\u818B\u818C\u818D\u818E\u8190\u8192",5,"\u8199\u819A\u819E",4,"\u81A4\u81A5"],["c480","\u81A7\u81A9\u81AB",7,"\u81B4",5,"\u81BC\u81BD\u81BE\u81BF\u81C4\u81C5\u81C7\u81C8\u81C9\u81CB\u81CD",6,"\u6479\u8611\u6A21\u819C\u78E8\u6469\u9B54\u62B9\u672B\u83AB\u58A8\u9ED8\u6CAB\u6F20\u5BDE\u964C\u8C0B\u725F\u67D0\u62C7\u7261\u4EA9\u59C6\u6BCD\u5893\u66AE\u5E55\u52DF\u6155\u6728\u76EE\u7766\u7267\u7A46\u62FF\u54EA\u5450\u94A0\u90A3\u5A1C\u7EB3\u6C16\u4E43\u5976\u8010\u5948\u5357\u7537\u96BE\u56CA\u6320\u8111\u607C\u95F9\u6DD6\u5462\u9981\u5185\u5AE9\u80FD\u59AE\u9713\u502A\u6CE5\u5C3C\u62DF\u4F60\u533F\u817B\u9006\u6EBA\u852B\u62C8\u5E74\u78BE\u64B5\u637B\u5FF5\u5A18\u917F\u9E1F\u5C3F\u634F\u8042\u5B7D\u556E\u954A\u954D\u6D85\u60A8\u67E0\u72DE\u51DD\u5B81"],["c540","\u81D4",14,"\u81E4\u81E5\u81E6\u81E8\u81E9\u81EB\u81EE",4,"\u81F5",5,"\u81FD\u81FF\u8203\u8207",4,"\u820E\u820F\u8211\u8213\u8215",5,"\u821D\u8220\u8224\u8225\u8226\u8227\u8229\u822E\u8232\u823A\u823C\u823D\u823F"],["c580","\u8240\u8241\u8242\u8243\u8245\u8246\u8248\u824A\u824C\u824D\u824E\u8250",7,"\u8259\u825B\u825C\u825D\u825E\u8260",7,"\u8269\u62E7\u6CDE\u725B\u626D\u94AE\u7EBD\u8113\u6D53\u519C\u5F04\u5974\u52AA\u6012\u5973\u6696\u8650\u759F\u632A\u61E6\u7CEF\u8BFA\u54E6\u6B27\u9E25\u6BB4\u85D5\u5455\u5076\u6CA4\u556A\u8DB4\u722C\u5E15\u6015\u7436\u62CD\u6392\u724C\u5F98\u6E43\u6D3E\u6500\u6F58\u76D8\u78D0\u76FC\u7554\u5224\u53DB\u4E53\u5E9E\u65C1\u802A\u80D6\u629B\u5486\u5228\u70AE\u888D\u8DD1\u6CE1\u5478\u80DA\u57F9\u88F4\u8D54\u966A\u914D\u4F69\u6C9B\u55B7\u76C6\u7830\u62A8\u70F9\u6F8E\u5F6D\u84EC\u68DA\u787C\u7BF7\u81A8\u670B\u9E4F\u6367\u78B0\u576F\u7812\u9739\u6279\u62AB\u5288\u7435\u6BD7"],["c640","\u826A\u826B\u826C\u826D\u8271\u8275\u8276\u8277\u8278\u827B\u827C\u8280\u8281\u8283\u8285\u8286\u8287\u8289\u828C\u8290\u8293\u8294\u8295\u8296\u829A\u829B\u829E\u82A0\u82A2\u82A3\u82A7\u82B2\u82B5\u82B6\u82BA\u82BB\u82BC\u82BF\u82C0\u82C2\u82C3\u82C5\u82C6\u82C9\u82D0\u82D6\u82D9\u82DA\u82DD\u82E2\u82E7\u82E8\u82E9\u82EA\u82EC\u82ED\u82EE\u82F0\u82F2\u82F3\u82F5\u82F6\u82F8"],["c680","\u82FA\u82FC",4,"\u830A\u830B\u830D\u8310\u8312\u8313\u8316\u8318\u8319\u831D",9,"\u8329\u832A\u832E\u8330\u8332\u8337\u833B\u833D\u5564\u813E\u75B2\u76AE\u5339\u75DE\u50FB\u5C41\u8B6C\u7BC7\u504F\u7247\u9A97\u98D8\u6F02\u74E2\u7968\u6487\u77A5\u62FC\u9891\u8D2B\u54C1\u8058\u4E52\u576A\u82F9\u840D\u5E73\u51ED\u74F6\u8BC4\u5C4F\u5761\u6CFC\u9887\u5A46\u7834\u9B44\u8FEB\u7C95\u5256\u6251\u94FA\u4EC6\u8386\u8461\u83E9\u84B2\u57D4\u6734\u5703\u666E\u6D66\u8C31\u66DD\u7011\u671F\u6B3A\u6816\u621A\u59BB\u4E03\u51C4\u6F06\u67D2\u6C8F\u5176\u68CB\u5947\u6B67\u7566\u5D0E\u8110\u9F50\u65D7\u7948\u7941\u9A91\u8D77\u5C82\u4E5E\u4F01\u542F\u5951\u780C\u5668\u6C14\u8FC4\u5F03\u6C7D\u6CE3\u8BAB\u6390"],["c740","\u833E\u833F\u8341\u8342\u8344\u8345\u8348\u834A",4,"\u8353\u8355",4,"\u835D\u8362\u8370",6,"\u8379\u837A\u837E",6,"\u8387\u8388\u838A\u838B\u838C\u838D\u838F\u8390\u8391\u8394\u8395\u8396\u8397\u8399\u839A\u839D\u839F\u83A1",6,"\u83AC\u83AD\u83AE"],["c780","\u83AF\u83B5\u83BB\u83BE\u83BF\u83C2\u83C3\u83C4\u83C6\u83C8\u83C9\u83CB\u83CD\u83CE\u83D0\u83D1\u83D2\u83D3\u83D5\u83D7\u83D9\u83DA\u83DB\u83DE\u83E2\u83E3\u83E4\u83E6\u83E7\u83E8\u83EB\u83EC\u83ED\u6070\u6D3D\u7275\u6266\u948E\u94C5\u5343\u8FC1\u7B7E\u4EDF\u8C26\u4E7E\u9ED4\u94B1\u94B3\u524D\u6F5C\u9063\u6D45\u8C34\u5811\u5D4C\u6B20\u6B49\u67AA\u545B\u8154\u7F8C\u5899\u8537\u5F3A\u62A2\u6A47\u9539\u6572\u6084\u6865\u77A7\u4E54\u4FA8\u5DE7\u9798\u64AC\u7FD8\u5CED\u4FCF\u7A8D\u5207\u8304\u4E14\u602F\u7A83\u94A6\u4FB5\u4EB2\u79E6\u7434\u52E4\u82B9\u64D2\u79BD\u5BDD\u6C81\u9752\u8F7B\u6C22\u503E\u537F\u6E05\u64CE\u6674\u6C30\u60C5\u9877\u8BF7\u5E86\u743C\u7A77\u79CB\u4E18\u90B1\u7403\u6C42\u56DA\u914B\u6CC5\u8D8B\u533A\u86C6\u66F2\u8EAF\u5C48\u9A71\u6E20"],["c840","\u83EE\u83EF\u83F3",4,"\u83FA\u83FB\u83FC\u83FE\u83FF\u8400\u8402\u8405\u8407\u8408\u8409\u840A\u8410\u8412",5,"\u8419\u841A\u841B\u841E",5,"\u8429",7,"\u8432",5,"\u8439\u843A\u843B\u843E",7,"\u8447\u8448\u8449"],["c880","\u844A",6,"\u8452",4,"\u8458\u845D\u845E\u845F\u8460\u8462\u8464",4,"\u846A\u846E\u846F\u8470\u8472\u8474\u8477\u8479\u847B\u847C\u53D6\u5A36\u9F8B\u8DA3\u53BB\u5708\u98A7\u6743\u919B\u6CC9\u5168\u75CA\u62F3\u72AC\u5238\u529D\u7F3A\u7094\u7638\u5374\u9E4A\u69B7\u786E\u96C0\u88D9\u7FA4\u7136\u71C3\u5189\u67D3\u74E4\u58E4\u6518\u56B7\u8BA9\u9976\u6270\u7ED5\u60F9\u70ED\u58EC\u4EC1\u4EBA\u5FCD\u97E7\u4EFB\u8BA4\u5203\u598A\u7EAB\u6254\u4ECD\u65E5\u620E\u8338\u84C9\u8363\u878D\u7194\u6EB6\u5BB9\u7ED2\u5197\u63C9\u67D4\u8089\u8339\u8815\u5112\u5B7A\u5982\u8FB1\u4E73\u6C5D\u5165\u8925\u8F6F\u962E\u854A\u745E\u9510\u95F0\u6DA6\u82E5\u5F31\u6492\u6D12\u8428\u816E\u9CC3\u585E\u8D5B\u4E09\u53C1"],["c940","\u847D",4,"\u8483\u8484\u8485\u8486\u848A\u848D\u848F",7,"\u8498\u849A\u849B\u849D\u849E\u849F\u84A0\u84A2",12,"\u84B0\u84B1\u84B3\u84B5\u84B6\u84B7\u84BB\u84BC\u84BE\u84C0\u84C2\u84C3\u84C5\u84C6\u84C7\u84C8\u84CB\u84CC\u84CE\u84CF\u84D2\u84D4\u84D5\u84D7"],["c980","\u84D8",4,"\u84DE\u84E1\u84E2\u84E4\u84E7",4,"\u84ED\u84EE\u84EF\u84F1",10,"\u84FD\u84FE\u8500\u8501\u8502\u4F1E\u6563\u6851\u55D3\u4E27\u6414\u9A9A\u626B\u5AC2\u745F\u8272\u6DA9\u68EE\u50E7\u838E\u7802\u6740\u5239\u6C99\u7EB1\u50BB\u5565\u715E\u7B5B\u6652\u73CA\u82EB\u6749\u5C71\u5220\u717D\u886B\u95EA\u9655\u64C5\u8D61\u81B3\u5584\u6C55\u6247\u7F2E\u5892\u4F24\u5546\u8D4F\u664C\u4E0A\u5C1A\u88F3\u68A2\u634E\u7A0D\u70E7\u828D\u52FA\u97F6\u5C11\u54E8\u90B5\u7ECD\u5962\u8D4A\u86C7\u820C\u820D\u8D66\u6444\u5C04\u6151\u6D89\u793E\u8BBE\u7837\u7533\u547B\u4F38\u8EAB\u6DF1\u5A20\u7EC5\u795E\u6C88\u5BA1\u5A76\u751A\u80BE\u614E\u6E17\u58F0\u751F\u7525\u7272\u5347\u7EF3"],["ca40","\u8503",8,"\u850D\u850E\u850F\u8510\u8512\u8514\u8515\u8516\u8518\u8519\u851B\u851C\u851D\u851E\u8520\u8522",8,"\u852D",9,"\u853E",4,"\u8544\u8545\u8546\u8547\u854B",10],["ca80","\u8557\u8558\u855A\u855B\u855C\u855D\u855F",4,"\u8565\u8566\u8567\u8569",8,"\u8573\u8575\u8576\u8577\u8578\u857C\u857D\u857F\u8580\u8581\u7701\u76DB\u5269\u80DC\u5723\u5E08\u5931\u72EE\u65BD\u6E7F\u8BD7\u5C38\u8671\u5341\u77F3\u62FE\u65F6\u4EC0\u98DF\u8680\u5B9E\u8BC6\u53F2\u77E2\u4F7F\u5C4E\u9A76\u59CB\u5F0F\u793A\u58EB\u4E16\u67FF\u4E8B\u62ED\u8A93\u901D\u52BF\u662F\u55DC\u566C\u9002\u4ED5\u4F8D\u91CA\u9970\u6C0F\u5E02\u6043\u5BA4\u89C6\u8BD5\u6536\u624B\u9996\u5B88\u5BFF\u6388\u552E\u53D7\u7626\u517D\u852C\u67A2\u68B3\u6B8A\u6292\u8F93\u53D4\u8212\u6DD1\u758F\u4E66\u8D4E\u5B70\u719F\u85AF\u6691\u66D9\u7F72\u8700\u9ECD\u9F20\u5C5E\u672F\u8FF0\u6811\u675F\u620D\u7AD6\u5885\u5EB6\u6570\u6F31"],["cb40","\u8582\u8583\u8586\u8588",6,"\u8590",10,"\u859D",6,"\u85A5\u85A6\u85A7\u85A9\u85AB\u85AC\u85AD\u85B1",5,"\u85B8\u85BA",6,"\u85C2",6,"\u85CA",4,"\u85D1\u85D2"],["cb80","\u85D4\u85D6",5,"\u85DD",6,"\u85E5\u85E6\u85E7\u85E8\u85EA",14,"\u6055\u5237\u800D\u6454\u8870\u7529\u5E05\u6813\u62F4\u971C\u53CC\u723D\u8C01\u6C34\u7761\u7A0E\u542E\u77AC\u987A\u821C\u8BF4\u7855\u6714\u70C1\u65AF\u6495\u5636\u601D\u79C1\u53F8\u4E1D\u6B7B\u8086\u5BFA\u55E3\u56DB\u4F3A\u4F3C\u9972\u5DF3\u677E\u8038\u6002\u9882\u9001\u5B8B\u8BBC\u8BF5\u641C\u8258\u64DE\u55FD\u82CF\u9165\u4FD7\u7D20\u901F\u7C9F\u50F3\u5851\u6EAF\u5BBF\u8BC9\u8083\u9178\u849C\u7B97\u867D\u968B\u968F\u7EE5\u9AD3\u788E\u5C81\u7A57\u9042\u96A7\u795F\u5B59\u635F\u7B0B\u84D1\u68AD\u5506\u7F29\u7410\u7D22\u9501\u6240\u584C\u4ED6\u5B83\u5979\u5854"],["cc40","\u85F9\u85FA\u85FC\u85FD\u85FE\u8600",4,"\u8606",10,"\u8612\u8613\u8614\u8615\u8617",15,"\u8628\u862A",13,"\u8639\u863A\u863B\u863D\u863E\u863F\u8640"],["cc80","\u8641",11,"\u8652\u8653\u8655",4,"\u865B\u865C\u865D\u865F\u8660\u8661\u8663",7,"\u736D\u631E\u8E4B\u8E0F\u80CE\u82D4\u62AC\u53F0\u6CF0\u915E\u592A\u6001\u6C70\u574D\u644A\u8D2A\u762B\u6EE9\u575B\u6A80\u75F0\u6F6D\u8C2D\u8C08\u5766\u6BEF\u8892\u78B3\u63A2\u53F9\u70AD\u6C64\u5858\u642A\u5802\u68E0\u819B\u5510\u7CD6\u5018\u8EBA\u6DCC\u8D9F\u70EB\u638F\u6D9B\u6ED4\u7EE6\u8404\u6843\u9003\u6DD8\u9676\u8BA8\u5957\u7279\u85E4\u817E\u75BC\u8A8A\u68AF\u5254\u8E22\u9511\u63D0\u9898\u8E44\u557C\u4F53\u66FF\u568F\u60D5\u6D95\u5243\u5C49\u5929\u6DFB\u586B\u7530\u751C\u606C\u8214\u8146\u6311\u6761\u8FE2\u773A\u8DF3\u8D34\u94C1\u5E16\u5385\u542C\u70C3"],["cd40","\u866D\u866F\u8670\u8672",6,"\u8683",6,"\u868E",4,"\u8694\u8696",5,"\u869E",4,"\u86A5\u86A6\u86AB\u86AD\u86AE\u86B2\u86B3\u86B7\u86B8\u86B9\u86BB",4,"\u86C1\u86C2\u86C3\u86C5\u86C8\u86CC\u86CD\u86D2\u86D3\u86D5\u86D6\u86D7\u86DA\u86DC"],["cd80","\u86DD\u86E0\u86E1\u86E2\u86E3\u86E5\u86E6\u86E7\u86E8\u86EA\u86EB\u86EC\u86EF\u86F5\u86F6\u86F7\u86FA\u86FB\u86FC\u86FD\u86FF\u8701\u8704\u8705\u8706\u870B\u870C\u870E\u870F\u8710\u8711\u8714\u8716\u6C40\u5EF7\u505C\u4EAD\u5EAD\u633A\u8247\u901A\u6850\u916E\u77B3\u540C\u94DC\u5F64\u7AE5\u6876\u6345\u7B52\u7EDF\u75DB\u5077\u6295\u5934\u900F\u51F8\u79C3\u7A81\u56FE\u5F92\u9014\u6D82\u5C60\u571F\u5410\u5154\u6E4D\u56E2\u63A8\u9893\u817F\u8715\u892A\u9000\u541E\u5C6F\u81C0\u62D6\u6258\u8131\u9E35\u9640\u9A6E\u9A7C\u692D\u59A5\u62D3\u553E\u6316\u54C7\u86D9\u6D3C\u5A03\u74E6\u889C\u6B6A\u5916\u8C4C\u5F2F\u6E7E\u73A9\u987D\u4E38\u70F7\u5B8C\u7897\u633D\u665A\u7696\u60CB\u5B9B\u5A49\u4E07\u8155\u6C6A\u738B\u4EA1\u6789\u7F51\u5F80\u65FA\u671B\u5FD8\u5984\u5A01"],["ce40","\u8719\u871B\u871D\u871F\u8720\u8724\u8726\u8727\u8728\u872A\u872B\u872C\u872D\u872F\u8730\u8732\u8733\u8735\u8736\u8738\u8739\u873A\u873C\u873D\u8740",6,"\u874A\u874B\u874D\u874F\u8750\u8751\u8752\u8754\u8755\u8756\u8758\u875A",5,"\u8761\u8762\u8766",7,"\u876F\u8771\u8772\u8773\u8775"],["ce80","\u8777\u8778\u8779\u877A\u877F\u8780\u8781\u8784\u8786\u8787\u8789\u878A\u878C\u878E",4,"\u8794\u8795\u8796\u8798",6,"\u87A0",4,"\u5DCD\u5FAE\u5371\u97E6\u8FDD\u6845\u56F4\u552F\u60DF\u4E3A\u6F4D\u7EF4\u82C7\u840E\u59D4\u4F1F\u4F2A\u5C3E\u7EAC\u672A\u851A\u5473\u754F\u80C3\u5582\u9B4F\u4F4D\u6E2D\u8C13\u5C09\u6170\u536B\u761F\u6E29\u868A\u6587\u95FB\u7EB9\u543B\u7A33\u7D0A\u95EE\u55E1\u7FC1\u74EE\u631D\u8717\u6DA1\u7A9D\u6211\u65A1\u5367\u63E1\u6C83\u5DEB\u545C\u94A8\u4E4C\u6C61\u8BEC\u5C4B\u65E0\u829C\u68A7\u543E\u5434\u6BCB\u6B66\u4E94\u6342\u5348\u821E\u4F0D\u4FAE\u575E\u620A\u96FE\u6664\u7269\u52FF\u52A1\u609F\u8BEF\u6614\u7199\u6790\u897F\u7852\u77FD\u6670\u563B\u5438\u9521\u727A"],["cf40","\u87A5\u87A6\u87A7\u87A9\u87AA\u87AE\u87B0\u87B1\u87B2\u87B4\u87B6\u87B7\u87B8\u87B9\u87BB\u87BC\u87BE\u87BF\u87C1",4,"\u87C7\u87C8\u87C9\u87CC",4,"\u87D4",6,"\u87DC\u87DD\u87DE\u87DF\u87E1\u87E2\u87E3\u87E4\u87E6\u87E7\u87E8\u87E9\u87EB\u87EC\u87ED\u87EF",9],["cf80","\u87FA\u87FB\u87FC\u87FD\u87FF\u8800\u8801\u8802\u8804",5,"\u880B",7,"\u8814\u8817\u8818\u8819\u881A\u881C",4,"\u8823\u7A00\u606F\u5E0C\u6089\u819D\u5915\u60DC\u7184\u70EF\u6EAA\u6C50\u7280\u6A84\u88AD\u5E2D\u4E60\u5AB3\u559C\u94E3\u6D17\u7CFB\u9699\u620F\u7EC6\u778E\u867E\u5323\u971E\u8F96\u6687\u5CE1\u4FA0\u72ED\u4E0B\u53A6\u590F\u5413\u6380\u9528\u5148\u4ED9\u9C9C\u7EA4\u54B8\u8D24\u8854\u8237\u95F2\u6D8E\u5F26\u5ACC\u663E\u9669\u73B0\u732E\u53BF\u817A\u9985\u7FA1\u5BAA\u9677\u9650\u7EBF\u76F8\u53A2\u9576\u9999\u7BB1\u8944\u6E58\u4E61\u7FD4\u7965\u8BE6\u60F3\u54CD\u4EAB\u9879\u5DF7\u6A61\u50CF\u5411\u8C61\u8427\u785D\u9704\u524A\u54EE\u56A3\u9500\u6D88\u5BB5\u6DC6\u6653"],["d040","\u8824",13,"\u8833",5,"\u883A\u883B\u883D\u883E\u883F\u8841\u8842\u8843\u8846",5,"\u884E",5,"\u8855\u8856\u8858\u885A",6,"\u8866\u8867\u886A\u886D\u886F\u8871\u8873\u8874\u8875\u8876\u8878\u8879\u887A"],["d080","\u887B\u887C\u8880\u8883\u8886\u8887\u8889\u888A\u888C\u888E\u888F\u8890\u8891\u8893\u8894\u8895\u8897",4,"\u889D",4,"\u88A3\u88A5",5,"\u5C0F\u5B5D\u6821\u8096\u5578\u7B11\u6548\u6954\u4E9B\u6B47\u874E\u978B\u534F\u631F\u643A\u90AA\u659C\u80C1\u8C10\u5199\u68B0\u5378\u87F9\u61C8\u6CC4\u6CFB\u8C22\u5C51\u85AA\u82AF\u950C\u6B23\u8F9B\u65B0\u5FFB\u5FC3\u4FE1\u8845\u661F\u8165\u7329\u60FA\u5174\u5211\u578B\u5F62\u90A2\u884C\u9192\u5E78\u674F\u6027\u59D3\u5144\u51F6\u80F8\u5308\u6C79\u96C4\u718A\u4F11\u4FEE\u7F9E\u673D\u55C5\u9508\u79C0\u8896\u7EE3\u589F\u620C\u9700\u865A\u5618\u987B\u5F90\u8BB8\u84C4\u9157\u53D9\u65ED\u5E8F\u755C\u6064\u7D6E\u5A7F\u7EEA\u7EED\u8F69\u55A7\u5BA3\u60AC\u65CB\u7384"],["d140","\u88AC\u88AE\u88AF\u88B0\u88B2",4,"\u88B8\u88B9\u88BA\u88BB\u88BD\u88BE\u88BF\u88C0\u88C3\u88C4\u88C7\u88C8\u88CA\u88CB\u88CC\u88CD\u88CF\u88D0\u88D1\u88D3\u88D6\u88D7\u88DA",4,"\u88E0\u88E1\u88E6\u88E7\u88E9",6,"\u88F2\u88F5\u88F6\u88F7\u88FA\u88FB\u88FD\u88FF\u8900\u8901\u8903",5],["d180","\u8909\u890B",4,"\u8911\u8914",4,"\u891C",4,"\u8922\u8923\u8924\u8926\u8927\u8928\u8929\u892C\u892D\u892E\u892F\u8931\u8932\u8933\u8935\u8937\u9009\u7663\u7729\u7EDA\u9774\u859B\u5B66\u7A74\u96EA\u8840\u52CB\u718F\u5FAA\u65EC\u8BE2\u5BFB\u9A6F\u5DE1\u6B89\u6C5B\u8BAD\u8BAF\u900A\u8FC5\u538B\u62BC\u9E26\u9E2D\u5440\u4E2B\u82BD\u7259\u869C\u5D16\u8859\u6DAF\u96C5\u54D1\u4E9A\u8BB6\u7109\u54BD\u9609\u70DF\u6DF9\u76D0\u4E25\u7814\u8712\u5CA9\u5EF6\u8A00\u989C\u960E\u708E\u6CBF\u5944\u63A9\u773C\u884D\u6F14\u8273\u5830\u71D5\u538C\u781A\u96C1\u5501\u5F66\u7130\u5BB4\u8C1A\u9A8C\u6B83\u592E\u9E2F\u79E7\u6768\u626C\u4F6F\u75A1\u7F8A\u6D0B\u9633\u6C27\u4EF0\u75D2\u517B\u6837\u6F3E\u9080\u8170\u5996\u7476"],["d240","\u8938",8,"\u8942\u8943\u8945",24,"\u8960",5,"\u8967",19,"\u897C"],["d280","\u897D\u897E\u8980\u8982\u8984\u8985\u8987",26,"\u6447\u5C27\u9065\u7A91\u8C23\u59DA\u54AC\u8200\u836F\u8981\u8000\u6930\u564E\u8036\u7237\u91CE\u51B6\u4E5F\u9875\u6396\u4E1A\u53F6\u66F3\u814B\u591C\u6DB2\u4E00\u58F9\u533B\u63D6\u94F1\u4F9D\u4F0A\u8863\u9890\u5937\u9057\u79FB\u4EEA\u80F0\u7591\u6C82\u5B9C\u59E8\u5F5D\u6905\u8681\u501A\u5DF2\u4E59\u77E3\u4EE5\u827A\u6291\u6613\u9091\u5C79\u4EBF\u5F79\u81C6\u9038\u8084\u75AB\u4EA6\u88D4\u610F\u6BC5\u5FC6\u4E49\u76CA\u6EA2\u8BE3\u8BAE\u8C0A\u8BD1\u5F02\u7FFC\u7FCC\u7ECE\u8335\u836B\u56E0\u6BB7\u97F3\u9634\u59FB\u541F\u94F6\u6DEB\u5BC5\u996E\u5C39\u5F15\u9690"],["d340","\u89A2",30,"\u89C3\u89CD\u89D3\u89D4\u89D5\u89D7\u89D8\u89D9\u89DB\u89DD\u89DF\u89E0\u89E1\u89E2\u89E4\u89E7\u89E8\u89E9\u89EA\u89EC\u89ED\u89EE\u89F0\u89F1\u89F2\u89F4",6],["d380","\u89FB",4,"\u8A01",5,"\u8A08",21,"\u5370\u82F1\u6A31\u5A74\u9E70\u5E94\u7F28\u83B9\u8424\u8425\u8367\u8747\u8FCE\u8D62\u76C8\u5F71\u9896\u786C\u6620\u54DF\u62E5\u4F63\u81C3\u75C8\u5EB8\u96CD\u8E0A\u86F9\u548F\u6CF3\u6D8C\u6C38\u607F\u52C7\u7528\u5E7D\u4F18\u60A0\u5FE7\u5C24\u7531\u90AE\u94C0\u72B9\u6CB9\u6E38\u9149\u6709\u53CB\u53F3\u4F51\u91C9\u8BF1\u53C8\u5E7C\u8FC2\u6DE4\u4E8E\u76C2\u6986\u865E\u611A\u8206\u4F59\u4FDE\u903E\u9C7C\u6109\u6E1D\u6E14\u9685\u4E88\u5A31\u96E8\u4E0E\u5C7F\u79B9\u5B87\u8BED\u7FBD\u7389\u57DF\u828B\u90C1\u5401\u9047\u55BB\u5CEA\u5FA1\u6108\u6B32\u72F1\u80B2\u8A89"],["d440","\u8A1E",31,"\u8A3F",8,"\u8A49",21],["d480","\u8A5F",25,"\u8A7A",6,"\u6D74\u5BD3\u88D5\u9884\u8C6B\u9A6D\u9E33\u6E0A\u51A4\u5143\u57A3\u8881\u539F\u63F4\u8F95\u56ED\u5458\u5706\u733F\u6E90\u7F18\u8FDC\u82D1\u613F\u6028\u9662\u66F0\u7EA6\u8D8A\u8DC3\u94A5\u5CB3\u7CA4\u6708\u60A6\u9605\u8018\u4E91\u90E7\u5300\u9668\u5141\u8FD0\u8574\u915D\u6655\u97F5\u5B55\u531D\u7838\u6742\u683D\u54C9\u707E\u5BB0\u8F7D\u518D\u5728\u54B1\u6512\u6682\u8D5E\u8D43\u810F\u846C\u906D\u7CDF\u51FF\u85FB\u67A3\u65E9\u6FA1\u86A4\u8E81\u566A\u9020\u7682\u7076\u71E5\u8D23\u62E9\u5219\u6CFD\u8D3C\u600E\u589E\u618E\u66FE\u8D60\u624E\u55B3\u6E23\u672D\u8F67"],["d540","\u8A81",7,"\u8A8B",7,"\u8A94",46],["d580","\u8AC3",32,"\u94E1\u95F8\u7728\u6805\u69A8\u548B\u4E4D\u70B8\u8BC8\u6458\u658B\u5B85\u7A84\u503A\u5BE8\u77BB\u6BE1\u8A79\u7C98\u6CBE\u76CF\u65A9\u8F97\u5D2D\u5C55\u8638\u6808\u5360\u6218\u7AD9\u6E5B\u7EFD\u6A1F\u7AE0\u5F70\u6F33\u5F20\u638C\u6DA8\u6756\u4E08\u5E10\u8D26\u4ED7\u80C0\u7634\u969C\u62DB\u662D\u627E\u6CBC\u8D75\u7167\u7F69\u5146\u8087\u53EC\u906E\u6298\u54F2\u86F0\u8F99\u8005\u9517\u8517\u8FD9\u6D59\u73CD\u659F\u771F\u7504\u7827\u81FB\u8D1E\u9488\u4FA6\u6795\u75B9\u8BCA\u9707\u632F\u9547\u9635\u84B8\u6323\u7741\u5F81\u72F0\u4E89\u6014\u6574\u62EF\u6B63\u653F"],["d640","\u8AE4",34,"\u8B08",27],["d680","\u8B24\u8B25\u8B27",30,"\u5E27\u75C7\u90D1\u8BC1\u829D\u679D\u652F\u5431\u8718\u77E5\u80A2\u8102\u6C41\u4E4B\u7EC7\u804C\u76F4\u690D\u6B96\u6267\u503C\u4F84\u5740\u6307\u6B62\u8DBE\u53EA\u65E8\u7EB8\u5FD7\u631A\u63B7\u81F3\u81F4\u7F6E\u5E1C\u5CD9\u5236\u667A\u79E9\u7A1A\u8D28\u7099\u75D4\u6EDE\u6CBB\u7A92\u4E2D\u76C5\u5FE0\u949F\u8877\u7EC8\u79CD\u80BF\u91CD\u4EF2\u4F17\u821F\u5468\u5DDE\u6D32\u8BCC\u7CA5\u8F74\u8098\u5E1A\u5492\u76B1\u5B99\u663C\u9AA4\u73E0\u682A\u86DB\u6731\u732A\u8BF8\u8BDB\u9010\u7AF9\u70DB\u716E\u62C4\u77A9\u5631\u4E3B\u8457\u67F1\u52A9\u86C0\u8D2E\u94F8\u7B51"],["d740","\u8B46",31,"\u8B67",4,"\u8B6D",25],["d780","\u8B87",24,"\u8BAC\u8BB1\u8BBB\u8BC7\u8BD0\u8BEA\u8C09\u8C1E\u4F4F\u6CE8\u795D\u9A7B\u6293\u722A\u62FD\u4E13\u7816\u8F6C\u64B0\u8D5A\u7BC6\u6869\u5E84\u88C5\u5986\u649E\u58EE\u72B6\u690E\u9525\u8FFD\u8D58\u5760\u7F00\u8C06\u51C6\u6349\u62D9\u5353\u684C\u7422\u8301\u914C\u5544\u7740\u707C\u6D4A\u5179\u54A8\u8D44\u59FF\u6ECB\u6DC4\u5B5C\u7D2B\u4ED4\u7C7D\u6ED3\u5B50\u81EA\u6E0D\u5B57\u9B03\u68D5\u8E2A\u5B97\u7EFC\u603B\u7EB5\u90B9\u8D70\u594F\u63CD\u79DF\u8DB3\u5352\u65CF\u7956\u8BC5\u963B\u7EC4\u94BB\u7E82\u5634\u9189\u6700\u7F6A\u5C0A\u9075\u6628\u5DE6\u4F50\u67DE\u505A\u4F5C\u5750\u5EA7"],["d840","\u8C38",8,"\u8C42\u8C43\u8C44\u8C45\u8C48\u8C4A\u8C4B\u8C4D",7,"\u8C56\u8C57\u8C58\u8C59\u8C5B",5,"\u8C63",6,"\u8C6C",6,"\u8C74\u8C75\u8C76\u8C77\u8C7B",6,"\u8C83\u8C84\u8C86\u8C87"],["d880","\u8C88\u8C8B\u8C8D",6,"\u8C95\u8C96\u8C97\u8C99",20,"\u4E8D\u4E0C\u5140\u4E10\u5EFF\u5345\u4E15\u4E98\u4E1E\u9B32\u5B6C\u5669\u4E28\u79BA\u4E3F\u5315\u4E47\u592D\u723B\u536E\u6C10\u56DF\u80E4\u9997\u6BD3\u777E\u9F17\u4E36\u4E9F\u9F10\u4E5C\u4E69\u4E93\u8288\u5B5B\u556C\u560F\u4EC4\u538D\u539D\u53A3\u53A5\u53AE\u9765\u8D5D\u531A\u53F5\u5326\u532E\u533E\u8D5C\u5366\u5363\u5202\u5208\u520E\u522D\u5233\u523F\u5240\u524C\u525E\u5261\u525C\u84AF\u527D\u5282\u5281\u5290\u5293\u5182\u7F54\u4EBB\u4EC3\u4EC9\u4EC2\u4EE8\u4EE1\u4EEB\u4EDE\u4F1B\u4EF3\u4F22\u4F64\u4EF5\u4F25\u4F27\u4F09\u4F2B\u4F5E\u4F67\u6538\u4F5A\u4F5D"],["d940","\u8CAE",62],["d980","\u8CED",32,"\u4F5F\u4F57\u4F32\u4F3D\u4F76\u4F74\u4F91\u4F89\u4F83\u4F8F\u4F7E\u4F7B\u4FAA\u4F7C\u4FAC\u4F94\u4FE6\u4FE8\u4FEA\u4FC5\u4FDA\u4FE3\u4FDC\u4FD1\u4FDF\u4FF8\u5029\u504C\u4FF3\u502C\u500F\u502E\u502D\u4FFE\u501C\u500C\u5025\u5028\u507E\u5043\u5055\u5048\u504E\u506C\u507B\u50A5\u50A7\u50A9\u50BA\u50D6\u5106\u50ED\u50EC\u50E6\u50EE\u5107\u510B\u4EDD\u6C3D\u4F58\u4F65\u4FCE\u9FA0\u6C46\u7C74\u516E\u5DFD\u9EC9\u9998\u5181\u5914\u52F9\u530D\u8A07\u5310\u51EB\u5919\u5155\u4EA0\u5156\u4EB3\u886E\u88A4\u4EB5\u8114\u88D2\u7980\u5B34\u8803\u7FB8\u51AB\u51B1\u51BD\u51BC"],["da40","\u8D0E",14,"\u8D20\u8D51\u8D52\u8D57\u8D5F\u8D65\u8D68\u8D69\u8D6A\u8D6C\u8D6E\u8D6F\u8D71\u8D72\u8D78",8,"\u8D82\u8D83\u8D86\u8D87\u8D88\u8D89\u8D8C",4,"\u8D92\u8D93\u8D95",9,"\u8DA0\u8DA1"],["da80","\u8DA2\u8DA4",12,"\u8DB2\u8DB6\u8DB7\u8DB9\u8DBB\u8DBD\u8DC0\u8DC1\u8DC2\u8DC5\u8DC7\u8DC8\u8DC9\u8DCA\u8DCD\u8DD0\u8DD2\u8DD3\u8DD4\u51C7\u5196\u51A2\u51A5\u8BA0\u8BA6\u8BA7\u8BAA\u8BB4\u8BB5\u8BB7\u8BC2\u8BC3\u8BCB\u8BCF\u8BCE\u8BD2\u8BD3\u8BD4\u8BD6\u8BD8\u8BD9\u8BDC\u8BDF\u8BE0\u8BE4\u8BE8\u8BE9\u8BEE\u8BF0\u8BF3\u8BF6\u8BF9\u8BFC\u8BFF\u8C00\u8C02\u8C04\u8C07\u8C0C\u8C0F\u8C11\u8C12\u8C14\u8C15\u8C16\u8C19\u8C1B\u8C18\u8C1D\u8C1F\u8C20\u8C21\u8C25\u8C27\u8C2A\u8C2B\u8C2E\u8C2F\u8C32\u8C33\u8C35\u8C36\u5369\u537A\u961D\u9622\u9621\u9631\u962A\u963D\u963C\u9642\u9649\u9654\u965F\u9667\u966C\u9672\u9674\u9688\u968D\u9697\u96B0\u9097\u909B\u909D\u9099\u90AC\u90A1\u90B4\u90B3\u90B6\u90BA"],["db40","\u8DD5\u8DD8\u8DD9\u8DDC\u8DE0\u8DE1\u8DE2\u8DE5\u8DE6\u8DE7\u8DE9\u8DED\u8DEE\u8DF0\u8DF1\u8DF2\u8DF4\u8DF6\u8DFC\u8DFE",6,"\u8E06\u8E07\u8E08\u8E0B\u8E0D\u8E0E\u8E10\u8E11\u8E12\u8E13\u8E15",7,"\u8E20\u8E21\u8E24",4,"\u8E2B\u8E2D\u8E30\u8E32\u8E33\u8E34\u8E36\u8E37\u8E38\u8E3B\u8E3C\u8E3E"],["db80","\u8E3F\u8E43\u8E45\u8E46\u8E4C",4,"\u8E53",5,"\u8E5A",11,"\u8E67\u8E68\u8E6A\u8E6B\u8E6E\u8E71\u90B8\u90B0\u90CF\u90C5\u90BE\u90D0\u90C4\u90C7\u90D3\u90E6\u90E2\u90DC\u90D7\u90DB\u90EB\u90EF\u90FE\u9104\u9122\u911E\u9123\u9131\u912F\u9139\u9143\u9146\u520D\u5942\u52A2\u52AC\u52AD\u52BE\u54FF\u52D0\u52D6\u52F0\u53DF\u71EE\u77CD\u5EF4\u51F5\u51FC\u9B2F\u53B6\u5F01\u755A\u5DEF\u574C\u57A9\u57A1\u587E\u58BC\u58C5\u58D1\u5729\u572C\u572A\u5733\u5739\u572E\u572F\u575C\u573B\u5742\u5769\u5785\u576B\u5786\u577C\u577B\u5768\u576D\u5776\u5773\u57AD\u57A4\u578C\u57B2\u57CF\u57A7\u57B4\u5793\u57A0\u57D5\u57D8\u57DA\u57D9\u57D2\u57B8\u57F4\u57EF\u57F8\u57E4\u57DD"],["dc40","\u8E73\u8E75\u8E77",4,"\u8E7D\u8E7E\u8E80\u8E82\u8E83\u8E84\u8E86\u8E88",6,"\u8E91\u8E92\u8E93\u8E95",6,"\u8E9D\u8E9F",11,"\u8EAD\u8EAE\u8EB0\u8EB1\u8EB3",6,"\u8EBB",7],["dc80","\u8EC3",10,"\u8ECF",21,"\u580B\u580D\u57FD\u57ED\u5800\u581E\u5819\u5844\u5820\u5865\u586C\u5881\u5889\u589A\u5880\u99A8\u9F19\u61FF\u8279\u827D\u827F\u828F\u828A\u82A8\u8284\u828E\u8291\u8297\u8299\u82AB\u82B8\u82BE\u82B0\u82C8\u82CA\u82E3\u8298\u82B7\u82AE\u82CB\u82CC\u82C1\u82A9\u82B4\u82A1\u82AA\u829F\u82C4\u82CE\u82A4\u82E1\u8309\u82F7\u82E4\u830F\u8307\u82DC\u82F4\u82D2\u82D8\u830C\u82FB\u82D3\u8311\u831A\u8306\u8314\u8315\u82E0\u82D5\u831C\u8351\u835B\u835C\u8308\u8392\u833C\u8334\u8331\u839B\u835E\u832F\u834F\u8347\u8343\u835F\u8340\u8317\u8360\u832D\u833A\u8333\u8366\u8365"],["dd40","\u8EE5",62],["dd80","\u8F24",32,"\u8368\u831B\u8369\u836C\u836A\u836D\u836E\u83B0\u8378\u83B3\u83B4\u83A0\u83AA\u8393\u839C\u8385\u837C\u83B6\u83A9\u837D\u83B8\u837B\u8398\u839E\u83A8\u83BA\u83BC\u83C1\u8401\u83E5\u83D8\u5807\u8418\u840B\u83DD\u83FD\u83D6\u841C\u8438\u8411\u8406\u83D4\u83DF\u840F\u8403\u83F8\u83F9\u83EA\u83C5\u83C0\u8426\u83F0\u83E1\u845C\u8451\u845A\u8459\u8473\u8487\u8488\u847A\u8489\u8478\u843C\u8446\u8469\u8476\u848C\u848E\u8431\u846D\u84C1\u84CD\u84D0\u84E6\u84BD\u84D3\u84CA\u84BF\u84BA\u84E0\u84A1\u84B9\u84B4\u8497\u84E5\u84E3\u850C\u750D\u8538\u84F0\u8539\u851F\u853A"],["de40","\u8F45",32,"\u8F6A\u8F80\u8F8C\u8F92\u8F9D\u8FA0\u8FA1\u8FA2\u8FA4\u8FA5\u8FA6\u8FA7\u8FAA\u8FAC\u8FAD\u8FAE\u8FAF\u8FB2\u8FB3\u8FB4\u8FB5\u8FB7\u8FB8\u8FBA\u8FBB\u8FBC\u8FBF\u8FC0\u8FC3\u8FC6"],["de80","\u8FC9",4,"\u8FCF\u8FD2\u8FD6\u8FD7\u8FDA\u8FE0\u8FE1\u8FE3\u8FE7\u8FEC\u8FEF\u8FF1\u8FF2\u8FF4\u8FF5\u8FF6\u8FFA\u8FFB\u8FFC\u8FFE\u8FFF\u9007\u9008\u900C\u900E\u9013\u9015\u9018\u8556\u853B\u84FF\u84FC\u8559\u8548\u8568\u8564\u855E\u857A\u77A2\u8543\u8572\u857B\u85A4\u85A8\u8587\u858F\u8579\u85AE\u859C\u8585\u85B9\u85B7\u85B0\u85D3\u85C1\u85DC\u85FF\u8627\u8605\u8629\u8616\u863C\u5EFE\u5F08\u593C\u5941\u8037\u5955\u595A\u5958\u530F\u5C22\u5C25\u5C2C\u5C34\u624C\u626A\u629F\u62BB\u62CA\u62DA\u62D7\u62EE\u6322\u62F6\u6339\u634B\u6343\u63AD\u63F6\u6371\u637A\u638E\u63B4\u636D\u63AC\u638A\u6369\u63AE\u63BC\u63F2\u63F8\u63E0\u63FF\u63C4\u63DE\u63CE\u6452\u63C6\u63BE\u6445\u6441\u640B\u641B\u6420\u640C\u6426\u6421\u645E\u6484\u646D\u6496"],["df40","\u9019\u901C\u9023\u9024\u9025\u9027",5,"\u9030",4,"\u9037\u9039\u903A\u903D\u903F\u9040\u9043\u9045\u9046\u9048",4,"\u904E\u9054\u9055\u9056\u9059\u905A\u905C",5,"\u9064\u9066\u9067\u9069\u906A\u906B\u906C\u906F",4,"\u9076",6,"\u907E\u9081"],["df80","\u9084\u9085\u9086\u9087\u9089\u908A\u908C",4,"\u9092\u9094\u9096\u9098\u909A\u909C\u909E\u909F\u90A0\u90A4\u90A5\u90A7\u90A8\u90A9\u90AB\u90AD\u90B2\u90B7\u90BC\u90BD\u90BF\u90C0\u647A\u64B7\u64B8\u6499\u64BA\u64C0\u64D0\u64D7\u64E4\u64E2\u6509\u6525\u652E\u5F0B\u5FD2\u7519\u5F11\u535F\u53F1\u53FD\u53E9\u53E8\u53FB\u5412\u5416\u5406\u544B\u5452\u5453\u5454\u5456\u5443\u5421\u5457\u5459\u5423\u5432\u5482\u5494\u5477\u5471\u5464\u549A\u549B\u5484\u5476\u5466\u549D\u54D0\u54AD\u54C2\u54B4\u54D2\u54A7\u54A6\u54D3\u54D4\u5472\u54A3\u54D5\u54BB\u54BF\u54CC\u54D9\u54DA\u54DC\u54A9\u54AA\u54A4\u54DD\u54CF\u54DE\u551B\u54E7\u5520\u54FD\u5514\u54F3\u5522\u5523\u550F\u5511\u5527\u552A\u5567\u558F\u55B5\u5549\u556D\u5541\u5555\u553F\u5550\u553C"],["e040","\u90C2\u90C3\u90C6\u90C8\u90C9\u90CB\u90CC\u90CD\u90D2\u90D4\u90D5\u90D6\u90D8\u90D9\u90DA\u90DE\u90DF\u90E0\u90E3\u90E4\u90E5\u90E9\u90EA\u90EC\u90EE\u90F0\u90F1\u90F2\u90F3\u90F5\u90F6\u90F7\u90F9\u90FA\u90FB\u90FC\u90FF\u9100\u9101\u9103\u9105",19,"\u911A\u911B\u911C"],["e080","\u911D\u911F\u9120\u9121\u9124",10,"\u9130\u9132",6,"\u913A",8,"\u9144\u5537\u5556\u5575\u5576\u5577\u5533\u5530\u555C\u558B\u55D2\u5583\u55B1\u55B9\u5588\u5581\u559F\u557E\u55D6\u5591\u557B\u55DF\u55BD\u55BE\u5594\u5599\u55EA\u55F7\u55C9\u561F\u55D1\u55EB\u55EC\u55D4\u55E6\u55DD\u55C4\u55EF\u55E5\u55F2\u55F3\u55CC\u55CD\u55E8\u55F5\u55E4\u8F94\u561E\u5608\u560C\u5601\u5624\u5623\u55FE\u5600\u5627\u562D\u5658\u5639\u5657\u562C\u564D\u5662\u5659\u565C\u564C\u5654\u5686\u5664\u5671\u566B\u567B\u567C\u5685\u5693\u56AF\u56D4\u56D7\u56DD\u56E1\u56F5\u56EB\u56F9\u56FF\u5704\u570A\u5709\u571C\u5E0F\u5E19\u5E14\u5E11\u5E31\u5E3B\u5E3C"],["e140","\u9145\u9147\u9148\u9151\u9153\u9154\u9155\u9156\u9158\u9159\u915B\u915C\u915F\u9160\u9166\u9167\u9168\u916B\u916D\u9173\u917A\u917B\u917C\u9180",4,"\u9186\u9188\u918A\u918E\u918F\u9193",6,"\u919C",5,"\u91A4",5,"\u91AB\u91AC\u91B0\u91B1\u91B2\u91B3\u91B6\u91B7\u91B8\u91B9\u91BB"],["e180","\u91BC",10,"\u91C8\u91CB\u91D0\u91D2",9,"\u91DD",8,"\u5E37\u5E44\u5E54\u5E5B\u5E5E\u5E61\u5C8C\u5C7A\u5C8D\u5C90\u5C96\u5C88\u5C98\u5C99\u5C91\u5C9A\u5C9C\u5CB5\u5CA2\u5CBD\u5CAC\u5CAB\u5CB1\u5CA3\u5CC1\u5CB7\u5CC4\u5CD2\u5CE4\u5CCB\u5CE5\u5D02\u5D03\u5D27\u5D26\u5D2E\u5D24\u5D1E\u5D06\u5D1B\u5D58\u5D3E\u5D34\u5D3D\u5D6C\u5D5B\u5D6F\u5D5D\u5D6B\u5D4B\u5D4A\u5D69\u5D74\u5D82\u5D99\u5D9D\u8C73\u5DB7\u5DC5\u5F73\u5F77\u5F82\u5F87\u5F89\u5F8C\u5F95\u5F99\u5F9C\u5FA8\u5FAD\u5FB5\u5FBC\u8862\u5F61\u72AD\u72B0\u72B4\u72B7\u72B8\u72C3\u72C1\u72CE\u72CD\u72D2\u72E8\u72EF\u72E9\u72F2\u72F4\u72F7\u7301\u72F3\u7303\u72FA"],["e240","\u91E6",62],["e280","\u9225",32,"\u72FB\u7317\u7313\u7321\u730A\u731E\u731D\u7315\u7322\u7339\u7325\u732C\u7338\u7331\u7350\u734D\u7357\u7360\u736C\u736F\u737E\u821B\u5925\u98E7\u5924\u5902\u9963\u9967",5,"\u9974\u9977\u997D\u9980\u9984\u9987\u998A\u998D\u9990\u9991\u9993\u9994\u9995\u5E80\u5E91\u5E8B\u5E96\u5EA5\u5EA0\u5EB9\u5EB5\u5EBE\u5EB3\u8D53\u5ED2\u5ED1\u5EDB\u5EE8\u5EEA\u81BA\u5FC4\u5FC9\u5FD6\u5FCF\u6003\u5FEE\u6004\u5FE1\u5FE4\u5FFE\u6005\u6006\u5FEA\u5FED\u5FF8\u6019\u6035\u6026\u601B\u600F\u600D\u6029\u602B\u600A\u603F\u6021\u6078\u6079\u607B\u607A\u6042"],["e340","\u9246",45,"\u9275",16],["e380","\u9286",7,"\u928F",24,"\u606A\u607D\u6096\u609A\u60AD\u609D\u6083\u6092\u608C\u609B\u60EC\u60BB\u60B1\u60DD\u60D8\u60C6\u60DA\u60B4\u6120\u6126\u6115\u6123\u60F4\u6100\u610E\u612B\u614A\u6175\u61AC\u6194\u61A7\u61B7\u61D4\u61F5\u5FDD\u96B3\u95E9\u95EB\u95F1\u95F3\u95F5\u95F6\u95FC\u95FE\u9603\u9604\u9606\u9608\u960A\u960B\u960C\u960D\u960F\u9612\u9615\u9616\u9617\u9619\u961A\u4E2C\u723F\u6215\u6C35\u6C54\u6C5C\u6C4A\u6CA3\u6C85\u6C90\u6C94\u6C8C\u6C68\u6C69\u6C74\u6C76\u6C86\u6CA9\u6CD0\u6CD4\u6CAD\u6CF7\u6CF8\u6CF1\u6CD7\u6CB2\u6CE0\u6CD6\u6CFA\u6CEB\u6CEE\u6CB1\u6CD3\u6CEF\u6CFE"],["e440","\u92A8",5,"\u92AF",24,"\u92C9",31],["e480","\u92E9",32,"\u6D39\u6D27\u6D0C\u6D43\u6D48\u6D07\u6D04\u6D19\u6D0E\u6D2B\u6D4D\u6D2E\u6D35\u6D1A\u6D4F\u6D52\u6D54\u6D33\u6D91\u6D6F\u6D9E\u6DA0\u6D5E\u6D93\u6D94\u6D5C\u6D60\u6D7C\u6D63\u6E1A\u6DC7\u6DC5\u6DDE\u6E0E\u6DBF\u6DE0\u6E11\u6DE6\u6DDD\u6DD9\u6E16\u6DAB\u6E0C\u6DAE\u6E2B\u6E6E\u6E4E\u6E6B\u6EB2\u6E5F\u6E86\u6E53\u6E54\u6E32\u6E25\u6E44\u6EDF\u6EB1\u6E98\u6EE0\u6F2D\u6EE2\u6EA5\u6EA7\u6EBD\u6EBB\u6EB7\u6ED7\u6EB4\u6ECF\u6E8F\u6EC2\u6E9F\u6F62\u6F46\u6F47\u6F24\u6F15\u6EF9\u6F2F\u6F36\u6F4B\u6F74\u6F2A\u6F09\u6F29\u6F89\u6F8D\u6F8C\u6F78\u6F72\u6F7C\u6F7A\u6FD1"],["e540","\u930A",51,"\u933F",10],["e580","\u934A",31,"\u936B\u6FC9\u6FA7\u6FB9\u6FB6\u6FC2\u6FE1\u6FEE\u6FDE\u6FE0\u6FEF\u701A\u7023\u701B\u7039\u7035\u704F\u705E\u5B80\u5B84\u5B95\u5B93\u5BA5\u5BB8\u752F\u9A9E\u6434\u5BE4\u5BEE\u8930\u5BF0\u8E47\u8B07\u8FB6\u8FD3\u8FD5\u8FE5\u8FEE\u8FE4\u8FE9\u8FE6\u8FF3\u8FE8\u9005\u9004\u900B\u9026\u9011\u900D\u9016\u9021\u9035\u9036\u902D\u902F\u9044\u9051\u9052\u9050\u9068\u9058\u9062\u905B\u66B9\u9074\u907D\u9082\u9088\u9083\u908B\u5F50\u5F57\u5F56\u5F58\u5C3B\u54AB\u5C50\u5C59\u5B71\u5C63\u5C66\u7FBC\u5F2A\u5F29\u5F2D\u8274\u5F3C\u9B3B\u5C6E\u5981\u5983\u598D\u59A9\u59AA\u59A3"],["e640","\u936C",34,"\u9390",27],["e680","\u93AC",29,"\u93CB\u93CC\u93CD\u5997\u59CA\u59AB\u599E\u59A4\u59D2\u59B2\u59AF\u59D7\u59BE\u5A05\u5A06\u59DD\u5A08\u59E3\u59D8\u59F9\u5A0C\u5A09\u5A32\u5A34\u5A11\u5A23\u5A13\u5A40\u5A67\u5A4A\u5A55\u5A3C\u5A62\u5A75\u80EC\u5AAA\u5A9B\u5A77\u5A7A\u5ABE\u5AEB\u5AB2\u5AD2\u5AD4\u5AB8\u5AE0\u5AE3\u5AF1\u5AD6\u5AE6\u5AD8\u5ADC\u5B09\u5B17\u5B16\u5B32\u5B37\u5B40\u5C15\u5C1C\u5B5A\u5B65\u5B73\u5B51\u5B53\u5B62\u9A75\u9A77\u9A78\u9A7A\u9A7F\u9A7D\u9A80\u9A81\u9A85\u9A88\u9A8A\u9A90\u9A92\u9A93\u9A96\u9A98\u9A9B\u9A9C\u9A9D\u9A9F\u9AA0\u9AA2\u9AA3\u9AA5\u9AA7\u7E9F\u7EA1\u7EA3\u7EA5\u7EA8\u7EA9"],["e740","\u93CE",7,"\u93D7",54],["e780","\u940E",32,"\u7EAD\u7EB0\u7EBE\u7EC0\u7EC1\u7EC2\u7EC9\u7ECB\u7ECC\u7ED0\u7ED4\u7ED7\u7EDB\u7EE0\u7EE1\u7EE8\u7EEB\u7EEE\u7EEF\u7EF1\u7EF2\u7F0D\u7EF6\u7EFA\u7EFB\u7EFE\u7F01\u7F02\u7F03\u7F07\u7F08\u7F0B\u7F0C\u7F0F\u7F11\u7F12\u7F17\u7F19\u7F1C\u7F1B\u7F1F\u7F21",6,"\u7F2A\u7F2B\u7F2C\u7F2D\u7F2F",4,"\u7F35\u5E7A\u757F\u5DDB\u753E\u9095\u738E\u7391\u73AE\u73A2\u739F\u73CF\u73C2\u73D1\u73B7\u73B3\u73C0\u73C9\u73C8\u73E5\u73D9\u987C\u740A\u73E9\u73E7\u73DE\u73BA\u73F2\u740F\u742A\u745B\u7426\u7425\u7428\u7430\u742E\u742C"],["e840","\u942F",14,"\u943F",43,"\u946C\u946D\u946E\u946F"],["e880","\u9470",20,"\u9491\u9496\u9498\u94C7\u94CF\u94D3\u94D4\u94DA\u94E6\u94FB\u951C\u9520\u741B\u741A\u7441\u745C\u7457\u7455\u7459\u7477\u746D\u747E\u749C\u748E\u7480\u7481\u7487\u748B\u749E\u74A8\u74A9\u7490\u74A7\u74D2\u74BA\u97EA\u97EB\u97EC\u674C\u6753\u675E\u6748\u6769\u67A5\u6787\u676A\u6773\u6798\u67A7\u6775\u67A8\u679E\u67AD\u678B\u6777\u677C\u67F0\u6809\u67D8\u680A\u67E9\u67B0\u680C\u67D9\u67B5\u67DA\u67B3\u67DD\u6800\u67C3\u67B8\u67E2\u680E\u67C1\u67FD\u6832\u6833\u6860\u6861\u684E\u6862\u6844\u6864\u6883\u681D\u6855\u6866\u6841\u6867\u6840\u683E\u684A\u6849\u6829\u68B5\u688F\u6874\u6877\u6893\u686B\u68C2\u696E\u68FC\u691F\u6920\u68F9"],["e940","\u9527\u9533\u953D\u9543\u9548\u954B\u9555\u955A\u9560\u956E\u9574\u9575\u9577",7,"\u9580",42],["e980","\u95AB",32,"\u6924\u68F0\u690B\u6901\u6957\u68E3\u6910\u6971\u6939\u6960\u6942\u695D\u6984\u696B\u6980\u6998\u6978\u6934\u69CC\u6987\u6988\u69CE\u6989\u6966\u6963\u6979\u699B\u69A7\u69BB\u69AB\u69AD\u69D4\u69B1\u69C1\u69CA\u69DF\u6995\u69E0\u698D\u69FF\u6A2F\u69ED\u6A17\u6A18\u6A65\u69F2\u6A44\u6A3E\u6AA0\u6A50\u6A5B\u6A35\u6A8E\u6A79\u6A3D\u6A28\u6A58\u6A7C\u6A91\u6A90\u6AA9\u6A97\u6AAB\u7337\u7352\u6B81\u6B82\u6B87\u6B84\u6B92\u6B93\u6B8D\u6B9A\u6B9B\u6BA1\u6BAA\u8F6B\u8F6D\u8F71\u8F72\u8F73\u8F75\u8F76\u8F78\u8F77\u8F79\u8F7A\u8F7C\u8F7E\u8F81\u8F82\u8F84\u8F87\u8F8B"],["ea40","\u95CC",27,"\u95EC\u95FF\u9607\u9613\u9618\u961B\u961E\u9620\u9623",6,"\u962B\u962C\u962D\u962F\u9630\u9637\u9638\u9639\u963A\u963E\u9641\u9643\u964A\u964E\u964F\u9651\u9652\u9653\u9656\u9657"],["ea80","\u9658\u9659\u965A\u965C\u965D\u965E\u9660\u9663\u9665\u9666\u966B\u966D",4,"\u9673\u9678",12,"\u9687\u9689\u968A\u8F8D\u8F8E\u8F8F\u8F98\u8F9A\u8ECE\u620B\u6217\u621B\u621F\u6222\u6221\u6225\u6224\u622C\u81E7\u74EF\u74F4\u74FF\u750F\u7511\u7513\u6534\u65EE\u65EF\u65F0\u660A\u6619\u6772\u6603\u6615\u6600\u7085\u66F7\u661D\u6634\u6631\u6636\u6635\u8006\u665F\u6654\u6641\u664F\u6656\u6661\u6657\u6677\u6684\u668C\u66A7\u669D\u66BE\u66DB\u66DC\u66E6\u66E9\u8D32\u8D33\u8D36\u8D3B\u8D3D\u8D40\u8D45\u8D46\u8D48\u8D49\u8D47\u8D4D\u8D55\u8D59\u89C7\u89CA\u89CB\u89CC\u89CE\u89CF\u89D0\u89D1\u726E\u729F\u725D\u7266\u726F\u727E\u727F\u7284\u728B\u728D\u728F\u7292\u6308\u6332\u63B0"],["eb40","\u968C\u968E\u9691\u9692\u9693\u9695\u9696\u969A\u969B\u969D",9,"\u96A8",7,"\u96B1\u96B2\u96B4\u96B5\u96B7\u96B8\u96BA\u96BB\u96BF\u96C2\u96C3\u96C8\u96CA\u96CB\u96D0\u96D1\u96D3\u96D4\u96D6",9,"\u96E1",6,"\u96EB"],["eb80","\u96EC\u96ED\u96EE\u96F0\u96F1\u96F2\u96F4\u96F5\u96F8\u96FA\u96FB\u96FC\u96FD\u96FF\u9702\u9703\u9705\u970A\u970B\u970C\u9710\u9711\u9712\u9714\u9715\u9717",4,"\u971D\u971F\u9720\u643F\u64D8\u8004\u6BEA\u6BF3\u6BFD\u6BF5\u6BF9\u6C05\u6C07\u6C06\u6C0D\u6C15\u6C18\u6C19\u6C1A\u6C21\u6C29\u6C24\u6C2A\u6C32\u6535\u6555\u656B\u724D\u7252\u7256\u7230\u8662\u5216\u809F\u809C\u8093\u80BC\u670A\u80BD\u80B1\u80AB\u80AD\u80B4\u80B7\u80E7\u80E8\u80E9\u80EA\u80DB\u80C2\u80C4\u80D9\u80CD\u80D7\u6710\u80DD\u80EB\u80F1\u80F4\u80ED\u810D\u810E\u80F2\u80FC\u6715\u8112\u8C5A\u8136\u811E\u812C\u8118\u8132\u8148\u814C\u8153\u8174\u8159\u815A\u8171\u8160\u8169\u817C\u817D\u816D\u8167\u584D\u5AB5\u8188\u8182\u8191\u6ED5\u81A3\u81AA\u81CC\u6726\u81CA\u81BB"],["ec40","\u9721",8,"\u972B\u972C\u972E\u972F\u9731\u9733",4,"\u973A\u973B\u973C\u973D\u973F",18,"\u9754\u9755\u9757\u9758\u975A\u975C\u975D\u975F\u9763\u9764\u9766\u9767\u9768\u976A",7],["ec80","\u9772\u9775\u9777",4,"\u977D",7,"\u9786",4,"\u978C\u978E\u978F\u9790\u9793\u9795\u9796\u9797\u9799",4,"\u81C1\u81A6\u6B24\u6B37\u6B39\u6B43\u6B46\u6B59\u98D1\u98D2\u98D3\u98D5\u98D9\u98DA\u6BB3\u5F40\u6BC2\u89F3\u6590\u9F51\u6593\u65BC\u65C6\u65C4\u65C3\u65CC\u65CE\u65D2\u65D6\u7080\u709C\u7096\u709D\u70BB\u70C0\u70B7\u70AB\u70B1\u70E8\u70CA\u7110\u7113\u7116\u712F\u7131\u7173\u715C\u7168\u7145\u7172\u714A\u7178\u717A\u7198\u71B3\u71B5\u71A8\u71A0\u71E0\u71D4\u71E7\u71F9\u721D\u7228\u706C\u7118\u7166\u71B9\u623E\u623D\u6243\u6248\u6249\u793B\u7940\u7946\u7949\u795B\u795C\u7953\u795A\u7962\u7957\u7960\u796F\u7967\u797A\u7985\u798A\u799A\u79A7\u79B3\u5FD1\u5FD0"],["ed40","\u979E\u979F\u97A1\u97A2\u97A4",6,"\u97AC\u97AE\u97B0\u97B1\u97B3\u97B5",46],["ed80","\u97E4\u97E5\u97E8\u97EE",4,"\u97F4\u97F7",23,"\u603C\u605D\u605A\u6067\u6041\u6059\u6063\u60AB\u6106\u610D\u615D\u61A9\u619D\u61CB\u61D1\u6206\u8080\u807F\u6C93\u6CF6\u6DFC\u77F6\u77F8\u7800\u7809\u7817\u7818\u7811\u65AB\u782D\u781C\u781D\u7839\u783A\u783B\u781F\u783C\u7825\u782C\u7823\u7829\u784E\u786D\u7856\u7857\u7826\u7850\u7847\u784C\u786A\u789B\u7893\u789A\u7887\u789C\u78A1\u78A3\u78B2\u78B9\u78A5\u78D4\u78D9\u78C9\u78EC\u78F2\u7905\u78F4\u7913\u7924\u791E\u7934\u9F9B\u9EF9\u9EFB\u9EFC\u76F1\u7704\u770D\u76F9\u7707\u7708\u771A\u7722\u7719\u772D\u7726\u7735\u7738\u7750\u7751\u7747\u7743\u775A\u7768"],["ee40","\u980F",62],["ee80","\u984E",32,"\u7762\u7765\u777F\u778D\u777D\u7780\u778C\u7791\u779F\u77A0\u77B0\u77B5\u77BD\u753A\u7540\u754E\u754B\u7548\u755B\u7572\u7579\u7583\u7F58\u7F61\u7F5F\u8A48\u7F68\u7F74\u7F71\u7F79\u7F81\u7F7E\u76CD\u76E5\u8832\u9485\u9486\u9487\u948B\u948A\u948C\u948D\u948F\u9490\u9494\u9497\u9495\u949A\u949B\u949C\u94A3\u94A4\u94AB\u94AA\u94AD\u94AC\u94AF\u94B0\u94B2\u94B4\u94B6",4,"\u94BC\u94BD\u94BF\u94C4\u94C8",6,"\u94D0\u94D1\u94D2\u94D5\u94D6\u94D7\u94D9\u94D8\u94DB\u94DE\u94DF\u94E0\u94E2\u94E4\u94E5\u94E7\u94E8\u94EA"],["ef40","\u986F",5,"\u988B\u988E\u9892\u9895\u9899\u98A3\u98A8",37,"\u98CF\u98D0\u98D4\u98D6\u98D7\u98DB\u98DC\u98DD\u98E0",4],["ef80","\u98E5\u98E6\u98E9",30,"\u94E9\u94EB\u94EE\u94EF\u94F3\u94F4\u94F5\u94F7\u94F9\u94FC\u94FD\u94FF\u9503\u9502\u9506\u9507\u9509\u950A\u950D\u950E\u950F\u9512",4,"\u9518\u951B\u951D\u951E\u951F\u9522\u952A\u952B\u9529\u952C\u9531\u9532\u9534\u9536\u9537\u9538\u953C\u953E\u953F\u9542\u9535\u9544\u9545\u9546\u9549\u954C\u954E\u954F\u9552\u9553\u9554\u9556\u9557\u9558\u9559\u955B\u955E\u955F\u955D\u9561\u9562\u9564",8,"\u956F\u9571\u9572\u9573\u953A\u77E7\u77EC\u96C9\u79D5\u79ED\u79E3\u79EB\u7A06\u5D47\u7A03\u7A02\u7A1E\u7A14"],["f040","\u9908",4,"\u990E\u990F\u9911",28,"\u992F",26],["f080","\u994A",9,"\u9956",12,"\u9964\u9966\u9973\u9978\u9979\u997B\u997E\u9982\u9983\u9989\u7A39\u7A37\u7A51\u9ECF\u99A5\u7A70\u7688\u768E\u7693\u7699\u76A4\u74DE\u74E0\u752C\u9E20\u9E22\u9E28",4,"\u9E32\u9E31\u9E36\u9E38\u9E37\u9E39\u9E3A\u9E3E\u9E41\u9E42\u9E44\u9E46\u9E47\u9E48\u9E49\u9E4B\u9E4C\u9E4E\u9E51\u9E55\u9E57\u9E5A\u9E5B\u9E5C\u9E5E\u9E63\u9E66",6,"\u9E71\u9E6D\u9E73\u7592\u7594\u7596\u75A0\u759D\u75AC\u75A3\u75B3\u75B4\u75B8\u75C4\u75B1\u75B0\u75C3\u75C2\u75D6\u75CD\u75E3\u75E8\u75E6\u75E4\u75EB\u75E7\u7603\u75F1\u75FC\u75FF\u7610\u7600\u7605\u760C\u7617\u760A\u7625\u7618\u7615\u7619"],["f140","\u998C\u998E\u999A",10,"\u99A6\u99A7\u99A9",47],["f180","\u99D9",32,"\u761B\u763C\u7622\u7620\u7640\u762D\u7630\u763F\u7635\u7643\u763E\u7633\u764D\u765E\u7654\u765C\u7656\u766B\u766F\u7FCA\u7AE6\u7A78\u7A79\u7A80\u7A86\u7A88\u7A95\u7AA6\u7AA0\u7AAC\u7AA8\u7AAD\u7AB3\u8864\u8869\u8872\u887D\u887F\u8882\u88A2\u88C6\u88B7\u88BC\u88C9\u88E2\u88CE\u88E3\u88E5\u88F1\u891A\u88FC\u88E8\u88FE\u88F0\u8921\u8919\u8913\u891B\u890A\u8934\u892B\u8936\u8941\u8966\u897B\u758B\u80E5\u76B2\u76B4\u77DC\u8012\u8014\u8016\u801C\u8020\u8022\u8025\u8026\u8027\u8029\u8028\u8031\u800B\u8035\u8043\u8046\u804D\u8052\u8069\u8071\u8983\u9878\u9880\u9883"],["f240","\u99FA",62],["f280","\u9A39",32,"\u9889\u988C\u988D\u988F\u9894\u989A\u989B\u989E\u989F\u98A1\u98A2\u98A5\u98A6\u864D\u8654\u866C\u866E\u867F\u867A\u867C\u867B\u86A8\u868D\u868B\u86AC\u869D\u86A7\u86A3\u86AA\u8693\u86A9\u86B6\u86C4\u86B5\u86CE\u86B0\u86BA\u86B1\u86AF\u86C9\u86CF\u86B4\u86E9\u86F1\u86F2\u86ED\u86F3\u86D0\u8713\u86DE\u86F4\u86DF\u86D8\u86D1\u8703\u8707\u86F8\u8708\u870A\u870D\u8709\u8723\u873B\u871E\u8725\u872E\u871A\u873E\u8748\u8734\u8731\u8729\u8737\u873F\u8782\u8722\u877D\u877E\u877B\u8760\u8770\u874C\u876E\u878B\u8753\u8763\u877C\u8764\u8759\u8765\u8793\u87AF\u87A8\u87D2"],["f340","\u9A5A",17,"\u9A72\u9A83\u9A89\u9A8D\u9A8E\u9A94\u9A95\u9A99\u9AA6\u9AA9",6,"\u9AB2\u9AB3\u9AB4\u9AB5\u9AB9\u9ABB\u9ABD\u9ABE\u9ABF\u9AC3\u9AC4\u9AC6",4,"\u9ACD\u9ACE\u9ACF\u9AD0\u9AD2\u9AD4\u9AD5\u9AD6\u9AD7\u9AD9\u9ADA\u9ADB\u9ADC"],["f380","\u9ADD\u9ADE\u9AE0\u9AE2\u9AE3\u9AE4\u9AE5\u9AE7\u9AE8\u9AE9\u9AEA\u9AEC\u9AEE\u9AF0",8,"\u9AFA\u9AFC",6,"\u9B04\u9B05\u9B06\u87C6\u8788\u8785\u87AD\u8797\u8783\u87AB\u87E5\u87AC\u87B5\u87B3\u87CB\u87D3\u87BD\u87D1\u87C0\u87CA\u87DB\u87EA\u87E0\u87EE\u8816\u8813\u87FE\u880A\u881B\u8821\u8839\u883C\u7F36\u7F42\u7F44\u7F45\u8210\u7AFA\u7AFD\u7B08\u7B03\u7B04\u7B15\u7B0A\u7B2B\u7B0F\u7B47\u7B38\u7B2A\u7B19\u7B2E\u7B31\u7B20\u7B25\u7B24\u7B33\u7B3E\u7B1E\u7B58\u7B5A\u7B45\u7B75\u7B4C\u7B5D\u7B60\u7B6E\u7B7B\u7B62\u7B72\u7B71\u7B90\u7BA6\u7BA7\u7BB8\u7BAC\u7B9D\u7BA8\u7B85\u7BAA\u7B9C\u7BA2\u7BAB\u7BB4\u7BD1\u7BC1\u7BCC\u7BDD\u7BDA\u7BE5\u7BE6\u7BEA\u7C0C\u7BFE\u7BFC\u7C0F\u7C16\u7C0B"],["f440","\u9B07\u9B09",5,"\u9B10\u9B11\u9B12\u9B14",10,"\u9B20\u9B21\u9B22\u9B24",10,"\u9B30\u9B31\u9B33",7,"\u9B3D\u9B3E\u9B3F\u9B40\u9B46\u9B4A\u9B4B\u9B4C\u9B4E\u9B50\u9B52\u9B53\u9B55",5],["f480","\u9B5B",32,"\u7C1F\u7C2A\u7C26\u7C38\u7C41\u7C40\u81FE\u8201\u8202\u8204\u81EC\u8844\u8221\u8222\u8223\u822D\u822F\u8228\u822B\u8238\u823B\u8233\u8234\u823E\u8244\u8249\u824B\u824F\u825A\u825F\u8268\u887E\u8885\u8888\u88D8\u88DF\u895E\u7F9D\u7F9F\u7FA7\u7FAF\u7FB0\u7FB2\u7C7C\u6549\u7C91\u7C9D\u7C9C\u7C9E\u7CA2\u7CB2\u7CBC\u7CBD\u7CC1\u7CC7\u7CCC\u7CCD\u7CC8\u7CC5\u7CD7\u7CE8\u826E\u66A8\u7FBF\u7FCE\u7FD5\u7FE5\u7FE1\u7FE6\u7FE9\u7FEE\u7FF3\u7CF8\u7D77\u7DA6\u7DAE\u7E47\u7E9B\u9EB8\u9EB4\u8D73\u8D84\u8D94\u8D91\u8DB1\u8D67\u8D6D\u8C47\u8C49\u914A\u9150\u914E\u914F\u9164"],["f540","\u9B7C",62],["f580","\u9BBB",32,"\u9162\u9161\u9170\u9169\u916F\u917D\u917E\u9172\u9174\u9179\u918C\u9185\u9190\u918D\u9191\u91A2\u91A3\u91AA\u91AD\u91AE\u91AF\u91B5\u91B4\u91BA\u8C55\u9E7E\u8DB8\u8DEB\u8E05\u8E59\u8E69\u8DB5\u8DBF\u8DBC\u8DBA\u8DC4\u8DD6\u8DD7\u8DDA\u8DDE\u8DCE\u8DCF\u8DDB\u8DC6\u8DEC\u8DF7\u8DF8\u8DE3\u8DF9\u8DFB\u8DE4\u8E09\u8DFD\u8E14\u8E1D\u8E1F\u8E2C\u8E2E\u8E23\u8E2F\u8E3A\u8E40\u8E39\u8E35\u8E3D\u8E31\u8E49\u8E41\u8E42\u8E51\u8E52\u8E4A\u8E70\u8E76\u8E7C\u8E6F\u8E74\u8E85\u8E8F\u8E94\u8E90\u8E9C\u8E9E\u8C78\u8C82\u8C8A\u8C85\u8C98\u8C94\u659B\u89D6\u89DE\u89DA\u89DC"],["f640","\u9BDC",62],["f680","\u9C1B",32,"\u89E5\u89EB\u89EF\u8A3E\u8B26\u9753\u96E9\u96F3\u96EF\u9706\u9701\u9708\u970F\u970E\u972A\u972D\u9730\u973E\u9F80\u9F83\u9F85",5,"\u9F8C\u9EFE\u9F0B\u9F0D\u96B9\u96BC\u96BD\u96CE\u96D2\u77BF\u96E0\u928E\u92AE\u92C8\u933E\u936A\u93CA\u938F\u943E\u946B\u9C7F\u9C82\u9C85\u9C86\u9C87\u9C88\u7A23\u9C8B\u9C8E\u9C90\u9C91\u9C92\u9C94\u9C95\u9C9A\u9C9B\u9C9E",5,"\u9CA5",4,"\u9CAB\u9CAD\u9CAE\u9CB0",7,"\u9CBA\u9CBB\u9CBC\u9CBD\u9CC4\u9CC5\u9CC6\u9CC7\u9CCA\u9CCB"],["f740","\u9C3C",62],["f780","\u9C7B\u9C7D\u9C7E\u9C80\u9C83\u9C84\u9C89\u9C8A\u9C8C\u9C8F\u9C93\u9C96\u9C97\u9C98\u9C99\u9C9D\u9CAA\u9CAC\u9CAF\u9CB9\u9CBE",4,"\u9CC8\u9CC9\u9CD1\u9CD2\u9CDA\u9CDB\u9CE0\u9CE1\u9CCC",4,"\u9CD3\u9CD4\u9CD5\u9CD7\u9CD8\u9CD9\u9CDC\u9CDD\u9CDF\u9CE2\u977C\u9785\u9791\u9792\u9794\u97AF\u97AB\u97A3\u97B2\u97B4\u9AB1\u9AB0\u9AB7\u9E58\u9AB6\u9ABA\u9ABC\u9AC1\u9AC0\u9AC5\u9AC2\u9ACB\u9ACC\u9AD1\u9B45\u9B43\u9B47\u9B49\u9B48\u9B4D\u9B51\u98E8\u990D\u992E\u9955\u9954\u9ADF\u9AE1\u9AE6\u9AEF\u9AEB\u9AFB\u9AED\u9AF9\u9B08\u9B0F\u9B13\u9B1F\u9B23\u9EBD\u9EBE\u7E3B\u9E82\u9E87\u9E88\u9E8B\u9E92\u93D6\u9E9D\u9E9F\u9EDB\u9EDC\u9EDD\u9EE0\u9EDF\u9EE2\u9EE9\u9EE7\u9EE5\u9EEA\u9EEF\u9F22\u9F2C\u9F2F\u9F39\u9F37\u9F3D\u9F3E\u9F44"],["f840","\u9CE3",62],["f880","\u9D22",32],["f940","\u9D43",62],["f980","\u9D82",32],["fa40","\u9DA3",62],["fa80","\u9DE2",32],["fb40","\u9E03",27,"\u9E24\u9E27\u9E2E\u9E30\u9E34\u9E3B\u9E3C\u9E40\u9E4D\u9E50\u9E52\u9E53\u9E54\u9E56\u9E59\u9E5D\u9E5F\u9E60\u9E61\u9E62\u9E65\u9E6E\u9E6F\u9E72\u9E74",9,"\u9E80"],["fb80","\u9E81\u9E83\u9E84\u9E85\u9E86\u9E89\u9E8A\u9E8C",5,"\u9E94",8,"\u9E9E\u9EA0",5,"\u9EA7\u9EA8\u9EA9\u9EAA"],["fc40","\u9EAB",8,"\u9EB5\u9EB6\u9EB7\u9EB9\u9EBA\u9EBC\u9EBF",4,"\u9EC5\u9EC6\u9EC7\u9EC8\u9ECA\u9ECB\u9ECC\u9ED0\u9ED2\u9ED3\u9ED5\u9ED6\u9ED7\u9ED9\u9EDA\u9EDE\u9EE1\u9EE3\u9EE4\u9EE6\u9EE8\u9EEB\u9EEC\u9EED\u9EEE\u9EF0",8,"\u9EFA\u9EFD\u9EFF",6],["fc80","\u9F06",4,"\u9F0C\u9F0F\u9F11\u9F12\u9F14\u9F15\u9F16\u9F18\u9F1A",5,"\u9F21\u9F23",8,"\u9F2D\u9F2E\u9F30\u9F31"],["fd40","\u9F32",4,"\u9F38\u9F3A\u9F3C\u9F3F",4,"\u9F45",10,"\u9F52",38],["fd80","\u9F79",5,"\u9F81\u9F82\u9F8D",11,"\u9F9C\u9F9D\u9F9E\u9FA1",4,"\uF92C\uF979\uF995\uF9E7\uF9F1"],["fe40","\uFA0C\uFA0D\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA18\uFA1F\uFA20\uFA21\uFA23\uFA24\uFA27\uFA28\uFA29"]]});var bK=S((Ckr,$qt)=>{$qt.exports=[["a140","\uE4C6",62],["a180","\uE505",32],["a240","\uE526",62],["a280","\uE565",32],["a2ab","\uE766",5],["a2e3","\u20AC\uE76D"],["a2ef","\uE76E\uE76F"],["a2fd","\uE770\uE771"],["a340","\uE586",62],["a380","\uE5C5",31,"\u3000"],["a440","\uE5E6",62],["a480","\uE625",32],["a4f4","\uE772",10],["a540","\uE646",62],["a580","\uE685",32],["a5f7","\uE77D",7],["a640","\uE6A6",62],["a680","\uE6E5",32],["a6b9","\uE785",7],["a6d9","\uE78D",6],["a6ec","\uE794\uE795"],["a6f3","\uE796"],["a6f6","\uE797",8],["a740","\uE706",62],["a780","\uE745",32],["a7c2","\uE7A0",14],["a7f2","\uE7AF",12],["a896","\uE7BC",10],["a8bc","\u1E3F"],["a8bf","\u01F9"],["a8c1","\uE7C9\uE7CA\uE7CB\uE7CC"],["a8ea","\uE7CD",20],["a958","\uE7E2"],["a95b","\uE7E3"],["a95d","\uE7E4\uE7E5\uE7E6"],["a989","\u303E\u2FF0",11],["a997","\uE7F4",12],["a9f0","\uE801",14],["aaa1","\uE000",93],["aba1","\uE05E",93],["aca1","\uE0BC",93],["ada1","\uE11A",93],["aea1","\uE178",93],["afa1","\uE1D6",93],["d7fa","\uE810",4],["f8a1","\uE234",93],["f9a1","\uE292",93],["faa1","\uE2F0",93],["fba1","\uE34E",93],["fca1","\uE3AC",93],["fda1","\uE40A",93],["fe50","\u2E81\uE816\uE817\uE818\u2E84\u3473\u3447\u2E88\u2E8B\uE81E\u359E\u361A\u360E\u2E8C\u2E97\u396E\u3918\uE826\u39CF\u39DF\u3A73\u39D0\uE82B\uE82C\u3B4E\u3C6E\u3CE0\u2EA7\uE831\uE832\u2EAA\u4056\u415F\u2EAE\u4337\u2EB3\u2EB6\u2EB7\uE83B\u43B1\u43AC\u2EBB\u43DD\u44D6\u4661\u464C\uE843"],["fe80","\u4723\u4729\u477C\u478D\u2ECA\u4947\u497A\u497D\u4982\u4983\u4985\u4986\u499F\u499B\u49B7\u49B6\uE854\uE855\u4CA3\u4C9F\u4CA0\u4CA1\u4C77\u4CA2\u4D13",6,"\u4DAE\uE864\uE468",93],["8135f437","\uE7C7"]]});var Wje=S(($kr,kqt)=>{kqt.exports={uChars:[128,165,169,178,184,216,226,235,238,244,248,251,253,258,276,284,300,325,329,334,364,463,465,467,469,471,473,475,477,506,594,610,712,716,730,930,938,962,970,1026,1104,1106,8209,8215,8218,8222,8231,8241,8244,8246,8252,8365,8452,8454,8458,8471,8482,8556,8570,8596,8602,8713,8720,8722,8726,8731,8737,8740,8742,8748,8751,8760,8766,8777,8781,8787,8802,8808,8816,8854,8858,8870,8896,8979,9322,9372,9548,9588,9616,9622,9634,9652,9662,9672,9676,9680,9702,9735,9738,9793,9795,11906,11909,11913,11917,11928,11944,11947,11951,11956,11960,11964,11979,12284,12292,12312,12319,12330,12351,12436,12447,12535,12543,12586,12842,12850,12964,13200,13215,13218,13253,13263,13267,13270,13384,13428,13727,13839,13851,14617,14703,14801,14816,14964,15183,15471,15585,16471,16736,17208,17325,17330,17374,17623,17997,18018,18212,18218,18301,18318,18760,18811,18814,18820,18823,18844,18848,18872,19576,19620,19738,19887,40870,59244,59336,59367,59413,59417,59423,59431,59437,59443,59452,59460,59478,59493,63789,63866,63894,63976,63986,64016,64018,64021,64025,64034,64037,64042,65074,65093,65107,65112,65127,65132,65375,65510,65536],gbChars:[0,36,38,45,50,81,89,95,96,100,103,104,105,109,126,133,148,172,175,179,208,306,307,308,309,310,311,312,313,341,428,443,544,545,558,741,742,749,750,805,819,820,7922,7924,7925,7927,7934,7943,7944,7945,7950,8062,8148,8149,8152,8164,8174,8236,8240,8262,8264,8374,8380,8381,8384,8388,8390,8392,8393,8394,8396,8401,8406,8416,8419,8424,8437,8439,8445,8482,8485,8496,8521,8603,8936,8946,9046,9050,9063,9066,9076,9092,9100,9108,9111,9113,9131,9162,9164,9218,9219,11329,11331,11334,11336,11346,11361,11363,11366,11370,11372,11375,11389,11682,11686,11687,11692,11694,11714,11716,11723,11725,11730,11736,11982,11989,12102,12336,12348,12350,12384,12393,12395,12397,12510,12553,12851,12962,12973,13738,13823,13919,13933,14080,14298,14585,14698,15583,15847,16318,16434,16438,16481,16729,17102,17122,17315,17320,17402,17418,17859,17909,17911,17915,17916,17936,17939,17961,18664,18703,18814,18962,19043,33469,33470,33471,33484,33485,33490,33497,33501,33505,33513,33520,33536,33550,37845,37921,37948,38029,38038,38064,38065,38066,38069,38075,38076,38078,39108,39109,39113,39114,39115,39116,39265,39394,189e3]}});var Kje=S((kkr,Mqt)=>{Mqt.exports=[["0","\0",127],["8141","\uAC02\uAC03\uAC05\uAC06\uAC0B",4,"\uAC18\uAC1E\uAC1F\uAC21\uAC22\uAC23\uAC25",6,"\uAC2E\uAC32\uAC33\uAC34"],["8161","\uAC35\uAC36\uAC37\uAC3A\uAC3B\uAC3D\uAC3E\uAC3F\uAC41",9,"\uAC4C\uAC4E",5,"\uAC55"],["8181","\uAC56\uAC57\uAC59\uAC5A\uAC5B\uAC5D",18,"\uAC72\uAC73\uAC75\uAC76\uAC79\uAC7B",4,"\uAC82\uAC87\uAC88\uAC8D\uAC8E\uAC8F\uAC91\uAC92\uAC93\uAC95",6,"\uAC9E\uACA2",5,"\uACAB\uACAD\uACAE\uACB1",6,"\uACBA\uACBE\uACBF\uACC0\uACC2\uACC3\uACC5\uACC6\uACC7\uACC9\uACCA\uACCB\uACCD",7,"\uACD6\uACD8",7,"\uACE2\uACE3\uACE5\uACE6\uACE9\uACEB\uACED\uACEE\uACF2\uACF4\uACF7",4,"\uACFE\uACFF\uAD01\uAD02\uAD03\uAD05\uAD07",4,"\uAD0E\uAD10\uAD12\uAD13"],["8241","\uAD14\uAD15\uAD16\uAD17\uAD19\uAD1A\uAD1B\uAD1D\uAD1E\uAD1F\uAD21",7,"\uAD2A\uAD2B\uAD2E",5],["8261","\uAD36\uAD37\uAD39\uAD3A\uAD3B\uAD3D",6,"\uAD46\uAD48\uAD4A",5,"\uAD51\uAD52\uAD53\uAD55\uAD56\uAD57"],["8281","\uAD59",7,"\uAD62\uAD64",7,"\uAD6E\uAD6F\uAD71\uAD72\uAD77\uAD78\uAD79\uAD7A\uAD7E\uAD80\uAD83",4,"\uAD8A\uAD8B\uAD8D\uAD8E\uAD8F\uAD91",10,"\uAD9E",5,"\uADA5",17,"\uADB8",7,"\uADC2\uADC3\uADC5\uADC6\uADC7\uADC9",6,"\uADD2\uADD4",7,"\uADDD\uADDE\uADDF\uADE1\uADE2\uADE3\uADE5",18],["8341","\uADFA\uADFB\uADFD\uADFE\uAE02",5,"\uAE0A\uAE0C\uAE0E",5,"\uAE15",7],["8361","\uAE1D",18,"\uAE32\uAE33\uAE35\uAE36\uAE39\uAE3B\uAE3C"],["8381","\uAE3D\uAE3E\uAE3F\uAE42\uAE44\uAE47\uAE48\uAE49\uAE4B\uAE4F\uAE51\uAE52\uAE53\uAE55\uAE57",4,"\uAE5E\uAE62\uAE63\uAE64\uAE66\uAE67\uAE6A\uAE6B\uAE6D\uAE6E\uAE6F\uAE71",6,"\uAE7A\uAE7E",5,"\uAE86",5,"\uAE8D",46,"\uAEBF\uAEC1\uAEC2\uAEC3\uAEC5",6,"\uAECE\uAED2",5,"\uAEDA\uAEDB\uAEDD",8],["8441","\uAEE6\uAEE7\uAEE9\uAEEA\uAEEC\uAEEE",5,"\uAEF5\uAEF6\uAEF7\uAEF9\uAEFA\uAEFB\uAEFD",8],["8461","\uAF06\uAF09\uAF0A\uAF0B\uAF0C\uAF0E\uAF0F\uAF11",18],["8481","\uAF24",7,"\uAF2E\uAF2F\uAF31\uAF33\uAF35",6,"\uAF3E\uAF40\uAF44\uAF45\uAF46\uAF47\uAF4A",5,"\uAF51",10,"\uAF5E",5,"\uAF66",18,"\uAF7A",5,"\uAF81\uAF82\uAF83\uAF85\uAF86\uAF87\uAF89",6,"\uAF92\uAF93\uAF94\uAF96",5,"\uAF9D",26,"\uAFBA\uAFBB\uAFBD\uAFBE"],["8541","\uAFBF\uAFC1",5,"\uAFCA\uAFCC\uAFCF",4,"\uAFD5",6,"\uAFDD",4],["8561","\uAFE2",5,"\uAFEA",5,"\uAFF2\uAFF3\uAFF5\uAFF6\uAFF7\uAFF9",6,"\uB002\uB003"],["8581","\uB005",6,"\uB00D\uB00E\uB00F\uB011\uB012\uB013\uB015",6,"\uB01E",9,"\uB029",26,"\uB046\uB047\uB049\uB04B\uB04D\uB04F\uB050\uB051\uB052\uB056\uB058\uB05A\uB05B\uB05C\uB05E",29,"\uB07E\uB07F\uB081\uB082\uB083\uB085",6,"\uB08E\uB090\uB092",5,"\uB09B\uB09D\uB09E\uB0A3\uB0A4"],["8641","\uB0A5\uB0A6\uB0A7\uB0AA\uB0B0\uB0B2\uB0B6\uB0B7\uB0B9\uB0BA\uB0BB\uB0BD",6,"\uB0C6\uB0CA",5,"\uB0D2"],["8661","\uB0D3\uB0D5\uB0D6\uB0D7\uB0D9",6,"\uB0E1\uB0E2\uB0E3\uB0E4\uB0E6",10],["8681","\uB0F1",22,"\uB10A\uB10D\uB10E\uB10F\uB111\uB114\uB115\uB116\uB117\uB11A\uB11E",4,"\uB126\uB127\uB129\uB12A\uB12B\uB12D",6,"\uB136\uB13A",5,"\uB142\uB143\uB145\uB146\uB147\uB149",6,"\uB152\uB153\uB156\uB157\uB159\uB15A\uB15B\uB15D\uB15E\uB15F\uB161",22,"\uB17A\uB17B\uB17D\uB17E\uB17F\uB181\uB183",4,"\uB18A\uB18C\uB18E\uB18F\uB190\uB191\uB195\uB196\uB197\uB199\uB19A\uB19B\uB19D"],["8741","\uB19E",9,"\uB1A9",15],["8761","\uB1B9",18,"\uB1CD\uB1CE\uB1CF\uB1D1\uB1D2\uB1D3\uB1D5"],["8781","\uB1D6",5,"\uB1DE\uB1E0",7,"\uB1EA\uB1EB\uB1ED\uB1EE\uB1EF\uB1F1",7,"\uB1FA\uB1FC\uB1FE",5,"\uB206\uB207\uB209\uB20A\uB20D",6,"\uB216\uB218\uB21A",5,"\uB221",18,"\uB235",6,"\uB23D",26,"\uB259\uB25A\uB25B\uB25D\uB25E\uB25F\uB261",6,"\uB26A",4],["8841","\uB26F",4,"\uB276",5,"\uB27D",6,"\uB286\uB287\uB288\uB28A",4],["8861","\uB28F\uB292\uB293\uB295\uB296\uB297\uB29B",4,"\uB2A2\uB2A4\uB2A7\uB2A8\uB2A9\uB2AB\uB2AD\uB2AE\uB2AF\uB2B1\uB2B2\uB2B3\uB2B5\uB2B6\uB2B7"],["8881","\uB2B8",15,"\uB2CA\uB2CB\uB2CD\uB2CE\uB2CF\uB2D1\uB2D3",4,"\uB2DA\uB2DC\uB2DE\uB2DF\uB2E0\uB2E1\uB2E3\uB2E7\uB2E9\uB2EA\uB2F0\uB2F1\uB2F2\uB2F6\uB2FC\uB2FD\uB2FE\uB302\uB303\uB305\uB306\uB307\uB309",6,"\uB312\uB316",5,"\uB31D",54,"\uB357\uB359\uB35A\uB35D\uB360\uB361\uB362\uB363"],["8941","\uB366\uB368\uB36A\uB36C\uB36D\uB36F\uB372\uB373\uB375\uB376\uB377\uB379",6,"\uB382\uB386",5,"\uB38D"],["8961","\uB38E\uB38F\uB391\uB392\uB393\uB395",10,"\uB3A2",5,"\uB3A9\uB3AA\uB3AB\uB3AD"],["8981","\uB3AE",21,"\uB3C6\uB3C7\uB3C9\uB3CA\uB3CD\uB3CF\uB3D1\uB3D2\uB3D3\uB3D6\uB3D8\uB3DA\uB3DC\uB3DE\uB3DF\uB3E1\uB3E2\uB3E3\uB3E5\uB3E6\uB3E7\uB3E9",18,"\uB3FD",18,"\uB411",6,"\uB419\uB41A\uB41B\uB41D\uB41E\uB41F\uB421",6,"\uB42A\uB42C",7,"\uB435",15],["8a41","\uB445",10,"\uB452\uB453\uB455\uB456\uB457\uB459",6,"\uB462\uB464\uB466"],["8a61","\uB467",4,"\uB46D",18,"\uB481\uB482"],["8a81","\uB483",4,"\uB489",19,"\uB49E",5,"\uB4A5\uB4A6\uB4A7\uB4A9\uB4AA\uB4AB\uB4AD",7,"\uB4B6\uB4B8\uB4BA",5,"\uB4C1\uB4C2\uB4C3\uB4C5\uB4C6\uB4C7\uB4C9",6,"\uB4D1\uB4D2\uB4D3\uB4D4\uB4D6",5,"\uB4DE\uB4DF\uB4E1\uB4E2\uB4E5\uB4E7",4,"\uB4EE\uB4F0\uB4F2",5,"\uB4F9",26,"\uB516\uB517\uB519\uB51A\uB51D"],["8b41","\uB51E",5,"\uB526\uB52B",4,"\uB532\uB533\uB535\uB536\uB537\uB539",6,"\uB542\uB546"],["8b61","\uB547\uB548\uB549\uB54A\uB54E\uB54F\uB551\uB552\uB553\uB555",6,"\uB55E\uB562",8],["8b81","\uB56B",52,"\uB5A2\uB5A3\uB5A5\uB5A6\uB5A7\uB5A9\uB5AC\uB5AD\uB5AE\uB5AF\uB5B2\uB5B6",4,"\uB5BE\uB5BF\uB5C1\uB5C2\uB5C3\uB5C5",6,"\uB5CE\uB5D2",5,"\uB5D9",18,"\uB5ED",18],["8c41","\uB600",15,"\uB612\uB613\uB615\uB616\uB617\uB619",4],["8c61","\uB61E",6,"\uB626",5,"\uB62D",6,"\uB635",5],["8c81","\uB63B",12,"\uB649",26,"\uB665\uB666\uB667\uB669",50,"\uB69E\uB69F\uB6A1\uB6A2\uB6A3\uB6A5",5,"\uB6AD\uB6AE\uB6AF\uB6B0\uB6B2",16],["8d41","\uB6C3",16,"\uB6D5",8],["8d61","\uB6DE",17,"\uB6F1\uB6F2\uB6F3\uB6F5\uB6F6\uB6F7\uB6F9\uB6FA"],["8d81","\uB6FB",4,"\uB702\uB703\uB704\uB706",33,"\uB72A\uB72B\uB72D\uB72E\uB731",6,"\uB73A\uB73C",7,"\uB745\uB746\uB747\uB749\uB74A\uB74B\uB74D",6,"\uB756",9,"\uB761\uB762\uB763\uB765\uB766\uB767\uB769",6,"\uB772\uB774\uB776",5,"\uB77E\uB77F\uB781\uB782\uB783\uB785",6,"\uB78E\uB793\uB794\uB795\uB79A\uB79B\uB79D\uB79E"],["8e41","\uB79F\uB7A1",6,"\uB7AA\uB7AE",5,"\uB7B6\uB7B7\uB7B9",8],["8e61","\uB7C2",4,"\uB7C8\uB7CA",19],["8e81","\uB7DE",13,"\uB7EE\uB7EF\uB7F1\uB7F2\uB7F3\uB7F5",6,"\uB7FE\uB802",4,"\uB80A\uB80B\uB80D\uB80E\uB80F\uB811",6,"\uB81A\uB81C\uB81E",5,"\uB826\uB827\uB829\uB82A\uB82B\uB82D",6,"\uB836\uB83A",5,"\uB841\uB842\uB843\uB845",11,"\uB852\uB854",7,"\uB85E\uB85F\uB861\uB862\uB863\uB865",6,"\uB86E\uB870\uB872",5,"\uB879\uB87A\uB87B\uB87D",7],["8f41","\uB885",7,"\uB88E",17],["8f61","\uB8A0",7,"\uB8A9",6,"\uB8B1\uB8B2\uB8B3\uB8B5\uB8B6\uB8B7\uB8B9",4],["8f81","\uB8BE\uB8BF\uB8C2\uB8C4\uB8C6",5,"\uB8CD\uB8CE\uB8CF\uB8D1\uB8D2\uB8D3\uB8D5",7,"\uB8DE\uB8E0\uB8E2",5,"\uB8EA\uB8EB\uB8ED\uB8EE\uB8EF\uB8F1",6,"\uB8FA\uB8FC\uB8FE",5,"\uB905",18,"\uB919",6,"\uB921",26,"\uB93E\uB93F\uB941\uB942\uB943\uB945",6,"\uB94D\uB94E\uB950\uB952",5],["9041","\uB95A\uB95B\uB95D\uB95E\uB95F\uB961",6,"\uB96A\uB96C\uB96E",5,"\uB976\uB977\uB979\uB97A\uB97B\uB97D"],["9061","\uB97E",5,"\uB986\uB988\uB98B\uB98C\uB98F",15],["9081","\uB99F",12,"\uB9AE\uB9AF\uB9B1\uB9B2\uB9B3\uB9B5",6,"\uB9BE\uB9C0\uB9C2",5,"\uB9CA\uB9CB\uB9CD\uB9D3",4,"\uB9DA\uB9DC\uB9DF\uB9E0\uB9E2\uB9E6\uB9E7\uB9E9\uB9EA\uB9EB\uB9ED",6,"\uB9F6\uB9FB",4,"\uBA02",5,"\uBA09",11,"\uBA16",33,"\uBA3A\uBA3B\uBA3D\uBA3E\uBA3F\uBA41\uBA43\uBA44\uBA45\uBA46"],["9141","\uBA47\uBA4A\uBA4C\uBA4F\uBA50\uBA51\uBA52\uBA56\uBA57\uBA59\uBA5A\uBA5B\uBA5D",6,"\uBA66\uBA6A",5],["9161","\uBA72\uBA73\uBA75\uBA76\uBA77\uBA79",9,"\uBA86\uBA88\uBA89\uBA8A\uBA8B\uBA8D",5],["9181","\uBA93",20,"\uBAAA\uBAAD\uBAAE\uBAAF\uBAB1\uBAB3",4,"\uBABA\uBABC\uBABE",5,"\uBAC5\uBAC6\uBAC7\uBAC9",14,"\uBADA",33,"\uBAFD\uBAFE\uBAFF\uBB01\uBB02\uBB03\uBB05",7,"\uBB0E\uBB10\uBB12",5,"\uBB19\uBB1A\uBB1B\uBB1D\uBB1E\uBB1F\uBB21",6],["9241","\uBB28\uBB2A\uBB2C",7,"\uBB37\uBB39\uBB3A\uBB3F",4,"\uBB46\uBB48\uBB4A\uBB4B\uBB4C\uBB4E\uBB51\uBB52"],["9261","\uBB53\uBB55\uBB56\uBB57\uBB59",7,"\uBB62\uBB64",7,"\uBB6D",4],["9281","\uBB72",21,"\uBB89\uBB8A\uBB8B\uBB8D\uBB8E\uBB8F\uBB91",18,"\uBBA5\uBBA6\uBBA7\uBBA9\uBBAA\uBBAB\uBBAD",6,"\uBBB5\uBBB6\uBBB8",7,"\uBBC1\uBBC2\uBBC3\uBBC5\uBBC6\uBBC7\uBBC9",6,"\uBBD1\uBBD2\uBBD4",35,"\uBBFA\uBBFB\uBBFD\uBBFE\uBC01"],["9341","\uBC03",4,"\uBC0A\uBC0E\uBC10\uBC12\uBC13\uBC19\uBC1A\uBC20\uBC21\uBC22\uBC23\uBC26\uBC28\uBC2A\uBC2B\uBC2C\uBC2E\uBC2F\uBC32\uBC33\uBC35"],["9361","\uBC36\uBC37\uBC39",6,"\uBC42\uBC46\uBC47\uBC48\uBC4A\uBC4B\uBC4E\uBC4F\uBC51",8],["9381","\uBC5A\uBC5B\uBC5C\uBC5E",37,"\uBC86\uBC87\uBC89\uBC8A\uBC8D\uBC8F",4,"\uBC96\uBC98\uBC9B",4,"\uBCA2\uBCA3\uBCA5\uBCA6\uBCA9",6,"\uBCB2\uBCB6",5,"\uBCBE\uBCBF\uBCC1\uBCC2\uBCC3\uBCC5",7,"\uBCCE\uBCD2\uBCD3\uBCD4\uBCD6\uBCD7\uBCD9\uBCDA\uBCDB\uBCDD",22,"\uBCF7\uBCF9\uBCFA\uBCFB\uBCFD"],["9441","\uBCFE",5,"\uBD06\uBD08\uBD0A",5,"\uBD11\uBD12\uBD13\uBD15",8],["9461","\uBD1E",5,"\uBD25",6,"\uBD2D",12],["9481","\uBD3A",5,"\uBD41",6,"\uBD4A\uBD4B\uBD4D\uBD4E\uBD4F\uBD51",6,"\uBD5A",9,"\uBD65\uBD66\uBD67\uBD69",22,"\uBD82\uBD83\uBD85\uBD86\uBD8B",4,"\uBD92\uBD94\uBD96\uBD97\uBD98\uBD9B\uBD9D",6,"\uBDA5",10,"\uBDB1",6,"\uBDB9",24],["9541","\uBDD2\uBDD3\uBDD6\uBDD7\uBDD9\uBDDA\uBDDB\uBDDD",11,"\uBDEA",5,"\uBDF1"],["9561","\uBDF2\uBDF3\uBDF5\uBDF6\uBDF7\uBDF9",6,"\uBE01\uBE02\uBE04\uBE06",5,"\uBE0E\uBE0F\uBE11\uBE12\uBE13"],["9581","\uBE15",6,"\uBE1E\uBE20",35,"\uBE46\uBE47\uBE49\uBE4A\uBE4B\uBE4D\uBE4F",4,"\uBE56\uBE58\uBE5C\uBE5D\uBE5E\uBE5F\uBE62\uBE63\uBE65\uBE66\uBE67\uBE69\uBE6B",4,"\uBE72\uBE76",4,"\uBE7E\uBE7F\uBE81\uBE82\uBE83\uBE85",6,"\uBE8E\uBE92",5,"\uBE9A",13,"\uBEA9",14],["9641","\uBEB8",23,"\uBED2\uBED3"],["9661","\uBED5\uBED6\uBED9",6,"\uBEE1\uBEE2\uBEE6",5,"\uBEED",8],["9681","\uBEF6",10,"\uBF02",5,"\uBF0A",13,"\uBF1A\uBF1E",33,"\uBF42\uBF43\uBF45\uBF46\uBF47\uBF49",6,"\uBF52\uBF53\uBF54\uBF56",44],["9741","\uBF83",16,"\uBF95",8],["9761","\uBF9E",17,"\uBFB1",7],["9781","\uBFB9",11,"\uBFC6",5,"\uBFCE\uBFCF\uBFD1\uBFD2\uBFD3\uBFD5",6,"\uBFDD\uBFDE\uBFE0\uBFE2",89,"\uC03D\uC03E\uC03F"],["9841","\uC040",16,"\uC052",5,"\uC059\uC05A\uC05B"],["9861","\uC05D\uC05E\uC05F\uC061",6,"\uC06A",15],["9881","\uC07A",21,"\uC092\uC093\uC095\uC096\uC097\uC099",6,"\uC0A2\uC0A4\uC0A6",5,"\uC0AE\uC0B1\uC0B2\uC0B7",4,"\uC0BE\uC0C2\uC0C3\uC0C4\uC0C6\uC0C7\uC0CA\uC0CB\uC0CD\uC0CE\uC0CF\uC0D1",6,"\uC0DA\uC0DE",5,"\uC0E6\uC0E7\uC0E9\uC0EA\uC0EB\uC0ED",6,"\uC0F6\uC0F8\uC0FA",5,"\uC101\uC102\uC103\uC105\uC106\uC107\uC109",6,"\uC111\uC112\uC113\uC114\uC116",5,"\uC121\uC122\uC125\uC128\uC129\uC12A\uC12B\uC12E"],["9941","\uC132\uC133\uC134\uC135\uC137\uC13A\uC13B\uC13D\uC13E\uC13F\uC141",6,"\uC14A\uC14E",5,"\uC156\uC157"],["9961","\uC159\uC15A\uC15B\uC15D",6,"\uC166\uC16A",5,"\uC171\uC172\uC173\uC175\uC176\uC177\uC179\uC17A\uC17B"],["9981","\uC17C",8,"\uC186",5,"\uC18F\uC191\uC192\uC193\uC195\uC197",4,"\uC19E\uC1A0\uC1A2\uC1A3\uC1A4\uC1A6\uC1A7\uC1AA\uC1AB\uC1AD\uC1AE\uC1AF\uC1B1",11,"\uC1BE",5,"\uC1C5\uC1C6\uC1C7\uC1C9\uC1CA\uC1CB\uC1CD",6,"\uC1D5\uC1D6\uC1D9",6,"\uC1E1\uC1E2\uC1E3\uC1E5\uC1E6\uC1E7\uC1E9",6,"\uC1F2\uC1F4",7,"\uC1FE\uC1FF\uC201\uC202\uC203\uC205",6,"\uC20E\uC210\uC212",5,"\uC21A\uC21B\uC21D\uC21E\uC221\uC222\uC223"],["9a41","\uC224\uC225\uC226\uC227\uC22A\uC22C\uC22E\uC230\uC233\uC235",16],["9a61","\uC246\uC247\uC249",6,"\uC252\uC253\uC255\uC256\uC257\uC259",6,"\uC261\uC262\uC263\uC264\uC266"],["9a81","\uC267",4,"\uC26E\uC26F\uC271\uC272\uC273\uC275",6,"\uC27E\uC280\uC282",5,"\uC28A",5,"\uC291",6,"\uC299\uC29A\uC29C\uC29E",5,"\uC2A6\uC2A7\uC2A9\uC2AA\uC2AB\uC2AE",5,"\uC2B6\uC2B8\uC2BA",33,"\uC2DE\uC2DF\uC2E1\uC2E2\uC2E5",5,"\uC2EE\uC2F0\uC2F2\uC2F3\uC2F4\uC2F5\uC2F7\uC2FA\uC2FD\uC2FE\uC2FF\uC301",6,"\uC30A\uC30B\uC30E\uC30F"],["9b41","\uC310\uC311\uC312\uC316\uC317\uC319\uC31A\uC31B\uC31D",6,"\uC326\uC327\uC32A",8],["9b61","\uC333",17,"\uC346",7],["9b81","\uC34E",25,"\uC36A\uC36B\uC36D\uC36E\uC36F\uC371\uC373",4,"\uC37A\uC37B\uC37E",5,"\uC385\uC386\uC387\uC389\uC38A\uC38B\uC38D",50,"\uC3C1",22,"\uC3DA"],["9c41","\uC3DB\uC3DD\uC3DE\uC3E1\uC3E3",4,"\uC3EA\uC3EB\uC3EC\uC3EE",5,"\uC3F6\uC3F7\uC3F9",5],["9c61","\uC3FF",8,"\uC409",6,"\uC411",9],["9c81","\uC41B",8,"\uC425",6,"\uC42D\uC42E\uC42F\uC431\uC432\uC433\uC435",6,"\uC43E",9,"\uC449",26,"\uC466\uC467\uC469\uC46A\uC46B\uC46D",6,"\uC476\uC477\uC478\uC47A",5,"\uC481",18,"\uC495",6,"\uC49D",12],["9d41","\uC4AA",13,"\uC4B9\uC4BA\uC4BB\uC4BD",8],["9d61","\uC4C6",25],["9d81","\uC4E0",8,"\uC4EA",5,"\uC4F2\uC4F3\uC4F5\uC4F6\uC4F7\uC4F9\uC4FB\uC4FC\uC4FD\uC4FE\uC502",9,"\uC50D\uC50E\uC50F\uC511\uC512\uC513\uC515",6,"\uC51D",10,"\uC52A\uC52B\uC52D\uC52E\uC52F\uC531",6,"\uC53A\uC53C\uC53E",5,"\uC546\uC547\uC54B\uC54F\uC550\uC551\uC552\uC556\uC55A\uC55B\uC55C\uC55F\uC562\uC563\uC565\uC566\uC567\uC569",6,"\uC572\uC576",5,"\uC57E\uC57F\uC581\uC582\uC583\uC585\uC586\uC588\uC589\uC58A\uC58B\uC58E\uC590\uC592\uC593\uC594"],["9e41","\uC596\uC599\uC59A\uC59B\uC59D\uC59E\uC59F\uC5A1",7,"\uC5AA",9,"\uC5B6"],["9e61","\uC5B7\uC5BA\uC5BF",4,"\uC5CB\uC5CD\uC5CF\uC5D2\uC5D3\uC5D5\uC5D6\uC5D7\uC5D9",6,"\uC5E2\uC5E4\uC5E6\uC5E7"],["9e81","\uC5E8\uC5E9\uC5EA\uC5EB\uC5EF\uC5F1\uC5F2\uC5F3\uC5F5\uC5F8\uC5F9\uC5FA\uC5FB\uC602\uC603\uC604\uC609\uC60A\uC60B\uC60D\uC60E\uC60F\uC611",6,"\uC61A\uC61D",6,"\uC626\uC627\uC629\uC62A\uC62B\uC62F\uC631\uC632\uC636\uC638\uC63A\uC63C\uC63D\uC63E\uC63F\uC642\uC643\uC645\uC646\uC647\uC649",6,"\uC652\uC656",5,"\uC65E\uC65F\uC661",10,"\uC66D\uC66E\uC670\uC672",5,"\uC67A\uC67B\uC67D\uC67E\uC67F\uC681",6,"\uC68A\uC68C\uC68E",5,"\uC696\uC697\uC699\uC69A\uC69B\uC69D",6,"\uC6A6"],["9f41","\uC6A8\uC6AA",5,"\uC6B2\uC6B3\uC6B5\uC6B6\uC6B7\uC6BB",4,"\uC6C2\uC6C4\uC6C6",5,"\uC6CE"],["9f61","\uC6CF\uC6D1\uC6D2\uC6D3\uC6D5",6,"\uC6DE\uC6DF\uC6E2",5,"\uC6EA\uC6EB\uC6ED\uC6EE\uC6EF\uC6F1\uC6F2"],["9f81","\uC6F3",4,"\uC6FA\uC6FB\uC6FC\uC6FE",5,"\uC706\uC707\uC709\uC70A\uC70B\uC70D",6,"\uC716\uC718\uC71A",5,"\uC722\uC723\uC725\uC726\uC727\uC729",6,"\uC732\uC734\uC736\uC738\uC739\uC73A\uC73B\uC73E\uC73F\uC741\uC742\uC743\uC745",4,"\uC74B\uC74E\uC750\uC759\uC75A\uC75B\uC75D\uC75E\uC75F\uC761",6,"\uC769\uC76A\uC76C",7,"\uC776\uC777\uC779\uC77A\uC77B\uC77F\uC780\uC781\uC782\uC786\uC78B\uC78C\uC78D\uC78F\uC792\uC793\uC795\uC799\uC79B",4,"\uC7A2\uC7A7",4,"\uC7AE\uC7AF\uC7B1\uC7B2\uC7B3\uC7B5\uC7B6\uC7B7"],["a041","\uC7B8\uC7B9\uC7BA\uC7BB\uC7BE\uC7C2",5,"\uC7CA\uC7CB\uC7CD\uC7CF\uC7D1",6,"\uC7D9\uC7DA\uC7DB\uC7DC"],["a061","\uC7DE",5,"\uC7E5\uC7E6\uC7E7\uC7E9\uC7EA\uC7EB\uC7ED",13],["a081","\uC7FB",4,"\uC802\uC803\uC805\uC806\uC807\uC809\uC80B",4,"\uC812\uC814\uC817",4,"\uC81E\uC81F\uC821\uC822\uC823\uC825",6,"\uC82E\uC830\uC832",5,"\uC839\uC83A\uC83B\uC83D\uC83E\uC83F\uC841",6,"\uC84A\uC84B\uC84E",5,"\uC855",26,"\uC872\uC873\uC875\uC876\uC877\uC879\uC87B",4,"\uC882\uC884\uC888\uC889\uC88A\uC88E",5,"\uC895",7,"\uC89E\uC8A0\uC8A2\uC8A3\uC8A4"],["a141","\uC8A5\uC8A6\uC8A7\uC8A9",18,"\uC8BE\uC8BF\uC8C0\uC8C1"],["a161","\uC8C2\uC8C3\uC8C5\uC8C6\uC8C7\uC8C9\uC8CA\uC8CB\uC8CD",6,"\uC8D6\uC8D8\uC8DA",5,"\uC8E2\uC8E3\uC8E5"],["a181","\uC8E6",14,"\uC8F6",5,"\uC8FE\uC8FF\uC901\uC902\uC903\uC907",4,"\uC90E\u3000\u3001\u3002\xB7\u2025\u2026\xA8\u3003\xAD\u2015\u2225\uFF3C\u223C\u2018\u2019\u201C\u201D\u3014\u3015\u3008",9,"\xB1\xD7\xF7\u2260\u2264\u2265\u221E\u2234\xB0\u2032\u2033\u2103\u212B\uFFE0\uFFE1\uFFE5\u2642\u2640\u2220\u22A5\u2312\u2202\u2207\u2261\u2252\xA7\u203B\u2606\u2605\u25CB\u25CF\u25CE\u25C7\u25C6\u25A1\u25A0\u25B3\u25B2\u25BD\u25BC\u2192\u2190\u2191\u2193\u2194\u3013\u226A\u226B\u221A\u223D\u221D\u2235\u222B\u222C\u2208\u220B\u2286\u2287\u2282\u2283\u222A\u2229\u2227\u2228\uFFE2"],["a241","\uC910\uC912",5,"\uC919",18],["a261","\uC92D",6,"\uC935",18],["a281","\uC948",7,"\uC952\uC953\uC955\uC956\uC957\uC959",6,"\uC962\uC964",7,"\uC96D\uC96E\uC96F\u21D2\u21D4\u2200\u2203\xB4\uFF5E\u02C7\u02D8\u02DD\u02DA\u02D9\xB8\u02DB\xA1\xBF\u02D0\u222E\u2211\u220F\xA4\u2109\u2030\u25C1\u25C0\u25B7\u25B6\u2664\u2660\u2661\u2665\u2667\u2663\u2299\u25C8\u25A3\u25D0\u25D1\u2592\u25A4\u25A5\u25A8\u25A7\u25A6\u25A9\u2668\u260F\u260E\u261C\u261E\xB6\u2020\u2021\u2195\u2197\u2199\u2196\u2198\u266D\u2669\u266A\u266C\u327F\u321C\u2116\u33C7\u2122\u33C2\u33D8\u2121\u20AC\xAE"],["a341","\uC971\uC972\uC973\uC975",6,"\uC97D",10,"\uC98A\uC98B\uC98D\uC98E\uC98F"],["a361","\uC991",6,"\uC99A\uC99C\uC99E",16],["a381","\uC9AF",16,"\uC9C2\uC9C3\uC9C5\uC9C6\uC9C9\uC9CB",4,"\uC9D2\uC9D4\uC9D7\uC9D8\uC9DB\uFF01",58,"\uFFE6\uFF3D",32,"\uFFE3"],["a441","\uC9DE\uC9DF\uC9E1\uC9E3\uC9E5\uC9E6\uC9E8\uC9E9\uC9EA\uC9EB\uC9EE\uC9F2",5,"\uC9FA\uC9FB\uC9FD\uC9FE\uC9FF\uCA01\uCA02\uCA03\uCA04"],["a461","\uCA05\uCA06\uCA07\uCA0A\uCA0E",5,"\uCA15\uCA16\uCA17\uCA19",12],["a481","\uCA26\uCA27\uCA28\uCA2A",28,"\u3131",93],["a541","\uCA47",4,"\uCA4E\uCA4F\uCA51\uCA52\uCA53\uCA55",6,"\uCA5E\uCA62",5,"\uCA69\uCA6A"],["a561","\uCA6B",17,"\uCA7E",5,"\uCA85\uCA86"],["a581","\uCA87",16,"\uCA99",14,"\u2170",9],["a5b0","\u2160",9],["a5c1","\u0391",16,"\u03A3",6],["a5e1","\u03B1",16,"\u03C3",6],["a641","\uCAA8",19,"\uCABE\uCABF\uCAC1\uCAC2\uCAC3\uCAC5"],["a661","\uCAC6",5,"\uCACE\uCAD0\uCAD2\uCAD4\uCAD5\uCAD6\uCAD7\uCADA",5,"\uCAE1",6],["a681","\uCAE8\uCAE9\uCAEA\uCAEB\uCAED",6,"\uCAF5",18,"\uCB09\uCB0A\u2500\u2502\u250C\u2510\u2518\u2514\u251C\u252C\u2524\u2534\u253C\u2501\u2503\u250F\u2513\u251B\u2517\u2523\u2533\u252B\u253B\u254B\u2520\u252F\u2528\u2537\u253F\u251D\u2530\u2525\u2538\u2542\u2512\u2511\u251A\u2519\u2516\u2515\u250E\u250D\u251E\u251F\u2521\u2522\u2526\u2527\u2529\u252A\u252D\u252E\u2531\u2532\u2535\u2536\u2539\u253A\u253D\u253E\u2540\u2541\u2543",7],["a741","\uCB0B",4,"\uCB11\uCB12\uCB13\uCB15\uCB16\uCB17\uCB19",6,"\uCB22",7],["a761","\uCB2A",22,"\uCB42\uCB43\uCB44"],["a781","\uCB45\uCB46\uCB47\uCB4A\uCB4B\uCB4D\uCB4E\uCB4F\uCB51",6,"\uCB5A\uCB5B\uCB5C\uCB5E",5,"\uCB65",7,"\u3395\u3396\u3397\u2113\u3398\u33C4\u33A3\u33A4\u33A5\u33A6\u3399",9,"\u33CA\u338D\u338E\u338F\u33CF\u3388\u3389\u33C8\u33A7\u33A8\u33B0",9,"\u3380",4,"\u33BA",5,"\u3390",4,"\u2126\u33C0\u33C1\u338A\u338B\u338C\u33D6\u33C5\u33AD\u33AE\u33AF\u33DB\u33A9\u33AA\u33AB\u33AC\u33DD\u33D0\u33D3\u33C3\u33C9\u33DC\u33C6"],["a841","\uCB6D",10,"\uCB7A",14],["a861","\uCB89",18,"\uCB9D",6],["a881","\uCBA4",19,"\uCBB9",11,"\xC6\xD0\xAA\u0126"],["a8a6","\u0132"],["a8a8","\u013F\u0141\xD8\u0152\xBA\xDE\u0166\u014A"],["a8b1","\u3260",27,"\u24D0",25,"\u2460",14,"\xBD\u2153\u2154\xBC\xBE\u215B\u215C\u215D\u215E"],["a941","\uCBC5",14,"\uCBD5",10],["a961","\uCBE0\uCBE1\uCBE2\uCBE3\uCBE5\uCBE6\uCBE8\uCBEA",18],["a981","\uCBFD",14,"\uCC0E\uCC0F\uCC11\uCC12\uCC13\uCC15",6,"\uCC1E\uCC1F\uCC20\uCC23\uCC24\xE6\u0111\xF0\u0127\u0131\u0133\u0138\u0140\u0142\xF8\u0153\xDF\xFE\u0167\u014B\u0149\u3200",27,"\u249C",25,"\u2474",14,"\xB9\xB2\xB3\u2074\u207F\u2081\u2082\u2083\u2084"],["aa41","\uCC25\uCC26\uCC2A\uCC2B\uCC2D\uCC2F\uCC31",6,"\uCC3A\uCC3F",4,"\uCC46\uCC47\uCC49\uCC4A\uCC4B\uCC4D\uCC4E"],["aa61","\uCC4F",4,"\uCC56\uCC5A",5,"\uCC61\uCC62\uCC63\uCC65\uCC67\uCC69",6,"\uCC71\uCC72"],["aa81","\uCC73\uCC74\uCC76",29,"\u3041",82],["ab41","\uCC94\uCC95\uCC96\uCC97\uCC9A\uCC9B\uCC9D\uCC9E\uCC9F\uCCA1",6,"\uCCAA\uCCAE",5,"\uCCB6\uCCB7\uCCB9"],["ab61","\uCCBA\uCCBB\uCCBD",6,"\uCCC6\uCCC8\uCCCA",5,"\uCCD1\uCCD2\uCCD3\uCCD5",5],["ab81","\uCCDB",8,"\uCCE5",6,"\uCCED\uCCEE\uCCEF\uCCF1",12,"\u30A1",85],["ac41","\uCCFE\uCCFF\uCD00\uCD02",5,"\uCD0A\uCD0B\uCD0D\uCD0E\uCD0F\uCD11",6,"\uCD1A\uCD1C\uCD1E\uCD1F\uCD20"],["ac61","\uCD21\uCD22\uCD23\uCD25\uCD26\uCD27\uCD29\uCD2A\uCD2B\uCD2D",11,"\uCD3A",4],["ac81","\uCD3F",28,"\uCD5D\uCD5E\uCD5F\u0410",5,"\u0401\u0416",25],["acd1","\u0430",5,"\u0451\u0436",25],["ad41","\uCD61\uCD62\uCD63\uCD65",6,"\uCD6E\uCD70\uCD72",5,"\uCD79",7],["ad61","\uCD81",6,"\uCD89",10,"\uCD96\uCD97\uCD99\uCD9A\uCD9B\uCD9D\uCD9E\uCD9F"],["ad81","\uCDA0\uCDA1\uCDA2\uCDA3\uCDA6\uCDA8\uCDAA",5,"\uCDB1",18,"\uCDC5"],["ae41","\uCDC6",5,"\uCDCD\uCDCE\uCDCF\uCDD1",16],["ae61","\uCDE2",5,"\uCDE9\uCDEA\uCDEB\uCDED\uCDEE\uCDEF\uCDF1",6,"\uCDFA\uCDFC\uCDFE",4],["ae81","\uCE03\uCE05\uCE06\uCE07\uCE09\uCE0A\uCE0B\uCE0D",6,"\uCE15\uCE16\uCE17\uCE18\uCE1A",5,"\uCE22\uCE23\uCE25\uCE26\uCE27\uCE29\uCE2A\uCE2B"],["af41","\uCE2C\uCE2D\uCE2E\uCE2F\uCE32\uCE34\uCE36",19],["af61","\uCE4A",13,"\uCE5A\uCE5B\uCE5D\uCE5E\uCE62",5,"\uCE6A\uCE6C"],["af81","\uCE6E",5,"\uCE76\uCE77\uCE79\uCE7A\uCE7B\uCE7D",6,"\uCE86\uCE88\uCE8A",5,"\uCE92\uCE93\uCE95\uCE96\uCE97\uCE99"],["b041","\uCE9A",5,"\uCEA2\uCEA6",5,"\uCEAE",12],["b061","\uCEBB",5,"\uCEC2",19],["b081","\uCED6",13,"\uCEE6\uCEE7\uCEE9\uCEEA\uCEED",6,"\uCEF6\uCEFA",5,"\uAC00\uAC01\uAC04\uAC07\uAC08\uAC09\uAC0A\uAC10",7,"\uAC19",4,"\uAC20\uAC24\uAC2C\uAC2D\uAC2F\uAC30\uAC31\uAC38\uAC39\uAC3C\uAC40\uAC4B\uAC4D\uAC54\uAC58\uAC5C\uAC70\uAC71\uAC74\uAC77\uAC78\uAC7A\uAC80\uAC81\uAC83\uAC84\uAC85\uAC86\uAC89\uAC8A\uAC8B\uAC8C\uAC90\uAC94\uAC9C\uAC9D\uAC9F\uACA0\uACA1\uACA8\uACA9\uACAA\uACAC\uACAF\uACB0\uACB8\uACB9\uACBB\uACBC\uACBD\uACC1\uACC4\uACC8\uACCC\uACD5\uACD7\uACE0\uACE1\uACE4\uACE7\uACE8\uACEA\uACEC\uACEF\uACF0\uACF1\uACF3\uACF5\uACF6\uACFC\uACFD\uAD00\uAD04\uAD06"],["b141","\uCF02\uCF03\uCF05\uCF06\uCF07\uCF09",6,"\uCF12\uCF14\uCF16",5,"\uCF1D\uCF1E\uCF1F\uCF21\uCF22\uCF23"],["b161","\uCF25",6,"\uCF2E\uCF32",5,"\uCF39",11],["b181","\uCF45",14,"\uCF56\uCF57\uCF59\uCF5A\uCF5B\uCF5D",6,"\uCF66\uCF68\uCF6A\uCF6B\uCF6C\uAD0C\uAD0D\uAD0F\uAD11\uAD18\uAD1C\uAD20\uAD29\uAD2C\uAD2D\uAD34\uAD35\uAD38\uAD3C\uAD44\uAD45\uAD47\uAD49\uAD50\uAD54\uAD58\uAD61\uAD63\uAD6C\uAD6D\uAD70\uAD73\uAD74\uAD75\uAD76\uAD7B\uAD7C\uAD7D\uAD7F\uAD81\uAD82\uAD88\uAD89\uAD8C\uAD90\uAD9C\uAD9D\uADA4\uADB7\uADC0\uADC1\uADC4\uADC8\uADD0\uADD1\uADD3\uADDC\uADE0\uADE4\uADF8\uADF9\uADFC\uADFF\uAE00\uAE01\uAE08\uAE09\uAE0B\uAE0D\uAE14\uAE30\uAE31\uAE34\uAE37\uAE38\uAE3A\uAE40\uAE41\uAE43\uAE45\uAE46\uAE4A\uAE4C\uAE4D\uAE4E\uAE50\uAE54\uAE56\uAE5C\uAE5D\uAE5F\uAE60\uAE61\uAE65\uAE68\uAE69\uAE6C\uAE70\uAE78"],["b241","\uCF6D\uCF6E\uCF6F\uCF72\uCF73\uCF75\uCF76\uCF77\uCF79",6,"\uCF81\uCF82\uCF83\uCF84\uCF86",5,"\uCF8D"],["b261","\uCF8E",18,"\uCFA2",5,"\uCFA9"],["b281","\uCFAA",5,"\uCFB1",18,"\uCFC5",6,"\uAE79\uAE7B\uAE7C\uAE7D\uAE84\uAE85\uAE8C\uAEBC\uAEBD\uAEBE\uAEC0\uAEC4\uAECC\uAECD\uAECF\uAED0\uAED1\uAED8\uAED9\uAEDC\uAEE8\uAEEB\uAEED\uAEF4\uAEF8\uAEFC\uAF07\uAF08\uAF0D\uAF10\uAF2C\uAF2D\uAF30\uAF32\uAF34\uAF3C\uAF3D\uAF3F\uAF41\uAF42\uAF43\uAF48\uAF49\uAF50\uAF5C\uAF5D\uAF64\uAF65\uAF79\uAF80\uAF84\uAF88\uAF90\uAF91\uAF95\uAF9C\uAFB8\uAFB9\uAFBC\uAFC0\uAFC7\uAFC8\uAFC9\uAFCB\uAFCD\uAFCE\uAFD4\uAFDC\uAFE8\uAFE9\uAFF0\uAFF1\uAFF4\uAFF8\uB000\uB001\uB004\uB00C\uB010\uB014\uB01C\uB01D\uB028\uB044\uB045\uB048\uB04A\uB04C\uB04E\uB053\uB054\uB055\uB057\uB059"],["b341","\uCFCC",19,"\uCFE2\uCFE3\uCFE5\uCFE6\uCFE7\uCFE9"],["b361","\uCFEA",5,"\uCFF2\uCFF4\uCFF6",5,"\uCFFD\uCFFE\uCFFF\uD001\uD002\uD003\uD005",5],["b381","\uD00B",5,"\uD012",5,"\uD019",19,"\uB05D\uB07C\uB07D\uB080\uB084\uB08C\uB08D\uB08F\uB091\uB098\uB099\uB09A\uB09C\uB09F\uB0A0\uB0A1\uB0A2\uB0A8\uB0A9\uB0AB",4,"\uB0B1\uB0B3\uB0B4\uB0B5\uB0B8\uB0BC\uB0C4\uB0C5\uB0C7\uB0C8\uB0C9\uB0D0\uB0D1\uB0D4\uB0D8\uB0E0\uB0E5\uB108\uB109\uB10B\uB10C\uB110\uB112\uB113\uB118\uB119\uB11B\uB11C\uB11D\uB123\uB124\uB125\uB128\uB12C\uB134\uB135\uB137\uB138\uB139\uB140\uB141\uB144\uB148\uB150\uB151\uB154\uB155\uB158\uB15C\uB160\uB178\uB179\uB17C\uB180\uB182\uB188\uB189\uB18B\uB18D\uB192\uB193\uB194\uB198\uB19C\uB1A8\uB1CC\uB1D0\uB1D4\uB1DC\uB1DD"],["b441","\uD02E",5,"\uD036\uD037\uD039\uD03A\uD03B\uD03D",6,"\uD046\uD048\uD04A",5],["b461","\uD051\uD052\uD053\uD055\uD056\uD057\uD059",6,"\uD061",10,"\uD06E\uD06F"],["b481","\uD071\uD072\uD073\uD075",6,"\uD07E\uD07F\uD080\uD082",18,"\uB1DF\uB1E8\uB1E9\uB1EC\uB1F0\uB1F9\uB1FB\uB1FD\uB204\uB205\uB208\uB20B\uB20C\uB214\uB215\uB217\uB219\uB220\uB234\uB23C\uB258\uB25C\uB260\uB268\uB269\uB274\uB275\uB27C\uB284\uB285\uB289\uB290\uB291\uB294\uB298\uB299\uB29A\uB2A0\uB2A1\uB2A3\uB2A5\uB2A6\uB2AA\uB2AC\uB2B0\uB2B4\uB2C8\uB2C9\uB2CC\uB2D0\uB2D2\uB2D8\uB2D9\uB2DB\uB2DD\uB2E2\uB2E4\uB2E5\uB2E6\uB2E8\uB2EB",4,"\uB2F3\uB2F4\uB2F5\uB2F7",4,"\uB2FF\uB300\uB301\uB304\uB308\uB310\uB311\uB313\uB314\uB315\uB31C\uB354\uB355\uB356\uB358\uB35B\uB35C\uB35E\uB35F\uB364\uB365"],["b541","\uD095",14,"\uD0A6\uD0A7\uD0A9\uD0AA\uD0AB\uD0AD",5],["b561","\uD0B3\uD0B6\uD0B8\uD0BA",5,"\uD0C2\uD0C3\uD0C5\uD0C6\uD0C7\uD0CA",5,"\uD0D2\uD0D6",4],["b581","\uD0DB\uD0DE\uD0DF\uD0E1\uD0E2\uD0E3\uD0E5",6,"\uD0EE\uD0F2",5,"\uD0F9",11,"\uB367\uB369\uB36B\uB36E\uB370\uB371\uB374\uB378\uB380\uB381\uB383\uB384\uB385\uB38C\uB390\uB394\uB3A0\uB3A1\uB3A8\uB3AC\uB3C4\uB3C5\uB3C8\uB3CB\uB3CC\uB3CE\uB3D0\uB3D4\uB3D5\uB3D7\uB3D9\uB3DB\uB3DD\uB3E0\uB3E4\uB3E8\uB3FC\uB410\uB418\uB41C\uB420\uB428\uB429\uB42B\uB434\uB450\uB451\uB454\uB458\uB460\uB461\uB463\uB465\uB46C\uB480\uB488\uB49D\uB4A4\uB4A8\uB4AC\uB4B5\uB4B7\uB4B9\uB4C0\uB4C4\uB4C8\uB4D0\uB4D5\uB4DC\uB4DD\uB4E0\uB4E3\uB4E4\uB4E6\uB4EC\uB4ED\uB4EF\uB4F1\uB4F8\uB514\uB515\uB518\uB51B\uB51C\uB524\uB525\uB527\uB528\uB529\uB52A\uB530\uB531\uB534\uB538"],["b641","\uD105",7,"\uD10E",17],["b661","\uD120",15,"\uD132\uD133\uD135\uD136\uD137\uD139\uD13B\uD13C\uD13D\uD13E"],["b681","\uD13F\uD142\uD146",5,"\uD14E\uD14F\uD151\uD152\uD153\uD155",6,"\uD15E\uD160\uD162",5,"\uD169\uD16A\uD16B\uD16D\uB540\uB541\uB543\uB544\uB545\uB54B\uB54C\uB54D\uB550\uB554\uB55C\uB55D\uB55F\uB560\uB561\uB5A0\uB5A1\uB5A4\uB5A8\uB5AA\uB5AB\uB5B0\uB5B1\uB5B3\uB5B4\uB5B5\uB5BB\uB5BC\uB5BD\uB5C0\uB5C4\uB5CC\uB5CD\uB5CF\uB5D0\uB5D1\uB5D8\uB5EC\uB610\uB611\uB614\uB618\uB625\uB62C\uB634\uB648\uB664\uB668\uB69C\uB69D\uB6A0\uB6A4\uB6AB\uB6AC\uB6B1\uB6D4\uB6F0\uB6F4\uB6F8\uB700\uB701\uB705\uB728\uB729\uB72C\uB72F\uB730\uB738\uB739\uB73B\uB744\uB748\uB74C\uB754\uB755\uB760\uB764\uB768\uB770\uB771\uB773\uB775\uB77C\uB77D\uB780\uB784\uB78C\uB78D\uB78F\uB790\uB791\uB792\uB796\uB797"],["b741","\uD16E",13,"\uD17D",6,"\uD185\uD186\uD187\uD189\uD18A"],["b761","\uD18B",20,"\uD1A2\uD1A3\uD1A5\uD1A6\uD1A7"],["b781","\uD1A9",6,"\uD1B2\uD1B4\uD1B6\uD1B7\uD1B8\uD1B9\uD1BB\uD1BD\uD1BE\uD1BF\uD1C1",14,"\uB798\uB799\uB79C\uB7A0\uB7A8\uB7A9\uB7AB\uB7AC\uB7AD\uB7B4\uB7B5\uB7B8\uB7C7\uB7C9\uB7EC\uB7ED\uB7F0\uB7F4\uB7FC\uB7FD\uB7FF\uB800\uB801\uB807\uB808\uB809\uB80C\uB810\uB818\uB819\uB81B\uB81D\uB824\uB825\uB828\uB82C\uB834\uB835\uB837\uB838\uB839\uB840\uB844\uB851\uB853\uB85C\uB85D\uB860\uB864\uB86C\uB86D\uB86F\uB871\uB878\uB87C\uB88D\uB8A8\uB8B0\uB8B4\uB8B8\uB8C0\uB8C1\uB8C3\uB8C5\uB8CC\uB8D0\uB8D4\uB8DD\uB8DF\uB8E1\uB8E8\uB8E9\uB8EC\uB8F0\uB8F8\uB8F9\uB8FB\uB8FD\uB904\uB918\uB920\uB93C\uB93D\uB940\uB944\uB94C\uB94F\uB951\uB958\uB959\uB95C\uB960\uB968\uB969"],["b841","\uD1D0",7,"\uD1D9",17],["b861","\uD1EB",8,"\uD1F5\uD1F6\uD1F7\uD1F9",13],["b881","\uD208\uD20A",5,"\uD211",24,"\uB96B\uB96D\uB974\uB975\uB978\uB97C\uB984\uB985\uB987\uB989\uB98A\uB98D\uB98E\uB9AC\uB9AD\uB9B0\uB9B4\uB9BC\uB9BD\uB9BF\uB9C1\uB9C8\uB9C9\uB9CC\uB9CE",4,"\uB9D8\uB9D9\uB9DB\uB9DD\uB9DE\uB9E1\uB9E3\uB9E4\uB9E5\uB9E8\uB9EC\uB9F4\uB9F5\uB9F7\uB9F8\uB9F9\uB9FA\uBA00\uBA01\uBA08\uBA15\uBA38\uBA39\uBA3C\uBA40\uBA42\uBA48\uBA49\uBA4B\uBA4D\uBA4E\uBA53\uBA54\uBA55\uBA58\uBA5C\uBA64\uBA65\uBA67\uBA68\uBA69\uBA70\uBA71\uBA74\uBA78\uBA83\uBA84\uBA85\uBA87\uBA8C\uBAA8\uBAA9\uBAAB\uBAAC\uBAB0\uBAB2\uBAB8\uBAB9\uBABB\uBABD\uBAC4\uBAC8\uBAD8\uBAD9\uBAFC"],["b941","\uD22A\uD22B\uD22E\uD22F\uD231\uD232\uD233\uD235",6,"\uD23E\uD240\uD242",5,"\uD249\uD24A\uD24B\uD24C"],["b961","\uD24D",14,"\uD25D",6,"\uD265\uD266\uD267\uD268"],["b981","\uD269",22,"\uD282\uD283\uD285\uD286\uD287\uD289\uD28A\uD28B\uD28C\uBB00\uBB04\uBB0D\uBB0F\uBB11\uBB18\uBB1C\uBB20\uBB29\uBB2B\uBB34\uBB35\uBB36\uBB38\uBB3B\uBB3C\uBB3D\uBB3E\uBB44\uBB45\uBB47\uBB49\uBB4D\uBB4F\uBB50\uBB54\uBB58\uBB61\uBB63\uBB6C\uBB88\uBB8C\uBB90\uBBA4\uBBA8\uBBAC\uBBB4\uBBB7\uBBC0\uBBC4\uBBC8\uBBD0\uBBD3\uBBF8\uBBF9\uBBFC\uBBFF\uBC00\uBC02\uBC08\uBC09\uBC0B\uBC0C\uBC0D\uBC0F\uBC11\uBC14",4,"\uBC1B",4,"\uBC24\uBC25\uBC27\uBC29\uBC2D\uBC30\uBC31\uBC34\uBC38\uBC40\uBC41\uBC43\uBC44\uBC45\uBC49\uBC4C\uBC4D\uBC50\uBC5D\uBC84\uBC85\uBC88\uBC8B\uBC8C\uBC8E\uBC94\uBC95\uBC97"],["ba41","\uD28D\uD28E\uD28F\uD292\uD293\uD294\uD296",5,"\uD29D\uD29E\uD29F\uD2A1\uD2A2\uD2A3\uD2A5",6,"\uD2AD"],["ba61","\uD2AE\uD2AF\uD2B0\uD2B2",5,"\uD2BA\uD2BB\uD2BD\uD2BE\uD2C1\uD2C3",4,"\uD2CA\uD2CC",5],["ba81","\uD2D2\uD2D3\uD2D5\uD2D6\uD2D7\uD2D9\uD2DA\uD2DB\uD2DD",6,"\uD2E6",9,"\uD2F2\uD2F3\uD2F5\uD2F6\uD2F7\uD2F9\uD2FA\uBC99\uBC9A\uBCA0\uBCA1\uBCA4\uBCA7\uBCA8\uBCB0\uBCB1\uBCB3\uBCB4\uBCB5\uBCBC\uBCBD\uBCC0\uBCC4\uBCCD\uBCCF\uBCD0\uBCD1\uBCD5\uBCD8\uBCDC\uBCF4\uBCF5\uBCF6\uBCF8\uBCFC\uBD04\uBD05\uBD07\uBD09\uBD10\uBD14\uBD24\uBD2C\uBD40\uBD48\uBD49\uBD4C\uBD50\uBD58\uBD59\uBD64\uBD68\uBD80\uBD81\uBD84\uBD87\uBD88\uBD89\uBD8A\uBD90\uBD91\uBD93\uBD95\uBD99\uBD9A\uBD9C\uBDA4\uBDB0\uBDB8\uBDD4\uBDD5\uBDD8\uBDDC\uBDE9\uBDF0\uBDF4\uBDF8\uBE00\uBE03\uBE05\uBE0C\uBE0D\uBE10\uBE14\uBE1C\uBE1D\uBE1F\uBE44\uBE45\uBE48\uBE4C\uBE4E\uBE54\uBE55\uBE57\uBE59\uBE5A\uBE5B\uBE60\uBE61\uBE64"],["bb41","\uD2FB",4,"\uD302\uD304\uD306",5,"\uD30F\uD311\uD312\uD313\uD315\uD317",4,"\uD31E\uD322\uD323"],["bb61","\uD324\uD326\uD327\uD32A\uD32B\uD32D\uD32E\uD32F\uD331",6,"\uD33A\uD33E",5,"\uD346\uD347\uD348\uD349"],["bb81","\uD34A",31,"\uBE68\uBE6A\uBE70\uBE71\uBE73\uBE74\uBE75\uBE7B\uBE7C\uBE7D\uBE80\uBE84\uBE8C\uBE8D\uBE8F\uBE90\uBE91\uBE98\uBE99\uBEA8\uBED0\uBED1\uBED4\uBED7\uBED8\uBEE0\uBEE3\uBEE4\uBEE5\uBEEC\uBF01\uBF08\uBF09\uBF18\uBF19\uBF1B\uBF1C\uBF1D\uBF40\uBF41\uBF44\uBF48\uBF50\uBF51\uBF55\uBF94\uBFB0\uBFC5\uBFCC\uBFCD\uBFD0\uBFD4\uBFDC\uBFDF\uBFE1\uC03C\uC051\uC058\uC05C\uC060\uC068\uC069\uC090\uC091\uC094\uC098\uC0A0\uC0A1\uC0A3\uC0A5\uC0AC\uC0AD\uC0AF\uC0B0\uC0B3\uC0B4\uC0B5\uC0B6\uC0BC\uC0BD\uC0BF\uC0C0\uC0C1\uC0C5\uC0C8\uC0C9\uC0CC\uC0D0\uC0D8\uC0D9\uC0DB\uC0DC\uC0DD\uC0E4"],["bc41","\uD36A",17,"\uD37E\uD37F\uD381\uD382\uD383\uD385\uD386\uD387"],["bc61","\uD388\uD389\uD38A\uD38B\uD38E\uD392",5,"\uD39A\uD39B\uD39D\uD39E\uD39F\uD3A1",6,"\uD3AA\uD3AC\uD3AE"],["bc81","\uD3AF",4,"\uD3B5\uD3B6\uD3B7\uD3B9\uD3BA\uD3BB\uD3BD",6,"\uD3C6\uD3C7\uD3CA",5,"\uD3D1",5,"\uC0E5\uC0E8\uC0EC\uC0F4\uC0F5\uC0F7\uC0F9\uC100\uC104\uC108\uC110\uC115\uC11C",4,"\uC123\uC124\uC126\uC127\uC12C\uC12D\uC12F\uC130\uC131\uC136\uC138\uC139\uC13C\uC140\uC148\uC149\uC14B\uC14C\uC14D\uC154\uC155\uC158\uC15C\uC164\uC165\uC167\uC168\uC169\uC170\uC174\uC178\uC185\uC18C\uC18D\uC18E\uC190\uC194\uC196\uC19C\uC19D\uC19F\uC1A1\uC1A5\uC1A8\uC1A9\uC1AC\uC1B0\uC1BD\uC1C4\uC1C8\uC1CC\uC1D4\uC1D7\uC1D8\uC1E0\uC1E4\uC1E8\uC1F0\uC1F1\uC1F3\uC1FC\uC1FD\uC200\uC204\uC20C\uC20D\uC20F\uC211\uC218\uC219\uC21C\uC21F\uC220\uC228\uC229\uC22B\uC22D"],["bd41","\uD3D7\uD3D9",7,"\uD3E2\uD3E4",7,"\uD3EE\uD3EF\uD3F1\uD3F2\uD3F3\uD3F5\uD3F6\uD3F7"],["bd61","\uD3F8\uD3F9\uD3FA\uD3FB\uD3FE\uD400\uD402",5,"\uD409",13],["bd81","\uD417",5,"\uD41E",25,"\uC22F\uC231\uC232\uC234\uC248\uC250\uC251\uC254\uC258\uC260\uC265\uC26C\uC26D\uC270\uC274\uC27C\uC27D\uC27F\uC281\uC288\uC289\uC290\uC298\uC29B\uC29D\uC2A4\uC2A5\uC2A8\uC2AC\uC2AD\uC2B4\uC2B5\uC2B7\uC2B9\uC2DC\uC2DD\uC2E0\uC2E3\uC2E4\uC2EB\uC2EC\uC2ED\uC2EF\uC2F1\uC2F6\uC2F8\uC2F9\uC2FB\uC2FC\uC300\uC308\uC309\uC30C\uC30D\uC313\uC314\uC315\uC318\uC31C\uC324\uC325\uC328\uC329\uC345\uC368\uC369\uC36C\uC370\uC372\uC378\uC379\uC37C\uC37D\uC384\uC388\uC38C\uC3C0\uC3D8\uC3D9\uC3DC\uC3DF\uC3E0\uC3E2\uC3E8\uC3E9\uC3ED\uC3F4\uC3F5\uC3F8\uC408\uC410\uC424\uC42C\uC430"],["be41","\uD438",7,"\uD441\uD442\uD443\uD445",14],["be61","\uD454",7,"\uD45D\uD45E\uD45F\uD461\uD462\uD463\uD465",7,"\uD46E\uD470\uD471\uD472"],["be81","\uD473",4,"\uD47A\uD47B\uD47D\uD47E\uD481\uD483",4,"\uD48A\uD48C\uD48E",5,"\uD495",8,"\uC434\uC43C\uC43D\uC448\uC464\uC465\uC468\uC46C\uC474\uC475\uC479\uC480\uC494\uC49C\uC4B8\uC4BC\uC4E9\uC4F0\uC4F1\uC4F4\uC4F8\uC4FA\uC4FF\uC500\uC501\uC50C\uC510\uC514\uC51C\uC528\uC529\uC52C\uC530\uC538\uC539\uC53B\uC53D\uC544\uC545\uC548\uC549\uC54A\uC54C\uC54D\uC54E\uC553\uC554\uC555\uC557\uC558\uC559\uC55D\uC55E\uC560\uC561\uC564\uC568\uC570\uC571\uC573\uC574\uC575\uC57C\uC57D\uC580\uC584\uC587\uC58C\uC58D\uC58F\uC591\uC595\uC597\uC598\uC59C\uC5A0\uC5A9\uC5B4\uC5B5\uC5B8\uC5B9\uC5BB\uC5BC\uC5BD\uC5BE\uC5C4",6,"\uC5CC\uC5CE"],["bf41","\uD49E",10,"\uD4AA",14],["bf61","\uD4B9",18,"\uD4CD\uD4CE\uD4CF\uD4D1\uD4D2\uD4D3\uD4D5"],["bf81","\uD4D6",5,"\uD4DD\uD4DE\uD4E0",7,"\uD4E9\uD4EA\uD4EB\uD4ED\uD4EE\uD4EF\uD4F1",6,"\uD4F9\uD4FA\uD4FC\uC5D0\uC5D1\uC5D4\uC5D8\uC5E0\uC5E1\uC5E3\uC5E5\uC5EC\uC5ED\uC5EE\uC5F0\uC5F4\uC5F6\uC5F7\uC5FC",5,"\uC605\uC606\uC607\uC608\uC60C\uC610\uC618\uC619\uC61B\uC61C\uC624\uC625\uC628\uC62C\uC62D\uC62E\uC630\uC633\uC634\uC635\uC637\uC639\uC63B\uC640\uC641\uC644\uC648\uC650\uC651\uC653\uC654\uC655\uC65C\uC65D\uC660\uC66C\uC66F\uC671\uC678\uC679\uC67C\uC680\uC688\uC689\uC68B\uC68D\uC694\uC695\uC698\uC69C\uC6A4\uC6A5\uC6A7\uC6A9\uC6B0\uC6B1\uC6B4\uC6B8\uC6B9\uC6BA\uC6C0\uC6C1\uC6C3\uC6C5\uC6CC\uC6CD\uC6D0\uC6D4\uC6DC\uC6DD\uC6E0\uC6E1\uC6E8"],["c041","\uD4FE",5,"\uD505\uD506\uD507\uD509\uD50A\uD50B\uD50D",6,"\uD516\uD518",5],["c061","\uD51E",25],["c081","\uD538\uD539\uD53A\uD53B\uD53E\uD53F\uD541\uD542\uD543\uD545",6,"\uD54E\uD550\uD552",5,"\uD55A\uD55B\uD55D\uD55E\uD55F\uD561\uD562\uD563\uC6E9\uC6EC\uC6F0\uC6F8\uC6F9\uC6FD\uC704\uC705\uC708\uC70C\uC714\uC715\uC717\uC719\uC720\uC721\uC724\uC728\uC730\uC731\uC733\uC735\uC737\uC73C\uC73D\uC740\uC744\uC74A\uC74C\uC74D\uC74F\uC751",7,"\uC75C\uC760\uC768\uC76B\uC774\uC775\uC778\uC77C\uC77D\uC77E\uC783\uC784\uC785\uC787\uC788\uC789\uC78A\uC78E\uC790\uC791\uC794\uC796\uC797\uC798\uC79A\uC7A0\uC7A1\uC7A3\uC7A4\uC7A5\uC7A6\uC7AC\uC7AD\uC7B0\uC7B4\uC7BC\uC7BD\uC7BF\uC7C0\uC7C1\uC7C8\uC7C9\uC7CC\uC7CE\uC7D0\uC7D8\uC7DD\uC7E4\uC7E8\uC7EC\uC800\uC801\uC804\uC808\uC80A"],["c141","\uD564\uD566\uD567\uD56A\uD56C\uD56E",5,"\uD576\uD577\uD579\uD57A\uD57B\uD57D",6,"\uD586\uD58A\uD58B"],["c161","\uD58C\uD58D\uD58E\uD58F\uD591",19,"\uD5A6\uD5A7"],["c181","\uD5A8",31,"\uC810\uC811\uC813\uC815\uC816\uC81C\uC81D\uC820\uC824\uC82C\uC82D\uC82F\uC831\uC838\uC83C\uC840\uC848\uC849\uC84C\uC84D\uC854\uC870\uC871\uC874\uC878\uC87A\uC880\uC881\uC883\uC885\uC886\uC887\uC88B\uC88C\uC88D\uC894\uC89D\uC89F\uC8A1\uC8A8\uC8BC\uC8BD\uC8C4\uC8C8\uC8CC\uC8D4\uC8D5\uC8D7\uC8D9\uC8E0\uC8E1\uC8E4\uC8F5\uC8FC\uC8FD\uC900\uC904\uC905\uC906\uC90C\uC90D\uC90F\uC911\uC918\uC92C\uC934\uC950\uC951\uC954\uC958\uC960\uC961\uC963\uC96C\uC970\uC974\uC97C\uC988\uC989\uC98C\uC990\uC998\uC999\uC99B\uC99D\uC9C0\uC9C1\uC9C4\uC9C7\uC9C8\uC9CA\uC9D0\uC9D1\uC9D3"],["c241","\uD5CA\uD5CB\uD5CD\uD5CE\uD5CF\uD5D1\uD5D3",4,"\uD5DA\uD5DC\uD5DE",5,"\uD5E6\uD5E7\uD5E9\uD5EA\uD5EB\uD5ED\uD5EE"],["c261","\uD5EF",4,"\uD5F6\uD5F8\uD5FA",5,"\uD602\uD603\uD605\uD606\uD607\uD609",6,"\uD612"],["c281","\uD616",5,"\uD61D\uD61E\uD61F\uD621\uD622\uD623\uD625",7,"\uD62E",9,"\uD63A\uD63B\uC9D5\uC9D6\uC9D9\uC9DA\uC9DC\uC9DD\uC9E0\uC9E2\uC9E4\uC9E7\uC9EC\uC9ED\uC9EF\uC9F0\uC9F1\uC9F8\uC9F9\uC9FC\uCA00\uCA08\uCA09\uCA0B\uCA0C\uCA0D\uCA14\uCA18\uCA29\uCA4C\uCA4D\uCA50\uCA54\uCA5C\uCA5D\uCA5F\uCA60\uCA61\uCA68\uCA7D\uCA84\uCA98\uCABC\uCABD\uCAC0\uCAC4\uCACC\uCACD\uCACF\uCAD1\uCAD3\uCAD8\uCAD9\uCAE0\uCAEC\uCAF4\uCB08\uCB10\uCB14\uCB18\uCB20\uCB21\uCB41\uCB48\uCB49\uCB4C\uCB50\uCB58\uCB59\uCB5D\uCB64\uCB78\uCB79\uCB9C\uCBB8\uCBD4\uCBE4\uCBE7\uCBE9\uCC0C\uCC0D\uCC10\uCC14\uCC1C\uCC1D\uCC21\uCC22\uCC27\uCC28\uCC29\uCC2C\uCC2E\uCC30\uCC38\uCC39\uCC3B"],["c341","\uD63D\uD63E\uD63F\uD641\uD642\uD643\uD644\uD646\uD647\uD64A\uD64C\uD64E\uD64F\uD650\uD652\uD653\uD656\uD657\uD659\uD65A\uD65B\uD65D",4],["c361","\uD662",4,"\uD668\uD66A",5,"\uD672\uD673\uD675",11],["c381","\uD681\uD682\uD684\uD686",5,"\uD68E\uD68F\uD691\uD692\uD693\uD695",7,"\uD69E\uD6A0\uD6A2",5,"\uD6A9\uD6AA\uCC3C\uCC3D\uCC3E\uCC44\uCC45\uCC48\uCC4C\uCC54\uCC55\uCC57\uCC58\uCC59\uCC60\uCC64\uCC66\uCC68\uCC70\uCC75\uCC98\uCC99\uCC9C\uCCA0\uCCA8\uCCA9\uCCAB\uCCAC\uCCAD\uCCB4\uCCB5\uCCB8\uCCBC\uCCC4\uCCC5\uCCC7\uCCC9\uCCD0\uCCD4\uCCE4\uCCEC\uCCF0\uCD01\uCD08\uCD09\uCD0C\uCD10\uCD18\uCD19\uCD1B\uCD1D\uCD24\uCD28\uCD2C\uCD39\uCD5C\uCD60\uCD64\uCD6C\uCD6D\uCD6F\uCD71\uCD78\uCD88\uCD94\uCD95\uCD98\uCD9C\uCDA4\uCDA5\uCDA7\uCDA9\uCDB0\uCDC4\uCDCC\uCDD0\uCDE8\uCDEC\uCDF0\uCDF8\uCDF9\uCDFB\uCDFD\uCE04\uCE08\uCE0C\uCE14\uCE19\uCE20\uCE21\uCE24\uCE28\uCE30\uCE31\uCE33\uCE35"],["c441","\uD6AB\uD6AD\uD6AE\uD6AF\uD6B1",7,"\uD6BA\uD6BC",7,"\uD6C6\uD6C7\uD6C9\uD6CA\uD6CB"],["c461","\uD6CD\uD6CE\uD6CF\uD6D0\uD6D2\uD6D3\uD6D5\uD6D6\uD6D8\uD6DA",5,"\uD6E1\uD6E2\uD6E3\uD6E5\uD6E6\uD6E7\uD6E9",4],["c481","\uD6EE\uD6EF\uD6F1\uD6F2\uD6F3\uD6F4\uD6F6",5,"\uD6FE\uD6FF\uD701\uD702\uD703\uD705",11,"\uD712\uD713\uD714\uCE58\uCE59\uCE5C\uCE5F\uCE60\uCE61\uCE68\uCE69\uCE6B\uCE6D\uCE74\uCE75\uCE78\uCE7C\uCE84\uCE85\uCE87\uCE89\uCE90\uCE91\uCE94\uCE98\uCEA0\uCEA1\uCEA3\uCEA4\uCEA5\uCEAC\uCEAD\uCEC1\uCEE4\uCEE5\uCEE8\uCEEB\uCEEC\uCEF4\uCEF5\uCEF7\uCEF8\uCEF9\uCF00\uCF01\uCF04\uCF08\uCF10\uCF11\uCF13\uCF15\uCF1C\uCF20\uCF24\uCF2C\uCF2D\uCF2F\uCF30\uCF31\uCF38\uCF54\uCF55\uCF58\uCF5C\uCF64\uCF65\uCF67\uCF69\uCF70\uCF71\uCF74\uCF78\uCF80\uCF85\uCF8C\uCFA1\uCFA8\uCFB0\uCFC4\uCFE0\uCFE1\uCFE4\uCFE8\uCFF0\uCFF1\uCFF3\uCFF5\uCFFC\uD000\uD004\uD011\uD018\uD02D\uD034\uD035\uD038\uD03C"],["c541","\uD715\uD716\uD717\uD71A\uD71B\uD71D\uD71E\uD71F\uD721",6,"\uD72A\uD72C\uD72E",5,"\uD736\uD737\uD739"],["c561","\uD73A\uD73B\uD73D",6,"\uD745\uD746\uD748\uD74A",5,"\uD752\uD753\uD755\uD75A",4],["c581","\uD75F\uD762\uD764\uD766\uD767\uD768\uD76A\uD76B\uD76D\uD76E\uD76F\uD771\uD772\uD773\uD775",6,"\uD77E\uD77F\uD780\uD782",5,"\uD78A\uD78B\uD044\uD045\uD047\uD049\uD050\uD054\uD058\uD060\uD06C\uD06D\uD070\uD074\uD07C\uD07D\uD081\uD0A4\uD0A5\uD0A8\uD0AC\uD0B4\uD0B5\uD0B7\uD0B9\uD0C0\uD0C1\uD0C4\uD0C8\uD0C9\uD0D0\uD0D1\uD0D3\uD0D4\uD0D5\uD0DC\uD0DD\uD0E0\uD0E4\uD0EC\uD0ED\uD0EF\uD0F0\uD0F1\uD0F8\uD10D\uD130\uD131\uD134\uD138\uD13A\uD140\uD141\uD143\uD144\uD145\uD14C\uD14D\uD150\uD154\uD15C\uD15D\uD15F\uD161\uD168\uD16C\uD17C\uD184\uD188\uD1A0\uD1A1\uD1A4\uD1A8\uD1B0\uD1B1\uD1B3\uD1B5\uD1BA\uD1BC\uD1C0\uD1D8\uD1F4\uD1F8\uD207\uD209\uD210\uD22C\uD22D\uD230\uD234\uD23C\uD23D\uD23F\uD241\uD248\uD25C"],["c641","\uD78D\uD78E\uD78F\uD791",6,"\uD79A\uD79C\uD79E",5],["c6a1","\uD264\uD280\uD281\uD284\uD288\uD290\uD291\uD295\uD29C\uD2A0\uD2A4\uD2AC\uD2B1\uD2B8\uD2B9\uD2BC\uD2BF\uD2C0\uD2C2\uD2C8\uD2C9\uD2CB\uD2D4\uD2D8\uD2DC\uD2E4\uD2E5\uD2F0\uD2F1\uD2F4\uD2F8\uD300\uD301\uD303\uD305\uD30C\uD30D\uD30E\uD310\uD314\uD316\uD31C\uD31D\uD31F\uD320\uD321\uD325\uD328\uD329\uD32C\uD330\uD338\uD339\uD33B\uD33C\uD33D\uD344\uD345\uD37C\uD37D\uD380\uD384\uD38C\uD38D\uD38F\uD390\uD391\uD398\uD399\uD39C\uD3A0\uD3A8\uD3A9\uD3AB\uD3AD\uD3B4\uD3B8\uD3BC\uD3C4\uD3C5\uD3C8\uD3C9\uD3D0\uD3D8\uD3E1\uD3E3\uD3EC\uD3ED\uD3F0\uD3F4\uD3FC\uD3FD\uD3FF\uD401"],["c7a1","\uD408\uD41D\uD440\uD444\uD45C\uD460\uD464\uD46D\uD46F\uD478\uD479\uD47C\uD47F\uD480\uD482\uD488\uD489\uD48B\uD48D\uD494\uD4A9\uD4CC\uD4D0\uD4D4\uD4DC\uD4DF\uD4E8\uD4EC\uD4F0\uD4F8\uD4FB\uD4FD\uD504\uD508\uD50C\uD514\uD515\uD517\uD53C\uD53D\uD540\uD544\uD54C\uD54D\uD54F\uD551\uD558\uD559\uD55C\uD560\uD565\uD568\uD569\uD56B\uD56D\uD574\uD575\uD578\uD57C\uD584\uD585\uD587\uD588\uD589\uD590\uD5A5\uD5C8\uD5C9\uD5CC\uD5D0\uD5D2\uD5D8\uD5D9\uD5DB\uD5DD\uD5E4\uD5E5\uD5E8\uD5EC\uD5F4\uD5F5\uD5F7\uD5F9\uD600\uD601\uD604\uD608\uD610\uD611\uD613\uD614\uD615\uD61C\uD620"],["c8a1","\uD624\uD62D\uD638\uD639\uD63C\uD640\uD645\uD648\uD649\uD64B\uD64D\uD651\uD654\uD655\uD658\uD65C\uD667\uD669\uD670\uD671\uD674\uD683\uD685\uD68C\uD68D\uD690\uD694\uD69D\uD69F\uD6A1\uD6A8\uD6AC\uD6B0\uD6B9\uD6BB\uD6C4\uD6C5\uD6C8\uD6CC\uD6D1\uD6D4\uD6D7\uD6D9\uD6E0\uD6E4\uD6E8\uD6F0\uD6F5\uD6FC\uD6FD\uD700\uD704\uD711\uD718\uD719\uD71C\uD720\uD728\uD729\uD72B\uD72D\uD734\uD735\uD738\uD73C\uD744\uD747\uD749\uD750\uD751\uD754\uD756\uD757\uD758\uD759\uD760\uD761\uD763\uD765\uD769\uD76C\uD770\uD774\uD77C\uD77D\uD781\uD788\uD789\uD78C\uD790\uD798\uD799\uD79B\uD79D"],["caa1","\u4F3D\u4F73\u5047\u50F9\u52A0\u53EF\u5475\u54E5\u5609\u5AC1\u5BB6\u6687\u67B6\u67B7\u67EF\u6B4C\u73C2\u75C2\u7A3C\u82DB\u8304\u8857\u8888\u8A36\u8CC8\u8DCF\u8EFB\u8FE6\u99D5\u523B\u5374\u5404\u606A\u6164\u6BBC\u73CF\u811A\u89BA\u89D2\u95A3\u4F83\u520A\u58BE\u5978\u59E6\u5E72\u5E79\u61C7\u63C0\u6746\u67EC\u687F\u6F97\u764E\u770B\u78F5\u7A08\u7AFF\u7C21\u809D\u826E\u8271\u8AEB\u9593\u4E6B\u559D\u66F7\u6E34\u78A3\u7AED\u845B\u8910\u874E\u97A8\u52D8\u574E\u582A\u5D4C\u611F\u61BE\u6221\u6562\u67D1\u6A44\u6E1B\u7518\u75B3\u76E3\u77B0\u7D3A\u90AF\u9451\u9452\u9F95"],["cba1","\u5323\u5CAC\u7532\u80DB\u9240\u9598\u525B\u5808\u59DC\u5CA1\u5D17\u5EB7\u5F3A\u5F4A\u6177\u6C5F\u757A\u7586\u7CE0\u7D73\u7DB1\u7F8C\u8154\u8221\u8591\u8941\u8B1B\u92FC\u964D\u9C47\u4ECB\u4EF7\u500B\u51F1\u584F\u6137\u613E\u6168\u6539\u69EA\u6F11\u75A5\u7686\u76D6\u7B87\u82A5\u84CB\uF900\u93A7\u958B\u5580\u5BA2\u5751\uF901\u7CB3\u7FB9\u91B5\u5028\u53BB\u5C45\u5DE8\u62D2\u636E\u64DA\u64E7\u6E20\u70AC\u795B\u8DDD\u8E1E\uF902\u907D\u9245\u92F8\u4E7E\u4EF6\u5065\u5DFE\u5EFA\u6106\u6957\u8171\u8654\u8E47\u9375\u9A2B\u4E5E\u5091\u6770\u6840\u5109\u528D\u5292\u6AA2"],["cca1","\u77BC\u9210\u9ED4\u52AB\u602F\u8FF2\u5048\u61A9\u63ED\u64CA\u683C\u6A84\u6FC0\u8188\u89A1\u9694\u5805\u727D\u72AC\u7504\u7D79\u7E6D\u80A9\u898B\u8B74\u9063\u9D51\u6289\u6C7A\u6F54\u7D50\u7F3A\u8A23\u517C\u614A\u7B9D\u8B19\u9257\u938C\u4EAC\u4FD3\u501E\u50BE\u5106\u52C1\u52CD\u537F\u5770\u5883\u5E9A\u5F91\u6176\u61AC\u64CE\u656C\u666F\u66BB\u66F4\u6897\u6D87\u7085\u70F1\u749F\u74A5\u74CA\u75D9\u786C\u78EC\u7ADF\u7AF6\u7D45\u7D93\u8015\u803F\u811B\u8396\u8B66\u8F15\u9015\u93E1\u9803\u9838\u9A5A\u9BE8\u4FC2\u5553\u583A\u5951\u5B63\u5C46\u60B8\u6212\u6842\u68B0"],["cda1","\u68E8\u6EAA\u754C\u7678\u78CE\u7A3D\u7CFB\u7E6B\u7E7C\u8A08\u8AA1\u8C3F\u968E\u9DC4\u53E4\u53E9\u544A\u5471\u56FA\u59D1\u5B64\u5C3B\u5EAB\u62F7\u6537\u6545\u6572\u66A0\u67AF\u69C1\u6CBD\u75FC\u7690\u777E\u7A3F\u7F94\u8003\u80A1\u818F\u82E6\u82FD\u83F0\u85C1\u8831\u88B4\u8AA5\uF903\u8F9C\u932E\u96C7\u9867\u9AD8\u9F13\u54ED\u659B\u66F2\u688F\u7A40\u8C37\u9D60\u56F0\u5764\u5D11\u6606\u68B1\u68CD\u6EFE\u7428\u889E\u9BE4\u6C68\uF904\u9AA8\u4F9B\u516C\u5171\u529F\u5B54\u5DE5\u6050\u606D\u62F1\u63A7\u653B\u73D9\u7A7A\u86A3\u8CA2\u978F\u4E32\u5BE1\u6208\u679C\u74DC"],["cea1","\u79D1\u83D3\u8A87\u8AB2\u8DE8\u904E\u934B\u9846\u5ED3\u69E8\u85FF\u90ED\uF905\u51A0\u5B98\u5BEC\u6163\u68FA\u6B3E\u704C\u742F\u74D8\u7BA1\u7F50\u83C5\u89C0\u8CAB\u95DC\u9928\u522E\u605D\u62EC\u9002\u4F8A\u5149\u5321\u58D9\u5EE3\u66E0\u6D38\u709A\u72C2\u73D6\u7B50\u80F1\u945B\u5366\u639B\u7F6B\u4E56\u5080\u584A\u58DE\u602A\u6127\u62D0\u69D0\u9B41\u5B8F\u7D18\u80B1\u8F5F\u4EA4\u50D1\u54AC\u55AC\u5B0C\u5DA0\u5DE7\u652A\u654E\u6821\u6A4B\u72E1\u768E\u77EF\u7D5E\u7FF9\u81A0\u854E\u86DF\u8F03\u8F4E\u90CA\u9903\u9A55\u9BAB\u4E18\u4E45\u4E5D\u4EC7\u4FF1\u5177\u52FE"],["cfa1","\u5340\u53E3\u53E5\u548E\u5614\u5775\u57A2\u5BC7\u5D87\u5ED0\u61FC\u62D8\u6551\u67B8\u67E9\u69CB\u6B50\u6BC6\u6BEC\u6C42\u6E9D\u7078\u72D7\u7396\u7403\u77BF\u77E9\u7A76\u7D7F\u8009\u81FC\u8205\u820A\u82DF\u8862\u8B33\u8CFC\u8EC0\u9011\u90B1\u9264\u92B6\u99D2\u9A45\u9CE9\u9DD7\u9F9C\u570B\u5C40\u83CA\u97A0\u97AB\u9EB4\u541B\u7A98\u7FA4\u88D9\u8ECD\u90E1\u5800\u5C48\u6398\u7A9F\u5BAE\u5F13\u7A79\u7AAE\u828E\u8EAC\u5026\u5238\u52F8\u5377\u5708\u62F3\u6372\u6B0A\u6DC3\u7737\u53A5\u7357\u8568\u8E76\u95D5\u673A\u6AC3\u6F70\u8A6D\u8ECC\u994B\uF906\u6677\u6B78\u8CB4"],["d0a1","\u9B3C\uF907\u53EB\u572D\u594E\u63C6\u69FB\u73EA\u7845\u7ABA\u7AC5\u7CFE\u8475\u898F\u8D73\u9035\u95A8\u52FB\u5747\u7547\u7B60\u83CC\u921E\uF908\u6A58\u514B\u524B\u5287\u621F\u68D8\u6975\u9699\u50C5\u52A4\u52E4\u61C3\u65A4\u6839\u69FF\u747E\u7B4B\u82B9\u83EB\u89B2\u8B39\u8FD1\u9949\uF909\u4ECA\u5997\u64D2\u6611\u6A8E\u7434\u7981\u79BD\u82A9\u887E\u887F\u895F\uF90A\u9326\u4F0B\u53CA\u6025\u6271\u6C72\u7D1A\u7D66\u4E98\u5162\u77DC\u80AF\u4F01\u4F0E\u5176\u5180\u55DC\u5668\u573B\u57FA\u57FC\u5914\u5947\u5993\u5BC4\u5C90\u5D0E\u5DF1\u5E7E\u5FCC\u6280\u65D7\u65E3"],["d1a1","\u671E\u671F\u675E\u68CB\u68C4\u6A5F\u6B3A\u6C23\u6C7D\u6C82\u6DC7\u7398\u7426\u742A\u7482\u74A3\u7578\u757F\u7881\u78EF\u7941\u7947\u7948\u797A\u7B95\u7D00\u7DBA\u7F88\u8006\u802D\u808C\u8A18\u8B4F\u8C48\u8D77\u9321\u9324\u98E2\u9951\u9A0E\u9A0F\u9A65\u9E92\u7DCA\u4F76\u5409\u62EE\u6854\u91D1\u55AB\u513A\uF90B\uF90C\u5A1C\u61E6\uF90D\u62CF\u62FF\uF90E",5,"\u90A3\uF914",4,"\u8AFE\uF919\uF91A\uF91B\uF91C\u6696\uF91D\u7156\uF91E\uF91F\u96E3\uF920\u634F\u637A\u5357\uF921\u678F\u6960\u6E73\uF922\u7537\uF923\uF924\uF925"],["d2a1","\u7D0D\uF926\uF927\u8872\u56CA\u5A18\uF928",4,"\u4E43\uF92D\u5167\u5948\u67F0\u8010\uF92E\u5973\u5E74\u649A\u79CA\u5FF5\u606C\u62C8\u637B\u5BE7\u5BD7\u52AA\uF92F\u5974\u5F29\u6012\uF930\uF931\uF932\u7459\uF933",5,"\u99D1\uF939",10,"\u6FC3\uF944\uF945\u81BF\u8FB2\u60F1\uF946\uF947\u8166\uF948\uF949\u5C3F\uF94A",7,"\u5AE9\u8A25\u677B\u7D10\uF952",5,"\u80FD\uF958\uF959\u5C3C\u6CE5\u533F\u6EBA\u591A\u8336"],["d3a1","\u4E39\u4EB6\u4F46\u55AE\u5718\u58C7\u5F56\u65B7\u65E6\u6A80\u6BB5\u6E4D\u77ED\u7AEF\u7C1E\u7DDE\u86CB\u8892\u9132\u935B\u64BB\u6FBE\u737A\u75B8\u9054\u5556\u574D\u61BA\u64D4\u66C7\u6DE1\u6E5B\u6F6D\u6FB9\u75F0\u8043\u81BD\u8541\u8983\u8AC7\u8B5A\u931F\u6C93\u7553\u7B54\u8E0F\u905D\u5510\u5802\u5858\u5E62\u6207\u649E\u68E0\u7576\u7CD6\u87B3\u9EE8\u4EE3\u5788\u576E\u5927\u5C0D\u5CB1\u5E36\u5F85\u6234\u64E1\u73B3\u81FA\u888B\u8CB8\u968A\u9EDB\u5B85\u5FB7\u60B3\u5012\u5200\u5230\u5716\u5835\u5857\u5C0E\u5C60\u5CF6\u5D8B\u5EA6\u5F92\u60BC\u6311\u6389\u6417\u6843"],["d4a1","\u68F9\u6AC2\u6DD8\u6E21\u6ED4\u6FE4\u71FE\u76DC\u7779\u79B1\u7A3B\u8404\u89A9\u8CED\u8DF3\u8E48\u9003\u9014\u9053\u90FD\u934D\u9676\u97DC\u6BD2\u7006\u7258\u72A2\u7368\u7763\u79BF\u7BE4\u7E9B\u8B80\u58A9\u60C7\u6566\u65FD\u66BE\u6C8C\u711E\u71C9\u8C5A\u9813\u4E6D\u7A81\u4EDD\u51AC\u51CD\u52D5\u540C\u61A7\u6771\u6850\u68DF\u6D1E\u6F7C\u75BC\u77B3\u7AE5\u80F4\u8463\u9285\u515C\u6597\u675C\u6793\u75D8\u7AC7\u8373\uF95A\u8C46\u9017\u982D\u5C6F\u81C0\u829A\u9041\u906F\u920D\u5F97\u5D9D\u6A59\u71C8\u767B\u7B49\u85E4\u8B04\u9127\u9A30\u5587\u61F6\uF95B\u7669\u7F85"],["d5a1","\u863F\u87BA\u88F8\u908F\uF95C\u6D1B\u70D9\u73DE\u7D61\u843D\uF95D\u916A\u99F1\uF95E\u4E82\u5375\u6B04\u6B12\u703E\u721B\u862D\u9E1E\u524C\u8FA3\u5D50\u64E5\u652C\u6B16\u6FEB\u7C43\u7E9C\u85CD\u8964\u89BD\u62C9\u81D8\u881F\u5ECA\u6717\u6D6A\u72FC\u7405\u746F\u8782\u90DE\u4F86\u5D0D\u5FA0\u840A\u51B7\u63A0\u7565\u4EAE\u5006\u5169\u51C9\u6881\u6A11\u7CAE\u7CB1\u7CE7\u826F\u8AD2\u8F1B\u91CF\u4FB6\u5137\u52F5\u5442\u5EEC\u616E\u623E\u65C5\u6ADA\u6FFE\u792A\u85DC\u8823\u95AD\u9A62\u9A6A\u9E97\u9ECE\u529B\u66C6\u6B77\u701D\u792B\u8F62\u9742\u6190\u6200\u6523\u6F23"],["d6a1","\u7149\u7489\u7DF4\u806F\u84EE\u8F26\u9023\u934A\u51BD\u5217\u52A3\u6D0C\u70C8\u88C2\u5EC9\u6582\u6BAE\u6FC2\u7C3E\u7375\u4EE4\u4F36\u56F9\uF95F\u5CBA\u5DBA\u601C\u73B2\u7B2D\u7F9A\u7FCE\u8046\u901E\u9234\u96F6\u9748\u9818\u9F61\u4F8B\u6FA7\u79AE\u91B4\u96B7\u52DE\uF960\u6488\u64C4\u6AD3\u6F5E\u7018\u7210\u76E7\u8001\u8606\u865C\u8DEF\u8F05\u9732\u9B6F\u9DFA\u9E75\u788C\u797F\u7DA0\u83C9\u9304\u9E7F\u9E93\u8AD6\u58DF\u5F04\u6727\u7027\u74CF\u7C60\u807E\u5121\u7028\u7262\u78CA\u8CC2\u8CDA\u8CF4\u96F7\u4E86\u50DA\u5BEE\u5ED6\u6599\u71CE\u7642\u77AD\u804A\u84FC"],["d7a1","\u907C\u9B27\u9F8D\u58D8\u5A41\u5C62\u6A13\u6DDA\u6F0F\u763B\u7D2F\u7E37\u851E\u8938\u93E4\u964B\u5289\u65D2\u67F3\u69B4\u6D41\u6E9C\u700F\u7409\u7460\u7559\u7624\u786B\u8B2C\u985E\u516D\u622E\u9678\u4F96\u502B\u5D19\u6DEA\u7DB8\u8F2A\u5F8B\u6144\u6817\uF961\u9686\u52D2\u808B\u51DC\u51CC\u695E\u7A1C\u7DBE\u83F1\u9675\u4FDA\u5229\u5398\u540F\u550E\u5C65\u60A7\u674E\u68A8\u6D6C\u7281\u72F8\u7406\u7483\uF962\u75E2\u7C6C\u7F79\u7FB8\u8389\u88CF\u88E1\u91CC\u91D0\u96E2\u9BC9\u541D\u6F7E\u71D0\u7498\u85FA\u8EAA\u96A3\u9C57\u9E9F\u6797\u6DCB\u7433\u81E8\u9716\u782C"],["d8a1","\u7ACB\u7B20\u7C92\u6469\u746A\u75F2\u78BC\u78E8\u99AC\u9B54\u9EBB\u5BDE\u5E55\u6F20\u819C\u83AB\u9088\u4E07\u534D\u5A29\u5DD2\u5F4E\u6162\u633D\u6669\u66FC\u6EFF\u6F2B\u7063\u779E\u842C\u8513\u883B\u8F13\u9945\u9C3B\u551C\u62B9\u672B\u6CAB\u8309\u896A\u977A\u4EA1\u5984\u5FD8\u5FD9\u671B\u7DB2\u7F54\u8292\u832B\u83BD\u8F1E\u9099\u57CB\u59B9\u5A92\u5BD0\u6627\u679A\u6885\u6BCF\u7164\u7F75\u8CB7\u8CE3\u9081\u9B45\u8108\u8C8A\u964C\u9A40\u9EA5\u5B5F\u6C13\u731B\u76F2\u76DF\u840C\u51AA\u8993\u514D\u5195\u52C9\u68C9\u6C94\u7704\u7720\u7DBF\u7DEC\u9762\u9EB5\u6EC5"],["d9a1","\u8511\u51A5\u540D\u547D\u660E\u669D\u6927\u6E9F\u76BF\u7791\u8317\u84C2\u879F\u9169\u9298\u9CF4\u8882\u4FAE\u5192\u52DF\u59C6\u5E3D\u6155\u6478\u6479\u66AE\u67D0\u6A21\u6BCD\u6BDB\u725F\u7261\u7441\u7738\u77DB\u8017\u82BC\u8305\u8B00\u8B28\u8C8C\u6728\u6C90\u7267\u76EE\u7766\u7A46\u9DA9\u6B7F\u6C92\u5922\u6726\u8499\u536F\u5893\u5999\u5EDF\u63CF\u6634\u6773\u6E3A\u732B\u7AD7\u82D7\u9328\u52D9\u5DEB\u61AE\u61CB\u620A\u62C7\u64AB\u65E0\u6959\u6B66\u6BCB\u7121\u73F7\u755D\u7E46\u821E\u8302\u856A\u8AA3\u8CBF\u9727\u9D61\u58A8\u9ED8\u5011\u520E\u543B\u554F\u6587"],["daa1","\u6C76\u7D0A\u7D0B\u805E\u868A\u9580\u96EF\u52FF\u6C95\u7269\u5473\u5A9A\u5C3E\u5D4B\u5F4C\u5FAE\u672A\u68B6\u6963\u6E3C\u6E44\u7709\u7C73\u7F8E\u8587\u8B0E\u8FF7\u9761\u9EF4\u5CB7\u60B6\u610D\u61AB\u654F\u65FB\u65FC\u6C11\u6CEF\u739F\u73C9\u7DE1\u9594\u5BC6\u871C\u8B10\u525D\u535A\u62CD\u640F\u64B2\u6734\u6A38\u6CCA\u73C0\u749E\u7B94\u7C95\u7E1B\u818A\u8236\u8584\u8FEB\u96F9\u99C1\u4F34\u534A\u53CD\u53DB\u62CC\u642C\u6500\u6591\u69C3\u6CEE\u6F58\u73ED\u7554\u7622\u76E4\u76FC\u78D0\u78FB\u792C\u7D46\u822C\u87E0\u8FD4\u9812\u98EF\u52C3\u62D4\u64A5\u6E24\u6F51"],["dba1","\u767C\u8DCB\u91B1\u9262\u9AEE\u9B43\u5023\u508D\u574A\u59A8\u5C28\u5E47\u5F77\u623F\u653E\u65B9\u65C1\u6609\u678B\u699C\u6EC2\u78C5\u7D21\u80AA\u8180\u822B\u82B3\u84A1\u868C\u8A2A\u8B17\u90A6\u9632\u9F90\u500D\u4FF3\uF963\u57F9\u5F98\u62DC\u6392\u676F\u6E43\u7119\u76C3\u80CC\u80DA\u88F4\u88F5\u8919\u8CE0\u8F29\u914D\u966A\u4F2F\u4F70\u5E1B\u67CF\u6822\u767D\u767E\u9B44\u5E61\u6A0A\u7169\u71D4\u756A\uF964\u7E41\u8543\u85E9\u98DC\u4F10\u7B4F\u7F70\u95A5\u51E1\u5E06\u68B5\u6C3E\u6C4E\u6CDB\u72AF\u7BC4\u8303\u6CD5\u743A\u50FB\u5288\u58C1\u64D8\u6A97\u74A7\u7656"],["dca1","\u78A7\u8617\u95E2\u9739\uF965\u535E\u5F01\u8B8A\u8FA8\u8FAF\u908A\u5225\u77A5\u9C49\u9F08\u4E19\u5002\u5175\u5C5B\u5E77\u661E\u663A\u67C4\u68C5\u70B3\u7501\u75C5\u79C9\u7ADD\u8F27\u9920\u9A08\u4FDD\u5821\u5831\u5BF6\u666E\u6B65\u6D11\u6E7A\u6F7D\u73E4\u752B\u83E9\u88DC\u8913\u8B5C\u8F14\u4F0F\u50D5\u5310\u535C\u5B93\u5FA9\u670D\u798F\u8179\u832F\u8514\u8907\u8986\u8F39\u8F3B\u99A5\u9C12\u672C\u4E76\u4FF8\u5949\u5C01\u5CEF\u5CF0\u6367\u68D2\u70FD\u71A2\u742B\u7E2B\u84EC\u8702\u9022\u92D2\u9CF3\u4E0D\u4ED8\u4FEF\u5085\u5256\u526F\u5426\u5490\u57E0\u592B\u5A66"],["dda1","\u5B5A\u5B75\u5BCC\u5E9C\uF966\u6276\u6577\u65A7\u6D6E\u6EA5\u7236\u7B26\u7C3F\u7F36\u8150\u8151\u819A\u8240\u8299\u83A9\u8A03\u8CA0\u8CE6\u8CFB\u8D74\u8DBA\u90E8\u91DC\u961C\u9644\u99D9\u9CE7\u5317\u5206\u5429\u5674\u58B3\u5954\u596E\u5FFF\u61A4\u626E\u6610\u6C7E\u711A\u76C6\u7C89\u7CDE\u7D1B\u82AC\u8CC1\u96F0\uF967\u4F5B\u5F17\u5F7F\u62C2\u5D29\u670B\u68DA\u787C\u7E43\u9D6C\u4E15\u5099\u5315\u532A\u5351\u5983\u5A62\u5E87\u60B2\u618A\u6249\u6279\u6590\u6787\u69A7\u6BD4\u6BD6\u6BD7\u6BD8\u6CB8\uF968\u7435\u75FA\u7812\u7891\u79D5\u79D8\u7C83\u7DCB\u7FE1\u80A5"],["dea1","\u813E\u81C2\u83F2\u871A\u88E8\u8AB9\u8B6C\u8CBB\u9119\u975E\u98DB\u9F3B\u56AC\u5B2A\u5F6C\u658C\u6AB3\u6BAF\u6D5C\u6FF1\u7015\u725D\u73AD\u8CA7\u8CD3\u983B\u6191\u6C37\u8058\u9A01\u4E4D\u4E8B\u4E9B\u4ED5\u4F3A\u4F3C\u4F7F\u4FDF\u50FF\u53F2\u53F8\u5506\u55E3\u56DB\u58EB\u5962\u5A11\u5BEB\u5BFA\u5C04\u5DF3\u5E2B\u5F99\u601D\u6368\u659C\u65AF\u67F6\u67FB\u68AD\u6B7B\u6C99\u6CD7\u6E23\u7009\u7345\u7802\u793E\u7940\u7960\u79C1\u7BE9\u7D17\u7D72\u8086\u820D\u838E\u84D1\u86C7\u88DF\u8A50\u8A5E\u8B1D\u8CDC\u8D66\u8FAD\u90AA\u98FC\u99DF\u9E9D\u524A\uF969\u6714\uF96A"],["dfa1","\u5098\u522A\u5C71\u6563\u6C55\u73CA\u7523\u759D\u7B97\u849C\u9178\u9730\u4E77\u6492\u6BBA\u715E\u85A9\u4E09\uF96B\u6749\u68EE\u6E17\u829F\u8518\u886B\u63F7\u6F81\u9212\u98AF\u4E0A\u50B7\u50CF\u511F\u5546\u55AA\u5617\u5B40\u5C19\u5CE0\u5E38\u5E8A\u5EA0\u5EC2\u60F3\u6851\u6A61\u6E58\u723D\u7240\u72C0\u76F8\u7965\u7BB1\u7FD4\u88F3\u89F4\u8A73\u8C61\u8CDE\u971C\u585E\u74BD\u8CFD\u55C7\uF96C\u7A61\u7D22\u8272\u7272\u751F\u7525\uF96D\u7B19\u5885\u58FB\u5DBC\u5E8F\u5EB6\u5F90\u6055\u6292\u637F\u654D\u6691\u66D9\u66F8\u6816\u68F2\u7280\u745E\u7B6E\u7D6E\u7DD6\u7F72"],["e0a1","\u80E5\u8212\u85AF\u897F\u8A93\u901D\u92E4\u9ECD\u9F20\u5915\u596D\u5E2D\u60DC\u6614\u6673\u6790\u6C50\u6DC5\u6F5F\u77F3\u78A9\u84C6\u91CB\u932B\u4ED9\u50CA\u5148\u5584\u5B0B\u5BA3\u6247\u657E\u65CB\u6E32\u717D\u7401\u7444\u7487\u74BF\u766C\u79AA\u7DDA\u7E55\u7FA8\u817A\u81B3\u8239\u861A\u87EC\u8A75\u8DE3\u9078\u9291\u9425\u994D\u9BAE\u5368\u5C51\u6954\u6CC4\u6D29\u6E2B\u820C\u859B\u893B\u8A2D\u8AAA\u96EA\u9F67\u5261\u66B9\u6BB2\u7E96\u87FE\u8D0D\u9583\u965D\u651D\u6D89\u71EE\uF96E\u57CE\u59D3\u5BAC\u6027\u60FA\u6210\u661F\u665F\u7329\u73F9\u76DB\u7701\u7B6C"],["e1a1","\u8056\u8072\u8165\u8AA0\u9192\u4E16\u52E2\u6B72\u6D17\u7A05\u7B39\u7D30\uF96F\u8CB0\u53EC\u562F\u5851\u5BB5\u5C0F\u5C11\u5DE2\u6240\u6383\u6414\u662D\u68B3\u6CBC\u6D88\u6EAF\u701F\u70A4\u71D2\u7526\u758F\u758E\u7619\u7B11\u7BE0\u7C2B\u7D20\u7D39\u852C\u856D\u8607\u8A34\u900D\u9061\u90B5\u92B7\u97F6\u9A37\u4FD7\u5C6C\u675F\u6D91\u7C9F\u7E8C\u8B16\u8D16\u901F\u5B6B\u5DFD\u640D\u84C0\u905C\u98E1\u7387\u5B8B\u609A\u677E\u6DDE\u8A1F\u8AA6\u9001\u980C\u5237\uF970\u7051\u788E\u9396\u8870\u91D7\u4FEE\u53D7\u55FD\u56DA\u5782\u58FD\u5AC2\u5B88\u5CAB\u5CC0\u5E25\u6101"],["e2a1","\u620D\u624B\u6388\u641C\u6536\u6578\u6A39\u6B8A\u6C34\u6D19\u6F31\u71E7\u72E9\u7378\u7407\u74B2\u7626\u7761\u79C0\u7A57\u7AEA\u7CB9\u7D8F\u7DAC\u7E61\u7F9E\u8129\u8331\u8490\u84DA\u85EA\u8896\u8AB0\u8B90\u8F38\u9042\u9083\u916C\u9296\u92B9\u968B\u96A7\u96A8\u96D6\u9700\u9808\u9996\u9AD3\u9B1A\u53D4\u587E\u5919\u5B70\u5BBF\u6DD1\u6F5A\u719F\u7421\u74B9\u8085\u83FD\u5DE1\u5F87\u5FAA\u6042\u65EC\u6812\u696F\u6A53\u6B89\u6D35\u6DF3\u73E3\u76FE\u77AC\u7B4D\u7D14\u8123\u821C\u8340\u84F4\u8563\u8A62\u8AC4\u9187\u931E\u9806\u99B4\u620C\u8853\u8FF0\u9265\u5D07\u5D27"],["e3a1","\u5D69\u745F\u819D\u8768\u6FD5\u62FE\u7FD2\u8936\u8972\u4E1E\u4E58\u50E7\u52DD\u5347\u627F\u6607\u7E69\u8805\u965E\u4F8D\u5319\u5636\u59CB\u5AA4\u5C38\u5C4E\u5C4D\u5E02\u5F11\u6043\u65BD\u662F\u6642\u67BE\u67F4\u731C\u77E2\u793A\u7FC5\u8494\u84CD\u8996\u8A66\u8A69\u8AE1\u8C55\u8C7A\u57F4\u5BD4\u5F0F\u606F\u62ED\u690D\u6B96\u6E5C\u7184\u7BD2\u8755\u8B58\u8EFE\u98DF\u98FE\u4F38\u4F81\u4FE1\u547B\u5A20\u5BB8\u613C\u65B0\u6668\u71FC\u7533\u795E\u7D33\u814E\u81E3\u8398\u85AA\u85CE\u8703\u8A0A\u8EAB\u8F9B\uF971\u8FC5\u5931\u5BA4\u5BE6\u6089\u5BE9\u5C0B\u5FC3\u6C81"],["e4a1","\uF972\u6DF1\u700B\u751A\u82AF\u8AF6\u4EC0\u5341\uF973\u96D9\u6C0F\u4E9E\u4FC4\u5152\u555E\u5A25\u5CE8\u6211\u7259\u82BD\u83AA\u86FE\u8859\u8A1D\u963F\u96C5\u9913\u9D09\u9D5D\u580A\u5CB3\u5DBD\u5E44\u60E1\u6115\u63E1\u6A02\u6E25\u9102\u9354\u984E\u9C10\u9F77\u5B89\u5CB8\u6309\u664F\u6848\u773C\u96C1\u978D\u9854\u9B9F\u65A1\u8B01\u8ECB\u95BC\u5535\u5CA9\u5DD6\u5EB5\u6697\u764C\u83F4\u95C7\u58D3\u62BC\u72CE\u9D28\u4EF0\u592E\u600F\u663B\u6B83\u79E7\u9D26\u5393\u54C0\u57C3\u5D16\u611B\u66D6\u6DAF\u788D\u827E\u9698\u9744\u5384\u627C\u6396\u6DB2\u7E0A\u814B\u984D"],["e5a1","\u6AFB\u7F4C\u9DAF\u9E1A\u4E5F\u503B\u51B6\u591C\u60F9\u63F6\u6930\u723A\u8036\uF974\u91CE\u5F31\uF975\uF976\u7D04\u82E5\u846F\u84BB\u85E5\u8E8D\uF977\u4F6F\uF978\uF979\u58E4\u5B43\u6059\u63DA\u6518\u656D\u6698\uF97A\u694A\u6A23\u6D0B\u7001\u716C\u75D2\u760D\u79B3\u7A70\uF97B\u7F8A\uF97C\u8944\uF97D\u8B93\u91C0\u967D\uF97E\u990A\u5704\u5FA1\u65BC\u6F01\u7600\u79A6\u8A9E\u99AD\u9B5A\u9F6C\u5104\u61B6\u6291\u6A8D\u81C6\u5043\u5830\u5F66\u7109\u8A00\u8AFA\u5B7C\u8616\u4FFA\u513C\u56B4\u5944\u63A9\u6DF9\u5DAA\u696D\u5186\u4E88\u4F59\uF97F\uF980\uF981\u5982\uF982"],["e6a1","\uF983\u6B5F\u6C5D\uF984\u74B5\u7916\uF985\u8207\u8245\u8339\u8F3F\u8F5D\uF986\u9918\uF987\uF988\uF989\u4EA6\uF98A\u57DF\u5F79\u6613\uF98B\uF98C\u75AB\u7E79\u8B6F\uF98D\u9006\u9A5B\u56A5\u5827\u59F8\u5A1F\u5BB4\uF98E\u5EF6\uF98F\uF990\u6350\u633B\uF991\u693D\u6C87\u6CBF\u6D8E\u6D93\u6DF5\u6F14\uF992\u70DF\u7136\u7159\uF993\u71C3\u71D5\uF994\u784F\u786F\uF995\u7B75\u7DE3\uF996\u7E2F\uF997\u884D\u8EDF\uF998\uF999\uF99A\u925B\uF99B\u9CF6\uF99C\uF99D\uF99E\u6085\u6D85\uF99F\u71B1\uF9A0\uF9A1\u95B1\u53AD\uF9A2\uF9A3\uF9A4\u67D3\uF9A5\u708E\u7130\u7430\u8276\u82D2"],["e7a1","\uF9A6\u95BB\u9AE5\u9E7D\u66C4\uF9A7\u71C1\u8449\uF9A8\uF9A9\u584B\uF9AA\uF9AB\u5DB8\u5F71\uF9AC\u6620\u668E\u6979\u69AE\u6C38\u6CF3\u6E36\u6F41\u6FDA\u701B\u702F\u7150\u71DF\u7370\uF9AD\u745B\uF9AE\u74D4\u76C8\u7A4E\u7E93\uF9AF\uF9B0\u82F1\u8A60\u8FCE\uF9B1\u9348\uF9B2\u9719\uF9B3\uF9B4\u4E42\u502A\uF9B5\u5208\u53E1\u66F3\u6C6D\u6FCA\u730A\u777F\u7A62\u82AE\u85DD\u8602\uF9B6\u88D4\u8A63\u8B7D\u8C6B\uF9B7\u92B3\uF9B8\u9713\u9810\u4E94\u4F0D\u4FC9\u50B2\u5348\u543E\u5433\u55DA\u5862\u58BA\u5967\u5A1B\u5BE4\u609F\uF9B9\u61CA\u6556\u65FF\u6664\u68A7\u6C5A\u6FB3"],["e8a1","\u70CF\u71AC\u7352\u7B7D\u8708\u8AA4\u9C32\u9F07\u5C4B\u6C83\u7344\u7389\u923A\u6EAB\u7465\u761F\u7A69\u7E15\u860A\u5140\u58C5\u64C1\u74EE\u7515\u7670\u7FC1\u9095\u96CD\u9954\u6E26\u74E6\u7AA9\u7AAA\u81E5\u86D9\u8778\u8A1B\u5A49\u5B8C\u5B9B\u68A1\u6900\u6D63\u73A9\u7413\u742C\u7897\u7DE9\u7FEB\u8118\u8155\u839E\u8C4C\u962E\u9811\u66F0\u5F80\u65FA\u6789\u6C6A\u738B\u502D\u5A03\u6B6A\u77EE\u5916\u5D6C\u5DCD\u7325\u754F\uF9BA\uF9BB\u50E5\u51F9\u582F\u592D\u5996\u59DA\u5BE5\uF9BC\uF9BD\u5DA2\u62D7\u6416\u6493\u64FE\uF9BE\u66DC\uF9BF\u6A48\uF9C0\u71FF\u7464\uF9C1"],["e9a1","\u7A88\u7AAF\u7E47\u7E5E\u8000\u8170\uF9C2\u87EF\u8981\u8B20\u9059\uF9C3\u9080\u9952\u617E\u6B32\u6D74\u7E1F\u8925\u8FB1\u4FD1\u50AD\u5197\u52C7\u57C7\u5889\u5BB9\u5EB8\u6142\u6995\u6D8C\u6E67\u6EB6\u7194\u7462\u7528\u752C\u8073\u8338\u84C9\u8E0A\u9394\u93DE\uF9C4\u4E8E\u4F51\u5076\u512A\u53C8\u53CB\u53F3\u5B87\u5BD3\u5C24\u611A\u6182\u65F4\u725B\u7397\u7440\u76C2\u7950\u7991\u79B9\u7D06\u7FBD\u828B\u85D5\u865E\u8FC2\u9047\u90F5\u91EA\u9685\u96E8\u96E9\u52D6\u5F67\u65ED\u6631\u682F\u715C\u7A36\u90C1\u980A\u4E91\uF9C5\u6A52\u6B9E\u6F90\u7189\u8018\u82B8\u8553"],["eaa1","\u904B\u9695\u96F2\u97FB\u851A\u9B31\u4E90\u718A\u96C4\u5143\u539F\u54E1\u5713\u5712\u57A3\u5A9B\u5AC4\u5BC3\u6028\u613F\u63F4\u6C85\u6D39\u6E72\u6E90\u7230\u733F\u7457\u82D1\u8881\u8F45\u9060\uF9C6\u9662\u9858\u9D1B\u6708\u8D8A\u925E\u4F4D\u5049\u50DE\u5371\u570D\u59D4\u5A01\u5C09\u6170\u6690\u6E2D\u7232\u744B\u7DEF\u80C3\u840E\u8466\u853F\u875F\u885B\u8918\u8B02\u9055\u97CB\u9B4F\u4E73\u4F91\u5112\u516A\uF9C7\u552F\u55A9\u5B7A\u5BA5\u5E7C\u5E7D\u5EBE\u60A0\u60DF\u6108\u6109\u63C4\u6538\u6709\uF9C8\u67D4\u67DA\uF9C9\u6961\u6962\u6CB9\u6D27\uF9CA\u6E38\uF9CB"],["eba1","\u6FE1\u7336\u7337\uF9CC\u745C\u7531\uF9CD\u7652\uF9CE\uF9CF\u7DAD\u81FE\u8438\u88D5\u8A98\u8ADB\u8AED\u8E30\u8E42\u904A\u903E\u907A\u9149\u91C9\u936E\uF9D0\uF9D1\u5809\uF9D2\u6BD3\u8089\u80B2\uF9D3\uF9D4\u5141\u596B\u5C39\uF9D5\uF9D6\u6F64\u73A7\u80E4\u8D07\uF9D7\u9217\u958F\uF9D8\uF9D9\uF9DA\uF9DB\u807F\u620E\u701C\u7D68\u878D\uF9DC\u57A0\u6069\u6147\u6BB7\u8ABE\u9280\u96B1\u4E59\u541F\u6DEB\u852D\u9670\u97F3\u98EE\u63D6\u6CE3\u9091\u51DD\u61C9\u81BA\u9DF9\u4F9D\u501A\u5100\u5B9C\u610F\u61FF\u64EC\u6905\u6BC5\u7591\u77E3\u7FA9\u8264\u858F\u87FB\u8863\u8ABC"],["eca1","\u8B70\u91AB\u4E8C\u4EE5\u4F0A\uF9DD\uF9DE\u5937\u59E8\uF9DF\u5DF2\u5F1B\u5F5B\u6021\uF9E0\uF9E1\uF9E2\uF9E3\u723E\u73E5\uF9E4\u7570\u75CD\uF9E5\u79FB\uF9E6\u800C\u8033\u8084\u82E1\u8351\uF9E7\uF9E8\u8CBD\u8CB3\u9087\uF9E9\uF9EA\u98F4\u990C\uF9EB\uF9EC\u7037\u76CA\u7FCA\u7FCC\u7FFC\u8B1A\u4EBA\u4EC1\u5203\u5370\uF9ED\u54BD\u56E0\u59FB\u5BC5\u5F15\u5FCD\u6E6E\uF9EE\uF9EF\u7D6A\u8335\uF9F0\u8693\u8A8D\uF9F1\u976D\u9777\uF9F2\uF9F3\u4E00\u4F5A\u4F7E\u58F9\u65E5\u6EA2\u9038\u93B0\u99B9\u4EFB\u58EC\u598A\u59D9\u6041\uF9F4\uF9F5\u7A14\uF9F6\u834F\u8CC3\u5165\u5344"],["eda1","\uF9F7\uF9F8\uF9F9\u4ECD\u5269\u5B55\u82BF\u4ED4\u523A\u54A8\u59C9\u59FF\u5B50\u5B57\u5B5C\u6063\u6148\u6ECB\u7099\u716E\u7386\u74F7\u75B5\u78C1\u7D2B\u8005\u81EA\u8328\u8517\u85C9\u8AEE\u8CC7\u96CC\u4F5C\u52FA\u56BC\u65AB\u6628\u707C\u70B8\u7235\u7DBD\u828D\u914C\u96C0\u9D72\u5B71\u68E7\u6B98\u6F7A\u76DE\u5C91\u66AB\u6F5B\u7BB4\u7C2A\u8836\u96DC\u4E08\u4ED7\u5320\u5834\u58BB\u58EF\u596C\u5C07\u5E33\u5E84\u5F35\u638C\u66B2\u6756\u6A1F\u6AA3\u6B0C\u6F3F\u7246\uF9FA\u7350\u748B\u7AE0\u7CA7\u8178\u81DF\u81E7\u838A\u846C\u8523\u8594\u85CF\u88DD\u8D13\u91AC\u9577"],["eea1","\u969C\u518D\u54C9\u5728\u5BB0\u624D\u6750\u683D\u6893\u6E3D\u6ED3\u707D\u7E21\u88C1\u8CA1\u8F09\u9F4B\u9F4E\u722D\u7B8F\u8ACD\u931A\u4F47\u4F4E\u5132\u5480\u59D0\u5E95\u62B5\u6775\u696E\u6A17\u6CAE\u6E1A\u72D9\u732A\u75BD\u7BB8\u7D35\u82E7\u83F9\u8457\u85F7\u8A5B\u8CAF\u8E87\u9019\u90B8\u96CE\u9F5F\u52E3\u540A\u5AE1\u5BC2\u6458\u6575\u6EF4\u72C4\uF9FB\u7684\u7A4D\u7B1B\u7C4D\u7E3E\u7FDF\u837B\u8B2B\u8CCA\u8D64\u8DE1\u8E5F\u8FEA\u8FF9\u9069\u93D1\u4F43\u4F7A\u50B3\u5168\u5178\u524D\u526A\u5861\u587C\u5960\u5C08\u5C55\u5EDB\u609B\u6230\u6813\u6BBF\u6C08\u6FB1"],["efa1","\u714E\u7420\u7530\u7538\u7551\u7672\u7B4C\u7B8B\u7BAD\u7BC6\u7E8F\u8A6E\u8F3E\u8F49\u923F\u9293\u9322\u942B\u96FB\u985A\u986B\u991E\u5207\u622A\u6298\u6D59\u7664\u7ACA\u7BC0\u7D76\u5360\u5CBE\u5E97\u6F38\u70B9\u7C98\u9711\u9B8E\u9EDE\u63A5\u647A\u8776\u4E01\u4E95\u4EAD\u505C\u5075\u5448\u59C3\u5B9A\u5E40\u5EAD\u5EF7\u5F81\u60C5\u633A\u653F\u6574\u65CC\u6676\u6678\u67FE\u6968\u6A89\u6B63\u6C40\u6DC0\u6DE8\u6E1F\u6E5E\u701E\u70A1\u738E\u73FD\u753A\u775B\u7887\u798E\u7A0B\u7A7D\u7CBE\u7D8E\u8247\u8A02\u8AEA\u8C9E\u912D\u914A\u91D8\u9266\u92CC\u9320\u9706\u9756"],["f0a1","\u975C\u9802\u9F0E\u5236\u5291\u557C\u5824\u5E1D\u5F1F\u608C\u63D0\u68AF\u6FDF\u796D\u7B2C\u81CD\u85BA\u88FD\u8AF8\u8E44\u918D\u9664\u969B\u973D\u984C\u9F4A\u4FCE\u5146\u51CB\u52A9\u5632\u5F14\u5F6B\u63AA\u64CD\u65E9\u6641\u66FA\u66F9\u671D\u689D\u68D7\u69FD\u6F15\u6F6E\u7167\u71E5\u722A\u74AA\u773A\u7956\u795A\u79DF\u7A20\u7A95\u7C97\u7CDF\u7D44\u7E70\u8087\u85FB\u86A4\u8A54\u8ABF\u8D99\u8E81\u9020\u906D\u91E3\u963B\u96D5\u9CE5\u65CF\u7C07\u8DB3\u93C3\u5B58\u5C0A\u5352\u62D9\u731D\u5027\u5B97\u5F9E\u60B0\u616B\u68D5\u6DD9\u742E\u7A2E\u7D42\u7D9C\u7E31\u816B"],["f1a1","\u8E2A\u8E35\u937E\u9418\u4F50\u5750\u5DE6\u5EA7\u632B\u7F6A\u4E3B\u4F4F\u4F8F\u505A\u59DD\u80C4\u546A\u5468\u55FE\u594F\u5B99\u5DDE\u5EDA\u665D\u6731\u67F1\u682A\u6CE8\u6D32\u6E4A\u6F8D\u70B7\u73E0\u7587\u7C4C\u7D02\u7D2C\u7DA2\u821F\u86DB\u8A3B\u8A85\u8D70\u8E8A\u8F33\u9031\u914E\u9152\u9444\u99D0\u7AF9\u7CA5\u4FCA\u5101\u51C6\u57C8\u5BEF\u5CFB\u6659\u6A3D\u6D5A\u6E96\u6FEC\u710C\u756F\u7AE3\u8822\u9021\u9075\u96CB\u99FF\u8301\u4E2D\u4EF2\u8846\u91CD\u537D\u6ADB\u696B\u6C41\u847A\u589E\u618E\u66FE\u62EF\u70DD\u7511\u75C7\u7E52\u84B8\u8B49\u8D08\u4E4B\u53EA"],["f2a1","\u54AB\u5730\u5740\u5FD7\u6301\u6307\u646F\u652F\u65E8\u667A\u679D\u67B3\u6B62\u6C60\u6C9A\u6F2C\u77E5\u7825\u7949\u7957\u7D19\u80A2\u8102\u81F3\u829D\u82B7\u8718\u8A8C\uF9FC\u8D04\u8DBE\u9072\u76F4\u7A19\u7A37\u7E54\u8077\u5507\u55D4\u5875\u632F\u6422\u6649\u664B\u686D\u699B\u6B84\u6D25\u6EB1\u73CD\u7468\u74A1\u755B\u75B9\u76E1\u771E\u778B\u79E6\u7E09\u7E1D\u81FB\u852F\u8897\u8A3A\u8CD1\u8EEB\u8FB0\u9032\u93AD\u9663\u9673\u9707\u4F84\u53F1\u59EA\u5AC9\u5E19\u684E\u74C6\u75BE\u79E9\u7A92\u81A3\u86ED\u8CEA\u8DCC\u8FED\u659F\u6715\uF9FD\u57F7\u6F57\u7DDD\u8F2F"],["f3a1","\u93F6\u96C6\u5FB5\u61F2\u6F84\u4E14\u4F98\u501F\u53C9\u55DF\u5D6F\u5DEE\u6B21\u6B64\u78CB\u7B9A\uF9FE\u8E49\u8ECA\u906E\u6349\u643E\u7740\u7A84\u932F\u947F\u9F6A\u64B0\u6FAF\u71E6\u74A8\u74DA\u7AC4\u7C12\u7E82\u7CB2\u7E98\u8B9A\u8D0A\u947D\u9910\u994C\u5239\u5BDF\u64E6\u672D\u7D2E\u50ED\u53C3\u5879\u6158\u6159\u61FA\u65AC\u7AD9\u8B92\u8B96\u5009\u5021\u5275\u5531\u5A3C\u5EE0\u5F70\u6134\u655E\u660C\u6636\u66A2\u69CD\u6EC4\u6F32\u7316\u7621\u7A93\u8139\u8259\u83D6\u84BC\u50B5\u57F0\u5BC0\u5BE8\u5F69\u63A1\u7826\u7DB5\u83DC\u8521\u91C7\u91F5\u518A\u67F5\u7B56"],["f4a1","\u8CAC\u51C4\u59BB\u60BD\u8655\u501C\uF9FF\u5254\u5C3A\u617D\u621A\u62D3\u64F2\u65A5\u6ECC\u7620\u810A\u8E60\u965F\u96BB\u4EDF\u5343\u5598\u5929\u5DDD\u64C5\u6CC9\u6DFA\u7394\u7A7F\u821B\u85A6\u8CE4\u8E10\u9077\u91E7\u95E1\u9621\u97C6\u51F8\u54F2\u5586\u5FB9\u64A4\u6F88\u7DB4\u8F1F\u8F4D\u9435\u50C9\u5C16\u6CBE\u6DFB\u751B\u77BB\u7C3D\u7C64\u8A79\u8AC2\u581E\u59BE\u5E16\u6377\u7252\u758A\u776B\u8ADC\u8CBC\u8F12\u5EF3\u6674\u6DF8\u807D\u83C1\u8ACB\u9751\u9BD6\uFA00\u5243\u66FF\u6D95\u6EEF\u7DE0\u8AE6\u902E\u905E\u9AD4\u521D\u527F\u54E8\u6194\u6284\u62DB\u68A2"],["f5a1","\u6912\u695A\u6A35\u7092\u7126\u785D\u7901\u790E\u79D2\u7A0D\u8096\u8278\u82D5\u8349\u8549\u8C82\u8D85\u9162\u918B\u91AE\u4FC3\u56D1\u71ED\u77D7\u8700\u89F8\u5BF8\u5FD6\u6751\u90A8\u53E2\u585A\u5BF5\u60A4\u6181\u6460\u7E3D\u8070\u8525\u9283\u64AE\u50AC\u5D14\u6700\u589C\u62BD\u63A8\u690E\u6978\u6A1E\u6E6B\u76BA\u79CB\u82BB\u8429\u8ACF\u8DA8\u8FFD\u9112\u914B\u919C\u9310\u9318\u939A\u96DB\u9A36\u9C0D\u4E11\u755C\u795D\u7AFA\u7B51\u7BC9\u7E2E\u84C4\u8E59\u8E74\u8EF8\u9010\u6625\u693F\u7443\u51FA\u672E\u9EDC\u5145\u5FE0\u6C96\u87F2\u885D\u8877\u60B4\u81B5\u8403"],["f6a1","\u8D05\u53D6\u5439\u5634\u5A36\u5C31\u708A\u7FE0\u805A\u8106\u81ED\u8DA3\u9189\u9A5F\u9DF2\u5074\u4EC4\u53A0\u60FB\u6E2C\u5C64\u4F88\u5024\u55E4\u5CD9\u5E5F\u6065\u6894\u6CBB\u6DC4\u71BE\u75D4\u75F4\u7661\u7A1A\u7A49\u7DC7\u7DFB\u7F6E\u81F4\u86A9\u8F1C\u96C9\u99B3\u9F52\u5247\u52C5\u98ED\u89AA\u4E03\u67D2\u6F06\u4FB5\u5BE2\u6795\u6C88\u6D78\u741B\u7827\u91DD\u937C\u87C4\u79E4\u7A31\u5FEB\u4ED6\u54A4\u553E\u58AE\u59A5\u60F0\u6253\u62D6\u6736\u6955\u8235\u9640\u99B1\u99DD\u502C\u5353\u5544\u577C\uFA01\u6258\uFA02\u64E2\u666B\u67DD\u6FC1\u6FEF\u7422\u7438\u8A17"],["f7a1","\u9438\u5451\u5606\u5766\u5F48\u619A\u6B4E\u7058\u70AD\u7DBB\u8A95\u596A\u812B\u63A2\u7708\u803D\u8CAA\u5854\u642D\u69BB\u5B95\u5E11\u6E6F\uFA03\u8569\u514C\u53F0\u592A\u6020\u614B\u6B86\u6C70\u6CF0\u7B1E\u80CE\u82D4\u8DC6\u90B0\u98B1\uFA04\u64C7\u6FA4\u6491\u6504\u514E\u5410\u571F\u8A0E\u615F\u6876\uFA05\u75DB\u7B52\u7D71\u901A\u5806\u69CC\u817F\u892A\u9000\u9839\u5078\u5957\u59AC\u6295\u900F\u9B2A\u615D\u7279\u95D6\u5761\u5A46\u5DF4\u628A\u64AD\u64FA\u6777\u6CE2\u6D3E\u722C\u7436\u7834\u7F77\u82AD\u8DDB\u9817\u5224\u5742\u677F\u7248\u74E3\u8CA9\u8FA6\u9211"],["f8a1","\u962A\u516B\u53ED\u634C\u4F69\u5504\u6096\u6557\u6C9B\u6D7F\u724C\u72FD\u7A17\u8987\u8C9D\u5F6D\u6F8E\u70F9\u81A8\u610E\u4FBF\u504F\u6241\u7247\u7BC7\u7DE8\u7FE9\u904D\u97AD\u9A19\u8CB6\u576A\u5E73\u67B0\u840D\u8A55\u5420\u5B16\u5E63\u5EE2\u5F0A\u6583\u80BA\u853D\u9589\u965B\u4F48\u5305\u530D\u530F\u5486\u54FA\u5703\u5E03\u6016\u629B\u62B1\u6355\uFA06\u6CE1\u6D66\u75B1\u7832\u80DE\u812F\u82DE\u8461\u84B2\u888D\u8912\u900B\u92EA\u98FD\u9B91\u5E45\u66B4\u66DD\u7011\u7206\uFA07\u4FF5\u527D\u5F6A\u6153\u6753\u6A19\u6F02\u74E2\u7968\u8868\u8C79\u98C7\u98C4\u9A43"],["f9a1","\u54C1\u7A1F\u6953\u8AF7\u8C4A\u98A8\u99AE\u5F7C\u62AB\u75B2\u76AE\u88AB\u907F\u9642\u5339\u5F3C\u5FC5\u6CCC\u73CC\u7562\u758B\u7B46\u82FE\u999D\u4E4F\u903C\u4E0B\u4F55\u53A6\u590F\u5EC8\u6630\u6CB3\u7455\u8377\u8766\u8CC0\u9050\u971E\u9C15\u58D1\u5B78\u8650\u8B14\u9DB4\u5BD2\u6068\u608D\u65F1\u6C57\u6F22\u6FA3\u701A\u7F55\u7FF0\u9591\u9592\u9650\u97D3\u5272\u8F44\u51FD\u542B\u54B8\u5563\u558A\u6ABB\u6DB5\u7DD8\u8266\u929C\u9677\u9E79\u5408\u54C8\u76D2\u86E4\u95A4\u95D4\u965C\u4EA2\u4F09\u59EE\u5AE6\u5DF7\u6052\u6297\u676D\u6841\u6C86\u6E2F\u7F38\u809B\u822A"],["faa1","\uFA08\uFA09\u9805\u4EA5\u5055\u54B3\u5793\u595A\u5B69\u5BB3\u61C8\u6977\u6D77\u7023\u87F9\u89E3\u8A72\u8AE7\u9082\u99ED\u9AB8\u52BE\u6838\u5016\u5E78\u674F\u8347\u884C\u4EAB\u5411\u56AE\u73E6\u9115\u97FF\u9909\u9957\u9999\u5653\u589F\u865B\u8A31\u61B2\u6AF6\u737B\u8ED2\u6B47\u96AA\u9A57\u5955\u7200\u8D6B\u9769\u4FD4\u5CF4\u5F26\u61F8\u665B\u6CEB\u70AB\u7384\u73B9\u73FE\u7729\u774D\u7D43\u7D62\u7E23\u8237\u8852\uFA0A\u8CE2\u9249\u986F\u5B51\u7A74\u8840\u9801\u5ACC\u4FE0\u5354\u593E\u5CFD\u633E\u6D79\u72F9\u8105\u8107\u83A2\u92CF\u9830\u4EA8\u5144\u5211\u578B"],["fba1","\u5F62\u6CC2\u6ECE\u7005\u7050\u70AF\u7192\u73E9\u7469\u834A\u87A2\u8861\u9008\u90A2\u93A3\u99A8\u516E\u5F57\u60E0\u6167\u66B3\u8559\u8E4A\u91AF\u978B\u4E4E\u4E92\u547C\u58D5\u58FA\u597D\u5CB5\u5F27\u6236\u6248\u660A\u6667\u6BEB\u6D69\u6DCF\u6E56\u6EF8\u6F94\u6FE0\u6FE9\u705D\u72D0\u7425\u745A\u74E0\u7693\u795C\u7CCA\u7E1E\u80E1\u82A6\u846B\u84BF\u864E\u865F\u8774\u8B77\u8C6A\u93AC\u9800\u9865\u60D1\u6216\u9177\u5A5A\u660F\u6DF7\u6E3E\u743F\u9B42\u5FFD\u60DA\u7B0F\u54C4\u5F18\u6C5E\u6CD3\u6D2A\u70D8\u7D05\u8679\u8A0C\u9D3B\u5316\u548C\u5B05\u6A3A\u706B\u7575"],["fca1","\u798D\u79BE\u82B1\u83EF\u8A71\u8B41\u8CA8\u9774\uFA0B\u64F4\u652B\u78BA\u78BB\u7A6B\u4E38\u559A\u5950\u5BA6\u5E7B\u60A3\u63DB\u6B61\u6665\u6853\u6E19\u7165\u74B0\u7D08\u9084\u9A69\u9C25\u6D3B\u6ED1\u733E\u8C41\u95CA\u51F0\u5E4C\u5FA8\u604D\u60F6\u6130\u614C\u6643\u6644\u69A5\u6CC1\u6E5F\u6EC9\u6F62\u714C\u749C\u7687\u7BC1\u7C27\u8352\u8757\u9051\u968D\u9EC3\u532F\u56DE\u5EFB\u5F8A\u6062\u6094\u61F7\u6666\u6703\u6A9C\u6DEE\u6FAE\u7070\u736A\u7E6A\u81BE\u8334\u86D4\u8AA8\u8CC4\u5283\u7372\u5B96\u6A6B\u9404\u54EE\u5686\u5B5D\u6548\u6585\u66C9\u689F\u6D8D\u6DC6"],["fda1","\u723B\u80B4\u9175\u9A4D\u4FAF\u5019\u539A\u540E\u543C\u5589\u55C5\u5E3F\u5F8C\u673D\u7166\u73DD\u9005\u52DB\u52F3\u5864\u58CE\u7104\u718F\u71FB\u85B0\u8A13\u6688\u85A8\u55A7\u6684\u714A\u8431\u5349\u5599\u6BC1\u5F59\u5FBD\u63EE\u6689\u7147\u8AF1\u8F1D\u9EBE\u4F11\u643A\u70CB\u7566\u8667\u6064\u8B4E\u9DF8\u5147\u51F6\u5308\u6D36\u80F8\u9ED1\u6615\u6B23\u7098\u75D5\u5403\u5C79\u7D07\u8A16\u6B20\u6B3D\u6B46\u5438\u6070\u6D3D\u7FD5\u8208\u50D6\u51DE\u559C\u566B\u56CD\u59EC\u5B09\u5E0C\u6199\u6198\u6231\u665E\u66E6\u7199\u71B9\u71BA\u72A7\u79A7\u7A00\u7FB2\u8A70"]]});var xK=S((Mkr,Dqt)=>{Dqt.exports=[["0","\0",127],["a140","\u3000\uFF0C\u3001\u3002\uFF0E\u2027\uFF1B\uFF1A\uFF1F\uFF01\uFE30\u2026\u2025\uFE50\uFE51\uFE52\xB7\uFE54\uFE55\uFE56\uFE57\uFF5C\u2013\uFE31\u2014\uFE33\u2574\uFE34\uFE4F\uFF08\uFF09\uFE35\uFE36\uFF5B\uFF5D\uFE37\uFE38\u3014\u3015\uFE39\uFE3A\u3010\u3011\uFE3B\uFE3C\u300A\u300B\uFE3D\uFE3E\u3008\u3009\uFE3F\uFE40\u300C\u300D\uFE41\uFE42\u300E\u300F\uFE43\uFE44\uFE59\uFE5A"],["a1a1","\uFE5B\uFE5C\uFE5D\uFE5E\u2018\u2019\u201C\u201D\u301D\u301E\u2035\u2032\uFF03\uFF06\uFF0A\u203B\xA7\u3003\u25CB\u25CF\u25B3\u25B2\u25CE\u2606\u2605\u25C7\u25C6\u25A1\u25A0\u25BD\u25BC\u32A3\u2105\xAF\uFFE3\uFF3F\u02CD\uFE49\uFE4A\uFE4D\uFE4E\uFE4B\uFE4C\uFE5F\uFE60\uFE61\uFF0B\uFF0D\xD7\xF7\xB1\u221A\uFF1C\uFF1E\uFF1D\u2266\u2267\u2260\u221E\u2252\u2261\uFE62",4,"\uFF5E\u2229\u222A\u22A5\u2220\u221F\u22BF\u33D2\u33D1\u222B\u222E\u2235\u2234\u2640\u2642\u2295\u2299\u2191\u2193\u2190\u2192\u2196\u2197\u2199\u2198\u2225\u2223\uFF0F"],["a240","\uFF3C\u2215\uFE68\uFF04\uFFE5\u3012\uFFE0\uFFE1\uFF05\uFF20\u2103\u2109\uFE69\uFE6A\uFE6B\u33D5\u339C\u339D\u339E\u33CE\u33A1\u338E\u338F\u33C4\xB0\u5159\u515B\u515E\u515D\u5161\u5163\u55E7\u74E9\u7CCE\u2581",7,"\u258F\u258E\u258D\u258C\u258B\u258A\u2589\u253C\u2534\u252C\u2524\u251C\u2594\u2500\u2502\u2595\u250C\u2510\u2514\u2518\u256D"],["a2a1","\u256E\u2570\u256F\u2550\u255E\u256A\u2561\u25E2\u25E3\u25E5\u25E4\u2571\u2572\u2573\uFF10",9,"\u2160",9,"\u3021",8,"\u5341\u5344\u5345\uFF21",25,"\uFF41",21],["a340","\uFF57\uFF58\uFF59\uFF5A\u0391",16,"\u03A3",6,"\u03B1",16,"\u03C3",6,"\u3105",10],["a3a1","\u3110",25,"\u02D9\u02C9\u02CA\u02C7\u02CB"],["a3e1","\u20AC"],["a440","\u4E00\u4E59\u4E01\u4E03\u4E43\u4E5D\u4E86\u4E8C\u4EBA\u513F\u5165\u516B\u51E0\u5200\u5201\u529B\u5315\u5341\u535C\u53C8\u4E09\u4E0B\u4E08\u4E0A\u4E2B\u4E38\u51E1\u4E45\u4E48\u4E5F\u4E5E\u4E8E\u4EA1\u5140\u5203\u52FA\u5343\u53C9\u53E3\u571F\u58EB\u5915\u5927\u5973\u5B50\u5B51\u5B53\u5BF8\u5C0F\u5C22\u5C38\u5C71\u5DDD\u5DE5\u5DF1\u5DF2\u5DF3\u5DFE\u5E72\u5EFE\u5F0B\u5F13\u624D"],["a4a1","\u4E11\u4E10\u4E0D\u4E2D\u4E30\u4E39\u4E4B\u5C39\u4E88\u4E91\u4E95\u4E92\u4E94\u4EA2\u4EC1\u4EC0\u4EC3\u4EC6\u4EC7\u4ECD\u4ECA\u4ECB\u4EC4\u5143\u5141\u5167\u516D\u516E\u516C\u5197\u51F6\u5206\u5207\u5208\u52FB\u52FE\u52FF\u5316\u5339\u5348\u5347\u5345\u535E\u5384\u53CB\u53CA\u53CD\u58EC\u5929\u592B\u592A\u592D\u5B54\u5C11\u5C24\u5C3A\u5C6F\u5DF4\u5E7B\u5EFF\u5F14\u5F15\u5FC3\u6208\u6236\u624B\u624E\u652F\u6587\u6597\u65A4\u65B9\u65E5\u66F0\u6708\u6728\u6B20\u6B62\u6B79\u6BCB\u6BD4\u6BDB\u6C0F\u6C34\u706B\u722A\u7236\u723B\u7247\u7259\u725B\u72AC\u738B\u4E19"],["a540","\u4E16\u4E15\u4E14\u4E18\u4E3B\u4E4D\u4E4F\u4E4E\u4EE5\u4ED8\u4ED4\u4ED5\u4ED6\u4ED7\u4EE3\u4EE4\u4ED9\u4EDE\u5145\u5144\u5189\u518A\u51AC\u51F9\u51FA\u51F8\u520A\u52A0\u529F\u5305\u5306\u5317\u531D\u4EDF\u534A\u5349\u5361\u5360\u536F\u536E\u53BB\u53EF\u53E4\u53F3\u53EC\u53EE\u53E9\u53E8\u53FC\u53F8\u53F5\u53EB\u53E6\u53EA\u53F2\u53F1\u53F0\u53E5\u53ED\u53FB\u56DB\u56DA\u5916"],["a5a1","\u592E\u5931\u5974\u5976\u5B55\u5B83\u5C3C\u5DE8\u5DE7\u5DE6\u5E02\u5E03\u5E73\u5E7C\u5F01\u5F18\u5F17\u5FC5\u620A\u6253\u6254\u6252\u6251\u65A5\u65E6\u672E\u672C\u672A\u672B\u672D\u6B63\u6BCD\u6C11\u6C10\u6C38\u6C41\u6C40\u6C3E\u72AF\u7384\u7389\u74DC\u74E6\u7518\u751F\u7528\u7529\u7530\u7531\u7532\u7533\u758B\u767D\u76AE\u76BF\u76EE\u77DB\u77E2\u77F3\u793A\u79BE\u7A74\u7ACB\u4E1E\u4E1F\u4E52\u4E53\u4E69\u4E99\u4EA4\u4EA6\u4EA5\u4EFF\u4F09\u4F19\u4F0A\u4F15\u4F0D\u4F10\u4F11\u4F0F\u4EF2\u4EF6\u4EFB\u4EF0\u4EF3\u4EFD\u4F01\u4F0B\u5149\u5147\u5146\u5148\u5168"],["a640","\u5171\u518D\u51B0\u5217\u5211\u5212\u520E\u5216\u52A3\u5308\u5321\u5320\u5370\u5371\u5409\u540F\u540C\u540A\u5410\u5401\u540B\u5404\u5411\u540D\u5408\u5403\u540E\u5406\u5412\u56E0\u56DE\u56DD\u5733\u5730\u5728\u572D\u572C\u572F\u5729\u5919\u591A\u5937\u5938\u5984\u5978\u5983\u597D\u5979\u5982\u5981\u5B57\u5B58\u5B87\u5B88\u5B85\u5B89\u5BFA\u5C16\u5C79\u5DDE\u5E06\u5E76\u5E74"],["a6a1","\u5F0F\u5F1B\u5FD9\u5FD6\u620E\u620C\u620D\u6210\u6263\u625B\u6258\u6536\u65E9\u65E8\u65EC\u65ED\u66F2\u66F3\u6709\u673D\u6734\u6731\u6735\u6B21\u6B64\u6B7B\u6C16\u6C5D\u6C57\u6C59\u6C5F\u6C60\u6C50\u6C55\u6C61\u6C5B\u6C4D\u6C4E\u7070\u725F\u725D\u767E\u7AF9\u7C73\u7CF8\u7F36\u7F8A\u7FBD\u8001\u8003\u800C\u8012\u8033\u807F\u8089\u808B\u808C\u81E3\u81EA\u81F3\u81FC\u820C\u821B\u821F\u826E\u8272\u827E\u866B\u8840\u884C\u8863\u897F\u9621\u4E32\u4EA8\u4F4D\u4F4F\u4F47\u4F57\u4F5E\u4F34\u4F5B\u4F55\u4F30\u4F50\u4F51\u4F3D\u4F3A\u4F38\u4F43\u4F54\u4F3C\u4F46\u4F63"],["a740","\u4F5C\u4F60\u4F2F\u4F4E\u4F36\u4F59\u4F5D\u4F48\u4F5A\u514C\u514B\u514D\u5175\u51B6\u51B7\u5225\u5224\u5229\u522A\u5228\u52AB\u52A9\u52AA\u52AC\u5323\u5373\u5375\u541D\u542D\u541E\u543E\u5426\u544E\u5427\u5446\u5443\u5433\u5448\u5442\u541B\u5429\u544A\u5439\u543B\u5438\u542E\u5435\u5436\u5420\u543C\u5440\u5431\u542B\u541F\u542C\u56EA\u56F0\u56E4\u56EB\u574A\u5751\u5740\u574D"],["a7a1","\u5747\u574E\u573E\u5750\u574F\u573B\u58EF\u593E\u599D\u5992\u59A8\u599E\u59A3\u5999\u5996\u598D\u59A4\u5993\u598A\u59A5\u5B5D\u5B5C\u5B5A\u5B5B\u5B8C\u5B8B\u5B8F\u5C2C\u5C40\u5C41\u5C3F\u5C3E\u5C90\u5C91\u5C94\u5C8C\u5DEB\u5E0C\u5E8F\u5E87\u5E8A\u5EF7\u5F04\u5F1F\u5F64\u5F62\u5F77\u5F79\u5FD8\u5FCC\u5FD7\u5FCD\u5FF1\u5FEB\u5FF8\u5FEA\u6212\u6211\u6284\u6297\u6296\u6280\u6276\u6289\u626D\u628A\u627C\u627E\u6279\u6273\u6292\u626F\u6298\u626E\u6295\u6293\u6291\u6286\u6539\u653B\u6538\u65F1\u66F4\u675F\u674E\u674F\u6750\u6751\u675C\u6756\u675E\u6749\u6746\u6760"],["a840","\u6753\u6757\u6B65\u6BCF\u6C42\u6C5E\u6C99\u6C81\u6C88\u6C89\u6C85\u6C9B\u6C6A\u6C7A\u6C90\u6C70\u6C8C\u6C68\u6C96\u6C92\u6C7D\u6C83\u6C72\u6C7E\u6C74\u6C86\u6C76\u6C8D\u6C94\u6C98\u6C82\u7076\u707C\u707D\u7078\u7262\u7261\u7260\u72C4\u72C2\u7396\u752C\u752B\u7537\u7538\u7682\u76EF\u77E3\u79C1\u79C0\u79BF\u7A76\u7CFB\u7F55\u8096\u8093\u809D\u8098\u809B\u809A\u80B2\u826F\u8292"],["a8a1","\u828B\u828D\u898B\u89D2\u8A00\u8C37\u8C46\u8C55\u8C9D\u8D64\u8D70\u8DB3\u8EAB\u8ECA\u8F9B\u8FB0\u8FC2\u8FC6\u8FC5\u8FC4\u5DE1\u9091\u90A2\u90AA\u90A6\u90A3\u9149\u91C6\u91CC\u9632\u962E\u9631\u962A\u962C\u4E26\u4E56\u4E73\u4E8B\u4E9B\u4E9E\u4EAB\u4EAC\u4F6F\u4F9D\u4F8D\u4F73\u4F7F\u4F6C\u4F9B\u4F8B\u4F86\u4F83\u4F70\u4F75\u4F88\u4F69\u4F7B\u4F96\u4F7E\u4F8F\u4F91\u4F7A\u5154\u5152\u5155\u5169\u5177\u5176\u5178\u51BD\u51FD\u523B\u5238\u5237\u523A\u5230\u522E\u5236\u5241\u52BE\u52BB\u5352\u5354\u5353\u5351\u5366\u5377\u5378\u5379\u53D6\u53D4\u53D7\u5473\u5475"],["a940","\u5496\u5478\u5495\u5480\u547B\u5477\u5484\u5492\u5486\u547C\u5490\u5471\u5476\u548C\u549A\u5462\u5468\u548B\u547D\u548E\u56FA\u5783\u5777\u576A\u5769\u5761\u5766\u5764\u577C\u591C\u5949\u5947\u5948\u5944\u5954\u59BE\u59BB\u59D4\u59B9\u59AE\u59D1\u59C6\u59D0\u59CD\u59CB\u59D3\u59CA\u59AF\u59B3\u59D2\u59C5\u5B5F\u5B64\u5B63\u5B97\u5B9A\u5B98\u5B9C\u5B99\u5B9B\u5C1A\u5C48\u5C45"],["a9a1","\u5C46\u5CB7\u5CA1\u5CB8\u5CA9\u5CAB\u5CB1\u5CB3\u5E18\u5E1A\u5E16\u5E15\u5E1B\u5E11\u5E78\u5E9A\u5E97\u5E9C\u5E95\u5E96\u5EF6\u5F26\u5F27\u5F29\u5F80\u5F81\u5F7F\u5F7C\u5FDD\u5FE0\u5FFD\u5FF5\u5FFF\u600F\u6014\u602F\u6035\u6016\u602A\u6015\u6021\u6027\u6029\u602B\u601B\u6216\u6215\u623F\u623E\u6240\u627F\u62C9\u62CC\u62C4\u62BF\u62C2\u62B9\u62D2\u62DB\u62AB\u62D3\u62D4\u62CB\u62C8\u62A8\u62BD\u62BC\u62D0\u62D9\u62C7\u62CD\u62B5\u62DA\u62B1\u62D8\u62D6\u62D7\u62C6\u62AC\u62CE\u653E\u65A7\u65BC\u65FA\u6614\u6613\u660C\u6606\u6602\u660E\u6600\u660F\u6615\u660A"],["aa40","\u6607\u670D\u670B\u676D\u678B\u6795\u6771\u679C\u6773\u6777\u6787\u679D\u6797\u676F\u6770\u677F\u6789\u677E\u6790\u6775\u679A\u6793\u677C\u676A\u6772\u6B23\u6B66\u6B67\u6B7F\u6C13\u6C1B\u6CE3\u6CE8\u6CF3\u6CB1\u6CCC\u6CE5\u6CB3\u6CBD\u6CBE\u6CBC\u6CE2\u6CAB\u6CD5\u6CD3\u6CB8\u6CC4\u6CB9\u6CC1\u6CAE\u6CD7\u6CC5\u6CF1\u6CBF\u6CBB\u6CE1\u6CDB\u6CCA\u6CAC\u6CEF\u6CDC\u6CD6\u6CE0"],["aaa1","\u7095\u708E\u7092\u708A\u7099\u722C\u722D\u7238\u7248\u7267\u7269\u72C0\u72CE\u72D9\u72D7\u72D0\u73A9\u73A8\u739F\u73AB\u73A5\u753D\u759D\u7599\u759A\u7684\u76C2\u76F2\u76F4\u77E5\u77FD\u793E\u7940\u7941\u79C9\u79C8\u7A7A\u7A79\u7AFA\u7CFE\u7F54\u7F8C\u7F8B\u8005\u80BA\u80A5\u80A2\u80B1\u80A1\u80AB\u80A9\u80B4\u80AA\u80AF\u81E5\u81FE\u820D\u82B3\u829D\u8299\u82AD\u82BD\u829F\u82B9\u82B1\u82AC\u82A5\u82AF\u82B8\u82A3\u82B0\u82BE\u82B7\u864E\u8671\u521D\u8868\u8ECB\u8FCE\u8FD4\u8FD1\u90B5\u90B8\u90B1\u90B6\u91C7\u91D1\u9577\u9580\u961C\u9640\u963F\u963B\u9644"],["ab40","\u9642\u96B9\u96E8\u9752\u975E\u4E9F\u4EAD\u4EAE\u4FE1\u4FB5\u4FAF\u4FBF\u4FE0\u4FD1\u4FCF\u4FDD\u4FC3\u4FB6\u4FD8\u4FDF\u4FCA\u4FD7\u4FAE\u4FD0\u4FC4\u4FC2\u4FDA\u4FCE\u4FDE\u4FB7\u5157\u5192\u5191\u51A0\u524E\u5243\u524A\u524D\u524C\u524B\u5247\u52C7\u52C9\u52C3\u52C1\u530D\u5357\u537B\u539A\u53DB\u54AC\u54C0\u54A8\u54CE\u54C9\u54B8\u54A6\u54B3\u54C7\u54C2\u54BD\u54AA\u54C1"],["aba1","\u54C4\u54C8\u54AF\u54AB\u54B1\u54BB\u54A9\u54A7\u54BF\u56FF\u5782\u578B\u57A0\u57A3\u57A2\u57CE\u57AE\u5793\u5955\u5951\u594F\u594E\u5950\u59DC\u59D8\u59FF\u59E3\u59E8\u5A03\u59E5\u59EA\u59DA\u59E6\u5A01\u59FB\u5B69\u5BA3\u5BA6\u5BA4\u5BA2\u5BA5\u5C01\u5C4E\u5C4F\u5C4D\u5C4B\u5CD9\u5CD2\u5DF7\u5E1D\u5E25\u5E1F\u5E7D\u5EA0\u5EA6\u5EFA\u5F08\u5F2D\u5F65\u5F88\u5F85\u5F8A\u5F8B\u5F87\u5F8C\u5F89\u6012\u601D\u6020\u6025\u600E\u6028\u604D\u6070\u6068\u6062\u6046\u6043\u606C\u606B\u606A\u6064\u6241\u62DC\u6316\u6309\u62FC\u62ED\u6301\u62EE\u62FD\u6307\u62F1\u62F7"],["ac40","\u62EF\u62EC\u62FE\u62F4\u6311\u6302\u653F\u6545\u65AB\u65BD\u65E2\u6625\u662D\u6620\u6627\u662F\u661F\u6628\u6631\u6624\u66F7\u67FF\u67D3\u67F1\u67D4\u67D0\u67EC\u67B6\u67AF\u67F5\u67E9\u67EF\u67C4\u67D1\u67B4\u67DA\u67E5\u67B8\u67CF\u67DE\u67F3\u67B0\u67D9\u67E2\u67DD\u67D2\u6B6A\u6B83\u6B86\u6BB5\u6BD2\u6BD7\u6C1F\u6CC9\u6D0B\u6D32\u6D2A\u6D41\u6D25\u6D0C\u6D31\u6D1E\u6D17"],["aca1","\u6D3B\u6D3D\u6D3E\u6D36\u6D1B\u6CF5\u6D39\u6D27\u6D38\u6D29\u6D2E\u6D35\u6D0E\u6D2B\u70AB\u70BA\u70B3\u70AC\u70AF\u70AD\u70B8\u70AE\u70A4\u7230\u7272\u726F\u7274\u72E9\u72E0\u72E1\u73B7\u73CA\u73BB\u73B2\u73CD\u73C0\u73B3\u751A\u752D\u754F\u754C\u754E\u754B\u75AB\u75A4\u75A5\u75A2\u75A3\u7678\u7686\u7687\u7688\u76C8\u76C6\u76C3\u76C5\u7701\u76F9\u76F8\u7709\u770B\u76FE\u76FC\u7707\u77DC\u7802\u7814\u780C\u780D\u7946\u7949\u7948\u7947\u79B9\u79BA\u79D1\u79D2\u79CB\u7A7F\u7A81\u7AFF\u7AFD\u7C7D\u7D02\u7D05\u7D00\u7D09\u7D07\u7D04\u7D06\u7F38\u7F8E\u7FBF\u8004"],["ad40","\u8010\u800D\u8011\u8036\u80D6\u80E5\u80DA\u80C3\u80C4\u80CC\u80E1\u80DB\u80CE\u80DE\u80E4\u80DD\u81F4\u8222\u82E7\u8303\u8305\u82E3\u82DB\u82E6\u8304\u82E5\u8302\u8309\u82D2\u82D7\u82F1\u8301\u82DC\u82D4\u82D1\u82DE\u82D3\u82DF\u82EF\u8306\u8650\u8679\u867B\u867A\u884D\u886B\u8981\u89D4\u8A08\u8A02\u8A03\u8C9E\u8CA0\u8D74\u8D73\u8DB4\u8ECD\u8ECC\u8FF0\u8FE6\u8FE2\u8FEA\u8FE5"],["ada1","\u8FED\u8FEB\u8FE4\u8FE8\u90CA\u90CE\u90C1\u90C3\u914B\u914A\u91CD\u9582\u9650\u964B\u964C\u964D\u9762\u9769\u97CB\u97ED\u97F3\u9801\u98A8\u98DB\u98DF\u9996\u9999\u4E58\u4EB3\u500C\u500D\u5023\u4FEF\u5026\u5025\u4FF8\u5029\u5016\u5006\u503C\u501F\u501A\u5012\u5011\u4FFA\u5000\u5014\u5028\u4FF1\u5021\u500B\u5019\u5018\u4FF3\u4FEE\u502D\u502A\u4FFE\u502B\u5009\u517C\u51A4\u51A5\u51A2\u51CD\u51CC\u51C6\u51CB\u5256\u525C\u5254\u525B\u525D\u532A\u537F\u539F\u539D\u53DF\u54E8\u5510\u5501\u5537\u54FC\u54E5\u54F2\u5506\u54FA\u5514\u54E9\u54ED\u54E1\u5509\u54EE\u54EA"],["ae40","\u54E6\u5527\u5507\u54FD\u550F\u5703\u5704\u57C2\u57D4\u57CB\u57C3\u5809\u590F\u5957\u5958\u595A\u5A11\u5A18\u5A1C\u5A1F\u5A1B\u5A13\u59EC\u5A20\u5A23\u5A29\u5A25\u5A0C\u5A09\u5B6B\u5C58\u5BB0\u5BB3\u5BB6\u5BB4\u5BAE\u5BB5\u5BB9\u5BB8\u5C04\u5C51\u5C55\u5C50\u5CED\u5CFD\u5CFB\u5CEA\u5CE8\u5CF0\u5CF6\u5D01\u5CF4\u5DEE\u5E2D\u5E2B\u5EAB\u5EAD\u5EA7\u5F31\u5F92\u5F91\u5F90\u6059"],["aea1","\u6063\u6065\u6050\u6055\u606D\u6069\u606F\u6084\u609F\u609A\u608D\u6094\u608C\u6085\u6096\u6247\u62F3\u6308\u62FF\u634E\u633E\u632F\u6355\u6342\u6346\u634F\u6349\u633A\u6350\u633D\u632A\u632B\u6328\u634D\u634C\u6548\u6549\u6599\u65C1\u65C5\u6642\u6649\u664F\u6643\u6652\u664C\u6645\u6641\u66F8\u6714\u6715\u6717\u6821\u6838\u6848\u6846\u6853\u6839\u6842\u6854\u6829\u68B3\u6817\u684C\u6851\u683D\u67F4\u6850\u6840\u683C\u6843\u682A\u6845\u6813\u6818\u6841\u6B8A\u6B89\u6BB7\u6C23\u6C27\u6C28\u6C26\u6C24\u6CF0\u6D6A\u6D95\u6D88\u6D87\u6D66\u6D78\u6D77\u6D59\u6D93"],["af40","\u6D6C\u6D89\u6D6E\u6D5A\u6D74\u6D69\u6D8C\u6D8A\u6D79\u6D85\u6D65\u6D94\u70CA\u70D8\u70E4\u70D9\u70C8\u70CF\u7239\u7279\u72FC\u72F9\u72FD\u72F8\u72F7\u7386\u73ED\u7409\u73EE\u73E0\u73EA\u73DE\u7554\u755D\u755C\u755A\u7559\u75BE\u75C5\u75C7\u75B2\u75B3\u75BD\u75BC\u75B9\u75C2\u75B8\u768B\u76B0\u76CA\u76CD\u76CE\u7729\u771F\u7720\u7728\u77E9\u7830\u7827\u7838\u781D\u7834\u7837"],["afa1","\u7825\u782D\u7820\u781F\u7832\u7955\u7950\u7960\u795F\u7956\u795E\u795D\u7957\u795A\u79E4\u79E3\u79E7\u79DF\u79E6\u79E9\u79D8\u7A84\u7A88\u7AD9\u7B06\u7B11\u7C89\u7D21\u7D17\u7D0B\u7D0A\u7D20\u7D22\u7D14\u7D10\u7D15\u7D1A\u7D1C\u7D0D\u7D19\u7D1B\u7F3A\u7F5F\u7F94\u7FC5\u7FC1\u8006\u8018\u8015\u8019\u8017\u803D\u803F\u80F1\u8102\u80F0\u8105\u80ED\u80F4\u8106\u80F8\u80F3\u8108\u80FD\u810A\u80FC\u80EF\u81ED\u81EC\u8200\u8210\u822A\u822B\u8228\u822C\u82BB\u832B\u8352\u8354\u834A\u8338\u8350\u8349\u8335\u8334\u834F\u8332\u8339\u8336\u8317\u8340\u8331\u8328\u8343"],["b040","\u8654\u868A\u86AA\u8693\u86A4\u86A9\u868C\u86A3\u869C\u8870\u8877\u8881\u8882\u887D\u8879\u8A18\u8A10\u8A0E\u8A0C\u8A15\u8A0A\u8A17\u8A13\u8A16\u8A0F\u8A11\u8C48\u8C7A\u8C79\u8CA1\u8CA2\u8D77\u8EAC\u8ED2\u8ED4\u8ECF\u8FB1\u9001\u9006\u8FF7\u9000\u8FFA\u8FF4\u9003\u8FFD\u9005\u8FF8\u9095\u90E1\u90DD\u90E2\u9152\u914D\u914C\u91D8\u91DD\u91D7\u91DC\u91D9\u9583\u9662\u9663\u9661"],["b0a1","\u965B\u965D\u9664\u9658\u965E\u96BB\u98E2\u99AC\u9AA8\u9AD8\u9B25\u9B32\u9B3C\u4E7E\u507A\u507D\u505C\u5047\u5043\u504C\u505A\u5049\u5065\u5076\u504E\u5055\u5075\u5074\u5077\u504F\u500F\u506F\u506D\u515C\u5195\u51F0\u526A\u526F\u52D2\u52D9\u52D8\u52D5\u5310\u530F\u5319\u533F\u5340\u533E\u53C3\u66FC\u5546\u556A\u5566\u5544\u555E\u5561\u5543\u554A\u5531\u5556\u554F\u5555\u552F\u5564\u5538\u552E\u555C\u552C\u5563\u5533\u5541\u5557\u5708\u570B\u5709\u57DF\u5805\u580A\u5806\u57E0\u57E4\u57FA\u5802\u5835\u57F7\u57F9\u5920\u5962\u5A36\u5A41\u5A49\u5A66\u5A6A\u5A40"],["b140","\u5A3C\u5A62\u5A5A\u5A46\u5A4A\u5B70\u5BC7\u5BC5\u5BC4\u5BC2\u5BBF\u5BC6\u5C09\u5C08\u5C07\u5C60\u5C5C\u5C5D\u5D07\u5D06\u5D0E\u5D1B\u5D16\u5D22\u5D11\u5D29\u5D14\u5D19\u5D24\u5D27\u5D17\u5DE2\u5E38\u5E36\u5E33\u5E37\u5EB7\u5EB8\u5EB6\u5EB5\u5EBE\u5F35\u5F37\u5F57\u5F6C\u5F69\u5F6B\u5F97\u5F99\u5F9E\u5F98\u5FA1\u5FA0\u5F9C\u607F\u60A3\u6089\u60A0\u60A8\u60CB\u60B4\u60E6\u60BD"],["b1a1","\u60C5\u60BB\u60B5\u60DC\u60BC\u60D8\u60D5\u60C6\u60DF\u60B8\u60DA\u60C7\u621A\u621B\u6248\u63A0\u63A7\u6372\u6396\u63A2\u63A5\u6377\u6367\u6398\u63AA\u6371\u63A9\u6389\u6383\u639B\u636B\u63A8\u6384\u6388\u6399\u63A1\u63AC\u6392\u638F\u6380\u637B\u6369\u6368\u637A\u655D\u6556\u6551\u6559\u6557\u555F\u654F\u6558\u6555\u6554\u659C\u659B\u65AC\u65CF\u65CB\u65CC\u65CE\u665D\u665A\u6664\u6668\u6666\u665E\u66F9\u52D7\u671B\u6881\u68AF\u68A2\u6893\u68B5\u687F\u6876\u68B1\u68A7\u6897\u68B0\u6883\u68C4\u68AD\u6886\u6885\u6894\u689D\u68A8\u689F\u68A1\u6882\u6B32\u6BBA"],["b240","\u6BEB\u6BEC\u6C2B\u6D8E\u6DBC\u6DF3\u6DD9\u6DB2\u6DE1\u6DCC\u6DE4\u6DFB\u6DFA\u6E05\u6DC7\u6DCB\u6DAF\u6DD1\u6DAE\u6DDE\u6DF9\u6DB8\u6DF7\u6DF5\u6DC5\u6DD2\u6E1A\u6DB5\u6DDA\u6DEB\u6DD8\u6DEA\u6DF1\u6DEE\u6DE8\u6DC6\u6DC4\u6DAA\u6DEC\u6DBF\u6DE6\u70F9\u7109\u710A\u70FD\u70EF\u723D\u727D\u7281\u731C\u731B\u7316\u7313\u7319\u7387\u7405\u740A\u7403\u7406\u73FE\u740D\u74E0\u74F6"],["b2a1","\u74F7\u751C\u7522\u7565\u7566\u7562\u7570\u758F\u75D4\u75D5\u75B5\u75CA\u75CD\u768E\u76D4\u76D2\u76DB\u7737\u773E\u773C\u7736\u7738\u773A\u786B\u7843\u784E\u7965\u7968\u796D\u79FB\u7A92\u7A95\u7B20\u7B28\u7B1B\u7B2C\u7B26\u7B19\u7B1E\u7B2E\u7C92\u7C97\u7C95\u7D46\u7D43\u7D71\u7D2E\u7D39\u7D3C\u7D40\u7D30\u7D33\u7D44\u7D2F\u7D42\u7D32\u7D31\u7F3D\u7F9E\u7F9A\u7FCC\u7FCE\u7FD2\u801C\u804A\u8046\u812F\u8116\u8123\u812B\u8129\u8130\u8124\u8202\u8235\u8237\u8236\u8239\u838E\u839E\u8398\u8378\u83A2\u8396\u83BD\u83AB\u8392\u838A\u8393\u8389\u83A0\u8377\u837B\u837C"],["b340","\u8386\u83A7\u8655\u5F6A\u86C7\u86C0\u86B6\u86C4\u86B5\u86C6\u86CB\u86B1\u86AF\u86C9\u8853\u889E\u8888\u88AB\u8892\u8896\u888D\u888B\u8993\u898F\u8A2A\u8A1D\u8A23\u8A25\u8A31\u8A2D\u8A1F\u8A1B\u8A22\u8C49\u8C5A\u8CA9\u8CAC\u8CAB\u8CA8\u8CAA\u8CA7\u8D67\u8D66\u8DBE\u8DBA\u8EDB\u8EDF\u9019\u900D\u901A\u9017\u9023\u901F\u901D\u9010\u9015\u901E\u9020\u900F\u9022\u9016\u901B\u9014"],["b3a1","\u90E8\u90ED\u90FD\u9157\u91CE\u91F5\u91E6\u91E3\u91E7\u91ED\u91E9\u9589\u966A\u9675\u9673\u9678\u9670\u9674\u9676\u9677\u966C\u96C0\u96EA\u96E9\u7AE0\u7ADF\u9802\u9803\u9B5A\u9CE5\u9E75\u9E7F\u9EA5\u9EBB\u50A2\u508D\u5085\u5099\u5091\u5080\u5096\u5098\u509A\u6700\u51F1\u5272\u5274\u5275\u5269\u52DE\u52DD\u52DB\u535A\u53A5\u557B\u5580\u55A7\u557C\u558A\u559D\u5598\u5582\u559C\u55AA\u5594\u5587\u558B\u5583\u55B3\u55AE\u559F\u553E\u55B2\u559A\u55BB\u55AC\u55B1\u557E\u5589\u55AB\u5599\u570D\u582F\u582A\u5834\u5824\u5830\u5831\u5821\u581D\u5820\u58F9\u58FA\u5960"],["b440","\u5A77\u5A9A\u5A7F\u5A92\u5A9B\u5AA7\u5B73\u5B71\u5BD2\u5BCC\u5BD3\u5BD0\u5C0A\u5C0B\u5C31\u5D4C\u5D50\u5D34\u5D47\u5DFD\u5E45\u5E3D\u5E40\u5E43\u5E7E\u5ECA\u5EC1\u5EC2\u5EC4\u5F3C\u5F6D\u5FA9\u5FAA\u5FA8\u60D1\u60E1\u60B2\u60B6\u60E0\u611C\u6123\u60FA\u6115\u60F0\u60FB\u60F4\u6168\u60F1\u610E\u60F6\u6109\u6100\u6112\u621F\u6249\u63A3\u638C\u63CF\u63C0\u63E9\u63C9\u63C6\u63CD"],["b4a1","\u63D2\u63E3\u63D0\u63E1\u63D6\u63ED\u63EE\u6376\u63F4\u63EA\u63DB\u6452\u63DA\u63F9\u655E\u6566\u6562\u6563\u6591\u6590\u65AF\u666E\u6670\u6674\u6676\u666F\u6691\u667A\u667E\u6677\u66FE\u66FF\u671F\u671D\u68FA\u68D5\u68E0\u68D8\u68D7\u6905\u68DF\u68F5\u68EE\u68E7\u68F9\u68D2\u68F2\u68E3\u68CB\u68CD\u690D\u6912\u690E\u68C9\u68DA\u696E\u68FB\u6B3E\u6B3A\u6B3D\u6B98\u6B96\u6BBC\u6BEF\u6C2E\u6C2F\u6C2C\u6E2F\u6E38\u6E54\u6E21\u6E32\u6E67\u6E4A\u6E20\u6E25\u6E23\u6E1B\u6E5B\u6E58\u6E24\u6E56\u6E6E\u6E2D\u6E26\u6E6F\u6E34\u6E4D\u6E3A\u6E2C\u6E43\u6E1D\u6E3E\u6ECB"],["b540","\u6E89\u6E19\u6E4E\u6E63\u6E44\u6E72\u6E69\u6E5F\u7119\u711A\u7126\u7130\u7121\u7136\u716E\u711C\u724C\u7284\u7280\u7336\u7325\u7334\u7329\u743A\u742A\u7433\u7422\u7425\u7435\u7436\u7434\u742F\u741B\u7426\u7428\u7525\u7526\u756B\u756A\u75E2\u75DB\u75E3\u75D9\u75D8\u75DE\u75E0\u767B\u767C\u7696\u7693\u76B4\u76DC\u774F\u77ED\u785D\u786C\u786F\u7A0D\u7A08\u7A0B\u7A05\u7A00\u7A98"],["b5a1","\u7A97\u7A96\u7AE5\u7AE3\u7B49\u7B56\u7B46\u7B50\u7B52\u7B54\u7B4D\u7B4B\u7B4F\u7B51\u7C9F\u7CA5\u7D5E\u7D50\u7D68\u7D55\u7D2B\u7D6E\u7D72\u7D61\u7D66\u7D62\u7D70\u7D73\u5584\u7FD4\u7FD5\u800B\u8052\u8085\u8155\u8154\u814B\u8151\u814E\u8139\u8146\u813E\u814C\u8153\u8174\u8212\u821C\u83E9\u8403\u83F8\u840D\u83E0\u83C5\u840B\u83C1\u83EF\u83F1\u83F4\u8457\u840A\u83F0\u840C\u83CC\u83FD\u83F2\u83CA\u8438\u840E\u8404\u83DC\u8407\u83D4\u83DF\u865B\u86DF\u86D9\u86ED\u86D4\u86DB\u86E4\u86D0\u86DE\u8857\u88C1\u88C2\u88B1\u8983\u8996\u8A3B\u8A60\u8A55\u8A5E\u8A3C\u8A41"],["b640","\u8A54\u8A5B\u8A50\u8A46\u8A34\u8A3A\u8A36\u8A56\u8C61\u8C82\u8CAF\u8CBC\u8CB3\u8CBD\u8CC1\u8CBB\u8CC0\u8CB4\u8CB7\u8CB6\u8CBF\u8CB8\u8D8A\u8D85\u8D81\u8DCE\u8DDD\u8DCB\u8DDA\u8DD1\u8DCC\u8DDB\u8DC6\u8EFB\u8EF8\u8EFC\u8F9C\u902E\u9035\u9031\u9038\u9032\u9036\u9102\u90F5\u9109\u90FE\u9163\u9165\u91CF\u9214\u9215\u9223\u9209\u921E\u920D\u9210\u9207\u9211\u9594\u958F\u958B\u9591"],["b6a1","\u9593\u9592\u958E\u968A\u968E\u968B\u967D\u9685\u9686\u968D\u9672\u9684\u96C1\u96C5\u96C4\u96C6\u96C7\u96EF\u96F2\u97CC\u9805\u9806\u9808\u98E7\u98EA\u98EF\u98E9\u98F2\u98ED\u99AE\u99AD\u9EC3\u9ECD\u9ED1\u4E82\u50AD\u50B5\u50B2\u50B3\u50C5\u50BE\u50AC\u50B7\u50BB\u50AF\u50C7\u527F\u5277\u527D\u52DF\u52E6\u52E4\u52E2\u52E3\u532F\u55DF\u55E8\u55D3\u55E6\u55CE\u55DC\u55C7\u55D1\u55E3\u55E4\u55EF\u55DA\u55E1\u55C5\u55C6\u55E5\u55C9\u5712\u5713\u585E\u5851\u5858\u5857\u585A\u5854\u586B\u584C\u586D\u584A\u5862\u5852\u584B\u5967\u5AC1\u5AC9\u5ACC\u5ABE\u5ABD\u5ABC"],["b740","\u5AB3\u5AC2\u5AB2\u5D69\u5D6F\u5E4C\u5E79\u5EC9\u5EC8\u5F12\u5F59\u5FAC\u5FAE\u611A\u610F\u6148\u611F\u60F3\u611B\u60F9\u6101\u6108\u614E\u614C\u6144\u614D\u613E\u6134\u6127\u610D\u6106\u6137\u6221\u6222\u6413\u643E\u641E\u642A\u642D\u643D\u642C\u640F\u641C\u6414\u640D\u6436\u6416\u6417\u6406\u656C\u659F\u65B0\u6697\u6689\u6687\u6688\u6696\u6684\u6698\u668D\u6703\u6994\u696D"],["b7a1","\u695A\u6977\u6960\u6954\u6975\u6930\u6982\u694A\u6968\u696B\u695E\u6953\u6979\u6986\u695D\u6963\u695B\u6B47\u6B72\u6BC0\u6BBF\u6BD3\u6BFD\u6EA2\u6EAF\u6ED3\u6EB6\u6EC2\u6E90\u6E9D\u6EC7\u6EC5\u6EA5\u6E98\u6EBC\u6EBA\u6EAB\u6ED1\u6E96\u6E9C\u6EC4\u6ED4\u6EAA\u6EA7\u6EB4\u714E\u7159\u7169\u7164\u7149\u7167\u715C\u716C\u7166\u714C\u7165\u715E\u7146\u7168\u7156\u723A\u7252\u7337\u7345\u733F\u733E\u746F\u745A\u7455\u745F\u745E\u7441\u743F\u7459\u745B\u745C\u7576\u7578\u7600\u75F0\u7601\u75F2\u75F1\u75FA\u75FF\u75F4\u75F3\u76DE\u76DF\u775B\u776B\u7766\u775E\u7763"],["b840","\u7779\u776A\u776C\u775C\u7765\u7768\u7762\u77EE\u788E\u78B0\u7897\u7898\u788C\u7889\u787C\u7891\u7893\u787F\u797A\u797F\u7981\u842C\u79BD\u7A1C\u7A1A\u7A20\u7A14\u7A1F\u7A1E\u7A9F\u7AA0\u7B77\u7BC0\u7B60\u7B6E\u7B67\u7CB1\u7CB3\u7CB5\u7D93\u7D79\u7D91\u7D81\u7D8F\u7D5B\u7F6E\u7F69\u7F6A\u7F72\u7FA9\u7FA8\u7FA4\u8056\u8058\u8086\u8084\u8171\u8170\u8178\u8165\u816E\u8173\u816B"],["b8a1","\u8179\u817A\u8166\u8205\u8247\u8482\u8477\u843D\u8431\u8475\u8466\u846B\u8449\u846C\u845B\u843C\u8435\u8461\u8463\u8469\u846D\u8446\u865E\u865C\u865F\u86F9\u8713\u8708\u8707\u8700\u86FE\u86FB\u8702\u8703\u8706\u870A\u8859\u88DF\u88D4\u88D9\u88DC\u88D8\u88DD\u88E1\u88CA\u88D5\u88D2\u899C\u89E3\u8A6B\u8A72\u8A73\u8A66\u8A69\u8A70\u8A87\u8A7C\u8A63\u8AA0\u8A71\u8A85\u8A6D\u8A62\u8A6E\u8A6C\u8A79\u8A7B\u8A3E\u8A68\u8C62\u8C8A\u8C89\u8CCA\u8CC7\u8CC8\u8CC4\u8CB2\u8CC3\u8CC2\u8CC5\u8DE1\u8DDF\u8DE8\u8DEF\u8DF3\u8DFA\u8DEA\u8DE4\u8DE6\u8EB2\u8F03\u8F09\u8EFE\u8F0A"],["b940","\u8F9F\u8FB2\u904B\u904A\u9053\u9042\u9054\u903C\u9055\u9050\u9047\u904F\u904E\u904D\u9051\u903E\u9041\u9112\u9117\u916C\u916A\u9169\u91C9\u9237\u9257\u9238\u923D\u9240\u923E\u925B\u924B\u9264\u9251\u9234\u9249\u924D\u9245\u9239\u923F\u925A\u9598\u9698\u9694\u9695\u96CD\u96CB\u96C9\u96CA\u96F7\u96FB\u96F9\u96F6\u9756\u9774\u9776\u9810\u9811\u9813\u980A\u9812\u980C\u98FC\u98F4"],["b9a1","\u98FD\u98FE\u99B3\u99B1\u99B4\u9AE1\u9CE9\u9E82\u9F0E\u9F13\u9F20\u50E7\u50EE\u50E5\u50D6\u50ED\u50DA\u50D5\u50CF\u50D1\u50F1\u50CE\u50E9\u5162\u51F3\u5283\u5282\u5331\u53AD\u55FE\u5600\u561B\u5617\u55FD\u5614\u5606\u5609\u560D\u560E\u55F7\u5616\u561F\u5608\u5610\u55F6\u5718\u5716\u5875\u587E\u5883\u5893\u588A\u5879\u5885\u587D\u58FD\u5925\u5922\u5924\u596A\u5969\u5AE1\u5AE6\u5AE9\u5AD7\u5AD6\u5AD8\u5AE3\u5B75\u5BDE\u5BE7\u5BE1\u5BE5\u5BE6\u5BE8\u5BE2\u5BE4\u5BDF\u5C0D\u5C62\u5D84\u5D87\u5E5B\u5E63\u5E55\u5E57\u5E54\u5ED3\u5ED6\u5F0A\u5F46\u5F70\u5FB9\u6147"],["ba40","\u613F\u614B\u6177\u6162\u6163\u615F\u615A\u6158\u6175\u622A\u6487\u6458\u6454\u64A4\u6478\u645F\u647A\u6451\u6467\u6434\u646D\u647B\u6572\u65A1\u65D7\u65D6\u66A2\u66A8\u669D\u699C\u69A8\u6995\u69C1\u69AE\u69D3\u69CB\u699B\u69B7\u69BB\u69AB\u69B4\u69D0\u69CD\u69AD\u69CC\u69A6\u69C3\u69A3\u6B49\u6B4C\u6C33\u6F33\u6F14\u6EFE\u6F13\u6EF4\u6F29\u6F3E\u6F20\u6F2C\u6F0F\u6F02\u6F22"],["baa1","\u6EFF\u6EEF\u6F06\u6F31\u6F38\u6F32\u6F23\u6F15\u6F2B\u6F2F\u6F88\u6F2A\u6EEC\u6F01\u6EF2\u6ECC\u6EF7\u7194\u7199\u717D\u718A\u7184\u7192\u723E\u7292\u7296\u7344\u7350\u7464\u7463\u746A\u7470\u746D\u7504\u7591\u7627\u760D\u760B\u7609\u7613\u76E1\u76E3\u7784\u777D\u777F\u7761\u78C1\u789F\u78A7\u78B3\u78A9\u78A3\u798E\u798F\u798D\u7A2E\u7A31\u7AAA\u7AA9\u7AED\u7AEF\u7BA1\u7B95\u7B8B\u7B75\u7B97\u7B9D\u7B94\u7B8F\u7BB8\u7B87\u7B84\u7CB9\u7CBD\u7CBE\u7DBB\u7DB0\u7D9C\u7DBD\u7DBE\u7DA0\u7DCA\u7DB4\u7DB2\u7DB1\u7DBA\u7DA2\u7DBF\u7DB5\u7DB8\u7DAD\u7DD2\u7DC7\u7DAC"],["bb40","\u7F70\u7FE0\u7FE1\u7FDF\u805E\u805A\u8087\u8150\u8180\u818F\u8188\u818A\u817F\u8182\u81E7\u81FA\u8207\u8214\u821E\u824B\u84C9\u84BF\u84C6\u84C4\u8499\u849E\u84B2\u849C\u84CB\u84B8\u84C0\u84D3\u8490\u84BC\u84D1\u84CA\u873F\u871C\u873B\u8722\u8725\u8734\u8718\u8755\u8737\u8729\u88F3\u8902\u88F4\u88F9\u88F8\u88FD\u88E8\u891A\u88EF\u8AA6\u8A8C\u8A9E\u8AA3\u8A8D\u8AA1\u8A93\u8AA4"],["bba1","\u8AAA\u8AA5\u8AA8\u8A98\u8A91\u8A9A\u8AA7\u8C6A\u8C8D\u8C8C\u8CD3\u8CD1\u8CD2\u8D6B\u8D99\u8D95\u8DFC\u8F14\u8F12\u8F15\u8F13\u8FA3\u9060\u9058\u905C\u9063\u9059\u905E\u9062\u905D\u905B\u9119\u9118\u911E\u9175\u9178\u9177\u9174\u9278\u9280\u9285\u9298\u9296\u927B\u9293\u929C\u92A8\u927C\u9291\u95A1\u95A8\u95A9\u95A3\u95A5\u95A4\u9699\u969C\u969B\u96CC\u96D2\u9700\u977C\u9785\u97F6\u9817\u9818\u98AF\u98B1\u9903\u9905\u990C\u9909\u99C1\u9AAF\u9AB0\u9AE6\u9B41\u9B42\u9CF4\u9CF6\u9CF3\u9EBC\u9F3B\u9F4A\u5104\u5100\u50FB\u50F5\u50F9\u5102\u5108\u5109\u5105\u51DC"],["bc40","\u5287\u5288\u5289\u528D\u528A\u52F0\u53B2\u562E\u563B\u5639\u5632\u563F\u5634\u5629\u5653\u564E\u5657\u5674\u5636\u562F\u5630\u5880\u589F\u589E\u58B3\u589C\u58AE\u58A9\u58A6\u596D\u5B09\u5AFB\u5B0B\u5AF5\u5B0C\u5B08\u5BEE\u5BEC\u5BE9\u5BEB\u5C64\u5C65\u5D9D\u5D94\u5E62\u5E5F\u5E61\u5EE2\u5EDA\u5EDF\u5EDD\u5EE3\u5EE0\u5F48\u5F71\u5FB7\u5FB5\u6176\u6167\u616E\u615D\u6155\u6182"],["bca1","\u617C\u6170\u616B\u617E\u61A7\u6190\u61AB\u618E\u61AC\u619A\u61A4\u6194\u61AE\u622E\u6469\u646F\u6479\u649E\u64B2\u6488\u6490\u64B0\u64A5\u6493\u6495\u64A9\u6492\u64AE\u64AD\u64AB\u649A\u64AC\u6499\u64A2\u64B3\u6575\u6577\u6578\u66AE\u66AB\u66B4\u66B1\u6A23\u6A1F\u69E8\u6A01\u6A1E\u6A19\u69FD\u6A21\u6A13\u6A0A\u69F3\u6A02\u6A05\u69ED\u6A11\u6B50\u6B4E\u6BA4\u6BC5\u6BC6\u6F3F\u6F7C\u6F84\u6F51\u6F66\u6F54\u6F86\u6F6D\u6F5B\u6F78\u6F6E\u6F8E\u6F7A\u6F70\u6F64\u6F97\u6F58\u6ED5\u6F6F\u6F60\u6F5F\u719F\u71AC\u71B1\u71A8\u7256\u729B\u734E\u7357\u7469\u748B\u7483"],["bd40","\u747E\u7480\u757F\u7620\u7629\u761F\u7624\u7626\u7621\u7622\u769A\u76BA\u76E4\u778E\u7787\u778C\u7791\u778B\u78CB\u78C5\u78BA\u78CA\u78BE\u78D5\u78BC\u78D0\u7A3F\u7A3C\u7A40\u7A3D\u7A37\u7A3B\u7AAF\u7AAE\u7BAD\u7BB1\u7BC4\u7BB4\u7BC6\u7BC7\u7BC1\u7BA0\u7BCC\u7CCA\u7DE0\u7DF4\u7DEF\u7DFB\u7DD8\u7DEC\u7DDD\u7DE8\u7DE3\u7DDA\u7DDE\u7DE9\u7D9E\u7DD9\u7DF2\u7DF9\u7F75\u7F77\u7FAF"],["bda1","\u7FE9\u8026\u819B\u819C\u819D\u81A0\u819A\u8198\u8517\u853D\u851A\u84EE\u852C\u852D\u8513\u8511\u8523\u8521\u8514\u84EC\u8525\u84FF\u8506\u8782\u8774\u8776\u8760\u8766\u8778\u8768\u8759\u8757\u874C\u8753\u885B\u885D\u8910\u8907\u8912\u8913\u8915\u890A\u8ABC\u8AD2\u8AC7\u8AC4\u8A95\u8ACB\u8AF8\u8AB2\u8AC9\u8AC2\u8ABF\u8AB0\u8AD6\u8ACD\u8AB6\u8AB9\u8ADB\u8C4C\u8C4E\u8C6C\u8CE0\u8CDE\u8CE6\u8CE4\u8CEC\u8CED\u8CE2\u8CE3\u8CDC\u8CEA\u8CE1\u8D6D\u8D9F\u8DA3\u8E2B\u8E10\u8E1D\u8E22\u8E0F\u8E29\u8E1F\u8E21\u8E1E\u8EBA\u8F1D\u8F1B\u8F1F\u8F29\u8F26\u8F2A\u8F1C\u8F1E"],["be40","\u8F25\u9069\u906E\u9068\u906D\u9077\u9130\u912D\u9127\u9131\u9187\u9189\u918B\u9183\u92C5\u92BB\u92B7\u92EA\u92AC\u92E4\u92C1\u92B3\u92BC\u92D2\u92C7\u92F0\u92B2\u95AD\u95B1\u9704\u9706\u9707\u9709\u9760\u978D\u978B\u978F\u9821\u982B\u981C\u98B3\u990A\u9913\u9912\u9918\u99DD\u99D0\u99DF\u99DB\u99D1\u99D5\u99D2\u99D9\u9AB7\u9AEE\u9AEF\u9B27\u9B45\u9B44\u9B77\u9B6F\u9D06\u9D09"],["bea1","\u9D03\u9EA9\u9EBE\u9ECE\u58A8\u9F52\u5112\u5118\u5114\u5110\u5115\u5180\u51AA\u51DD\u5291\u5293\u52F3\u5659\u566B\u5679\u5669\u5664\u5678\u566A\u5668\u5665\u5671\u566F\u566C\u5662\u5676\u58C1\u58BE\u58C7\u58C5\u596E\u5B1D\u5B34\u5B78\u5BF0\u5C0E\u5F4A\u61B2\u6191\u61A9\u618A\u61CD\u61B6\u61BE\u61CA\u61C8\u6230\u64C5\u64C1\u64CB\u64BB\u64BC\u64DA\u64C4\u64C7\u64C2\u64CD\u64BF\u64D2\u64D4\u64BE\u6574\u66C6\u66C9\u66B9\u66C4\u66C7\u66B8\u6A3D\u6A38\u6A3A\u6A59\u6A6B\u6A58\u6A39\u6A44\u6A62\u6A61\u6A4B\u6A47\u6A35\u6A5F\u6A48\u6B59\u6B77\u6C05\u6FC2\u6FB1\u6FA1"],["bf40","\u6FC3\u6FA4\u6FC1\u6FA7\u6FB3\u6FC0\u6FB9\u6FB6\u6FA6\u6FA0\u6FB4\u71BE\u71C9\u71D0\u71D2\u71C8\u71D5\u71B9\u71CE\u71D9\u71DC\u71C3\u71C4\u7368\u749C\u74A3\u7498\u749F\u749E\u74E2\u750C\u750D\u7634\u7638\u763A\u76E7\u76E5\u77A0\u779E\u779F\u77A5\u78E8\u78DA\u78EC\u78E7\u79A6\u7A4D\u7A4E\u7A46\u7A4C\u7A4B\u7ABA\u7BD9\u7C11\u7BC9\u7BE4\u7BDB\u7BE1\u7BE9\u7BE6\u7CD5\u7CD6\u7E0A"],["bfa1","\u7E11\u7E08\u7E1B\u7E23\u7E1E\u7E1D\u7E09\u7E10\u7F79\u7FB2\u7FF0\u7FF1\u7FEE\u8028\u81B3\u81A9\u81A8\u81FB\u8208\u8258\u8259\u854A\u8559\u8548\u8568\u8569\u8543\u8549\u856D\u856A\u855E\u8783\u879F\u879E\u87A2\u878D\u8861\u892A\u8932\u8925\u892B\u8921\u89AA\u89A6\u8AE6\u8AFA\u8AEB\u8AF1\u8B00\u8ADC\u8AE7\u8AEE\u8AFE\u8B01\u8B02\u8AF7\u8AED\u8AF3\u8AF6\u8AFC\u8C6B\u8C6D\u8C93\u8CF4\u8E44\u8E31\u8E34\u8E42\u8E39\u8E35\u8F3B\u8F2F\u8F38\u8F33\u8FA8\u8FA6\u9075\u9074\u9078\u9072\u907C\u907A\u9134\u9192\u9320\u9336\u92F8\u9333\u932F\u9322\u92FC\u932B\u9304\u931A"],["c040","\u9310\u9326\u9321\u9315\u932E\u9319\u95BB\u96A7\u96A8\u96AA\u96D5\u970E\u9711\u9716\u970D\u9713\u970F\u975B\u975C\u9766\u9798\u9830\u9838\u983B\u9837\u982D\u9839\u9824\u9910\u9928\u991E\u991B\u9921\u991A\u99ED\u99E2\u99F1\u9AB8\u9ABC\u9AFB\u9AED\u9B28\u9B91\u9D15\u9D23\u9D26\u9D28\u9D12\u9D1B\u9ED8\u9ED4\u9F8D\u9F9C\u512A\u511F\u5121\u5132\u52F5\u568E\u5680\u5690\u5685\u5687"],["c0a1","\u568F\u58D5\u58D3\u58D1\u58CE\u5B30\u5B2A\u5B24\u5B7A\u5C37\u5C68\u5DBC\u5DBA\u5DBD\u5DB8\u5E6B\u5F4C\u5FBD\u61C9\u61C2\u61C7\u61E6\u61CB\u6232\u6234\u64CE\u64CA\u64D8\u64E0\u64F0\u64E6\u64EC\u64F1\u64E2\u64ED\u6582\u6583\u66D9\u66D6\u6A80\u6A94\u6A84\u6AA2\u6A9C\u6ADB\u6AA3\u6A7E\u6A97\u6A90\u6AA0\u6B5C\u6BAE\u6BDA\u6C08\u6FD8\u6FF1\u6FDF\u6FE0\u6FDB\u6FE4\u6FEB\u6FEF\u6F80\u6FEC\u6FE1\u6FE9\u6FD5\u6FEE\u6FF0\u71E7\u71DF\u71EE\u71E6\u71E5\u71ED\u71EC\u71F4\u71E0\u7235\u7246\u7370\u7372\u74A9\u74B0\u74A6\u74A8\u7646\u7642\u764C\u76EA\u77B3\u77AA\u77B0\u77AC"],["c140","\u77A7\u77AD\u77EF\u78F7\u78FA\u78F4\u78EF\u7901\u79A7\u79AA\u7A57\u7ABF\u7C07\u7C0D\u7BFE\u7BF7\u7C0C\u7BE0\u7CE0\u7CDC\u7CDE\u7CE2\u7CDF\u7CD9\u7CDD\u7E2E\u7E3E\u7E46\u7E37\u7E32\u7E43\u7E2B\u7E3D\u7E31\u7E45\u7E41\u7E34\u7E39\u7E48\u7E35\u7E3F\u7E2F\u7F44\u7FF3\u7FFC\u8071\u8072\u8070\u806F\u8073\u81C6\u81C3\u81BA\u81C2\u81C0\u81BF\u81BD\u81C9\u81BE\u81E8\u8209\u8271\u85AA"],["c1a1","\u8584\u857E\u859C\u8591\u8594\u85AF\u859B\u8587\u85A8\u858A\u8667\u87C0\u87D1\u87B3\u87D2\u87C6\u87AB\u87BB\u87BA\u87C8\u87CB\u893B\u8936\u8944\u8938\u893D\u89AC\u8B0E\u8B17\u8B19\u8B1B\u8B0A\u8B20\u8B1D\u8B04\u8B10\u8C41\u8C3F\u8C73\u8CFA\u8CFD\u8CFC\u8CF8\u8CFB\u8DA8\u8E49\u8E4B\u8E48\u8E4A\u8F44\u8F3E\u8F42\u8F45\u8F3F\u907F\u907D\u9084\u9081\u9082\u9080\u9139\u91A3\u919E\u919C\u934D\u9382\u9328\u9375\u934A\u9365\u934B\u9318\u937E\u936C\u935B\u9370\u935A\u9354\u95CA\u95CB\u95CC\u95C8\u95C6\u96B1\u96B8\u96D6\u971C\u971E\u97A0\u97D3\u9846\u98B6\u9935\u9A01"],["c240","\u99FF\u9BAE\u9BAB\u9BAA\u9BAD\u9D3B\u9D3F\u9E8B\u9ECF\u9EDE\u9EDC\u9EDD\u9EDB\u9F3E\u9F4B\u53E2\u5695\u56AE\u58D9\u58D8\u5B38\u5F5D\u61E3\u6233\u64F4\u64F2\u64FE\u6506\u64FA\u64FB\u64F7\u65B7\u66DC\u6726\u6AB3\u6AAC\u6AC3\u6ABB\u6AB8\u6AC2\u6AAE\u6AAF\u6B5F\u6B78\u6BAF\u7009\u700B\u6FFE\u7006\u6FFA\u7011\u700F\u71FB\u71FC\u71FE\u71F8\u7377\u7375\u74A7\u74BF\u7515\u7656\u7658"],["c2a1","\u7652\u77BD\u77BF\u77BB\u77BC\u790E\u79AE\u7A61\u7A62\u7A60\u7AC4\u7AC5\u7C2B\u7C27\u7C2A\u7C1E\u7C23\u7C21\u7CE7\u7E54\u7E55\u7E5E\u7E5A\u7E61\u7E52\u7E59\u7F48\u7FF9\u7FFB\u8077\u8076\u81CD\u81CF\u820A\u85CF\u85A9\u85CD\u85D0\u85C9\u85B0\u85BA\u85B9\u85A6\u87EF\u87EC\u87F2\u87E0\u8986\u89B2\u89F4\u8B28\u8B39\u8B2C\u8B2B\u8C50\u8D05\u8E59\u8E63\u8E66\u8E64\u8E5F\u8E55\u8EC0\u8F49\u8F4D\u9087\u9083\u9088\u91AB\u91AC\u91D0\u9394\u938A\u9396\u93A2\u93B3\u93AE\u93AC\u93B0\u9398\u939A\u9397\u95D4\u95D6\u95D0\u95D5\u96E2\u96DC\u96D9\u96DB\u96DE\u9724\u97A3\u97A6"],["c340","\u97AD\u97F9\u984D\u984F\u984C\u984E\u9853\u98BA\u993E\u993F\u993D\u992E\u99A5\u9A0E\u9AC1\u9B03\u9B06\u9B4F\u9B4E\u9B4D\u9BCA\u9BC9\u9BFD\u9BC8\u9BC0\u9D51\u9D5D\u9D60\u9EE0\u9F15\u9F2C\u5133\u56A5\u58DE\u58DF\u58E2\u5BF5\u9F90\u5EEC\u61F2\u61F7\u61F6\u61F5\u6500\u650F\u66E0\u66DD\u6AE5\u6ADD\u6ADA\u6AD3\u701B\u701F\u7028\u701A\u701D\u7015\u7018\u7206\u720D\u7258\u72A2\u7378"],["c3a1","\u737A\u74BD\u74CA\u74E3\u7587\u7586\u765F\u7661\u77C7\u7919\u79B1\u7A6B\u7A69\u7C3E\u7C3F\u7C38\u7C3D\u7C37\u7C40\u7E6B\u7E6D\u7E79\u7E69\u7E6A\u7F85\u7E73\u7FB6\u7FB9\u7FB8\u81D8\u85E9\u85DD\u85EA\u85D5\u85E4\u85E5\u85F7\u87FB\u8805\u880D\u87F9\u87FE\u8960\u895F\u8956\u895E\u8B41\u8B5C\u8B58\u8B49\u8B5A\u8B4E\u8B4F\u8B46\u8B59\u8D08\u8D0A\u8E7C\u8E72\u8E87\u8E76\u8E6C\u8E7A\u8E74\u8F54\u8F4E\u8FAD\u908A\u908B\u91B1\u91AE\u93E1\u93D1\u93DF\u93C3\u93C8\u93DC\u93DD\u93D6\u93E2\u93CD\u93D8\u93E4\u93D7\u93E8\u95DC\u96B4\u96E3\u972A\u9727\u9761\u97DC\u97FB\u985E"],["c440","\u9858\u985B\u98BC\u9945\u9949\u9A16\u9A19\u9B0D\u9BE8\u9BE7\u9BD6\u9BDB\u9D89\u9D61\u9D72\u9D6A\u9D6C\u9E92\u9E97\u9E93\u9EB4\u52F8\u56A8\u56B7\u56B6\u56B4\u56BC\u58E4\u5B40\u5B43\u5B7D\u5BF6\u5DC9\u61F8\u61FA\u6518\u6514\u6519\u66E6\u6727\u6AEC\u703E\u7030\u7032\u7210\u737B\u74CF\u7662\u7665\u7926\u792A\u792C\u792B\u7AC7\u7AF6\u7C4C\u7C43\u7C4D\u7CEF\u7CF0\u8FAE\u7E7D\u7E7C"],["c4a1","\u7E82\u7F4C\u8000\u81DA\u8266\u85FB\u85F9\u8611\u85FA\u8606\u860B\u8607\u860A\u8814\u8815\u8964\u89BA\u89F8\u8B70\u8B6C\u8B66\u8B6F\u8B5F\u8B6B\u8D0F\u8D0D\u8E89\u8E81\u8E85\u8E82\u91B4\u91CB\u9418\u9403\u93FD\u95E1\u9730\u98C4\u9952\u9951\u99A8\u9A2B\u9A30\u9A37\u9A35\u9C13\u9C0D\u9E79\u9EB5\u9EE8\u9F2F\u9F5F\u9F63\u9F61\u5137\u5138\u56C1\u56C0\u56C2\u5914\u5C6C\u5DCD\u61FC\u61FE\u651D\u651C\u6595\u66E9\u6AFB\u6B04\u6AFA\u6BB2\u704C\u721B\u72A7\u74D6\u74D4\u7669\u77D3\u7C50\u7E8F\u7E8C\u7FBC\u8617\u862D\u861A\u8823\u8822\u8821\u881F\u896A\u896C\u89BD\u8B74"],["c540","\u8B77\u8B7D\u8D13\u8E8A\u8E8D\u8E8B\u8F5F\u8FAF\u91BA\u942E\u9433\u9435\u943A\u9438\u9432\u942B\u95E2\u9738\u9739\u9732\u97FF\u9867\u9865\u9957\u9A45\u9A43\u9A40\u9A3E\u9ACF\u9B54\u9B51\u9C2D\u9C25\u9DAF\u9DB4\u9DC2\u9DB8\u9E9D\u9EEF\u9F19\u9F5C\u9F66\u9F67\u513C\u513B\u56C8\u56CA\u56C9\u5B7F\u5DD4\u5DD2\u5F4E\u61FF\u6524\u6B0A\u6B61\u7051\u7058\u7380\u74E4\u758A\u766E\u766C"],["c5a1","\u79B3\u7C60\u7C5F\u807E\u807D\u81DF\u8972\u896F\u89FC\u8B80\u8D16\u8D17\u8E91\u8E93\u8F61\u9148\u9444\u9451\u9452\u973D\u973E\u97C3\u97C1\u986B\u9955\u9A55\u9A4D\u9AD2\u9B1A\u9C49\u9C31\u9C3E\u9C3B\u9DD3\u9DD7\u9F34\u9F6C\u9F6A\u9F94\u56CC\u5DD6\u6200\u6523\u652B\u652A\u66EC\u6B10\u74DA\u7ACA\u7C64\u7C63\u7C65\u7E93\u7E96\u7E94\u81E2\u8638\u863F\u8831\u8B8A\u9090\u908F\u9463\u9460\u9464\u9768\u986F\u995C\u9A5A\u9A5B\u9A57\u9AD3\u9AD4\u9AD1\u9C54\u9C57\u9C56\u9DE5\u9E9F\u9EF4\u56D1\u58E9\u652C\u705E\u7671\u7672\u77D7\u7F50\u7F88\u8836\u8839\u8862\u8B93\u8B92"],["c640","\u8B96\u8277\u8D1B\u91C0\u946A\u9742\u9748\u9744\u97C6\u9870\u9A5F\u9B22\u9B58\u9C5F\u9DF9\u9DFA\u9E7C\u9E7D\u9F07\u9F77\u9F72\u5EF3\u6B16\u7063\u7C6C\u7C6E\u883B\u89C0\u8EA1\u91C1\u9472\u9470\u9871\u995E\u9AD6\u9B23\u9ECC\u7064\u77DA\u8B9A\u9477\u97C9\u9A62\u9A65\u7E9C\u8B9C\u8EAA\u91C5\u947D\u947E\u947C\u9C77\u9C78\u9EF7\u8C54\u947F\u9E1A\u7228\u9A6A\u9B31\u9E1B\u9E1E\u7C72"],["c940","\u4E42\u4E5C\u51F5\u531A\u5382\u4E07\u4E0C\u4E47\u4E8D\u56D7\uFA0C\u5C6E\u5F73\u4E0F\u5187\u4E0E\u4E2E\u4E93\u4EC2\u4EC9\u4EC8\u5198\u52FC\u536C\u53B9\u5720\u5903\u592C\u5C10\u5DFF\u65E1\u6BB3\u6BCC\u6C14\u723F\u4E31\u4E3C\u4EE8\u4EDC\u4EE9\u4EE1\u4EDD\u4EDA\u520C\u531C\u534C\u5722\u5723\u5917\u592F\u5B81\u5B84\u5C12\u5C3B\u5C74\u5C73\u5E04\u5E80\u5E82\u5FC9\u6209\u6250\u6C15"],["c9a1","\u6C36\u6C43\u6C3F\u6C3B\u72AE\u72B0\u738A\u79B8\u808A\u961E\u4F0E\u4F18\u4F2C\u4EF5\u4F14\u4EF1\u4F00\u4EF7\u4F08\u4F1D\u4F02\u4F05\u4F22\u4F13\u4F04\u4EF4\u4F12\u51B1\u5213\u5209\u5210\u52A6\u5322\u531F\u534D\u538A\u5407\u56E1\u56DF\u572E\u572A\u5734\u593C\u5980\u597C\u5985\u597B\u597E\u5977\u597F\u5B56\u5C15\u5C25\u5C7C\u5C7A\u5C7B\u5C7E\u5DDF\u5E75\u5E84\u5F02\u5F1A\u5F74\u5FD5\u5FD4\u5FCF\u625C\u625E\u6264\u6261\u6266\u6262\u6259\u6260\u625A\u6265\u65EF\u65EE\u673E\u6739\u6738\u673B\u673A\u673F\u673C\u6733\u6C18\u6C46\u6C52\u6C5C\u6C4F\u6C4A\u6C54\u6C4B"],["ca40","\u6C4C\u7071\u725E\u72B4\u72B5\u738E\u752A\u767F\u7A75\u7F51\u8278\u827C\u8280\u827D\u827F\u864D\u897E\u9099\u9097\u9098\u909B\u9094\u9622\u9624\u9620\u9623\u4F56\u4F3B\u4F62\u4F49\u4F53\u4F64\u4F3E\u4F67\u4F52\u4F5F\u4F41\u4F58\u4F2D\u4F33\u4F3F\u4F61\u518F\u51B9\u521C\u521E\u5221\u52AD\u52AE\u5309\u5363\u5372\u538E\u538F\u5430\u5437\u542A\u5454\u5445\u5419\u541C\u5425\u5418"],["caa1","\u543D\u544F\u5441\u5428\u5424\u5447\u56EE\u56E7\u56E5\u5741\u5745\u574C\u5749\u574B\u5752\u5906\u5940\u59A6\u5998\u59A0\u5997\u598E\u59A2\u5990\u598F\u59A7\u59A1\u5B8E\u5B92\u5C28\u5C2A\u5C8D\u5C8F\u5C88\u5C8B\u5C89\u5C92\u5C8A\u5C86\u5C93\u5C95\u5DE0\u5E0A\u5E0E\u5E8B\u5E89\u5E8C\u5E88\u5E8D\u5F05\u5F1D\u5F78\u5F76\u5FD2\u5FD1\u5FD0\u5FED\u5FE8\u5FEE\u5FF3\u5FE1\u5FE4\u5FE3\u5FFA\u5FEF\u5FF7\u5FFB\u6000\u5FF4\u623A\u6283\u628C\u628E\u628F\u6294\u6287\u6271\u627B\u627A\u6270\u6281\u6288\u6277\u627D\u6272\u6274\u6537\u65F0\u65F4\u65F3\u65F2\u65F5\u6745\u6747"],["cb40","\u6759\u6755\u674C\u6748\u675D\u674D\u675A\u674B\u6BD0\u6C19\u6C1A\u6C78\u6C67\u6C6B\u6C84\u6C8B\u6C8F\u6C71\u6C6F\u6C69\u6C9A\u6C6D\u6C87\u6C95\u6C9C\u6C66\u6C73\u6C65\u6C7B\u6C8E\u7074\u707A\u7263\u72BF\u72BD\u72C3\u72C6\u72C1\u72BA\u72C5\u7395\u7397\u7393\u7394\u7392\u753A\u7539\u7594\u7595\u7681\u793D\u8034\u8095\u8099\u8090\u8092\u809C\u8290\u828F\u8285\u828E\u8291\u8293"],["cba1","\u828A\u8283\u8284\u8C78\u8FC9\u8FBF\u909F\u90A1\u90A5\u909E\u90A7\u90A0\u9630\u9628\u962F\u962D\u4E33\u4F98\u4F7C\u4F85\u4F7D\u4F80\u4F87\u4F76\u4F74\u4F89\u4F84\u4F77\u4F4C\u4F97\u4F6A\u4F9A\u4F79\u4F81\u4F78\u4F90\u4F9C\u4F94\u4F9E\u4F92\u4F82\u4F95\u4F6B\u4F6E\u519E\u51BC\u51BE\u5235\u5232\u5233\u5246\u5231\u52BC\u530A\u530B\u533C\u5392\u5394\u5487\u547F\u5481\u5491\u5482\u5488\u546B\u547A\u547E\u5465\u546C\u5474\u5466\u548D\u546F\u5461\u5460\u5498\u5463\u5467\u5464\u56F7\u56F9\u576F\u5772\u576D\u576B\u5771\u5770\u5776\u5780\u5775\u577B\u5773\u5774\u5762"],["cc40","\u5768\u577D\u590C\u5945\u59B5\u59BA\u59CF\u59CE\u59B2\u59CC\u59C1\u59B6\u59BC\u59C3\u59D6\u59B1\u59BD\u59C0\u59C8\u59B4\u59C7\u5B62\u5B65\u5B93\u5B95\u5C44\u5C47\u5CAE\u5CA4\u5CA0\u5CB5\u5CAF\u5CA8\u5CAC\u5C9F\u5CA3\u5CAD\u5CA2\u5CAA\u5CA7\u5C9D\u5CA5\u5CB6\u5CB0\u5CA6\u5E17\u5E14\u5E19\u5F28\u5F22\u5F23\u5F24\u5F54\u5F82\u5F7E\u5F7D\u5FDE\u5FE5\u602D\u6026\u6019\u6032\u600B"],["cca1","\u6034\u600A\u6017\u6033\u601A\u601E\u602C\u6022\u600D\u6010\u602E\u6013\u6011\u600C\u6009\u601C\u6214\u623D\u62AD\u62B4\u62D1\u62BE\u62AA\u62B6\u62CA\u62AE\u62B3\u62AF\u62BB\u62A9\u62B0\u62B8\u653D\u65A8\u65BB\u6609\u65FC\u6604\u6612\u6608\u65FB\u6603\u660B\u660D\u6605\u65FD\u6611\u6610\u66F6\u670A\u6785\u676C\u678E\u6792\u6776\u677B\u6798\u6786\u6784\u6774\u678D\u678C\u677A\u679F\u6791\u6799\u6783\u677D\u6781\u6778\u6779\u6794\u6B25\u6B80\u6B7E\u6BDE\u6C1D\u6C93\u6CEC\u6CEB\u6CEE\u6CD9\u6CB6\u6CD4\u6CAD\u6CE7\u6CB7\u6CD0\u6CC2\u6CBA\u6CC3\u6CC6\u6CED\u6CF2"],["cd40","\u6CD2\u6CDD\u6CB4\u6C8A\u6C9D\u6C80\u6CDE\u6CC0\u6D30\u6CCD\u6CC7\u6CB0\u6CF9\u6CCF\u6CE9\u6CD1\u7094\u7098\u7085\u7093\u7086\u7084\u7091\u7096\u7082\u709A\u7083\u726A\u72D6\u72CB\u72D8\u72C9\u72DC\u72D2\u72D4\u72DA\u72CC\u72D1\u73A4\u73A1\u73AD\u73A6\u73A2\u73A0\u73AC\u739D\u74DD\u74E8\u753F\u7540\u753E\u758C\u7598\u76AF\u76F3\u76F1\u76F0\u76F5\u77F8\u77FC\u77F9\u77FB\u77FA"],["cda1","\u77F7\u7942\u793F\u79C5\u7A78\u7A7B\u7AFB\u7C75\u7CFD\u8035\u808F\u80AE\u80A3\u80B8\u80B5\u80AD\u8220\u82A0\u82C0\u82AB\u829A\u8298\u829B\u82B5\u82A7\u82AE\u82BC\u829E\u82BA\u82B4\u82A8\u82A1\u82A9\u82C2\u82A4\u82C3\u82B6\u82A2\u8670\u866F\u866D\u866E\u8C56\u8FD2\u8FCB\u8FD3\u8FCD\u8FD6\u8FD5\u8FD7\u90B2\u90B4\u90AF\u90B3\u90B0\u9639\u963D\u963C\u963A\u9643\u4FCD\u4FC5\u4FD3\u4FB2\u4FC9\u4FCB\u4FC1\u4FD4\u4FDC\u4FD9\u4FBB\u4FB3\u4FDB\u4FC7\u4FD6\u4FBA\u4FC0\u4FB9\u4FEC\u5244\u5249\u52C0\u52C2\u533D\u537C\u5397\u5396\u5399\u5398\u54BA\u54A1\u54AD\u54A5\u54CF"],["ce40","\u54C3\u830D\u54B7\u54AE\u54D6\u54B6\u54C5\u54C6\u54A0\u5470\u54BC\u54A2\u54BE\u5472\u54DE\u54B0\u57B5\u579E\u579F\u57A4\u578C\u5797\u579D\u579B\u5794\u5798\u578F\u5799\u57A5\u579A\u5795\u58F4\u590D\u5953\u59E1\u59DE\u59EE\u5A00\u59F1\u59DD\u59FA\u59FD\u59FC\u59F6\u59E4\u59F2\u59F7\u59DB\u59E9\u59F3\u59F5\u59E0\u59FE\u59F4\u59ED\u5BA8\u5C4C\u5CD0\u5CD8\u5CCC\u5CD7\u5CCB\u5CDB"],["cea1","\u5CDE\u5CDA\u5CC9\u5CC7\u5CCA\u5CD6\u5CD3\u5CD4\u5CCF\u5CC8\u5CC6\u5CCE\u5CDF\u5CF8\u5DF9\u5E21\u5E22\u5E23\u5E20\u5E24\u5EB0\u5EA4\u5EA2\u5E9B\u5EA3\u5EA5\u5F07\u5F2E\u5F56\u5F86\u6037\u6039\u6054\u6072\u605E\u6045\u6053\u6047\u6049\u605B\u604C\u6040\u6042\u605F\u6024\u6044\u6058\u6066\u606E\u6242\u6243\u62CF\u630D\u630B\u62F5\u630E\u6303\u62EB\u62F9\u630F\u630C\u62F8\u62F6\u6300\u6313\u6314\u62FA\u6315\u62FB\u62F0\u6541\u6543\u65AA\u65BF\u6636\u6621\u6632\u6635\u661C\u6626\u6622\u6633\u662B\u663A\u661D\u6634\u6639\u662E\u670F\u6710\u67C1\u67F2\u67C8\u67BA"],["cf40","\u67DC\u67BB\u67F8\u67D8\u67C0\u67B7\u67C5\u67EB\u67E4\u67DF\u67B5\u67CD\u67B3\u67F7\u67F6\u67EE\u67E3\u67C2\u67B9\u67CE\u67E7\u67F0\u67B2\u67FC\u67C6\u67ED\u67CC\u67AE\u67E6\u67DB\u67FA\u67C9\u67CA\u67C3\u67EA\u67CB\u6B28\u6B82\u6B84\u6BB6\u6BD6\u6BD8\u6BE0\u6C20\u6C21\u6D28\u6D34\u6D2D\u6D1F\u6D3C\u6D3F\u6D12\u6D0A\u6CDA\u6D33\u6D04\u6D19\u6D3A\u6D1A\u6D11\u6D00\u6D1D\u6D42"],["cfa1","\u6D01\u6D18\u6D37\u6D03\u6D0F\u6D40\u6D07\u6D20\u6D2C\u6D08\u6D22\u6D09\u6D10\u70B7\u709F\u70BE\u70B1\u70B0\u70A1\u70B4\u70B5\u70A9\u7241\u7249\u724A\u726C\u7270\u7273\u726E\u72CA\u72E4\u72E8\u72EB\u72DF\u72EA\u72E6\u72E3\u7385\u73CC\u73C2\u73C8\u73C5\u73B9\u73B6\u73B5\u73B4\u73EB\u73BF\u73C7\u73BE\u73C3\u73C6\u73B8\u73CB\u74EC\u74EE\u752E\u7547\u7548\u75A7\u75AA\u7679\u76C4\u7708\u7703\u7704\u7705\u770A\u76F7\u76FB\u76FA\u77E7\u77E8\u7806\u7811\u7812\u7805\u7810\u780F\u780E\u7809\u7803\u7813\u794A\u794C\u794B\u7945\u7944\u79D5\u79CD\u79CF\u79D6\u79CE\u7A80"],["d040","\u7A7E\u7AD1\u7B00\u7B01\u7C7A\u7C78\u7C79\u7C7F\u7C80\u7C81\u7D03\u7D08\u7D01\u7F58\u7F91\u7F8D\u7FBE\u8007\u800E\u800F\u8014\u8037\u80D8\u80C7\u80E0\u80D1\u80C8\u80C2\u80D0\u80C5\u80E3\u80D9\u80DC\u80CA\u80D5\u80C9\u80CF\u80D7\u80E6\u80CD\u81FF\u8221\u8294\u82D9\u82FE\u82F9\u8307\u82E8\u8300\u82D5\u833A\u82EB\u82D6\u82F4\u82EC\u82E1\u82F2\u82F5\u830C\u82FB\u82F6\u82F0\u82EA"],["d0a1","\u82E4\u82E0\u82FA\u82F3\u82ED\u8677\u8674\u867C\u8673\u8841\u884E\u8867\u886A\u8869\u89D3\u8A04\u8A07\u8D72\u8FE3\u8FE1\u8FEE\u8FE0\u90F1\u90BD\u90BF\u90D5\u90C5\u90BE\u90C7\u90CB\u90C8\u91D4\u91D3\u9654\u964F\u9651\u9653\u964A\u964E\u501E\u5005\u5007\u5013\u5022\u5030\u501B\u4FF5\u4FF4\u5033\u5037\u502C\u4FF6\u4FF7\u5017\u501C\u5020\u5027\u5035\u502F\u5031\u500E\u515A\u5194\u5193\u51CA\u51C4\u51C5\u51C8\u51CE\u5261\u525A\u5252\u525E\u525F\u5255\u5262\u52CD\u530E\u539E\u5526\u54E2\u5517\u5512\u54E7\u54F3\u54E4\u551A\u54FF\u5504\u5508\u54EB\u5511\u5505\u54F1"],["d140","\u550A\u54FB\u54F7\u54F8\u54E0\u550E\u5503\u550B\u5701\u5702\u57CC\u5832\u57D5\u57D2\u57BA\u57C6\u57BD\u57BC\u57B8\u57B6\u57BF\u57C7\u57D0\u57B9\u57C1\u590E\u594A\u5A19\u5A16\u5A2D\u5A2E\u5A15\u5A0F\u5A17\u5A0A\u5A1E\u5A33\u5B6C\u5BA7\u5BAD\u5BAC\u5C03\u5C56\u5C54\u5CEC\u5CFF\u5CEE\u5CF1\u5CF7\u5D00\u5CF9\u5E29\u5E28\u5EA8\u5EAE\u5EAA\u5EAC\u5F33\u5F30\u5F67\u605D\u605A\u6067"],["d1a1","\u6041\u60A2\u6088\u6080\u6092\u6081\u609D\u6083\u6095\u609B\u6097\u6087\u609C\u608E\u6219\u6246\u62F2\u6310\u6356\u632C\u6344\u6345\u6336\u6343\u63E4\u6339\u634B\u634A\u633C\u6329\u6341\u6334\u6358\u6354\u6359\u632D\u6347\u6333\u635A\u6351\u6338\u6357\u6340\u6348\u654A\u6546\u65C6\u65C3\u65C4\u65C2\u664A\u665F\u6647\u6651\u6712\u6713\u681F\u681A\u6849\u6832\u6833\u683B\u684B\u684F\u6816\u6831\u681C\u6835\u682B\u682D\u682F\u684E\u6844\u6834\u681D\u6812\u6814\u6826\u6828\u682E\u684D\u683A\u6825\u6820\u6B2C\u6B2F\u6B2D\u6B31\u6B34\u6B6D\u8082\u6B88\u6BE6\u6BE4"],["d240","\u6BE8\u6BE3\u6BE2\u6BE7\u6C25\u6D7A\u6D63\u6D64\u6D76\u6D0D\u6D61\u6D92\u6D58\u6D62\u6D6D\u6D6F\u6D91\u6D8D\u6DEF\u6D7F\u6D86\u6D5E\u6D67\u6D60\u6D97\u6D70\u6D7C\u6D5F\u6D82\u6D98\u6D2F\u6D68\u6D8B\u6D7E\u6D80\u6D84\u6D16\u6D83\u6D7B\u6D7D\u6D75\u6D90\u70DC\u70D3\u70D1\u70DD\u70CB\u7F39\u70E2\u70D7\u70D2\u70DE\u70E0\u70D4\u70CD\u70C5\u70C6\u70C7\u70DA\u70CE\u70E1\u7242\u7278"],["d2a1","\u7277\u7276\u7300\u72FA\u72F4\u72FE\u72F6\u72F3\u72FB\u7301\u73D3\u73D9\u73E5\u73D6\u73BC\u73E7\u73E3\u73E9\u73DC\u73D2\u73DB\u73D4\u73DD\u73DA\u73D7\u73D8\u73E8\u74DE\u74DF\u74F4\u74F5\u7521\u755B\u755F\u75B0\u75C1\u75BB\u75C4\u75C0\u75BF\u75B6\u75BA\u768A\u76C9\u771D\u771B\u7710\u7713\u7712\u7723\u7711\u7715\u7719\u771A\u7722\u7727\u7823\u782C\u7822\u7835\u782F\u7828\u782E\u782B\u7821\u7829\u7833\u782A\u7831\u7954\u795B\u794F\u795C\u7953\u7952\u7951\u79EB\u79EC\u79E0\u79EE\u79ED\u79EA\u79DC\u79DE\u79DD\u7A86\u7A89\u7A85\u7A8B\u7A8C\u7A8A\u7A87\u7AD8\u7B10"],["d340","\u7B04\u7B13\u7B05\u7B0F\u7B08\u7B0A\u7B0E\u7B09\u7B12\u7C84\u7C91\u7C8A\u7C8C\u7C88\u7C8D\u7C85\u7D1E\u7D1D\u7D11\u7D0E\u7D18\u7D16\u7D13\u7D1F\u7D12\u7D0F\u7D0C\u7F5C\u7F61\u7F5E\u7F60\u7F5D\u7F5B\u7F96\u7F92\u7FC3\u7FC2\u7FC0\u8016\u803E\u8039\u80FA\u80F2\u80F9\u80F5\u8101\u80FB\u8100\u8201\u822F\u8225\u8333\u832D\u8344\u8319\u8351\u8325\u8356\u833F\u8341\u8326\u831C\u8322"],["d3a1","\u8342\u834E\u831B\u832A\u8308\u833C\u834D\u8316\u8324\u8320\u8337\u832F\u8329\u8347\u8345\u834C\u8353\u831E\u832C\u834B\u8327\u8348\u8653\u8652\u86A2\u86A8\u8696\u868D\u8691\u869E\u8687\u8697\u8686\u868B\u869A\u8685\u86A5\u8699\u86A1\u86A7\u8695\u8698\u868E\u869D\u8690\u8694\u8843\u8844\u886D\u8875\u8876\u8872\u8880\u8871\u887F\u886F\u8883\u887E\u8874\u887C\u8A12\u8C47\u8C57\u8C7B\u8CA4\u8CA3\u8D76\u8D78\u8DB5\u8DB7\u8DB6\u8ED1\u8ED3\u8FFE\u8FF5\u9002\u8FFF\u8FFB\u9004\u8FFC\u8FF6\u90D6\u90E0\u90D9\u90DA\u90E3\u90DF\u90E5\u90D8\u90DB\u90D7\u90DC\u90E4\u9150"],["d440","\u914E\u914F\u91D5\u91E2\u91DA\u965C\u965F\u96BC\u98E3\u9ADF\u9B2F\u4E7F\u5070\u506A\u5061\u505E\u5060\u5053\u504B\u505D\u5072\u5048\u504D\u5041\u505B\u504A\u5062\u5015\u5045\u505F\u5069\u506B\u5063\u5064\u5046\u5040\u506E\u5073\u5057\u5051\u51D0\u526B\u526D\u526C\u526E\u52D6\u52D3\u532D\u539C\u5575\u5576\u553C\u554D\u5550\u5534\u552A\u5551\u5562\u5536\u5535\u5530\u5552\u5545"],["d4a1","\u550C\u5532\u5565\u554E\u5539\u5548\u552D\u553B\u5540\u554B\u570A\u5707\u57FB\u5814\u57E2\u57F6\u57DC\u57F4\u5800\u57ED\u57FD\u5808\u57F8\u580B\u57F3\u57CF\u5807\u57EE\u57E3\u57F2\u57E5\u57EC\u57E1\u580E\u57FC\u5810\u57E7\u5801\u580C\u57F1\u57E9\u57F0\u580D\u5804\u595C\u5A60\u5A58\u5A55\u5A67\u5A5E\u5A38\u5A35\u5A6D\u5A50\u5A5F\u5A65\u5A6C\u5A53\u5A64\u5A57\u5A43\u5A5D\u5A52\u5A44\u5A5B\u5A48\u5A8E\u5A3E\u5A4D\u5A39\u5A4C\u5A70\u5A69\u5A47\u5A51\u5A56\u5A42\u5A5C\u5B72\u5B6E\u5BC1\u5BC0\u5C59\u5D1E\u5D0B\u5D1D\u5D1A\u5D20\u5D0C\u5D28\u5D0D\u5D26\u5D25\u5D0F"],["d540","\u5D30\u5D12\u5D23\u5D1F\u5D2E\u5E3E\u5E34\u5EB1\u5EB4\u5EB9\u5EB2\u5EB3\u5F36\u5F38\u5F9B\u5F96\u5F9F\u608A\u6090\u6086\u60BE\u60B0\u60BA\u60D3\u60D4\u60CF\u60E4\u60D9\u60DD\u60C8\u60B1\u60DB\u60B7\u60CA\u60BF\u60C3\u60CD\u60C0\u6332\u6365\u638A\u6382\u637D\u63BD\u639E\u63AD\u639D\u6397\u63AB\u638E\u636F\u6387\u6390\u636E\u63AF\u6375\u639C\u636D\u63AE\u637C\u63A4\u633B\u639F"],["d5a1","\u6378\u6385\u6381\u6391\u638D\u6370\u6553\u65CD\u6665\u6661\u665B\u6659\u665C\u6662\u6718\u6879\u6887\u6890\u689C\u686D\u686E\u68AE\u68AB\u6956\u686F\u68A3\u68AC\u68A9\u6875\u6874\u68B2\u688F\u6877\u6892\u687C\u686B\u6872\u68AA\u6880\u6871\u687E\u689B\u6896\u688B\u68A0\u6889\u68A4\u6878\u687B\u6891\u688C\u688A\u687D\u6B36\u6B33\u6B37\u6B38\u6B91\u6B8F\u6B8D\u6B8E\u6B8C\u6C2A\u6DC0\u6DAB\u6DB4\u6DB3\u6E74\u6DAC\u6DE9\u6DE2\u6DB7\u6DF6\u6DD4\u6E00\u6DC8\u6DE0\u6DDF\u6DD6\u6DBE\u6DE5\u6DDC\u6DDD\u6DDB\u6DF4\u6DCA\u6DBD\u6DED\u6DF0\u6DBA\u6DD5\u6DC2\u6DCF\u6DC9"],["d640","\u6DD0\u6DF2\u6DD3\u6DFD\u6DD7\u6DCD\u6DE3\u6DBB\u70FA\u710D\u70F7\u7117\u70F4\u710C\u70F0\u7104\u70F3\u7110\u70FC\u70FF\u7106\u7113\u7100\u70F8\u70F6\u710B\u7102\u710E\u727E\u727B\u727C\u727F\u731D\u7317\u7307\u7311\u7318\u730A\u7308\u72FF\u730F\u731E\u7388\u73F6\u73F8\u73F5\u7404\u7401\u73FD\u7407\u7400\u73FA\u73FC\u73FF\u740C\u740B\u73F4\u7408\u7564\u7563\u75CE\u75D2\u75CF"],["d6a1","\u75CB\u75CC\u75D1\u75D0\u768F\u7689\u76D3\u7739\u772F\u772D\u7731\u7732\u7734\u7733\u773D\u7725\u773B\u7735\u7848\u7852\u7849\u784D\u784A\u784C\u7826\u7845\u7850\u7964\u7967\u7969\u796A\u7963\u796B\u7961\u79BB\u79FA\u79F8\u79F6\u79F7\u7A8F\u7A94\u7A90\u7B35\u7B47\u7B34\u7B25\u7B30\u7B22\u7B24\u7B33\u7B18\u7B2A\u7B1D\u7B31\u7B2B\u7B2D\u7B2F\u7B32\u7B38\u7B1A\u7B23\u7C94\u7C98\u7C96\u7CA3\u7D35\u7D3D\u7D38\u7D36\u7D3A\u7D45\u7D2C\u7D29\u7D41\u7D47\u7D3E\u7D3F\u7D4A\u7D3B\u7D28\u7F63\u7F95\u7F9C\u7F9D\u7F9B\u7FCA\u7FCB\u7FCD\u7FD0\u7FD1\u7FC7\u7FCF\u7FC9\u801F"],["d740","\u801E\u801B\u8047\u8043\u8048\u8118\u8125\u8119\u811B\u812D\u811F\u812C\u811E\u8121\u8115\u8127\u811D\u8122\u8211\u8238\u8233\u823A\u8234\u8232\u8274\u8390\u83A3\u83A8\u838D\u837A\u8373\u83A4\u8374\u838F\u8381\u8395\u8399\u8375\u8394\u83A9\u837D\u8383\u838C\u839D\u839B\u83AA\u838B\u837E\u83A5\u83AF\u8388\u8397\u83B0\u837F\u83A6\u8387\u83AE\u8376\u839A\u8659\u8656\u86BF\u86B7"],["d7a1","\u86C2\u86C1\u86C5\u86BA\u86B0\u86C8\u86B9\u86B3\u86B8\u86CC\u86B4\u86BB\u86BC\u86C3\u86BD\u86BE\u8852\u8889\u8895\u88A8\u88A2\u88AA\u889A\u8891\u88A1\u889F\u8898\u88A7\u8899\u889B\u8897\u88A4\u88AC\u888C\u8893\u888E\u8982\u89D6\u89D9\u89D5\u8A30\u8A27\u8A2C\u8A1E\u8C39\u8C3B\u8C5C\u8C5D\u8C7D\u8CA5\u8D7D\u8D7B\u8D79\u8DBC\u8DC2\u8DB9\u8DBF\u8DC1\u8ED8\u8EDE\u8EDD\u8EDC\u8ED7\u8EE0\u8EE1\u9024\u900B\u9011\u901C\u900C\u9021\u90EF\u90EA\u90F0\u90F4\u90F2\u90F3\u90D4\u90EB\u90EC\u90E9\u9156\u9158\u915A\u9153\u9155\u91EC\u91F4\u91F1\u91F3\u91F8\u91E4\u91F9\u91EA"],["d840","\u91EB\u91F7\u91E8\u91EE\u957A\u9586\u9588\u967C\u966D\u966B\u9671\u966F\u96BF\u976A\u9804\u98E5\u9997\u509B\u5095\u5094\u509E\u508B\u50A3\u5083\u508C\u508E\u509D\u5068\u509C\u5092\u5082\u5087\u515F\u51D4\u5312\u5311\u53A4\u53A7\u5591\u55A8\u55A5\u55AD\u5577\u5645\u55A2\u5593\u5588\u558F\u55B5\u5581\u55A3\u5592\u55A4\u557D\u558C\u55A6\u557F\u5595\u55A1\u558E\u570C\u5829\u5837"],["d8a1","\u5819\u581E\u5827\u5823\u5828\u57F5\u5848\u5825\u581C\u581B\u5833\u583F\u5836\u582E\u5839\u5838\u582D\u582C\u583B\u5961\u5AAF\u5A94\u5A9F\u5A7A\u5AA2\u5A9E\u5A78\u5AA6\u5A7C\u5AA5\u5AAC\u5A95\u5AAE\u5A37\u5A84\u5A8A\u5A97\u5A83\u5A8B\u5AA9\u5A7B\u5A7D\u5A8C\u5A9C\u5A8F\u5A93\u5A9D\u5BEA\u5BCD\u5BCB\u5BD4\u5BD1\u5BCA\u5BCE\u5C0C\u5C30\u5D37\u5D43\u5D6B\u5D41\u5D4B\u5D3F\u5D35\u5D51\u5D4E\u5D55\u5D33\u5D3A\u5D52\u5D3D\u5D31\u5D59\u5D42\u5D39\u5D49\u5D38\u5D3C\u5D32\u5D36\u5D40\u5D45\u5E44\u5E41\u5F58\u5FA6\u5FA5\u5FAB\u60C9\u60B9\u60CC\u60E2\u60CE\u60C4\u6114"],["d940","\u60F2\u610A\u6116\u6105\u60F5\u6113\u60F8\u60FC\u60FE\u60C1\u6103\u6118\u611D\u6110\u60FF\u6104\u610B\u624A\u6394\u63B1\u63B0\u63CE\u63E5\u63E8\u63EF\u63C3\u649D\u63F3\u63CA\u63E0\u63F6\u63D5\u63F2\u63F5\u6461\u63DF\u63BE\u63DD\u63DC\u63C4\u63D8\u63D3\u63C2\u63C7\u63CC\u63CB\u63C8\u63F0\u63D7\u63D9\u6532\u6567\u656A\u6564\u655C\u6568\u6565\u658C\u659D\u659E\u65AE\u65D0\u65D2"],["d9a1","\u667C\u666C\u667B\u6680\u6671\u6679\u666A\u6672\u6701\u690C\u68D3\u6904\u68DC\u692A\u68EC\u68EA\u68F1\u690F\u68D6\u68F7\u68EB\u68E4\u68F6\u6913\u6910\u68F3\u68E1\u6907\u68CC\u6908\u6970\u68B4\u6911\u68EF\u68C6\u6914\u68F8\u68D0\u68FD\u68FC\u68E8\u690B\u690A\u6917\u68CE\u68C8\u68DD\u68DE\u68E6\u68F4\u68D1\u6906\u68D4\u68E9\u6915\u6925\u68C7\u6B39\u6B3B\u6B3F\u6B3C\u6B94\u6B97\u6B99\u6B95\u6BBD\u6BF0\u6BF2\u6BF3\u6C30\u6DFC\u6E46\u6E47\u6E1F\u6E49\u6E88\u6E3C\u6E3D\u6E45\u6E62\u6E2B\u6E3F\u6E41\u6E5D\u6E73\u6E1C\u6E33\u6E4B\u6E40\u6E51\u6E3B\u6E03\u6E2E\u6E5E"],["da40","\u6E68\u6E5C\u6E61\u6E31\u6E28\u6E60\u6E71\u6E6B\u6E39\u6E22\u6E30\u6E53\u6E65\u6E27\u6E78\u6E64\u6E77\u6E55\u6E79\u6E52\u6E66\u6E35\u6E36\u6E5A\u7120\u711E\u712F\u70FB\u712E\u7131\u7123\u7125\u7122\u7132\u711F\u7128\u713A\u711B\u724B\u725A\u7288\u7289\u7286\u7285\u728B\u7312\u730B\u7330\u7322\u7331\u7333\u7327\u7332\u732D\u7326\u7323\u7335\u730C\u742E\u742C\u7430\u742B\u7416"],["daa1","\u741A\u7421\u742D\u7431\u7424\u7423\u741D\u7429\u7420\u7432\u74FB\u752F\u756F\u756C\u75E7\u75DA\u75E1\u75E6\u75DD\u75DF\u75E4\u75D7\u7695\u7692\u76DA\u7746\u7747\u7744\u774D\u7745\u774A\u774E\u774B\u774C\u77DE\u77EC\u7860\u7864\u7865\u785C\u786D\u7871\u786A\u786E\u7870\u7869\u7868\u785E\u7862\u7974\u7973\u7972\u7970\u7A02\u7A0A\u7A03\u7A0C\u7A04\u7A99\u7AE6\u7AE4\u7B4A\u7B3B\u7B44\u7B48\u7B4C\u7B4E\u7B40\u7B58\u7B45\u7CA2\u7C9E\u7CA8\u7CA1\u7D58\u7D6F\u7D63\u7D53\u7D56\u7D67\u7D6A\u7D4F\u7D6D\u7D5C\u7D6B\u7D52\u7D54\u7D69\u7D51\u7D5F\u7D4E\u7F3E\u7F3F\u7F65"],["db40","\u7F66\u7FA2\u7FA0\u7FA1\u7FD7\u8051\u804F\u8050\u80FE\u80D4\u8143\u814A\u8152\u814F\u8147\u813D\u814D\u813A\u81E6\u81EE\u81F7\u81F8\u81F9\u8204\u823C\u823D\u823F\u8275\u833B\u83CF\u83F9\u8423\u83C0\u83E8\u8412\u83E7\u83E4\u83FC\u83F6\u8410\u83C6\u83C8\u83EB\u83E3\u83BF\u8401\u83DD\u83E5\u83D8\u83FF\u83E1\u83CB\u83CE\u83D6\u83F5\u83C9\u8409\u840F\u83DE\u8411\u8406\u83C2\u83F3"],["dba1","\u83D5\u83FA\u83C7\u83D1\u83EA\u8413\u83C3\u83EC\u83EE\u83C4\u83FB\u83D7\u83E2\u841B\u83DB\u83FE\u86D8\u86E2\u86E6\u86D3\u86E3\u86DA\u86EA\u86DD\u86EB\u86DC\u86EC\u86E9\u86D7\u86E8\u86D1\u8848\u8856\u8855\u88BA\u88D7\u88B9\u88B8\u88C0\u88BE\u88B6\u88BC\u88B7\u88BD\u88B2\u8901\u88C9\u8995\u8998\u8997\u89DD\u89DA\u89DB\u8A4E\u8A4D\u8A39\u8A59\u8A40\u8A57\u8A58\u8A44\u8A45\u8A52\u8A48\u8A51\u8A4A\u8A4C\u8A4F\u8C5F\u8C81\u8C80\u8CBA\u8CBE\u8CB0\u8CB9\u8CB5\u8D84\u8D80\u8D89\u8DD8\u8DD3\u8DCD\u8DC7\u8DD6\u8DDC\u8DCF\u8DD5\u8DD9\u8DC8\u8DD7\u8DC5\u8EEF\u8EF7\u8EFA"],["dc40","\u8EF9\u8EE6\u8EEE\u8EE5\u8EF5\u8EE7\u8EE8\u8EF6\u8EEB\u8EF1\u8EEC\u8EF4\u8EE9\u902D\u9034\u902F\u9106\u912C\u9104\u90FF\u90FC\u9108\u90F9\u90FB\u9101\u9100\u9107\u9105\u9103\u9161\u9164\u915F\u9162\u9160\u9201\u920A\u9225\u9203\u921A\u9226\u920F\u920C\u9200\u9212\u91FF\u91FD\u9206\u9204\u9227\u9202\u921C\u9224\u9219\u9217\u9205\u9216\u957B\u958D\u958C\u9590\u9687\u967E\u9688"],["dca1","\u9689\u9683\u9680\u96C2\u96C8\u96C3\u96F1\u96F0\u976C\u9770\u976E\u9807\u98A9\u98EB\u9CE6\u9EF9\u4E83\u4E84\u4EB6\u50BD\u50BF\u50C6\u50AE\u50C4\u50CA\u50B4\u50C8\u50C2\u50B0\u50C1\u50BA\u50B1\u50CB\u50C9\u50B6\u50B8\u51D7\u527A\u5278\u527B\u527C\u55C3\u55DB\u55CC\u55D0\u55CB\u55CA\u55DD\u55C0\u55D4\u55C4\u55E9\u55BF\u55D2\u558D\u55CF\u55D5\u55E2\u55D6\u55C8\u55F2\u55CD\u55D9\u55C2\u5714\u5853\u5868\u5864\u584F\u584D\u5849\u586F\u5855\u584E\u585D\u5859\u5865\u585B\u583D\u5863\u5871\u58FC\u5AC7\u5AC4\u5ACB\u5ABA\u5AB8\u5AB1\u5AB5\u5AB0\u5ABF\u5AC8\u5ABB\u5AC6"],["dd40","\u5AB7\u5AC0\u5ACA\u5AB4\u5AB6\u5ACD\u5AB9\u5A90\u5BD6\u5BD8\u5BD9\u5C1F\u5C33\u5D71\u5D63\u5D4A\u5D65\u5D72\u5D6C\u5D5E\u5D68\u5D67\u5D62\u5DF0\u5E4F\u5E4E\u5E4A\u5E4D\u5E4B\u5EC5\u5ECC\u5EC6\u5ECB\u5EC7\u5F40\u5FAF\u5FAD\u60F7\u6149\u614A\u612B\u6145\u6136\u6132\u612E\u6146\u612F\u614F\u6129\u6140\u6220\u9168\u6223\u6225\u6224\u63C5\u63F1\u63EB\u6410\u6412\u6409\u6420\u6424"],["dda1","\u6433\u6443\u641F\u6415\u6418\u6439\u6437\u6422\u6423\u640C\u6426\u6430\u6428\u6441\u6435\u642F\u640A\u641A\u6440\u6425\u6427\u640B\u63E7\u641B\u642E\u6421\u640E\u656F\u6592\u65D3\u6686\u668C\u6695\u6690\u668B\u668A\u6699\u6694\u6678\u6720\u6966\u695F\u6938\u694E\u6962\u6971\u693F\u6945\u696A\u6939\u6942\u6957\u6959\u697A\u6948\u6949\u6935\u696C\u6933\u693D\u6965\u68F0\u6978\u6934\u6969\u6940\u696F\u6944\u6976\u6958\u6941\u6974\u694C\u693B\u694B\u6937\u695C\u694F\u6951\u6932\u6952\u692F\u697B\u693C\u6B46\u6B45\u6B43\u6B42\u6B48\u6B41\u6B9B\uFA0D\u6BFB\u6BFC"],["de40","\u6BF9\u6BF7\u6BF8\u6E9B\u6ED6\u6EC8\u6E8F\u6EC0\u6E9F\u6E93\u6E94\u6EA0\u6EB1\u6EB9\u6EC6\u6ED2\u6EBD\u6EC1\u6E9E\u6EC9\u6EB7\u6EB0\u6ECD\u6EA6\u6ECF\u6EB2\u6EBE\u6EC3\u6EDC\u6ED8\u6E99\u6E92\u6E8E\u6E8D\u6EA4\u6EA1\u6EBF\u6EB3\u6ED0\u6ECA\u6E97\u6EAE\u6EA3\u7147\u7154\u7152\u7163\u7160\u7141\u715D\u7162\u7172\u7178\u716A\u7161\u7142\u7158\u7143\u714B\u7170\u715F\u7150\u7153"],["dea1","\u7144\u714D\u715A\u724F\u728D\u728C\u7291\u7290\u728E\u733C\u7342\u733B\u733A\u7340\u734A\u7349\u7444\u744A\u744B\u7452\u7451\u7457\u7440\u744F\u7450\u744E\u7442\u7446\u744D\u7454\u74E1\u74FF\u74FE\u74FD\u751D\u7579\u7577\u6983\u75EF\u760F\u7603\u75F7\u75FE\u75FC\u75F9\u75F8\u7610\u75FB\u75F6\u75ED\u75F5\u75FD\u7699\u76B5\u76DD\u7755\u775F\u7760\u7752\u7756\u775A\u7769\u7767\u7754\u7759\u776D\u77E0\u7887\u789A\u7894\u788F\u7884\u7895\u7885\u7886\u78A1\u7883\u7879\u7899\u7880\u7896\u787B\u797C\u7982\u797D\u7979\u7A11\u7A18\u7A19\u7A12\u7A17\u7A15\u7A22\u7A13"],["df40","\u7A1B\u7A10\u7AA3\u7AA2\u7A9E\u7AEB\u7B66\u7B64\u7B6D\u7B74\u7B69\u7B72\u7B65\u7B73\u7B71\u7B70\u7B61\u7B78\u7B76\u7B63\u7CB2\u7CB4\u7CAF\u7D88\u7D86\u7D80\u7D8D\u7D7F\u7D85\u7D7A\u7D8E\u7D7B\u7D83\u7D7C\u7D8C\u7D94\u7D84\u7D7D\u7D92\u7F6D\u7F6B\u7F67\u7F68\u7F6C\u7FA6\u7FA5\u7FA7\u7FDB\u7FDC\u8021\u8164\u8160\u8177\u815C\u8169\u815B\u8162\u8172\u6721\u815E\u8176\u8167\u816F"],["dfa1","\u8144\u8161\u821D\u8249\u8244\u8240\u8242\u8245\u84F1\u843F\u8456\u8476\u8479\u848F\u848D\u8465\u8451\u8440\u8486\u8467\u8430\u844D\u847D\u845A\u8459\u8474\u8473\u845D\u8507\u845E\u8437\u843A\u8434\u847A\u8443\u8478\u8432\u8445\u8429\u83D9\u844B\u842F\u8442\u842D\u845F\u8470\u8439\u844E\u844C\u8452\u846F\u84C5\u848E\u843B\u8447\u8436\u8433\u8468\u847E\u8444\u842B\u8460\u8454\u846E\u8450\u870B\u8704\u86F7\u870C\u86FA\u86D6\u86F5\u874D\u86F8\u870E\u8709\u8701\u86F6\u870D\u8705\u88D6\u88CB\u88CD\u88CE\u88DE\u88DB\u88DA\u88CC\u88D0\u8985\u899B\u89DF\u89E5\u89E4"],["e040","\u89E1\u89E0\u89E2\u89DC\u89E6\u8A76\u8A86\u8A7F\u8A61\u8A3F\u8A77\u8A82\u8A84\u8A75\u8A83\u8A81\u8A74\u8A7A\u8C3C\u8C4B\u8C4A\u8C65\u8C64\u8C66\u8C86\u8C84\u8C85\u8CCC\u8D68\u8D69\u8D91\u8D8C\u8D8E\u8D8F\u8D8D\u8D93\u8D94\u8D90\u8D92\u8DF0\u8DE0\u8DEC\u8DF1\u8DEE\u8DD0\u8DE9\u8DE3\u8DE2\u8DE7\u8DF2\u8DEB\u8DF4\u8F06\u8EFF\u8F01\u8F00\u8F05\u8F07\u8F08\u8F02\u8F0B\u9052\u903F"],["e0a1","\u9044\u9049\u903D\u9110\u910D\u910F\u9111\u9116\u9114\u910B\u910E\u916E\u916F\u9248\u9252\u9230\u923A\u9266\u9233\u9265\u925E\u9283\u922E\u924A\u9246\u926D\u926C\u924F\u9260\u9267\u926F\u9236\u9261\u9270\u9231\u9254\u9263\u9250\u9272\u924E\u9253\u924C\u9256\u9232\u959F\u959C\u959E\u959B\u9692\u9693\u9691\u9697\u96CE\u96FA\u96FD\u96F8\u96F5\u9773\u9777\u9778\u9772\u980F\u980D\u980E\u98AC\u98F6\u98F9\u99AF\u99B2\u99B0\u99B5\u9AAD\u9AAB\u9B5B\u9CEA\u9CED\u9CE7\u9E80\u9EFD\u50E6\u50D4\u50D7\u50E8\u50F3\u50DB\u50EA\u50DD\u50E4\u50D3\u50EC\u50F0\u50EF\u50E3\u50E0"],["e140","\u51D8\u5280\u5281\u52E9\u52EB\u5330\u53AC\u5627\u5615\u560C\u5612\u55FC\u560F\u561C\u5601\u5613\u5602\u55FA\u561D\u5604\u55FF\u55F9\u5889\u587C\u5890\u5898\u5886\u5881\u587F\u5874\u588B\u587A\u5887\u5891\u588E\u5876\u5882\u5888\u587B\u5894\u588F\u58FE\u596B\u5ADC\u5AEE\u5AE5\u5AD5\u5AEA\u5ADA\u5AED\u5AEB\u5AF3\u5AE2\u5AE0\u5ADB\u5AEC\u5ADE\u5ADD\u5AD9\u5AE8\u5ADF\u5B77\u5BE0"],["e1a1","\u5BE3\u5C63\u5D82\u5D80\u5D7D\u5D86\u5D7A\u5D81\u5D77\u5D8A\u5D89\u5D88\u5D7E\u5D7C\u5D8D\u5D79\u5D7F\u5E58\u5E59\u5E53\u5ED8\u5ED1\u5ED7\u5ECE\u5EDC\u5ED5\u5ED9\u5ED2\u5ED4\u5F44\u5F43\u5F6F\u5FB6\u612C\u6128\u6141\u615E\u6171\u6173\u6152\u6153\u6172\u616C\u6180\u6174\u6154\u617A\u615B\u6165\u613B\u616A\u6161\u6156\u6229\u6227\u622B\u642B\u644D\u645B\u645D\u6474\u6476\u6472\u6473\u647D\u6475\u6466\u64A6\u644E\u6482\u645E\u645C\u644B\u6453\u6460\u6450\u647F\u643F\u646C\u646B\u6459\u6465\u6477\u6573\u65A0\u66A1\u66A0\u669F\u6705\u6704\u6722\u69B1\u69B6\u69C9"],["e240","\u69A0\u69CE\u6996\u69B0\u69AC\u69BC\u6991\u6999\u698E\u69A7\u698D\u69A9\u69BE\u69AF\u69BF\u69C4\u69BD\u69A4\u69D4\u69B9\u69CA\u699A\u69CF\u69B3\u6993\u69AA\u69A1\u699E\u69D9\u6997\u6990\u69C2\u69B5\u69A5\u69C6\u6B4A\u6B4D\u6B4B\u6B9E\u6B9F\u6BA0\u6BC3\u6BC4\u6BFE\u6ECE\u6EF5\u6EF1\u6F03\u6F25\u6EF8\u6F37\u6EFB\u6F2E\u6F09\u6F4E\u6F19\u6F1A\u6F27\u6F18\u6F3B\u6F12\u6EED\u6F0A"],["e2a1","\u6F36\u6F73\u6EF9\u6EEE\u6F2D\u6F40\u6F30\u6F3C\u6F35\u6EEB\u6F07\u6F0E\u6F43\u6F05\u6EFD\u6EF6\u6F39\u6F1C\u6EFC\u6F3A\u6F1F\u6F0D\u6F1E\u6F08\u6F21\u7187\u7190\u7189\u7180\u7185\u7182\u718F\u717B\u7186\u7181\u7197\u7244\u7253\u7297\u7295\u7293\u7343\u734D\u7351\u734C\u7462\u7473\u7471\u7475\u7472\u7467\u746E\u7500\u7502\u7503\u757D\u7590\u7616\u7608\u760C\u7615\u7611\u760A\u7614\u76B8\u7781\u777C\u7785\u7782\u776E\u7780\u776F\u777E\u7783\u78B2\u78AA\u78B4\u78AD\u78A8\u787E\u78AB\u789E\u78A5\u78A0\u78AC\u78A2\u78A4\u7998\u798A\u798B\u7996\u7995\u7994\u7993"],["e340","\u7997\u7988\u7992\u7990\u7A2B\u7A4A\u7A30\u7A2F\u7A28\u7A26\u7AA8\u7AAB\u7AAC\u7AEE\u7B88\u7B9C\u7B8A\u7B91\u7B90\u7B96\u7B8D\u7B8C\u7B9B\u7B8E\u7B85\u7B98\u5284\u7B99\u7BA4\u7B82\u7CBB\u7CBF\u7CBC\u7CBA\u7DA7\u7DB7\u7DC2\u7DA3\u7DAA\u7DC1\u7DC0\u7DC5\u7D9D\u7DCE\u7DC4\u7DC6\u7DCB\u7DCC\u7DAF\u7DB9\u7D96\u7DBC\u7D9F\u7DA6\u7DAE\u7DA9\u7DA1\u7DC9\u7F73\u7FE2\u7FE3\u7FE5\u7FDE"],["e3a1","\u8024\u805D\u805C\u8189\u8186\u8183\u8187\u818D\u818C\u818B\u8215\u8497\u84A4\u84A1\u849F\u84BA\u84CE\u84C2\u84AC\u84AE\u84AB\u84B9\u84B4\u84C1\u84CD\u84AA\u849A\u84B1\u84D0\u849D\u84A7\u84BB\u84A2\u8494\u84C7\u84CC\u849B\u84A9\u84AF\u84A8\u84D6\u8498\u84B6\u84CF\u84A0\u84D7\u84D4\u84D2\u84DB\u84B0\u8491\u8661\u8733\u8723\u8728\u876B\u8740\u872E\u871E\u8721\u8719\u871B\u8743\u872C\u8741\u873E\u8746\u8720\u8732\u872A\u872D\u873C\u8712\u873A\u8731\u8735\u8742\u8726\u8727\u8738\u8724\u871A\u8730\u8711\u88F7\u88E7\u88F1\u88F2\u88FA\u88FE\u88EE\u88FC\u88F6\u88FB"],["e440","\u88F0\u88EC\u88EB\u899D\u89A1\u899F\u899E\u89E9\u89EB\u89E8\u8AAB\u8A99\u8A8B\u8A92\u8A8F\u8A96\u8C3D\u8C68\u8C69\u8CD5\u8CCF\u8CD7\u8D96\u8E09\u8E02\u8DFF\u8E0D\u8DFD\u8E0A\u8E03\u8E07\u8E06\u8E05\u8DFE\u8E00\u8E04\u8F10\u8F11\u8F0E\u8F0D\u9123\u911C\u9120\u9122\u911F\u911D\u911A\u9124\u9121\u911B\u917A\u9172\u9179\u9173\u92A5\u92A4\u9276\u929B\u927A\u92A0\u9294\u92AA\u928D"],["e4a1","\u92A6\u929A\u92AB\u9279\u9297\u927F\u92A3\u92EE\u928E\u9282\u9295\u92A2\u927D\u9288\u92A1\u928A\u9286\u928C\u9299\u92A7\u927E\u9287\u92A9\u929D\u928B\u922D\u969E\u96A1\u96FF\u9758\u977D\u977A\u977E\u9783\u9780\u9782\u977B\u9784\u9781\u977F\u97CE\u97CD\u9816\u98AD\u98AE\u9902\u9900\u9907\u999D\u999C\u99C3\u99B9\u99BB\u99BA\u99C2\u99BD\u99C7\u9AB1\u9AE3\u9AE7\u9B3E\u9B3F\u9B60\u9B61\u9B5F\u9CF1\u9CF2\u9CF5\u9EA7\u50FF\u5103\u5130\u50F8\u5106\u5107\u50F6\u50FE\u510B\u510C\u50FD\u510A\u528B\u528C\u52F1\u52EF\u5648\u5642\u564C\u5635\u5641\u564A\u5649\u5646\u5658"],["e540","\u565A\u5640\u5633\u563D\u562C\u563E\u5638\u562A\u563A\u571A\u58AB\u589D\u58B1\u58A0\u58A3\u58AF\u58AC\u58A5\u58A1\u58FF\u5AFF\u5AF4\u5AFD\u5AF7\u5AF6\u5B03\u5AF8\u5B02\u5AF9\u5B01\u5B07\u5B05\u5B0F\u5C67\u5D99\u5D97\u5D9F\u5D92\u5DA2\u5D93\u5D95\u5DA0\u5D9C\u5DA1\u5D9A\u5D9E\u5E69\u5E5D\u5E60\u5E5C\u7DF3\u5EDB\u5EDE\u5EE1\u5F49\u5FB2\u618B\u6183\u6179\u61B1\u61B0\u61A2\u6189"],["e5a1","\u619B\u6193\u61AF\u61AD\u619F\u6192\u61AA\u61A1\u618D\u6166\u61B3\u622D\u646E\u6470\u6496\u64A0\u6485\u6497\u649C\u648F\u648B\u648A\u648C\u64A3\u649F\u6468\u64B1\u6498\u6576\u657A\u6579\u657B\u65B2\u65B3\u66B5\u66B0\u66A9\u66B2\u66B7\u66AA\u66AF\u6A00\u6A06\u6A17\u69E5\u69F8\u6A15\u69F1\u69E4\u6A20\u69FF\u69EC\u69E2\u6A1B\u6A1D\u69FE\u6A27\u69F2\u69EE\u6A14\u69F7\u69E7\u6A40\u6A08\u69E6\u69FB\u6A0D\u69FC\u69EB\u6A09\u6A04\u6A18\u6A25\u6A0F\u69F6\u6A26\u6A07\u69F4\u6A16\u6B51\u6BA5\u6BA3\u6BA2\u6BA6\u6C01\u6C00\u6BFF\u6C02\u6F41\u6F26\u6F7E\u6F87\u6FC6\u6F92"],["e640","\u6F8D\u6F89\u6F8C\u6F62\u6F4F\u6F85\u6F5A\u6F96\u6F76\u6F6C\u6F82\u6F55\u6F72\u6F52\u6F50\u6F57\u6F94\u6F93\u6F5D\u6F00\u6F61\u6F6B\u6F7D\u6F67\u6F90\u6F53\u6F8B\u6F69\u6F7F\u6F95\u6F63\u6F77\u6F6A\u6F7B\u71B2\u71AF\u719B\u71B0\u71A0\u719A\u71A9\u71B5\u719D\u71A5\u719E\u71A4\u71A1\u71AA\u719C\u71A7\u71B3\u7298\u729A\u7358\u7352\u735E\u735F\u7360\u735D\u735B\u7361\u735A\u7359"],["e6a1","\u7362\u7487\u7489\u748A\u7486\u7481\u747D\u7485\u7488\u747C\u7479\u7508\u7507\u757E\u7625\u761E\u7619\u761D\u761C\u7623\u761A\u7628\u761B\u769C\u769D\u769E\u769B\u778D\u778F\u7789\u7788\u78CD\u78BB\u78CF\u78CC\u78D1\u78CE\u78D4\u78C8\u78C3\u78C4\u78C9\u799A\u79A1\u79A0\u799C\u79A2\u799B\u6B76\u7A39\u7AB2\u7AB4\u7AB3\u7BB7\u7BCB\u7BBE\u7BAC\u7BCE\u7BAF\u7BB9\u7BCA\u7BB5\u7CC5\u7CC8\u7CCC\u7CCB\u7DF7\u7DDB\u7DEA\u7DE7\u7DD7\u7DE1\u7E03\u7DFA\u7DE6\u7DF6\u7DF1\u7DF0\u7DEE\u7DDF\u7F76\u7FAC\u7FB0\u7FAD\u7FED\u7FEB\u7FEA\u7FEC\u7FE6\u7FE8\u8064\u8067\u81A3\u819F"],["e740","\u819E\u8195\u81A2\u8199\u8197\u8216\u824F\u8253\u8252\u8250\u824E\u8251\u8524\u853B\u850F\u8500\u8529\u850E\u8509\u850D\u851F\u850A\u8527\u851C\u84FB\u852B\u84FA\u8508\u850C\u84F4\u852A\u84F2\u8515\u84F7\u84EB\u84F3\u84FC\u8512\u84EA\u84E9\u8516\u84FE\u8528\u851D\u852E\u8502\u84FD\u851E\u84F6\u8531\u8526\u84E7\u84E8\u84F0\u84EF\u84F9\u8518\u8520\u8530\u850B\u8519\u852F\u8662"],["e7a1","\u8756\u8763\u8764\u8777\u87E1\u8773\u8758\u8754\u875B\u8752\u8761\u875A\u8751\u875E\u876D\u876A\u8750\u874E\u875F\u875D\u876F\u876C\u877A\u876E\u875C\u8765\u874F\u877B\u8775\u8762\u8767\u8769\u885A\u8905\u890C\u8914\u890B\u8917\u8918\u8919\u8906\u8916\u8911\u890E\u8909\u89A2\u89A4\u89A3\u89ED\u89F0\u89EC\u8ACF\u8AC6\u8AB8\u8AD3\u8AD1\u8AD4\u8AD5\u8ABB\u8AD7\u8ABE\u8AC0\u8AC5\u8AD8\u8AC3\u8ABA\u8ABD\u8AD9\u8C3E\u8C4D\u8C8F\u8CE5\u8CDF\u8CD9\u8CE8\u8CDA\u8CDD\u8CE7\u8DA0\u8D9C\u8DA1\u8D9B\u8E20\u8E23\u8E25\u8E24\u8E2E\u8E15\u8E1B\u8E16\u8E11\u8E19\u8E26\u8E27"],["e840","\u8E14\u8E12\u8E18\u8E13\u8E1C\u8E17\u8E1A\u8F2C\u8F24\u8F18\u8F1A\u8F20\u8F23\u8F16\u8F17\u9073\u9070\u906F\u9067\u906B\u912F\u912B\u9129\u912A\u9132\u9126\u912E\u9185\u9186\u918A\u9181\u9182\u9184\u9180\u92D0\u92C3\u92C4\u92C0\u92D9\u92B6\u92CF\u92F1\u92DF\u92D8\u92E9\u92D7\u92DD\u92CC\u92EF\u92C2\u92E8\u92CA\u92C8\u92CE\u92E6\u92CD\u92D5\u92C9\u92E0\u92DE\u92E7\u92D1\u92D3"],["e8a1","\u92B5\u92E1\u92C6\u92B4\u957C\u95AC\u95AB\u95AE\u95B0\u96A4\u96A2\u96D3\u9705\u9708\u9702\u975A\u978A\u978E\u9788\u97D0\u97CF\u981E\u981D\u9826\u9829\u9828\u9820\u981B\u9827\u98B2\u9908\u98FA\u9911\u9914\u9916\u9917\u9915\u99DC\u99CD\u99CF\u99D3\u99D4\u99CE\u99C9\u99D6\u99D8\u99CB\u99D7\u99CC\u9AB3\u9AEC\u9AEB\u9AF3\u9AF2\u9AF1\u9B46\u9B43\u9B67\u9B74\u9B71\u9B66\u9B76\u9B75\u9B70\u9B68\u9B64\u9B6C\u9CFC\u9CFA\u9CFD\u9CFF\u9CF7\u9D07\u9D00\u9CF9\u9CFB\u9D08\u9D05\u9D04\u9E83\u9ED3\u9F0F\u9F10\u511C\u5113\u5117\u511A\u5111\u51DE\u5334\u53E1\u5670\u5660\u566E"],["e940","\u5673\u5666\u5663\u566D\u5672\u565E\u5677\u571C\u571B\u58C8\u58BD\u58C9\u58BF\u58BA\u58C2\u58BC\u58C6\u5B17\u5B19\u5B1B\u5B21\u5B14\u5B13\u5B10\u5B16\u5B28\u5B1A\u5B20\u5B1E\u5BEF\u5DAC\u5DB1\u5DA9\u5DA7\u5DB5\u5DB0\u5DAE\u5DAA\u5DA8\u5DB2\u5DAD\u5DAF\u5DB4\u5E67\u5E68\u5E66\u5E6F\u5EE9\u5EE7\u5EE6\u5EE8\u5EE5\u5F4B\u5FBC\u619D\u61A8\u6196\u61C5\u61B4\u61C6\u61C1\u61CC\u61BA"],["e9a1","\u61BF\u61B8\u618C\u64D7\u64D6\u64D0\u64CF\u64C9\u64BD\u6489\u64C3\u64DB\u64F3\u64D9\u6533\u657F\u657C\u65A2\u66C8\u66BE\u66C0\u66CA\u66CB\u66CF\u66BD\u66BB\u66BA\u66CC\u6723\u6A34\u6A66\u6A49\u6A67\u6A32\u6A68\u6A3E\u6A5D\u6A6D\u6A76\u6A5B\u6A51\u6A28\u6A5A\u6A3B\u6A3F\u6A41\u6A6A\u6A64\u6A50\u6A4F\u6A54\u6A6F\u6A69\u6A60\u6A3C\u6A5E\u6A56\u6A55\u6A4D\u6A4E\u6A46\u6B55\u6B54\u6B56\u6BA7\u6BAA\u6BAB\u6BC8\u6BC7\u6C04\u6C03\u6C06\u6FAD\u6FCB\u6FA3\u6FC7\u6FBC\u6FCE\u6FC8\u6F5E\u6FC4\u6FBD\u6F9E\u6FCA\u6FA8\u7004\u6FA5\u6FAE\u6FBA\u6FAC\u6FAA\u6FCF\u6FBF\u6FB8"],["ea40","\u6FA2\u6FC9\u6FAB\u6FCD\u6FAF\u6FB2\u6FB0\u71C5\u71C2\u71BF\u71B8\u71D6\u71C0\u71C1\u71CB\u71D4\u71CA\u71C7\u71CF\u71BD\u71D8\u71BC\u71C6\u71DA\u71DB\u729D\u729E\u7369\u7366\u7367\u736C\u7365\u736B\u736A\u747F\u749A\u74A0\u7494\u7492\u7495\u74A1\u750B\u7580\u762F\u762D\u7631\u763D\u7633\u763C\u7635\u7632\u7630\u76BB\u76E6\u779A\u779D\u77A1\u779C\u779B\u77A2\u77A3\u7795\u7799"],["eaa1","\u7797\u78DD\u78E9\u78E5\u78EA\u78DE\u78E3\u78DB\u78E1\u78E2\u78ED\u78DF\u78E0\u79A4\u7A44\u7A48\u7A47\u7AB6\u7AB8\u7AB5\u7AB1\u7AB7\u7BDE\u7BE3\u7BE7\u7BDD\u7BD5\u7BE5\u7BDA\u7BE8\u7BF9\u7BD4\u7BEA\u7BE2\u7BDC\u7BEB\u7BD8\u7BDF\u7CD2\u7CD4\u7CD7\u7CD0\u7CD1\u7E12\u7E21\u7E17\u7E0C\u7E1F\u7E20\u7E13\u7E0E\u7E1C\u7E15\u7E1A\u7E22\u7E0B\u7E0F\u7E16\u7E0D\u7E14\u7E25\u7E24\u7F43\u7F7B\u7F7C\u7F7A\u7FB1\u7FEF\u802A\u8029\u806C\u81B1\u81A6\u81AE\u81B9\u81B5\u81AB\u81B0\u81AC\u81B4\u81B2\u81B7\u81A7\u81F2\u8255\u8256\u8257\u8556\u8545\u856B\u854D\u8553\u8561\u8558"],["eb40","\u8540\u8546\u8564\u8541\u8562\u8544\u8551\u8547\u8563\u853E\u855B\u8571\u854E\u856E\u8575\u8555\u8567\u8560\u858C\u8566\u855D\u8554\u8565\u856C\u8663\u8665\u8664\u879B\u878F\u8797\u8793\u8792\u8788\u8781\u8796\u8798\u8779\u8787\u87A3\u8785\u8790\u8791\u879D\u8784\u8794\u879C\u879A\u8789\u891E\u8926\u8930\u892D\u892E\u8927\u8931\u8922\u8929\u8923\u892F\u892C\u891F\u89F1\u8AE0"],["eba1","\u8AE2\u8AF2\u8AF4\u8AF5\u8ADD\u8B14\u8AE4\u8ADF\u8AF0\u8AC8\u8ADE\u8AE1\u8AE8\u8AFF\u8AEF\u8AFB\u8C91\u8C92\u8C90\u8CF5\u8CEE\u8CF1\u8CF0\u8CF3\u8D6C\u8D6E\u8DA5\u8DA7\u8E33\u8E3E\u8E38\u8E40\u8E45\u8E36\u8E3C\u8E3D\u8E41\u8E30\u8E3F\u8EBD\u8F36\u8F2E\u8F35\u8F32\u8F39\u8F37\u8F34\u9076\u9079\u907B\u9086\u90FA\u9133\u9135\u9136\u9193\u9190\u9191\u918D\u918F\u9327\u931E\u9308\u931F\u9306\u930F\u937A\u9338\u933C\u931B\u9323\u9312\u9301\u9346\u932D\u930E\u930D\u92CB\u931D\u92FA\u9325\u9313\u92F9\u92F7\u9334\u9302\u9324\u92FF\u9329\u9339\u9335\u932A\u9314\u930C"],["ec40","\u930B\u92FE\u9309\u9300\u92FB\u9316\u95BC\u95CD\u95BE\u95B9\u95BA\u95B6\u95BF\u95B5\u95BD\u96A9\u96D4\u970B\u9712\u9710\u9799\u9797\u9794\u97F0\u97F8\u9835\u982F\u9832\u9924\u991F\u9927\u9929\u999E\u99EE\u99EC\u99E5\u99E4\u99F0\u99E3\u99EA\u99E9\u99E7\u9AB9\u9ABF\u9AB4\u9ABB\u9AF6\u9AFA\u9AF9\u9AF7\u9B33\u9B80\u9B85\u9B87\u9B7C\u9B7E\u9B7B\u9B82\u9B93\u9B92\u9B90\u9B7A\u9B95"],["eca1","\u9B7D\u9B88\u9D25\u9D17\u9D20\u9D1E\u9D14\u9D29\u9D1D\u9D18\u9D22\u9D10\u9D19\u9D1F\u9E88\u9E86\u9E87\u9EAE\u9EAD\u9ED5\u9ED6\u9EFA\u9F12\u9F3D\u5126\u5125\u5122\u5124\u5120\u5129\u52F4\u5693\u568C\u568D\u5686\u5684\u5683\u567E\u5682\u567F\u5681\u58D6\u58D4\u58CF\u58D2\u5B2D\u5B25\u5B32\u5B23\u5B2C\u5B27\u5B26\u5B2F\u5B2E\u5B7B\u5BF1\u5BF2\u5DB7\u5E6C\u5E6A\u5FBE\u5FBB\u61C3\u61B5\u61BC\u61E7\u61E0\u61E5\u61E4\u61E8\u61DE\u64EF\u64E9\u64E3\u64EB\u64E4\u64E8\u6581\u6580\u65B6\u65DA\u66D2\u6A8D\u6A96\u6A81\u6AA5\u6A89\u6A9F\u6A9B\u6AA1\u6A9E\u6A87\u6A93\u6A8E"],["ed40","\u6A95\u6A83\u6AA8\u6AA4\u6A91\u6A7F\u6AA6\u6A9A\u6A85\u6A8C\u6A92\u6B5B\u6BAD\u6C09\u6FCC\u6FA9\u6FF4\u6FD4\u6FE3\u6FDC\u6FED\u6FE7\u6FE6\u6FDE\u6FF2\u6FDD\u6FE2\u6FE8\u71E1\u71F1\u71E8\u71F2\u71E4\u71F0\u71E2\u7373\u736E\u736F\u7497\u74B2\u74AB\u7490\u74AA\u74AD\u74B1\u74A5\u74AF\u7510\u7511\u7512\u750F\u7584\u7643\u7648\u7649\u7647\u76A4\u76E9\u77B5\u77AB\u77B2\u77B7\u77B6"],["eda1","\u77B4\u77B1\u77A8\u77F0\u78F3\u78FD\u7902\u78FB\u78FC\u78F2\u7905\u78F9\u78FE\u7904\u79AB\u79A8\u7A5C\u7A5B\u7A56\u7A58\u7A54\u7A5A\u7ABE\u7AC0\u7AC1\u7C05\u7C0F\u7BF2\u7C00\u7BFF\u7BFB\u7C0E\u7BF4\u7C0B\u7BF3\u7C02\u7C09\u7C03\u7C01\u7BF8\u7BFD\u7C06\u7BF0\u7BF1\u7C10\u7C0A\u7CE8\u7E2D\u7E3C\u7E42\u7E33\u9848\u7E38\u7E2A\u7E49\u7E40\u7E47\u7E29\u7E4C\u7E30\u7E3B\u7E36\u7E44\u7E3A\u7F45\u7F7F\u7F7E\u7F7D\u7FF4\u7FF2\u802C\u81BB\u81C4\u81CC\u81CA\u81C5\u81C7\u81BC\u81E9\u825B\u825A\u825C\u8583\u8580\u858F\u85A7\u8595\u85A0\u858B\u85A3\u857B\u85A4\u859A\u859E"],["ee40","\u8577\u857C\u8589\u85A1\u857A\u8578\u8557\u858E\u8596\u8586\u858D\u8599\u859D\u8581\u85A2\u8582\u8588\u8585\u8579\u8576\u8598\u8590\u859F\u8668\u87BE\u87AA\u87AD\u87C5\u87B0\u87AC\u87B9\u87B5\u87BC\u87AE\u87C9\u87C3\u87C2\u87CC\u87B7\u87AF\u87C4\u87CA\u87B4\u87B6\u87BF\u87B8\u87BD\u87DE\u87B2\u8935\u8933\u893C\u893E\u8941\u8952\u8937\u8942\u89AD\u89AF\u89AE\u89F2\u89F3\u8B1E"],["eea1","\u8B18\u8B16\u8B11\u8B05\u8B0B\u8B22\u8B0F\u8B12\u8B15\u8B07\u8B0D\u8B08\u8B06\u8B1C\u8B13\u8B1A\u8C4F\u8C70\u8C72\u8C71\u8C6F\u8C95\u8C94\u8CF9\u8D6F\u8E4E\u8E4D\u8E53\u8E50\u8E4C\u8E47\u8F43\u8F40\u9085\u907E\u9138\u919A\u91A2\u919B\u9199\u919F\u91A1\u919D\u91A0\u93A1\u9383\u93AF\u9364\u9356\u9347\u937C\u9358\u935C\u9376\u9349\u9350\u9351\u9360\u936D\u938F\u934C\u936A\u9379\u9357\u9355\u9352\u934F\u9371\u9377\u937B\u9361\u935E\u9363\u9367\u9380\u934E\u9359\u95C7\u95C0\u95C9\u95C3\u95C5\u95B7\u96AE\u96B0\u96AC\u9720\u971F\u9718\u971D\u9719\u979A\u97A1\u979C"],["ef40","\u979E\u979D\u97D5\u97D4\u97F1\u9841\u9844\u984A\u9849\u9845\u9843\u9925\u992B\u992C\u992A\u9933\u9932\u992F\u992D\u9931\u9930\u9998\u99A3\u99A1\u9A02\u99FA\u99F4\u99F7\u99F9\u99F8\u99F6\u99FB\u99FD\u99FE\u99FC\u9A03\u9ABE\u9AFE\u9AFD\u9B01\u9AFC\u9B48\u9B9A\u9BA8\u9B9E\u9B9B\u9BA6\u9BA1\u9BA5\u9BA4\u9B86\u9BA2\u9BA0\u9BAF\u9D33\u9D41\u9D67\u9D36\u9D2E\u9D2F\u9D31\u9D38\u9D30"],["efa1","\u9D45\u9D42\u9D43\u9D3E\u9D37\u9D40\u9D3D\u7FF5\u9D2D\u9E8A\u9E89\u9E8D\u9EB0\u9EC8\u9EDA\u9EFB\u9EFF\u9F24\u9F23\u9F22\u9F54\u9FA0\u5131\u512D\u512E\u5698\u569C\u5697\u569A\u569D\u5699\u5970\u5B3C\u5C69\u5C6A\u5DC0\u5E6D\u5E6E\u61D8\u61DF\u61ED\u61EE\u61F1\u61EA\u61F0\u61EB\u61D6\u61E9\u64FF\u6504\u64FD\u64F8\u6501\u6503\u64FC\u6594\u65DB\u66DA\u66DB\u66D8\u6AC5\u6AB9\u6ABD\u6AE1\u6AC6\u6ABA\u6AB6\u6AB7\u6AC7\u6AB4\u6AAD\u6B5E\u6BC9\u6C0B\u7007\u700C\u700D\u7001\u7005\u7014\u700E\u6FFF\u7000\u6FFB\u7026\u6FFC\u6FF7\u700A\u7201\u71FF\u71F9\u7203\u71FD\u7376"],["f040","\u74B8\u74C0\u74B5\u74C1\u74BE\u74B6\u74BB\u74C2\u7514\u7513\u765C\u7664\u7659\u7650\u7653\u7657\u765A\u76A6\u76BD\u76EC\u77C2\u77BA\u78FF\u790C\u7913\u7914\u7909\u7910\u7912\u7911\u79AD\u79AC\u7A5F\u7C1C\u7C29\u7C19\u7C20\u7C1F\u7C2D\u7C1D\u7C26\u7C28\u7C22\u7C25\u7C30\u7E5C\u7E50\u7E56\u7E63\u7E58\u7E62\u7E5F\u7E51\u7E60\u7E57\u7E53\u7FB5\u7FB3\u7FF7\u7FF8\u8075\u81D1\u81D2"],["f0a1","\u81D0\u825F\u825E\u85B4\u85C6\u85C0\u85C3\u85C2\u85B3\u85B5\u85BD\u85C7\u85C4\u85BF\u85CB\u85CE\u85C8\u85C5\u85B1\u85B6\u85D2\u8624\u85B8\u85B7\u85BE\u8669\u87E7\u87E6\u87E2\u87DB\u87EB\u87EA\u87E5\u87DF\u87F3\u87E4\u87D4\u87DC\u87D3\u87ED\u87D8\u87E3\u87A4\u87D7\u87D9\u8801\u87F4\u87E8\u87DD\u8953\u894B\u894F\u894C\u8946\u8950\u8951\u8949\u8B2A\u8B27\u8B23\u8B33\u8B30\u8B35\u8B47\u8B2F\u8B3C\u8B3E\u8B31\u8B25\u8B37\u8B26\u8B36\u8B2E\u8B24\u8B3B\u8B3D\u8B3A\u8C42\u8C75\u8C99\u8C98\u8C97\u8CFE\u8D04\u8D02\u8D00\u8E5C\u8E62\u8E60\u8E57\u8E56\u8E5E\u8E65\u8E67"],["f140","\u8E5B\u8E5A\u8E61\u8E5D\u8E69\u8E54\u8F46\u8F47\u8F48\u8F4B\u9128\u913A\u913B\u913E\u91A8\u91A5\u91A7\u91AF\u91AA\u93B5\u938C\u9392\u93B7\u939B\u939D\u9389\u93A7\u938E\u93AA\u939E\u93A6\u9395\u9388\u9399\u939F\u938D\u93B1\u9391\u93B2\u93A4\u93A8\u93B4\u93A3\u93A5\u95D2\u95D3\u95D1\u96B3\u96D7\u96DA\u5DC2\u96DF\u96D8\u96DD\u9723\u9722\u9725\u97AC\u97AE\u97A8\u97AB\u97A4\u97AA"],["f1a1","\u97A2\u97A5\u97D7\u97D9\u97D6\u97D8\u97FA\u9850\u9851\u9852\u98B8\u9941\u993C\u993A\u9A0F\u9A0B\u9A09\u9A0D\u9A04\u9A11\u9A0A\u9A05\u9A07\u9A06\u9AC0\u9ADC\u9B08\u9B04\u9B05\u9B29\u9B35\u9B4A\u9B4C\u9B4B\u9BC7\u9BC6\u9BC3\u9BBF\u9BC1\u9BB5\u9BB8\u9BD3\u9BB6\u9BC4\u9BB9\u9BBD\u9D5C\u9D53\u9D4F\u9D4A\u9D5B\u9D4B\u9D59\u9D56\u9D4C\u9D57\u9D52\u9D54\u9D5F\u9D58\u9D5A\u9E8E\u9E8C\u9EDF\u9F01\u9F00\u9F16\u9F25\u9F2B\u9F2A\u9F29\u9F28\u9F4C\u9F55\u5134\u5135\u5296\u52F7\u53B4\u56AB\u56AD\u56A6\u56A7\u56AA\u56AC\u58DA\u58DD\u58DB\u5912\u5B3D\u5B3E\u5B3F\u5DC3\u5E70"],["f240","\u5FBF\u61FB\u6507\u6510\u650D\u6509\u650C\u650E\u6584\u65DE\u65DD\u66DE\u6AE7\u6AE0\u6ACC\u6AD1\u6AD9\u6ACB\u6ADF\u6ADC\u6AD0\u6AEB\u6ACF\u6ACD\u6ADE\u6B60\u6BB0\u6C0C\u7019\u7027\u7020\u7016\u702B\u7021\u7022\u7023\u7029\u7017\u7024\u701C\u702A\u720C\u720A\u7207\u7202\u7205\u72A5\u72A6\u72A4\u72A3\u72A1\u74CB\u74C5\u74B7\u74C3\u7516\u7660\u77C9\u77CA\u77C4\u77F1\u791D\u791B"],["f2a1","\u7921\u791C\u7917\u791E\u79B0\u7A67\u7A68\u7C33\u7C3C\u7C39\u7C2C\u7C3B\u7CEC\u7CEA\u7E76\u7E75\u7E78\u7E70\u7E77\u7E6F\u7E7A\u7E72\u7E74\u7E68\u7F4B\u7F4A\u7F83\u7F86\u7FB7\u7FFD\u7FFE\u8078\u81D7\u81D5\u8264\u8261\u8263\u85EB\u85F1\u85ED\u85D9\u85E1\u85E8\u85DA\u85D7\u85EC\u85F2\u85F8\u85D8\u85DF\u85E3\u85DC\u85D1\u85F0\u85E6\u85EF\u85DE\u85E2\u8800\u87FA\u8803\u87F6\u87F7\u8809\u880C\u880B\u8806\u87FC\u8808\u87FF\u880A\u8802\u8962\u895A\u895B\u8957\u8961\u895C\u8958\u895D\u8959\u8988\u89B7\u89B6\u89F6\u8B50\u8B48\u8B4A\u8B40\u8B53\u8B56\u8B54\u8B4B\u8B55"],["f340","\u8B51\u8B42\u8B52\u8B57\u8C43\u8C77\u8C76\u8C9A\u8D06\u8D07\u8D09\u8DAC\u8DAA\u8DAD\u8DAB\u8E6D\u8E78\u8E73\u8E6A\u8E6F\u8E7B\u8EC2\u8F52\u8F51\u8F4F\u8F50\u8F53\u8FB4\u9140\u913F\u91B0\u91AD\u93DE\u93C7\u93CF\u93C2\u93DA\u93D0\u93F9\u93EC\u93CC\u93D9\u93A9\u93E6\u93CA\u93D4\u93EE\u93E3\u93D5\u93C4\u93CE\u93C0\u93D2\u93E7\u957D\u95DA\u95DB\u96E1\u9729\u972B\u972C\u9728\u9726"],["f3a1","\u97B3\u97B7\u97B6\u97DD\u97DE\u97DF\u985C\u9859\u985D\u9857\u98BF\u98BD\u98BB\u98BE\u9948\u9947\u9943\u99A6\u99A7\u9A1A\u9A15\u9A25\u9A1D\u9A24\u9A1B\u9A22\u9A20\u9A27\u9A23\u9A1E\u9A1C\u9A14\u9AC2\u9B0B\u9B0A\u9B0E\u9B0C\u9B37\u9BEA\u9BEB\u9BE0\u9BDE\u9BE4\u9BE6\u9BE2\u9BF0\u9BD4\u9BD7\u9BEC\u9BDC\u9BD9\u9BE5\u9BD5\u9BE1\u9BDA\u9D77\u9D81\u9D8A\u9D84\u9D88\u9D71\u9D80\u9D78\u9D86\u9D8B\u9D8C\u9D7D\u9D6B\u9D74\u9D75\u9D70\u9D69\u9D85\u9D73\u9D7B\u9D82\u9D6F\u9D79\u9D7F\u9D87\u9D68\u9E94\u9E91\u9EC0\u9EFC\u9F2D\u9F40\u9F41\u9F4D\u9F56\u9F57\u9F58\u5337\u56B2"],["f440","\u56B5\u56B3\u58E3\u5B45\u5DC6\u5DC7\u5EEE\u5EEF\u5FC0\u5FC1\u61F9\u6517\u6516\u6515\u6513\u65DF\u66E8\u66E3\u66E4\u6AF3\u6AF0\u6AEA\u6AE8\u6AF9\u6AF1\u6AEE\u6AEF\u703C\u7035\u702F\u7037\u7034\u7031\u7042\u7038\u703F\u703A\u7039\u7040\u703B\u7033\u7041\u7213\u7214\u72A8\u737D\u737C\u74BA\u76AB\u76AA\u76BE\u76ED\u77CC\u77CE\u77CF\u77CD\u77F2\u7925\u7923\u7927\u7928\u7924\u7929"],["f4a1","\u79B2\u7A6E\u7A6C\u7A6D\u7AF7\u7C49\u7C48\u7C4A\u7C47\u7C45\u7CEE\u7E7B\u7E7E\u7E81\u7E80\u7FBA\u7FFF\u8079\u81DB\u81D9\u820B\u8268\u8269\u8622\u85FF\u8601\u85FE\u861B\u8600\u85F6\u8604\u8609\u8605\u860C\u85FD\u8819\u8810\u8811\u8817\u8813\u8816\u8963\u8966\u89B9\u89F7\u8B60\u8B6A\u8B5D\u8B68\u8B63\u8B65\u8B67\u8B6D\u8DAE\u8E86\u8E88\u8E84\u8F59\u8F56\u8F57\u8F55\u8F58\u8F5A\u908D\u9143\u9141\u91B7\u91B5\u91B2\u91B3\u940B\u9413\u93FB\u9420\u940F\u9414\u93FE\u9415\u9410\u9428\u9419\u940D\u93F5\u9400\u93F7\u9407\u940E\u9416\u9412\u93FA\u9409\u93F8\u940A\u93FF"],["f540","\u93FC\u940C\u93F6\u9411\u9406\u95DE\u95E0\u95DF\u972E\u972F\u97B9\u97BB\u97FD\u97FE\u9860\u9862\u9863\u985F\u98C1\u98C2\u9950\u994E\u9959\u994C\u994B\u9953\u9A32\u9A34\u9A31\u9A2C\u9A2A\u9A36\u9A29\u9A2E\u9A38\u9A2D\u9AC7\u9ACA\u9AC6\u9B10\u9B12\u9B11\u9C0B\u9C08\u9BF7\u9C05\u9C12\u9BF8\u9C40\u9C07\u9C0E\u9C06\u9C17\u9C14\u9C09\u9D9F\u9D99\u9DA4\u9D9D\u9D92\u9D98\u9D90\u9D9B"],["f5a1","\u9DA0\u9D94\u9D9C\u9DAA\u9D97\u9DA1\u9D9A\u9DA2\u9DA8\u9D9E\u9DA3\u9DBF\u9DA9\u9D96\u9DA6\u9DA7\u9E99\u9E9B\u9E9A\u9EE5\u9EE4\u9EE7\u9EE6\u9F30\u9F2E\u9F5B\u9F60\u9F5E\u9F5D\u9F59\u9F91\u513A\u5139\u5298\u5297\u56C3\u56BD\u56BE\u5B48\u5B47\u5DCB\u5DCF\u5EF1\u61FD\u651B\u6B02\u6AFC\u6B03\u6AF8\u6B00\u7043\u7044\u704A\u7048\u7049\u7045\u7046\u721D\u721A\u7219\u737E\u7517\u766A\u77D0\u792D\u7931\u792F\u7C54\u7C53\u7CF2\u7E8A\u7E87\u7E88\u7E8B\u7E86\u7E8D\u7F4D\u7FBB\u8030\u81DD\u8618\u862A\u8626\u861F\u8623\u861C\u8619\u8627\u862E\u8621\u8620\u8629\u861E\u8625"],["f640","\u8829\u881D\u881B\u8820\u8824\u881C\u882B\u884A\u896D\u8969\u896E\u896B\u89FA\u8B79\u8B78\u8B45\u8B7A\u8B7B\u8D10\u8D14\u8DAF\u8E8E\u8E8C\u8F5E\u8F5B\u8F5D\u9146\u9144\u9145\u91B9\u943F\u943B\u9436\u9429\u943D\u943C\u9430\u9439\u942A\u9437\u942C\u9440\u9431\u95E5\u95E4\u95E3\u9735\u973A\u97BF\u97E1\u9864\u98C9\u98C6\u98C0\u9958\u9956\u9A39\u9A3D\u9A46\u9A44\u9A42\u9A41\u9A3A"],["f6a1","\u9A3F\u9ACD\u9B15\u9B17\u9B18\u9B16\u9B3A\u9B52\u9C2B\u9C1D\u9C1C\u9C2C\u9C23\u9C28\u9C29\u9C24\u9C21\u9DB7\u9DB6\u9DBC\u9DC1\u9DC7\u9DCA\u9DCF\u9DBE\u9DC5\u9DC3\u9DBB\u9DB5\u9DCE\u9DB9\u9DBA\u9DAC\u9DC8\u9DB1\u9DAD\u9DCC\u9DB3\u9DCD\u9DB2\u9E7A\u9E9C\u9EEB\u9EEE\u9EED\u9F1B\u9F18\u9F1A\u9F31\u9F4E\u9F65\u9F64\u9F92\u4EB9\u56C6\u56C5\u56CB\u5971\u5B4B\u5B4C\u5DD5\u5DD1\u5EF2\u6521\u6520\u6526\u6522\u6B0B\u6B08\u6B09\u6C0D\u7055\u7056\u7057\u7052\u721E\u721F\u72A9\u737F\u74D8\u74D5\u74D9\u74D7\u766D\u76AD\u7935\u79B4\u7A70\u7A71\u7C57\u7C5C\u7C59\u7C5B\u7C5A"],["f740","\u7CF4\u7CF1\u7E91\u7F4F\u7F87\u81DE\u826B\u8634\u8635\u8633\u862C\u8632\u8636\u882C\u8828\u8826\u882A\u8825\u8971\u89BF\u89BE\u89FB\u8B7E\u8B84\u8B82\u8B86\u8B85\u8B7F\u8D15\u8E95\u8E94\u8E9A\u8E92\u8E90\u8E96\u8E97\u8F60\u8F62\u9147\u944C\u9450\u944A\u944B\u944F\u9447\u9445\u9448\u9449\u9446\u973F\u97E3\u986A\u9869\u98CB\u9954\u995B\u9A4E\u9A53\u9A54\u9A4C\u9A4F\u9A48\u9A4A"],["f7a1","\u9A49\u9A52\u9A50\u9AD0\u9B19\u9B2B\u9B3B\u9B56\u9B55\u9C46\u9C48\u9C3F\u9C44\u9C39\u9C33\u9C41\u9C3C\u9C37\u9C34\u9C32\u9C3D\u9C36\u9DDB\u9DD2\u9DDE\u9DDA\u9DCB\u9DD0\u9DDC\u9DD1\u9DDF\u9DE9\u9DD9\u9DD8\u9DD6\u9DF5\u9DD5\u9DDD\u9EB6\u9EF0\u9F35\u9F33\u9F32\u9F42\u9F6B\u9F95\u9FA2\u513D\u5299\u58E8\u58E7\u5972\u5B4D\u5DD8\u882F\u5F4F\u6201\u6203\u6204\u6529\u6525\u6596\u66EB\u6B11\u6B12\u6B0F\u6BCA\u705B\u705A\u7222\u7382\u7381\u7383\u7670\u77D4\u7C67\u7C66\u7E95\u826C\u863A\u8640\u8639\u863C\u8631\u863B\u863E\u8830\u8832\u882E\u8833\u8976\u8974\u8973\u89FE"],["f840","\u8B8C\u8B8E\u8B8B\u8B88\u8C45\u8D19\u8E98\u8F64\u8F63\u91BC\u9462\u9455\u945D\u9457\u945E\u97C4\u97C5\u9800\u9A56\u9A59\u9B1E\u9B1F\u9B20\u9C52\u9C58\u9C50\u9C4A\u9C4D\u9C4B\u9C55\u9C59\u9C4C\u9C4E\u9DFB\u9DF7\u9DEF\u9DE3\u9DEB\u9DF8\u9DE4\u9DF6\u9DE1\u9DEE\u9DE6\u9DF2\u9DF0\u9DE2\u9DEC\u9DF4\u9DF3\u9DE8\u9DED\u9EC2\u9ED0\u9EF2\u9EF3\u9F06\u9F1C\u9F38\u9F37\u9F36\u9F43\u9F4F"],["f8a1","\u9F71\u9F70\u9F6E\u9F6F\u56D3\u56CD\u5B4E\u5C6D\u652D\u66ED\u66EE\u6B13\u705F\u7061\u705D\u7060\u7223\u74DB\u74E5\u77D5\u7938\u79B7\u79B6\u7C6A\u7E97\u7F89\u826D\u8643\u8838\u8837\u8835\u884B\u8B94\u8B95\u8E9E\u8E9F\u8EA0\u8E9D\u91BE\u91BD\u91C2\u946B\u9468\u9469\u96E5\u9746\u9743\u9747\u97C7\u97E5\u9A5E\u9AD5\u9B59\u9C63\u9C67\u9C66\u9C62\u9C5E\u9C60\u9E02\u9DFE\u9E07\u9E03\u9E06\u9E05\u9E00\u9E01\u9E09\u9DFF\u9DFD\u9E04\u9EA0\u9F1E\u9F46\u9F74\u9F75\u9F76\u56D4\u652E\u65B8\u6B18\u6B19\u6B17\u6B1A\u7062\u7226\u72AA\u77D8\u77D9\u7939\u7C69\u7C6B\u7CF6\u7E9A"],["f940","\u7E98\u7E9B\u7E99\u81E0\u81E1\u8646\u8647\u8648\u8979\u897A\u897C\u897B\u89FF\u8B98\u8B99\u8EA5\u8EA4\u8EA3\u946E\u946D\u946F\u9471\u9473\u9749\u9872\u995F\u9C68\u9C6E\u9C6D\u9E0B\u9E0D\u9E10\u9E0F\u9E12\u9E11\u9EA1\u9EF5\u9F09\u9F47\u9F78\u9F7B\u9F7A\u9F79\u571E\u7066\u7C6F\u883C\u8DB2\u8EA6\u91C3\u9474\u9478\u9476\u9475\u9A60\u9C74\u9C73\u9C71\u9C75\u9E14\u9E13\u9EF6\u9F0A"],["f9a1","\u9FA4\u7068\u7065\u7CF7\u866A\u883E\u883D\u883F\u8B9E\u8C9C\u8EA9\u8EC9\u974B\u9873\u9874\u98CC\u9961\u99AB\u9A64\u9A66\u9A67\u9B24\u9E15\u9E17\u9F48\u6207\u6B1E\u7227\u864C\u8EA8\u9482\u9480\u9481\u9A69\u9A68\u9B2E\u9E19\u7229\u864B\u8B9F\u9483\u9C79\u9EB7\u7675\u9A6B\u9C7A\u9E1D\u7069\u706A\u9EA4\u9F7E\u9F49\u9F98\u7881\u92B9\u88CF\u58BB\u6052\u7CA7\u5AFA\u2554\u2566\u2557\u2560\u256C\u2563\u255A\u2569\u255D\u2552\u2564\u2555\u255E\u256A\u2561\u2558\u2567\u255B\u2553\u2565\u2556\u255F\u256B\u2562\u2559\u2568\u255C\u2551\u2550\u256D\u256E\u2570\u256F\u2593"]]});var Zje=S((Dkr,Lqt)=>{Lqt.exports=[["8740","\u43F0\u4C32\u4603\u45A6\u4578\u{27267}\u4D77\u45B3\u{27CB1}\u4CE2\u{27CC5}\u3B95\u4736\u4744\u4C47\u4C40\u{242BF}\u{23617}\u{27352}\u{26E8B}\u{270D2}\u4C57\u{2A351}\u474F\u45DA\u4C85\u{27C6C}\u4D07\u4AA4\u46A1\u{26B23}\u7225\u{25A54}\u{21A63}\u{23E06}\u{23F61}\u664D\u56FB"],["8767","\u7D95\u591D\u{28BB9}\u3DF4\u9734\u{27BEF}\u5BDB\u{21D5E}\u5AA4\u3625\u{29EB0}\u5AD1\u5BB7\u5CFC\u676E\u8593\u{29945}\u7461\u749D\u3875\u{21D53}\u{2369E}\u{26021}\u3EEC"],["87a1","\u{258DE}\u3AF5\u7AFC\u9F97\u{24161}\u{2890D}\u{231EA}\u{20A8A}\u{2325E}\u430A\u8484\u9F96\u942F\u4930\u8613\u5896\u974A\u9218\u79D0\u7A32\u6660\u6A29\u889D\u744C\u7BC5\u6782\u7A2C\u524F\u9046\u34E6\u73C4\u{25DB9}\u74C6\u9FC7\u57B3\u492F\u544C\u4131\u{2368E}\u5818\u7A72\u{27B65}\u8B8F\u46AE\u{26E88}\u4181\u{25D99}\u7BAE\u{224BC}\u9FC8\u{224C1}\u{224C9}\u{224CC}\u9FC9\u8504\u{235BB}\u40B4\u9FCA\u44E1\u{2ADFF}\u62C1\u706E\u9FCB"],["8840","\u31C0",4,"\u{2010C}\u31C5\u{200D1}\u{200CD}\u31C6\u31C7\u{200CB}\u{21FE8}\u31C8\u{200CA}\u31C9\u31CA\u31CB\u31CC\u{2010E}\u31CD\u31CE\u0100\xC1\u01CD\xC0\u0112\xC9\u011A\xC8\u014C\xD3\u01D1\xD2\u0FFF\xCA\u0304\u1EBE\u0FFF\xCA\u030C\u1EC0\xCA\u0101\xE1\u01CE\xE0\u0251\u0113\xE9\u011B\xE8\u012B\xED\u01D0\xEC\u014D\xF3\u01D2\xF2\u016B\xFA\u01D4\xF9\u01D6\u01D8\u01DA"],["88a1","\u01DC\xFC\u0FFF\xEA\u0304\u1EBF\u0FFF\xEA\u030C\u1EC1\xEA\u0261\u23DA\u23DB"],["8940","\u{2A3A9}\u{21145}"],["8943","\u650A"],["8946","\u4E3D\u6EDD\u9D4E\u91DF"],["894c","\u{27735}\u6491\u4F1A\u4F28\u4FA8\u5156\u5174\u519C\u51E4\u52A1\u52A8\u533B\u534E\u53D1\u53D8\u56E2\u58F0\u5904\u5907\u5932\u5934\u5B66\u5B9E\u5B9F\u5C9A\u5E86\u603B\u6589\u67FE\u6804\u6865\u6D4E\u70BC\u7535\u7EA4\u7EAC\u7EBA\u7EC7\u7ECF\u7EDF\u7F06\u7F37\u827A\u82CF\u836F\u89C6\u8BBE\u8BE2\u8F66\u8F67\u8F6E"],["89a1","\u7411\u7CFC\u7DCD\u6946\u7AC9\u5227"],["89ab","\u918C\u78B8\u915E\u80BC"],["89b0","\u8D0B\u80F6\u{209E7}"],["89b5","\u809F\u9EC7\u4CCD\u9DC9\u9E0C\u4C3E\u{29DF6}\u{2700E}\u9E0A\u{2A133}\u35C1"],["89c1","\u6E9A\u823E\u7519"],["89c5","\u4911\u9A6C\u9A8F\u9F99\u7987\u{2846C}\u{21DCA}\u{205D0}\u{22AE6}\u4E24\u4E81\u4E80\u4E87\u4EBF\u4EEB\u4F37\u344C\u4FBD\u3E48\u5003\u5088\u347D\u3493\u34A5\u5186\u5905\u51DB\u51FC\u5205\u4E89\u5279\u5290\u5327\u35C7\u53A9\u3551\u53B0\u3553\u53C2\u5423\u356D\u3572\u3681\u5493\u54A3\u54B4\u54B9\u54D0\u54EF\u5518\u5523\u5528\u3598\u553F\u35A5\u35BF\u55D7\u35C5"],["8a40","\u{27D84}\u5525"],["8a43","\u{20C42}\u{20D15}\u{2512B}\u5590\u{22CC6}\u39EC\u{20341}\u8E46\u{24DB8}\u{294E5}\u4053\u{280BE}\u777A\u{22C38}\u3A34\u47D5\u{2815D}\u{269F2}\u{24DEA}\u64DD\u{20D7C}\u{20FB4}\u{20CD5}\u{210F4}\u648D\u8E7E\u{20E96}\u{20C0B}\u{20F64}\u{22CA9}\u{28256}\u{244D3}"],["8a64","\u{20D46}\u{29A4D}\u{280E9}\u47F4\u{24EA7}\u{22CC2}\u9AB2\u3A67\u{295F4}\u3FED\u3506\u{252C7}\u{297D4}\u{278C8}\u{22D44}\u9D6E\u9815"],["8a76","\u43D9\u{260A5}\u64B4\u54E3\u{22D4C}\u{22BCA}\u{21077}\u39FB\u{2106F}"],["8aa1","\u{266DA}\u{26716}\u{279A0}\u64EA\u{25052}\u{20C43}\u8E68\u{221A1}\u{28B4C}\u{20731}"],["8aac","\u480B\u{201A9}\u3FFA\u5873\u{22D8D}"],["8ab2","\u{245C8}\u{204FC}\u{26097}\u{20F4C}\u{20D96}\u5579\u40BB\u43BA"],["8abb","\u4AB4\u{22A66}\u{2109D}\u81AA\u98F5\u{20D9C}\u6379\u39FE\u{22775}\u8DC0\u56A1\u647C\u3E43"],["8ac9","\u{2A601}\u{20E09}\u{22ACF}\u{22CC9}"],["8ace","\u{210C8}\u{239C2}\u3992\u3A06\u{2829B}\u3578\u{25E49}\u{220C7}\u5652\u{20F31}\u{22CB2}\u{29720}\u34BC\u6C3D\u{24E3B}"],["8adf","\u{27574}\u{22E8B}\u{22208}\u{2A65B}\u{28CCD}\u{20E7A}\u{20C34}\u{2681C}\u7F93\u{210CF}\u{22803}\u{22939}\u35FB\u{251E3}\u{20E8C}\u{20F8D}\u{20EAA}\u3F93\u{20F30}\u{20D47}\u{2114F}\u{20E4C}"],["8af6","\u{20EAB}\u{20BA9}\u{20D48}\u{210C0}\u{2113D}\u3FF9\u{22696}\u6432\u{20FAD}"],["8b40","\u{233F4}\u{27639}\u{22BCE}\u{20D7E}\u{20D7F}\u{22C51}\u{22C55}\u3A18\u{20E98}\u{210C7}\u{20F2E}\u{2A632}\u{26B50}\u{28CD2}\u{28D99}\u{28CCA}\u95AA\u54CC\u82C4\u55B9"],["8b55","\u{29EC3}\u9C26\u9AB6\u{2775E}\u{22DEE}\u7140\u816D\u80EC\u5C1C\u{26572}\u8134\u3797\u535F\u{280BD}\u91B6\u{20EFA}\u{20E0F}\u{20E77}\u{20EFB}\u35DD\u{24DEB}\u3609\u{20CD6}\u56AF\u{227B5}\u{210C9}\u{20E10}\u{20E78}\u{21078}\u{21148}\u{28207}\u{21455}\u{20E79}\u{24E50}\u{22DA4}\u5A54\u{2101D}\u{2101E}\u{210F5}\u{210F6}\u579C\u{20E11}"],["8ba1","\u{27694}\u{282CD}\u{20FB5}\u{20E7B}\u{2517E}\u3703\u{20FB6}\u{21180}\u{252D8}\u{2A2BD}\u{249DA}\u{2183A}\u{24177}\u{2827C}\u5899\u5268\u361A\u{2573D}\u7BB2\u5B68\u4800\u4B2C\u9F27\u49E7\u9C1F\u9B8D\u{25B74}\u{2313D}\u55FB\u35F2\u5689\u4E28\u5902\u{21BC1}\u{2F878}\u9751\u{20086}\u4E5B\u4EBB\u353E\u5C23\u5F51\u5FC4\u38FA\u624C\u6535\u6B7A\u6C35\u6C3A\u706C\u722B\u4E2C\u72AD\u{248E9}\u7F52\u793B\u7CF9\u7F53\u{2626A}\u34C1"],["8bde","\u{2634B}\u8002\u8080\u{26612}\u{26951}\u535D\u8864\u89C1\u{278B2}\u8BA0\u8D1D\u9485\u9578\u957F\u95E8\u{28E0F}\u97E6\u9875\u98CE\u98DE\u9963\u{29810}\u9C7C\u9E1F\u9EC4\u6B6F\uF907\u4E37\u{20087}\u961D\u6237\u94A2"],["8c40","\u503B\u6DFE\u{29C73}\u9FA6\u3DC9\u888F\u{2414E}\u7077\u5CF5\u4B20\u{251CD}\u3559\u{25D30}\u6122\u{28A32}\u8FA7\u91F6\u7191\u6719\u73BA\u{23281}\u{2A107}\u3C8B\u{21980}\u4B10\u78E4\u7402\u51AE\u{2870F}\u4009\u6A63\u{2A2BA}\u4223\u860F\u{20A6F}\u7A2A\u{29947}\u{28AEA}\u9755\u704D\u5324\u{2207E}\u93F4\u76D9\u{289E3}\u9FA7\u77DD\u4EA3\u4FF0\u50BC\u4E2F\u4F17\u9FA8\u5434\u7D8B\u5892\u58D0\u{21DB6}\u5E92\u5E99\u5FC2\u{22712}\u658B"],["8ca1","\u{233F9}\u6919\u6A43\u{23C63}\u6CFF"],["8ca7","\u7200\u{24505}\u738C\u3EDB\u{24A13}\u5B15\u74B9\u8B83\u{25CA4}\u{25695}\u7A93\u7BEC\u7CC3\u7E6C\u82F8\u8597\u9FA9\u8890\u9FAA\u8EB9\u9FAB\u8FCF\u855F\u99E0\u9221\u9FAC\u{28DB9}\u{2143F}\u4071\u42A2\u5A1A"],["8cc9","\u9868\u676B\u4276\u573D"],["8cce","\u85D6\u{2497B}\u82BF\u{2710D}\u4C81\u{26D74}\u5D7B\u{26B15}\u{26FBE}\u9FAD\u9FAE\u5B96\u9FAF\u66E7\u7E5B\u6E57\u79CA\u3D88\u44C3\u{23256}\u{22796}\u439A\u4536"],["8ce6","\u5CD5\u{23B1A}\u8AF9\u5C78\u3D12\u{23551}\u5D78\u9FB2\u7157\u4558\u{240EC}\u{21E23}\u4C77\u3978\u344A\u{201A4}\u{26C41}\u8ACC\u4FB4\u{20239}\u59BF\u816C\u9856\u{298FA}\u5F3B"],["8d40","\u{20B9F}"],["8d42","\u{221C1}\u{2896D}\u4102\u46BB\u{29079}\u3F07\u9FB3\u{2A1B5}\u40F8\u37D6\u46F7\u{26C46}\u417C\u{286B2}\u{273FF}\u456D\u38D4\u{2549A}\u4561\u451B\u4D89\u4C7B\u4D76\u45EA\u3FC8\u{24B0F}\u3661\u44DE\u44BD\u41ED\u5D3E\u5D48\u5D56\u3DFC\u380F\u5DA4\u5DB9\u3820\u3838\u5E42\u5EBD\u5F25\u5F83\u3908\u3914\u393F\u394D\u60D7\u613D\u5CE5\u3989\u61B7\u61B9\u61CF\u39B8\u622C\u6290\u62E5\u6318\u39F8\u56B1"],["8da1","\u3A03\u63E2\u63FB\u6407\u645A\u3A4B\u64C0\u5D15\u5621\u9F9F\u3A97\u6586\u3ABD\u65FF\u6653\u3AF2\u6692\u3B22\u6716\u3B42\u67A4\u6800\u3B58\u684A\u6884\u3B72\u3B71\u3B7B\u6909\u6943\u725C\u6964\u699F\u6985\u3BBC\u69D6\u3BDD\u6A65\u6A74\u6A71\u6A82\u3BEC\u6A99\u3BF2\u6AAB\u6AB5\u6AD4\u6AF6\u6B81\u6BC1\u6BEA\u6C75\u6CAA\u3CCB\u6D02\u6D06\u6D26\u6D81\u3CEF\u6DA4\u6DB1\u6E15\u6E18\u6E29\u6E86\u{289C0}\u6EBB\u6EE2\u6EDA\u9F7F\u6EE8\u6EE9\u6F24\u6F34\u3D46\u{23F41}\u6F81\u6FBE\u3D6A\u3D75\u71B7\u5C99\u3D8A\u702C\u3D91\u7050\u7054\u706F\u707F\u7089\u{20325}\u43C1\u35F1\u{20ED8}"],["8e40","\u{23ED7}\u57BE\u{26ED3}\u713E\u{257E0}\u364E\u69A2\u{28BE9}\u5B74\u7A49\u{258E1}\u{294D9}\u7A65\u7A7D\u{259AC}\u7ABB\u7AB0\u7AC2\u7AC3\u71D1\u{2648D}\u41CA\u7ADA\u7ADD\u7AEA\u41EF\u54B2\u{25C01}\u7B0B\u7B55\u7B29\u{2530E}\u{25CFE}\u7BA2\u7B6F\u839C\u{25BB4}\u{26C7F}\u7BD0\u8421\u7B92\u7BB8\u{25D20}\u3DAD\u{25C65}\u8492\u7BFA\u7C06\u7C35\u{25CC1}\u7C44\u7C83\u{24882}\u7CA6\u667D\u{24578}\u7CC9\u7CC7\u7CE6\u7C74\u7CF3\u7CF5\u7CCE"],["8ea1","\u7E67\u451D\u{26E44}\u7D5D\u{26ED6}\u748D\u7D89\u7DAB\u7135\u7DB3\u7DD2\u{24057}\u{26029}\u7DE4\u3D13\u7DF5\u{217F9}\u7DE5\u{2836D}\u7E1D\u{26121}\u{2615A}\u7E6E\u7E92\u432B\u946C\u7E27\u7F40\u7F41\u7F47\u7936\u{262D0}\u99E1\u7F97\u{26351}\u7FA3\u{21661}\u{20068}\u455C\u{23766}\u4503\u{2833A}\u7FFA\u{26489}\u8005\u8008\u801D\u8028\u802F\u{2A087}\u{26CC3}\u803B\u803C\u8061\u{22714}\u4989\u{26626}\u{23DE3}\u{266E8}\u6725\u80A7\u{28A48}\u8107\u811A\u58B0\u{226F6}\u6C7F\u{26498}\u{24FB8}\u64E7\u{2148A}\u8218\u{2185E}\u6A53\u{24A65}\u{24A95}\u447A\u8229\u{20B0D}\u{26A52}\u{23D7E}\u4FF9\u{214FD}\u84E2\u8362\u{26B0A}\u{249A7}\u{23530}\u{21773}\u{23DF8}\u82AA\u691B\u{2F994}\u41DB"],["8f40","\u854B\u82D0\u831A\u{20E16}\u{217B4}\u36C1\u{2317D}\u{2355A}\u827B\u82E2\u8318\u{23E8B}\u{26DA3}\u{26B05}\u{26B97}\u{235CE}\u3DBF\u831D\u55EC\u8385\u450B\u{26DA5}\u83AC\u83C1\u83D3\u347E\u{26ED4}\u6A57\u855A\u3496\u{26E42}\u{22EEF}\u8458\u{25BE4}\u8471\u3DD3\u44E4\u6AA7\u844A\u{23CB5}\u7958\u84A8\u{26B96}\u{26E77}\u{26E43}\u84DE\u840F\u8391\u44A0\u8493\u84E4\u{25C91}\u4240\u{25CC0}\u4543\u8534\u5AF2\u{26E99}\u4527\u8573\u4516\u67BF\u8616"],["8fa1","\u{28625}\u{2863B}\u85C1\u{27088}\u8602\u{21582}\u{270CD}\u{2F9B2}\u456A\u8628\u3648\u{218A2}\u53F7\u{2739A}\u867E\u8771\u{2A0F8}\u87EE\u{22C27}\u87B1\u87DA\u880F\u5661\u866C\u6856\u460F\u8845\u8846\u{275E0}\u{23DB9}\u{275E4}\u885E\u889C\u465B\u88B4\u88B5\u63C1\u88C5\u7777\u{2770F}\u8987\u898A\u89A6\u89A9\u89A7\u89BC\u{28A25}\u89E7\u{27924}\u{27ABD}\u8A9C\u7793\u91FE\u8A90\u{27A59}\u7AE9\u{27B3A}\u{23F8F}\u4713\u{27B38}\u717C\u8B0C\u8B1F\u{25430}\u{25565}\u8B3F\u8B4C\u8B4D\u8AA9\u{24A7A}\u8B90\u8B9B\u8AAF\u{216DF}\u4615\u884F\u8C9B\u{27D54}\u{27D8F}\u{2F9D4}\u3725\u{27D53}\u8CD6\u{27D98}\u{27DBD}\u8D12\u8D03\u{21910}\u8CDB\u705C\u8D11\u{24CC9}\u3ED0\u8D77"],["9040","\u8DA9\u{28002}\u{21014}\u{2498A}\u3B7C\u{281BC}\u{2710C}\u7AE7\u8EAD\u8EB6\u8EC3\u92D4\u8F19\u8F2D\u{28365}\u{28412}\u8FA5\u9303\u{2A29F}\u{20A50}\u8FB3\u492A\u{289DE}\u{2853D}\u{23DBB}\u5EF8\u{23262}\u8FF9\u{2A014}\u{286BC}\u{28501}\u{22325}\u3980\u{26ED7}\u9037\u{2853C}\u{27ABE}\u9061\u{2856C}\u{2860B}\u90A8\u{28713}\u90C4\u{286E6}\u90AE\u90FD\u9167\u3AF0\u91A9\u91C4\u7CAC\u{28933}\u{21E89}\u920E\u6C9F\u9241\u9262\u{255B9}\u92B9\u{28AC6}\u{23C9B}\u{28B0C}\u{255DB}"],["90a1","\u{20D31}\u932C\u936B\u{28AE1}\u{28BEB}\u708F\u5AC3\u{28AE2}\u{28AE5}\u4965\u9244\u{28BEC}\u{28C39}\u{28BFF}\u9373\u945B\u8EBC\u9585\u95A6\u9426\u95A0\u6FF6\u42B9\u{2267A}\u{286D8}\u{2127C}\u{23E2E}\u49DF\u6C1C\u967B\u9696\u416C\u96A3\u{26ED5}\u61DA\u96B6\u78F5\u{28AE0}\u96BD\u53CC\u49A1\u{26CB8}\u{20274}\u{26410}\u{290AF}\u{290E5}\u{24AD1}\u{21915}\u{2330A}\u9731\u8642\u9736\u4A0F\u453D\u4585\u{24AE9}\u7075\u5B41\u971B\u975C\u{291D5}\u9757\u5B4A\u{291EB}\u975F\u9425\u50D0\u{230B7}\u{230BC}\u9789\u979F\u97B1\u97BE\u97C0\u97D2\u97E0\u{2546C}\u97EE\u741C\u{29433}\u97FF\u97F5\u{2941D}\u{2797A}\u4AD1\u9834\u9833\u984B\u9866\u3B0E\u{27175}\u3D51\u{20630}\u{2415C}"],["9140","\u{25706}\u98CA\u98B7\u98C8\u98C7\u4AFF\u{26D27}\u{216D3}\u55B0\u98E1\u98E6\u98EC\u9378\u9939\u{24A29}\u4B72\u{29857}\u{29905}\u99F5\u9A0C\u9A3B\u9A10\u9A58\u{25725}\u36C4\u{290B1}\u{29BD5}\u9AE0\u9AE2\u{29B05}\u9AF4\u4C0E\u9B14\u9B2D\u{28600}\u5034\u9B34\u{269A8}\u38C3\u{2307D}\u9B50\u9B40\u{29D3E}\u5A45\u{21863}\u9B8E\u{2424B}\u9C02\u9BFF\u9C0C\u{29E68}\u9DD4\u{29FB7}\u{2A192}\u{2A1AB}\u{2A0E1}\u{2A123}\u{2A1DF}\u9D7E\u9D83\u{2A134}\u9E0E\u6888"],["91a1","\u9DC4\u{2215B}\u{2A193}\u{2A220}\u{2193B}\u{2A233}\u9D39\u{2A0B9}\u{2A2B4}\u9E90\u9E95\u9E9E\u9EA2\u4D34\u9EAA\u9EAF\u{24364}\u9EC1\u3B60\u39E5\u3D1D\u4F32\u37BE\u{28C2B}\u9F02\u9F08\u4B96\u9424\u{26DA2}\u9F17\u9F16\u9F39\u569F\u568A\u9F45\u99B8\u{2908B}\u97F2\u847F\u9F62\u9F69\u7ADC\u9F8E\u7216\u4BBE\u{24975}\u{249BB}\u7177\u{249F8}\u{24348}\u{24A51}\u739E\u{28BDA}\u{218FA}\u799F\u{2897E}\u{28E36}\u9369\u93F3\u{28A44}\u92EC\u9381\u93CB\u{2896C}\u{244B9}\u7217\u3EEB\u7772\u7A43\u70D0\u{24473}\u{243F8}\u717E\u{217EF}\u70A3\u{218BE}\u{23599}\u3EC7\u{21885}\u{2542F}\u{217F8}\u3722\u{216FB}\u{21839}\u36E1\u{21774}\u{218D1}\u{25F4B}\u3723\u{216C0}\u575B\u{24A25}\u{213FE}\u{212A8}"],["9240","\u{213C6}\u{214B6}\u8503\u{236A6}\u8503\u8455\u{24994}\u{27165}\u{23E31}\u{2555C}\u{23EFB}\u{27052}\u44F4\u{236EE}\u{2999D}\u{26F26}\u67F9\u3733\u3C15\u3DE7\u586C\u{21922}\u6810\u4057\u{2373F}\u{240E1}\u{2408B}\u{2410F}\u{26C21}\u54CB\u569E\u{266B1}\u5692\u{20FDF}\u{20BA8}\u{20E0D}\u93C6\u{28B13}\u939C\u4EF8\u512B\u3819\u{24436}\u4EBC\u{20465}\u{2037F}\u4F4B\u4F8A\u{25651}\u5A68\u{201AB}\u{203CB}\u3999\u{2030A}\u{20414}\u3435\u4F29\u{202C0}\u{28EB3}\u{20275}\u8ADA\u{2020C}\u4E98"],["92a1","\u50CD\u510D\u4FA2\u4F03\u{24A0E}\u{23E8A}\u4F42\u502E\u506C\u5081\u4FCC\u4FE5\u5058\u50FC\u5159\u515B\u515D\u515E\u6E76\u{23595}\u{23E39}\u{23EBF}\u6D72\u{21884}\u{23E89}\u51A8\u51C3\u{205E0}\u44DD\u{204A3}\u{20492}\u{20491}\u8D7A\u{28A9C}\u{2070E}\u5259\u52A4\u{20873}\u52E1\u936E\u467A\u718C\u{2438C}\u{20C20}\u{249AC}\u{210E4}\u69D1\u{20E1D}\u7479\u3EDE\u7499\u7414\u7456\u7398\u4B8E\u{24ABC}\u{2408D}\u53D0\u3584\u720F\u{240C9}\u55B4\u{20345}\u54CD\u{20BC6}\u571D\u925D\u96F4\u9366\u57DD\u578D\u577F\u363E\u58CB\u5A99\u{28A46}\u{216FA}\u{2176F}\u{21710}\u5A2C\u59B8\u928F\u5A7E\u5ACF\u5A12\u{25946}\u{219F3}\u{21861}\u{24295}\u36F5\u6D05\u7443\u5A21\u{25E83}"],["9340","\u5A81\u{28BD7}\u{20413}\u93E0\u748C\u{21303}\u7105\u4972\u9408\u{289FB}\u93BD\u37A0\u5C1E\u5C9E\u5E5E\u5E48\u{21996}\u{2197C}\u{23AEE}\u5ECD\u5B4F\u{21903}\u{21904}\u3701\u{218A0}\u36DD\u{216FE}\u36D3\u812A\u{28A47}\u{21DBA}\u{23472}\u{289A8}\u5F0C\u5F0E\u{21927}\u{217AB}\u5A6B\u{2173B}\u5B44\u8614\u{275FD}\u8860\u607E\u{22860}\u{2262B}\u5FDB\u3EB8\u{225AF}\u{225BE}\u{29088}\u{26F73}\u61C0\u{2003E}\u{20046}\u{2261B}\u6199\u6198\u6075\u{22C9B}\u{22D07}\u{246D4}\u{2914D}"],["93a1","\u6471\u{24665}\u{22B6A}\u3A29\u{22B22}\u{23450}\u{298EA}\u{22E78}\u6337\u{2A45B}\u64B6\u6331\u63D1\u{249E3}\u{22D67}\u62A4\u{22CA1}\u643B\u656B\u6972\u3BF4\u{2308E}\u{232AD}\u{24989}\u{232AB}\u550D\u{232E0}\u{218D9}\u{2943F}\u66CE\u{23289}\u{231B3}\u3AE0\u4190\u{25584}\u{28B22}\u{2558F}\u{216FC}\u{2555B}\u{25425}\u78EE\u{23103}\u{2182A}\u{23234}\u3464\u{2320F}\u{23182}\u{242C9}\u668E\u{26D24}\u666B\u4B93\u6630\u{27870}\u{21DEB}\u6663\u{232D2}\u{232E1}\u661E\u{25872}\u38D1\u{2383A}\u{237BC}\u3B99\u{237A2}\u{233FE}\u74D0\u3B96\u678F\u{2462A}\u68B6\u681E\u3BC4\u6ABE\u3863\u{237D5}\u{24487}\u6A33\u6A52\u6AC9\u6B05\u{21912}\u6511\u6898\u6A4C\u3BD7\u6A7A\u6B57\u{23FC0}\u{23C9A}\u93A0\u92F2\u{28BEA}\u{28ACB}"],["9440","\u9289\u{2801E}\u{289DC}\u9467\u6DA5\u6F0B\u{249EC}\u6D67\u{23F7F}\u3D8F\u6E04\u{2403C}\u5A3D\u6E0A\u5847\u6D24\u7842\u713B\u{2431A}\u{24276}\u70F1\u7250\u7287\u7294\u{2478F}\u{24725}\u5179\u{24AA4}\u{205EB}\u747A\u{23EF8}\u{2365F}\u{24A4A}\u{24917}\u{25FE1}\u3F06\u3EB1\u{24ADF}\u{28C23}\u{23F35}\u60A7\u3EF3\u74CC\u743C\u9387\u7437\u449F\u{26DEA}\u4551\u7583\u3F63\u{24CD9}\u{24D06}\u3F58\u7555\u7673\u{2A5C6}\u3B19\u7468\u{28ACC}\u{249AB}\u{2498E}\u3AFB"],["94a1","\u3DCD\u{24A4E}\u3EFF\u{249C5}\u{248F3}\u91FA\u5732\u9342\u{28AE3}\u{21864}\u50DF\u{25221}\u{251E7}\u7778\u{23232}\u770E\u770F\u777B\u{24697}\u{23781}\u3A5E\u{248F0}\u7438\u749B\u3EBF\u{24ABA}\u{24AC7}\u40C8\u{24A96}\u{261AE}\u9307\u{25581}\u781E\u788D\u7888\u78D2\u73D0\u7959\u{27741}\u{256E3}\u410E\u799B\u8496\u79A5\u6A2D\u{23EFA}\u7A3A\u79F4\u416E\u{216E6}\u4132\u9235\u79F1\u{20D4C}\u{2498C}\u{20299}\u{23DBA}\u{2176E}\u3597\u556B\u3570\u36AA\u{201D4}\u{20C0D}\u7AE2\u5A59\u{226F5}\u{25AAF}\u{25A9C}\u5A0D\u{2025B}\u78F0\u5A2A\u{25BC6}\u7AFE\u41F9\u7C5D\u7C6D\u4211\u{25BB3}\u{25EBC}\u{25EA6}\u7CCD\u{249F9}\u{217B0}\u7C8E\u7C7C\u7CAE\u6AB2\u7DDC\u7E07\u7DD3\u7F4E\u{26261}"],["9540","\u{2615C}\u{27B48}\u7D97\u{25E82}\u426A\u{26B75}\u{20916}\u67D6\u{2004E}\u{235CF}\u57C4\u{26412}\u{263F8}\u{24962}\u7FDD\u7B27\u{2082C}\u{25AE9}\u{25D43}\u7B0C\u{25E0E}\u99E6\u8645\u9A63\u6A1C\u{2343F}\u39E2\u{249F7}\u{265AD}\u9A1F\u{265A0}\u8480\u{27127}\u{26CD1}\u44EA\u8137\u4402\u80C6\u8109\u8142\u{267B4}\u98C3\u{26A42}\u8262\u8265\u{26A51}\u8453\u{26DA7}\u8610\u{2721B}\u5A86\u417F\u{21840}\u5B2B\u{218A1}\u5AE4\u{218D8}\u86A0\u{2F9BC}\u{23D8F}\u882D\u{27422}\u5A02"],["95a1","\u886E\u4F45\u8887\u88BF\u88E6\u8965\u894D\u{25683}\u8954\u{27785}\u{27784}\u{28BF5}\u{28BD9}\u{28B9C}\u{289F9}\u3EAD\u84A3\u46F5\u46CF\u37F2\u8A3D\u8A1C\u{29448}\u5F4D\u922B\u{24284}\u65D4\u7129\u70C4\u{21845}\u9D6D\u8C9F\u8CE9\u{27DDC}\u599A\u77C3\u59F0\u436E\u36D4\u8E2A\u8EA7\u{24C09}\u8F30\u8F4A\u42F4\u6C58\u6FBB\u{22321}\u489B\u6F79\u6E8B\u{217DA}\u9BE9\u36B5\u{2492F}\u90BB\u9097\u5571\u4906\u91BB\u9404\u{28A4B}\u4062\u{28AFC}\u9427\u{28C1D}\u{28C3B}\u84E5\u8A2B\u9599\u95A7\u9597\u9596\u{28D34}\u7445\u3EC2\u{248FF}\u{24A42}\u{243EA}\u3EE7\u{23225}\u968F\u{28EE7}\u{28E66}\u{28E65}\u3ECC\u{249ED}\u{24A78}\u{23FEE}\u7412\u746B\u3EFC\u9741\u{290B0}"],["9640","\u6847\u4A1D\u{29093}\u{257DF}\u975D\u9368\u{28989}\u{28C26}\u{28B2F}\u{263BE}\u92BA\u5B11\u8B69\u493C\u73F9\u{2421B}\u979B\u9771\u9938\u{20F26}\u5DC1\u{28BC5}\u{24AB2}\u981F\u{294DA}\u92F6\u{295D7}\u91E5\u44C0\u{28B50}\u{24A67}\u{28B64}\u98DC\u{28A45}\u3F00\u922A\u4925\u8414\u993B\u994D\u{27B06}\u3DFD\u999B\u4B6F\u99AA\u9A5C\u{28B65}\u{258C8}\u6A8F\u9A21\u5AFE\u9A2F\u{298F1}\u4B90\u{29948}\u99BC\u4BBD\u4B97\u937D\u5872\u{21302}\u5822\u{249B8}"],["96a1","\u{214E8}\u7844\u{2271F}\u{23DB8}\u68C5\u3D7D\u9458\u3927\u6150\u{22781}\u{2296B}\u6107\u9C4F\u9C53\u9C7B\u9C35\u9C10\u9B7F\u9BCF\u{29E2D}\u9B9F\u{2A1F5}\u{2A0FE}\u9D21\u4CAE\u{24104}\u9E18\u4CB0\u9D0C\u{2A1B4}\u{2A0ED}\u{2A0F3}\u{2992F}\u9DA5\u84BD\u{26E12}\u{26FDF}\u{26B82}\u85FC\u4533\u{26DA4}\u{26E84}\u{26DF0}\u8420\u85EE\u{26E00}\u{237D7}\u{26064}\u79E2\u{2359C}\u{23640}\u492D\u{249DE}\u3D62\u93DB\u92BE\u9348\u{202BF}\u78B9\u9277\u944D\u4FE4\u3440\u9064\u{2555D}\u783D\u7854\u78B6\u784B\u{21757}\u{231C9}\u{24941}\u369A\u4F72\u6FDA\u6FD9\u701E\u701E\u5414\u{241B5}\u57BB\u58F3\u578A\u9D16\u57D7\u7134\u34AF\u{241AC}\u71EB\u{26C40}\u{24F97}\u5B28\u{217B5}\u{28A49}"],["9740","\u610C\u5ACE\u5A0B\u42BC\u{24488}\u372C\u4B7B\u{289FC}\u93BB\u93B8\u{218D6}\u{20F1D}\u8472\u{26CC0}\u{21413}\u{242FA}\u{22C26}\u{243C1}\u5994\u{23DB7}\u{26741}\u7DA8\u{2615B}\u{260A4}\u{249B9}\u{2498B}\u{289FA}\u92E5\u73E2\u3EE9\u74B4\u{28B63}\u{2189F}\u3EE1\u{24AB3}\u6AD8\u73F3\u73FB\u3ED6\u{24A3E}\u{24A94}\u{217D9}\u{24A66}\u{203A7}\u{21424}\u{249E5}\u7448\u{24916}\u70A5\u{24976}\u9284\u73E6\u935F\u{204FE}\u9331\u{28ACE}\u{28A16}\u9386\u{28BE7}\u{255D5}\u4935\u{28A82}\u716B"],["97a1","\u{24943}\u{20CFF}\u56A4\u{2061A}\u{20BEB}\u{20CB8}\u5502\u79C4\u{217FA}\u7DFE\u{216C2}\u{24A50}\u{21852}\u452E\u9401\u370A\u{28AC0}\u{249AD}\u59B0\u{218BF}\u{21883}\u{27484}\u5AA1\u36E2\u{23D5B}\u36B0\u925F\u5A79\u{28A81}\u{21862}\u9374\u3CCD\u{20AB4}\u4A96\u398A\u50F4\u3D69\u3D4C\u{2139C}\u7175\u42FB\u{28218}\u6E0F\u{290E4}\u44EB\u6D57\u{27E4F}\u7067\u6CAF\u3CD6\u{23FED}\u{23E2D}\u6E02\u6F0C\u3D6F\u{203F5}\u7551\u36BC\u34C8\u4680\u3EDA\u4871\u59C4\u926E\u493E\u8F41\u{28C1C}\u{26BC0}\u5812\u57C8\u36D6\u{21452}\u70FE\u{24362}\u{24A71}\u{22FE3}\u{212B0}\u{223BD}\u68B9\u6967\u{21398}\u{234E5}\u{27BF4}\u{236DF}\u{28A83}\u{237D6}\u{233FA}\u{24C9F}\u6A1A\u{236AD}\u{26CB7}\u843E\u44DF\u44CE"],["9840","\u{26D26}\u{26D51}\u{26C82}\u{26FDE}\u6F17\u{27109}\u833D\u{2173A}\u83ED\u{26C80}\u{27053}\u{217DB}\u5989\u5A82\u{217B3}\u5A61\u5A71\u{21905}\u{241FC}\u372D\u59EF\u{2173C}\u36C7\u718E\u9390\u669A\u{242A5}\u5A6E\u5A2B\u{24293}\u6A2B\u{23EF9}\u{27736}\u{2445B}\u{242CA}\u711D\u{24259}\u{289E1}\u4FB0\u{26D28}\u5CC2\u{244CE}\u{27E4D}\u{243BD}\u6A0C\u{24256}\u{21304}\u70A6\u7133\u{243E9}\u3DA5\u6CDF\u{2F825}\u{24A4F}\u7E65\u59EB\u5D2F\u3DF3\u5F5C\u{24A5D}\u{217DF}\u7DA4\u8426"],["98a1","\u5485\u{23AFA}\u{23300}\u{20214}\u577E\u{208D5}\u{20619}\u3FE5\u{21F9E}\u{2A2B6}\u7003\u{2915B}\u5D70\u738F\u7CD3\u{28A59}\u{29420}\u4FC8\u7FE7\u72CD\u7310\u{27AF4}\u7338\u7339\u{256F6}\u7341\u7348\u3EA9\u{27B18}\u906C\u71F5\u{248F2}\u73E1\u81F6\u3ECA\u770C\u3ED1\u6CA2\u56FD\u7419\u741E\u741F\u3EE2\u3EF0\u3EF4\u3EFA\u74D3\u3F0E\u3F53\u7542\u756D\u7572\u758D\u3F7C\u75C8\u75DC\u3FC0\u764D\u3FD7\u7674\u3FDC\u767A\u{24F5C}\u7188\u5623\u8980\u5869\u401D\u7743\u4039\u6761\u4045\u35DB\u7798\u406A\u406F\u5C5E\u77BE\u77CB\u58F2\u7818\u70B9\u781C\u40A8\u7839\u7847\u7851\u7866\u8448\u{25535}\u7933\u6803\u7932\u4103"],["9940","\u4109\u7991\u7999\u8FBB\u7A06\u8FBC\u4167\u7A91\u41B2\u7ABC\u8279\u41C4\u7ACF\u7ADB\u41CF\u4E21\u7B62\u7B6C\u7B7B\u7C12\u7C1B\u4260\u427A\u7C7B\u7C9C\u428C\u7CB8\u4294\u7CED\u8F93\u70C0\u{20CCF}\u7DCF\u7DD4\u7DD0\u7DFD\u7FAE\u7FB4\u729F\u4397\u8020\u8025\u7B39\u802E\u8031\u8054\u3DCC\u57B4\u70A0\u80B7\u80E9\u43ED\u810C\u732A\u810E\u8112\u7560\u8114\u4401\u3B39\u8156\u8159\u815A"],["99a1","\u4413\u583A\u817C\u8184\u4425\u8193\u442D\u81A5\u57EF\u81C1\u81E4\u8254\u448F\u82A6\u8276\u82CA\u82D8\u82FF\u44B0\u8357\u9669\u698A\u8405\u70F5\u8464\u60E3\u8488\u4504\u84BE\u84E1\u84F8\u8510\u8538\u8552\u453B\u856F\u8570\u85E0\u4577\u8672\u8692\u86B2\u86EF\u9645\u878B\u4606\u4617\u88AE\u88FF\u8924\u8947\u8991\u{27967}\u8A29\u8A38\u8A94\u8AB4\u8C51\u8CD4\u8CF2\u8D1C\u4798\u585F\u8DC3\u47ED\u4EEE\u8E3A\u55D8\u5754\u8E71\u55F5\u8EB0\u4837\u8ECE\u8EE2\u8EE4\u8EED\u8EF2\u8FB7\u8FC1\u8FCA\u8FCC\u9033\u99C4\u48AD\u98E0\u9213\u491E\u9228\u9258\u926B\u92B1\u92AE\u92BF"],["9a40","\u92E3\u92EB\u92F3\u92F4\u92FD\u9343\u9384\u93AD\u4945\u4951\u9EBF\u9417\u5301\u941D\u942D\u943E\u496A\u9454\u9479\u952D\u95A2\u49A7\u95F4\u9633\u49E5\u67A0\u4A24\u9740\u4A35\u97B2\u97C2\u5654\u4AE4\u60E8\u98B9\u4B19\u98F1\u5844\u990E\u9919\u51B4\u991C\u9937\u9942\u995D\u9962\u4B70\u99C5\u4B9D\u9A3C\u9B0F\u7A83\u9B69\u9B81\u9BDD\u9BF1\u9BF4\u4C6D\u9C20\u376F\u{21BC2}\u9D49\u9C3A"],["9aa1","\u9EFE\u5650\u9D93\u9DBD\u9DC0\u9DFC\u94F6\u8FB6\u9E7B\u9EAC\u9EB1\u9EBD\u9EC6\u94DC\u9EE2\u9EF1\u9EF8\u7AC8\u9F44\u{20094}\u{202B7}\u{203A0}\u691A\u94C3\u59AC\u{204D7}\u5840\u94C1\u37B9\u{205D5}\u{20615}\u{20676}\u{216BA}\u5757\u7173\u{20AC2}\u{20ACD}\u{20BBF}\u546A\u{2F83B}\u{20BCB}\u549E\u{20BFB}\u{20C3B}\u{20C53}\u{20C65}\u{20C7C}\u60E7\u{20C8D}\u567A\u{20CB5}\u{20CDD}\u{20CED}\u{20D6F}\u{20DB2}\u{20DC8}\u6955\u9C2F\u87A5\u{20E04}\u{20E0E}\u{20ED7}\u{20F90}\u{20F2D}\u{20E73}\u5C20\u{20FBC}\u5E0B\u{2105C}\u{2104F}\u{21076}\u671E\u{2107B}\u{21088}\u{21096}\u3647\u{210BF}\u{210D3}\u{2112F}\u{2113B}\u5364\u84AD\u{212E3}\u{21375}\u{21336}\u8B81\u{21577}\u{21619}\u{217C3}\u{217C7}\u4E78\u70BB\u{2182D}\u{2196A}"],["9b40","\u{21A2D}\u{21A45}\u{21C2A}\u{21C70}\u{21CAC}\u{21EC8}\u62C3\u{21ED5}\u{21F15}\u7198\u6855\u{22045}\u69E9\u36C8\u{2227C}\u{223D7}\u{223FA}\u{2272A}\u{22871}\u{2294F}\u82FD\u{22967}\u{22993}\u{22AD5}\u89A5\u{22AE8}\u8FA0\u{22B0E}\u97B8\u{22B3F}\u9847\u9ABD\u{22C4C}"],["9b62","\u{22C88}\u{22CB7}\u{25BE8}\u{22D08}\u{22D12}\u{22DB7}\u{22D95}\u{22E42}\u{22F74}\u{22FCC}\u{23033}\u{23066}\u{2331F}\u{233DE}\u5FB1\u6648\u66BF\u{27A79}\u{23567}\u{235F3}\u7201\u{249BA}\u77D7\u{2361A}\u{23716}\u7E87\u{20346}\u58B5\u670E"],["9ba1","\u6918\u{23AA7}\u{27657}\u{25FE2}\u{23E11}\u{23EB9}\u{275FE}\u{2209A}\u48D0\u4AB8\u{24119}\u{28A9A}\u{242EE}\u{2430D}\u{2403B}\u{24334}\u{24396}\u{24A45}\u{205CA}\u51D2\u{20611}\u599F\u{21EA8}\u3BBE\u{23CFF}\u{24404}\u{244D6}\u5788\u{24674}\u399B\u{2472F}\u{285E8}\u{299C9}\u3762\u{221C3}\u8B5E\u{28B4E}\u99D6\u{24812}\u{248FB}\u{24A15}\u7209\u{24AC0}\u{20C78}\u5965\u{24EA5}\u{24F86}\u{20779}\u8EDA\u{2502C}\u528F\u573F\u7171\u{25299}\u{25419}\u{23F4A}\u{24AA7}\u55BC\u{25446}\u{2546E}\u{26B52}\u91D4\u3473\u{2553F}\u{27632}\u{2555E}\u4718\u{25562}\u{25566}\u{257C7}\u{2493F}\u{2585D}\u5066\u34FB\u{233CC}\u60DE\u{25903}\u477C\u{28948}\u{25AAE}\u{25B89}\u{25C06}\u{21D90}\u57A1\u7151\u6FB6\u{26102}\u{27C12}\u9056\u{261B2}\u{24F9A}\u8B62\u{26402}\u{2644A}"],["9c40","\u5D5B\u{26BF7}\u8F36\u{26484}\u{2191C}\u8AEA\u{249F6}\u{26488}\u{23FEF}\u{26512}\u4BC0\u{265BF}\u{266B5}\u{2271B}\u9465\u{257E1}\u6195\u5A27\u{2F8CD}\u4FBB\u56B9\u{24521}\u{266FC}\u4E6A\u{24934}\u9656\u6D8F\u{26CBD}\u3618\u8977\u{26799}\u{2686E}\u{26411}\u{2685E}\u71DF\u{268C7}\u7B42\u{290C0}\u{20A11}\u{26926}\u9104\u{26939}\u7A45\u9DF0\u{269FA}\u9A26\u{26A2D}\u365F\u{26469}\u{20021}\u7983\u{26A34}\u{26B5B}\u5D2C\u{23519}\u83CF\u{26B9D}\u46D0\u{26CA4}\u753B\u8865\u{26DAE}\u58B6"],["9ca1","\u371C\u{2258D}\u{2704B}\u{271CD}\u3C54\u{27280}\u{27285}\u9281\u{2217A}\u{2728B}\u9330\u{272E6}\u{249D0}\u6C39\u949F\u{27450}\u{20EF8}\u8827\u88F5\u{22926}\u{28473}\u{217B1}\u6EB8\u{24A2A}\u{21820}\u39A4\u36B9\u5C10\u79E3\u453F\u66B6\u{29CAD}\u{298A4}\u8943\u{277CC}\u{27858}\u56D6\u40DF\u{2160A}\u39A1\u{2372F}\u{280E8}\u{213C5}\u71AD\u8366\u{279DD}\u{291A8}\u5A67\u4CB7\u{270AF}\u{289AB}\u{279FD}\u{27A0A}\u{27B0B}\u{27D66}\u{2417A}\u7B43\u797E\u{28009}\u6FB5\u{2A2DF}\u6A03\u{28318}\u53A2\u{26E07}\u93BF\u6836\u975D\u{2816F}\u{28023}\u{269B5}\u{213ED}\u{2322F}\u{28048}\u5D85\u{28C30}\u{28083}\u5715\u9823\u{28949}\u5DAB\u{24988}\u65BE\u69D5\u53D2\u{24AA5}\u{23F81}\u3C11\u6736\u{28090}\u{280F4}\u{2812E}\u{21FA1}\u{2814F}"],["9d40","\u{28189}\u{281AF}\u{2821A}\u{28306}\u{2832F}\u{2838A}\u35CA\u{28468}\u{286AA}\u48FA\u63E6\u{28956}\u7808\u9255\u{289B8}\u43F2\u{289E7}\u43DF\u{289E8}\u{28B46}\u{28BD4}\u59F8\u{28C09}\u8F0B\u{28FC5}\u{290EC}\u7B51\u{29110}\u{2913C}\u3DF7\u{2915E}\u{24ACA}\u8FD0\u728F\u568B\u{294E7}\u{295E9}\u{295B0}\u{295B8}\u{29732}\u{298D1}\u{29949}\u{2996A}\u{299C3}\u{29A28}\u{29B0E}\u{29D5A}\u{29D9B}\u7E9F\u{29EF8}\u{29F23}\u4CA4\u9547\u{2A293}\u71A2\u{2A2FF}\u4D91\u9012\u{2A5CB}\u4D9C\u{20C9C}\u8FBE\u55C1"],["9da1","\u8FBA\u{224B0}\u8FB9\u{24A93}\u4509\u7E7F\u6F56\u6AB1\u4EEA\u34E4\u{28B2C}\u{2789D}\u373A\u8E80\u{217F5}\u{28024}\u{28B6C}\u{28B99}\u{27A3E}\u{266AF}\u3DEB\u{27655}\u{23CB7}\u{25635}\u{25956}\u4E9A\u{25E81}\u{26258}\u56BF\u{20E6D}\u8E0E\u5B6D\u{23E88}\u{24C9E}\u63DE\u62D0\u{217F6}\u{2187B}\u6530\u562D\u{25C4A}\u541A\u{25311}\u3DC6\u{29D98}\u4C7D\u5622\u561E\u7F49\u{25ED8}\u5975\u{23D40}\u8770\u4E1C\u{20FEA}\u{20D49}\u{236BA}\u8117\u9D5E\u8D18\u763B\u9C45\u764E\u77B9\u9345\u5432\u8148\u82F7\u5625\u8132\u8418\u80BD\u55EA\u7962\u5643\u5416\u{20E9D}\u35CE\u5605\u55F1\u66F1\u{282E2}\u362D\u7534\u55F0\u55BA\u5497\u5572\u{20C41}\u{20C96}\u5ED0\u{25148}\u{20E76}\u{22C62}"],["9e40","\u{20EA2}\u9EAB\u7D5A\u55DE\u{21075}\u629D\u976D\u5494\u8CCD\u71F6\u9176\u63FC\u63B9\u63FE\u5569\u{22B43}\u9C72\u{22EB3}\u519A\u34DF\u{20DA7}\u51A7\u544D\u551E\u5513\u7666\u8E2D\u{2688A}\u75B1\u80B6\u8804\u8786\u88C7\u81B6\u841C\u{210C1}\u44EC\u7304\u{24706}\u5B90\u830B\u{26893}\u567B\u{226F4}\u{27D2F}\u{241A3}\u{27D73}\u{26ED0}\u{272B6}\u9170\u{211D9}\u9208\u{23CFC}\u{2A6A9}\u{20EAC}\u{20EF9}\u7266\u{21CA2}\u474E\u{24FC2}\u{27FF9}\u{20FEB}\u40FA"],["9ea1","\u9C5D\u651F\u{22DA0}\u48F3\u{247E0}\u{29D7C}\u{20FEC}\u{20E0A}\u6062\u{275A3}\u{20FED}"],["9ead","\u{26048}\u{21187}\u71A3\u7E8E\u9D50\u4E1A\u4E04\u3577\u5B0D\u6CB2\u5367\u36AC\u39DC\u537D\u36A5\u{24618}\u589A\u{24B6E}\u822D\u544B\u57AA\u{25A95}\u{20979}"],["9ec5","\u3A52\u{22465}\u7374\u{29EAC}\u4D09\u9BED\u{23CFE}\u{29F30}\u4C5B\u{24FA9}\u{2959E}\u{29FDE}\u845C\u{23DB6}\u{272B2}\u{267B3}\u{23720}\u632E\u7D25\u{23EF7}\u{23E2C}\u3A2A\u9008\u52CC\u3E74\u367A\u45E9\u{2048E}\u7640\u5AF0\u{20EB6}\u787A\u{27F2E}\u58A7\u40BF\u567C\u9B8B\u5D74\u7654\u{2A434}\u9E85\u4CE1\u75F9\u37FB\u6119\u{230DA}\u{243F2}"],["9ef5","\u565D\u{212A9}\u57A7\u{24963}\u{29E06}\u5234\u{270AE}\u35AD\u6C4A\u9D7C"],["9f40","\u7C56\u9B39\u57DE\u{2176C}\u5C53\u64D3\u{294D0}\u{26335}\u{27164}\u86AD\u{20D28}\u{26D22}\u{24AE2}\u{20D71}"],["9f4f","\u51FE\u{21F0F}\u5D8E\u9703\u{21DD1}\u9E81\u904C\u7B1F\u9B02\u5CD1\u7BA3\u6268\u6335\u9AFF\u7BCF\u9B2A\u7C7E\u9B2E\u7C42\u7C86\u9C15\u7BFC\u9B09\u9F17\u9C1B\u{2493E}\u9F5A\u5573\u5BC3\u4FFD\u9E98\u4FF2\u5260\u3E06\u52D1\u5767\u5056\u59B7\u5E12\u97C8\u9DAB\u8F5C\u5469\u97B4\u9940\u97BA\u532C\u6130"],["9fa1","\u692C\u53DA\u9C0A\u9D02\u4C3B\u9641\u6980\u50A6\u7546\u{2176D}\u99DA\u5273"],["9fae","\u9159\u9681\u915C"],["9fb2","\u9151\u{28E97}\u637F\u{26D23}\u6ACA\u5611\u918E\u757A\u6285\u{203FC}\u734F\u7C70\u{25C21}\u{23CFD}"],["9fc1","\u{24919}\u76D6\u9B9D\u4E2A\u{20CD4}\u83BE\u8842"],["9fc9","\u5C4A\u69C0\u50ED\u577A\u521F\u5DF5\u4ECE\u6C31\u{201F2}\u4F39\u549C\u54DA\u529A\u8D82\u35FE\u5F0C\u35F3"],["9fdb","\u6B52\u917C\u9FA5\u9B97\u982E\u98B4\u9ABA\u9EA8\u9E84\u717A\u7B14"],["9fe7","\u6BFA\u8818\u7F78"],["9feb","\u5620\u{2A64A}\u8E77\u9F53"],["9ff0","\u8DD4\u8E4F\u9E1C\u8E01\u6282\u{2837D}\u8E28\u8E75\u7AD3\u{24A77}\u7A3E\u78D8\u6CEA\u8A67\u7607"],["a040","\u{28A5A}\u9F26\u6CCE\u87D6\u75C3\u{2A2B2}\u7853\u{2F840}\u8D0C\u72E2\u7371\u8B2D\u7302\u74F1\u8CEB\u{24ABB}\u862F\u5FBA\u88A0\u44B7"],["a055","\u{2183B}\u{26E05}"],["a058","\u8A7E\u{2251B}"],["a05b","\u60FD\u7667\u9AD7\u9D44\u936E\u9B8F\u87F5"],["a063","\u880F\u8CF7\u732C\u9721\u9BB0\u35D6\u72B2\u4C07\u7C51\u994A\u{26159}\u6159\u4C04\u9E96\u617D"],["a073","\u575F\u616F\u62A6\u6239\u62CE\u3A5C\u61E2\u53AA\u{233F5}\u6364\u6802\u35D2"],["a0a1","\u5D57\u{28BC2}\u8FDA\u{28E39}"],["a0a6","\u50D9\u{21D46}\u7906\u5332\u9638\u{20F3B}\u4065"],["a0ae","\u77FE"],["a0b0","\u7CC2\u{25F1A}\u7CDA\u7A2D\u8066\u8063\u7D4D\u7505\u74F2\u8994\u821A\u670C\u8062\u{27486}\u805B\u74F0\u8103\u7724\u8989\u{267CC}\u7553\u{26ED1}\u87A9\u87CE\u81C8\u878C\u8A49\u8CAD\u8B43\u772B\u74F8\u84DA\u3635\u69B2\u8DA6"],["a0d4","\u89A9\u7468\u6DB9\u87C1\u{24011}\u74E7\u3DDB\u7176\u60A4\u619C\u3CD1\u7162\u6077"],["a0e2","\u7F71\u{28B2D}\u7250\u60E9\u4B7E\u5220\u3C18\u{23CC7}\u{25ED7}\u{27656}\u{25531}\u{21944}\u{212FE}\u{29903}\u{26DDC}\u{270AD}\u5CC1\u{261AD}\u{28A0F}\u{23677}\u{200EE}\u{26846}\u{24F0E}\u4562\u5B1F\u{2634C}\u9F50\u9EA6\u{2626B}"],["a3c0","\u2400",31,"\u2421"],["c6a1","\u2460",9,"\u2474",9,"\u2170",9,"\u4E36\u4E3F\u4E85\u4EA0\u5182\u5196\u51AB\u52F9\u5338\u5369\u53B6\u590A\u5B80\u5DDB\u2F33\u5E7F\u5EF4\u5F50\u5F61\u6534\u65E0\u7592\u7676\u8FB5\u96B6\xA8\u02C6\u30FD\u30FE\u309D\u309E\u3003\u4EDD\u3005\u3006\u3007\u30FC\uFF3B\uFF3D\u273D\u3041",23],["c740","\u3059",58,"\u30A1\u30A2\u30A3\u30A4"],["c7a1","\u30A5",81,"\u0410",5,"\u0401\u0416",4],["c840","\u041B",26,"\u0451\u0436",25,"\u21E7\u21B8\u21B9\u31CF\u{200CC}\u4E5A\u{2008A}\u5202\u4491"],["c8a1","\u9FB0\u5188\u9FB1\u{27607}"],["c8cd","\uFFE2\uFFE4\uFF07\uFF02\u3231\u2116\u2121\u309B\u309C\u2E80\u2E84\u2E86\u2E87\u2E88\u2E8A\u2E8C\u2E8D\u2E95\u2E9C\u2E9D\u2EA5\u2EA7\u2EAA\u2EAC\u2EAE\u2EB6\u2EBC\u2EBE\u2EC6\u2ECA\u2ECC\u2ECD\u2ECF\u2ED6\u2ED7\u2EDE\u2EE3"],["c8f5","\u0283\u0250\u025B\u0254\u0275\u0153\xF8\u014B\u028A\u026A"],["f9fe","\uFFED"],["fa40","\u{20547}\u92DB\u{205DF}\u{23FC5}\u854C\u42B5\u73EF\u51B5\u3649\u{24942}\u{289E4}\u9344\u{219DB}\u82EE\u{23CC8}\u783C\u6744\u62DF\u{24933}\u{289AA}\u{202A0}\u{26BB3}\u{21305}\u4FAB\u{224ED}\u5008\u{26D29}\u{27A84}\u{23600}\u{24AB1}\u{22513}\u5029\u{2037E}\u5FA4\u{20380}\u{20347}\u6EDB\u{2041F}\u507D\u5101\u347A\u510E\u986C\u3743\u8416\u{249A4}\u{20487}\u5160\u{233B4}\u516A\u{20BFF}\u{220FC}\u{202E5}\u{22530}\u{2058E}\u{23233}\u{21983}\u5B82\u877D\u{205B3}\u{23C99}\u51B2\u51B8"],["faa1","\u9D34\u51C9\u51CF\u51D1\u3CDC\u51D3\u{24AA6}\u51B3\u51E2\u5342\u51ED\u83CD\u693E\u{2372D}\u5F7B\u520B\u5226\u523C\u52B5\u5257\u5294\u52B9\u52C5\u7C15\u8542\u52E0\u860D\u{26B13}\u5305\u{28ADE}\u5549\u6ED9\u{23F80}\u{20954}\u{23FEC}\u5333\u5344\u{20BE2}\u6CCB\u{21726}\u681B\u73D5\u604A\u3EAA\u38CC\u{216E8}\u71DD\u44A2\u536D\u5374\u{286AB}\u537E\u537F\u{21596}\u{21613}\u77E6\u5393\u{28A9B}\u53A0\u53AB\u53AE\u73A7\u{25772}\u3F59\u739C\u53C1\u53C5\u6C49\u4E49\u57FE\u53D9\u3AAB\u{20B8F}\u53E0\u{23FEB}\u{22DA3}\u53F6\u{20C77}\u5413\u7079\u552B\u6657\u6D5B\u546D\u{26B53}\u{20D74}\u555D\u548F\u54A4\u47A6\u{2170D}\u{20EDD}\u3DB4\u{20D4D}"],["fb40","\u{289BC}\u{22698}\u5547\u4CED\u542F\u7417\u5586\u55A9\u5605\u{218D7}\u{2403A}\u4552\u{24435}\u66B3\u{210B4}\u5637\u66CD\u{2328A}\u66A4\u66AD\u564D\u564F\u78F1\u56F1\u9787\u53FE\u5700\u56EF\u56ED\u{28B66}\u3623\u{2124F}\u5746\u{241A5}\u6C6E\u708B\u5742\u36B1\u{26C7E}\u57E6\u{21416}\u5803\u{21454}\u{24363}\u5826\u{24BF5}\u585C\u58AA\u3561\u58E0\u58DC\u{2123C}\u58FB\u5BFF\u5743\u{2A150}\u{24278}\u93D3\u35A1\u591F\u68A6\u36C3\u6E59"],["fba1","\u{2163E}\u5A24\u5553\u{21692}\u8505\u59C9\u{20D4E}\u{26C81}\u{26D2A}\u{217DC}\u59D9\u{217FB}\u{217B2}\u{26DA6}\u6D71\u{21828}\u{216D5}\u59F9\u{26E45}\u5AAB\u5A63\u36E6\u{249A9}\u5A77\u3708\u5A96\u7465\u5AD3\u{26FA1}\u{22554}\u3D85\u{21911}\u3732\u{216B8}\u5E83\u52D0\u5B76\u6588\u5B7C\u{27A0E}\u4004\u485D\u{20204}\u5BD5\u6160\u{21A34}\u{259CC}\u{205A5}\u5BF3\u5B9D\u4D10\u5C05\u{21B44}\u5C13\u73CE\u5C14\u{21CA5}\u{26B28}\u5C49\u48DD\u5C85\u5CE9\u5CEF\u5D8B\u{21DF9}\u{21E37}\u5D10\u5D18\u5D46\u{21EA4}\u5CBA\u5DD7\u82FC\u382D\u{24901}\u{22049}\u{22173}\u8287\u3836\u3BC2\u5E2E\u6A8A\u5E75\u5E7A\u{244BC}\u{20CD3}\u53A6\u4EB7\u5ED0\u53A8\u{21771}\u5E09\u5EF4\u{28482}"],["fc40","\u5EF9\u5EFB\u38A0\u5EFC\u683E\u941B\u5F0D\u{201C1}\u{2F894}\u3ADE\u48AE\u{2133A}\u5F3A\u{26888}\u{223D0}\u5F58\u{22471}\u5F63\u97BD\u{26E6E}\u5F72\u9340\u{28A36}\u5FA7\u5DB6\u3D5F\u{25250}\u{21F6A}\u{270F8}\u{22668}\u91D6\u{2029E}\u{28A29}\u6031\u6685\u{21877}\u3963\u3DC7\u3639\u5790\u{227B4}\u7971\u3E40\u609E\u60A4\u60B3\u{24982}\u{2498F}\u{27A53}\u74A4\u50E1\u5AA0\u6164\u8424\u6142\u{2F8A6}\u{26ED2}\u6181\u51F4\u{20656}\u6187\u5BAA\u{23FB7}"],["fca1","\u{2285F}\u61D3\u{28B9D}\u{2995D}\u61D0\u3932\u{22980}\u{228C1}\u6023\u615C\u651E\u638B\u{20118}\u62C5\u{21770}\u62D5\u{22E0D}\u636C\u{249DF}\u3A17\u6438\u63F8\u{2138E}\u{217FC}\u6490\u6F8A\u{22E36}\u9814\u{2408C}\u{2571D}\u64E1\u64E5\u947B\u3A66\u643A\u3A57\u654D\u6F16\u{24A28}\u{24A23}\u6585\u656D\u655F\u{2307E}\u65B5\u{24940}\u4B37\u65D1\u40D8\u{21829}\u65E0\u65E3\u5FDF\u{23400}\u6618\u{231F7}\u{231F8}\u6644\u{231A4}\u{231A5}\u664B\u{20E75}\u6667\u{251E6}\u6673\u6674\u{21E3D}\u{23231}\u{285F4}\u{231C8}\u{25313}\u77C5\u{228F7}\u99A4\u6702\u{2439C}\u{24A21}\u3B2B\u69FA\u{237C2}\u675E\u6767\u6762\u{241CD}\u{290ED}\u67D7\u44E9\u6822\u6E50\u923C\u6801\u{233E6}\u{26DA0}\u685D"],["fd40","\u{2346F}\u69E1\u6A0B\u{28ADF}\u6973\u68C3\u{235CD}\u6901\u6900\u3D32\u3A01\u{2363C}\u3B80\u67AC\u6961\u{28A4A}\u42FC\u6936\u6998\u3BA1\u{203C9}\u8363\u5090\u69F9\u{23659}\u{2212A}\u6A45\u{23703}\u6A9D\u3BF3\u67B1\u6AC8\u{2919C}\u3C0D\u6B1D\u{20923}\u60DE\u6B35\u6B74\u{227CD}\u6EB5\u{23ADB}\u{203B5}\u{21958}\u3740\u5421\u{23B5A}\u6BE1\u{23EFC}\u6BDC\u6C37\u{2248B}\u{248F1}\u{26B51}\u6C5A\u8226\u6C79\u{23DBC}\u44C5\u{23DBD}\u{241A4}\u{2490C}\u{24900}"],["fda1","\u{23CC9}\u36E5\u3CEB\u{20D32}\u9B83\u{231F9}\u{22491}\u7F8F\u6837\u{26D25}\u{26DA1}\u{26DEB}\u6D96\u6D5C\u6E7C\u6F04\u{2497F}\u{24085}\u{26E72}\u8533\u{26F74}\u51C7\u6C9C\u6E1D\u842E\u{28B21}\u6E2F\u{23E2F}\u7453\u{23F82}\u79CC\u6E4F\u5A91\u{2304B}\u6FF8\u370D\u6F9D\u{23E30}\u6EFA\u{21497}\u{2403D}\u4555\u93F0\u6F44\u6F5C\u3D4E\u6F74\u{29170}\u3D3B\u6F9F\u{24144}\u6FD3\u{24091}\u{24155}\u{24039}\u{23FF0}\u{23FB4}\u{2413F}\u51DF\u{24156}\u{24157}\u{24140}\u{261DD}\u704B\u707E\u70A7\u7081\u70CC\u70D5\u70D6\u70DF\u4104\u3DE8\u71B4\u7196\u{24277}\u712B\u7145\u5A88\u714A\u716E\u5C9C\u{24365}\u714F\u9362\u{242C1}\u712C\u{2445A}\u{24A27}\u{24A22}\u71BA\u{28BE8}\u70BD\u720E"],["fe40","\u9442\u7215\u5911\u9443\u7224\u9341\u{25605}\u722E\u7240\u{24974}\u68BD\u7255\u7257\u3E55\u{23044}\u680D\u6F3D\u7282\u732A\u732B\u{24823}\u{2882B}\u48ED\u{28804}\u7328\u732E\u73CF\u73AA\u{20C3A}\u{26A2E}\u73C9\u7449\u{241E2}\u{216E7}\u{24A24}\u6623\u36C5\u{249B7}\u{2498D}\u{249FB}\u73F7\u7415\u6903\u{24A26}\u7439\u{205C3}\u3ED7\u745C\u{228AD}\u7460\u{28EB2}\u7447\u73E4\u7476\u83B9\u746C\u3730\u7474\u93F1\u6A2C\u7482\u4953\u{24A8C}"],["fea1","\u{2415F}\u{24A79}\u{28B8F}\u5B46\u{28C03}\u{2189E}\u74C8\u{21988}\u750E\u74E9\u751E\u{28ED9}\u{21A4B}\u5BD7\u{28EAC}\u9385\u754D\u754A\u7567\u756E\u{24F82}\u3F04\u{24D13}\u758E\u745D\u759E\u75B4\u7602\u762C\u7651\u764F\u766F\u7676\u{263F5}\u7690\u81EF\u37F8\u{26911}\u{2690E}\u76A1\u76A5\u76B7\u76CC\u{26F9F}\u8462\u{2509D}\u{2517D}\u{21E1C}\u771E\u7726\u7740\u64AF\u{25220}\u7758\u{232AC}\u77AF\u{28964}\u{28968}\u{216C1}\u77F4\u7809\u{21376}\u{24A12}\u68CA\u78AF\u78C7\u78D3\u96A5\u792E\u{255E0}\u78D7\u7934\u78B1\u{2760C}\u8FB8\u8884\u{28B2B}\u{26083}\u{2261C}\u7986\u8900\u6902\u7980\u{25857}\u799D\u{27B39}\u793C\u79A9\u6E2A\u{27126}\u3EA8\u79C6\u{2910D}\u79D4"]]});var Jje=S((Lkr,Yje)=>{"use strict";Yje.exports={shiftjis:{type:"_dbcs",table:function(){return Vje()},encodeAdd:{"\xA5":92,"\u203E":126},encodeSkipVals:[{from:60736,to:63808}]},csshiftjis:"shiftjis",mskanji:"shiftjis",sjis:"shiftjis",windows31j:"shiftjis",ms31j:"shiftjis",xsjis:"shiftjis",windows932:"shiftjis",ms932:"shiftjis",932:"shiftjis",cp932:"shiftjis",eucjp:{type:"_dbcs",table:function(){return Hje()},encodeAdd:{"\xA5":92,"\u203E":126}},gb2312:"cp936",gb231280:"cp936",gb23121980:"cp936",csgb2312:"cp936",csiso58gb231280:"cp936",euccn:"cp936",windows936:"cp936",ms936:"cp936",936:"cp936",cp936:{type:"_dbcs",table:function(){return vD()}},gbk:{type:"_dbcs",table:function(){return vD().concat(bK())}},xgbk:"gbk",isoir58:"gbk",gb18030:{type:"_dbcs",table:function(){return vD().concat(bK())},gb18030:function(){return Wje()},encodeSkipVals:[128],encodeAdd:{"\u20AC":41699}},chinese:"gb18030",windows949:"cp949",ms949:"cp949",949:"cp949",cp949:{type:"_dbcs",table:function(){return Kje()}},cseuckr:"cp949",csksc56011987:"cp949",euckr:"cp949",isoir149:"cp949",korean:"cp949",ksc56011987:"cp949",ksc56011989:"cp949",ksc5601:"cp949",windows950:"cp950",ms950:"cp950",950:"cp950",cp950:{type:"_dbcs",table:function(){return xK()}},big5:"big5hkscs",big5hkscs:{type:"_dbcs",table:function(){return xK().concat(Zje())},encodeSkipVals:[36457,36463,36478,36523,36532,36557,36560,36695,36713,36718,36811,36862,36973,36986,37060,37084,37105,37311,37551,37552,37553,37554,37585,37959,38090,38361,38652,39285,39798,39800,39803,39878,39902,39916,39926,40002,40019,40034,40040,40043,40055,40124,40125,40144,40279,40282,40388,40431,40443,40617,40687,40701,40800,40907,41079,41180,41183,36812,37576,38468,38637,41636,41637,41639,41638,41676,41678]},cnbig5:"big5hkscs",csbig5:"big5hkscs",xxbig5:"big5hkscs"}});var eze=S((Qje,yD)=>{"use strict";var Uqt=VW(),Xje=[Rje(),Oje(),$je(),Mje(),Lje(),jje(),Fje(),Gje(),Jje()];for(SD=0;SD{"use strict";var tze=Ll().Buffer;rze.exports=function(t){var e=t.Transform;function r(o,i){this.conv=o,i=i||{},i.decodeStrings=!1,e.call(this,i)}r.prototype=Object.create(e.prototype,{constructor:{value:r}}),r.prototype._transform=function(o,i,s){if(typeof o!="string")return s(new Error("Iconv encoding stream needs strings as its input."));try{var a=this.conv.write(o);a&&a.length&&this.push(a),s()}catch(c){s(c)}},r.prototype._flush=function(o){try{var i=this.conv.end();i&&i.length&&this.push(i),o()}catch(s){o(s)}},r.prototype.collect=function(o){var i=[];return this.on("error",o),this.on("data",function(s){i.push(s)}),this.on("end",function(){o(null,tze.concat(i))}),this};function n(o,i){this.conv=o,i=i||{},i.encoding=this.encoding="utf8",e.call(this,i)}return n.prototype=Object.create(e.prototype,{constructor:{value:n}}),n.prototype._transform=function(o,i,s){if(!tze.isBuffer(o)&&!(o instanceof Uint8Array))return s(new Error("Iconv decoding stream needs buffers as its input."));try{var a=this.conv.write(o);a&&a.length&&this.push(a,this.encoding),s()}catch(c){s(c)}},n.prototype._flush=function(o){try{var i=this.conv.end();i&&i.length&&this.push(i,this.encoding),o()}catch(s){o(s)}},n.prototype.collect=function(o){var i="";return this.on("error",o),this.on("data",function(s){i+=s}),this.on("end",function(){o(null,i)}),this},{IconvLiteEncoderStream:r,IconvLiteDecoderStream:n}}});var AK=S((jkr,sze)=>{"use strict";var oze=Ll().Buffer,ize=xje(),jqt=VW(),mt=sze.exports;mt.encodings=null;mt.defaultCharUnicode="\uFFFD";mt.defaultCharSingleByte="?";mt.encode=function(e,r,n){e=""+(e||"");var o=mt.getEncoder(r,n),i=o.write(e),s=o.end();return s&&s.length>0?oze.concat([i,s]):i};mt.decode=function(e,r,n){typeof e=="string"&&(mt.skipDecodeWarning||(console.error("Iconv-lite warning: decode()-ing strings is deprecated. Refer to https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding"),mt.skipDecodeWarning=!0),e=oze.from(""+(e||""),"binary"));var o=mt.getDecoder(r,n),i=o.write(e),s=o.end();return s?i+s:i};mt.encodingExists=function(e){try{return mt.getCodec(e),!0}catch{return!1}};mt.toEncoding=mt.encode;mt.fromEncoding=mt.decode;mt._codecDataCache={__proto__:null};mt.getCodec=function(e){if(!mt.encodings){var r=eze();mt.encodings={__proto__:null},jqt(mt.encodings,r)}for(var n=mt._canonicalizeEncoding(e),o={};;){var i=mt._codecDataCache[n];if(i)return i;var s=mt.encodings[n];switch(typeof s){case"string":n=s;break;case"object":for(var a in s)o[a]=s[a];o.encodingName||(o.encodingName=n),n=s.type;break;case"function":return o.encodingName||(o.encodingName=n),i=new s(o,mt),mt._codecDataCache[o.encodingName]=i,i;default:throw new Error("Encoding not recognized: '"+e+"' (searched as: '"+n+"')")}}};mt._canonicalizeEncoding=function(t){return(""+t).toLowerCase().replace(/:\d{4}$|[^0-9a-z]/g,"")};mt.getEncoder=function(e,r){var n=mt.getCodec(e),o=new n.encoder(r,n);return n.bomAware&&r&&r.addBOM&&(o=new ize.PrependBOM(o,r)),o};mt.getDecoder=function(e,r){var n=mt.getCodec(e),o=new n.decoder(r,n);return n.bomAware&&!(r&&r.stripBOM===!1)&&(o=new ize.StripBOM(o,r)),o};mt.enableStreamingAPI=function(e){if(!mt.supportsStreams){var r=nze()(e);mt.IconvLiteEncoderStream=r.IconvLiteEncoderStream,mt.IconvLiteDecoderStream=r.IconvLiteDecoderStream,mt.encodeStream=function(o,i){return new mt.IconvLiteEncoderStream(mt.getEncoder(o,i),i)},mt.decodeStream=function(o,i){return new mt.IconvLiteDecoderStream(mt.getDecoder(o,i),i)},mt.supportsStreams=!0}};var ED;try{ED=require("stream")}catch{}ED&&ED.Transform?mt.enableStreamingAPI(ED):mt.encodeStream=mt.decodeStream=function(){throw new Error("iconv-lite Streaming API is not enabled. Use iconv.enableStreamingAPI(require('stream')); to enable it.")}});var cze=S((zkr,aze)=>{"use strict";aze.exports=Fqt;function zqt(t){for(var e=t.listeners("data"),r=0;r{"use strict";var uze=Zqt(),qqt=FW(),Fm=sS(),Bqt=AK(),Gqt=cze();pze.exports=Wqt;var Vqt=/^Encoding not recognized: /;function Hqt(t){if(!t)return null;try{return Bqt.getDecoder(t)}catch(e){throw Vqt.test(e.message)?Fm(415,"specified encoding unsupported",{encoding:t,type:"encoding.unsupported"}):e}}function Wqt(t,e,r){var n=r,o=e||{};if(t===void 0)throw new TypeError("argument stream is required");if(typeof t!="object"||t===null||typeof t.on!="function")throw new TypeError("argument stream must be a stream");if((e===!0||typeof e=="string")&&(o={encoding:e}),typeof e=="function"&&(n=e,o={}),n!==void 0&&typeof n!="function")throw new TypeError("argument callback must be a function");if(!n&&!global.Promise)throw new TypeError("argument callback is required");var i=o.encoding!==!0?o.encoding:"utf-8",s=qqt.parse(o.limit),a=o.length!=null&&!isNaN(o.length)?parseInt(o.length,10):null;return n?lze(t,i,a,s,Yqt(n)):new Promise(function(u,p){lze(t,i,a,s,function(m,h){if(m)return p(m);u(h)})})}function Kqt(t){Gqt(t),typeof t.pause=="function"&&t.pause()}function lze(t,e,r,n,o){var i=!1,s=!0;if(n!==null&&r!==null&&r>n)return f(Fm(413,"request entity too large",{expected:r,length:r,limit:n,type:"entity.too.large"}));var a=t._readableState;if(t._decoder||a&&(a.encoding||a.decoder))return f(Fm(500,"stream encoding should not be set",{type:"stream.encoding.set"}));if(typeof t.readable<"u"&&!t.readable)return f(Fm(500,"stream is not readable",{type:"stream.not.readable"}));var c=0,u;try{u=Hqt(e)}catch(E){return f(E)}var p=u?"":[];t.on("aborted",m),t.on("close",v),t.on("data",h),t.on("end",_),t.on("error",_),s=!1;function f(){for(var E=new Array(arguments.length),x=0;xn?f(Fm(413,"request entity too large",{limit:n,received:c,type:"entity.too.large"})):u?p+=u.write(E):p.push(E))}function _(E){if(!i){if(E)return f(E);if(r!==null&&c!==r)f(Fm(400,"request size did not match content length",{expected:r,length:r,received:c,type:"request.size.invalid"}));else{var x=u?p+(u.end()||""):Buffer.concat(p);f(null,x)}}}function v(){p=null,t.removeListener("aborted",m),t.removeListener("data",h),t.removeListener("end",_),t.removeListener("error",_),t.removeListener("close",v)}}function Zqt(){try{return require("async_hooks")}catch{return{}}}function Yqt(t){var e;return uze.AsyncResource&&(e=new uze.AsyncResource(t.name||"bound-anonymous-fn")),!e||!e.runInAsyncScope?t:e.runInAsyncScope.bind(e,t,null)}});var mze=S((qkr,fze)=>{"use strict";fze.exports=Jqt;function Jqt(t,e){if(!Array.isArray(t))throw new TypeError("arg must be an array of [ee, events...] arrays");for(var r=[],n=0;n{"use strict";wK.exports=e6t;wK.exports.isFinished=_ze;var hze=i6t(),gze=mze(),Qqt=typeof setImmediate=="function"?setImmediate:function(t){process.nextTick(t.bind.apply(t,arguments))};function e6t(t,e){return _ze(t)!==!1?(Qqt(e,null,t),t):(r6t(t,s6t(e)),t)}function _ze(t){var e=t.socket;if(typeof t.finished=="boolean")return!!(t.finished||e&&!e.writable);if(typeof t.complete=="boolean")return!!(t.upgrade||!e||!e.readable||t.complete&&!t.readable)}function t6t(t,e){var r,n,o=!1;function i(a){r.cancel(),n.cancel(),o=!0,e(a)}r=n=gze([[t,"end","finish"]],i);function s(a){t.removeListener("socket",s),!o&&r===n&&(n=gze([[a,"error","close"]],i))}if(t.socket){s(t.socket);return}t.on("socket",s),t.socket===void 0&&o6t(t,s)}function r6t(t,e){var r=t.__onFinished;(!r||!r.queue)&&(r=t.__onFinished=n6t(t),t6t(t,r)),r.queue.push(e)}function n6t(t){function e(r){if(t.__onFinished===e&&(t.__onFinished=null),!!e.queue){var n=e.queue;e.queue=null;for(var o=0;o{"use strict";var vze=/; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g,a6t=/^[\u000b\u0020-\u007e\u0080-\u00ff]+$/,Sze=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/,c6t=/\\([\u000b\u0020-\u00ff])/g,u6t=/([\\"])/g,yze=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;RK.format=l6t;RK.parse=p6t;function l6t(t){if(!t||typeof t!="object")throw new TypeError("argument obj is required");var e=t.parameters,r=t.type;if(!r||!yze.test(r))throw new TypeError("invalid type");var n=r;if(e&&typeof e=="object")for(var o,i=Object.keys(e).sort(),s=0;s0&&!a6t.test(e))throw new TypeError("invalid parameter value");return'"'+e.replace(u6t,"\\$1")+'"'}function m6t(t){this.parameters=Object.create(null),this.type=t}});var Eze=S((Vkr,h6t)=>{h6t.exports={"application/1d-interleaved-parityfec":{source:"iana"},"application/3gpdash-qoe-report+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/3gpp-ims+xml":{source:"iana",compressible:!0},"application/3gpphal+json":{source:"iana",compressible:!0},"application/3gpphalforms+json":{source:"iana",compressible:!0},"application/a2l":{source:"iana"},"application/ace+cbor":{source:"iana"},"application/ace+json":{source:"iana",compressible:!0},"application/ace-groupcomm+cbor":{source:"iana"},"application/ace-trl+cbor":{source:"iana"},"application/activemessage":{source:"iana"},"application/activity+json":{source:"iana",compressible:!0},"application/aif+cbor":{source:"iana"},"application/aif+json":{source:"iana",compressible:!0},"application/alto-cdni+json":{source:"iana",compressible:!0},"application/alto-cdnifilter+json":{source:"iana",compressible:!0},"application/alto-costmap+json":{source:"iana",compressible:!0},"application/alto-costmapfilter+json":{source:"iana",compressible:!0},"application/alto-directory+json":{source:"iana",compressible:!0},"application/alto-endpointcost+json":{source:"iana",compressible:!0},"application/alto-endpointcostparams+json":{source:"iana",compressible:!0},"application/alto-endpointprop+json":{source:"iana",compressible:!0},"application/alto-endpointpropparams+json":{source:"iana",compressible:!0},"application/alto-error+json":{source:"iana",compressible:!0},"application/alto-networkmap+json":{source:"iana",compressible:!0},"application/alto-networkmapfilter+json":{source:"iana",compressible:!0},"application/alto-propmap+json":{source:"iana",compressible:!0},"application/alto-propmapparams+json":{source:"iana",compressible:!0},"application/alto-tips+json":{source:"iana",compressible:!0},"application/alto-tipsparams+json":{source:"iana",compressible:!0},"application/alto-updatestreamcontrol+json":{source:"iana",compressible:!0},"application/alto-updatestreamparams+json":{source:"iana",compressible:!0},"application/aml":{source:"iana"},"application/andrew-inset":{source:"iana",extensions:["ez"]},"application/appinstaller":{compressible:!1,extensions:["appinstaller"]},"application/applefile":{source:"iana"},"application/applixware":{source:"apache",extensions:["aw"]},"application/appx":{compressible:!1,extensions:["appx"]},"application/appxbundle":{compressible:!1,extensions:["appxbundle"]},"application/at+jwt":{source:"iana"},"application/atf":{source:"iana"},"application/atfx":{source:"iana"},"application/atom+xml":{source:"iana",compressible:!0,extensions:["atom"]},"application/atomcat+xml":{source:"iana",compressible:!0,extensions:["atomcat"]},"application/atomdeleted+xml":{source:"iana",compressible:!0,extensions:["atomdeleted"]},"application/atomicmail":{source:"iana"},"application/atomsvc+xml":{source:"iana",compressible:!0,extensions:["atomsvc"]},"application/atsc-dwd+xml":{source:"iana",compressible:!0,extensions:["dwd"]},"application/atsc-dynamic-event-message":{source:"iana"},"application/atsc-held+xml":{source:"iana",compressible:!0,extensions:["held"]},"application/atsc-rdt+json":{source:"iana",compressible:!0},"application/atsc-rsat+xml":{source:"iana",compressible:!0,extensions:["rsat"]},"application/atxml":{source:"iana"},"application/auth-policy+xml":{source:"iana",compressible:!0},"application/automationml-aml+xml":{source:"iana",compressible:!0,extensions:["aml"]},"application/automationml-amlx+zip":{source:"iana",compressible:!1,extensions:["amlx"]},"application/bacnet-xdd+zip":{source:"iana",compressible:!1},"application/batch-smtp":{source:"iana"},"application/bdoc":{compressible:!1,extensions:["bdoc"]},"application/beep+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/bufr":{source:"iana"},"application/c2pa":{source:"iana"},"application/calendar+json":{source:"iana",compressible:!0},"application/calendar+xml":{source:"iana",compressible:!0,extensions:["xcs"]},"application/call-completion":{source:"iana"},"application/cals-1840":{source:"iana"},"application/captive+json":{source:"iana",compressible:!0},"application/cbor":{source:"iana"},"application/cbor-seq":{source:"iana"},"application/cccex":{source:"iana"},"application/ccmp+xml":{source:"iana",compressible:!0},"application/ccxml+xml":{source:"iana",compressible:!0,extensions:["ccxml"]},"application/cda+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/cdfx+xml":{source:"iana",compressible:!0,extensions:["cdfx"]},"application/cdmi-capability":{source:"iana",extensions:["cdmia"]},"application/cdmi-container":{source:"iana",extensions:["cdmic"]},"application/cdmi-domain":{source:"iana",extensions:["cdmid"]},"application/cdmi-object":{source:"iana",extensions:["cdmio"]},"application/cdmi-queue":{source:"iana",extensions:["cdmiq"]},"application/cdni":{source:"iana"},"application/ce+cbor":{source:"iana"},"application/cea":{source:"iana"},"application/cea-2018+xml":{source:"iana",compressible:!0},"application/cellml+xml":{source:"iana",compressible:!0},"application/cfw":{source:"iana"},"application/cid-edhoc+cbor-seq":{source:"iana"},"application/city+json":{source:"iana",compressible:!0},"application/city+json-seq":{source:"iana"},"application/clr":{source:"iana"},"application/clue+xml":{source:"iana",compressible:!0},"application/clue_info+xml":{source:"iana",compressible:!0},"application/cms":{source:"iana"},"application/cnrp+xml":{source:"iana",compressible:!0},"application/coap-eap":{source:"iana"},"application/coap-group+json":{source:"iana",compressible:!0},"application/coap-payload":{source:"iana"},"application/commonground":{source:"iana"},"application/concise-problem-details+cbor":{source:"iana"},"application/conference-info+xml":{source:"iana",compressible:!0},"application/cose":{source:"iana"},"application/cose-key":{source:"iana"},"application/cose-key-set":{source:"iana"},"application/cose-x509":{source:"iana"},"application/cpl+xml":{source:"iana",compressible:!0,extensions:["cpl"]},"application/csrattrs":{source:"iana"},"application/csta+xml":{source:"iana",compressible:!0},"application/cstadata+xml":{source:"iana",compressible:!0},"application/csvm+json":{source:"iana",compressible:!0},"application/cu-seeme":{source:"apache",extensions:["cu"]},"application/cwl":{source:"iana",extensions:["cwl"]},"application/cwl+json":{source:"iana",compressible:!0},"application/cwl+yaml":{source:"iana"},"application/cwt":{source:"iana"},"application/cybercash":{source:"iana"},"application/dart":{compressible:!0},"application/dash+xml":{source:"iana",compressible:!0,extensions:["mpd"]},"application/dash-patch+xml":{source:"iana",compressible:!0,extensions:["mpp"]},"application/dashdelta":{source:"iana"},"application/davmount+xml":{source:"iana",compressible:!0,extensions:["davmount"]},"application/dca-rft":{source:"iana"},"application/dcd":{source:"iana"},"application/dec-dx":{source:"iana"},"application/dialog-info+xml":{source:"iana",compressible:!0},"application/dicom":{source:"iana",extensions:["dcm"]},"application/dicom+json":{source:"iana",compressible:!0},"application/dicom+xml":{source:"iana",compressible:!0},"application/dii":{source:"iana"},"application/dit":{source:"iana"},"application/dns":{source:"iana"},"application/dns+json":{source:"iana",compressible:!0},"application/dns-message":{source:"iana"},"application/docbook+xml":{source:"apache",compressible:!0,extensions:["dbk"]},"application/dots+cbor":{source:"iana"},"application/dpop+jwt":{source:"iana"},"application/dskpp+xml":{source:"iana",compressible:!0},"application/dssc+der":{source:"iana",extensions:["dssc"]},"application/dssc+xml":{source:"iana",compressible:!0,extensions:["xdssc"]},"application/dvcs":{source:"iana"},"application/eat+cwt":{source:"iana"},"application/eat+jwt":{source:"iana"},"application/eat-bun+cbor":{source:"iana"},"application/eat-bun+json":{source:"iana",compressible:!0},"application/eat-ucs+cbor":{source:"iana"},"application/eat-ucs+json":{source:"iana",compressible:!0},"application/ecmascript":{source:"apache",compressible:!0,extensions:["ecma"]},"application/edhoc+cbor-seq":{source:"iana"},"application/edi-consent":{source:"iana"},"application/edi-x12":{source:"iana",compressible:!1},"application/edifact":{source:"iana",compressible:!1},"application/efi":{source:"iana"},"application/elm+json":{source:"iana",charset:"UTF-8",compressible:!0},"application/elm+xml":{source:"iana",compressible:!0},"application/emergencycalldata.cap+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/emergencycalldata.comment+xml":{source:"iana",compressible:!0},"application/emergencycalldata.control+xml":{source:"iana",compressible:!0},"application/emergencycalldata.deviceinfo+xml":{source:"iana",compressible:!0},"application/emergencycalldata.ecall.msd":{source:"iana"},"application/emergencycalldata.legacyesn+json":{source:"iana",compressible:!0},"application/emergencycalldata.providerinfo+xml":{source:"iana",compressible:!0},"application/emergencycalldata.serviceinfo+xml":{source:"iana",compressible:!0},"application/emergencycalldata.subscriberinfo+xml":{source:"iana",compressible:!0},"application/emergencycalldata.veds+xml":{source:"iana",compressible:!0},"application/emma+xml":{source:"iana",compressible:!0,extensions:["emma"]},"application/emotionml+xml":{source:"iana",compressible:!0,extensions:["emotionml"]},"application/encaprtp":{source:"iana"},"application/entity-statement+jwt":{source:"iana"},"application/epp+xml":{source:"iana",compressible:!0},"application/epub+zip":{source:"iana",compressible:!1,extensions:["epub"]},"application/eshop":{source:"iana"},"application/exi":{source:"iana",extensions:["exi"]},"application/expect-ct-report+json":{source:"iana",compressible:!0},"application/express":{source:"iana",extensions:["exp"]},"application/fastinfoset":{source:"iana"},"application/fastsoap":{source:"iana"},"application/fdf":{source:"iana",extensions:["fdf"]},"application/fdt+xml":{source:"iana",compressible:!0,extensions:["fdt"]},"application/fhir+json":{source:"iana",charset:"UTF-8",compressible:!0},"application/fhir+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/fido.trusted-apps+json":{compressible:!0},"application/fits":{source:"iana"},"application/flexfec":{source:"iana"},"application/font-sfnt":{source:"iana"},"application/font-tdpfr":{source:"iana",extensions:["pfr"]},"application/font-woff":{source:"iana",compressible:!1},"application/framework-attributes+xml":{source:"iana",compressible:!0},"application/geo+json":{source:"iana",compressible:!0,extensions:["geojson"]},"application/geo+json-seq":{source:"iana"},"application/geopackage+sqlite3":{source:"iana"},"application/geopose+json":{source:"iana",compressible:!0},"application/geoxacml+json":{source:"iana",compressible:!0},"application/geoxacml+xml":{source:"iana",compressible:!0},"application/gltf-buffer":{source:"iana"},"application/gml+xml":{source:"iana",compressible:!0,extensions:["gml"]},"application/gnap-binding-jws":{source:"iana"},"application/gnap-binding-jwsd":{source:"iana"},"application/gnap-binding-rotation-jws":{source:"iana"},"application/gnap-binding-rotation-jwsd":{source:"iana"},"application/gpx+xml":{source:"apache",compressible:!0,extensions:["gpx"]},"application/grib":{source:"iana"},"application/gxf":{source:"apache",extensions:["gxf"]},"application/gzip":{source:"iana",compressible:!1,extensions:["gz"]},"application/h224":{source:"iana"},"application/held+xml":{source:"iana",compressible:!0},"application/hjson":{extensions:["hjson"]},"application/hl7v2+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/http":{source:"iana"},"application/hyperstudio":{source:"iana",extensions:["stk"]},"application/ibe-key-request+xml":{source:"iana",compressible:!0},"application/ibe-pkg-reply+xml":{source:"iana",compressible:!0},"application/ibe-pp-data":{source:"iana"},"application/iges":{source:"iana"},"application/im-iscomposing+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/index":{source:"iana"},"application/index.cmd":{source:"iana"},"application/index.obj":{source:"iana"},"application/index.response":{source:"iana"},"application/index.vnd":{source:"iana"},"application/inkml+xml":{source:"iana",compressible:!0,extensions:["ink","inkml"]},"application/iotp":{source:"iana"},"application/ipfix":{source:"iana",extensions:["ipfix"]},"application/ipp":{source:"iana"},"application/isup":{source:"iana"},"application/its+xml":{source:"iana",compressible:!0,extensions:["its"]},"application/java-archive":{source:"iana",compressible:!1,extensions:["jar","war","ear"]},"application/java-serialized-object":{source:"apache",compressible:!1,extensions:["ser"]},"application/java-vm":{source:"apache",compressible:!1,extensions:["class"]},"application/javascript":{source:"apache",charset:"UTF-8",compressible:!0,extensions:["js"]},"application/jf2feed+json":{source:"iana",compressible:!0},"application/jose":{source:"iana"},"application/jose+json":{source:"iana",compressible:!0},"application/jrd+json":{source:"iana",compressible:!0},"application/jscalendar+json":{source:"iana",compressible:!0},"application/jscontact+json":{source:"iana",compressible:!0},"application/json":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["json","map"]},"application/json-patch+json":{source:"iana",compressible:!0},"application/json-seq":{source:"iana"},"application/json5":{extensions:["json5"]},"application/jsonml+json":{source:"apache",compressible:!0,extensions:["jsonml"]},"application/jsonpath":{source:"iana"},"application/jwk+json":{source:"iana",compressible:!0},"application/jwk-set+json":{source:"iana",compressible:!0},"application/jwk-set+jwt":{source:"iana"},"application/jwt":{source:"iana"},"application/kpml-request+xml":{source:"iana",compressible:!0},"application/kpml-response+xml":{source:"iana",compressible:!0},"application/ld+json":{source:"iana",compressible:!0,extensions:["jsonld"]},"application/lgr+xml":{source:"iana",compressible:!0,extensions:["lgr"]},"application/link-format":{source:"iana"},"application/linkset":{source:"iana"},"application/linkset+json":{source:"iana",compressible:!0},"application/load-control+xml":{source:"iana",compressible:!0},"application/logout+jwt":{source:"iana"},"application/lost+xml":{source:"iana",compressible:!0,extensions:["lostxml"]},"application/lostsync+xml":{source:"iana",compressible:!0},"application/lpf+zip":{source:"iana",compressible:!1},"application/lxf":{source:"iana"},"application/mac-binhex40":{source:"iana",extensions:["hqx"]},"application/mac-compactpro":{source:"apache",extensions:["cpt"]},"application/macwriteii":{source:"iana"},"application/mads+xml":{source:"iana",compressible:!0,extensions:["mads"]},"application/manifest+json":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["webmanifest"]},"application/marc":{source:"iana",extensions:["mrc"]},"application/marcxml+xml":{source:"iana",compressible:!0,extensions:["mrcx"]},"application/mathematica":{source:"iana",extensions:["ma","nb","mb"]},"application/mathml+xml":{source:"iana",compressible:!0,extensions:["mathml"]},"application/mathml-content+xml":{source:"iana",compressible:!0},"application/mathml-presentation+xml":{source:"iana",compressible:!0},"application/mbms-associated-procedure-description+xml":{source:"iana",compressible:!0},"application/mbms-deregister+xml":{source:"iana",compressible:!0},"application/mbms-envelope+xml":{source:"iana",compressible:!0},"application/mbms-msk+xml":{source:"iana",compressible:!0},"application/mbms-msk-response+xml":{source:"iana",compressible:!0},"application/mbms-protection-description+xml":{source:"iana",compressible:!0},"application/mbms-reception-report+xml":{source:"iana",compressible:!0},"application/mbms-register+xml":{source:"iana",compressible:!0},"application/mbms-register-response+xml":{source:"iana",compressible:!0},"application/mbms-schedule+xml":{source:"iana",compressible:!0},"application/mbms-user-service-description+xml":{source:"iana",compressible:!0},"application/mbox":{source:"iana",extensions:["mbox"]},"application/media-policy-dataset+xml":{source:"iana",compressible:!0,extensions:["mpf"]},"application/media_control+xml":{source:"iana",compressible:!0},"application/mediaservercontrol+xml":{source:"iana",compressible:!0,extensions:["mscml"]},"application/merge-patch+json":{source:"iana",compressible:!0},"application/metalink+xml":{source:"apache",compressible:!0,extensions:["metalink"]},"application/metalink4+xml":{source:"iana",compressible:!0,extensions:["meta4"]},"application/mets+xml":{source:"iana",compressible:!0,extensions:["mets"]},"application/mf4":{source:"iana"},"application/mikey":{source:"iana"},"application/mipc":{source:"iana"},"application/missing-blocks+cbor-seq":{source:"iana"},"application/mmt-aei+xml":{source:"iana",compressible:!0,extensions:["maei"]},"application/mmt-usd+xml":{source:"iana",compressible:!0,extensions:["musd"]},"application/mods+xml":{source:"iana",compressible:!0,extensions:["mods"]},"application/moss-keys":{source:"iana"},"application/moss-signature":{source:"iana"},"application/mosskey-data":{source:"iana"},"application/mosskey-request":{source:"iana"},"application/mp21":{source:"iana",extensions:["m21","mp21"]},"application/mp4":{source:"iana",extensions:["mp4","mpg4","mp4s","m4p"]},"application/mpeg4-generic":{source:"iana"},"application/mpeg4-iod":{source:"iana"},"application/mpeg4-iod-xmt":{source:"iana"},"application/mrb-consumer+xml":{source:"iana",compressible:!0},"application/mrb-publish+xml":{source:"iana",compressible:!0},"application/msc-ivr+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/msc-mixer+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/msix":{compressible:!1,extensions:["msix"]},"application/msixbundle":{compressible:!1,extensions:["msixbundle"]},"application/msword":{source:"iana",compressible:!1,extensions:["doc","dot"]},"application/mud+json":{source:"iana",compressible:!0},"application/multipart-core":{source:"iana"},"application/mxf":{source:"iana",extensions:["mxf"]},"application/n-quads":{source:"iana",extensions:["nq"]},"application/n-triples":{source:"iana",extensions:["nt"]},"application/nasdata":{source:"iana"},"application/news-checkgroups":{source:"iana",charset:"US-ASCII"},"application/news-groupinfo":{source:"iana",charset:"US-ASCII"},"application/news-transmission":{source:"iana"},"application/nlsml+xml":{source:"iana",compressible:!0},"application/node":{source:"iana",extensions:["cjs"]},"application/nss":{source:"iana"},"application/oauth-authz-req+jwt":{source:"iana"},"application/oblivious-dns-message":{source:"iana"},"application/ocsp-request":{source:"iana"},"application/ocsp-response":{source:"iana"},"application/octet-stream":{source:"iana",compressible:!0,extensions:["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"]},"application/oda":{source:"iana",extensions:["oda"]},"application/odm+xml":{source:"iana",compressible:!0},"application/odx":{source:"iana"},"application/oebps-package+xml":{source:"iana",compressible:!0,extensions:["opf"]},"application/ogg":{source:"iana",compressible:!1,extensions:["ogx"]},"application/ohttp-keys":{source:"iana"},"application/omdoc+xml":{source:"apache",compressible:!0,extensions:["omdoc"]},"application/onenote":{source:"apache",extensions:["onetoc","onetoc2","onetmp","onepkg","one","onea"]},"application/opc-nodeset+xml":{source:"iana",compressible:!0},"application/oscore":{source:"iana"},"application/oxps":{source:"iana",extensions:["oxps"]},"application/p21":{source:"iana"},"application/p21+zip":{source:"iana",compressible:!1},"application/p2p-overlay+xml":{source:"iana",compressible:!0,extensions:["relo"]},"application/parityfec":{source:"iana"},"application/passport":{source:"iana"},"application/patch-ops-error+xml":{source:"iana",compressible:!0,extensions:["xer"]},"application/pdf":{source:"iana",compressible:!1,extensions:["pdf"]},"application/pdx":{source:"iana"},"application/pem-certificate-chain":{source:"iana"},"application/pgp-encrypted":{source:"iana",compressible:!1,extensions:["pgp"]},"application/pgp-keys":{source:"iana",extensions:["asc"]},"application/pgp-signature":{source:"iana",extensions:["sig","asc"]},"application/pics-rules":{source:"apache",extensions:["prf"]},"application/pidf+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/pidf-diff+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/pkcs10":{source:"iana",extensions:["p10"]},"application/pkcs12":{source:"iana"},"application/pkcs7-mime":{source:"iana",extensions:["p7m","p7c"]},"application/pkcs7-signature":{source:"iana",extensions:["p7s"]},"application/pkcs8":{source:"iana",extensions:["p8"]},"application/pkcs8-encrypted":{source:"iana"},"application/pkix-attr-cert":{source:"iana",extensions:["ac"]},"application/pkix-cert":{source:"iana",extensions:["cer"]},"application/pkix-crl":{source:"iana",extensions:["crl"]},"application/pkix-pkipath":{source:"iana",extensions:["pkipath"]},"application/pkixcmp":{source:"iana",extensions:["pki"]},"application/pls+xml":{source:"iana",compressible:!0,extensions:["pls"]},"application/poc-settings+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/postscript":{source:"iana",compressible:!0,extensions:["ai","eps","ps"]},"application/ppsp-tracker+json":{source:"iana",compressible:!0},"application/private-token-issuer-directory":{source:"iana"},"application/private-token-request":{source:"iana"},"application/private-token-response":{source:"iana"},"application/problem+json":{source:"iana",compressible:!0},"application/problem+xml":{source:"iana",compressible:!0},"application/provenance+xml":{source:"iana",compressible:!0,extensions:["provx"]},"application/provided-claims+jwt":{source:"iana"},"application/prs.alvestrand.titrax-sheet":{source:"iana"},"application/prs.cww":{source:"iana",extensions:["cww"]},"application/prs.cyn":{source:"iana",charset:"7-BIT"},"application/prs.hpub+zip":{source:"iana",compressible:!1},"application/prs.implied-document+xml":{source:"iana",compressible:!0},"application/prs.implied-executable":{source:"iana"},"application/prs.implied-object+json":{source:"iana",compressible:!0},"application/prs.implied-object+json-seq":{source:"iana"},"application/prs.implied-object+yaml":{source:"iana"},"application/prs.implied-structure":{source:"iana"},"application/prs.mayfile":{source:"iana"},"application/prs.nprend":{source:"iana"},"application/prs.plucker":{source:"iana"},"application/prs.rdf-xml-crypt":{source:"iana"},"application/prs.vcfbzip2":{source:"iana"},"application/prs.xsf+xml":{source:"iana",compressible:!0,extensions:["xsf"]},"application/pskc+xml":{source:"iana",compressible:!0,extensions:["pskcxml"]},"application/pvd+json":{source:"iana",compressible:!0},"application/qsig":{source:"iana"},"application/raml+yaml":{compressible:!0,extensions:["raml"]},"application/raptorfec":{source:"iana"},"application/rdap+json":{source:"iana",compressible:!0},"application/rdf+xml":{source:"iana",compressible:!0,extensions:["rdf","owl"]},"application/reginfo+xml":{source:"iana",compressible:!0,extensions:["rif"]},"application/relax-ng-compact-syntax":{source:"iana",extensions:["rnc"]},"application/remote-printing":{source:"apache"},"application/reputon+json":{source:"iana",compressible:!0},"application/resolve-response+jwt":{source:"iana"},"application/resource-lists+xml":{source:"iana",compressible:!0,extensions:["rl"]},"application/resource-lists-diff+xml":{source:"iana",compressible:!0,extensions:["rld"]},"application/rfc+xml":{source:"iana",compressible:!0},"application/riscos":{source:"iana"},"application/rlmi+xml":{source:"iana",compressible:!0},"application/rls-services+xml":{source:"iana",compressible:!0,extensions:["rs"]},"application/route-apd+xml":{source:"iana",compressible:!0,extensions:["rapd"]},"application/route-s-tsid+xml":{source:"iana",compressible:!0,extensions:["sls"]},"application/route-usd+xml":{source:"iana",compressible:!0,extensions:["rusd"]},"application/rpki-checklist":{source:"iana"},"application/rpki-ghostbusters":{source:"iana",extensions:["gbr"]},"application/rpki-manifest":{source:"iana",extensions:["mft"]},"application/rpki-publication":{source:"iana"},"application/rpki-roa":{source:"iana",extensions:["roa"]},"application/rpki-signed-tal":{source:"iana"},"application/rpki-updown":{source:"iana"},"application/rsd+xml":{source:"apache",compressible:!0,extensions:["rsd"]},"application/rss+xml":{source:"apache",compressible:!0,extensions:["rss"]},"application/rtf":{source:"iana",compressible:!0,extensions:["rtf"]},"application/rtploopback":{source:"iana"},"application/rtx":{source:"iana"},"application/samlassertion+xml":{source:"iana",compressible:!0},"application/samlmetadata+xml":{source:"iana",compressible:!0},"application/sarif+json":{source:"iana",compressible:!0},"application/sarif-external-properties+json":{source:"iana",compressible:!0},"application/sbe":{source:"iana"},"application/sbml+xml":{source:"iana",compressible:!0,extensions:["sbml"]},"application/scaip+xml":{source:"iana",compressible:!0},"application/scim+json":{source:"iana",compressible:!0},"application/scvp-cv-request":{source:"iana",extensions:["scq"]},"application/scvp-cv-response":{source:"iana",extensions:["scs"]},"application/scvp-vp-request":{source:"iana",extensions:["spq"]},"application/scvp-vp-response":{source:"iana",extensions:["spp"]},"application/sdp":{source:"iana",extensions:["sdp"]},"application/secevent+jwt":{source:"iana"},"application/senml+cbor":{source:"iana"},"application/senml+json":{source:"iana",compressible:!0},"application/senml+xml":{source:"iana",compressible:!0,extensions:["senmlx"]},"application/senml-etch+cbor":{source:"iana"},"application/senml-etch+json":{source:"iana",compressible:!0},"application/senml-exi":{source:"iana"},"application/sensml+cbor":{source:"iana"},"application/sensml+json":{source:"iana",compressible:!0},"application/sensml+xml":{source:"iana",compressible:!0,extensions:["sensmlx"]},"application/sensml-exi":{source:"iana"},"application/sep+xml":{source:"iana",compressible:!0},"application/sep-exi":{source:"iana"},"application/session-info":{source:"iana"},"application/set-payment":{source:"iana"},"application/set-payment-initiation":{source:"iana",extensions:["setpay"]},"application/set-registration":{source:"iana"},"application/set-registration-initiation":{source:"iana",extensions:["setreg"]},"application/sgml":{source:"iana"},"application/sgml-open-catalog":{source:"iana"},"application/shf+xml":{source:"iana",compressible:!0,extensions:["shf"]},"application/sieve":{source:"iana",extensions:["siv","sieve"]},"application/simple-filter+xml":{source:"iana",compressible:!0},"application/simple-message-summary":{source:"iana"},"application/simplesymbolcontainer":{source:"iana"},"application/sipc":{source:"iana"},"application/slate":{source:"iana"},"application/smil":{source:"apache"},"application/smil+xml":{source:"iana",compressible:!0,extensions:["smi","smil"]},"application/smpte336m":{source:"iana"},"application/soap+fastinfoset":{source:"iana"},"application/soap+xml":{source:"iana",compressible:!0},"application/sparql-query":{source:"iana",extensions:["rq"]},"application/sparql-results+xml":{source:"iana",compressible:!0,extensions:["srx"]},"application/spdx+json":{source:"iana",compressible:!0},"application/spirits-event+xml":{source:"iana",compressible:!0},"application/sql":{source:"iana",extensions:["sql"]},"application/srgs":{source:"iana",extensions:["gram"]},"application/srgs+xml":{source:"iana",compressible:!0,extensions:["grxml"]},"application/sru+xml":{source:"iana",compressible:!0,extensions:["sru"]},"application/ssdl+xml":{source:"apache",compressible:!0,extensions:["ssdl"]},"application/sslkeylogfile":{source:"iana"},"application/ssml+xml":{source:"iana",compressible:!0,extensions:["ssml"]},"application/st2110-41":{source:"iana"},"application/stix+json":{source:"iana",compressible:!0},"application/stratum":{source:"iana"},"application/swid+cbor":{source:"iana"},"application/swid+xml":{source:"iana",compressible:!0,extensions:["swidtag"]},"application/tamp-apex-update":{source:"iana"},"application/tamp-apex-update-confirm":{source:"iana"},"application/tamp-community-update":{source:"iana"},"application/tamp-community-update-confirm":{source:"iana"},"application/tamp-error":{source:"iana"},"application/tamp-sequence-adjust":{source:"iana"},"application/tamp-sequence-adjust-confirm":{source:"iana"},"application/tamp-status-query":{source:"iana"},"application/tamp-status-response":{source:"iana"},"application/tamp-update":{source:"iana"},"application/tamp-update-confirm":{source:"iana"},"application/tar":{compressible:!0},"application/taxii+json":{source:"iana",compressible:!0},"application/td+json":{source:"iana",compressible:!0},"application/tei+xml":{source:"iana",compressible:!0,extensions:["tei","teicorpus"]},"application/tetra_isi":{source:"iana"},"application/thraud+xml":{source:"iana",compressible:!0,extensions:["tfi"]},"application/timestamp-query":{source:"iana"},"application/timestamp-reply":{source:"iana"},"application/timestamped-data":{source:"iana",extensions:["tsd"]},"application/tlsrpt+gzip":{source:"iana"},"application/tlsrpt+json":{source:"iana",compressible:!0},"application/tm+json":{source:"iana",compressible:!0},"application/tnauthlist":{source:"iana"},"application/toc+cbor":{source:"iana"},"application/token-introspection+jwt":{source:"iana"},"application/toml":{source:"iana",compressible:!0,extensions:["toml"]},"application/trickle-ice-sdpfrag":{source:"iana"},"application/trig":{source:"iana",extensions:["trig"]},"application/trust-chain+json":{source:"iana",compressible:!0},"application/trust-mark+jwt":{source:"iana"},"application/trust-mark-delegation+jwt":{source:"iana"},"application/ttml+xml":{source:"iana",compressible:!0,extensions:["ttml"]},"application/tve-trigger":{source:"iana"},"application/tzif":{source:"iana"},"application/tzif-leap":{source:"iana"},"application/ubjson":{compressible:!1,extensions:["ubj"]},"application/uccs+cbor":{source:"iana"},"application/ujcs+json":{source:"iana",compressible:!0},"application/ulpfec":{source:"iana"},"application/urc-grpsheet+xml":{source:"iana",compressible:!0},"application/urc-ressheet+xml":{source:"iana",compressible:!0,extensions:["rsheet"]},"application/urc-targetdesc+xml":{source:"iana",compressible:!0,extensions:["td"]},"application/urc-uisocketdesc+xml":{source:"iana",compressible:!0},"application/vc":{source:"iana"},"application/vc+cose":{source:"iana"},"application/vc+jwt":{source:"iana"},"application/vcard+json":{source:"iana",compressible:!0},"application/vcard+xml":{source:"iana",compressible:!0},"application/vemmi":{source:"iana"},"application/vividence.scriptfile":{source:"apache"},"application/vnd.1000minds.decision-model+xml":{source:"iana",compressible:!0,extensions:["1km"]},"application/vnd.1ob":{source:"iana"},"application/vnd.3gpp-prose+xml":{source:"iana",compressible:!0},"application/vnd.3gpp-prose-pc3a+xml":{source:"iana",compressible:!0},"application/vnd.3gpp-prose-pc3ach+xml":{source:"iana",compressible:!0},"application/vnd.3gpp-prose-pc3ch+xml":{source:"iana",compressible:!0},"application/vnd.3gpp-prose-pc8+xml":{source:"iana",compressible:!0},"application/vnd.3gpp-v2x-local-service-information":{source:"iana"},"application/vnd.3gpp.5gnas":{source:"iana"},"application/vnd.3gpp.5gsa2x":{source:"iana"},"application/vnd.3gpp.5gsa2x-local-service-information":{source:"iana"},"application/vnd.3gpp.5gsv2x":{source:"iana"},"application/vnd.3gpp.5gsv2x-local-service-information":{source:"iana"},"application/vnd.3gpp.access-transfer-events+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.bsf+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.crs+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.current-location-discovery+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.gmop+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.gtpc":{source:"iana"},"application/vnd.3gpp.interworking-data":{source:"iana"},"application/vnd.3gpp.lpp":{source:"iana"},"application/vnd.3gpp.mc-signalling-ear":{source:"iana"},"application/vnd.3gpp.mcdata-affiliation-command+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcdata-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcdata-msgstore-ctrl-request+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcdata-payload":{source:"iana"},"application/vnd.3gpp.mcdata-regroup+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcdata-service-config+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcdata-signalling":{source:"iana"},"application/vnd.3gpp.mcdata-ue-config+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcdata-user-profile+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-affiliation-command+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-floor-request+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-location-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-mbms-usage-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-regroup+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-service-config+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-signed+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-ue-config+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-ue-init-config+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcptt-user-profile+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-affiliation-command+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-location-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-mbms-usage-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-regroup+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-service-config+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-transmission-request+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-ue-config+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mcvideo-user-profile+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.mid-call+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.ngap":{source:"iana"},"application/vnd.3gpp.pfcp":{source:"iana"},"application/vnd.3gpp.pic-bw-large":{source:"iana",extensions:["plb"]},"application/vnd.3gpp.pic-bw-small":{source:"iana",extensions:["psb"]},"application/vnd.3gpp.pic-bw-var":{source:"iana",extensions:["pvb"]},"application/vnd.3gpp.pinapp-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.s1ap":{source:"iana"},"application/vnd.3gpp.seal-group-doc+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.seal-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.seal-location-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.seal-mbms-usage-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.seal-network-qos-management-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.seal-ue-config-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.seal-unicast-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.seal-user-profile-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.sms":{source:"iana"},"application/vnd.3gpp.sms+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.srvcc-ext+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.srvcc-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.state-and-event-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.ussd+xml":{source:"iana",compressible:!0},"application/vnd.3gpp.v2x":{source:"iana"},"application/vnd.3gpp.vae-info+xml":{source:"iana",compressible:!0},"application/vnd.3gpp2.bcmcsinfo+xml":{source:"iana",compressible:!0},"application/vnd.3gpp2.sms":{source:"iana"},"application/vnd.3gpp2.tcap":{source:"iana",extensions:["tcap"]},"application/vnd.3lightssoftware.imagescal":{source:"iana"},"application/vnd.3m.post-it-notes":{source:"iana",extensions:["pwn"]},"application/vnd.accpac.simply.aso":{source:"iana",extensions:["aso"]},"application/vnd.accpac.simply.imp":{source:"iana",extensions:["imp"]},"application/vnd.acm.addressxfer+json":{source:"iana",compressible:!0},"application/vnd.acm.chatbot+json":{source:"iana",compressible:!0},"application/vnd.acucobol":{source:"iana",extensions:["acu"]},"application/vnd.acucorp":{source:"iana",extensions:["atc","acutc"]},"application/vnd.adobe.air-application-installer-package+zip":{source:"apache",compressible:!1,extensions:["air"]},"application/vnd.adobe.flash.movie":{source:"iana"},"application/vnd.adobe.formscentral.fcdt":{source:"iana",extensions:["fcdt"]},"application/vnd.adobe.fxp":{source:"iana",extensions:["fxp","fxpl"]},"application/vnd.adobe.partial-upload":{source:"iana"},"application/vnd.adobe.xdp+xml":{source:"iana",compressible:!0,extensions:["xdp"]},"application/vnd.adobe.xfdf":{source:"apache",extensions:["xfdf"]},"application/vnd.aether.imp":{source:"iana"},"application/vnd.afpc.afplinedata":{source:"iana"},"application/vnd.afpc.afplinedata-pagedef":{source:"iana"},"application/vnd.afpc.cmoca-cmresource":{source:"iana"},"application/vnd.afpc.foca-charset":{source:"iana"},"application/vnd.afpc.foca-codedfont":{source:"iana"},"application/vnd.afpc.foca-codepage":{source:"iana"},"application/vnd.afpc.modca":{source:"iana"},"application/vnd.afpc.modca-cmtable":{source:"iana"},"application/vnd.afpc.modca-formdef":{source:"iana"},"application/vnd.afpc.modca-mediummap":{source:"iana"},"application/vnd.afpc.modca-objectcontainer":{source:"iana"},"application/vnd.afpc.modca-overlay":{source:"iana"},"application/vnd.afpc.modca-pagesegment":{source:"iana"},"application/vnd.age":{source:"iana",extensions:["age"]},"application/vnd.ah-barcode":{source:"apache"},"application/vnd.ahead.space":{source:"iana",extensions:["ahead"]},"application/vnd.airzip.filesecure.azf":{source:"iana",extensions:["azf"]},"application/vnd.airzip.filesecure.azs":{source:"iana",extensions:["azs"]},"application/vnd.amadeus+json":{source:"iana",compressible:!0},"application/vnd.amazon.ebook":{source:"apache",extensions:["azw"]},"application/vnd.amazon.mobi8-ebook":{source:"iana"},"application/vnd.americandynamics.acc":{source:"iana",extensions:["acc"]},"application/vnd.amiga.ami":{source:"iana",extensions:["ami"]},"application/vnd.amundsen.maze+xml":{source:"iana",compressible:!0},"application/vnd.android.ota":{source:"iana"},"application/vnd.android.package-archive":{source:"apache",compressible:!1,extensions:["apk"]},"application/vnd.anki":{source:"iana"},"application/vnd.anser-web-certificate-issue-initiation":{source:"iana",extensions:["cii"]},"application/vnd.anser-web-funds-transfer-initiation":{source:"apache",extensions:["fti"]},"application/vnd.antix.game-component":{source:"iana",extensions:["atx"]},"application/vnd.apache.arrow.file":{source:"iana"},"application/vnd.apache.arrow.stream":{source:"iana"},"application/vnd.apache.parquet":{source:"iana"},"application/vnd.apache.thrift.binary":{source:"iana"},"application/vnd.apache.thrift.compact":{source:"iana"},"application/vnd.apache.thrift.json":{source:"iana"},"application/vnd.apexlang":{source:"iana"},"application/vnd.api+json":{source:"iana",compressible:!0},"application/vnd.aplextor.warrp+json":{source:"iana",compressible:!0},"application/vnd.apothekende.reservation+json":{source:"iana",compressible:!0},"application/vnd.apple.installer+xml":{source:"iana",compressible:!0,extensions:["mpkg"]},"application/vnd.apple.keynote":{source:"iana",extensions:["key"]},"application/vnd.apple.mpegurl":{source:"iana",extensions:["m3u8"]},"application/vnd.apple.numbers":{source:"iana",extensions:["numbers"]},"application/vnd.apple.pages":{source:"iana",extensions:["pages"]},"application/vnd.apple.pkpass":{compressible:!1,extensions:["pkpass"]},"application/vnd.arastra.swi":{source:"apache"},"application/vnd.aristanetworks.swi":{source:"iana",extensions:["swi"]},"application/vnd.artisan+json":{source:"iana",compressible:!0},"application/vnd.artsquare":{source:"iana"},"application/vnd.astraea-software.iota":{source:"iana",extensions:["iota"]},"application/vnd.audiograph":{source:"iana",extensions:["aep"]},"application/vnd.autodesk.fbx":{extensions:["fbx"]},"application/vnd.autopackage":{source:"iana"},"application/vnd.avalon+json":{source:"iana",compressible:!0},"application/vnd.avistar+xml":{source:"iana",compressible:!0},"application/vnd.balsamiq.bmml+xml":{source:"iana",compressible:!0,extensions:["bmml"]},"application/vnd.balsamiq.bmpr":{source:"iana"},"application/vnd.banana-accounting":{source:"iana"},"application/vnd.bbf.usp.error":{source:"iana"},"application/vnd.bbf.usp.msg":{source:"iana"},"application/vnd.bbf.usp.msg+json":{source:"iana",compressible:!0},"application/vnd.bekitzur-stech+json":{source:"iana",compressible:!0},"application/vnd.belightsoft.lhzd+zip":{source:"iana",compressible:!1},"application/vnd.belightsoft.lhzl+zip":{source:"iana",compressible:!1},"application/vnd.bint.med-content":{source:"iana"},"application/vnd.biopax.rdf+xml":{source:"iana",compressible:!0},"application/vnd.blink-idb-value-wrapper":{source:"iana"},"application/vnd.blueice.multipass":{source:"iana",extensions:["mpm"]},"application/vnd.bluetooth.ep.oob":{source:"iana"},"application/vnd.bluetooth.le.oob":{source:"iana"},"application/vnd.bmi":{source:"iana",extensions:["bmi"]},"application/vnd.bpf":{source:"iana"},"application/vnd.bpf3":{source:"iana"},"application/vnd.businessobjects":{source:"iana",extensions:["rep"]},"application/vnd.byu.uapi+json":{source:"iana",compressible:!0},"application/vnd.bzip3":{source:"iana"},"application/vnd.c3voc.schedule+xml":{source:"iana",compressible:!0},"application/vnd.cab-jscript":{source:"iana"},"application/vnd.canon-cpdl":{source:"iana"},"application/vnd.canon-lips":{source:"iana"},"application/vnd.capasystems-pg+json":{source:"iana",compressible:!0},"application/vnd.cendio.thinlinc.clientconf":{source:"iana"},"application/vnd.century-systems.tcp_stream":{source:"iana"},"application/vnd.chemdraw+xml":{source:"iana",compressible:!0,extensions:["cdxml"]},"application/vnd.chess-pgn":{source:"iana"},"application/vnd.chipnuts.karaoke-mmd":{source:"iana",extensions:["mmd"]},"application/vnd.ciedi":{source:"iana"},"application/vnd.cinderella":{source:"iana",extensions:["cdy"]},"application/vnd.cirpack.isdn-ext":{source:"iana"},"application/vnd.citationstyles.style+xml":{source:"iana",compressible:!0,extensions:["csl"]},"application/vnd.claymore":{source:"iana",extensions:["cla"]},"application/vnd.cloanto.rp9":{source:"iana",extensions:["rp9"]},"application/vnd.clonk.c4group":{source:"iana",extensions:["c4g","c4d","c4f","c4p","c4u"]},"application/vnd.cluetrust.cartomobile-config":{source:"iana",extensions:["c11amc"]},"application/vnd.cluetrust.cartomobile-config-pkg":{source:"iana",extensions:["c11amz"]},"application/vnd.cncf.helm.chart.content.v1.tar+gzip":{source:"iana"},"application/vnd.cncf.helm.chart.provenance.v1.prov":{source:"iana"},"application/vnd.cncf.helm.config.v1+json":{source:"iana",compressible:!0},"application/vnd.coffeescript":{source:"iana"},"application/vnd.collabio.xodocuments.document":{source:"iana"},"application/vnd.collabio.xodocuments.document-template":{source:"iana"},"application/vnd.collabio.xodocuments.presentation":{source:"iana"},"application/vnd.collabio.xodocuments.presentation-template":{source:"iana"},"application/vnd.collabio.xodocuments.spreadsheet":{source:"iana"},"application/vnd.collabio.xodocuments.spreadsheet-template":{source:"iana"},"application/vnd.collection+json":{source:"iana",compressible:!0},"application/vnd.collection.doc+json":{source:"iana",compressible:!0},"application/vnd.collection.next+json":{source:"iana",compressible:!0},"application/vnd.comicbook+zip":{source:"iana",compressible:!1},"application/vnd.comicbook-rar":{source:"iana"},"application/vnd.commerce-battelle":{source:"iana"},"application/vnd.commonspace":{source:"iana",extensions:["csp"]},"application/vnd.contact.cmsg":{source:"iana",extensions:["cdbcmsg"]},"application/vnd.coreos.ignition+json":{source:"iana",compressible:!0},"application/vnd.cosmocaller":{source:"iana",extensions:["cmc"]},"application/vnd.crick.clicker":{source:"iana",extensions:["clkx"]},"application/vnd.crick.clicker.keyboard":{source:"iana",extensions:["clkk"]},"application/vnd.crick.clicker.palette":{source:"iana",extensions:["clkp"]},"application/vnd.crick.clicker.template":{source:"iana",extensions:["clkt"]},"application/vnd.crick.clicker.wordbank":{source:"iana",extensions:["clkw"]},"application/vnd.criticaltools.wbs+xml":{source:"iana",compressible:!0,extensions:["wbs"]},"application/vnd.cryptii.pipe+json":{source:"iana",compressible:!0},"application/vnd.crypto-shade-file":{source:"iana"},"application/vnd.cryptomator.encrypted":{source:"iana"},"application/vnd.cryptomator.vault":{source:"iana"},"application/vnd.ctc-posml":{source:"iana",extensions:["pml"]},"application/vnd.ctct.ws+xml":{source:"iana",compressible:!0},"application/vnd.cups-pdf":{source:"iana"},"application/vnd.cups-postscript":{source:"iana"},"application/vnd.cups-ppd":{source:"iana",extensions:["ppd"]},"application/vnd.cups-raster":{source:"iana"},"application/vnd.cups-raw":{source:"iana"},"application/vnd.curl":{source:"iana"},"application/vnd.curl.car":{source:"apache",extensions:["car"]},"application/vnd.curl.pcurl":{source:"apache",extensions:["pcurl"]},"application/vnd.cyan.dean.root+xml":{source:"iana",compressible:!0},"application/vnd.cybank":{source:"iana"},"application/vnd.cyclonedx+json":{source:"iana",compressible:!0},"application/vnd.cyclonedx+xml":{source:"iana",compressible:!0},"application/vnd.d2l.coursepackage1p0+zip":{source:"iana",compressible:!1},"application/vnd.d3m-dataset":{source:"iana"},"application/vnd.d3m-problem":{source:"iana"},"application/vnd.dart":{source:"iana",compressible:!0,extensions:["dart"]},"application/vnd.data-vision.rdz":{source:"iana",extensions:["rdz"]},"application/vnd.datalog":{source:"iana"},"application/vnd.datapackage+json":{source:"iana",compressible:!0},"application/vnd.dataresource+json":{source:"iana",compressible:!0},"application/vnd.dbf":{source:"iana",extensions:["dbf"]},"application/vnd.dcmp+xml":{source:"iana",compressible:!0,extensions:["dcmp"]},"application/vnd.debian.binary-package":{source:"iana"},"application/vnd.dece.data":{source:"iana",extensions:["uvf","uvvf","uvd","uvvd"]},"application/vnd.dece.ttml+xml":{source:"iana",compressible:!0,extensions:["uvt","uvvt"]},"application/vnd.dece.unspecified":{source:"iana",extensions:["uvx","uvvx"]},"application/vnd.dece.zip":{source:"iana",extensions:["uvz","uvvz"]},"application/vnd.denovo.fcselayout-link":{source:"iana",extensions:["fe_launch"]},"application/vnd.desmume.movie":{source:"iana"},"application/vnd.dir-bi.plate-dl-nosuffix":{source:"iana"},"application/vnd.dm.delegation+xml":{source:"iana",compressible:!0},"application/vnd.dna":{source:"iana",extensions:["dna"]},"application/vnd.document+json":{source:"iana",compressible:!0},"application/vnd.dolby.mlp":{source:"apache",extensions:["mlp"]},"application/vnd.dolby.mobile.1":{source:"iana"},"application/vnd.dolby.mobile.2":{source:"iana"},"application/vnd.doremir.scorecloud-binary-document":{source:"iana"},"application/vnd.dpgraph":{source:"iana",extensions:["dpg"]},"application/vnd.dreamfactory":{source:"iana",extensions:["dfac"]},"application/vnd.drive+json":{source:"iana",compressible:!0},"application/vnd.ds-keypoint":{source:"apache",extensions:["kpxx"]},"application/vnd.dtg.local":{source:"iana"},"application/vnd.dtg.local.flash":{source:"iana"},"application/vnd.dtg.local.html":{source:"iana"},"application/vnd.dvb.ait":{source:"iana",extensions:["ait"]},"application/vnd.dvb.dvbisl+xml":{source:"iana",compressible:!0},"application/vnd.dvb.dvbj":{source:"iana"},"application/vnd.dvb.esgcontainer":{source:"iana"},"application/vnd.dvb.ipdcdftnotifaccess":{source:"iana"},"application/vnd.dvb.ipdcesgaccess":{source:"iana"},"application/vnd.dvb.ipdcesgaccess2":{source:"iana"},"application/vnd.dvb.ipdcesgpdd":{source:"iana"},"application/vnd.dvb.ipdcroaming":{source:"iana"},"application/vnd.dvb.iptv.alfec-base":{source:"iana"},"application/vnd.dvb.iptv.alfec-enhancement":{source:"iana"},"application/vnd.dvb.notif-aggregate-root+xml":{source:"iana",compressible:!0},"application/vnd.dvb.notif-container+xml":{source:"iana",compressible:!0},"application/vnd.dvb.notif-generic+xml":{source:"iana",compressible:!0},"application/vnd.dvb.notif-ia-msglist+xml":{source:"iana",compressible:!0},"application/vnd.dvb.notif-ia-registration-request+xml":{source:"iana",compressible:!0},"application/vnd.dvb.notif-ia-registration-response+xml":{source:"iana",compressible:!0},"application/vnd.dvb.notif-init+xml":{source:"iana",compressible:!0},"application/vnd.dvb.pfr":{source:"iana"},"application/vnd.dvb.service":{source:"iana",extensions:["svc"]},"application/vnd.dxr":{source:"iana"},"application/vnd.dynageo":{source:"iana",extensions:["geo"]},"application/vnd.dzr":{source:"iana"},"application/vnd.easykaraoke.cdgdownload":{source:"iana"},"application/vnd.ecdis-update":{source:"iana"},"application/vnd.ecip.rlp":{source:"iana"},"application/vnd.eclipse.ditto+json":{source:"iana",compressible:!0},"application/vnd.ecowin.chart":{source:"iana",extensions:["mag"]},"application/vnd.ecowin.filerequest":{source:"iana"},"application/vnd.ecowin.fileupdate":{source:"iana"},"application/vnd.ecowin.series":{source:"iana"},"application/vnd.ecowin.seriesrequest":{source:"iana"},"application/vnd.ecowin.seriesupdate":{source:"iana"},"application/vnd.efi.img":{source:"iana"},"application/vnd.efi.iso":{source:"iana"},"application/vnd.eln+zip":{source:"iana",compressible:!1},"application/vnd.emclient.accessrequest+xml":{source:"iana",compressible:!0},"application/vnd.enliven":{source:"iana",extensions:["nml"]},"application/vnd.enphase.envoy":{source:"iana"},"application/vnd.eprints.data+xml":{source:"iana",compressible:!0},"application/vnd.epson.esf":{source:"iana",extensions:["esf"]},"application/vnd.epson.msf":{source:"iana",extensions:["msf"]},"application/vnd.epson.quickanime":{source:"iana",extensions:["qam"]},"application/vnd.epson.salt":{source:"iana",extensions:["slt"]},"application/vnd.epson.ssf":{source:"iana",extensions:["ssf"]},"application/vnd.ericsson.quickcall":{source:"iana"},"application/vnd.erofs":{source:"iana"},"application/vnd.espass-espass+zip":{source:"iana",compressible:!1},"application/vnd.eszigno3+xml":{source:"iana",compressible:!0,extensions:["es3","et3"]},"application/vnd.etsi.aoc+xml":{source:"iana",compressible:!0},"application/vnd.etsi.asic-e+zip":{source:"iana",compressible:!1},"application/vnd.etsi.asic-s+zip":{source:"iana",compressible:!1},"application/vnd.etsi.cug+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvcommand+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvdiscovery+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvprofile+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvsad-bc+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvsad-cod+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvsad-npvr+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvservice+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvsync+xml":{source:"iana",compressible:!0},"application/vnd.etsi.iptvueprofile+xml":{source:"iana",compressible:!0},"application/vnd.etsi.mcid+xml":{source:"iana",compressible:!0},"application/vnd.etsi.mheg5":{source:"iana"},"application/vnd.etsi.overload-control-policy-dataset+xml":{source:"iana",compressible:!0},"application/vnd.etsi.pstn+xml":{source:"iana",compressible:!0},"application/vnd.etsi.sci+xml":{source:"iana",compressible:!0},"application/vnd.etsi.simservs+xml":{source:"iana",compressible:!0},"application/vnd.etsi.timestamp-token":{source:"iana"},"application/vnd.etsi.tsl+xml":{source:"iana",compressible:!0},"application/vnd.etsi.tsl.der":{source:"iana"},"application/vnd.eu.kasparian.car+json":{source:"iana",compressible:!0},"application/vnd.eudora.data":{source:"iana"},"application/vnd.evolv.ecig.profile":{source:"iana"},"application/vnd.evolv.ecig.settings":{source:"iana"},"application/vnd.evolv.ecig.theme":{source:"iana"},"application/vnd.exstream-empower+zip":{source:"iana",compressible:!1},"application/vnd.exstream-package":{source:"iana"},"application/vnd.ezpix-album":{source:"iana",extensions:["ez2"]},"application/vnd.ezpix-package":{source:"iana",extensions:["ez3"]},"application/vnd.f-secure.mobile":{source:"iana"},"application/vnd.familysearch.gedcom+zip":{source:"iana",compressible:!1},"application/vnd.fastcopy-disk-image":{source:"iana"},"application/vnd.fdf":{source:"apache",extensions:["fdf"]},"application/vnd.fdsn.mseed":{source:"iana",extensions:["mseed"]},"application/vnd.fdsn.seed":{source:"iana",extensions:["seed","dataless"]},"application/vnd.fdsn.stationxml+xml":{source:"iana",charset:"XML-BASED",compressible:!0},"application/vnd.ffsns":{source:"iana"},"application/vnd.ficlab.flb+zip":{source:"iana",compressible:!1},"application/vnd.filmit.zfc":{source:"iana"},"application/vnd.fints":{source:"iana"},"application/vnd.firemonkeys.cloudcell":{source:"iana"},"application/vnd.flographit":{source:"iana",extensions:["gph"]},"application/vnd.fluxtime.clip":{source:"iana",extensions:["ftc"]},"application/vnd.font-fontforge-sfd":{source:"iana"},"application/vnd.framemaker":{source:"iana",extensions:["fm","frame","maker","book"]},"application/vnd.freelog.comic":{source:"iana"},"application/vnd.frogans.fnc":{source:"apache",extensions:["fnc"]},"application/vnd.frogans.ltf":{source:"apache",extensions:["ltf"]},"application/vnd.fsc.weblaunch":{source:"iana",extensions:["fsc"]},"application/vnd.fujifilm.fb.docuworks":{source:"iana"},"application/vnd.fujifilm.fb.docuworks.binder":{source:"iana"},"application/vnd.fujifilm.fb.docuworks.container":{source:"iana"},"application/vnd.fujifilm.fb.jfi+xml":{source:"iana",compressible:!0},"application/vnd.fujitsu.oasys":{source:"iana",extensions:["oas"]},"application/vnd.fujitsu.oasys2":{source:"iana",extensions:["oa2"]},"application/vnd.fujitsu.oasys3":{source:"iana",extensions:["oa3"]},"application/vnd.fujitsu.oasysgp":{source:"iana",extensions:["fg5"]},"application/vnd.fujitsu.oasysprs":{source:"iana",extensions:["bh2"]},"application/vnd.fujixerox.art-ex":{source:"iana"},"application/vnd.fujixerox.art4":{source:"iana"},"application/vnd.fujixerox.ddd":{source:"iana",extensions:["ddd"]},"application/vnd.fujixerox.docuworks":{source:"iana",extensions:["xdw"]},"application/vnd.fujixerox.docuworks.binder":{source:"iana",extensions:["xbd"]},"application/vnd.fujixerox.docuworks.container":{source:"iana"},"application/vnd.fujixerox.hbpl":{source:"iana"},"application/vnd.fut-misnet":{source:"iana"},"application/vnd.futoin+cbor":{source:"iana"},"application/vnd.futoin+json":{source:"iana",compressible:!0},"application/vnd.fuzzysheet":{source:"iana",extensions:["fzs"]},"application/vnd.ga4gh.passport+jwt":{source:"iana"},"application/vnd.genomatix.tuxedo":{source:"iana",extensions:["txd"]},"application/vnd.genozip":{source:"iana"},"application/vnd.gentics.grd+json":{source:"iana",compressible:!0},"application/vnd.gentoo.catmetadata+xml":{source:"iana",compressible:!0},"application/vnd.gentoo.ebuild":{source:"iana"},"application/vnd.gentoo.eclass":{source:"iana"},"application/vnd.gentoo.gpkg":{source:"iana"},"application/vnd.gentoo.manifest":{source:"iana"},"application/vnd.gentoo.pkgmetadata+xml":{source:"iana",compressible:!0},"application/vnd.gentoo.xpak":{source:"iana"},"application/vnd.geo+json":{source:"apache",compressible:!0},"application/vnd.geocube+xml":{source:"apache",compressible:!0},"application/vnd.geogebra.file":{source:"iana",extensions:["ggb"]},"application/vnd.geogebra.pinboard":{source:"iana"},"application/vnd.geogebra.slides":{source:"iana",extensions:["ggs"]},"application/vnd.geogebra.tool":{source:"iana",extensions:["ggt"]},"application/vnd.geometry-explorer":{source:"iana",extensions:["gex","gre"]},"application/vnd.geonext":{source:"iana",extensions:["gxt"]},"application/vnd.geoplan":{source:"iana",extensions:["g2w"]},"application/vnd.geospace":{source:"iana",extensions:["g3w"]},"application/vnd.gerber":{source:"iana"},"application/vnd.globalplatform.card-content-mgt":{source:"iana"},"application/vnd.globalplatform.card-content-mgt-response":{source:"iana"},"application/vnd.gmx":{source:"iana",extensions:["gmx"]},"application/vnd.gnu.taler.exchange+json":{source:"iana",compressible:!0},"application/vnd.gnu.taler.merchant+json":{source:"iana",compressible:!0},"application/vnd.google-apps.audio":{},"application/vnd.google-apps.document":{compressible:!1,extensions:["gdoc"]},"application/vnd.google-apps.drawing":{compressible:!1,extensions:["gdraw"]},"application/vnd.google-apps.drive-sdk":{compressible:!1},"application/vnd.google-apps.file":{},"application/vnd.google-apps.folder":{compressible:!1},"application/vnd.google-apps.form":{compressible:!1,extensions:["gform"]},"application/vnd.google-apps.fusiontable":{},"application/vnd.google-apps.jam":{compressible:!1,extensions:["gjam"]},"application/vnd.google-apps.mail-layout":{},"application/vnd.google-apps.map":{compressible:!1,extensions:["gmap"]},"application/vnd.google-apps.photo":{},"application/vnd.google-apps.presentation":{compressible:!1,extensions:["gslides"]},"application/vnd.google-apps.script":{compressible:!1,extensions:["gscript"]},"application/vnd.google-apps.shortcut":{},"application/vnd.google-apps.site":{compressible:!1,extensions:["gsite"]},"application/vnd.google-apps.spreadsheet":{compressible:!1,extensions:["gsheet"]},"application/vnd.google-apps.unknown":{},"application/vnd.google-apps.video":{},"application/vnd.google-earth.kml+xml":{source:"iana",compressible:!0,extensions:["kml"]},"application/vnd.google-earth.kmz":{source:"iana",compressible:!1,extensions:["kmz"]},"application/vnd.gov.sk.e-form+xml":{source:"apache",compressible:!0},"application/vnd.gov.sk.e-form+zip":{source:"iana",compressible:!1},"application/vnd.gov.sk.xmldatacontainer+xml":{source:"iana",compressible:!0,extensions:["xdcf"]},"application/vnd.gpxsee.map+xml":{source:"iana",compressible:!0},"application/vnd.grafeq":{source:"iana",extensions:["gqf","gqs"]},"application/vnd.gridmp":{source:"iana"},"application/vnd.groove-account":{source:"iana",extensions:["gac"]},"application/vnd.groove-help":{source:"iana",extensions:["ghf"]},"application/vnd.groove-identity-message":{source:"iana",extensions:["gim"]},"application/vnd.groove-injector":{source:"iana",extensions:["grv"]},"application/vnd.groove-tool-message":{source:"iana",extensions:["gtm"]},"application/vnd.groove-tool-template":{source:"iana",extensions:["tpl"]},"application/vnd.groove-vcard":{source:"iana",extensions:["vcg"]},"application/vnd.hal+json":{source:"iana",compressible:!0},"application/vnd.hal+xml":{source:"iana",compressible:!0,extensions:["hal"]},"application/vnd.handheld-entertainment+xml":{source:"iana",compressible:!0,extensions:["zmm"]},"application/vnd.hbci":{source:"iana",extensions:["hbci"]},"application/vnd.hc+json":{source:"iana",compressible:!0},"application/vnd.hcl-bireports":{source:"iana"},"application/vnd.hdt":{source:"iana"},"application/vnd.heroku+json":{source:"iana",compressible:!0},"application/vnd.hhe.lesson-player":{source:"iana",extensions:["les"]},"application/vnd.hp-hpgl":{source:"iana",extensions:["hpgl"]},"application/vnd.hp-hpid":{source:"iana",extensions:["hpid"]},"application/vnd.hp-hps":{source:"iana",extensions:["hps"]},"application/vnd.hp-jlyt":{source:"iana",extensions:["jlt"]},"application/vnd.hp-pcl":{source:"iana",extensions:["pcl"]},"application/vnd.hp-pclxl":{source:"iana",extensions:["pclxl"]},"application/vnd.hsl":{source:"iana"},"application/vnd.httphone":{source:"iana"},"application/vnd.hydrostatix.sof-data":{source:"iana",extensions:["sfd-hdstx"]},"application/vnd.hyper+json":{source:"iana",compressible:!0},"application/vnd.hyper-item+json":{source:"iana",compressible:!0},"application/vnd.hyperdrive+json":{source:"iana",compressible:!0},"application/vnd.hzn-3d-crossword":{source:"iana"},"application/vnd.ibm.afplinedata":{source:"apache"},"application/vnd.ibm.electronic-media":{source:"iana"},"application/vnd.ibm.minipay":{source:"iana",extensions:["mpy"]},"application/vnd.ibm.modcap":{source:"apache",extensions:["afp","listafp","list3820"]},"application/vnd.ibm.rights-management":{source:"iana",extensions:["irm"]},"application/vnd.ibm.secure-container":{source:"iana",extensions:["sc"]},"application/vnd.iccprofile":{source:"iana",extensions:["icc","icm"]},"application/vnd.ieee.1905":{source:"iana"},"application/vnd.igloader":{source:"iana",extensions:["igl"]},"application/vnd.imagemeter.folder+zip":{source:"iana",compressible:!1},"application/vnd.imagemeter.image+zip":{source:"iana",compressible:!1},"application/vnd.immervision-ivp":{source:"iana",extensions:["ivp"]},"application/vnd.immervision-ivu":{source:"iana",extensions:["ivu"]},"application/vnd.ims.imsccv1p1":{source:"iana"},"application/vnd.ims.imsccv1p2":{source:"iana"},"application/vnd.ims.imsccv1p3":{source:"iana"},"application/vnd.ims.lis.v2.result+json":{source:"iana",compressible:!0},"application/vnd.ims.lti.v2.toolconsumerprofile+json":{source:"iana",compressible:!0},"application/vnd.ims.lti.v2.toolproxy+json":{source:"iana",compressible:!0},"application/vnd.ims.lti.v2.toolproxy.id+json":{source:"iana",compressible:!0},"application/vnd.ims.lti.v2.toolsettings+json":{source:"iana",compressible:!0},"application/vnd.ims.lti.v2.toolsettings.simple+json":{source:"iana",compressible:!0},"application/vnd.informedcontrol.rms+xml":{source:"iana",compressible:!0},"application/vnd.informix-visionary":{source:"apache"},"application/vnd.infotech.project":{source:"iana"},"application/vnd.infotech.project+xml":{source:"iana",compressible:!0},"application/vnd.innopath.wamp.notification":{source:"iana"},"application/vnd.insors.igm":{source:"iana",extensions:["igm"]},"application/vnd.intercon.formnet":{source:"iana",extensions:["xpw","xpx"]},"application/vnd.intergeo":{source:"iana",extensions:["i2g"]},"application/vnd.intertrust.digibox":{source:"iana"},"application/vnd.intertrust.nncp":{source:"iana"},"application/vnd.intu.qbo":{source:"iana",extensions:["qbo"]},"application/vnd.intu.qfx":{source:"iana",extensions:["qfx"]},"application/vnd.ipfs.ipns-record":{source:"iana"},"application/vnd.ipld.car":{source:"iana"},"application/vnd.ipld.dag-cbor":{source:"iana"},"application/vnd.ipld.dag-json":{source:"iana"},"application/vnd.ipld.raw":{source:"iana"},"application/vnd.iptc.g2.catalogitem+xml":{source:"iana",compressible:!0},"application/vnd.iptc.g2.conceptitem+xml":{source:"iana",compressible:!0},"application/vnd.iptc.g2.knowledgeitem+xml":{source:"iana",compressible:!0},"application/vnd.iptc.g2.newsitem+xml":{source:"iana",compressible:!0},"application/vnd.iptc.g2.newsmessage+xml":{source:"iana",compressible:!0},"application/vnd.iptc.g2.packageitem+xml":{source:"iana",compressible:!0},"application/vnd.iptc.g2.planningitem+xml":{source:"iana",compressible:!0},"application/vnd.ipunplugged.rcprofile":{source:"iana",extensions:["rcprofile"]},"application/vnd.irepository.package+xml":{source:"iana",compressible:!0,extensions:["irp"]},"application/vnd.is-xpr":{source:"iana",extensions:["xpr"]},"application/vnd.isac.fcs":{source:"iana",extensions:["fcs"]},"application/vnd.iso11783-10+zip":{source:"iana",compressible:!1},"application/vnd.jam":{source:"iana",extensions:["jam"]},"application/vnd.japannet-directory-service":{source:"iana"},"application/vnd.japannet-jpnstore-wakeup":{source:"iana"},"application/vnd.japannet-payment-wakeup":{source:"iana"},"application/vnd.japannet-registration":{source:"iana"},"application/vnd.japannet-registration-wakeup":{source:"iana"},"application/vnd.japannet-setstore-wakeup":{source:"iana"},"application/vnd.japannet-verification":{source:"iana"},"application/vnd.japannet-verification-wakeup":{source:"iana"},"application/vnd.jcp.javame.midlet-rms":{source:"iana",extensions:["rms"]},"application/vnd.jisp":{source:"iana",extensions:["jisp"]},"application/vnd.joost.joda-archive":{source:"iana",extensions:["joda"]},"application/vnd.jsk.isdn-ngn":{source:"iana"},"application/vnd.kahootz":{source:"iana",extensions:["ktz","ktr"]},"application/vnd.kde.karbon":{source:"iana",extensions:["karbon"]},"application/vnd.kde.kchart":{source:"iana",extensions:["chrt"]},"application/vnd.kde.kformula":{source:"iana",extensions:["kfo"]},"application/vnd.kde.kivio":{source:"iana",extensions:["flw"]},"application/vnd.kde.kontour":{source:"iana",extensions:["kon"]},"application/vnd.kde.kpresenter":{source:"iana",extensions:["kpr","kpt"]},"application/vnd.kde.kspread":{source:"iana",extensions:["ksp"]},"application/vnd.kde.kword":{source:"iana",extensions:["kwd","kwt"]},"application/vnd.kdl":{source:"iana"},"application/vnd.kenameaapp":{source:"iana",extensions:["htke"]},"application/vnd.keyman.kmp+zip":{source:"iana",compressible:!1},"application/vnd.keyman.kmx":{source:"iana"},"application/vnd.kidspiration":{source:"iana",extensions:["kia"]},"application/vnd.kinar":{source:"iana",extensions:["kne","knp"]},"application/vnd.koan":{source:"iana",extensions:["skp","skd","skt","skm"]},"application/vnd.kodak-descriptor":{source:"iana",extensions:["sse"]},"application/vnd.las":{source:"iana"},"application/vnd.las.las+json":{source:"iana",compressible:!0},"application/vnd.las.las+xml":{source:"iana",compressible:!0,extensions:["lasxml"]},"application/vnd.laszip":{source:"iana"},"application/vnd.ldev.productlicensing":{source:"iana"},"application/vnd.leap+json":{source:"iana",compressible:!0},"application/vnd.liberty-request+xml":{source:"iana",compressible:!0},"application/vnd.llamagraphics.life-balance.desktop":{source:"iana",extensions:["lbd"]},"application/vnd.llamagraphics.life-balance.exchange+xml":{source:"iana",compressible:!0,extensions:["lbe"]},"application/vnd.logipipe.circuit+zip":{source:"iana",compressible:!1},"application/vnd.loom":{source:"iana"},"application/vnd.lotus-1-2-3":{source:"iana",extensions:["123"]},"application/vnd.lotus-approach":{source:"iana",extensions:["apr"]},"application/vnd.lotus-freelance":{source:"iana",extensions:["pre"]},"application/vnd.lotus-notes":{source:"iana",extensions:["nsf"]},"application/vnd.lotus-organizer":{source:"iana",extensions:["org"]},"application/vnd.lotus-screencam":{source:"iana",extensions:["scm"]},"application/vnd.lotus-wordpro":{source:"iana",extensions:["lwp"]},"application/vnd.macports.portpkg":{source:"iana",extensions:["portpkg"]},"application/vnd.mapbox-vector-tile":{source:"iana",extensions:["mvt"]},"application/vnd.marlin.drm.actiontoken+xml":{source:"iana",compressible:!0},"application/vnd.marlin.drm.conftoken+xml":{source:"iana",compressible:!0},"application/vnd.marlin.drm.license+xml":{source:"iana",compressible:!0},"application/vnd.marlin.drm.mdcf":{source:"iana"},"application/vnd.mason+json":{source:"iana",compressible:!0},"application/vnd.maxar.archive.3tz+zip":{source:"iana",compressible:!1},"application/vnd.maxmind.maxmind-db":{source:"iana"},"application/vnd.mcd":{source:"iana",extensions:["mcd"]},"application/vnd.mdl":{source:"iana"},"application/vnd.mdl-mbsdf":{source:"iana"},"application/vnd.medcalcdata":{source:"iana",extensions:["mc1"]},"application/vnd.mediastation.cdkey":{source:"iana",extensions:["cdkey"]},"application/vnd.medicalholodeck.recordxr":{source:"iana"},"application/vnd.meridian-slingshot":{source:"iana"},"application/vnd.mermaid":{source:"iana"},"application/vnd.mfer":{source:"iana",extensions:["mwf"]},"application/vnd.mfmp":{source:"iana",extensions:["mfm"]},"application/vnd.micro+json":{source:"iana",compressible:!0},"application/vnd.micrografx.flo":{source:"iana",extensions:["flo"]},"application/vnd.micrografx.igx":{source:"iana",extensions:["igx"]},"application/vnd.microsoft.portable-executable":{source:"iana"},"application/vnd.microsoft.windows.thumbnail-cache":{source:"iana"},"application/vnd.miele+json":{source:"iana",compressible:!0},"application/vnd.mif":{source:"iana",extensions:["mif"]},"application/vnd.minisoft-hp3000-save":{source:"iana"},"application/vnd.mitsubishi.misty-guard.trustweb":{source:"iana"},"application/vnd.mobius.daf":{source:"iana",extensions:["daf"]},"application/vnd.mobius.dis":{source:"iana",extensions:["dis"]},"application/vnd.mobius.mbk":{source:"iana",extensions:["mbk"]},"application/vnd.mobius.mqy":{source:"iana",extensions:["mqy"]},"application/vnd.mobius.msl":{source:"iana",extensions:["msl"]},"application/vnd.mobius.plc":{source:"iana",extensions:["plc"]},"application/vnd.mobius.txf":{source:"iana",extensions:["txf"]},"application/vnd.modl":{source:"iana"},"application/vnd.mophun.application":{source:"iana",extensions:["mpn"]},"application/vnd.mophun.certificate":{source:"iana",extensions:["mpc"]},"application/vnd.motorola.flexsuite":{source:"iana"},"application/vnd.motorola.flexsuite.adsi":{source:"iana"},"application/vnd.motorola.flexsuite.fis":{source:"iana"},"application/vnd.motorola.flexsuite.gotap":{source:"iana"},"application/vnd.motorola.flexsuite.kmr":{source:"iana"},"application/vnd.motorola.flexsuite.ttc":{source:"iana"},"application/vnd.motorola.flexsuite.wem":{source:"iana"},"application/vnd.motorola.iprm":{source:"iana"},"application/vnd.mozilla.xul+xml":{source:"iana",compressible:!0,extensions:["xul"]},"application/vnd.ms-3mfdocument":{source:"iana"},"application/vnd.ms-artgalry":{source:"iana",extensions:["cil"]},"application/vnd.ms-asf":{source:"iana"},"application/vnd.ms-cab-compressed":{source:"iana",extensions:["cab"]},"application/vnd.ms-color.iccprofile":{source:"apache"},"application/vnd.ms-excel":{source:"iana",compressible:!1,extensions:["xls","xlm","xla","xlc","xlt","xlw"]},"application/vnd.ms-excel.addin.macroenabled.12":{source:"iana",extensions:["xlam"]},"application/vnd.ms-excel.sheet.binary.macroenabled.12":{source:"iana",extensions:["xlsb"]},"application/vnd.ms-excel.sheet.macroenabled.12":{source:"iana",extensions:["xlsm"]},"application/vnd.ms-excel.template.macroenabled.12":{source:"iana",extensions:["xltm"]},"application/vnd.ms-fontobject":{source:"iana",compressible:!0,extensions:["eot"]},"application/vnd.ms-htmlhelp":{source:"iana",extensions:["chm"]},"application/vnd.ms-ims":{source:"iana",extensions:["ims"]},"application/vnd.ms-lrm":{source:"iana",extensions:["lrm"]},"application/vnd.ms-office.activex+xml":{source:"iana",compressible:!0},"application/vnd.ms-officetheme":{source:"iana",extensions:["thmx"]},"application/vnd.ms-opentype":{source:"apache",compressible:!0},"application/vnd.ms-outlook":{compressible:!1,extensions:["msg"]},"application/vnd.ms-package.obfuscated-opentype":{source:"apache"},"application/vnd.ms-pki.seccat":{source:"apache",extensions:["cat"]},"application/vnd.ms-pki.stl":{source:"apache",extensions:["stl"]},"application/vnd.ms-playready.initiator+xml":{source:"iana",compressible:!0},"application/vnd.ms-powerpoint":{source:"iana",compressible:!1,extensions:["ppt","pps","pot"]},"application/vnd.ms-powerpoint.addin.macroenabled.12":{source:"iana",extensions:["ppam"]},"application/vnd.ms-powerpoint.presentation.macroenabled.12":{source:"iana",extensions:["pptm"]},"application/vnd.ms-powerpoint.slide.macroenabled.12":{source:"iana",extensions:["sldm"]},"application/vnd.ms-powerpoint.slideshow.macroenabled.12":{source:"iana",extensions:["ppsm"]},"application/vnd.ms-powerpoint.template.macroenabled.12":{source:"iana",extensions:["potm"]},"application/vnd.ms-printdevicecapabilities+xml":{source:"iana",compressible:!0},"application/vnd.ms-printing.printticket+xml":{source:"apache",compressible:!0},"application/vnd.ms-printschematicket+xml":{source:"iana",compressible:!0},"application/vnd.ms-project":{source:"iana",extensions:["mpp","mpt"]},"application/vnd.ms-tnef":{source:"iana"},"application/vnd.ms-visio.viewer":{extensions:["vdx"]},"application/vnd.ms-windows.devicepairing":{source:"iana"},"application/vnd.ms-windows.nwprinting.oob":{source:"iana"},"application/vnd.ms-windows.printerpairing":{source:"iana"},"application/vnd.ms-windows.wsd.oob":{source:"iana"},"application/vnd.ms-wmdrm.lic-chlg-req":{source:"iana"},"application/vnd.ms-wmdrm.lic-resp":{source:"iana"},"application/vnd.ms-wmdrm.meter-chlg-req":{source:"iana"},"application/vnd.ms-wmdrm.meter-resp":{source:"iana"},"application/vnd.ms-word.document.macroenabled.12":{source:"iana",extensions:["docm"]},"application/vnd.ms-word.template.macroenabled.12":{source:"iana",extensions:["dotm"]},"application/vnd.ms-works":{source:"iana",extensions:["wps","wks","wcm","wdb"]},"application/vnd.ms-wpl":{source:"iana",extensions:["wpl"]},"application/vnd.ms-xpsdocument":{source:"iana",compressible:!1,extensions:["xps"]},"application/vnd.msa-disk-image":{source:"iana"},"application/vnd.mseq":{source:"iana",extensions:["mseq"]},"application/vnd.msgpack":{source:"iana"},"application/vnd.msign":{source:"iana"},"application/vnd.multiad.creator":{source:"iana"},"application/vnd.multiad.creator.cif":{source:"iana"},"application/vnd.music-niff":{source:"iana"},"application/vnd.musician":{source:"iana",extensions:["mus"]},"application/vnd.muvee.style":{source:"iana",extensions:["msty"]},"application/vnd.mynfc":{source:"iana",extensions:["taglet"]},"application/vnd.nacamar.ybrid+json":{source:"iana",compressible:!0},"application/vnd.nato.bindingdataobject+cbor":{source:"iana"},"application/vnd.nato.bindingdataobject+json":{source:"iana",compressible:!0},"application/vnd.nato.bindingdataobject+xml":{source:"iana",compressible:!0,extensions:["bdo"]},"application/vnd.nato.openxmlformats-package.iepd+zip":{source:"iana",compressible:!1},"application/vnd.ncd.control":{source:"iana"},"application/vnd.ncd.reference":{source:"iana"},"application/vnd.nearst.inv+json":{source:"iana",compressible:!0},"application/vnd.nebumind.line":{source:"iana"},"application/vnd.nervana":{source:"iana"},"application/vnd.netfpx":{source:"iana"},"application/vnd.neurolanguage.nlu":{source:"iana",extensions:["nlu"]},"application/vnd.nimn":{source:"iana"},"application/vnd.nintendo.nitro.rom":{source:"iana"},"application/vnd.nintendo.snes.rom":{source:"iana"},"application/vnd.nitf":{source:"iana",extensions:["ntf","nitf"]},"application/vnd.noblenet-directory":{source:"iana",extensions:["nnd"]},"application/vnd.noblenet-sealer":{source:"iana",extensions:["nns"]},"application/vnd.noblenet-web":{source:"iana",extensions:["nnw"]},"application/vnd.nokia.catalogs":{source:"iana"},"application/vnd.nokia.conml+wbxml":{source:"iana"},"application/vnd.nokia.conml+xml":{source:"iana",compressible:!0},"application/vnd.nokia.iptv.config+xml":{source:"iana",compressible:!0},"application/vnd.nokia.isds-radio-presets":{source:"iana"},"application/vnd.nokia.landmark+wbxml":{source:"iana"},"application/vnd.nokia.landmark+xml":{source:"iana",compressible:!0},"application/vnd.nokia.landmarkcollection+xml":{source:"iana",compressible:!0},"application/vnd.nokia.n-gage.ac+xml":{source:"iana",compressible:!0,extensions:["ac"]},"application/vnd.nokia.n-gage.data":{source:"iana",extensions:["ngdat"]},"application/vnd.nokia.n-gage.symbian.install":{source:"apache",extensions:["n-gage"]},"application/vnd.nokia.ncd":{source:"iana"},"application/vnd.nokia.pcd+wbxml":{source:"iana"},"application/vnd.nokia.pcd+xml":{source:"iana",compressible:!0},"application/vnd.nokia.radio-preset":{source:"iana",extensions:["rpst"]},"application/vnd.nokia.radio-presets":{source:"iana",extensions:["rpss"]},"application/vnd.novadigm.edm":{source:"iana",extensions:["edm"]},"application/vnd.novadigm.edx":{source:"iana",extensions:["edx"]},"application/vnd.novadigm.ext":{source:"iana",extensions:["ext"]},"application/vnd.ntt-local.content-share":{source:"iana"},"application/vnd.ntt-local.file-transfer":{source:"iana"},"application/vnd.ntt-local.ogw_remote-access":{source:"iana"},"application/vnd.ntt-local.sip-ta_remote":{source:"iana"},"application/vnd.ntt-local.sip-ta_tcp_stream":{source:"iana"},"application/vnd.oai.workflows":{source:"iana"},"application/vnd.oai.workflows+json":{source:"iana",compressible:!0},"application/vnd.oai.workflows+yaml":{source:"iana"},"application/vnd.oasis.opendocument.base":{source:"iana"},"application/vnd.oasis.opendocument.chart":{source:"iana",extensions:["odc"]},"application/vnd.oasis.opendocument.chart-template":{source:"iana",extensions:["otc"]},"application/vnd.oasis.opendocument.database":{source:"apache",extensions:["odb"]},"application/vnd.oasis.opendocument.formula":{source:"iana",extensions:["odf"]},"application/vnd.oasis.opendocument.formula-template":{source:"iana",extensions:["odft"]},"application/vnd.oasis.opendocument.graphics":{source:"iana",compressible:!1,extensions:["odg"]},"application/vnd.oasis.opendocument.graphics-template":{source:"iana",extensions:["otg"]},"application/vnd.oasis.opendocument.image":{source:"iana",extensions:["odi"]},"application/vnd.oasis.opendocument.image-template":{source:"iana",extensions:["oti"]},"application/vnd.oasis.opendocument.presentation":{source:"iana",compressible:!1,extensions:["odp"]},"application/vnd.oasis.opendocument.presentation-template":{source:"iana",extensions:["otp"]},"application/vnd.oasis.opendocument.spreadsheet":{source:"iana",compressible:!1,extensions:["ods"]},"application/vnd.oasis.opendocument.spreadsheet-template":{source:"iana",extensions:["ots"]},"application/vnd.oasis.opendocument.text":{source:"iana",compressible:!1,extensions:["odt"]},"application/vnd.oasis.opendocument.text-master":{source:"iana",extensions:["odm"]},"application/vnd.oasis.opendocument.text-master-template":{source:"iana"},"application/vnd.oasis.opendocument.text-template":{source:"iana",extensions:["ott"]},"application/vnd.oasis.opendocument.text-web":{source:"iana",extensions:["oth"]},"application/vnd.obn":{source:"iana"},"application/vnd.ocf+cbor":{source:"iana"},"application/vnd.oci.image.manifest.v1+json":{source:"iana",compressible:!0},"application/vnd.oftn.l10n+json":{source:"iana",compressible:!0},"application/vnd.oipf.contentaccessdownload+xml":{source:"iana",compressible:!0},"application/vnd.oipf.contentaccessstreaming+xml":{source:"iana",compressible:!0},"application/vnd.oipf.cspg-hexbinary":{source:"iana"},"application/vnd.oipf.dae.svg+xml":{source:"iana",compressible:!0},"application/vnd.oipf.dae.xhtml+xml":{source:"iana",compressible:!0},"application/vnd.oipf.mippvcontrolmessage+xml":{source:"iana",compressible:!0},"application/vnd.oipf.pae.gem":{source:"iana"},"application/vnd.oipf.spdiscovery+xml":{source:"iana",compressible:!0},"application/vnd.oipf.spdlist+xml":{source:"iana",compressible:!0},"application/vnd.oipf.ueprofile+xml":{source:"iana",compressible:!0},"application/vnd.oipf.userprofile+xml":{source:"iana",compressible:!0},"application/vnd.olpc-sugar":{source:"iana",extensions:["xo"]},"application/vnd.oma-scws-config":{source:"iana"},"application/vnd.oma-scws-http-request":{source:"iana"},"application/vnd.oma-scws-http-response":{source:"iana"},"application/vnd.oma.bcast.associated-procedure-parameter+xml":{source:"iana",compressible:!0},"application/vnd.oma.bcast.drm-trigger+xml":{source:"apache",compressible:!0},"application/vnd.oma.bcast.imd+xml":{source:"iana",compressible:!0},"application/vnd.oma.bcast.ltkm":{source:"iana"},"application/vnd.oma.bcast.notification+xml":{source:"iana",compressible:!0},"application/vnd.oma.bcast.provisioningtrigger":{source:"iana"},"application/vnd.oma.bcast.sgboot":{source:"iana"},"application/vnd.oma.bcast.sgdd+xml":{source:"iana",compressible:!0},"application/vnd.oma.bcast.sgdu":{source:"iana"},"application/vnd.oma.bcast.simple-symbol-container":{source:"iana"},"application/vnd.oma.bcast.smartcard-trigger+xml":{source:"apache",compressible:!0},"application/vnd.oma.bcast.sprov+xml":{source:"iana",compressible:!0},"application/vnd.oma.bcast.stkm":{source:"iana"},"application/vnd.oma.cab-address-book+xml":{source:"iana",compressible:!0},"application/vnd.oma.cab-feature-handler+xml":{source:"iana",compressible:!0},"application/vnd.oma.cab-pcc+xml":{source:"iana",compressible:!0},"application/vnd.oma.cab-subs-invite+xml":{source:"iana",compressible:!0},"application/vnd.oma.cab-user-prefs+xml":{source:"iana",compressible:!0},"application/vnd.oma.dcd":{source:"iana"},"application/vnd.oma.dcdc":{source:"iana"},"application/vnd.oma.dd2+xml":{source:"iana",compressible:!0,extensions:["dd2"]},"application/vnd.oma.drm.risd+xml":{source:"iana",compressible:!0},"application/vnd.oma.group-usage-list+xml":{source:"iana",compressible:!0},"application/vnd.oma.lwm2m+cbor":{source:"iana"},"application/vnd.oma.lwm2m+json":{source:"iana",compressible:!0},"application/vnd.oma.lwm2m+tlv":{source:"iana"},"application/vnd.oma.pal+xml":{source:"iana",compressible:!0},"application/vnd.oma.poc.detailed-progress-report+xml":{source:"iana",compressible:!0},"application/vnd.oma.poc.final-report+xml":{source:"iana",compressible:!0},"application/vnd.oma.poc.groups+xml":{source:"iana",compressible:!0},"application/vnd.oma.poc.invocation-descriptor+xml":{source:"iana",compressible:!0},"application/vnd.oma.poc.optimized-progress-report+xml":{source:"iana",compressible:!0},"application/vnd.oma.push":{source:"iana"},"application/vnd.oma.scidm.messages+xml":{source:"iana",compressible:!0},"application/vnd.oma.xcap-directory+xml":{source:"iana",compressible:!0},"application/vnd.omads-email+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/vnd.omads-file+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/vnd.omads-folder+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/vnd.omaloc-supl-init":{source:"iana"},"application/vnd.onepager":{source:"iana"},"application/vnd.onepagertamp":{source:"iana"},"application/vnd.onepagertamx":{source:"iana"},"application/vnd.onepagertat":{source:"iana"},"application/vnd.onepagertatp":{source:"iana"},"application/vnd.onepagertatx":{source:"iana"},"application/vnd.onvif.metadata":{source:"iana"},"application/vnd.openblox.game+xml":{source:"iana",compressible:!0,extensions:["obgx"]},"application/vnd.openblox.game-binary":{source:"iana"},"application/vnd.openeye.oeb":{source:"iana"},"application/vnd.openofficeorg.extension":{source:"apache",extensions:["oxt"]},"application/vnd.openstreetmap.data+xml":{source:"iana",compressible:!0,extensions:["osm"]},"application/vnd.opentimestamps.ots":{source:"iana"},"application/vnd.openvpi.dspx+json":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.custom-properties+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.customxmlproperties+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.drawing+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.drawingml.chart+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.extended-properties+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.comments+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.presentation":{source:"iana",compressible:!1,extensions:["pptx"]},"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.presprops+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.slide":{source:"iana",extensions:["sldx"]},"application/vnd.openxmlformats-officedocument.presentationml.slide+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.slideshow":{source:"iana",extensions:["ppsx"]},"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.tags+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.template":{source:"iana",extensions:["potx"]},"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{source:"iana",compressible:!1,extensions:["xlsx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.template":{source:"iana",extensions:["xltx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.theme+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.themeoverride+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.vmldrawing":{source:"iana"},"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.document":{source:"iana",compressible:!1,extensions:["docx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.template":{source:"iana",extensions:["dotx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-package.core-properties+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml":{source:"iana",compressible:!0},"application/vnd.openxmlformats-package.relationships+xml":{source:"iana",compressible:!0},"application/vnd.oracle.resource+json":{source:"iana",compressible:!0},"application/vnd.orange.indata":{source:"iana"},"application/vnd.osa.netdeploy":{source:"iana"},"application/vnd.osgeo.mapguide.package":{source:"iana",extensions:["mgp"]},"application/vnd.osgi.bundle":{source:"iana"},"application/vnd.osgi.dp":{source:"iana",extensions:["dp"]},"application/vnd.osgi.subsystem":{source:"iana",extensions:["esa"]},"application/vnd.otps.ct-kip+xml":{source:"iana",compressible:!0},"application/vnd.oxli.countgraph":{source:"iana"},"application/vnd.pagerduty+json":{source:"iana",compressible:!0},"application/vnd.palm":{source:"iana",extensions:["pdb","pqa","oprc"]},"application/vnd.panoply":{source:"iana"},"application/vnd.paos.xml":{source:"iana"},"application/vnd.patentdive":{source:"iana"},"application/vnd.patientecommsdoc":{source:"iana"},"application/vnd.pawaafile":{source:"iana",extensions:["paw"]},"application/vnd.pcos":{source:"iana"},"application/vnd.pg.format":{source:"iana",extensions:["str"]},"application/vnd.pg.osasli":{source:"iana",extensions:["ei6"]},"application/vnd.piaccess.application-licence":{source:"iana"},"application/vnd.picsel":{source:"iana",extensions:["efif"]},"application/vnd.pmi.widget":{source:"iana",extensions:["wg"]},"application/vnd.poc.group-advertisement+xml":{source:"iana",compressible:!0},"application/vnd.pocketlearn":{source:"iana",extensions:["plf"]},"application/vnd.powerbuilder6":{source:"iana",extensions:["pbd"]},"application/vnd.powerbuilder6-s":{source:"iana"},"application/vnd.powerbuilder7":{source:"iana"},"application/vnd.powerbuilder7-s":{source:"iana"},"application/vnd.powerbuilder75":{source:"iana"},"application/vnd.powerbuilder75-s":{source:"iana"},"application/vnd.preminet":{source:"iana"},"application/vnd.previewsystems.box":{source:"iana",extensions:["box"]},"application/vnd.procrate.brushset":{extensions:["brushset"]},"application/vnd.procreate.brush":{extensions:["brush"]},"application/vnd.procreate.dream":{extensions:["drm"]},"application/vnd.proteus.magazine":{source:"iana",extensions:["mgz"]},"application/vnd.psfs":{source:"iana"},"application/vnd.pt.mundusmundi":{source:"iana"},"application/vnd.publishare-delta-tree":{source:"iana",extensions:["qps"]},"application/vnd.pvi.ptid1":{source:"iana",extensions:["ptid"]},"application/vnd.pwg-multiplexed":{source:"iana"},"application/vnd.pwg-xhtml-print+xml":{source:"iana",compressible:!0,extensions:["xhtm"]},"application/vnd.qualcomm.brew-app-res":{source:"iana"},"application/vnd.quarantainenet":{source:"iana"},"application/vnd.quark.quarkxpress":{source:"iana",extensions:["qxd","qxt","qwd","qwt","qxl","qxb"]},"application/vnd.quobject-quoxdocument":{source:"iana"},"application/vnd.radisys.moml+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-audit+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-audit-conf+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-audit-conn+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-audit-dialog+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-audit-stream+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-conf+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-dialog+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-dialog-base+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-dialog-fax-detect+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-dialog-fax-sendrecv+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-dialog-group+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-dialog-speech+xml":{source:"iana",compressible:!0},"application/vnd.radisys.msml-dialog-transform+xml":{source:"iana",compressible:!0},"application/vnd.rainstor.data":{source:"iana"},"application/vnd.rapid":{source:"iana"},"application/vnd.rar":{source:"iana",extensions:["rar"]},"application/vnd.realvnc.bed":{source:"iana",extensions:["bed"]},"application/vnd.recordare.musicxml":{source:"iana",extensions:["mxl"]},"application/vnd.recordare.musicxml+xml":{source:"iana",compressible:!0,extensions:["musicxml"]},"application/vnd.relpipe":{source:"iana"},"application/vnd.renlearn.rlprint":{source:"iana"},"application/vnd.resilient.logic":{source:"iana"},"application/vnd.restful+json":{source:"iana",compressible:!0},"application/vnd.rig.cryptonote":{source:"iana",extensions:["cryptonote"]},"application/vnd.rim.cod":{source:"apache",extensions:["cod"]},"application/vnd.rn-realmedia":{source:"apache",extensions:["rm"]},"application/vnd.rn-realmedia-vbr":{source:"apache",extensions:["rmvb"]},"application/vnd.route66.link66+xml":{source:"iana",compressible:!0,extensions:["link66"]},"application/vnd.rs-274x":{source:"iana"},"application/vnd.ruckus.download":{source:"iana"},"application/vnd.s3sms":{source:"iana"},"application/vnd.sailingtracker.track":{source:"iana",extensions:["st"]},"application/vnd.sar":{source:"iana"},"application/vnd.sbm.cid":{source:"iana"},"application/vnd.sbm.mid2":{source:"iana"},"application/vnd.scribus":{source:"iana"},"application/vnd.sealed.3df":{source:"iana"},"application/vnd.sealed.csf":{source:"iana"},"application/vnd.sealed.doc":{source:"iana"},"application/vnd.sealed.eml":{source:"iana"},"application/vnd.sealed.mht":{source:"iana"},"application/vnd.sealed.net":{source:"iana"},"application/vnd.sealed.ppt":{source:"iana"},"application/vnd.sealed.tiff":{source:"iana"},"application/vnd.sealed.xls":{source:"iana"},"application/vnd.sealedmedia.softseal.html":{source:"iana"},"application/vnd.sealedmedia.softseal.pdf":{source:"iana"},"application/vnd.seemail":{source:"iana",extensions:["see"]},"application/vnd.seis+json":{source:"iana",compressible:!0},"application/vnd.sema":{source:"iana",extensions:["sema"]},"application/vnd.semd":{source:"iana",extensions:["semd"]},"application/vnd.semf":{source:"iana",extensions:["semf"]},"application/vnd.shade-save-file":{source:"iana"},"application/vnd.shana.informed.formdata":{source:"iana",extensions:["ifm"]},"application/vnd.shana.informed.formtemplate":{source:"iana",extensions:["itp"]},"application/vnd.shana.informed.interchange":{source:"iana",extensions:["iif"]},"application/vnd.shana.informed.package":{source:"iana",extensions:["ipk"]},"application/vnd.shootproof+json":{source:"iana",compressible:!0},"application/vnd.shopkick+json":{source:"iana",compressible:!0},"application/vnd.shp":{source:"iana"},"application/vnd.shx":{source:"iana"},"application/vnd.sigrok.session":{source:"iana"},"application/vnd.simtech-mindmapper":{source:"iana",extensions:["twd","twds"]},"application/vnd.siren+json":{source:"iana",compressible:!0},"application/vnd.sketchometry":{source:"iana"},"application/vnd.smaf":{source:"iana",extensions:["mmf"]},"application/vnd.smart.notebook":{source:"iana"},"application/vnd.smart.teacher":{source:"iana",extensions:["teacher"]},"application/vnd.smintio.portals.archive":{source:"iana"},"application/vnd.snesdev-page-table":{source:"iana"},"application/vnd.software602.filler.form+xml":{source:"iana",compressible:!0,extensions:["fo"]},"application/vnd.software602.filler.form-xml-zip":{source:"iana"},"application/vnd.solent.sdkm+xml":{source:"iana",compressible:!0,extensions:["sdkm","sdkd"]},"application/vnd.spotfire.dxp":{source:"iana",extensions:["dxp"]},"application/vnd.spotfire.sfs":{source:"iana",extensions:["sfs"]},"application/vnd.sqlite3":{source:"iana"},"application/vnd.sss-cod":{source:"iana"},"application/vnd.sss-dtf":{source:"iana"},"application/vnd.sss-ntf":{source:"iana"},"application/vnd.stardivision.calc":{source:"apache",extensions:["sdc"]},"application/vnd.stardivision.draw":{source:"apache",extensions:["sda"]},"application/vnd.stardivision.impress":{source:"apache",extensions:["sdd"]},"application/vnd.stardivision.math":{source:"apache",extensions:["smf"]},"application/vnd.stardivision.writer":{source:"apache",extensions:["sdw","vor"]},"application/vnd.stardivision.writer-global":{source:"apache",extensions:["sgl"]},"application/vnd.stepmania.package":{source:"iana",extensions:["smzip"]},"application/vnd.stepmania.stepchart":{source:"iana",extensions:["sm"]},"application/vnd.street-stream":{source:"iana"},"application/vnd.sun.wadl+xml":{source:"iana",compressible:!0,extensions:["wadl"]},"application/vnd.sun.xml.calc":{source:"apache",extensions:["sxc"]},"application/vnd.sun.xml.calc.template":{source:"apache",extensions:["stc"]},"application/vnd.sun.xml.draw":{source:"apache",extensions:["sxd"]},"application/vnd.sun.xml.draw.template":{source:"apache",extensions:["std"]},"application/vnd.sun.xml.impress":{source:"apache",extensions:["sxi"]},"application/vnd.sun.xml.impress.template":{source:"apache",extensions:["sti"]},"application/vnd.sun.xml.math":{source:"apache",extensions:["sxm"]},"application/vnd.sun.xml.writer":{source:"apache",extensions:["sxw"]},"application/vnd.sun.xml.writer.global":{source:"apache",extensions:["sxg"]},"application/vnd.sun.xml.writer.template":{source:"apache",extensions:["stw"]},"application/vnd.sus-calendar":{source:"iana",extensions:["sus","susp"]},"application/vnd.svd":{source:"iana",extensions:["svd"]},"application/vnd.swiftview-ics":{source:"iana"},"application/vnd.sybyl.mol2":{source:"iana"},"application/vnd.sycle+xml":{source:"iana",compressible:!0},"application/vnd.syft+json":{source:"iana",compressible:!0},"application/vnd.symbian.install":{source:"apache",extensions:["sis","sisx"]},"application/vnd.syncml+xml":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["xsm"]},"application/vnd.syncml.dm+wbxml":{source:"iana",charset:"UTF-8",extensions:["bdm"]},"application/vnd.syncml.dm+xml":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["xdm"]},"application/vnd.syncml.dm.notification":{source:"iana"},"application/vnd.syncml.dmddf+wbxml":{source:"iana"},"application/vnd.syncml.dmddf+xml":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["ddf"]},"application/vnd.syncml.dmtnds+wbxml":{source:"iana"},"application/vnd.syncml.dmtnds+xml":{source:"iana",charset:"UTF-8",compressible:!0},"application/vnd.syncml.ds.notification":{source:"iana"},"application/vnd.tableschema+json":{source:"iana",compressible:!0},"application/vnd.tao.intent-module-archive":{source:"iana",extensions:["tao"]},"application/vnd.tcpdump.pcap":{source:"iana",extensions:["pcap","cap","dmp"]},"application/vnd.think-cell.ppttc+json":{source:"iana",compressible:!0},"application/vnd.tmd.mediaflex.api+xml":{source:"iana",compressible:!0},"application/vnd.tml":{source:"iana"},"application/vnd.tmobile-livetv":{source:"iana",extensions:["tmo"]},"application/vnd.tri.onesource":{source:"iana"},"application/vnd.trid.tpt":{source:"iana",extensions:["tpt"]},"application/vnd.triscape.mxs":{source:"iana",extensions:["mxs"]},"application/vnd.trueapp":{source:"iana",extensions:["tra"]},"application/vnd.truedoc":{source:"iana"},"application/vnd.ubisoft.webplayer":{source:"iana"},"application/vnd.ufdl":{source:"iana",extensions:["ufd","ufdl"]},"application/vnd.uic.osdm+json":{source:"iana",compressible:!0},"application/vnd.uiq.theme":{source:"iana",extensions:["utz"]},"application/vnd.umajin":{source:"iana",extensions:["umj"]},"application/vnd.unity":{source:"iana",extensions:["unityweb"]},"application/vnd.uoml+xml":{source:"iana",compressible:!0,extensions:["uoml","uo"]},"application/vnd.uplanet.alert":{source:"iana"},"application/vnd.uplanet.alert-wbxml":{source:"iana"},"application/vnd.uplanet.bearer-choice":{source:"iana"},"application/vnd.uplanet.bearer-choice-wbxml":{source:"iana"},"application/vnd.uplanet.cacheop":{source:"iana"},"application/vnd.uplanet.cacheop-wbxml":{source:"iana"},"application/vnd.uplanet.channel":{source:"iana"},"application/vnd.uplanet.channel-wbxml":{source:"iana"},"application/vnd.uplanet.list":{source:"iana"},"application/vnd.uplanet.list-wbxml":{source:"iana"},"application/vnd.uplanet.listcmd":{source:"iana"},"application/vnd.uplanet.listcmd-wbxml":{source:"iana"},"application/vnd.uplanet.signal":{source:"iana"},"application/vnd.uri-map":{source:"iana"},"application/vnd.valve.source.material":{source:"iana"},"application/vnd.vcx":{source:"iana",extensions:["vcx"]},"application/vnd.vd-study":{source:"iana"},"application/vnd.vectorworks":{source:"iana"},"application/vnd.vel+json":{source:"iana",compressible:!0},"application/vnd.veraison.tsm-report+cbor":{source:"iana"},"application/vnd.veraison.tsm-report+json":{source:"iana",compressible:!0},"application/vnd.verimatrix.vcas":{source:"iana"},"application/vnd.veritone.aion+json":{source:"iana",compressible:!0},"application/vnd.veryant.thin":{source:"iana"},"application/vnd.ves.encrypted":{source:"iana"},"application/vnd.vidsoft.vidconference":{source:"iana"},"application/vnd.visio":{source:"iana",extensions:["vsd","vst","vss","vsw","vsdx","vtx"]},"application/vnd.visionary":{source:"iana",extensions:["vis"]},"application/vnd.vividence.scriptfile":{source:"iana"},"application/vnd.vocalshaper.vsp4":{source:"iana"},"application/vnd.vsf":{source:"iana",extensions:["vsf"]},"application/vnd.wap.sic":{source:"iana"},"application/vnd.wap.slc":{source:"iana"},"application/vnd.wap.wbxml":{source:"iana",charset:"UTF-8",extensions:["wbxml"]},"application/vnd.wap.wmlc":{source:"iana",extensions:["wmlc"]},"application/vnd.wap.wmlscriptc":{source:"iana",extensions:["wmlsc"]},"application/vnd.wasmflow.wafl":{source:"iana"},"application/vnd.webturbo":{source:"iana",extensions:["wtb"]},"application/vnd.wfa.dpp":{source:"iana"},"application/vnd.wfa.p2p":{source:"iana"},"application/vnd.wfa.wsc":{source:"iana"},"application/vnd.windows.devicepairing":{source:"iana"},"application/vnd.wmc":{source:"iana"},"application/vnd.wmf.bootstrap":{source:"iana"},"application/vnd.wolfram.mathematica":{source:"iana"},"application/vnd.wolfram.mathematica.package":{source:"iana"},"application/vnd.wolfram.player":{source:"iana",extensions:["nbp"]},"application/vnd.wordlift":{source:"iana"},"application/vnd.wordperfect":{source:"iana",extensions:["wpd"]},"application/vnd.wqd":{source:"iana",extensions:["wqd"]},"application/vnd.wrq-hp3000-labelled":{source:"iana"},"application/vnd.wt.stf":{source:"iana",extensions:["stf"]},"application/vnd.wv.csp+wbxml":{source:"iana"},"application/vnd.wv.csp+xml":{source:"iana",compressible:!0},"application/vnd.wv.ssp+xml":{source:"iana",compressible:!0},"application/vnd.xacml+json":{source:"iana",compressible:!0},"application/vnd.xara":{source:"iana",extensions:["xar"]},"application/vnd.xarin.cpj":{source:"iana"},"application/vnd.xecrets-encrypted":{source:"iana"},"application/vnd.xfdl":{source:"iana",extensions:["xfdl"]},"application/vnd.xfdl.webform":{source:"iana"},"application/vnd.xmi+xml":{source:"iana",compressible:!0},"application/vnd.xmpie.cpkg":{source:"iana"},"application/vnd.xmpie.dpkg":{source:"iana"},"application/vnd.xmpie.plan":{source:"iana"},"application/vnd.xmpie.ppkg":{source:"iana"},"application/vnd.xmpie.xlim":{source:"iana"},"application/vnd.yamaha.hv-dic":{source:"iana",extensions:["hvd"]},"application/vnd.yamaha.hv-script":{source:"iana",extensions:["hvs"]},"application/vnd.yamaha.hv-voice":{source:"iana",extensions:["hvp"]},"application/vnd.yamaha.openscoreformat":{source:"iana",extensions:["osf"]},"application/vnd.yamaha.openscoreformat.osfpvg+xml":{source:"iana",compressible:!0,extensions:["osfpvg"]},"application/vnd.yamaha.remote-setup":{source:"iana"},"application/vnd.yamaha.smaf-audio":{source:"iana",extensions:["saf"]},"application/vnd.yamaha.smaf-phrase":{source:"iana",extensions:["spf"]},"application/vnd.yamaha.through-ngn":{source:"iana"},"application/vnd.yamaha.tunnel-udpencap":{source:"iana"},"application/vnd.yaoweme":{source:"iana"},"application/vnd.yellowriver-custom-menu":{source:"iana",extensions:["cmp"]},"application/vnd.zul":{source:"iana",extensions:["zir","zirz"]},"application/vnd.zzazz.deck+xml":{source:"iana",compressible:!0,extensions:["zaz"]},"application/voicexml+xml":{source:"iana",compressible:!0,extensions:["vxml"]},"application/voucher-cms+json":{source:"iana",compressible:!0},"application/voucher-jws+json":{source:"iana",compressible:!0},"application/vp":{source:"iana"},"application/vp+cose":{source:"iana"},"application/vp+jwt":{source:"iana"},"application/vq-rtcpxr":{source:"iana"},"application/wasm":{source:"iana",compressible:!0,extensions:["wasm"]},"application/watcherinfo+xml":{source:"iana",compressible:!0,extensions:["wif"]},"application/webpush-options+json":{source:"iana",compressible:!0},"application/whoispp-query":{source:"iana"},"application/whoispp-response":{source:"iana"},"application/widget":{source:"iana",extensions:["wgt"]},"application/winhlp":{source:"apache",extensions:["hlp"]},"application/wita":{source:"iana"},"application/wordperfect5.1":{source:"iana"},"application/wsdl+xml":{source:"iana",compressible:!0,extensions:["wsdl"]},"application/wspolicy+xml":{source:"iana",compressible:!0,extensions:["wspolicy"]},"application/x-7z-compressed":{source:"apache",compressible:!1,extensions:["7z"]},"application/x-abiword":{source:"apache",extensions:["abw"]},"application/x-ace-compressed":{source:"apache",extensions:["ace"]},"application/x-amf":{source:"apache"},"application/x-apple-diskimage":{source:"apache",extensions:["dmg"]},"application/x-arj":{compressible:!1,extensions:["arj"]},"application/x-authorware-bin":{source:"apache",extensions:["aab","x32","u32","vox"]},"application/x-authorware-map":{source:"apache",extensions:["aam"]},"application/x-authorware-seg":{source:"apache",extensions:["aas"]},"application/x-bcpio":{source:"apache",extensions:["bcpio"]},"application/x-bdoc":{compressible:!1,extensions:["bdoc"]},"application/x-bittorrent":{source:"apache",extensions:["torrent"]},"application/x-blender":{extensions:["blend"]},"application/x-blorb":{source:"apache",extensions:["blb","blorb"]},"application/x-bzip":{source:"apache",compressible:!1,extensions:["bz"]},"application/x-bzip2":{source:"apache",compressible:!1,extensions:["bz2","boz"]},"application/x-cbr":{source:"apache",extensions:["cbr","cba","cbt","cbz","cb7"]},"application/x-cdlink":{source:"apache",extensions:["vcd"]},"application/x-cfs-compressed":{source:"apache",extensions:["cfs"]},"application/x-chat":{source:"apache",extensions:["chat"]},"application/x-chess-pgn":{source:"apache",extensions:["pgn"]},"application/x-chrome-extension":{extensions:["crx"]},"application/x-cocoa":{source:"nginx",extensions:["cco"]},"application/x-compress":{source:"apache"},"application/x-compressed":{extensions:["rar"]},"application/x-conference":{source:"apache",extensions:["nsc"]},"application/x-cpio":{source:"apache",extensions:["cpio"]},"application/x-csh":{source:"apache",extensions:["csh"]},"application/x-deb":{compressible:!1},"application/x-debian-package":{source:"apache",extensions:["deb","udeb"]},"application/x-dgc-compressed":{source:"apache",extensions:["dgc"]},"application/x-director":{source:"apache",extensions:["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"]},"application/x-doom":{source:"apache",extensions:["wad"]},"application/x-dtbncx+xml":{source:"apache",compressible:!0,extensions:["ncx"]},"application/x-dtbook+xml":{source:"apache",compressible:!0,extensions:["dtb"]},"application/x-dtbresource+xml":{source:"apache",compressible:!0,extensions:["res"]},"application/x-dvi":{source:"apache",compressible:!1,extensions:["dvi"]},"application/x-envoy":{source:"apache",extensions:["evy"]},"application/x-eva":{source:"apache",extensions:["eva"]},"application/x-font-bdf":{source:"apache",extensions:["bdf"]},"application/x-font-dos":{source:"apache"},"application/x-font-framemaker":{source:"apache"},"application/x-font-ghostscript":{source:"apache",extensions:["gsf"]},"application/x-font-libgrx":{source:"apache"},"application/x-font-linux-psf":{source:"apache",extensions:["psf"]},"application/x-font-pcf":{source:"apache",extensions:["pcf"]},"application/x-font-snf":{source:"apache",extensions:["snf"]},"application/x-font-speedo":{source:"apache"},"application/x-font-sunos-news":{source:"apache"},"application/x-font-type1":{source:"apache",extensions:["pfa","pfb","pfm","afm"]},"application/x-font-vfont":{source:"apache"},"application/x-freearc":{source:"apache",extensions:["arc"]},"application/x-futuresplash":{source:"apache",extensions:["spl"]},"application/x-gca-compressed":{source:"apache",extensions:["gca"]},"application/x-glulx":{source:"apache",extensions:["ulx"]},"application/x-gnumeric":{source:"apache",extensions:["gnumeric"]},"application/x-gramps-xml":{source:"apache",extensions:["gramps"]},"application/x-gtar":{source:"apache",extensions:["gtar"]},"application/x-gzip":{source:"apache"},"application/x-hdf":{source:"apache",extensions:["hdf"]},"application/x-httpd-php":{compressible:!0,extensions:["php"]},"application/x-install-instructions":{source:"apache",extensions:["install"]},"application/x-ipynb+json":{compressible:!0,extensions:["ipynb"]},"application/x-iso9660-image":{source:"apache",extensions:["iso"]},"application/x-iwork-keynote-sffkey":{extensions:["key"]},"application/x-iwork-numbers-sffnumbers":{extensions:["numbers"]},"application/x-iwork-pages-sffpages":{extensions:["pages"]},"application/x-java-archive-diff":{source:"nginx",extensions:["jardiff"]},"application/x-java-jnlp-file":{source:"apache",compressible:!1,extensions:["jnlp"]},"application/x-javascript":{compressible:!0},"application/x-keepass2":{extensions:["kdbx"]},"application/x-latex":{source:"apache",compressible:!1,extensions:["latex"]},"application/x-lua-bytecode":{extensions:["luac"]},"application/x-lzh-compressed":{source:"apache",extensions:["lzh","lha"]},"application/x-makeself":{source:"nginx",extensions:["run"]},"application/x-mie":{source:"apache",extensions:["mie"]},"application/x-mobipocket-ebook":{source:"apache",extensions:["prc","mobi"]},"application/x-mpegurl":{compressible:!1},"application/x-ms-application":{source:"apache",extensions:["application"]},"application/x-ms-shortcut":{source:"apache",extensions:["lnk"]},"application/x-ms-wmd":{source:"apache",extensions:["wmd"]},"application/x-ms-wmz":{source:"apache",extensions:["wmz"]},"application/x-ms-xbap":{source:"apache",extensions:["xbap"]},"application/x-msaccess":{source:"apache",extensions:["mdb"]},"application/x-msbinder":{source:"apache",extensions:["obd"]},"application/x-mscardfile":{source:"apache",extensions:["crd"]},"application/x-msclip":{source:"apache",extensions:["clp"]},"application/x-msdos-program":{extensions:["exe"]},"application/x-msdownload":{source:"apache",extensions:["exe","dll","com","bat","msi"]},"application/x-msmediaview":{source:"apache",extensions:["mvb","m13","m14"]},"application/x-msmetafile":{source:"apache",extensions:["wmf","wmz","emf","emz"]},"application/x-msmoney":{source:"apache",extensions:["mny"]},"application/x-mspublisher":{source:"apache",extensions:["pub"]},"application/x-msschedule":{source:"apache",extensions:["scd"]},"application/x-msterminal":{source:"apache",extensions:["trm"]},"application/x-mswrite":{source:"apache",extensions:["wri"]},"application/x-netcdf":{source:"apache",extensions:["nc","cdf"]},"application/x-ns-proxy-autoconfig":{compressible:!0,extensions:["pac"]},"application/x-nzb":{source:"apache",extensions:["nzb"]},"application/x-perl":{source:"nginx",extensions:["pl","pm"]},"application/x-pilot":{source:"nginx",extensions:["prc","pdb"]},"application/x-pkcs12":{source:"apache",compressible:!1,extensions:["p12","pfx"]},"application/x-pkcs7-certificates":{source:"apache",extensions:["p7b","spc"]},"application/x-pkcs7-certreqresp":{source:"apache",extensions:["p7r"]},"application/x-pki-message":{source:"iana"},"application/x-rar-compressed":{source:"apache",compressible:!1,extensions:["rar"]},"application/x-redhat-package-manager":{source:"nginx",extensions:["rpm"]},"application/x-research-info-systems":{source:"apache",extensions:["ris"]},"application/x-sea":{source:"nginx",extensions:["sea"]},"application/x-sh":{source:"apache",compressible:!0,extensions:["sh"]},"application/x-shar":{source:"apache",extensions:["shar"]},"application/x-shockwave-flash":{source:"apache",compressible:!1,extensions:["swf"]},"application/x-silverlight-app":{source:"apache",extensions:["xap"]},"application/x-sql":{source:"apache",extensions:["sql"]},"application/x-stuffit":{source:"apache",compressible:!1,extensions:["sit"]},"application/x-stuffitx":{source:"apache",extensions:["sitx"]},"application/x-subrip":{source:"apache",extensions:["srt"]},"application/x-sv4cpio":{source:"apache",extensions:["sv4cpio"]},"application/x-sv4crc":{source:"apache",extensions:["sv4crc"]},"application/x-t3vm-image":{source:"apache",extensions:["t3"]},"application/x-tads":{source:"apache",extensions:["gam"]},"application/x-tar":{source:"apache",compressible:!0,extensions:["tar"]},"application/x-tcl":{source:"apache",extensions:["tcl","tk"]},"application/x-tex":{source:"apache",extensions:["tex"]},"application/x-tex-tfm":{source:"apache",extensions:["tfm"]},"application/x-texinfo":{source:"apache",extensions:["texinfo","texi"]},"application/x-tgif":{source:"apache",extensions:["obj"]},"application/x-ustar":{source:"apache",extensions:["ustar"]},"application/x-virtualbox-hdd":{compressible:!0,extensions:["hdd"]},"application/x-virtualbox-ova":{compressible:!0,extensions:["ova"]},"application/x-virtualbox-ovf":{compressible:!0,extensions:["ovf"]},"application/x-virtualbox-vbox":{compressible:!0,extensions:["vbox"]},"application/x-virtualbox-vbox-extpack":{compressible:!1,extensions:["vbox-extpack"]},"application/x-virtualbox-vdi":{compressible:!0,extensions:["vdi"]},"application/x-virtualbox-vhd":{compressible:!0,extensions:["vhd"]},"application/x-virtualbox-vmdk":{compressible:!0,extensions:["vmdk"]},"application/x-wais-source":{source:"apache",extensions:["src"]},"application/x-web-app-manifest+json":{compressible:!0,extensions:["webapp"]},"application/x-www-form-urlencoded":{source:"iana",compressible:!0},"application/x-x509-ca-cert":{source:"iana",extensions:["der","crt","pem"]},"application/x-x509-ca-ra-cert":{source:"iana"},"application/x-x509-next-ca-cert":{source:"iana"},"application/x-xfig":{source:"apache",extensions:["fig"]},"application/x-xliff+xml":{source:"apache",compressible:!0,extensions:["xlf"]},"application/x-xpinstall":{source:"apache",compressible:!1,extensions:["xpi"]},"application/x-xz":{source:"apache",extensions:["xz"]},"application/x-zip-compressed":{extensions:["zip"]},"application/x-zmachine":{source:"apache",extensions:["z1","z2","z3","z4","z5","z6","z7","z8"]},"application/x400-bp":{source:"iana"},"application/xacml+xml":{source:"iana",compressible:!0},"application/xaml+xml":{source:"apache",compressible:!0,extensions:["xaml"]},"application/xcap-att+xml":{source:"iana",compressible:!0,extensions:["xav"]},"application/xcap-caps+xml":{source:"iana",compressible:!0,extensions:["xca"]},"application/xcap-diff+xml":{source:"iana",compressible:!0,extensions:["xdf"]},"application/xcap-el+xml":{source:"iana",compressible:!0,extensions:["xel"]},"application/xcap-error+xml":{source:"iana",compressible:!0},"application/xcap-ns+xml":{source:"iana",compressible:!0,extensions:["xns"]},"application/xcon-conference-info+xml":{source:"iana",compressible:!0},"application/xcon-conference-info-diff+xml":{source:"iana",compressible:!0},"application/xenc+xml":{source:"iana",compressible:!0,extensions:["xenc"]},"application/xfdf":{source:"iana",extensions:["xfdf"]},"application/xhtml+xml":{source:"iana",compressible:!0,extensions:["xhtml","xht"]},"application/xhtml-voice+xml":{source:"apache",compressible:!0},"application/xliff+xml":{source:"iana",compressible:!0,extensions:["xlf"]},"application/xml":{source:"iana",compressible:!0,extensions:["xml","xsl","xsd","rng"]},"application/xml-dtd":{source:"iana",compressible:!0,extensions:["dtd"]},"application/xml-external-parsed-entity":{source:"iana"},"application/xml-patch+xml":{source:"iana",compressible:!0},"application/xmpp+xml":{source:"iana",compressible:!0},"application/xop+xml":{source:"iana",compressible:!0,extensions:["xop"]},"application/xproc+xml":{source:"apache",compressible:!0,extensions:["xpl"]},"application/xslt+xml":{source:"iana",compressible:!0,extensions:["xsl","xslt"]},"application/xspf+xml":{source:"apache",compressible:!0,extensions:["xspf"]},"application/xv+xml":{source:"iana",compressible:!0,extensions:["mxml","xhvml","xvml","xvm"]},"application/yaml":{source:"iana"},"application/yang":{source:"iana",extensions:["yang"]},"application/yang-data+cbor":{source:"iana"},"application/yang-data+json":{source:"iana",compressible:!0},"application/yang-data+xml":{source:"iana",compressible:!0},"application/yang-patch+json":{source:"iana",compressible:!0},"application/yang-patch+xml":{source:"iana",compressible:!0},"application/yang-sid+json":{source:"iana",compressible:!0},"application/yin+xml":{source:"iana",compressible:!0,extensions:["yin"]},"application/zip":{source:"iana",compressible:!1,extensions:["zip"]},"application/zip+dotlottie":{extensions:["lottie"]},"application/zlib":{source:"iana"},"application/zstd":{source:"iana"},"audio/1d-interleaved-parityfec":{source:"iana"},"audio/32kadpcm":{source:"iana"},"audio/3gpp":{source:"iana",compressible:!1,extensions:["3gpp"]},"audio/3gpp2":{source:"iana"},"audio/aac":{source:"iana",extensions:["adts","aac"]},"audio/ac3":{source:"iana"},"audio/adpcm":{source:"apache",extensions:["adp"]},"audio/amr":{source:"iana",extensions:["amr"]},"audio/amr-wb":{source:"iana"},"audio/amr-wb+":{source:"iana"},"audio/aptx":{source:"iana"},"audio/asc":{source:"iana"},"audio/atrac-advanced-lossless":{source:"iana"},"audio/atrac-x":{source:"iana"},"audio/atrac3":{source:"iana"},"audio/basic":{source:"iana",compressible:!1,extensions:["au","snd"]},"audio/bv16":{source:"iana"},"audio/bv32":{source:"iana"},"audio/clearmode":{source:"iana"},"audio/cn":{source:"iana"},"audio/dat12":{source:"iana"},"audio/dls":{source:"iana"},"audio/dsr-es201108":{source:"iana"},"audio/dsr-es202050":{source:"iana"},"audio/dsr-es202211":{source:"iana"},"audio/dsr-es202212":{source:"iana"},"audio/dv":{source:"iana"},"audio/dvi4":{source:"iana"},"audio/eac3":{source:"iana"},"audio/encaprtp":{source:"iana"},"audio/evrc":{source:"iana"},"audio/evrc-qcp":{source:"iana"},"audio/evrc0":{source:"iana"},"audio/evrc1":{source:"iana"},"audio/evrcb":{source:"iana"},"audio/evrcb0":{source:"iana"},"audio/evrcb1":{source:"iana"},"audio/evrcnw":{source:"iana"},"audio/evrcnw0":{source:"iana"},"audio/evrcnw1":{source:"iana"},"audio/evrcwb":{source:"iana"},"audio/evrcwb0":{source:"iana"},"audio/evrcwb1":{source:"iana"},"audio/evs":{source:"iana"},"audio/flac":{source:"iana"},"audio/flexfec":{source:"iana"},"audio/fwdred":{source:"iana"},"audio/g711-0":{source:"iana"},"audio/g719":{source:"iana"},"audio/g722":{source:"iana"},"audio/g7221":{source:"iana"},"audio/g723":{source:"iana"},"audio/g726-16":{source:"iana"},"audio/g726-24":{source:"iana"},"audio/g726-32":{source:"iana"},"audio/g726-40":{source:"iana"},"audio/g728":{source:"iana"},"audio/g729":{source:"iana"},"audio/g7291":{source:"iana"},"audio/g729d":{source:"iana"},"audio/g729e":{source:"iana"},"audio/gsm":{source:"iana"},"audio/gsm-efr":{source:"iana"},"audio/gsm-hr-08":{source:"iana"},"audio/ilbc":{source:"iana"},"audio/ip-mr_v2.5":{source:"iana"},"audio/isac":{source:"apache"},"audio/l16":{source:"iana"},"audio/l20":{source:"iana"},"audio/l24":{source:"iana",compressible:!1},"audio/l8":{source:"iana"},"audio/lpc":{source:"iana"},"audio/matroska":{source:"iana"},"audio/melp":{source:"iana"},"audio/melp1200":{source:"iana"},"audio/melp2400":{source:"iana"},"audio/melp600":{source:"iana"},"audio/mhas":{source:"iana"},"audio/midi":{source:"apache",extensions:["mid","midi","kar","rmi"]},"audio/midi-clip":{source:"iana"},"audio/mobile-xmf":{source:"iana",extensions:["mxmf"]},"audio/mp3":{compressible:!1,extensions:["mp3"]},"audio/mp4":{source:"iana",compressible:!1,extensions:["m4a","mp4a","m4b"]},"audio/mp4a-latm":{source:"iana"},"audio/mpa":{source:"iana"},"audio/mpa-robust":{source:"iana"},"audio/mpeg":{source:"iana",compressible:!1,extensions:["mpga","mp2","mp2a","mp3","m2a","m3a"]},"audio/mpeg4-generic":{source:"iana"},"audio/musepack":{source:"apache"},"audio/ogg":{source:"iana",compressible:!1,extensions:["oga","ogg","spx","opus"]},"audio/opus":{source:"iana"},"audio/parityfec":{source:"iana"},"audio/pcma":{source:"iana"},"audio/pcma-wb":{source:"iana"},"audio/pcmu":{source:"iana"},"audio/pcmu-wb":{source:"iana"},"audio/prs.sid":{source:"iana"},"audio/qcelp":{source:"iana"},"audio/raptorfec":{source:"iana"},"audio/red":{source:"iana"},"audio/rtp-enc-aescm128":{source:"iana"},"audio/rtp-midi":{source:"iana"},"audio/rtploopback":{source:"iana"},"audio/rtx":{source:"iana"},"audio/s3m":{source:"apache",extensions:["s3m"]},"audio/scip":{source:"iana"},"audio/silk":{source:"apache",extensions:["sil"]},"audio/smv":{source:"iana"},"audio/smv-qcp":{source:"iana"},"audio/smv0":{source:"iana"},"audio/sofa":{source:"iana"},"audio/sp-midi":{source:"iana"},"audio/speex":{source:"iana"},"audio/t140c":{source:"iana"},"audio/t38":{source:"iana"},"audio/telephone-event":{source:"iana"},"audio/tetra_acelp":{source:"iana"},"audio/tetra_acelp_bb":{source:"iana"},"audio/tone":{source:"iana"},"audio/tsvcis":{source:"iana"},"audio/uemclip":{source:"iana"},"audio/ulpfec":{source:"iana"},"audio/usac":{source:"iana"},"audio/vdvi":{source:"iana"},"audio/vmr-wb":{source:"iana"},"audio/vnd.3gpp.iufp":{source:"iana"},"audio/vnd.4sb":{source:"iana"},"audio/vnd.audiokoz":{source:"iana"},"audio/vnd.celp":{source:"iana"},"audio/vnd.cisco.nse":{source:"iana"},"audio/vnd.cmles.radio-events":{source:"iana"},"audio/vnd.cns.anp1":{source:"iana"},"audio/vnd.cns.inf1":{source:"iana"},"audio/vnd.dece.audio":{source:"iana",extensions:["uva","uvva"]},"audio/vnd.digital-winds":{source:"iana",extensions:["eol"]},"audio/vnd.dlna.adts":{source:"iana"},"audio/vnd.dolby.heaac.1":{source:"iana"},"audio/vnd.dolby.heaac.2":{source:"iana"},"audio/vnd.dolby.mlp":{source:"iana"},"audio/vnd.dolby.mps":{source:"iana"},"audio/vnd.dolby.pl2":{source:"iana"},"audio/vnd.dolby.pl2x":{source:"iana"},"audio/vnd.dolby.pl2z":{source:"iana"},"audio/vnd.dolby.pulse.1":{source:"iana"},"audio/vnd.dra":{source:"iana",extensions:["dra"]},"audio/vnd.dts":{source:"iana",extensions:["dts"]},"audio/vnd.dts.hd":{source:"iana",extensions:["dtshd"]},"audio/vnd.dts.uhd":{source:"iana"},"audio/vnd.dvb.file":{source:"iana"},"audio/vnd.everad.plj":{source:"iana"},"audio/vnd.hns.audio":{source:"iana"},"audio/vnd.lucent.voice":{source:"iana",extensions:["lvp"]},"audio/vnd.ms-playready.media.pya":{source:"iana",extensions:["pya"]},"audio/vnd.nokia.mobile-xmf":{source:"iana"},"audio/vnd.nortel.vbk":{source:"iana"},"audio/vnd.nuera.ecelp4800":{source:"iana",extensions:["ecelp4800"]},"audio/vnd.nuera.ecelp7470":{source:"iana",extensions:["ecelp7470"]},"audio/vnd.nuera.ecelp9600":{source:"iana",extensions:["ecelp9600"]},"audio/vnd.octel.sbc":{source:"iana"},"audio/vnd.presonus.multitrack":{source:"iana"},"audio/vnd.qcelp":{source:"apache"},"audio/vnd.rhetorex.32kadpcm":{source:"iana"},"audio/vnd.rip":{source:"iana",extensions:["rip"]},"audio/vnd.rn-realaudio":{compressible:!1},"audio/vnd.sealedmedia.softseal.mpeg":{source:"iana"},"audio/vnd.vmx.cvsd":{source:"iana"},"audio/vnd.wave":{compressible:!1},"audio/vorbis":{source:"iana",compressible:!1},"audio/vorbis-config":{source:"iana"},"audio/wav":{compressible:!1,extensions:["wav"]},"audio/wave":{compressible:!1,extensions:["wav"]},"audio/webm":{source:"apache",compressible:!1,extensions:["weba"]},"audio/x-aac":{source:"apache",compressible:!1,extensions:["aac"]},"audio/x-aiff":{source:"apache",extensions:["aif","aiff","aifc"]},"audio/x-caf":{source:"apache",compressible:!1,extensions:["caf"]},"audio/x-flac":{source:"apache",extensions:["flac"]},"audio/x-m4a":{source:"nginx",extensions:["m4a"]},"audio/x-matroska":{source:"apache",extensions:["mka"]},"audio/x-mpegurl":{source:"apache",extensions:["m3u"]},"audio/x-ms-wax":{source:"apache",extensions:["wax"]},"audio/x-ms-wma":{source:"apache",extensions:["wma"]},"audio/x-pn-realaudio":{source:"apache",extensions:["ram","ra"]},"audio/x-pn-realaudio-plugin":{source:"apache",extensions:["rmp"]},"audio/x-realaudio":{source:"nginx",extensions:["ra"]},"audio/x-tta":{source:"apache"},"audio/x-wav":{source:"apache",extensions:["wav"]},"audio/xm":{source:"apache",extensions:["xm"]},"chemical/x-cdx":{source:"apache",extensions:["cdx"]},"chemical/x-cif":{source:"apache",extensions:["cif"]},"chemical/x-cmdf":{source:"apache",extensions:["cmdf"]},"chemical/x-cml":{source:"apache",extensions:["cml"]},"chemical/x-csml":{source:"apache",extensions:["csml"]},"chemical/x-pdb":{source:"apache"},"chemical/x-xyz":{source:"apache",extensions:["xyz"]},"font/collection":{source:"iana",extensions:["ttc"]},"font/otf":{source:"iana",compressible:!0,extensions:["otf"]},"font/sfnt":{source:"iana"},"font/ttf":{source:"iana",compressible:!0,extensions:["ttf"]},"font/woff":{source:"iana",extensions:["woff"]},"font/woff2":{source:"iana",extensions:["woff2"]},"image/aces":{source:"iana",extensions:["exr"]},"image/apng":{source:"iana",compressible:!1,extensions:["apng"]},"image/avci":{source:"iana",extensions:["avci"]},"image/avcs":{source:"iana",extensions:["avcs"]},"image/avif":{source:"iana",compressible:!1,extensions:["avif"]},"image/bmp":{source:"iana",compressible:!0,extensions:["bmp","dib"]},"image/cgm":{source:"iana",extensions:["cgm"]},"image/dicom-rle":{source:"iana",extensions:["drle"]},"image/dpx":{source:"iana",extensions:["dpx"]},"image/emf":{source:"iana",extensions:["emf"]},"image/fits":{source:"iana",extensions:["fits"]},"image/g3fax":{source:"iana",extensions:["g3"]},"image/gif":{source:"iana",compressible:!1,extensions:["gif"]},"image/heic":{source:"iana",extensions:["heic"]},"image/heic-sequence":{source:"iana",extensions:["heics"]},"image/heif":{source:"iana",extensions:["heif"]},"image/heif-sequence":{source:"iana",extensions:["heifs"]},"image/hej2k":{source:"iana",extensions:["hej2"]},"image/ief":{source:"iana",extensions:["ief"]},"image/j2c":{source:"iana"},"image/jaii":{source:"iana",extensions:["jaii"]},"image/jais":{source:"iana",extensions:["jais"]},"image/jls":{source:"iana",extensions:["jls"]},"image/jp2":{source:"iana",compressible:!1,extensions:["jp2","jpg2"]},"image/jpeg":{source:"iana",compressible:!1,extensions:["jpg","jpeg","jpe"]},"image/jph":{source:"iana",extensions:["jph"]},"image/jphc":{source:"iana",extensions:["jhc"]},"image/jpm":{source:"iana",compressible:!1,extensions:["jpm","jpgm"]},"image/jpx":{source:"iana",compressible:!1,extensions:["jpx","jpf"]},"image/jxl":{source:"iana",extensions:["jxl"]},"image/jxr":{source:"iana",extensions:["jxr"]},"image/jxra":{source:"iana",extensions:["jxra"]},"image/jxrs":{source:"iana",extensions:["jxrs"]},"image/jxs":{source:"iana",extensions:["jxs"]},"image/jxsc":{source:"iana",extensions:["jxsc"]},"image/jxsi":{source:"iana",extensions:["jxsi"]},"image/jxss":{source:"iana",extensions:["jxss"]},"image/ktx":{source:"iana",extensions:["ktx"]},"image/ktx2":{source:"iana",extensions:["ktx2"]},"image/naplps":{source:"iana"},"image/pjpeg":{compressible:!1,extensions:["jfif"]},"image/png":{source:"iana",compressible:!1,extensions:["png"]},"image/prs.btif":{source:"iana",extensions:["btif","btf"]},"image/prs.pti":{source:"iana",extensions:["pti"]},"image/pwg-raster":{source:"iana"},"image/sgi":{source:"apache",extensions:["sgi"]},"image/svg+xml":{source:"iana",compressible:!0,extensions:["svg","svgz"]},"image/t38":{source:"iana",extensions:["t38"]},"image/tiff":{source:"iana",compressible:!1,extensions:["tif","tiff"]},"image/tiff-fx":{source:"iana",extensions:["tfx"]},"image/vnd.adobe.photoshop":{source:"iana",compressible:!0,extensions:["psd"]},"image/vnd.airzip.accelerator.azv":{source:"iana",extensions:["azv"]},"image/vnd.clip":{source:"iana"},"image/vnd.cns.inf2":{source:"iana"},"image/vnd.dece.graphic":{source:"iana",extensions:["uvi","uvvi","uvg","uvvg"]},"image/vnd.djvu":{source:"iana",extensions:["djvu","djv"]},"image/vnd.dvb.subtitle":{source:"iana",extensions:["sub"]},"image/vnd.dwg":{source:"iana",extensions:["dwg"]},"image/vnd.dxf":{source:"iana",extensions:["dxf"]},"image/vnd.fastbidsheet":{source:"iana",extensions:["fbs"]},"image/vnd.fpx":{source:"iana",extensions:["fpx"]},"image/vnd.fst":{source:"iana",extensions:["fst"]},"image/vnd.fujixerox.edmics-mmr":{source:"iana",extensions:["mmr"]},"image/vnd.fujixerox.edmics-rlc":{source:"iana",extensions:["rlc"]},"image/vnd.globalgraphics.pgb":{source:"iana"},"image/vnd.microsoft.icon":{source:"iana",compressible:!0,extensions:["ico"]},"image/vnd.mix":{source:"iana"},"image/vnd.mozilla.apng":{source:"iana"},"image/vnd.ms-dds":{compressible:!0,extensions:["dds"]},"image/vnd.ms-modi":{source:"iana",extensions:["mdi"]},"image/vnd.ms-photo":{source:"apache",extensions:["wdp"]},"image/vnd.net-fpx":{source:"iana",extensions:["npx"]},"image/vnd.pco.b16":{source:"iana",extensions:["b16"]},"image/vnd.radiance":{source:"iana"},"image/vnd.sealed.png":{source:"iana"},"image/vnd.sealedmedia.softseal.gif":{source:"iana"},"image/vnd.sealedmedia.softseal.jpg":{source:"iana"},"image/vnd.svf":{source:"iana"},"image/vnd.tencent.tap":{source:"iana",extensions:["tap"]},"image/vnd.valve.source.texture":{source:"iana",extensions:["vtf"]},"image/vnd.wap.wbmp":{source:"iana",extensions:["wbmp"]},"image/vnd.xiff":{source:"iana",extensions:["xif"]},"image/vnd.zbrush.pcx":{source:"iana",extensions:["pcx"]},"image/webp":{source:"iana",extensions:["webp"]},"image/wmf":{source:"iana",extensions:["wmf"]},"image/x-3ds":{source:"apache",extensions:["3ds"]},"image/x-adobe-dng":{extensions:["dng"]},"image/x-cmu-raster":{source:"apache",extensions:["ras"]},"image/x-cmx":{source:"apache",extensions:["cmx"]},"image/x-emf":{source:"iana"},"image/x-freehand":{source:"apache",extensions:["fh","fhc","fh4","fh5","fh7"]},"image/x-icon":{source:"apache",compressible:!0,extensions:["ico"]},"image/x-jng":{source:"nginx",extensions:["jng"]},"image/x-mrsid-image":{source:"apache",extensions:["sid"]},"image/x-ms-bmp":{source:"nginx",compressible:!0,extensions:["bmp"]},"image/x-pcx":{source:"apache",extensions:["pcx"]},"image/x-pict":{source:"apache",extensions:["pic","pct"]},"image/x-portable-anymap":{source:"apache",extensions:["pnm"]},"image/x-portable-bitmap":{source:"apache",extensions:["pbm"]},"image/x-portable-graymap":{source:"apache",extensions:["pgm"]},"image/x-portable-pixmap":{source:"apache",extensions:["ppm"]},"image/x-rgb":{source:"apache",extensions:["rgb"]},"image/x-tga":{source:"apache",extensions:["tga"]},"image/x-wmf":{source:"iana"},"image/x-xbitmap":{source:"apache",extensions:["xbm"]},"image/x-xcf":{compressible:!1},"image/x-xpixmap":{source:"apache",extensions:["xpm"]},"image/x-xwindowdump":{source:"apache",extensions:["xwd"]},"message/bhttp":{source:"iana"},"message/cpim":{source:"iana"},"message/delivery-status":{source:"iana"},"message/disposition-notification":{source:"iana",extensions:["disposition-notification"]},"message/external-body":{source:"iana"},"message/feedback-report":{source:"iana"},"message/global":{source:"iana",extensions:["u8msg"]},"message/global-delivery-status":{source:"iana",extensions:["u8dsn"]},"message/global-disposition-notification":{source:"iana",extensions:["u8mdn"]},"message/global-headers":{source:"iana",extensions:["u8hdr"]},"message/http":{source:"iana",compressible:!1},"message/imdn+xml":{source:"iana",compressible:!0},"message/mls":{source:"iana"},"message/news":{source:"apache"},"message/ohttp-req":{source:"iana"},"message/ohttp-res":{source:"iana"},"message/partial":{source:"iana",compressible:!1},"message/rfc822":{source:"iana",compressible:!0,extensions:["eml","mime","mht","mhtml"]},"message/s-http":{source:"apache"},"message/sip":{source:"iana"},"message/sipfrag":{source:"iana"},"message/tracking-status":{source:"iana"},"message/vnd.si.simp":{source:"apache"},"message/vnd.wfa.wsc":{source:"iana",extensions:["wsc"]},"model/3mf":{source:"iana",extensions:["3mf"]},"model/e57":{source:"iana"},"model/gltf+json":{source:"iana",compressible:!0,extensions:["gltf"]},"model/gltf-binary":{source:"iana",compressible:!0,extensions:["glb"]},"model/iges":{source:"iana",compressible:!1,extensions:["igs","iges"]},"model/jt":{source:"iana",extensions:["jt"]},"model/mesh":{source:"iana",compressible:!1,extensions:["msh","mesh","silo"]},"model/mtl":{source:"iana",extensions:["mtl"]},"model/obj":{source:"iana",extensions:["obj"]},"model/prc":{source:"iana",extensions:["prc"]},"model/step":{source:"iana",extensions:["step","stp","stpnc","p21","210"]},"model/step+xml":{source:"iana",compressible:!0,extensions:["stpx"]},"model/step+zip":{source:"iana",compressible:!1,extensions:["stpz"]},"model/step-xml+zip":{source:"iana",compressible:!1,extensions:["stpxz"]},"model/stl":{source:"iana",extensions:["stl"]},"model/u3d":{source:"iana",extensions:["u3d"]},"model/vnd.bary":{source:"iana",extensions:["bary"]},"model/vnd.cld":{source:"iana",extensions:["cld"]},"model/vnd.collada+xml":{source:"iana",compressible:!0,extensions:["dae"]},"model/vnd.dwf":{source:"iana",extensions:["dwf"]},"model/vnd.flatland.3dml":{source:"iana"},"model/vnd.gdl":{source:"iana",extensions:["gdl"]},"model/vnd.gs-gdl":{source:"apache"},"model/vnd.gs.gdl":{source:"iana"},"model/vnd.gtw":{source:"iana",extensions:["gtw"]},"model/vnd.moml+xml":{source:"iana",compressible:!0},"model/vnd.mts":{source:"iana",extensions:["mts"]},"model/vnd.opengex":{source:"iana",extensions:["ogex"]},"model/vnd.parasolid.transmit.binary":{source:"iana",extensions:["x_b"]},"model/vnd.parasolid.transmit.text":{source:"iana",extensions:["x_t"]},"model/vnd.pytha.pyox":{source:"iana",extensions:["pyo","pyox"]},"model/vnd.rosette.annotated-data-model":{source:"iana"},"model/vnd.sap.vds":{source:"iana",extensions:["vds"]},"model/vnd.usda":{source:"iana",extensions:["usda"]},"model/vnd.usdz+zip":{source:"iana",compressible:!1,extensions:["usdz"]},"model/vnd.valve.source.compiled-map":{source:"iana",extensions:["bsp"]},"model/vnd.vtu":{source:"iana",extensions:["vtu"]},"model/vrml":{source:"iana",compressible:!1,extensions:["wrl","vrml"]},"model/x3d+binary":{source:"apache",compressible:!1,extensions:["x3db","x3dbz"]},"model/x3d+fastinfoset":{source:"iana",extensions:["x3db"]},"model/x3d+vrml":{source:"apache",compressible:!1,extensions:["x3dv","x3dvz"]},"model/x3d+xml":{source:"iana",compressible:!0,extensions:["x3d","x3dz"]},"model/x3d-vrml":{source:"iana",extensions:["x3dv"]},"multipart/alternative":{source:"iana",compressible:!1},"multipart/appledouble":{source:"iana"},"multipart/byteranges":{source:"iana"},"multipart/digest":{source:"iana"},"multipart/encrypted":{source:"iana",compressible:!1},"multipart/form-data":{source:"iana",compressible:!1},"multipart/header-set":{source:"iana"},"multipart/mixed":{source:"iana"},"multipart/multilingual":{source:"iana"},"multipart/parallel":{source:"iana"},"multipart/related":{source:"iana",compressible:!1},"multipart/report":{source:"iana"},"multipart/signed":{source:"iana",compressible:!1},"multipart/vnd.bint.med-plus":{source:"iana"},"multipart/voice-message":{source:"iana"},"multipart/x-mixed-replace":{source:"iana"},"text/1d-interleaved-parityfec":{source:"iana"},"text/cache-manifest":{source:"iana",compressible:!0,extensions:["appcache","manifest"]},"text/calendar":{source:"iana",extensions:["ics","ifb"]},"text/calender":{compressible:!0},"text/cmd":{compressible:!0},"text/coffeescript":{extensions:["coffee","litcoffee"]},"text/cql":{source:"iana"},"text/cql-expression":{source:"iana"},"text/cql-identifier":{source:"iana"},"text/css":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["css"]},"text/csv":{source:"iana",compressible:!0,extensions:["csv"]},"text/csv-schema":{source:"iana"},"text/directory":{source:"iana"},"text/dns":{source:"iana"},"text/ecmascript":{source:"apache"},"text/encaprtp":{source:"iana"},"text/enriched":{source:"iana"},"text/fhirpath":{source:"iana"},"text/flexfec":{source:"iana"},"text/fwdred":{source:"iana"},"text/gff3":{source:"iana"},"text/grammar-ref-list":{source:"iana"},"text/hl7v2":{source:"iana"},"text/html":{source:"iana",compressible:!0,extensions:["html","htm","shtml"]},"text/jade":{extensions:["jade"]},"text/javascript":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["js","mjs"]},"text/jcr-cnd":{source:"iana"},"text/jsx":{compressible:!0,extensions:["jsx"]},"text/less":{compressible:!0,extensions:["less"]},"text/markdown":{source:"iana",compressible:!0,extensions:["md","markdown"]},"text/mathml":{source:"nginx",extensions:["mml"]},"text/mdx":{compressible:!0,extensions:["mdx"]},"text/mizar":{source:"iana"},"text/n3":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["n3"]},"text/parameters":{source:"iana",charset:"UTF-8"},"text/parityfec":{source:"iana"},"text/plain":{source:"iana",compressible:!0,extensions:["txt","text","conf","def","list","log","in","ini"]},"text/provenance-notation":{source:"iana",charset:"UTF-8"},"text/prs.fallenstein.rst":{source:"iana"},"text/prs.lines.tag":{source:"iana",extensions:["dsc"]},"text/prs.prop.logic":{source:"iana"},"text/prs.texi":{source:"iana"},"text/raptorfec":{source:"iana"},"text/red":{source:"iana"},"text/rfc822-headers":{source:"iana"},"text/richtext":{source:"iana",compressible:!0,extensions:["rtx"]},"text/rtf":{source:"iana",compressible:!0,extensions:["rtf"]},"text/rtp-enc-aescm128":{source:"iana"},"text/rtploopback":{source:"iana"},"text/rtx":{source:"iana"},"text/sgml":{source:"iana",extensions:["sgml","sgm"]},"text/shaclc":{source:"iana"},"text/shex":{source:"iana",extensions:["shex"]},"text/slim":{extensions:["slim","slm"]},"text/spdx":{source:"iana",extensions:["spdx"]},"text/strings":{source:"iana"},"text/stylus":{extensions:["stylus","styl"]},"text/t140":{source:"iana"},"text/tab-separated-values":{source:"iana",compressible:!0,extensions:["tsv"]},"text/troff":{source:"iana",extensions:["t","tr","roff","man","me","ms"]},"text/turtle":{source:"iana",charset:"UTF-8",extensions:["ttl"]},"text/ulpfec":{source:"iana"},"text/uri-list":{source:"iana",compressible:!0,extensions:["uri","uris","urls"]},"text/vcard":{source:"iana",compressible:!0,extensions:["vcard"]},"text/vnd.a":{source:"iana"},"text/vnd.abc":{source:"iana"},"text/vnd.ascii-art":{source:"iana"},"text/vnd.curl":{source:"iana",extensions:["curl"]},"text/vnd.curl.dcurl":{source:"apache",extensions:["dcurl"]},"text/vnd.curl.mcurl":{source:"apache",extensions:["mcurl"]},"text/vnd.curl.scurl":{source:"apache",extensions:["scurl"]},"text/vnd.debian.copyright":{source:"iana",charset:"UTF-8"},"text/vnd.dmclientscript":{source:"iana"},"text/vnd.dvb.subtitle":{source:"iana",extensions:["sub"]},"text/vnd.esmertec.theme-descriptor":{source:"iana",charset:"UTF-8"},"text/vnd.exchangeable":{source:"iana"},"text/vnd.familysearch.gedcom":{source:"iana",extensions:["ged"]},"text/vnd.ficlab.flt":{source:"iana"},"text/vnd.fly":{source:"iana",extensions:["fly"]},"text/vnd.fmi.flexstor":{source:"iana",extensions:["flx"]},"text/vnd.gml":{source:"iana"},"text/vnd.graphviz":{source:"iana",extensions:["gv"]},"text/vnd.hans":{source:"iana"},"text/vnd.hgl":{source:"iana"},"text/vnd.in3d.3dml":{source:"iana",extensions:["3dml"]},"text/vnd.in3d.spot":{source:"iana",extensions:["spot"]},"text/vnd.iptc.newsml":{source:"iana"},"text/vnd.iptc.nitf":{source:"iana"},"text/vnd.latex-z":{source:"iana"},"text/vnd.motorola.reflex":{source:"iana"},"text/vnd.ms-mediapackage":{source:"iana"},"text/vnd.net2phone.commcenter.command":{source:"iana"},"text/vnd.radisys.msml-basic-layout":{source:"iana"},"text/vnd.senx.warpscript":{source:"iana"},"text/vnd.si.uricatalogue":{source:"apache"},"text/vnd.sosi":{source:"iana"},"text/vnd.sun.j2me.app-descriptor":{source:"iana",charset:"UTF-8",extensions:["jad"]},"text/vnd.trolltech.linguist":{source:"iana",charset:"UTF-8"},"text/vnd.vcf":{source:"iana"},"text/vnd.wap.si":{source:"iana"},"text/vnd.wap.sl":{source:"iana"},"text/vnd.wap.wml":{source:"iana",extensions:["wml"]},"text/vnd.wap.wmlscript":{source:"iana",extensions:["wmls"]},"text/vnd.zoo.kcl":{source:"iana"},"text/vtt":{source:"iana",charset:"UTF-8",compressible:!0,extensions:["vtt"]},"text/wgsl":{source:"iana",extensions:["wgsl"]},"text/x-asm":{source:"apache",extensions:["s","asm"]},"text/x-c":{source:"apache",extensions:["c","cc","cxx","cpp","h","hh","dic"]},"text/x-component":{source:"nginx",extensions:["htc"]},"text/x-fortran":{source:"apache",extensions:["f","for","f77","f90"]},"text/x-gwt-rpc":{compressible:!0},"text/x-handlebars-template":{extensions:["hbs"]},"text/x-java-source":{source:"apache",extensions:["java"]},"text/x-jquery-tmpl":{compressible:!0},"text/x-lua":{extensions:["lua"]},"text/x-markdown":{compressible:!0,extensions:["mkd"]},"text/x-nfo":{source:"apache",extensions:["nfo"]},"text/x-opml":{source:"apache",extensions:["opml"]},"text/x-org":{compressible:!0,extensions:["org"]},"text/x-pascal":{source:"apache",extensions:["p","pas"]},"text/x-processing":{compressible:!0,extensions:["pde"]},"text/x-sass":{extensions:["sass"]},"text/x-scss":{extensions:["scss"]},"text/x-setext":{source:"apache",extensions:["etx"]},"text/x-sfv":{source:"apache",extensions:["sfv"]},"text/x-suse-ymp":{compressible:!0,extensions:["ymp"]},"text/x-uuencode":{source:"apache",extensions:["uu"]},"text/x-vcalendar":{source:"apache",extensions:["vcs"]},"text/x-vcard":{source:"apache",extensions:["vcf"]},"text/xml":{source:"iana",compressible:!0,extensions:["xml"]},"text/xml-external-parsed-entity":{source:"iana"},"text/yaml":{compressible:!0,extensions:["yaml","yml"]},"video/1d-interleaved-parityfec":{source:"iana"},"video/3gpp":{source:"iana",extensions:["3gp","3gpp"]},"video/3gpp-tt":{source:"iana"},"video/3gpp2":{source:"iana",extensions:["3g2"]},"video/av1":{source:"iana"},"video/bmpeg":{source:"iana"},"video/bt656":{source:"iana"},"video/celb":{source:"iana"},"video/dv":{source:"iana"},"video/encaprtp":{source:"iana"},"video/evc":{source:"iana"},"video/ffv1":{source:"iana"},"video/flexfec":{source:"iana"},"video/h261":{source:"iana",extensions:["h261"]},"video/h263":{source:"iana",extensions:["h263"]},"video/h263-1998":{source:"iana"},"video/h263-2000":{source:"iana"},"video/h264":{source:"iana",extensions:["h264"]},"video/h264-rcdo":{source:"iana"},"video/h264-svc":{source:"iana"},"video/h265":{source:"iana"},"video/h266":{source:"iana"},"video/iso.segment":{source:"iana",extensions:["m4s"]},"video/jpeg":{source:"iana",extensions:["jpgv"]},"video/jpeg2000":{source:"iana"},"video/jpm":{source:"apache",extensions:["jpm","jpgm"]},"video/jxsv":{source:"iana"},"video/lottie+json":{source:"iana",compressible:!0},"video/matroska":{source:"iana"},"video/matroska-3d":{source:"iana"},"video/mj2":{source:"iana",extensions:["mj2","mjp2"]},"video/mp1s":{source:"iana"},"video/mp2p":{source:"iana"},"video/mp2t":{source:"iana",extensions:["ts","m2t","m2ts","mts"]},"video/mp4":{source:"iana",compressible:!1,extensions:["mp4","mp4v","mpg4"]},"video/mp4v-es":{source:"iana"},"video/mpeg":{source:"iana",compressible:!1,extensions:["mpeg","mpg","mpe","m1v","m2v"]},"video/mpeg4-generic":{source:"iana"},"video/mpv":{source:"iana"},"video/nv":{source:"iana"},"video/ogg":{source:"iana",compressible:!1,extensions:["ogv"]},"video/parityfec":{source:"iana"},"video/pointer":{source:"iana"},"video/quicktime":{source:"iana",compressible:!1,extensions:["qt","mov"]},"video/raptorfec":{source:"iana"},"video/raw":{source:"iana"},"video/rtp-enc-aescm128":{source:"iana"},"video/rtploopback":{source:"iana"},"video/rtx":{source:"iana"},"video/scip":{source:"iana"},"video/smpte291":{source:"iana"},"video/smpte292m":{source:"iana"},"video/ulpfec":{source:"iana"},"video/vc1":{source:"iana"},"video/vc2":{source:"iana"},"video/vnd.cctv":{source:"iana"},"video/vnd.dece.hd":{source:"iana",extensions:["uvh","uvvh"]},"video/vnd.dece.mobile":{source:"iana",extensions:["uvm","uvvm"]},"video/vnd.dece.mp4":{source:"iana"},"video/vnd.dece.pd":{source:"iana",extensions:["uvp","uvvp"]},"video/vnd.dece.sd":{source:"iana",extensions:["uvs","uvvs"]},"video/vnd.dece.video":{source:"iana",extensions:["uvv","uvvv"]},"video/vnd.directv.mpeg":{source:"iana"},"video/vnd.directv.mpeg-tts":{source:"iana"},"video/vnd.dlna.mpeg-tts":{source:"iana"},"video/vnd.dvb.file":{source:"iana",extensions:["dvb"]},"video/vnd.fvt":{source:"iana",extensions:["fvt"]},"video/vnd.hns.video":{source:"iana"},"video/vnd.iptvforum.1dparityfec-1010":{source:"iana"},"video/vnd.iptvforum.1dparityfec-2005":{source:"iana"},"video/vnd.iptvforum.2dparityfec-1010":{source:"iana"},"video/vnd.iptvforum.2dparityfec-2005":{source:"iana"},"video/vnd.iptvforum.ttsavc":{source:"iana"},"video/vnd.iptvforum.ttsmpeg2":{source:"iana"},"video/vnd.motorola.video":{source:"iana"},"video/vnd.motorola.videop":{source:"iana"},"video/vnd.mpegurl":{source:"iana",extensions:["mxu","m4u"]},"video/vnd.ms-playready.media.pyv":{source:"iana",extensions:["pyv"]},"video/vnd.nokia.interleaved-multimedia":{source:"iana"},"video/vnd.nokia.mp4vr":{source:"iana"},"video/vnd.nokia.videovoip":{source:"iana"},"video/vnd.objectvideo":{source:"iana"},"video/vnd.planar":{source:"iana"},"video/vnd.radgamettools.bink":{source:"iana"},"video/vnd.radgamettools.smacker":{source:"apache"},"video/vnd.sealed.mpeg1":{source:"iana"},"video/vnd.sealed.mpeg4":{source:"iana"},"video/vnd.sealed.swf":{source:"iana"},"video/vnd.sealedmedia.softseal.mov":{source:"iana"},"video/vnd.uvvu.mp4":{source:"iana",extensions:["uvu","uvvu"]},"video/vnd.vivo":{source:"iana",extensions:["viv"]},"video/vnd.youtube.yt":{source:"iana"},"video/vp8":{source:"iana"},"video/vp9":{source:"iana"},"video/webm":{source:"apache",compressible:!1,extensions:["webm"]},"video/x-f4v":{source:"apache",extensions:["f4v"]},"video/x-fli":{source:"apache",extensions:["fli"]},"video/x-flv":{source:"apache",compressible:!1,extensions:["flv"]},"video/x-m4v":{source:"apache",extensions:["m4v"]},"video/x-matroska":{source:"apache",compressible:!1,extensions:["mkv","mk3d","mks"]},"video/x-mng":{source:"apache",extensions:["mng"]},"video/x-ms-asf":{source:"apache",extensions:["asf","asx"]},"video/x-ms-vob":{source:"apache",extensions:["vob"]},"video/x-ms-wm":{source:"apache",extensions:["wm"]},"video/x-ms-wmv":{source:"apache",compressible:!1,extensions:["wmv"]},"video/x-ms-wmx":{source:"apache",extensions:["wmx"]},"video/x-ms-wvx":{source:"apache",extensions:["wvx"]},"video/x-msvideo":{source:"apache",extensions:["avi"]},"video/x-sgi-movie":{source:"apache",extensions:["movie"]},"video/x-smv":{source:"apache",extensions:["smv"]},"x-conference/x-cooltalk":{source:"apache",extensions:["ice"]},"x-shader/x-fragment":{compressible:!0},"x-shader/x-vertex":{compressible:!0}}});var bze=S((Hkr,Tze)=>{Tze.exports=Eze()});var Pze=S((Wkr,Rze)=>{var xze={"prs.":100,"x-":200,"x.":300,"vnd.":400,default:900},Aze={nginx:10,apache:20,iana:40,default:30},wze={application:1,font:2,default:0};Rze.exports=function(e,r="default"){if(e==="application/octet-stream")return 0;let[n,o]=e.split("/"),i=o.replace(/(\.|x-).*/,"$1"),s=xze[i]||xze.default,a=Aze[r]||Aze.default,c=wze[n]||wze.default,u=1-e.length/100;return s+a+c+u}});var lS=S(vo=>{"use strict";var qm=bze(),g6t=require("path").extname,Ize=Pze(),Oze=/^\s*([^;\s]*)(?:;|\s|$)/,_6t=/^text\//i;vo.charset=Nze;vo.charsets={lookup:Nze};vo.contentType=v6t;vo.extension=PK;vo.extensions=Object.create(null);vo.lookup=S6t;vo.types=Object.create(null);vo._extensionConflicts=[];y6t(vo.extensions,vo.types);function Nze(t){if(!t||typeof t!="string")return!1;var e=Oze.exec(t),r=e&&qm[e[1].toLowerCase()];return r&&r.charset?r.charset:e&&_6t.test(e[1])?"UTF-8":!1}function v6t(t){if(!t||typeof t!="string")return!1;var e=t.indexOf("/")===-1?vo.lookup(t):t;if(!e)return!1;if(e.indexOf("charset")===-1){var r=vo.charset(e);r&&(e+="; charset="+r.toLowerCase())}return e}function PK(t){if(!t||typeof t!="string")return!1;var e=Oze.exec(t),r=e&&vo.extensions[e[1].toLowerCase()];return!r||!r.length?!1:r[0]}function S6t(t){if(!t||typeof t!="string")return!1;var e=g6t("x."+t).toLowerCase().slice(1);return e&&vo.types[e]||!1}function y6t(t,e){Object.keys(qm).forEach(function(n){var o=qm[n],i=o.extensions;if(!(!i||!i.length)){t[n]=i;for(var s=0;so?e:r}function T6t(t,e,r){var n=["nginx","apache",void 0,"iana"],o=e?n.indexOf(qm[e].source):0,i=r?n.indexOf(qm[r].source):0;return vo.types[PK]!=="application/octet-stream"&&(o>i||o===i&&vo.types[PK]?.slice(0,12)==="application/")||o>i?e:r}});var kze=S(bD=>{"use strict";var b6t=/^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$/,Cze=/^[A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126}$/,$ze=/^ *([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}) *$/;bD.format=x6t;bD.parse=w6t;bD.test=A6t;function x6t(t){if(!t||typeof t!="object")throw new TypeError("argument obj is required");var e=t.subtype,r=t.suffix,n=t.type;if(!n||!Cze.test(n))throw new TypeError("invalid type");if(!e||!b6t.test(e))throw new TypeError("invalid subtype");var o=n+"/"+e;if(r){if(!Cze.test(r))throw new TypeError("invalid suffix");o+="+"+r}return o}function A6t(t){if(!t)throw new TypeError("argument string is required");if(typeof t!="string")throw new TypeError("argument string is required to be a string");return $ze.test(t.toLowerCase())}function w6t(t){if(!t)throw new TypeError("argument string is required");if(typeof t!="string")throw new TypeError("argument string is required to be a string");var e=$ze.exec(t.toLowerCase());if(!e)throw new TypeError("invalid media type");var r=e[1],n=e[2],o,i=n.lastIndexOf("+");return i!==-1&&(o=n.substr(i+1),n=n.substr(0,i)),new R6t(r,n,o)}function R6t(t,e,r){this.type=t,this.subtype=e,this.suffix=r}});var xD=S((Ykr,pS)=>{"use strict";var P6t=TD(),I6t=lS(),O6t=kze();pS.exports=N6t;pS.exports.is=Mze;pS.exports.hasBody=Dze;pS.exports.normalize=Lze;pS.exports.match=Uze;function Mze(t,e){var r,n=e,o=$6t(t);if(!o)return!1;if(n&&!Array.isArray(n))for(n=new Array(arguments.length-1),r=0;r2?Array.prototype.slice.call(arguments,1):e,n=t.headers["content-type"];return Mze(n,r)}function Lze(t){if(typeof t!="string")return!1;switch(t){case"urlencoded":return"application/x-www-form-urlencoded";case"multipart":return"multipart/*"}return t[0]==="+"?"*/*"+t:t.indexOf("/")===-1?I6t.lookup(t):t}function Uze(t,e){if(t===!1)return!1;var r=e.split("/"),n=t.split("/");return r.length!==2||n.length!==2||n[0]!=="*"&&n[0]!==r[0]?!1:n[1].slice(0,2)==="*+"?n[1].length<=r[1].length+1&&n[1].slice(1)===r[1].slice(1-n[1].length):!(n[1]!=="*"&&n[1]!==r[1])}function C6t(t){var e=P6t.parse(t).type;return O6t.test(e)?e:null}function $6t(t){try{return t?C6t(t):null}catch{return null}}});var dS=S((Jkr,jze)=>{"use strict";var k6t=FW(),M6t=TD(),D6t=xD();jze.exports={getCharset:L6t,normalizeOptions:j6t,passthrough:z6t};function L6t(t){try{return(M6t.parse(t).parameters.charset||"").toLowerCase()}catch{return}}function U6t(t){return function(r){return!!D6t(r,t)}}function j6t(t,e){if(!e)throw new TypeError("defaultType must be provided");var r=t?.inflate!==!1,n=typeof t?.limit!="number"?k6t.parse(t?.limit||"100kb"):t?.limit,o=t?.type||e,i=t?.verify||!1,s=t?.defaultCharset||"utf-8";if(i!==!1&&typeof i!="function")throw new TypeError("option verify must be function");var a=typeof o!="function"?U6t(o):o;return{inflate:r,limit:n,verify:i,defaultCharset:s,shouldParse:a}}function z6t(t){return t}});var U0=S((Xkr,Fze)=>{"use strict";var zl=sS(),F6t=dze(),zze=AK(),OK=L0(),IK=require("node:zlib"),q6t=xD().hasBody,{getCharset:B6t}=dS();Fze.exports=G6t;function G6t(t,e,r,n,o,i){if(OK.isFinished(t)){o("body already parsed"),r();return}if("body"in t||(t.body=void 0),!q6t(t)){o("skip empty body"),r();return}if(o("content-type %j",t.headers["content-type"]),!i.shouldParse(t)){o("skip parsing"),r();return}var s=null;if(i?.skipCharset!==!0&&(s=B6t(t)||i.defaultCharset,i?.isValidCharset&&!i.isValidCharset(s))){o("invalid charset"),r(zl(415,'unsupported charset "'+s.toUpperCase()+'"',{charset:s,type:"charset.unsupported"}));return}var a,c=i,u,p=c.verify;try{u=V6t(t,o,c.inflate),a=u.length,u.length=void 0}catch(f){return r(f)}if(c.length=a,c.encoding=p?null:s,c.encoding===null&&s!==null&&!zze.encodingExists(s))return r(zl(415,'unsupported charset "'+s.toUpperCase()+'"',{charset:s.toLowerCase(),type:"charset.unsupported"}));o("read body"),F6t(u,c,function(f,m){if(f){var h;f.type==="encoding.unsupported"?h=zl(415,'unsupported charset "'+s.toUpperCase()+'"',{charset:s.toLowerCase(),type:"charset.unsupported"}):h=zl(400,f),u!==t&&(t.unpipe(),u.destroy()),W6t(t,function(){r(zl(400,h))});return}if(p)try{o("verify body"),p(t,e,m,s)}catch(v){r(zl(403,v,{body:m,type:v.type||"entity.verify.failed"}));return}var _=m;try{o("parse body"),_=typeof m!="string"&&s!==null?zze.decode(m,s):m,t.body=n(_,s)}catch(v){r(zl(400,v,{body:_,type:v.type||"entity.parse.failed"}));return}r()})}function V6t(t,e,r){var n=(t.headers["content-encoding"]||"identity").toLowerCase(),o=t.headers["content-length"];if(e('content-encoding "%s"',n),r===!1&&n!=="identity")throw zl(415,"content encoding unsupported",{encoding:n,type:"encoding.unsupported"});if(n==="identity")return t.length=o,t;var i=H6t(n,e);return t.pipe(i),i}function H6t(t,e){switch(t){case"deflate":return e("inflate body"),IK.createInflate();case"gzip":return e("gunzip body"),IK.createGunzip();case"br":return e("brotli decompress body"),IK.createBrotliDecompress();default:throw zl(415,'unsupported content encoding "'+t+'"',{encoding:t,type:"encoding.unsupported"})}}function W6t(t,e){OK.isFinished(t)?e(null):(OK(t,e),t.resume())}});var Vze=S((Qkr,Gze)=>{"use strict";var NK=bs()("body-parser:json"),K6t=U0(),{normalizeOptions:Z6t}=dS();Gze.exports=X6t;var Y6t=/^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/,qze="#",J6t=/#+/g;function X6t(t){let e=Z6t(t,"application/json");var r=t?.reviver,n=t?.strict!==!1;function o(s){if(s.length===0)return{};if(n){var a=eBt(s);if(a!=="{"&&a!=="[")throw NK("strict violation"),Q6t(s,a)}try{return NK("parse json"),JSON.parse(s,r)}catch(c){throw Bze(c,{message:c.message,stack:c.stack})}}let i={...e,isValidCharset:s=>s.slice(0,4)==="utf-"};return function(a,c,u){K6t(a,c,u,o,NK,i)}}function Q6t(t,e){var r=t.indexOf(e),n="";if(r!==-1){n=t.substring(0,r)+qze;for(var o=r+1;o{"use strict";var tBt=bs()("body-parser:raw"),rBt=U0(),{normalizeOptions:nBt,passthrough:oBt}=dS();Hze.exports=iBt;function iBt(t){let r={...nBt(t,"application/octet-stream"),skipCharset:!0};return function(o,i,s){rBt(o,i,s,oBt,tBt,r)}}});var Zze=S((tMr,Kze)=>{"use strict";var sBt=bs()("body-parser:text"),aBt=U0(),{normalizeOptions:cBt,passthrough:uBt}=dS();Kze.exports=lBt;function lBt(t){let e=cBt(t,"text/plain");return function(n,o,i){aBt(n,o,i,uBt,sBt,e)}}});var Bm=S((rMr,Yze)=>{"use strict";Yze.exports=TypeError});var Xze=S((nMr,Jze)=>{Jze.exports=require("util").inspect});var B0=S((oMr,v4e)=>{var FK=typeof Map=="function"&&Map.prototype,CK=Object.getOwnPropertyDescriptor&&FK?Object.getOwnPropertyDescriptor(Map.prototype,"size"):null,wD=FK&&CK&&typeof CK.get=="function"?CK.get:null,Qze=FK&&Map.prototype.forEach,qK=typeof Set=="function"&&Set.prototype,$K=Object.getOwnPropertyDescriptor&&qK?Object.getOwnPropertyDescriptor(Set.prototype,"size"):null,RD=qK&&$K&&typeof $K.get=="function"?$K.get:null,e4e=qK&&Set.prototype.forEach,pBt=typeof WeakMap=="function"&&WeakMap.prototype,z0=pBt?WeakMap.prototype.has:null,dBt=typeof WeakSet=="function"&&WeakSet.prototype,F0=dBt?WeakSet.prototype.has:null,fBt=typeof WeakRef=="function"&&WeakRef.prototype,t4e=fBt?WeakRef.prototype.deref:null,mBt=Boolean.prototype.valueOf,hBt=Object.prototype.toString,gBt=Function.prototype.toString,_Bt=String.prototype.match,BK=String.prototype.slice,qd=String.prototype.replace,vBt=String.prototype.toUpperCase,r4e=String.prototype.toLowerCase,p4e=RegExp.prototype.test,n4e=Array.prototype.concat,Ru=Array.prototype.join,SBt=Array.prototype.slice,o4e=Math.floor,DK=typeof BigInt=="function"?BigInt.prototype.valueOf:null,kK=Object.getOwnPropertySymbols,LK=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Symbol.prototype.toString:null,fS=typeof Symbol=="function"&&typeof Symbol.iterator=="object",q0=typeof Symbol=="function"&&Symbol.toStringTag&&(typeof Symbol.toStringTag===fS||!0)?Symbol.toStringTag:null,d4e=Object.prototype.propertyIsEnumerable,i4e=(typeof Reflect=="function"?Reflect.getPrototypeOf:Object.getPrototypeOf)||([].__proto__===Array.prototype?function(t){return t.__proto__}:null);function s4e(t,e){if(t===1/0||t===-1/0||t!==t||t&&t>-1e3&&t<1e3||p4e.call(/e/,e))return e;var r=/[0-9](?=(?:[0-9]{3})+(?![0-9]))/g;if(typeof t=="number"){var n=t<0?-o4e(-t):o4e(t);if(n!==t){var o=String(n),i=BK.call(e,o.length+1);return qd.call(o,r,"$&_")+"."+qd.call(qd.call(i,/([0-9]{3})/g,"$&_"),/_$/,"")}}return qd.call(e,r,"$&_")}var UK=Xze(),a4e=UK.custom,c4e=h4e(a4e)?a4e:null,f4e={__proto__:null,double:'"',single:"'"},yBt={__proto__:null,double:/(["\\])/g,single:/(['\\])/g};v4e.exports=function t(e,r,n,o){var i=r||{};if(Fl(i,"quoteStyle")&&!Fl(f4e,i.quoteStyle))throw new TypeError('option "quoteStyle" must be "single" or "double"');if(Fl(i,"maxStringLength")&&(typeof i.maxStringLength=="number"?i.maxStringLength<0&&i.maxStringLength!==1/0:i.maxStringLength!==null))throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`');var s=Fl(i,"customInspect")?i.customInspect:!0;if(typeof s!="boolean"&&s!=="symbol")throw new TypeError("option \"customInspect\", if provided, must be `true`, `false`, or `'symbol'`");if(Fl(i,"indent")&&i.indent!==null&&i.indent!==" "&&!(parseInt(i.indent,10)===i.indent&&i.indent>0))throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`');if(Fl(i,"numericSeparator")&&typeof i.numericSeparator!="boolean")throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var a=i.numericSeparator;if(typeof e>"u")return"undefined";if(e===null)return"null";if(typeof e=="boolean")return e?"true":"false";if(typeof e=="string")return _4e(e,i);if(typeof e=="number"){if(e===0)return 1/0/e>0?"0":"-0";var c=String(e);return a?s4e(e,c):c}if(typeof e=="bigint"){var u=String(e)+"n";return a?s4e(e,u):u}var p=typeof i.depth>"u"?5:i.depth;if(typeof n>"u"&&(n=0),n>=p&&p>0&&typeof e=="object")return jK(e)?"[Array]":"[Object]";var f=UBt(i,n);if(typeof o>"u")o=[];else if(g4e(o,e)>=0)return"[Circular]";function m(pt,Ke,it){if(Ke&&(o=SBt.call(o),o.push(Ke)),it){var Mr={depth:i.depth};return Fl(i,"quoteStyle")&&(Mr.quoteStyle=i.quoteStyle),t(pt,Mr,n+1,o)}return t(pt,i,n+1,o)}if(typeof e=="function"&&!u4e(e)){var h=IBt(e),_=AD(e,m);return"[Function"+(h?": "+h:" (anonymous)")+"]"+(_.length>0?" { "+Ru.call(_,", ")+" }":"")}if(h4e(e)){var v=fS?qd.call(String(e),/^(Symbol\(.*\))_[^)]*$/,"$1"):LK.call(e);return typeof e=="object"&&!fS?j0(v):v}if(MBt(e)){for(var E="<"+r4e.call(String(e.nodeName)),x=e.attributes||[],w=0;w",E}if(jK(e)){if(e.length===0)return"[]";var I=AD(e,m);return f&&!LBt(I)?"["+zK(I,f)+"]":"[ "+Ru.call(I,", ")+" ]"}if(bBt(e)){var N=AD(e,m);return!("cause"in Error.prototype)&&"cause"in e&&!d4e.call(e,"cause")?"{ ["+String(e)+"] "+Ru.call(n4e.call("[cause]: "+m(e.cause),N),", ")+" }":N.length===0?"["+String(e)+"]":"{ ["+String(e)+"] "+Ru.call(N,", ")+" }"}if(typeof e=="object"&&s){if(c4e&&typeof e[c4e]=="function"&&UK)return UK(e,{depth:p-n});if(s!=="symbol"&&typeof e.inspect=="function")return e.inspect()}if(OBt(e)){var $=[];return Qze&&Qze.call(e,function(pt,Ke){$.push(m(Ke,e,!0)+" => "+m(pt,e))}),l4e("Map",wD.call(e),$,f)}if($Bt(e)){var B=[];return e4e&&e4e.call(e,function(pt){B.push(m(pt,e))}),l4e("Set",RD.call(e),B,f)}if(NBt(e))return MK("WeakMap");if(kBt(e))return MK("WeakSet");if(CBt(e))return MK("WeakRef");if(ABt(e))return j0(m(Number(e)));if(RBt(e))return j0(m(DK.call(e)));if(wBt(e))return j0(mBt.call(e));if(xBt(e))return j0(m(String(e)));if(typeof window<"u"&&e===window)return"{ [object Window] }";if(typeof globalThis<"u"&&e===globalThis||typeof global<"u"&&e===global)return"{ [object globalThis] }";if(!TBt(e)&&!u4e(e)){var G=AD(e,m),he=i4e?i4e(e)===Object.prototype:e instanceof Object||e.constructor===Object,Q=e instanceof Object?"":"null prototype",me=!he&&q0&&Object(e)===e&&q0 in e?BK.call(Bd(e),8,-1):Q?"Object":"",J=he||typeof e.constructor!="function"?"":e.constructor.name?e.constructor.name+" ":"",Oe=J+(me||Q?"["+Ru.call(n4e.call([],me||[],Q||[]),": ")+"] ":"");return G.length===0?Oe+"{}":f?Oe+"{"+zK(G,f)+"}":Oe+"{ "+Ru.call(G,", ")+" }"}return String(e)};function m4e(t,e,r){var n=r.quoteStyle||e,o=f4e[n];return o+t+o}function EBt(t){return qd.call(String(t),/"/g,""")}function Gm(t){return!q0||!(typeof t=="object"&&(q0 in t||typeof t[q0]<"u"))}function jK(t){return Bd(t)==="[object Array]"&&Gm(t)}function TBt(t){return Bd(t)==="[object Date]"&&Gm(t)}function u4e(t){return Bd(t)==="[object RegExp]"&&Gm(t)}function bBt(t){return Bd(t)==="[object Error]"&&Gm(t)}function xBt(t){return Bd(t)==="[object String]"&&Gm(t)}function ABt(t){return Bd(t)==="[object Number]"&&Gm(t)}function wBt(t){return Bd(t)==="[object Boolean]"&&Gm(t)}function h4e(t){if(fS)return t&&typeof t=="object"&&t instanceof Symbol;if(typeof t=="symbol")return!0;if(!t||typeof t!="object"||!LK)return!1;try{return LK.call(t),!0}catch{}return!1}function RBt(t){if(!t||typeof t!="object"||!DK)return!1;try{return DK.call(t),!0}catch{}return!1}var PBt=Object.prototype.hasOwnProperty||function(t){return t in this};function Fl(t,e){return PBt.call(t,e)}function Bd(t){return hBt.call(t)}function IBt(t){if(t.name)return t.name;var e=_Bt.call(gBt.call(t),/^function\s*([\w$]+)/);return e?e[1]:null}function g4e(t,e){if(t.indexOf)return t.indexOf(e);for(var r=0,n=t.length;re.maxStringLength){var r=t.length-e.maxStringLength,n="... "+r+" more character"+(r>1?"s":"");return _4e(BK.call(t,0,e.maxStringLength),e)+n}var o=yBt[e.quoteStyle||"single"];o.lastIndex=0;var i=qd.call(qd.call(t,o,"\\$1"),/[\x00-\x1f]/g,DBt);return m4e(i,"single",e)}function DBt(t){var e=t.charCodeAt(0),r={8:"b",9:"t",10:"n",12:"f",13:"r"}[e];return r?"\\"+r:"\\x"+(e<16?"0":"")+vBt.call(e.toString(16))}function j0(t){return"Object("+t+")"}function MK(t){return t+" { ? }"}function l4e(t,e,r,n){var o=n?zK(r,n):Ru.call(r,", ");return t+" ("+e+") {"+o+"}"}function LBt(t){for(var e=0;e=0)return!1;return!0}function UBt(t,e){var r;if(t.indent===" ")r=" ";else if(typeof t.indent=="number"&&t.indent>0)r=Ru.call(Array(t.indent+1)," ");else return null;return{base:r,prev:Ru.call(Array(e+1),r)}}function zK(t,e){if(t.length===0)return"";var r=` -`+e.prev+e.base;return r+Ru.call(t,","+r)+` -`+e.prev}function AD(t,e){var r=jK(t),n=[];if(r){n.length=t.length;for(var o=0;o{"use strict";var jBt=B0(),zBt=Bm(),PD=function(t,e,r){for(var n=t,o;(o=n.next)!=null;n=o)if(o.key===e)return n.next=o.next,r||(o.next=t.next,t.next=o),o},FBt=function(t,e){if(t){var r=PD(t,e);return r&&r.value}},qBt=function(t,e,r){var n=PD(t,e);n?n.value=r:t.next={key:e,next:t.next,value:r}},BBt=function(t,e){return t?!!PD(t,e):!1},GBt=function(t,e){if(t)return PD(t,e,!0)};S4e.exports=function(){var e,r={assert:function(n){if(!r.has(n))throw new zBt("Side channel does not contain "+jBt(n))},delete:function(n){var o=e&&e.next,i=GBt(e,n);return i&&o&&o===i&&(e=void 0),!!i},get:function(n){return FBt(e,n)},has:function(n){return BBt(e,n)},set:function(n,o){e||(e={next:void 0}),qBt(e,n,o)}};return r}});var GK=S((sMr,E4e)=>{"use strict";E4e.exports=Object});var b4e=S((aMr,T4e)=>{"use strict";T4e.exports=Error});var A4e=S((cMr,x4e)=>{"use strict";x4e.exports=EvalError});var R4e=S((uMr,w4e)=>{"use strict";w4e.exports=RangeError});var I4e=S((lMr,P4e)=>{"use strict";P4e.exports=ReferenceError});var N4e=S((pMr,O4e)=>{"use strict";O4e.exports=SyntaxError});var $4e=S((dMr,C4e)=>{"use strict";C4e.exports=URIError});var M4e=S((fMr,k4e)=>{"use strict";k4e.exports=Math.abs});var L4e=S((mMr,D4e)=>{"use strict";D4e.exports=Math.floor});var j4e=S((hMr,U4e)=>{"use strict";U4e.exports=Math.max});var F4e=S((gMr,z4e)=>{"use strict";z4e.exports=Math.min});var B4e=S((_Mr,q4e)=>{"use strict";q4e.exports=Math.pow});var V4e=S((vMr,G4e)=>{"use strict";G4e.exports=Math.round});var W4e=S((SMr,H4e)=>{"use strict";H4e.exports=Number.isNaN||function(e){return e!==e}});var Z4e=S((yMr,K4e)=>{"use strict";var VBt=W4e();K4e.exports=function(e){return VBt(e)||e===0?e:e<0?-1:1}});var J4e=S((EMr,Y4e)=>{"use strict";Y4e.exports=Object.getOwnPropertyDescriptor});var VK=S((TMr,X4e)=>{"use strict";var ID=J4e();if(ID)try{ID([],"length")}catch{ID=null}X4e.exports=ID});var e2e=S((bMr,Q4e)=>{"use strict";var OD=Object.defineProperty||!1;if(OD)try{OD({},"a",{value:1})}catch{OD=!1}Q4e.exports=OD});var r2e=S((xMr,t2e)=>{"use strict";t2e.exports=function(){if(typeof Symbol!="function"||typeof Object.getOwnPropertySymbols!="function")return!1;if(typeof Symbol.iterator=="symbol")return!0;var e={},r=Symbol("test"),n=Object(r);if(typeof r=="string"||Object.prototype.toString.call(r)!=="[object Symbol]"||Object.prototype.toString.call(n)!=="[object Symbol]")return!1;var o=42;e[r]=o;for(var i in e)return!1;if(typeof Object.keys=="function"&&Object.keys(e).length!==0||typeof Object.getOwnPropertyNames=="function"&&Object.getOwnPropertyNames(e).length!==0)return!1;var s=Object.getOwnPropertySymbols(e);if(s.length!==1||s[0]!==r||!Object.prototype.propertyIsEnumerable.call(e,r))return!1;if(typeof Object.getOwnPropertyDescriptor=="function"){var a=Object.getOwnPropertyDescriptor(e,r);if(a.value!==o||a.enumerable!==!0)return!1}return!0}});var i2e=S((AMr,o2e)=>{"use strict";var n2e=typeof Symbol<"u"&&Symbol,HBt=r2e();o2e.exports=function(){return typeof n2e!="function"||typeof Symbol!="function"||typeof n2e("foo")!="symbol"||typeof Symbol("bar")!="symbol"?!1:HBt()}});var HK=S((wMr,s2e)=>{"use strict";s2e.exports=typeof Reflect<"u"&&Reflect.getPrototypeOf||null});var WK=S((RMr,a2e)=>{"use strict";var WBt=GK();a2e.exports=WBt.getPrototypeOf||null});var ND=S((PMr,c2e)=>{"use strict";c2e.exports=Function.prototype.call});var KK=S((IMr,u2e)=>{"use strict";u2e.exports=Function.prototype.apply});var p2e=S((OMr,l2e)=>{"use strict";l2e.exports=typeof Reflect<"u"&&Reflect&&Reflect.apply});var f2e=S((NMr,d2e)=>{"use strict";var KBt=Mb(),ZBt=KK(),YBt=ND(),JBt=p2e();d2e.exports=JBt||KBt.call(YBt,ZBt)});var ZK=S((CMr,m2e)=>{"use strict";var XBt=Mb(),QBt=Bm(),eGt=ND(),tGt=f2e();m2e.exports=function(e){if(e.length<1||typeof e[0]!="function")throw new QBt("a function is required");return tGt(XBt,eGt,e)}});var y2e=S(($Mr,S2e)=>{"use strict";var rGt=ZK(),h2e=VK(),_2e;try{_2e=[].__proto__===Array.prototype}catch(t){if(!t||typeof t!="object"||!("code"in t)||t.code!=="ERR_PROTO_ACCESS")throw t}var YK=!!_2e&&h2e&&h2e(Object.prototype,"__proto__"),v2e=Object,g2e=v2e.getPrototypeOf;S2e.exports=YK&&typeof YK.get=="function"?rGt([YK.get]):typeof g2e=="function"?function(e){return g2e(e==null?e:v2e(e))}:!1});var A2e=S((kMr,x2e)=>{"use strict";var E2e=HK(),T2e=WK(),b2e=y2e();x2e.exports=E2e?function(e){return E2e(e)}:T2e?function(e){if(!e||typeof e!="object"&&typeof e!="function")throw new TypeError("getProto: not an object");return T2e(e)}:b2e?function(e){return b2e(e)}:null});var kD=S((MMr,N2e)=>{"use strict";var Gt,nGt=GK(),oGt=b4e(),iGt=A4e(),sGt=R4e(),aGt=I4e(),_S=N4e(),gS=Bm(),cGt=$4e(),uGt=M4e(),lGt=L4e(),pGt=j4e(),dGt=F4e(),fGt=B4e(),mGt=V4e(),hGt=Z4e(),I2e=Function,JK=function(t){try{return I2e('"use strict"; return ('+t+").constructor;")()}catch{}},G0=VK(),gGt=e2e(),XK=function(){throw new gS},_Gt=G0?(function(){try{return arguments.callee,XK}catch{try{return G0(arguments,"callee").get}catch{return XK}}})():XK,mS=i2e()(),So=A2e(),vGt=WK(),SGt=HK(),O2e=KK(),V0=ND(),hS={},yGt=typeof Uint8Array>"u"||!So?Gt:So(Uint8Array),Vm={__proto__:null,"%AggregateError%":typeof AggregateError>"u"?Gt:AggregateError,"%Array%":Array,"%ArrayBuffer%":typeof ArrayBuffer>"u"?Gt:ArrayBuffer,"%ArrayIteratorPrototype%":mS&&So?So([][Symbol.iterator]()):Gt,"%AsyncFromSyncIteratorPrototype%":Gt,"%AsyncFunction%":hS,"%AsyncGenerator%":hS,"%AsyncGeneratorFunction%":hS,"%AsyncIteratorPrototype%":hS,"%Atomics%":typeof Atomics>"u"?Gt:Atomics,"%BigInt%":typeof BigInt>"u"?Gt:BigInt,"%BigInt64Array%":typeof BigInt64Array>"u"?Gt:BigInt64Array,"%BigUint64Array%":typeof BigUint64Array>"u"?Gt:BigUint64Array,"%Boolean%":Boolean,"%DataView%":typeof DataView>"u"?Gt:DataView,"%Date%":Date,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":oGt,"%eval%":eval,"%EvalError%":iGt,"%Float16Array%":typeof Float16Array>"u"?Gt:Float16Array,"%Float32Array%":typeof Float32Array>"u"?Gt:Float32Array,"%Float64Array%":typeof Float64Array>"u"?Gt:Float64Array,"%FinalizationRegistry%":typeof FinalizationRegistry>"u"?Gt:FinalizationRegistry,"%Function%":I2e,"%GeneratorFunction%":hS,"%Int8Array%":typeof Int8Array>"u"?Gt:Int8Array,"%Int16Array%":typeof Int16Array>"u"?Gt:Int16Array,"%Int32Array%":typeof Int32Array>"u"?Gt:Int32Array,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":mS&&So?So(So([][Symbol.iterator]())):Gt,"%JSON%":typeof JSON=="object"?JSON:Gt,"%Map%":typeof Map>"u"?Gt:Map,"%MapIteratorPrototype%":typeof Map>"u"||!mS||!So?Gt:So(new Map()[Symbol.iterator]()),"%Math%":Math,"%Number%":Number,"%Object%":nGt,"%Object.getOwnPropertyDescriptor%":G0,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":typeof Promise>"u"?Gt:Promise,"%Proxy%":typeof Proxy>"u"?Gt:Proxy,"%RangeError%":sGt,"%ReferenceError%":aGt,"%Reflect%":typeof Reflect>"u"?Gt:Reflect,"%RegExp%":RegExp,"%Set%":typeof Set>"u"?Gt:Set,"%SetIteratorPrototype%":typeof Set>"u"||!mS||!So?Gt:So(new Set()[Symbol.iterator]()),"%SharedArrayBuffer%":typeof SharedArrayBuffer>"u"?Gt:SharedArrayBuffer,"%String%":String,"%StringIteratorPrototype%":mS&&So?So(""[Symbol.iterator]()):Gt,"%Symbol%":mS?Symbol:Gt,"%SyntaxError%":_S,"%ThrowTypeError%":_Gt,"%TypedArray%":yGt,"%TypeError%":gS,"%Uint8Array%":typeof Uint8Array>"u"?Gt:Uint8Array,"%Uint8ClampedArray%":typeof Uint8ClampedArray>"u"?Gt:Uint8ClampedArray,"%Uint16Array%":typeof Uint16Array>"u"?Gt:Uint16Array,"%Uint32Array%":typeof Uint32Array>"u"?Gt:Uint32Array,"%URIError%":cGt,"%WeakMap%":typeof WeakMap>"u"?Gt:WeakMap,"%WeakRef%":typeof WeakRef>"u"?Gt:WeakRef,"%WeakSet%":typeof WeakSet>"u"?Gt:WeakSet,"%Function.prototype.call%":V0,"%Function.prototype.apply%":O2e,"%Object.defineProperty%":gGt,"%Object.getPrototypeOf%":vGt,"%Math.abs%":uGt,"%Math.floor%":lGt,"%Math.max%":pGt,"%Math.min%":dGt,"%Math.pow%":fGt,"%Math.round%":mGt,"%Math.sign%":hGt,"%Reflect.getPrototypeOf%":SGt};if(So)try{null.error}catch(t){w2e=So(So(t)),Vm["%Error.prototype%"]=w2e}var w2e,EGt=function t(e){var r;if(e==="%AsyncFunction%")r=JK("async function () {}");else if(e==="%GeneratorFunction%")r=JK("function* () {}");else if(e==="%AsyncGeneratorFunction%")r=JK("async function* () {}");else if(e==="%AsyncGenerator%"){var n=t("%AsyncGeneratorFunction%");n&&(r=n.prototype)}else if(e==="%AsyncIteratorPrototype%"){var o=t("%AsyncGenerator%");o&&So&&(r=So(o.prototype))}return Vm[e]=r,r},R2e={__proto__:null,"%ArrayBufferPrototype%":["ArrayBuffer","prototype"],"%ArrayPrototype%":["Array","prototype"],"%ArrayProto_entries%":["Array","prototype","entries"],"%ArrayProto_forEach%":["Array","prototype","forEach"],"%ArrayProto_keys%":["Array","prototype","keys"],"%ArrayProto_values%":["Array","prototype","values"],"%AsyncFunctionPrototype%":["AsyncFunction","prototype"],"%AsyncGenerator%":["AsyncGeneratorFunction","prototype"],"%AsyncGeneratorPrototype%":["AsyncGeneratorFunction","prototype","prototype"],"%BooleanPrototype%":["Boolean","prototype"],"%DataViewPrototype%":["DataView","prototype"],"%DatePrototype%":["Date","prototype"],"%ErrorPrototype%":["Error","prototype"],"%EvalErrorPrototype%":["EvalError","prototype"],"%Float32ArrayPrototype%":["Float32Array","prototype"],"%Float64ArrayPrototype%":["Float64Array","prototype"],"%FunctionPrototype%":["Function","prototype"],"%Generator%":["GeneratorFunction","prototype"],"%GeneratorPrototype%":["GeneratorFunction","prototype","prototype"],"%Int8ArrayPrototype%":["Int8Array","prototype"],"%Int16ArrayPrototype%":["Int16Array","prototype"],"%Int32ArrayPrototype%":["Int32Array","prototype"],"%JSONParse%":["JSON","parse"],"%JSONStringify%":["JSON","stringify"],"%MapPrototype%":["Map","prototype"],"%NumberPrototype%":["Number","prototype"],"%ObjectPrototype%":["Object","prototype"],"%ObjProto_toString%":["Object","prototype","toString"],"%ObjProto_valueOf%":["Object","prototype","valueOf"],"%PromisePrototype%":["Promise","prototype"],"%PromiseProto_then%":["Promise","prototype","then"],"%Promise_all%":["Promise","all"],"%Promise_reject%":["Promise","reject"],"%Promise_resolve%":["Promise","resolve"],"%RangeErrorPrototype%":["RangeError","prototype"],"%ReferenceErrorPrototype%":["ReferenceError","prototype"],"%RegExpPrototype%":["RegExp","prototype"],"%SetPrototype%":["Set","prototype"],"%SharedArrayBufferPrototype%":["SharedArrayBuffer","prototype"],"%StringPrototype%":["String","prototype"],"%SymbolPrototype%":["Symbol","prototype"],"%SyntaxErrorPrototype%":["SyntaxError","prototype"],"%TypedArrayPrototype%":["TypedArray","prototype"],"%TypeErrorPrototype%":["TypeError","prototype"],"%Uint8ArrayPrototype%":["Uint8Array","prototype"],"%Uint8ClampedArrayPrototype%":["Uint8ClampedArray","prototype"],"%Uint16ArrayPrototype%":["Uint16Array","prototype"],"%Uint32ArrayPrototype%":["Uint32Array","prototype"],"%URIErrorPrototype%":["URIError","prototype"],"%WeakMapPrototype%":["WeakMap","prototype"],"%WeakSetPrototype%":["WeakSet","prototype"]},H0=Mb(),CD=y8(),TGt=H0.call(V0,Array.prototype.concat),bGt=H0.call(O2e,Array.prototype.splice),P2e=H0.call(V0,String.prototype.replace),$D=H0.call(V0,String.prototype.slice),xGt=H0.call(V0,RegExp.prototype.exec),AGt=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,wGt=/\\(\\)?/g,RGt=function(e){var r=$D(e,0,1),n=$D(e,-1);if(r==="%"&&n!=="%")throw new _S("invalid intrinsic syntax, expected closing `%`");if(n==="%"&&r!=="%")throw new _S("invalid intrinsic syntax, expected opening `%`");var o=[];return P2e(e,AGt,function(i,s,a,c){o[o.length]=a?P2e(c,wGt,"$1"):s||i}),o},PGt=function(e,r){var n=e,o;if(CD(R2e,n)&&(o=R2e[n],n="%"+o[0]+"%"),CD(Vm,n)){var i=Vm[n];if(i===hS&&(i=EGt(n)),typeof i>"u"&&!r)throw new gS("intrinsic "+e+" exists, but is not available. Please file an issue!");return{alias:o,name:n,value:i}}throw new _S("intrinsic "+e+" does not exist!")};N2e.exports=function(e,r){if(typeof e!="string"||e.length===0)throw new gS("intrinsic name must be a non-empty string");if(arguments.length>1&&typeof r!="boolean")throw new gS('"allowMissing" argument must be a boolean');if(xGt(/^%?[^%]*%?$/,e)===null)throw new _S("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var n=RGt(e),o=n.length>0?n[0]:"",i=PGt("%"+o+"%",r),s=i.name,a=i.value,c=!1,u=i.alias;u&&(o=u[0],bGt(n,TGt([0,1],u)));for(var p=1,f=!0;p=n.length){var v=G0(a,m);f=!!v,f&&"get"in v&&!("originalValue"in v.get)?a=v.get:a=a[m]}else f=CD(a,m),a=a[m];f&&!c&&(Vm[s]=a)}}return a}});var QK=S((DMr,k2e)=>{"use strict";var C2e=kD(),$2e=ZK(),IGt=$2e([C2e("%String.prototype.indexOf%")]);k2e.exports=function(e,r){var n=C2e(e,!!r);return typeof n=="function"&&IGt(e,".prototype.")>-1?$2e([n]):n}});var eZ=S((LMr,D2e)=>{"use strict";var OGt=kD(),W0=QK(),NGt=B0(),CGt=Bm(),M2e=OGt("%Map%",!0),$Gt=W0("Map.prototype.get",!0),kGt=W0("Map.prototype.set",!0),MGt=W0("Map.prototype.has",!0),DGt=W0("Map.prototype.delete",!0),LGt=W0("Map.prototype.size",!0);D2e.exports=!!M2e&&function(){var e,r={assert:function(n){if(!r.has(n))throw new CGt("Side channel does not contain "+NGt(n))},delete:function(n){if(e){var o=DGt(e,n);return LGt(e)===0&&(e=void 0),o}return!1},get:function(n){if(e)return $Gt(e,n)},has:function(n){return e?MGt(e,n):!1},set:function(n,o){e||(e=new M2e),kGt(e,n,o)}};return r}});var U2e=S((UMr,L2e)=>{"use strict";var UGt=kD(),DD=QK(),jGt=B0(),MD=eZ(),zGt=Bm(),vS=UGt("%WeakMap%",!0),FGt=DD("WeakMap.prototype.get",!0),qGt=DD("WeakMap.prototype.set",!0),BGt=DD("WeakMap.prototype.has",!0),GGt=DD("WeakMap.prototype.delete",!0);L2e.exports=vS?function(){var e,r,n={assert:function(o){if(!n.has(o))throw new zGt("Side channel does not contain "+jGt(o))},delete:function(o){if(vS&&o&&(typeof o=="object"||typeof o=="function")){if(e)return GGt(e,o)}else if(MD&&r)return r.delete(o);return!1},get:function(o){return vS&&o&&(typeof o=="object"||typeof o=="function")&&e?FGt(e,o):r&&r.get(o)},has:function(o){return vS&&o&&(typeof o=="object"||typeof o=="function")&&e?BGt(e,o):!!r&&r.has(o)},set:function(o,i){vS&&o&&(typeof o=="object"||typeof o=="function")?(e||(e=new vS),qGt(e,o,i)):MD&&(r||(r=MD()),r.set(o,i))}};return n}:MD});var tZ=S((jMr,j2e)=>{"use strict";var VGt=Bm(),HGt=B0(),WGt=y4e(),KGt=eZ(),ZGt=U2e(),YGt=ZGt||KGt||WGt;j2e.exports=function(){var e,r={assert:function(n){if(!r.has(n))throw new VGt("Side channel does not contain "+HGt(n))},delete:function(n){return!!e&&e.delete(n)},get:function(n){return e&&e.get(n)},has:function(n){return!!e&&e.has(n)},set:function(n,o){e||(e=YGt()),e.set(n,o)}};return r}});var LD=S((zMr,z2e)=>{"use strict";var JGt=String.prototype.replace,XGt=/%20/g,rZ={RFC1738:"RFC1738",RFC3986:"RFC3986"};z2e.exports={default:rZ.RFC3986,formatters:{RFC1738:function(t){return JGt.call(t,XGt,"+")},RFC3986:function(t){return String(t)}},RFC1738:rZ.RFC1738,RFC3986:rZ.RFC3986}});var aZ=S((FMr,B2e)=>{"use strict";var QGt=LD(),e5t=tZ(),nZ=Object.prototype.hasOwnProperty,Hm=Array.isArray,jD=e5t(),F2e=function(e,r){return jD.set(e,r),e},UD=function(e){return jD.has(e)},iZ=function(e){return jD.get(e)},q2e=function(e,r){jD.set(e,r)},Pu=(function(){for(var t=[],e=0;e<256;++e)t.push("%"+((e<16?"0":"")+e.toString(16)).toUpperCase());return t})(),t5t=function(e){for(;e.length>1;){var r=e.pop(),n=r.obj[r.prop];if(Hm(n)){for(var o=[],i=0;i=oZ?s.slice(c,c+oZ):s,p=[],f=0;f=48&&m<=57||m>=65&&m<=90||m>=97&&m<=122||i===QGt.RFC1738&&(m===40||m===41)){p[p.length]=u.charAt(f);continue}if(m<128){p[p.length]=Pu[m];continue}if(m<2048){p[p.length]=Pu[192|m>>6]+Pu[128|m&63];continue}if(m<55296||m>=57344){p[p.length]=Pu[224|m>>12]+Pu[128|m>>6&63]+Pu[128|m&63];continue}f+=1,m=65536+((m&1023)<<10|u.charCodeAt(f)&1023),p[p.length]=Pu[240|m>>18]+Pu[128|m>>12&63]+Pu[128|m>>6&63]+Pu[128|m&63]}a+=p.join("")}return a},s5t=function(e){for(var r=[{obj:{o:e},prop:"o"}],n=[],o=0;on?F2e(sZ(s,{plainObjects:o}),s.length-1):s},l5t=function(e,r){if(Hm(e)){for(var n=[],o=0;o{"use strict";var V2e=tZ(),zD=aZ(),K0=LD(),p5t=Object.prototype.hasOwnProperty,H2e={brackets:function(e){return e+"[]"},comma:"comma",indices:function(e,r){return e+"["+r+"]"},repeat:function(e){return e}},Iu=Array.isArray,d5t=Array.prototype.push,W2e=function(t,e){d5t.apply(t,Iu(e)?e:[e])},f5t=Date.prototype.toISOString,G2e=K0.default,eo={addQueryPrefix:!1,allowDots:!1,allowEmptyArrays:!1,arrayFormat:"indices",charset:"utf-8",charsetSentinel:!1,commaRoundTrip:!1,delimiter:"&",encode:!0,encodeDotInKeys:!1,encoder:zD.encode,encodeValuesOnly:!1,filter:void 0,format:G2e,formatter:K0.formatters[G2e],indices:!1,serializeDate:function(e){return f5t.call(e)},skipNulls:!1,strictNullHandling:!1},m5t=function(e){return typeof e=="string"||typeof e=="number"||typeof e=="boolean"||typeof e=="symbol"||typeof e=="bigint"},cZ={},h5t=function t(e,r,n,o,i,s,a,c,u,p,f,m,h,_,v,E,x,w){for(var I=e,N=w,$=0,B=!1;(N=N.get(cZ))!==void 0&&!B;){var G=N.get(e);if($+=1,typeof G<"u"){if(G===$)throw new RangeError("Cyclic object value");B=!0}typeof N.get(cZ)>"u"&&($=0)}if(typeof p=="function"?I=p(r,I):I instanceof Date?I=h(I):n==="comma"&&Iu(I)&&(I=zD.maybeMap(I,function(ui){return ui instanceof Date?h(ui):ui})),I===null){if(s)return u&&!E?u(r,eo.encoder,x,"key",_):r;I=""}if(m5t(I)||zD.isBuffer(I)){if(u){var he=E?r:u(r,eo.encoder,x,"key",_);return[v(he)+"="+v(u(I,eo.encoder,x,"value",_))]}return[v(r)+"="+v(String(I))]}var Q=[];if(typeof I>"u")return Q;var me;if(n==="comma"&&Iu(I))E&&u&&(I=zD.maybeMap(I,u)),me=[{value:I.length>0?I.join(",")||null:void 0}];else if(Iu(p))me=p;else{var J=Object.keys(I);me=f?J.sort(f):J}var Oe=c?String(r).replace(/\./g,"%2E"):String(r),pt=o&&Iu(I)&&I.length===1?Oe+"[]":Oe;if(i&&Iu(I)&&I.length===0)return pt+"[]";for(var Ke=0;Ke"u"?e.encodeDotInKeys===!0?!0:eo.allowDots:!!e.allowDots;return{addQueryPrefix:typeof e.addQueryPrefix=="boolean"?e.addQueryPrefix:eo.addQueryPrefix,allowDots:a,allowEmptyArrays:typeof e.allowEmptyArrays=="boolean"?!!e.allowEmptyArrays:eo.allowEmptyArrays,arrayFormat:s,charset:r,charsetSentinel:typeof e.charsetSentinel=="boolean"?e.charsetSentinel:eo.charsetSentinel,commaRoundTrip:!!e.commaRoundTrip,delimiter:typeof e.delimiter>"u"?eo.delimiter:e.delimiter,encode:typeof e.encode=="boolean"?e.encode:eo.encode,encodeDotInKeys:typeof e.encodeDotInKeys=="boolean"?e.encodeDotInKeys:eo.encodeDotInKeys,encoder:typeof e.encoder=="function"?e.encoder:eo.encoder,encodeValuesOnly:typeof e.encodeValuesOnly=="boolean"?e.encodeValuesOnly:eo.encodeValuesOnly,filter:i,format:n,formatter:o,serializeDate:typeof e.serializeDate=="function"?e.serializeDate:eo.serializeDate,skipNulls:typeof e.skipNulls=="boolean"?e.skipNulls:eo.skipNulls,sort:typeof e.sort=="function"?e.sort:null,strictNullHandling:typeof e.strictNullHandling=="boolean"?e.strictNullHandling:eo.strictNullHandling}};K2e.exports=function(t,e){var r=t,n=g5t(e),o,i;typeof n.filter=="function"?(i=n.filter,r=i("",r)):Iu(n.filter)&&(i=n.filter,o=i);var s=[];if(typeof r!="object"||r===null)return"";var a=H2e[n.arrayFormat],c=a==="comma"&&n.commaRoundTrip;o||(o=Object.keys(r)),n.sort&&o.sort(n.sort);for(var u=V2e(),p=0;p0?_+h:""}});var Q2e=S((BMr,X2e)=>{"use strict";var Gd=aZ(),FD=Object.prototype.hasOwnProperty,Y2e=Array.isArray,bn={allowDots:!1,allowEmptyArrays:!1,allowPrototypes:!1,allowSparse:!1,arrayLimit:20,charset:"utf-8",charsetSentinel:!1,comma:!1,decodeDotInKeys:!1,decoder:Gd.decode,delimiter:"&",depth:5,duplicates:"combine",ignoreQueryPrefix:!1,interpretNumericEntities:!1,parameterLimit:1e3,parseArrays:!0,plainObjects:!1,strictDepth:!1,strictNullHandling:!1,throwOnLimitExceeded:!1},_5t=function(t){return t.replace(/&#(\d+);/g,function(e,r){return String.fromCharCode(parseInt(r,10))})},J2e=function(t,e,r){if(t&&typeof t=="string"&&e.comma&&t.indexOf(",")>-1)return t.split(",");if(e.throwOnLimitExceeded&&r>=e.arrayLimit)throw new RangeError("Array limit exceeded. Only "+e.arrayLimit+" element"+(e.arrayLimit===1?"":"s")+" allowed in an array.");return t},v5t="utf8=%26%2310003%3B",S5t="utf8=%E2%9C%93",y5t=function(e,r){var n={__proto__:null},o=r.ignoreQueryPrefix?e.replace(/^\?/,""):e;o=o.replace(/%5B/gi,"[").replace(/%5D/gi,"]");var i=r.parameterLimit===1/0?void 0:r.parameterLimit,s=o.split(r.delimiter,r.throwOnLimitExceeded?i+1:i);if(r.throwOnLimitExceeded&&s.length>i)throw new RangeError("Parameter limit exceeded. Only "+i+" parameter"+(i===1?"":"s")+" allowed.");var a=-1,c,u=r.charset;if(r.charsetSentinel)for(c=0;c-1&&(_=Y2e(_)?[_]:_),h!==null){var v=FD.call(n,h);v&&r.duplicates==="combine"?n[h]=Gd.combine(n[h],_,r.arrayLimit,r.plainObjects):(!v||r.duplicates==="last")&&(n[h]=_)}}return n},E5t=function(t,e,r,n){var o=0;if(t.length>0&&t[t.length-1]==="[]"){var i=t.slice(0,-1).join("");o=Array.isArray(e)&&e[i]?e[i].length:0}for(var s=n?e:J2e(e,r,o),a=t.length-1;a>=0;--a){var c,u=t[a];if(u==="[]"&&r.parseArrays)Gd.isOverflow(s)?c=s:c=r.allowEmptyArrays&&(s===""||r.strictNullHandling&&s===null)?[]:Gd.combine([],s,r.arrayLimit,r.plainObjects);else{c=r.plainObjects?{__proto__:null}:{};var p=u.charAt(0)==="["&&u.charAt(u.length-1)==="]"?u.slice(1,-1):u,f=r.decodeDotInKeys?p.replace(/%2E/g,"."):p,m=parseInt(f,10);!r.parseArrays&&f===""?c={0:s}:!isNaN(m)&&u!==f&&String(m)===f&&m>=0&&r.parseArrays&&m<=r.arrayLimit?(c=[],c[m]=s):f!=="__proto__"&&(c[f]=s)}s=c}return s},T5t=function(e,r){var n=r.allowDots?e.replace(/\.([^.[]+)/g,"[$1]"):e;if(r.depth<=0)return!r.plainObjects&&FD.call(Object.prototype,n)&&!r.allowPrototypes?void 0:[n];var o=/(\[[^[\]]*])/,i=/(\[[^[\]]*])/g,s=o.exec(n),a=s?n.slice(0,s.index):n,c=[];if(a){if(!r.plainObjects&&FD.call(Object.prototype,a)&&!r.allowPrototypes)return;c.push(a)}for(var u=0;(s=i.exec(n))!==null&&u"u"?bn.charset:e.charset,n=typeof e.duplicates>"u"?bn.duplicates:e.duplicates;if(n!=="combine"&&n!=="first"&&n!=="last")throw new TypeError("The duplicates option must be either combine, first, or last");var o=typeof e.allowDots>"u"?e.decodeDotInKeys===!0?!0:bn.allowDots:!!e.allowDots;return{allowDots:o,allowEmptyArrays:typeof e.allowEmptyArrays=="boolean"?!!e.allowEmptyArrays:bn.allowEmptyArrays,allowPrototypes:typeof e.allowPrototypes=="boolean"?e.allowPrototypes:bn.allowPrototypes,allowSparse:typeof e.allowSparse=="boolean"?e.allowSparse:bn.allowSparse,arrayLimit:typeof e.arrayLimit=="number"?e.arrayLimit:bn.arrayLimit,charset:r,charsetSentinel:typeof e.charsetSentinel=="boolean"?e.charsetSentinel:bn.charsetSentinel,comma:typeof e.comma=="boolean"?e.comma:bn.comma,decodeDotInKeys:typeof e.decodeDotInKeys=="boolean"?e.decodeDotInKeys:bn.decodeDotInKeys,decoder:typeof e.decoder=="function"?e.decoder:bn.decoder,delimiter:typeof e.delimiter=="string"||Gd.isRegExp(e.delimiter)?e.delimiter:bn.delimiter,depth:typeof e.depth=="number"||e.depth===!1?+e.depth:bn.depth,duplicates:n,ignoreQueryPrefix:e.ignoreQueryPrefix===!0,interpretNumericEntities:typeof e.interpretNumericEntities=="boolean"?e.interpretNumericEntities:bn.interpretNumericEntities,parameterLimit:typeof e.parameterLimit=="number"?e.parameterLimit:bn.parameterLimit,parseArrays:e.parseArrays!==!1,plainObjects:typeof e.plainObjects=="boolean"?e.plainObjects:bn.plainObjects,strictDepth:typeof e.strictDepth=="boolean"?!!e.strictDepth:bn.strictDepth,strictNullHandling:typeof e.strictNullHandling=="boolean"?e.strictNullHandling:bn.strictNullHandling,throwOnLimitExceeded:typeof e.throwOnLimitExceeded=="boolean"?e.throwOnLimitExceeded:!1}};X2e.exports=function(t,e){var r=x5t(e);if(t===""||t===null||typeof t>"u")return r.plainObjects?{__proto__:null}:{};for(var n=typeof t=="string"?y5t(t,r):t,o=r.plainObjects?{__proto__:null}:{},i=Object.keys(n),s=0;s{"use strict";var A5t=Z2e(),w5t=Q2e(),R5t=LD();eFe.exports={formats:R5t,parse:w5t,stringify:A5t}});var nFe=S((VMr,rFe)=>{"use strict";var tFe=sS(),lZ=bs()("body-parser:urlencoded"),P5t=U0(),I5t=uZ(),{normalizeOptions:O5t}=dS();rFe.exports=N5t;function N5t(t){let e=O5t(t,"application/x-www-form-urlencoded");if(e.defaultCharset!=="utf-8"&&e.defaultCharset!=="iso-8859-1")throw new TypeError("option defaultCharset must be either utf-8 or iso-8859-1");var r=C5t(t);function n(i,s){return i.length?r(i,s):{}}let o={...e,isValidCharset:i=>i==="utf-8"||i==="iso-8859-1"};return function(s,a,c){P5t(s,a,c,n,lZ,o)}}function C5t(t){var e=!!t?.extended,r=t?.parameterLimit!==void 0?t?.parameterLimit:1e3,n=t?.charsetSentinel,o=t?.interpretNumericEntities,i=e?t?.depth!==void 0?t?.depth:32:0;if(isNaN(r)||r<1)throw new TypeError("option parameterLimit must be a positive number");if(isNaN(i)||i<0)throw new TypeError("option depth must be a zero or a positive number");return isFinite(r)&&(r=r|0),function(a,c){var u=$5t(a,r);if(u===void 0)throw lZ("too many parameters"),tFe(413,"too many parameters",{type:"parameters.too.many"});var p=e?Math.max(100,u):0;lZ("parse "+(e?"extended ":"")+"urlencoding");try{return I5t.parse(a,{allowPrototypes:!0,arrayLimit:p,depth:i,charsetSentinel:n,interpretNumericEntities:o,charset:c,parameterLimit:r,strictDepth:!0})}catch(f){throw f instanceof RangeError?tFe(400,"The input exceeded the depth",{type:"querystring.parse.rangeError"}):f}}}function $5t(t,e){let r=0,n=-1;do{if(r++,r>e)return;n=t.indexOf("&",n+1)}while(n!==-1);return r}});var iFe=S((SS,oFe)=>{"use strict";SS=oFe.exports=k5t;Object.defineProperty(SS,"json",{configurable:!0,enumerable:!0,get:()=>Vze()});Object.defineProperty(SS,"raw",{configurable:!0,enumerable:!0,get:()=>Wze()});Object.defineProperty(SS,"text",{configurable:!0,enumerable:!0,get:()=>Zze()});Object.defineProperty(SS,"urlencoded",{configurable:!0,enumerable:!0,get:()=>nFe()});function k5t(){throw new Error("The bodyParser() generic has been split into individual middleware to use instead.")}});var aFe=S((HMr,sFe)=>{"use strict";function M5t(t,e,r=!0){if(!t)throw new TypeError("The `destination` argument is required.");if(!e)throw new TypeError("The `source` argument is required.");for(let n of Object.getOwnPropertyNames(e)){if(!r&&Object.hasOwn(t,n))continue;let o=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,o)}return t}sFe.exports=M5t});var Z0=S((WMr,cFe)=>{"use strict";cFe.exports=j5t;var D5t=/(?:[^\x21\x23-\x3B\x3D\x3F-\x5F\x61-\x7A\x7C\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g,L5t=/(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g,U5t="$1\uFFFD$2";function j5t(t){return String(t).replace(L5t,U5t).replace(D5t,encodeURI)}});var Y0=S((KMr,uFe)=>{"use strict";var z5t=/["'&<>]/;uFe.exports=F5t;function F5t(t){var e=""+t,r=z5t.exec(e);if(!r)return e;var n,o="",i=0,s=0;for(i=r.index;i{"use strict";var pFe=require("url"),lFe=pFe.parse,qD=pFe.Url;pZ.exports=dFe;pZ.exports.original=q5t;function dFe(t){var e=t.url;if(e!==void 0){var r=t._parsedUrl;return mFe(e,r)?r:(r=fFe(e),r._raw=e,t._parsedUrl=r)}}function q5t(t){var e=t.originalUrl;if(typeof e!="string")return dFe(t);var r=t._parsedOriginalUrl;return mFe(e,r)?r:(r=fFe(e),r._raw=e,t._parsedOriginalUrl=r)}function fFe(t){if(typeof t!="string"||t.charCodeAt(0)!==47)return lFe(t);for(var e=t,r=null,n=null,o=1;o{"use strict";var dZ=bs()("finalhandler"),B5t=Z0(),G5t=Y0(),hFe=L0(),V5t=J0(),gFe=k0(),H5t=hFe.isFinished;function W5t(t){var e=G5t(t).replaceAll(` -`,"
").replaceAll(" ","  ");return` - - - -Error - - -
`+e+`
- - -`}_Fe.exports=K5t;function K5t(t,e,r){var n=r||{},o=n.env||"production",i=n.onerror;return function(s){var a,c,u;if(!s&&e.headersSent){dZ("cannot 404 after headers sent");return}if(s?(u=J5t(s),u===void 0?u=Q5t(e):a=Z5t(s),c=Y5t(s,u,o)):(u=404,c="Cannot "+t.method+" "+B5t(X5t(t))),dZ("default %s",u),s&&i&&setImmediate(i,s,t,e),e.headersSent){dZ("cannot %d after headers sent",u),t.socket&&t.socket.destroy();return}e8t(t,e,u,a,c)}}function Z5t(t){if(!(!t.headers||typeof t.headers!="object"))return{...t.headers}}function Y5t(t,e,r){var n;return r!=="production"&&(n=t.stack,!n&&typeof t.toString=="function"&&(n=t.toString())),n||gFe.message[e]}function J5t(t){if(typeof t.status=="number"&&t.status>=400&&t.status<600)return t.status;if(typeof t.statusCode=="number"&&t.statusCode>=400&&t.statusCode<600)return t.statusCode}function X5t(t){try{return V5t.original(t).pathname}catch{return"resource"}}function Q5t(t){var e=t.statusCode;return(typeof e!="number"||e<400||e>599)&&(e=500),e}function e8t(t,e,r,n,o){function i(){var s=W5t(o);e.statusCode=r,t.httpVersionMajor<2&&(e.statusMessage=gFe.message[r]),e.removeHeader("Content-Encoding"),e.removeHeader("Content-Language"),e.removeHeader("Content-Range");for(let[a,c]of Object.entries(n??{}))e.setHeader(a,c);if(e.setHeader("Content-Security-Policy","default-src 'none'"),e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","text/html; charset=utf-8"),e.setHeader("Content-Length",Buffer.byteLength(s,"utf8")),t.method==="HEAD"){e.end();return}e.end(s,"utf8")}if(H5t(t)){i();return}t.unpipe(),hFe(t,i),t.resume()}});var bFe=S((JMr,TFe)=>{"use strict";var BD=bs()("express:view"),X0=require("node:path"),t8t=require("node:fs"),r8t=X0.dirname,EFe=X0.basename,n8t=X0.extname,SFe=X0.join,o8t=X0.resolve;TFe.exports=GD;function GD(t,e){var r=e||{};if(this.defaultEngine=r.defaultEngine,this.ext=n8t(t),this.name=t,this.root=r.root,!this.ext&&!this.defaultEngine)throw new Error("No default engine was specified and no extension was provided.");var n=t;if(this.ext||(this.ext=this.defaultEngine[0]!=="."?"."+this.defaultEngine:this.defaultEngine,n+=this.ext),!r.engines[this.ext]){var o=this.ext.slice(1);BD('require "%s"',o);var i=require(o).__express;if(typeof i!="function")throw new Error('Module "'+o+'" does not provide a view engine.');r.engines[this.ext]=i}this.engine=r.engines[this.ext],this.path=this.lookup(n)}GD.prototype.lookup=function(e){var r,n=[].concat(this.root);BD('lookup "%s"',e);for(var o=0;o{"use strict";wFe.exports=a8t;var i8t=require("crypto"),xFe=require("fs").Stats,AFe=Object.prototype.toString;function s8t(t){if(t.length===0)return'"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';var e=i8t.createHash("sha1").update(t,"utf8").digest("base64").substring(0,27),r=typeof t=="string"?Buffer.byteLength(t,"utf8"):t.length;return'"'+r.toString(16)+"-"+e+'"'}function a8t(t,e){if(t==null)throw new TypeError("argument entity is required");var r=c8t(t),n=e&&typeof e.weak=="boolean"?e.weak:r;if(!r&&typeof t!="string"&&!Buffer.isBuffer(t))throw new TypeError("argument entity must be string, Buffer, or fs.Stats");var o=r?u8t(t):s8t(t);return n?"W/"+o:o}function c8t(t){return typeof xFe=="function"&&t instanceof xFe?!0:t&&typeof t=="object"&&"ctime"in t&&AFe.call(t.ctime)==="[object Date]"&&"mtime"in t&&AFe.call(t.mtime)==="[object Date]"&&"ino"in t&&typeof t.ino=="number"&&"size"in t&&typeof t.size=="number"}function u8t(t){var e=t.mtime.getTime().toString(16),r=t.size.toString(16);return'"'+r+"-"+e+'"'}});var PFe=S((QMr,RFe)=>{"use strict";RFe.exports=l8t;function l8t(t){if(!t)throw new TypeError("argument req is required");var e=d8t(t.headers["x-forwarded-for"]||""),r=p8t(t),n=[r].concat(e);return n}function p8t(t){return t.socket?t.socket.remoteAddress:t.connection.remoteAddress}function d8t(t){for(var e=t.length,r=[],n=t.length,o=t.length-1;o>=0;o--)switch(t.charCodeAt(o)){case 32:n===e&&(n=e=o);break;case 44:n!==e&&r.push(t.substring(n,e)),n=e=o;break;default:n=o;break}return n!==e&&r.push(t.substring(n,e)),r}});var OFe=S((IFe,Q0)=>{(function(){var t,e,r,n,o,i,s,a,c;e={},a=this,typeof Q0<"u"&&Q0!==null&&Q0.exports?Q0.exports=e:a.ipaddr=e,s=function(u,p,f,m){var h,_;if(u.length!==p.length)throw new Error("ipaddr: cannot match CIDR for objects with different lengths");for(h=0;m>0;){if(_=f-m,_<0&&(_=0),u[h]>>_!==p[h]>>_)return!1;m-=f,h+=1}return!0},e.subnetMatch=function(u,p,f){var m,h,_,v,E;f==null&&(f="unicast");for(_ in p)for(v=p[_],v[0]&&!(v[0]instanceof Array)&&(v=[v]),m=0,h=v.length;m=0;f=m+=-1)if(h=this.octets[f],h in E){if(v=E[h],_&&v!==0)return null;v!==8&&(_=!0),p+=v}else return null;return 32-p},u})(),r="(0?\\d+|0x[a-f0-9]+)",n={fourOctet:new RegExp("^"+r+"\\."+r+"\\."+r+"\\."+r+"$","i"),longValue:new RegExp("^"+r+"$","i")},e.IPv4.parser=function(u){var p,f,m,h,_;if(f=function(v){return v[0]==="0"&&v[1]!=="x"?parseInt(v,8):parseInt(v)},p=u.match(n.fourOctet))return(function(){var v,E,x,w;for(x=p.slice(1,6),w=[],v=0,E=x.length;v4294967295||_<0)throw new Error("ipaddr: address outside defined range");return(function(){var v,E;for(E=[],h=v=0;v<=24;h=v+=8)E.push(_>>h&255);return E})().reverse()}else return null},e.IPv6=(function(){function u(p,f){var m,h,_,v,E,x;if(p.length===16)for(this.parts=[],m=h=0;h<=14;m=h+=2)this.parts.push(p[m]<<8|p[m+1]);else if(p.length===8)this.parts=p;else throw new Error("ipaddr: ipv6 part count should be 8 or 16");for(x=this.parts,_=0,v=x.length;_f&&(p=m.index,f=m[0].length);return f<0?_:_.substring(0,p)+"::"+_.substring(p+f)},u.prototype.toByteArray=function(){var p,f,m,h,_;for(p=[],_=this.parts,f=0,m=_.length;f>8),p.push(h&255);return p},u.prototype.toNormalizedString=function(){var p,f,m;return p=(function(){var h,_,v,E;for(v=this.parts,E=[],h=0,_=v.length;h<_;h++)f=v[h],E.push(f.toString(16));return E}).call(this).join(":"),m="",this.zoneId&&(m="%"+this.zoneId),p+m},u.prototype.toFixedLengthString=function(){var p,f,m;return p=(function(){var h,_,v,E;for(v=this.parts,E=[],h=0,_=v.length;h<_;h++)f=v[h],E.push(f.toString(16).padStart(4,"0"));return E}).call(this).join(":"),m="",this.zoneId&&(m="%"+this.zoneId),p+m},u.prototype.match=function(p,f){var m;if(f===void 0&&(m=p,p=m[0],f=m[1]),p.kind()!=="ipv6")throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one");return s(this.parts,p.parts,16,f)},u.prototype.SpecialRanges={unspecified:[new u([0,0,0,0,0,0,0,0]),128],linkLocal:[new u([65152,0,0,0,0,0,0,0]),10],multicast:[new u([65280,0,0,0,0,0,0,0]),8],loopback:[new u([0,0,0,0,0,0,0,1]),128],uniqueLocal:[new u([64512,0,0,0,0,0,0,0]),7],ipv4Mapped:[new u([0,0,0,0,0,65535,0,0]),96],rfc6145:[new u([0,0,0,0,65535,0,0,0]),96],rfc6052:[new u([100,65435,0,0,0,0,0,0]),96],"6to4":[new u([8194,0,0,0,0,0,0,0]),16],teredo:[new u([8193,0,0,0,0,0,0,0]),32],reserved:[[new u([8193,3512,0,0,0,0,0,0]),32]]},u.prototype.range=function(){return e.subnetMatch(this,this.SpecialRanges)},u.prototype.isIPv4MappedAddress=function(){return this.range()==="ipv4Mapped"},u.prototype.toIPv4Address=function(){var p,f,m;if(!this.isIPv4MappedAddress())throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4");return m=this.parts.slice(-2),p=m[0],f=m[1],new e.IPv4([p>>8,p&255,f>>8,f&255])},u.prototype.prefixLengthFromSubnetMask=function(){var p,f,m,h,_,v,E;for(E={0:16,32768:15,49152:14,57344:13,61440:12,63488:11,64512:10,65024:9,65280:8,65408:7,65472:6,65504:5,65520:4,65528:3,65532:2,65534:1,65535:0},p=0,_=!1,f=m=7;m>=0;f=m+=-1)if(h=this.parts[f],h in E){if(v=E[h],_&&v!==0)return null;v!==16&&(_=!0),p+=v}else return null;return 128-p},u})(),o="(?:[0-9a-f]+::?)+",c="%[0-9a-z]{1,}",i={zoneIndex:new RegExp(c,"i"),native:new RegExp("^(::)?("+o+")?([0-9a-f]+)?(::)?("+c+")?$","i"),transitional:new RegExp("^((?:"+o+")|(?:::)(?:"+o+")?)"+(r+"\\."+r+"\\."+r+"\\."+r)+("("+c+")?$"),"i")},t=function(u,p){var f,m,h,_,v,E;if(u.indexOf("::")!==u.lastIndexOf("::"))return null;for(E=(u.match(i.zoneIndex)||[])[0],E&&(E=E.substring(1),u=u.replace(/%.+$/,"")),f=0,m=-1;(m=u.indexOf(":",m+1))>=0;)f++;if(u.substr(0,2)==="::"&&f--,u.substr(-2,2)==="::"&&f--,f>p)return null;for(v=p-f,_=":";v--;)_+="0:";return u=u.replace("::",_),u[0]===":"&&(u=u.slice(1)),u[u.length-1]===":"&&(u=u.slice(0,-1)),p=(function(){var x,w,I,N;for(I=u.split(":"),N=[],x=0,w=I.length;x=0&&p<=32))return m=[this.parse(f[1]),p],Object.defineProperty(m,"toString",{value:function(){return this.join("/")}}),m;throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range")},e.IPv4.subnetMaskFromPrefixLength=function(u){var p,f,m;if(u=parseInt(u),u<0||u>32)throw new Error("ipaddr: invalid IPv4 prefix length");for(m=[0,0,0,0],f=0,p=Math.floor(u/8);f=0&&p<=128))return m=[this.parse(f[1]),p],Object.defineProperty(m,"toString",{value:function(){return this.join("/")}}),m;throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range")},e.isValid=function(u){return e.IPv6.isValid(u)||e.IPv4.isValid(u)},e.parse=function(u){if(e.IPv6.isValid(u))return e.IPv6.parse(u);if(e.IPv4.isValid(u))return e.IPv4.parse(u);throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format")},e.parseCIDR=function(u){var p;try{return e.IPv6.parseCIDR(u)}catch(f){p=f;try{return e.IPv4.parseCIDR(u)}catch(m){throw p=m,new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format")}}},e.fromByteArray=function(u){var p;if(p=u.length,p===4)return new e.IPv4(u);if(p===16)return new e.IPv6(u);throw new Error("ipaddr: the binary input is neither an IPv6 nor IPv4 address")},e.process=function(u){var p;return p=this.parse(u),p.kind()==="ipv6"&&p.isIPv4MappedAddress()?p.toIPv4Address():p}}).call(IFe)});var mZ=S((eDr,WD)=>{"use strict";WD.exports=S8t;WD.exports.all=$Fe;WD.exports.compile=kFe;var f8t=PFe(),CFe=OFe(),m8t=/^[0-9]+$/,VD=CFe.isValid,HD=CFe.parse,NFe={linklocal:["169.254.0.0/16","fe80::/10"],loopback:["127.0.0.1/8","::1/128"],uniquelocal:["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","fc00::/7"]};function $Fe(t,e){var r=f8t(t);if(!e)return r;typeof e!="function"&&(e=kFe(e));for(var n=0;no)throw new TypeError("invalid range on address: "+t);return[n,i]}function v8t(t){var e=HD(t),r=e.kind();return r==="ipv4"?e.prefixLengthFromSubnetMask():null}function S8t(t,e){if(!t)throw new TypeError("req argument is required");if(!e)throw new TypeError("trust argument is required");var r=$Fe(t,e),n=r[r.length-1];return n}function y8t(){return!1}function E8t(t){return function(r){if(!VD(r))return!1;for(var n=HD(r),o,i=n.kind(),s=0;s{"use strict";var{METHODS:b8t}=require("node:http"),MFe=TD(),x8t=fZ(),A8t=lS(),w8t=mZ(),R8t=uZ(),P8t=require("node:querystring"),{Buffer:DFe}=require("node:buffer");Os.methods=b8t.map(t=>t.toLowerCase());Os.etag=LFe({weak:!1});Os.wetag=LFe({weak:!0});Os.normalizeType=function(t){return~t.indexOf("/")?I8t(t):{value:A8t.lookup(t)||"application/octet-stream",params:{}}};Os.normalizeTypes=function(t){return t.map(Os.normalizeType)};function I8t(t){for(var e=t.length,r=t.indexOf(";"),n=r===-1?e:r,o={value:t.slice(0,n).trim(),quality:1,params:{}};ns){n=t.lastIndexOf(";",i-1)+1;continue}var a=t.slice(n,i).trim(),c=t.slice(i+1,s).trim();a==="q"?o.quality=parseFloat(c):o.params[a]=c,n=s+1}return o}Os.compileETag=function(t){var e;if(typeof t=="function")return t;switch(t){case!0:case"weak":e=Os.wetag;break;case!1:break;case"strong":e=Os.etag;break;default:throw new TypeError("unknown value for etag function: "+t)}return e};Os.compileQueryParser=function(e){var r;if(typeof e=="function")return e;switch(e){case!0:case"simple":r=P8t.parse;break;case!1:break;case"extended":r=O8t;break;default:throw new TypeError("unknown value for query parser function: "+e)}return r};Os.compileTrust=function(t){return typeof t=="function"?t:t===!0?function(){return!0}:typeof t=="number"?function(e,r){return r{jFe.exports=UFe;function UFe(t,e){if(t&&e)return UFe(t)(e);if(typeof t!="function")throw new TypeError("need wrapper function");return Object.keys(t).forEach(function(n){r[n]=t[n]}),r;function r(){for(var n=new Array(arguments.length),o=0;o{var FFe=zFe();hZ.exports=FFe(KD);hZ.exports.strict=FFe(qFe);KD.proto=KD(function(){Object.defineProperty(Function.prototype,"once",{value:function(){return KD(this)},configurable:!0}),Object.defineProperty(Function.prototype,"onceStrict",{value:function(){return qFe(this)},configurable:!0})});function KD(t){var e=function(){return e.called?e.value:(e.called=!0,e.value=t.apply(this,arguments))};return e.called=!1,e}function qFe(t){var e=function(){if(e.called)throw new Error(e.onceError);return e.called=!0,e.value=t.apply(this,arguments)},r=t.name||"Function wrapped with `once`";return e.onceError=r+" shouldn't be called more than once",e.called=!1,e}});var _Z=S((oDr,gZ)=>{gZ.exports=GFe;gZ.exports.default=GFe;function GFe(t){return!!t&&(typeof t=="object"||typeof t=="function")&&typeof t.then=="function"}});var KFe=S(ql=>{"use strict";Object.defineProperty(ql,"__esModule",{value:!0});ql.TokenData=void 0;ql.parse=TZ;ql.compile=k8t;ql.match=D8t;ql.pathToRegexp=WFe;ql.stringify=j8t;var SZ="/",yZ=t=>t,VFe=/^[$_\p{ID_Start}]$/u,EZ=/^[$\u200c\u200d\p{ID_Continue}]$/u,YD="https://git.new/pathToRegexpError",N8t={"{":"{","}":"}","(":"(",")":")","[":"[","]":"]","+":"+","?":"?","!":"!"};function C8t(t){return t.replace(/[{}()\[\]+?!:*]/g,"\\$&")}function Ou(t){return t.replace(/[.+*?^${}()[\]|/\\]/g,"\\$&")}function*$8t(t){let e=[...t],r=0;function n(){let o="";if(VFe.test(e[++r]))for(o+=e[r];EZ.test(e[++r]);)o+=e[r];else if(e[r]==='"'){let i=r;for(;rM8t(o,e,r));return o=>{let i=[""];for(let s of n){let[a,...c]=s(o);i[0]+=a,i.push(...c)}return i}}function M8t(t,e,r){if(t.type==="text")return()=>[t.value];if(t.type==="group"){let o=HFe(t.tokens,e,r);return i=>{let[s,...a]=o(i);return a.length?[""]:[s]}}let n=r||yZ;return t.type==="wildcard"&&r!==!1?o=>{let i=o[t.name];if(i==null)return["",t.name];if(!Array.isArray(i)||i.length===0)throw new TypeError(`Expected "${t.name}" to be a non-empty array`);return[i.map((s,a)=>{if(typeof s!="string")throw new TypeError(`Expected "${t.name}/${a}" to be a string`);return n(s)}).join(e)]}:o=>{let i=o[t.name];if(i==null)return["",t.name];if(typeof i!="string")throw new TypeError(`Expected "${t.name}" to be a string`);return[n(i)]}}function D8t(t,e={}){let{decode:r=decodeURIComponent,delimiter:n=SZ}=e,{regexp:o,keys:i}=WFe(t,e),s=i.map(a=>r===!1?yZ:a.type==="param"?r:c=>c.split(n).map(r));return function(c){let u=o.exec(c);if(!u)return!1;let p=u[0],f=Object.create(null);for(let m=1;mh instanceof yS?h:TZ(h,e));for(let{tokens:h}of p)for(let _ of ZD(h,0,[])){let v=L8t(_,r,s);a.push(v)}let f=`^(?:${a.join("|")})`;return i&&(f+=`(?:${Ou(r)}$)?`),f+=n?"$":`(?=${Ou(r)}|$)`,{regexp:new RegExp(f,c),keys:s}}function*ZD(t,e,r){if(e===t.length)return yield r;let n=t[e];if(n.type==="group"){let o=r.slice();for(let i of ZD(n.tokens,0,o))yield*ZD(t,e+1,i)}else r.push(n);yield*ZD(t,e+1,r)}function L8t(t,e,r){let n="",o="",i=!0;for(let s=0;sEZ.test(n)):!1}function F8t(t){return t?.type!=="text"?!0:!EZ.test(t.value[0])}});var bZ=S((sDr,QFe)=>{"use strict";var YFe=_Z(),q8t=KFe(),B8t=bs()("router:layer"),JFe=$0()("router"),G8t=/\/+$/,V8t=/\((?:\?<(.*?)>)?(?!\?)/g;QFe.exports=ES;function ES(t,e,r){if(!(this instanceof ES))return new ES(t,e,r);B8t("new %o",t);let n=e||{};this.handle=r,this.keys=[],this.name=r.name||"",this.params=void 0,this.path=void 0,this.slash=t==="/"&&n.end===!1;function o(i){if(i instanceof RegExp){let s=[],a=0,c;for(;c=V8t.exec(i.source);)s.push({name:c[1]||a++,offset:c.index});return function(p){let f=i.exec(p);if(!f)return!1;let m={};for(let h=1;h3)return n();try{let i=o(e,r,n);YFe(i)&&(i instanceof Promise||JFe("handlers that are Promise-like are deprecated, use a native Promise instead"),i.then(null,function(s){n(s||new Error("Rejected promise"))}))}catch(i){n(i)}};ES.prototype.match=function(e){let r;if(e!=null){if(this.slash)return this.params={},this.path="",!0;let n=0;for(;!r&&n{"use strict";var eqe=bs()("router:route"),tqe=bZ(),{METHODS:H8t}=require("node:http"),rqe=Array.prototype.slice,nqe=Array.prototype.flat,W8t=H8t.map(t=>t.toLowerCase());oqe.exports=TS;function TS(t){eqe("new %o",t),this.path=t,this.stack=[],this.methods=Object.create(null)}TS.prototype._handlesMethod=function(e){if(this.methods._all)return!0;let r=typeof e=="string"?e.toLowerCase():e;return r==="head"&&!this.methods.head&&(r="get"),!!this.methods[r]};TS.prototype._methods=function(){let e=Object.keys(this.methods);this.methods.get&&!this.methods.head&&e.push("head");for(let r=0;r=i.length)return n(u);if(++s>100)return setImmediate(c,u);let p,f;for(;f!==!0&&o{"use strict";var K8t=_Z(),sqe=bZ(),{METHODS:Z8t}=require("node:http"),Y8t=J0(),aqe=iqe(),JD=bs()("router"),J8t=$0()("router"),cqe=Array.prototype.slice,X8t=Array.prototype.flat,Q8t=Z8t.map(t=>t.toLowerCase());xZ.exports=Bl;xZ.exports.Route=aqe;function Bl(t){if(!(this instanceof Bl))return new Bl(t);let e=t||{};function r(n,o,i){r.handle(n,o,i)}return Object.setPrototypeOf(r,this),r.caseSensitive=e.caseSensitive,r.mergeParams=e.mergeParams,r.params={},r.strict=e.strict,r.stack=[],r}Bl.prototype=function(){};Bl.prototype.param=function(e,r){if(!e)throw new TypeError("argument name is required");if(typeof e!="string")throw new TypeError("argument name must be a string");if(!r)throw new TypeError("argument fn is required");if(typeof r!="function")throw new TypeError("argument fn must be a function");let n=this.params[e];return n||(n=this.params[e]=[]),n.push(r),this};Bl.prototype.handle=function(e,r,n){if(!n)throw new TypeError("argument callback is required");JD("dispatching %s %s",e.method,e.url);let o=0,i,s=rVt(e.url)||"",a="",c=this,u=!1,p=0,f={},m=this.stack,h=e.params,_=e.baseUrl||"",v=sVt(n,e,"baseUrl","next","params");e.next=E,e.method==="OPTIONS"&&(i=[],v=uVt(v,eVt(r,i))),e.baseUrl=_,e.originalUrl=e.originalUrl||e.url,E();function E(w){let I=w==="route"?null:w;if(u&&(e.url=e.url.slice(1),u=!1),a.length!==0&&(e.baseUrl=_,e.url=s+a+e.url.slice(s.length),a=""),I==="router"){setImmediate(v,null);return}if(o>=m.length){setImmediate(v,I);return}if(++p>100)return setImmediate(E,w);let N=tVt(e);if(N==null)return v(I);let $,B,G;for(;B!==!0&&o");let a=new sqe(n,{sensitive:this.caseSensitive,strict:!1,end:!1},s);a.route=void 0,this.stack.push(a)}return this};Bl.prototype.route=function(e){let r=new aqe(e),n=new sqe(e,{sensitive:this.caseSensitive,strict:this.strict,end:!0},o);function o(i,s,a){r.dispatch(i,s,a)}return n.route=r,this.stack.push(n),r};Q8t.concat("all").forEach(function(t){Bl.prototype[t]=function(e){let r=this.route(e);return r[t].apply(r,cqe.call(arguments,1)),this}});function eVt(t,e){return function(n,o){if(o||e.length===0)return n(o);cVt(t,e,n)}}function tVt(t){try{return Y8t(t).pathname}catch{return}}function rVt(t){if(typeof t!="string"||t.length===0||t[0]==="/")return;let e=t.indexOf("?"),r=e!==-1?e:t.length,n=t.substring(0,r).indexOf("://");return n!==-1?t.substring(0,t.indexOf("/",3+n)):void 0}function nVt(t,e){try{return t.match(e)}catch(r){return r}}function oVt(t,e){if(typeof e!="object"||!e)return t;let r=Object.assign({},e);if(!(0 in t)||!(0 in e))return Object.assign(r,t);let n=0,o=0;for(;n in t;)n++;for(;o in e;)o++;for(n--;n>=0;n--)t[n+o]=t[n],n=s.length)return i();if(c=0,u=s[a++],p=n.params[u],f=t[u],m=r[u],p===void 0||!f)return h();if(m&&(m.match===p||m.error&&m.error!=="route"))return n.params[u]=m.value,h(m.error);r[u]=m={error:null,match:p,value:p},_()}function _(v){let E=f[c++];if(m.value=n.params[u],v){m.error=v,h(v);return}if(!E)return h();try{let x=E(n,o,_,p,u);K8t(x)&&(x instanceof Promise||J8t("parameters that are Promise-like are deprecated, use a native Promise instead"),x.then(null,function(w){_(w||new Error("Rejected promise"))}))}catch(x){_(x)}}h()}function sVt(t,e){let r=new Array(arguments.length-2),n=new Array(arguments.length-2);for(let o=0;o{"use strict";var lVt=vFe(),PZ=bs()("express:application"),pVt=bFe(),dVt=require("node:http"),wZ=Vd().methods,fVt=Vd().compileETag,mVt=Vd().compileQueryParser,hVt=Vd().compileTrust,gVt=require("node:path").resolve,_Vt=BFe(),vVt=AZ(),XD=Array.prototype.slice,SVt=Array.prototype.flat,Lo=uqe=lqe.exports={},RZ="@@symbol:trust_proxy_default";Lo.init=function(){var e=null;this.cache=Object.create(null),this.engines=Object.create(null),this.settings=Object.create(null),this.defaultConfiguration(),Object.defineProperty(this,"router",{configurable:!0,enumerable:!0,get:function(){return e===null&&(e=new vVt({caseSensitive:this.enabled("case sensitive routing"),strict:this.enabled("strict routing")})),e}})};Lo.defaultConfiguration=function(){var e="production";this.enable("x-powered-by"),this.set("etag","weak"),this.set("env",e),this.set("query parser","simple"),this.set("subdomain offset",2),this.set("trust proxy",!1),Object.defineProperty(this.settings,RZ,{configurable:!0,value:!0}),PZ("booting in %s mode",e),this.on("mount",function(n){this.settings[RZ]===!0&&typeof n.settings["trust proxy fn"]=="function"&&(delete this.settings["trust proxy"],delete this.settings["trust proxy fn"]),Object.setPrototypeOf(this.request,n.request),Object.setPrototypeOf(this.response,n.response),Object.setPrototypeOf(this.engines,n.engines),Object.setPrototypeOf(this.settings,n.settings)}),this.locals=Object.create(null),this.mountpath="/",this.locals.settings=this.settings,this.set("view",pVt),this.set("views",gVt("views")),this.set("jsonp callback name","callback"),e==="production"&&this.enable("view cache")};Lo.handle=function(e,r,n){var o=n||lVt(e,r,{env:this.get("env"),onerror:yVt.bind(this)});this.enabled("x-powered-by")&&r.setHeader("X-Powered-By","Express"),e.res=r,r.req=e,Object.setPrototypeOf(e,this.request),Object.setPrototypeOf(r,this.response),r.locals||(r.locals=Object.create(null)),this.router.handle(e,r,o)};Lo.use=function(e){var r=0,n="/";if(typeof e!="function"){for(var o=e;Array.isArray(o)&&o.length!==0;)o=o[0];typeof o!="function"&&(r=1,n=e)}var i=SVt.call(XD.call(arguments,r),1/0);if(i.length===0)throw new TypeError("app.use() requires a middleware function");var s=this.router;return i.forEach(function(a){if(!a||!a.handle||!a.set)return s.use(n,a);PZ(".use app under %s",n),a.mountpath=n,a.parent=this,s.use(n,function(u,p,f){var m=u.app;a.handle(u,p,function(h){Object.setPrototypeOf(u,m.request),Object.setPrototypeOf(p,m.response),f(h)})}),a.emit("mount",this)},this),this};Lo.route=function(e){return this.router.route(e)};Lo.engine=function(e,r){if(typeof r!="function")throw new Error("callback function required");var n=e[0]!=="."?"."+e:e;return this.engines[n]=r,this};Lo.param=function(e,r){if(Array.isArray(e)){for(var n=0;n1?'directories "'+c.root.slice(0,-1).join('", "')+'" or "'+c.root[c.root.length-1]+'"':'directory "'+c.root+'"',m=new Error('Failed to lookup view "'+e+'" in views '+f);return m.view=c,i(m)}u.cache&&(o[e]=c)}EVt(c,u,i)};Lo.listen=function(){var e=dVt.createServer(this),r=XD.call(arguments);if(typeof r[r.length-1]=="function"){var n=r[r.length-1]=_Vt(r[r.length-1]);e.once("error",n)}return e.listen.apply(e,r)};function yVt(t){this.get("env")!=="test"&&console.error(t.stack||t.toString())}function EVt(t,e,r){try{t.render(e,r)}catch(n){r(n)}}});var hqe=S((uDr,IZ)=>{"use strict";IZ.exports=mqe;IZ.exports.preferredCharsets=mqe;var TVt=/^\s*([^\s;]+)\s*(?:;(.*))?$/;function bVt(t){for(var e=t.split(","),r=0,n=0;r0}});var Sqe=S((lDr,OZ)=>{"use strict";OZ.exports=vqe;OZ.exports.preferredEncodings=vqe;var PVt=/^\s*([^\s;]+)\s*(?:;(.*))?$/;function IVt(t){for(var e=t.split(","),r=!1,n=1,o=0,i=0;o0}});var xqe=S((pDr,NZ)=>{"use strict";NZ.exports=bqe;NZ.exports.preferredLanguages=bqe;var kVt=/^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;function MVt(t){for(var e=t.split(","),r=0,n=0;r0}});var Oqe=S((dDr,CZ)=>{"use strict";CZ.exports=Pqe;CZ.exports.preferredMediaTypes=Pqe;var jVt=/^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/;function zVt(t){for(var e=VVt(t),r=0,n=0;r0)if(i.every(function(s){return e.params[s]=="*"||(e.params[s]||"").toLowerCase()==(n.params[s]||"").toLowerCase()}))o|=1;else return null;return{i:r,o:e.i,q:e.q,s:o}}function Pqe(t,e){var r=zVt(t===void 0?"*/*":t||"");if(!e)return r.filter(wqe).sort(Aqe).map(BVt);var n=e.map(function(i,s){return FVt(i,r,s)});return n.filter(wqe).sort(Aqe).map(function(i){return e[n.indexOf(i)]})}function Aqe(t,e){return e.q-t.q||e.s-t.s||t.o-e.o||t.i-e.i||0}function BVt(t){return t.type+"/"+t.subtype}function wqe(t){return t.q>0}function Iqe(t){for(var e=0,r=0;(r=t.indexOf('"',r))!==-1;)e++,r++;return e}function GVt(t){var e=t.indexOf("="),r,n;return e===-1?r=t:(r=t.slice(0,e),n=t.slice(e+1)),[r,n]}function VVt(t){for(var e=t.split(","),r=1,n=0;r{"use strict";var WVt=hqe(),KVt=Sqe(),ZVt=xqe(),YVt=Oqe();$Z.exports=kr;$Z.exports.Negotiator=kr;function kr(t){if(!(this instanceof kr))return new kr(t);this.request=t}kr.prototype.charset=function(e){var r=this.charsets(e);return r&&r[0]};kr.prototype.charsets=function(e){return WVt(this.request.headers["accept-charset"],e)};kr.prototype.encoding=function(e,r){var n=this.encodings(e,r);return n&&n[0]};kr.prototype.encodings=function(e,r){var n=r||{};return KVt(this.request.headers["accept-encoding"],e,n.preferred)};kr.prototype.language=function(e){var r=this.languages(e);return r&&r[0]};kr.prototype.languages=function(e){return ZVt(this.request.headers["accept-language"],e)};kr.prototype.mediaType=function(e){var r=this.mediaTypes(e);return r&&r[0]};kr.prototype.mediaTypes=function(e){return YVt(this.request.headers.accept,e)};kr.prototype.preferredCharset=kr.prototype.charset;kr.prototype.preferredCharsets=kr.prototype.charsets;kr.prototype.preferredEncoding=kr.prototype.encoding;kr.prototype.preferredEncodings=kr.prototype.encodings;kr.prototype.preferredLanguage=kr.prototype.language;kr.prototype.preferredLanguages=kr.prototype.languages;kr.prototype.preferredMediaType=kr.prototype.mediaType;kr.prototype.preferredMediaTypes=kr.prototype.mediaTypes});var $qe=S((mDr,Cqe)=>{"use strict";var JVt=Nqe(),XVt=lS();Cqe.exports=Ns;function Ns(t){if(!(this instanceof Ns))return new Ns(t);this.headers=t.headers,this.negotiator=new JVt(t)}Ns.prototype.type=Ns.prototype.types=function(t){var e=t;if(e&&!Array.isArray(e)){e=new Array(arguments.length);for(var r=0;r{"use strict";var tHt=/(?:^|,)\s*?no-cache\s*?(?:,|$)/;Mqe.exports=rHt;function rHt(t,e){var r=t["if-modified-since"],n=t["if-none-match"];if(!r&&!n)return!1;var o=t["cache-control"];if(o&&tHt.test(o))return!1;if(n){if(n==="*")return!0;var i=e.etag;if(!i)return!1;for(var s=nHt(n),a=0;a{"use strict";Dqe.exports=oHt;function oHt(t,e,r){if(typeof e!="string")throw new TypeError("argument str must be a string");var n=e.indexOf("=");if(n===-1)return-2;var o=e.slice(n+1).split(","),i=[];i.type=e.slice(0,n);for(var s=0;st-1&&(u=t-1),!(isNaN(c)||isNaN(u)||c>u||c<0)&&i.push({start:c,end:u})}return i.length<1?-1:r&&r.combine?iHt(i):i}function iHt(t){for(var e=t.map(sHt).sort(uHt),r=0,n=1;ni.end+1?e[++r]=o:o.end>i.end&&(i.end=o.end,i.index=Math.min(i.index,o.index))}e.length=r+1;var s=e.sort(cHt).map(aHt);return s.type=t.type,s}function sHt(t,e){return{start:t.start,end:t.end,index:e}}function aHt(t){return{start:t.start,end:t.end}}function cHt(t,e){return t.index-e.index}function uHt(t,e){return t.start-e.start}});var zqe=S((_Dr,jqe)=>{"use strict";var QD=$qe(),lHt=require("node:net").isIP,pHt=xD(),dHt=require("node:http"),fHt=kZ(),mHt=MZ(),Lqe=J0(),Uqe=mZ(),$n=Object.create(dHt.IncomingMessage.prototype);jqe.exports=$n;$n.get=$n.header=function(e){if(!e)throw new TypeError("name argument is required to req.get");if(typeof e!="string")throw new TypeError("name must be a string to req.get");var r=e.toLowerCase();switch(r){case"referer":case"referrer":return this.headers.referrer||this.headers.referer;default:return this.headers[r]}};$n.accepts=function(){var t=QD(this);return t.types.apply(t,arguments)};$n.acceptsEncodings=function(){var t=QD(this);return t.encodings.apply(t,arguments)};$n.acceptsCharsets=function(){var t=QD(this);return t.charsets.apply(t,arguments)};$n.acceptsLanguages=function(...t){return QD(this).languages(...t)};$n.range=function(e,r){var n=this.get("Range");if(n)return mHt(e,n,r)};ka($n,"query",function(){var e=this.app.get("query parser fn");if(!e)return Object.create(null);var r=Lqe(this).query;return e(r)});$n.is=function(e){var r=e;if(!Array.isArray(e)){r=new Array(arguments.length);for(var n=0;n=200&&r<300||r===304?fHt(this.headers,{etag:e.get("ETag"),"last-modified":e.get("Last-Modified")}):!1});ka($n,"stale",function(){return!this.fresh});ka($n,"xhr",function(){var e=this.get("X-Requested-With")||"";return e.toLowerCase()==="xmlhttprequest"});function ka(t,e,r){Object.defineProperty(t,e,{configurable:!0,enumerable:!0,get:r})}});var Bqe=S((DZ,qqe)=>{var eL=require("buffer"),Nu=eL.Buffer;function Fqe(t,e){for(var r in t)e[r]=t[r]}Nu.from&&Nu.alloc&&Nu.allocUnsafe&&Nu.allocUnsafeSlow?qqe.exports=eL:(Fqe(eL,DZ),DZ.Buffer=Wm);function Wm(t,e,r){return Nu(t,e,r)}Wm.prototype=Object.create(Nu.prototype);Fqe(Nu,Wm);Wm.from=function(t,e,r){if(typeof t=="number")throw new TypeError("Argument must not be a number");return Nu(t,e,r)};Wm.alloc=function(t,e,r){if(typeof t!="number")throw new TypeError("Argument must be a number");var n=Nu(t);return e!==void 0?typeof r=="string"?n.fill(e,r):n.fill(e):n.fill(0),n};Wm.allocUnsafe=function(t){if(typeof t!="number")throw new TypeError("Argument must be a number");return Nu(t)};Wm.allocUnsafeSlow=function(t){if(typeof t!="number")throw new TypeError("Argument must be a number");return eL.SlowBuffer(t)}});var Zqe=S((vDr,LZ)=>{"use strict";LZ.exports=AHt;LZ.exports.parse=IHt;var Gqe=require("path").basename,hHt=Bqe().Buffer,gHt=/[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g,_Ht=/%[0-9A-Fa-f]{2}/,vHt=/%([0-9A-Fa-f]{2})/g,Hqe=/[^\x20-\x7e\xa0-\xff]/g,SHt=/\\([\u0000-\u007f])/g,yHt=/([\\"])/g,Vqe=/;[\x09\x20]*([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*=[\x09\x20]*("(?:[\x20!\x23-\x5b\x5d-\x7e\x80-\xff]|\\[\x20-\x7e])*"|[!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*/g,EHt=/^[\x20-\x7e\x80-\xff]+$/,THt=/^[!#$%&'*+.0-9A-Z^_`a-z|~-]+$/,bHt=/^([A-Za-z0-9!#$%&+\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[0-9A-Fa-f]{2}|[A-Za-z0-9!#$&+.^_`|~-])+)$/,xHt=/^([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*(?:$|;)/;function AHt(t,e){var r=e||{},n=r.type||"attachment",o=wHt(t,r.fallback);return RHt(new Kqe(n,o))}function wHt(t,e){if(t!==void 0){var r={};if(typeof t!="string")throw new TypeError("filename must be a string");if(e===void 0&&(e=!0),typeof e!="string"&&typeof e!="boolean")throw new TypeError("fallback must be a string or boolean");if(typeof e=="string"&&Hqe.test(e))throw new TypeError("fallback must be ISO-8859-1 string");var n=Gqe(t),o=EHt.test(n),i=typeof e!="string"?e&&Wqe(n):Gqe(e),s=typeof i=="string"&&i!==n;return(s||!o||_Ht.test(n))&&(r["filename*"]=n),(o||s)&&(r.filename=s?i:n),r}}function RHt(t){var e=t.parameters,r=t.type;if(!r||typeof r!="string"||!THt.test(r))throw new TypeError("invalid type");var n=String(r).toLowerCase();if(e&&typeof e=="object")for(var o,i=Object.keys(e).sort(),s=0;s{var Yqe=require("crypto");tL.sign=function(t,e){if(typeof t!="string")throw new TypeError("Cookie value must be provided as a string.");if(e==null)throw new TypeError("Secret key must be provided.");return t+"."+Yqe.createHmac("sha256",e).update(t).digest("base64").replace(/\=+$/,"")};tL.unsign=function(t,e){if(typeof t!="string")throw new TypeError("Signed cookie string must be provided.");if(e==null)throw new TypeError("Secret key must be provided.");var r=t.slice(0,t.lastIndexOf(".")),n=tL.sign(r,e),o=Buffer.from(n),i=Buffer.from(t);return o.length===i.length&&Yqe.timingSafeEqual(o,i)?r:!1}});var e6e=S(UZ=>{"use strict";UZ.parse=zHt;UZ.serialize=FHt;var kHt=Object.prototype.toString,MHt=Object.prototype.hasOwnProperty,DHt=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,LHt=/^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/,UHt=/^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i,jHt=/^[\u0020-\u003A\u003D-\u007E]*$/;function zHt(t,e){if(typeof t!="string")throw new TypeError("argument str must be a string");var r={},n=t.length;if(n<2)return r;var o=e&&e.decode||qHt,i=0,s=0,a=0;do{if(s=t.indexOf("=",i),s===-1)break;if(a=t.indexOf(";",i),a===-1)a=n;else if(s>a){i=t.lastIndexOf(";",s-1)+1;continue}var c=Xqe(t,i,s),u=Qqe(t,s,c),p=t.slice(c,u);if(!MHt.call(r,p)){var f=Xqe(t,s+1,a),m=Qqe(t,a,f);t.charCodeAt(f)===34&&t.charCodeAt(m-1)===34&&(f++,m--);var h=t.slice(f,m);r[p]=GHt(h,o)}i=a+1}while(ir;){var n=t.charCodeAt(--e);if(n!==32&&n!==9)return e+1}return r}function FHt(t,e,r){var n=r&&r.encode||encodeURIComponent;if(typeof n!="function")throw new TypeError("option encode is invalid");if(!DHt.test(t))throw new TypeError("argument name is invalid");var o=n(e);if(!LHt.test(o))throw new TypeError("argument val is invalid");var i=t+"="+o;if(!r)return i;if(r.maxAge!=null){var s=Math.floor(r.maxAge);if(!isFinite(s))throw new TypeError("option maxAge is invalid");i+="; Max-Age="+s}if(r.domain){if(!UHt.test(r.domain))throw new TypeError("option domain is invalid");i+="; Domain="+r.domain}if(r.path){if(!jHt.test(r.path))throw new TypeError("option path is invalid");i+="; Path="+r.path}if(r.expires){var a=r.expires;if(!BHt(a)||isNaN(a.valueOf()))throw new TypeError("option expires is invalid");i+="; Expires="+a.toUTCString()}if(r.httpOnly&&(i+="; HttpOnly"),r.secure&&(i+="; Secure"),r.partitioned&&(i+="; Partitioned"),r.priority){var c=typeof r.priority=="string"?r.priority.toLowerCase():r.priority;switch(c){case"low":i+="; Priority=Low";break;case"medium":i+="; Priority=Medium";break;case"high":i+="; Priority=High";break;default:throw new TypeError("option priority is invalid")}}if(r.sameSite){var u=typeof r.sameSite=="string"?r.sameSite.toLowerCase():r.sameSite;switch(u){case!0:i+="; SameSite=Strict";break;case"lax":i+="; SameSite=Lax";break;case"strict":i+="; SameSite=Strict";break;case"none":i+="; SameSite=None";break;default:throw new TypeError("option sameSite is invalid")}}return i}function qHt(t){return t.indexOf("%")!==-1?decodeURIComponent(t):t}function BHt(t){return kHt.call(t)==="[object Date]"}function GHt(t,e){try{return e(t)}catch{return t}}});var FZ=S((EDr,p6e)=>{"use strict";var jZ=sS(),Uo=bs()("send"),VHt=Z0(),o6e=Y0(),HHt=fZ(),WHt=kZ(),nL=require("fs"),KHt=lS(),ZHt=s8(),YHt=L0(),JHt=MZ(),eA=require("path"),XHt=k0(),i6e=require("stream"),QHt=require("util"),s6e=eA.extname,a6e=eA.join,zZ=eA.normalize,c6e=eA.resolve,rL=eA.sep,e3t=/^ *bytes=/,t3t=3600*24*365*1e3,t6e=/(?:^|[\\/])\.\.(?:[\\/]|$)/;p6e.exports=r3t;function r3t(t,e,r){return new kn(t,e,r)}function kn(t,e,r){i6e.call(this);var n=r||{};if(this.options=n,this.path=e,this.req=t,this._acceptRanges=n.acceptRanges!==void 0?!!n.acceptRanges:!0,this._cacheControl=n.cacheControl!==void 0?!!n.cacheControl:!0,this._etag=n.etag!==void 0?!!n.etag:!0,this._dotfiles=n.dotfiles!==void 0?n.dotfiles:"ignore",this._dotfiles!=="ignore"&&this._dotfiles!=="allow"&&this._dotfiles!=="deny")throw new TypeError('dotfiles option must be "allow", "deny", or "ignore"');this._extensions=n.extensions!==void 0?n6e(n.extensions,"extensions option"):[],this._immutable=n.immutable!==void 0?!!n.immutable:!1,this._index=n.index!==void 0?n6e(n.index,"index option"):["index.html"],this._lastModified=n.lastModified!==void 0?!!n.lastModified:!0,this._maxage=n.maxAge||n.maxage,this._maxage=typeof this._maxage=="string"?ZHt(this._maxage):Number(this._maxage),this._maxage=isNaN(this._maxage)?0:Math.min(Math.max(0,this._maxage),t3t),this._root=n.root?c6e(n.root):null}QHt.inherits(kn,i6e);kn.prototype.error=function(e,r){if(l6e(this,"error"))return this.emit("error",s3t(e,r));var n=this.res,o=XHt.message[e]||String(e),i=u6e("Error",o6e(o));n3t(n),r&&r.headers&&u3t(n,r.headers),n.statusCode=e,n.setHeader("Content-Type","text/html; charset=UTF-8"),n.setHeader("Content-Length",Buffer.byteLength(i)),n.setHeader("Content-Security-Policy","default-src 'none'"),n.setHeader("X-Content-Type-Options","nosniff"),n.end(i)};kn.prototype.hasTrailingSlash=function(){return this.path[this.path.length-1]==="/"};kn.prototype.isConditionalGET=function(){return this.req.headers["if-match"]||this.req.headers["if-unmodified-since"]||this.req.headers["if-none-match"]||this.req.headers["if-modified-since"]};kn.prototype.isPreconditionFailure=function(){var e=this.req,r=this.res,n=e.headers["if-match"];if(n){var o=r.getHeader("ETag");return!o||n!=="*"&&c3t(n).every(function(a){return a!==o&&a!=="W/"+o&&"W/"+a!==o})}var i=oL(e.headers["if-unmodified-since"]);if(!isNaN(i)){var s=oL(r.getHeader("Last-Modified"));return isNaN(s)||s>i}return!1};kn.prototype.removeContentHeaderFields=function(){var e=this.res;e.removeHeader("Content-Encoding"),e.removeHeader("Content-Language"),e.removeHeader("Content-Length"),e.removeHeader("Content-Range"),e.removeHeader("Content-Type")};kn.prototype.notModified=function(){var e=this.res;Uo("not modified"),this.removeContentHeaderFields(),e.statusCode=304,e.end()};kn.prototype.headersAlreadySent=function(){var e=new Error("Can't set headers after they are sent.");Uo("headers already sent"),this.error(500,e)};kn.prototype.isCachable=function(){var e=this.res.statusCode;return e>=200&&e<300||e===304};kn.prototype.onStatError=function(e){switch(e.code){case"ENAMETOOLONG":case"ENOENT":case"ENOTDIR":this.error(404,e);break;default:this.error(500,e);break}};kn.prototype.isFresh=function(){return WHt(this.req.headers,{etag:this.res.getHeader("ETag"),"last-modified":this.res.getHeader("Last-Modified")})};kn.prototype.isRangeFresh=function(){var e=this.req.headers["if-range"];if(!e)return!0;if(e.indexOf('"')!==-1){var r=this.res.getHeader("ETag");return!!(r&&e.indexOf(r)!==-1)}var n=this.res.getHeader("Last-Modified");return oL(n)<=oL(e)};kn.prototype.redirect=function(e){var r=this.res;if(l6e(this,"directory")){this.emit("directory",r,e);return}if(this.hasTrailingSlash()){this.error(403);return}var n=VHt(o3t(this.path+"/")),o=u6e("Redirecting","Redirecting to "+o6e(n));r.statusCode=301,r.setHeader("Content-Type","text/html; charset=UTF-8"),r.setHeader("Content-Length",Buffer.byteLength(o)),r.setHeader("Content-Security-Policy","default-src 'none'"),r.setHeader("X-Content-Type-Options","nosniff"),r.setHeader("Location",n),r.end(o)};kn.prototype.pipe=function(e){var r=this._root;this.res=e;var n=a3t(this.path);if(n===-1)return this.error(400),e;if(~n.indexOf("\0"))return this.error(400),e;var o;if(r!==null){if(n&&(n=zZ("."+rL+n)),t6e.test(n))return Uo('malicious path "%s"',n),this.error(403),e;o=n.split(rL),n=zZ(a6e(r,n))}else{if(t6e.test(n))return Uo('malicious path "%s"',n),this.error(403),e;o=zZ(n).split(rL),n=c6e(n)}if(i3t(o))switch(Uo('%s dotfile "%s"',this._dotfiles,n),this._dotfiles){case"allow":break;case"deny":return this.error(403),e;case"ignore":default:return this.error(404),e}return this._index.length&&this.hasTrailingSlash()?(this.sendIndex(n),e):(this.sendFile(n),e)};kn.prototype.send=function(e,r){var n=r.size,o=this.options,i={},s=this.res,a=this.req,c=a.headers.range,u=o.start||0;if(s.headersSent){this.headersAlreadySent();return}if(Uo('pipe "%s"',e),this.setHeader(e,r),this.type(e),this.isConditionalGET()){if(this.isPreconditionFailure()){this.error(412);return}if(this.isCachable()&&this.isFresh()){this.notModified();return}}if(n=Math.max(0,n-u),o.end!==void 0){var p=o.end-u+1;n>p&&(n=p)}if(this._acceptRanges&&e3t.test(c)){if(c=JHt(n,c,{combine:!0}),this.isRangeFresh()||(Uo("range stale"),c=-2),c===-1)return Uo("range unsatisfiable"),s.setHeader("Content-Range",r6e("bytes",n)),this.error(416,{headers:{"Content-Range":s.getHeader("Content-Range")}});c!==-2&&c.length===1&&(Uo("range %j",c),s.statusCode=206,s.setHeader("Content-Range",r6e("bytes",n,c[0])),u+=c[0].start,n=c[0].end-c[0].start+1)}for(var f in o)i[f]=o[f];if(i.start=u,i.end=Math.max(u,u+n-1),s.setHeader("Content-Length",n),a.method==="HEAD"){s.end();return}this.stream(e,i)};kn.prototype.sendFile=function(e){var r=0,n=this;Uo('stat "%s"',e),nL.stat(e,function(s,a){var c=e[e.length-1]===rL;if(s&&s.code==="ENOENT"&&!s6e(e)&&!c)return o(s);if(s)return n.onStatError(s);if(a.isDirectory())return n.redirect(e);if(c)return n.error(404);n.emit("file",e,a),n.send(e,a)});function o(i){if(n._extensions.length<=r)return i?n.onStatError(i):n.error(404);var s=e+"."+n._extensions[r++];Uo('stat "%s"',s),nL.stat(s,function(a,c){if(a)return o(a);if(c.isDirectory())return o();n.emit("file",s,c),n.send(s,c)})}};kn.prototype.sendIndex=function(e){var r=-1,n=this;function o(i){if(++r>=n._index.length)return i?n.onStatError(i):n.error(404);var s=a6e(e,n._index[r]);Uo('stat "%s"',s),nL.stat(s,function(a,c){if(a)return o(a);if(c.isDirectory())return o();n.emit("file",s,c),n.send(s,c)})}o()};kn.prototype.stream=function(e,r){var n=this,o=this.res,i=nL.createReadStream(e,r);this.emit("stream",i),i.pipe(o);function s(){i.destroy()}YHt(o,s),i.on("error",function(c){s(),n.onStatError(c)}),i.on("end",function(){n.emit("end")})};kn.prototype.type=function(e){var r=this.res;if(!r.getHeader("Content-Type")){var n=s6e(e),o=KHt.contentType(n)||"application/octet-stream";Uo("content-type %s",o),r.setHeader("Content-Type",o)}};kn.prototype.setHeader=function(e,r){var n=this.res;if(this.emit("headers",n,e,r),this._acceptRanges&&!n.getHeader("Accept-Ranges")&&(Uo("accept ranges"),n.setHeader("Accept-Ranges","bytes")),this._cacheControl&&!n.getHeader("Cache-Control")){var o="public, max-age="+Math.floor(this._maxage/1e3);this._immutable&&(o+=", immutable"),Uo("cache-control %s",o),n.setHeader("Cache-Control",o)}if(this._lastModified&&!n.getHeader("Last-Modified")){var i=r.mtime.toUTCString();Uo("modified %s",i),n.setHeader("Last-Modified",i)}if(this._etag&&!n.getHeader("ETag")){var s=HHt(r);Uo("etag %s",s),n.setHeader("ETag",s)}};function n3t(t){for(let e of t.getHeaderNames())t.removeHeader(e)}function o3t(t){for(var e=0;e1?"/"+t.substr(e):t}function i3t(t){for(var e=0;e1&&r[0]===".")return!0}return!1}function r6e(t,e,r){return t+" "+(r?r.start+"-"+r.end:"*")+"/"+e}function u6e(t,e){return` - - - -`+t+` - - -
`+e+`
- - -`}function s3t(t,e){return e?e instanceof Error?jZ(t,e,{expose:!1}):jZ(t,e):jZ(t)}function a3t(t){try{return decodeURIComponent(t)}catch{return-1}}function l6e(t,e){var r=typeof t.listenerCount!="function"?t.listeners(e).length:t.listenerCount(e);return r>0}function n6e(t,e){for(var r=[].concat(t||[]),n=0;n{"use strict";qZ.exports=p3t;qZ.exports.append=f6e;var l3t=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;function f6e(t,e){if(typeof t!="string")throw new TypeError("header argument is required");if(!e)throw new TypeError("field argument is required");for(var r=Array.isArray(e)?e:d6e(String(e)),n=0;n{"use strict";var m6e=Zqe(),d3t=sS(),GZ=$0()("express"),f3t=Z0(),m3t=Y0(),h3t=require("node:http"),g3t=L0(),h6e=lS(),g6e=require("node:path"),_3t=require("node:path").isAbsolute,VZ=k0(),v3t=Jqe().sign,S3t=Vd().normalizeType,y3t=Vd().normalizeTypes,E3t=Vd().setCharset,T3t=e6e(),b3t=FZ(),x3t=g6e.extname,A3t=g6e.resolve,w3t=BZ(),{Buffer:iL}=require("node:buffer"),dn=Object.create(h3t.ServerResponse.prototype);v6e.exports=dn;dn.status=function(e){if(!Number.isInteger(e))throw new TypeError(`Invalid status code: ${JSON.stringify(e)}. Status code must be an integer.`);if(e<100||e>999)throw new RangeError(`Invalid status code: ${JSON.stringify(e)}. Status code must be greater than 99 and less than 1000.`);return this.statusCode=e,this};dn.links=function(t){var e=this.get("Link")||"";return e&&(e+=", "),this.set("Link",e+Object.keys(t).map(function(r){return Array.isArray(t[r])?t[r].map(function(n){return`<${n}>; rel="${r}"`}).join(", "):`<${t[r]}>; rel="${r}"`}).join(", "))};dn.send=function(e){var r=e,n,o=this.req,i,s=this.app;switch(typeof r){case"string":this.get("Content-Type")||this.type("html");break;case"boolean":case"number":case"object":if(r===null)r="";else if(ArrayBuffer.isView(r))this.get("Content-Type")||this.type("bin");else return this.json(r);break}typeof r=="string"&&(n="utf8",i=this.get("Content-Type"),typeof i=="string"&&this.set("Content-Type",E3t(i,"utf-8")));var a=s.get("etag fn"),c=!this.get("ETag")&&typeof a=="function",u;r!==void 0&&(iL.isBuffer(r)?u=r.length:!c&&r.length<1e3?u=iL.byteLength(r,n):(r=iL.from(r,n),n=void 0,u=r.length),this.set("Content-Length",u));var p;return c&&u!==void 0&&(p=a(r,n))&&this.set("ETag",p),o.fresh&&this.status(304),(this.statusCode===204||this.statusCode===304)&&(this.removeHeader("Content-Type"),this.removeHeader("Content-Length"),this.removeHeader("Transfer-Encoding"),r=""),this.statusCode===205&&(this.set("Content-Length","0"),this.removeHeader("Transfer-Encoding"),r=""),o.method==="HEAD"?this.end():this.end(r,n),this};dn.json=function(e){var r=this.app,n=r.get("json escape"),o=r.get("json replacer"),i=r.get("json spaces"),s=_6e(e,o,i,n);return this.get("Content-Type")||this.set("Content-Type","application/json"),this.send(s)};dn.jsonp=function(e){var r=this.app,n=r.get("json escape"),o=r.get("json replacer"),i=r.get("json spaces"),s=_6e(e,o,i,n),a=this.req.query[r.get("jsonp callback name")];return this.get("Content-Type")||(this.set("X-Content-Type-Options","nosniff"),this.set("Content-Type","application/json")),Array.isArray(a)&&(a=a[0]),typeof a=="string"&&a.length!==0&&(this.set("X-Content-Type-Options","nosniff"),this.set("Content-Type","text/javascript"),a=a.replace(/[^\[\]\w$.]/g,""),s===void 0?s="":typeof s=="string"&&(s=s.replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")),s="/**/ typeof "+a+" === 'function' && "+a+"("+s+");"),this.send(s)};dn.sendStatus=function(e){var r=VZ.message[e]||String(e);return this.status(e),this.type("txt"),this.send(r)};dn.sendFile=function(e,r,n){var o=n,i=this.req,s=this,a=i.next,c=r||{};if(!e)throw new TypeError("path argument is required to res.sendFile");if(typeof e!="string")throw new TypeError("path must be a string to res.sendFile");if(typeof r=="function"&&(o=r,c={}),!c.root&&!_3t(e))throw new TypeError("path must be absolute or specify root to res.sendFile");var u=encodeURI(e);c.etag=this.app.enabled("etag");var p=b3t(i,u,c);R3t(s,p,c,function(f){if(o)return o(f);if(f&&f.code==="EISDIR")return a();f&&f.code!=="ECONNABORTED"&&f.syscall!=="write"&&a(f)})};dn.download=function(e,r,n,o){var i=o,s=r,a=n||null;typeof r=="function"?(i=r,s=null,a=null):typeof n=="function"&&(i=n,a=null),typeof r=="object"&&(typeof n=="function"||n===void 0)&&(s=null,a=r);var c={"Content-Disposition":m6e(s||e)};if(a&&a.headers)for(var u=Object.keys(a.headers),p=0;p0?e.accepts(n):!1;return this.vary("Accept"),o?(this.set("Content-Type",S3t(o).value),t[o](e,this,r)):t.default?t.default(e,this,r):r(d3t(406,{types:y3t(n).map(function(i){return i.value})})),this};dn.attachment=function(e){return e&&this.type(x3t(e)),this.set("Content-Disposition",m6e(e)),this};dn.append=function(e,r){var n=this.get(e),o=r;return n&&(o=Array.isArray(n)?n.concat(r):Array.isArray(r)?[n].concat(r):[n,r]),this.set(e,o)};dn.set=dn.header=function(e,r){if(arguments.length===2){var n=Array.isArray(r)?r.map(String):String(r);if(e.toLowerCase()==="content-type"){if(Array.isArray(n))throw new TypeError("Content-Type cannot be set to an Array");n=h6e.contentType(n)}this.setHeader(e,n)}else for(var o in e)this.set(o,e[o]);return this};dn.get=function(t){return this.getHeader(t)};dn.clearCookie=function(e,r){let n={path:"/",...r,expires:new Date(1)};return delete n.maxAge,this.cookie(e,"",n)};dn.cookie=function(t,e,r){var n={...r},o=this.req.secret,i=n.signed;if(i&&!o)throw new Error('cookieParser("secret") required for signed cookies');var s=typeof e=="object"?"j:"+JSON.stringify(e):String(e);if(i&&(s="s:"+v3t(s,o)),n.maxAge!=null){var a=n.maxAge-0;isNaN(a)||(n.expires=new Date(Date.now()+a),n.maxAge=Math.floor(a/1e3))}return n.path==null&&(n.path="/"),this.append("Set-Cookie",T3t.serialize(t,String(s),n)),this};dn.location=function(e){return this.set("Location",f3t(e))};dn.redirect=function(e){var r=e,n,o=302;arguments.length===2&&(o=arguments[0],r=arguments[1]),r||GZ("Provide a url argument"),typeof r!="string"&&GZ("Url must be a string"),typeof o!="number"&&GZ("Status must be a number"),r=this.location(r).get("Location"),this.format({text:function(){n=VZ.message[o]+". Redirecting to "+r},html:function(){var i=m3t(r);n="

"+VZ.message[o]+". Redirecting to "+i+"

"},default:function(){n=""}}),this.status(o),this.set("Content-Length",iL.byteLength(n)),this.req.method==="HEAD"?this.end():this.end(n)};dn.vary=function(t){return w3t(this,t),this};dn.render=function(e,r,n){var o=this.req.app,i=n,s=r||{},a=this.req,c=this;typeof r=="function"&&(i=r,s={}),s._locals=c.locals,i=i||function(u,p){if(u)return a.next(u);c.send(p)},o.render(e,s,i)};function R3t(t,e,r,n){var o=!1,i;function s(){if(!o){o=!0;var h=new Error("Request aborted");h.code="ECONNABORTED",n(h)}}function a(){if(!o){o=!0;var h=new Error("EISDIR, read");h.code="EISDIR",n(h)}}function c(h){o||(o=!0,n(h))}function u(){o||(o=!0,n())}function p(){i=!1}function f(h){if(h&&h.code==="ECONNRESET")return s();if(h)return c(h);o||setImmediate(function(){if(i!==!1&&!o){s();return}o||(o=!0,n())})}function m(){i=!0}e.on("directory",a),e.on("end",u),e.on("error",c),e.on("file",p),e.on("stream",m),g3t(t,f),r.headers&&e.on("headers",function(_){for(var v=r.headers,E=Object.keys(v),x=0;x&]/g,function(i){switch(i.charCodeAt(0)){case 60:return"\\u003c";case 62:return"\\u003e";case 38:return"\\u0026";default:return i}})),o}});var E6e=S((xDr,y6e)=>{"use strict";var P3t=Z0(),I3t=Y0(),HZ=J0(),O3t=require("path").resolve,N3t=FZ(),C3t=require("url");y6e.exports=$3t;function $3t(t,e){if(!t)throw new TypeError("root path required");if(typeof t!="string")throw new TypeError("root path must be a string");var r=Object.create(e||null),n=r.fallthrough!==!1,o=r.redirect!==!1,i=r.setHeaders;if(i&&typeof i!="function")throw new TypeError("option setHeaders must be function");r.maxage=r.maxage||r.maxAge||0,r.root=O3t(t);var s=o?L3t():D3t();return function(c,u,p){if(c.method!=="GET"&&c.method!=="HEAD"){if(n)return p();u.statusCode=405,u.setHeader("Allow","GET, HEAD"),u.setHeader("Content-Length","0"),u.end();return}var f=!n,m=HZ.original(c),h=HZ(c).pathname;h==="/"&&m.pathname.substr(-1)!=="/"&&(h="");var _=N3t(c,h,r);_.on("directory",s),i&&_.on("headers",i),n&&_.on("file",function(){f=!0}),_.on("error",function(E){if(f||!(E.statusCode<500)){p(E);return}p()}),_.pipe(u)}}function k3t(t){for(var e=0;e1?"/"+t.substr(e):t}function M3t(t,e){return` - - - -`+t+` - - -
`+e+`
- - -`}function D3t(){return function(){this.error(404)}}function L3t(){return function(e){if(this.hasTrailingSlash()){this.error(404);return}var r=HZ.original(this.req);r.path=null,r.pathname=k3t(r.pathname+"/");var n=P3t(C3t.format(r)),o=M3t("Redirecting","Redirecting to "+I3t(n));e.statusCode=301,e.setHeader("Content-Type","text/html; charset=UTF-8"),e.setHeader("Content-Length",Buffer.byteLength(o)),e.setHeader("Content-Security-Policy","default-src 'none'"),e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Location",n),e.end(o)}}});var P6e=S((Ma,R6e)=>{"use strict";var sL=iFe(),U3t=require("node:events").EventEmitter,T6e=aFe(),b6e=pqe(),x6e=AZ(),A6e=zqe(),w6e=S6e();Ma=R6e.exports=j3t;function j3t(){var t=function(e,r,n){t.handle(e,r,n)};return T6e(t,U3t.prototype,!1),T6e(t,b6e,!1),t.request=Object.create(A6e,{app:{configurable:!0,enumerable:!0,writable:!0,value:t}}),t.response=Object.create(w6e,{app:{configurable:!0,enumerable:!0,writable:!0,value:t}}),t.init(),t}Ma.application=b6e;Ma.request=A6e;Ma.response=w6e;Ma.Route=x6e.Route;Ma.Router=x6e;Ma.json=sL.json;Ma.raw=sL.raw;Ma.static=E6e();Ma.text=sL.text;Ma.urlencoded=sL.urlencoded});var Pc=S((ADr,I6e)=>{"use strict";I6e.exports=P6e()});var RBe=S(($Ur,wBe)=>{"use strict";var ABe=Object.getOwnPropertySymbols,z9t=Object.prototype.hasOwnProperty,F9t=Object.prototype.propertyIsEnumerable;function q9t(t){if(t==null)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(t)}function B9t(){try{if(!Object.assign)return!1;var t=new String("abc");if(t[5]="de",Object.getOwnPropertyNames(t)[0]==="5")return!1;for(var e={},r=0;r<10;r++)e["_"+String.fromCharCode(r)]=r;var n=Object.getOwnPropertyNames(e).map(function(i){return e[i]});if(n.join("")!=="0123456789")return!1;var o={};return"abcdefghijklmnopqrst".split("").forEach(function(i){o[i]=i}),Object.keys(Object.assign({},o)).join("")==="abcdefghijklmnopqrst"}catch{return!1}}wBe.exports=B9t()?Object.assign:function(t,e){for(var r,n=q9t(t),o,i=1;i{(function(){"use strict";var t=RBe(),e=BZ(),r={origin:"*",methods:"GET,HEAD,PUT,PATCH,POST,DELETE",preflightContinue:!1,optionsSuccessStatus:204};function n(_){return typeof _=="string"||_ instanceof String}function o(_,v){if(Array.isArray(v)){for(var E=0;E{"use strict";GBe.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}});var wY=S((Ljr,WBe)=>{var mA=VBe(),HBe={};for(let t of Object.keys(mA))HBe[mA[t]]=t;var ke={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};WBe.exports=ke;for(let t of Object.keys(ke)){if(!("channels"in ke[t]))throw new Error("missing channels property: "+t);if(!("labels"in ke[t]))throw new Error("missing channel labels property: "+t);if(ke[t].labels.length!==ke[t].channels)throw new Error("channel and label counts mismatch: "+t);let{channels:e,labels:r}=ke[t];delete ke[t].channels,delete ke[t].labels,Object.defineProperty(ke[t],"channels",{value:e}),Object.defineProperty(ke[t],"labels",{value:r})}ke.rgb.hsl=function(t){let e=t[0]/255,r=t[1]/255,n=t[2]/255,o=Math.min(e,r,n),i=Math.max(e,r,n),s=i-o,a,c;i===o?a=0:e===i?a=(r-n)/s:r===i?a=2+(n-e)/s:n===i&&(a=4+(e-r)/s),a=Math.min(a*60,360),a<0&&(a+=360);let u=(o+i)/2;return i===o?c=0:u<=.5?c=s/(i+o):c=s/(2-i-o),[a,c*100,u*100]};ke.rgb.hsv=function(t){let e,r,n,o,i,s=t[0]/255,a=t[1]/255,c=t[2]/255,u=Math.max(s,a,c),p=u-Math.min(s,a,c),f=function(m){return(u-m)/6/p+1/2};return p===0?(o=0,i=0):(i=p/u,e=f(s),r=f(a),n=f(c),s===u?o=n-r:a===u?o=1/3+e-n:c===u&&(o=2/3+r-e),o<0?o+=1:o>1&&(o-=1)),[o*360,i*100,u*100]};ke.rgb.hwb=function(t){let e=t[0],r=t[1],n=t[2],o=ke.rgb.hsl(t)[0],i=1/255*Math.min(e,Math.min(r,n));return n=1-1/255*Math.max(e,Math.max(r,n)),[o,i*100,n*100]};ke.rgb.cmyk=function(t){let e=t[0]/255,r=t[1]/255,n=t[2]/255,o=Math.min(1-e,1-r,1-n),i=(1-e-o)/(1-o)||0,s=(1-r-o)/(1-o)||0,a=(1-n-o)/(1-o)||0;return[i*100,s*100,a*100,o*100]};function Q9t(t,e){return(t[0]-e[0])**2+(t[1]-e[1])**2+(t[2]-e[2])**2}ke.rgb.keyword=function(t){let e=HBe[t];if(e)return e;let r=1/0,n;for(let o of Object.keys(mA)){let i=mA[o],s=Q9t(t,i);s.04045?((e+.055)/1.055)**2.4:e/12.92,r=r>.04045?((r+.055)/1.055)**2.4:r/12.92,n=n>.04045?((n+.055)/1.055)**2.4:n/12.92;let o=e*.4124+r*.3576+n*.1805,i=e*.2126+r*.7152+n*.0722,s=e*.0193+r*.1192+n*.9505;return[o*100,i*100,s*100]};ke.rgb.lab=function(t){let e=ke.rgb.xyz(t),r=e[0],n=e[1],o=e[2];r/=95.047,n/=100,o/=108.883,r=r>.008856?r**(1/3):7.787*r+16/116,n=n>.008856?n**(1/3):7.787*n+16/116,o=o>.008856?o**(1/3):7.787*o+16/116;let i=116*n-16,s=500*(r-n),a=200*(n-o);return[i,s,a]};ke.hsl.rgb=function(t){let e=t[0]/360,r=t[1]/100,n=t[2]/100,o,i,s;if(r===0)return s=n*255,[s,s,s];n<.5?o=n*(1+r):o=n+r-n*r;let a=2*n-o,c=[0,0,0];for(let u=0;u<3;u++)i=e+1/3*-(u-1),i<0&&i++,i>1&&i--,6*i<1?s=a+(o-a)*6*i:2*i<1?s=o:3*i<2?s=a+(o-a)*(2/3-i)*6:s=a,c[u]=s*255;return c};ke.hsl.hsv=function(t){let e=t[0],r=t[1]/100,n=t[2]/100,o=r,i=Math.max(n,.01);n*=2,r*=n<=1?n:2-n,o*=i<=1?i:2-i;let s=(n+r)/2,a=n===0?2*o/(i+o):2*r/(n+r);return[e,a*100,s*100]};ke.hsv.rgb=function(t){let e=t[0]/60,r=t[1]/100,n=t[2]/100,o=Math.floor(e)%6,i=e-Math.floor(e),s=255*n*(1-r),a=255*n*(1-r*i),c=255*n*(1-r*(1-i));switch(n*=255,o){case 0:return[n,c,s];case 1:return[a,n,s];case 2:return[s,n,c];case 3:return[s,a,n];case 4:return[c,s,n];case 5:return[n,s,a]}};ke.hsv.hsl=function(t){let e=t[0],r=t[1]/100,n=t[2]/100,o=Math.max(n,.01),i,s;s=(2-r)*n;let a=(2-r)*o;return i=r*o,i/=a<=1?a:2-a,i=i||0,s/=2,[e,i*100,s*100]};ke.hwb.rgb=function(t){let e=t[0]/360,r=t[1]/100,n=t[2]/100,o=r+n,i;o>1&&(r/=o,n/=o);let s=Math.floor(6*e),a=1-n;i=6*e-s,(s&1)!==0&&(i=1-i);let c=r+i*(a-r),u,p,f;switch(s){default:case 6:case 0:u=a,p=c,f=r;break;case 1:u=c,p=a,f=r;break;case 2:u=r,p=a,f=c;break;case 3:u=r,p=c,f=a;break;case 4:u=c,p=r,f=a;break;case 5:u=a,p=r,f=c;break}return[u*255,p*255,f*255]};ke.cmyk.rgb=function(t){let e=t[0]/100,r=t[1]/100,n=t[2]/100,o=t[3]/100,i=1-Math.min(1,e*(1-o)+o),s=1-Math.min(1,r*(1-o)+o),a=1-Math.min(1,n*(1-o)+o);return[i*255,s*255,a*255]};ke.xyz.rgb=function(t){let e=t[0]/100,r=t[1]/100,n=t[2]/100,o,i,s;return o=e*3.2406+r*-1.5372+n*-.4986,i=e*-.9689+r*1.8758+n*.0415,s=e*.0557+r*-.204+n*1.057,o=o>.0031308?1.055*o**(1/2.4)-.055:o*12.92,i=i>.0031308?1.055*i**(1/2.4)-.055:i*12.92,s=s>.0031308?1.055*s**(1/2.4)-.055:s*12.92,o=Math.min(Math.max(0,o),1),i=Math.min(Math.max(0,i),1),s=Math.min(Math.max(0,s),1),[o*255,i*255,s*255]};ke.xyz.lab=function(t){let e=t[0],r=t[1],n=t[2];e/=95.047,r/=100,n/=108.883,e=e>.008856?e**(1/3):7.787*e+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,n=n>.008856?n**(1/3):7.787*n+16/116;let o=116*r-16,i=500*(e-r),s=200*(r-n);return[o,i,s]};ke.lab.xyz=function(t){let e=t[0],r=t[1],n=t[2],o,i,s;i=(e+16)/116,o=r/500+i,s=i-n/200;let a=i**3,c=o**3,u=s**3;return i=a>.008856?a:(i-16/116)/7.787,o=c>.008856?c:(o-16/116)/7.787,s=u>.008856?u:(s-16/116)/7.787,o*=95.047,i*=100,s*=108.883,[o,i,s]};ke.lab.lch=function(t){let e=t[0],r=t[1],n=t[2],o;o=Math.atan2(n,r)*360/2/Math.PI,o<0&&(o+=360);let s=Math.sqrt(r*r+n*n);return[e,s,o]};ke.lch.lab=function(t){let e=t[0],r=t[1],o=t[2]/360*2*Math.PI,i=r*Math.cos(o),s=r*Math.sin(o);return[e,i,s]};ke.rgb.ansi16=function(t,e=null){let[r,n,o]=t,i=e===null?ke.rgb.hsv(t)[2]:e;if(i=Math.round(i/50),i===0)return 30;let s=30+(Math.round(o/255)<<2|Math.round(n/255)<<1|Math.round(r/255));return i===2&&(s+=60),s};ke.hsv.ansi16=function(t){return ke.rgb.ansi16(ke.hsv.rgb(t),t[2])};ke.rgb.ansi256=function(t){let e=t[0],r=t[1],n=t[2];return e===r&&r===n?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(r/255*5)+Math.round(n/255*5)};ke.ansi16.rgb=function(t){let e=t%10;if(e===0||e===7)return t>50&&(e+=3.5),e=e/10.5*255,[e,e,e];let r=(~~(t>50)+1)*.5,n=(e&1)*r*255,o=(e>>1&1)*r*255,i=(e>>2&1)*r*255;return[n,o,i]};ke.ansi256.rgb=function(t){if(t>=232){let i=(t-232)*10+8;return[i,i,i]}t-=16;let e,r=Math.floor(t/36)/5*255,n=Math.floor((e=t%36)/6)/5*255,o=e%6/5*255;return[r,n,o]};ke.rgb.hex=function(t){let r=(((Math.round(t[0])&255)<<16)+((Math.round(t[1])&255)<<8)+(Math.round(t[2])&255)).toString(16).toUpperCase();return"000000".substring(r.length)+r};ke.hex.rgb=function(t){let e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];let r=e[0];e[0].length===3&&(r=r.split("").map(a=>a+a).join(""));let n=parseInt(r,16),o=n>>16&255,i=n>>8&255,s=n&255;return[o,i,s]};ke.rgb.hcg=function(t){let e=t[0]/255,r=t[1]/255,n=t[2]/255,o=Math.max(Math.max(e,r),n),i=Math.min(Math.min(e,r),n),s=o-i,a,c;return s<1?a=i/(1-s):a=0,s<=0?c=0:o===e?c=(r-n)/s%6:o===r?c=2+(n-e)/s:c=4+(e-r)/s,c/=6,c%=1,[c*360,s*100,a*100]};ke.hsl.hcg=function(t){let e=t[1]/100,r=t[2]/100,n=r<.5?2*e*r:2*e*(1-r),o=0;return n<1&&(o=(r-.5*n)/(1-n)),[t[0],n*100,o*100]};ke.hsv.hcg=function(t){let e=t[1]/100,r=t[2]/100,n=e*r,o=0;return n<1&&(o=(r-n)/(1-n)),[t[0],n*100,o*100]};ke.hcg.rgb=function(t){let e=t[0]/360,r=t[1]/100,n=t[2]/100;if(r===0)return[n*255,n*255,n*255];let o=[0,0,0],i=e%1*6,s=i%1,a=1-s,c=0;switch(Math.floor(i)){case 0:o[0]=1,o[1]=s,o[2]=0;break;case 1:o[0]=a,o[1]=1,o[2]=0;break;case 2:o[0]=0,o[1]=1,o[2]=s;break;case 3:o[0]=0,o[1]=a,o[2]=1;break;case 4:o[0]=s,o[1]=0,o[2]=1;break;default:o[0]=1,o[1]=0,o[2]=a}return c=(1-r)*n,[(r*o[0]+c)*255,(r*o[1]+c)*255,(r*o[2]+c)*255]};ke.hcg.hsv=function(t){let e=t[1]/100,r=t[2]/100,n=e+r*(1-e),o=0;return n>0&&(o=e/n),[t[0],o*100,n*100]};ke.hcg.hsl=function(t){let e=t[1]/100,n=t[2]/100*(1-e)+.5*e,o=0;return n>0&&n<.5?o=e/(2*n):n>=.5&&n<1&&(o=e/(2*(1-n))),[t[0],o*100,n*100]};ke.hcg.hwb=function(t){let e=t[1]/100,r=t[2]/100,n=e+r*(1-e);return[t[0],(n-e)*100,(1-n)*100]};ke.hwb.hcg=function(t){let e=t[1]/100,n=1-t[2]/100,o=n-e,i=0;return o<1&&(i=(n-o)/(1-o)),[t[0],o*100,i*100]};ke.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]};ke.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]};ke.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]};ke.gray.hsl=function(t){return[0,0,t[0]]};ke.gray.hsv=ke.gray.hsl;ke.gray.hwb=function(t){return[0,100,t[0]]};ke.gray.cmyk=function(t){return[0,0,0,t[0]]};ke.gray.lab=function(t){return[t[0],0,0]};ke.gray.hex=function(t){let e=Math.round(t[0]/100*255)&255,n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n};ke.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}});var ZBe=S((Ujr,KBe)=>{var EL=wY();function eWt(){let t={},e=Object.keys(EL);for(let r=e.length,n=0;n{var RY=wY(),oWt=ZBe(),$S={},iWt=Object.keys(RY);function sWt(t){let e=function(...r){let n=r[0];return n==null?n:(n.length>1&&(r=n),t(r))};return"conversion"in t&&(e.conversion=t.conversion),e}function aWt(t){let e=function(...r){let n=r[0];if(n==null)return n;n.length>1&&(r=n);let o=t(r);if(typeof o=="object")for(let i=o.length,s=0;s{$S[t]={},Object.defineProperty($S[t],"channels",{value:RY[t].channels}),Object.defineProperty($S[t],"labels",{value:RY[t].labels});let e=oWt(t);Object.keys(e).forEach(n=>{let o=e[n];$S[t][n]=aWt(o),$S[t][n].raw=sWt(o)})});YBe.exports=$S});var nGe=S((zjr,rGe)=>{"use strict";var XBe=(t,e)=>(...r)=>`\x1B[${t(...r)+e}m`,QBe=(t,e)=>(...r)=>{let n=t(...r);return`\x1B[${38+e};5;${n}m`},eGe=(t,e)=>(...r)=>{let n=t(...r);return`\x1B[${38+e};2;${n[0]};${n[1]};${n[2]}m`},TL=t=>t,tGe=(t,e,r)=>[t,e,r],kS=(t,e,r)=>{Object.defineProperty(t,e,{get:()=>{let n=r();return Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0}),n},enumerable:!0,configurable:!0})},PY,MS=(t,e,r,n)=>{PY===void 0&&(PY=JBe());let o=n?10:0,i={};for(let[s,a]of Object.entries(PY)){let c=s==="ansi16"?"ansi":s;s===e?i[c]=t(r,o):typeof a=="object"&&(i[c]=t(a[e],o))}return i};function cWt(){let t=new Map,e={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};e.color.gray=e.color.blackBright,e.bgColor.bgGray=e.bgColor.bgBlackBright,e.color.grey=e.color.blackBright,e.bgColor.bgGrey=e.bgColor.bgBlackBright;for(let[r,n]of Object.entries(e)){for(let[o,i]of Object.entries(n))e[o]={open:`\x1B[${i[0]}m`,close:`\x1B[${i[1]}m`},n[o]=e[o],t.set(i[0],i[1]);Object.defineProperty(e,r,{value:n,enumerable:!1})}return Object.defineProperty(e,"codes",{value:t,enumerable:!1}),e.color.close="\x1B[39m",e.bgColor.close="\x1B[49m",kS(e.color,"ansi",()=>MS(XBe,"ansi16",TL,!1)),kS(e.color,"ansi256",()=>MS(QBe,"ansi256",TL,!1)),kS(e.color,"ansi16m",()=>MS(eGe,"rgb",tGe,!1)),kS(e.bgColor,"ansi",()=>MS(XBe,"ansi16",TL,!0)),kS(e.bgColor,"ansi256",()=>MS(QBe,"ansi256",TL,!0)),kS(e.bgColor,"ansi16m",()=>MS(eGe,"rgb",tGe,!0)),e}Object.defineProperty(rGe,"exports",{enumerable:!0,get:cWt})});var iGe=S((Fjr,oGe)=>{"use strict";var uWt=(t,e,r)=>{let n=t.indexOf(e);if(n===-1)return t;let o=e.length,i=0,s="";do s+=t.substr(i,n-i)+e+r,i=n+o,n=t.indexOf(e,i);while(n!==-1);return s+=t.substr(i),s},lWt=(t,e,r,n)=>{let o=0,i="";do{let s=t[n-1]==="\r";i+=t.substr(o,(s?n-1:n)-o)+e+(s?`\r -`:` -`)+r,o=n+1,n=t.indexOf(` -`,o)}while(n!==-1);return i+=t.substr(o),i};oGe.exports={stringReplaceAll:uWt,stringEncaseCRLFWithFirstIndex:lWt}});var lGe=S((qjr,uGe)=>{"use strict";var pWt=/(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,sGe=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,dWt=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,fWt=/\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi,mWt=new Map([["n",` -`],["r","\r"],["t"," "],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e","\x1B"],["a","\x07"]]);function cGe(t){let e=t[0]==="u",r=t[1]==="{";return e&&!r&&t.length===5||t[0]==="x"&&t.length===3?String.fromCharCode(parseInt(t.slice(1),16)):e&&r?String.fromCodePoint(parseInt(t.slice(2,-1),16)):mWt.get(t)||t}function hWt(t,e){let r=[],n=e.trim().split(/\s*,\s*/g),o;for(let i of n){let s=Number(i);if(!Number.isNaN(s))r.push(s);else if(o=i.match(dWt))r.push(o[2].replace(fWt,(a,c,u)=>c?cGe(c):u));else throw new Error(`Invalid Chalk template style argument: ${i} (in style '${t}')`)}return r}function gWt(t){sGe.lastIndex=0;let e=[],r;for(;(r=sGe.exec(t))!==null;){let n=r[1];if(r[2]){let o=hWt(n,r[2]);e.push([n].concat(o))}else e.push([n])}return e}function aGe(t,e){let r={};for(let o of e)for(let i of o.styles)r[i[0]]=o.inverse?null:i.slice(1);let n=t;for(let[o,i]of Object.entries(r))if(Array.isArray(i)){if(!(o in n))throw new Error(`Unknown Chalk style: ${o}`);n=i.length>0?n[o](...i):n[o]}return n}uGe.exports=(t,e)=>{let r=[],n=[],o=[];if(e.replace(pWt,(i,s,a,c,u,p)=>{if(s)o.push(cGe(s));else if(c){let f=o.join("");o=[],n.push(r.length===0?f:aGe(t,r)(f)),r.push({inverse:a,styles:gWt(c)})}else if(u){if(r.length===0)throw new Error("Found extraneous } in Chalk template literal");n.push(aGe(t,r)(o.join(""))),o=[],r.pop()}else o.push(p)}),n.push(o.join("")),r.length>0){let i=`Chalk template literal is missing ${r.length} closing bracket${r.length===1?"":"s"} (\`}\`)`;throw new Error(i)}return n.join("")}});var _Ge=S((Bjr,gGe)=>{"use strict";var hA=nGe(),{stdout:OY,stderr:NY}=l8(),{stringReplaceAll:_Wt,stringEncaseCRLFWithFirstIndex:vWt}=iGe(),{isArray:bL}=Array,dGe=["ansi","ansi","ansi256","ansi16m"],DS=Object.create(null),SWt=(t,e={})=>{if(e.level&&!(Number.isInteger(e.level)&&e.level>=0&&e.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let r=OY?OY.level:0;t.level=e.level===void 0?r:e.level},CY=class{constructor(e){return fGe(e)}},fGe=t=>{let e={};return SWt(e,t),e.template=(...r)=>hGe(e.template,...r),Object.setPrototypeOf(e,xL.prototype),Object.setPrototypeOf(e.template,e),e.template.constructor=()=>{throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.")},e.template.Instance=CY,e.template};function xL(t){return fGe(t)}for(let[t,e]of Object.entries(hA))DS[t]={get(){let r=AL(this,$Y(e.open,e.close,this._styler),this._isEmpty);return Object.defineProperty(this,t,{value:r}),r}};DS.visible={get(){let t=AL(this,this._styler,!0);return Object.defineProperty(this,"visible",{value:t}),t}};var mGe=["rgb","hex","keyword","hsl","hsv","hwb","ansi","ansi256"];for(let t of mGe)DS[t]={get(){let{level:e}=this;return function(...r){let n=$Y(hA.color[dGe[e]][t](...r),hA.color.close,this._styler);return AL(this,n,this._isEmpty)}}};for(let t of mGe){let e="bg"+t[0].toUpperCase()+t.slice(1);DS[e]={get(){let{level:r}=this;return function(...n){let o=$Y(hA.bgColor[dGe[r]][t](...n),hA.bgColor.close,this._styler);return AL(this,o,this._isEmpty)}}}}var yWt=Object.defineProperties(()=>{},{...DS,level:{enumerable:!0,get(){return this._generator.level},set(t){this._generator.level=t}}}),$Y=(t,e,r)=>{let n,o;return r===void 0?(n=t,o=e):(n=r.openAll+t,o=e+r.closeAll),{open:t,close:e,openAll:n,closeAll:o,parent:r}},AL=(t,e,r)=>{let n=(...o)=>bL(o[0])&&bL(o[0].raw)?pGe(n,hGe(n,...o)):pGe(n,o.length===1?""+o[0]:o.join(" "));return Object.setPrototypeOf(n,yWt),n._generator=t,n._styler=e,n._isEmpty=r,n},pGe=(t,e)=>{if(t.level<=0||!e)return t._isEmpty?"":e;let r=t._styler;if(r===void 0)return e;let{openAll:n,closeAll:o}=r;if(e.indexOf("\x1B")!==-1)for(;r!==void 0;)e=_Wt(e,r.close,r.open),r=r.parent;let i=e.indexOf(` -`);return i!==-1&&(e=vWt(e,o,n,i)),n+e+o},IY,hGe=(t,...e)=>{let[r]=e;if(!bL(r)||!bL(r.raw))return e.join(" ");let n=e.slice(1),o=[r.raw[0]];for(let i=1;iPFt,default:()=>BUe});Te();lE();lE();var Q4="2025-11-25",Zte="2025-03-26",dE=[Q4,"2025-06-18","2025-03-26","2024-11-05","2024-10-07"],_p="io.modelcontextprotocol/related-task",pI="2.0",bo=uI(t=>t!==null&&(typeof t=="object"||typeof t=="function")),Yte=br([T(),ce().int()]),Jte=T(),_Yt=wn({ttl:br([ce(),Bh()]).optional(),pollInterval:ce().optional()}),UYe=k({ttl:ce().optional()}),jYe=k({taskId:T()}),e2=wn({progressToken:Yte.optional(),[_p]:jYe.optional()}),ms=k({_meta:e2.optional()}),fE=ms.extend({task:UYe.optional()}),Xte=t=>fE.safeParse(t).success,xo=k({method:T(),params:ms.loose().optional()}),Ws=k({_meta:e2.optional()}),Ks=k({method:T(),params:Ws.loose().optional()}),Ao=wn({_meta:e2.optional()}),dI=br([T(),ce().int()]),Qte=k({jsonrpc:Ie(pI),id:dI,...xo.shape}).strict(),vp=t=>Qte.safeParse(t).success,ere=k({jsonrpc:Ie(pI),...Ks.shape}).strict(),tre=t=>ere.safeParse(t).success,t2=k({jsonrpc:Ie(pI),id:dI,result:Ao}).strict(),Ju=t=>t2.safeParse(t).success;var We;(function(t){t[t.ConnectionClosed=-32e3]="ConnectionClosed",t[t.RequestTimeout=-32001]="RequestTimeout",t[t.ParseError=-32700]="ParseError",t[t.InvalidRequest=-32600]="InvalidRequest",t[t.MethodNotFound=-32601]="MethodNotFound",t[t.InvalidParams=-32602]="InvalidParams",t[t.InternalError=-32603]="InternalError",t[t.UrlElicitationRequired=-32042]="UrlElicitationRequired"})(We||(We={}));var r2=k({jsonrpc:Ie(pI),id:dI.optional(),error:k({code:ce().int(),message:T(),data:Pr().optional()})}).strict();var Wh=t=>r2.safeParse(t).success;var fI=br([Qte,ere,t2,r2]),vYt=br([t2,r2]),mI=Ao.strict(),zYe=Ws.extend({requestId:dI.optional(),reason:T().optional()}),hI=Ks.extend({method:Ie("notifications/cancelled"),params:zYe}),FYe=k({src:T(),mimeType:T().optional(),sizes:H(T()).optional(),theme:Ze(["light","dark"]).optional()}),mE=k({icons:H(FYe).optional()}),Hh=k({name:T(),title:T().optional()}),rre=Hh.extend({...Hh.shape,...mE.shape,version:T(),websiteUrl:T().optional(),description:T().optional()}),qYe=Gh(k({applyDefaults:ue().optional()}),ir(T(),Pr())),BYe=Bt(t=>t&&typeof t=="object"&&!Array.isArray(t)&&Object.keys(t).length===0?{form:{}}:t,Gh(k({form:qYe.optional(),url:bo.optional()}),ir(T(),Pr()).optional())),GYe=wn({list:bo.optional(),cancel:bo.optional(),requests:wn({sampling:wn({createMessage:bo.optional()}).optional(),elicitation:wn({create:bo.optional()}).optional()}).optional()}),VYe=wn({list:bo.optional(),cancel:bo.optional(),requests:wn({tools:wn({call:bo.optional()}).optional()}).optional()}),HYe=k({experimental:ir(T(),bo).optional(),sampling:k({context:bo.optional(),tools:bo.optional()}).optional(),elicitation:BYe.optional(),roots:k({listChanged:ue().optional()}).optional(),tasks:GYe.optional()}),WYe=ms.extend({protocolVersion:T(),capabilities:HYe,clientInfo:rre}),gI=xo.extend({method:Ie("initialize"),params:WYe}),hE=t=>gI.safeParse(t).success,KYe=k({experimental:ir(T(),bo).optional(),logging:bo.optional(),completions:bo.optional(),prompts:k({listChanged:ue().optional()}).optional(),resources:k({subscribe:ue().optional(),listChanged:ue().optional()}).optional(),tools:k({listChanged:ue().optional()}).optional(),tasks:VYe.optional()}),ZYe=Ao.extend({protocolVersion:T(),capabilities:KYe,serverInfo:rre,instructions:T().optional()}),n2=Ks.extend({method:Ie("notifications/initialized"),params:Ws.optional()});var _I=xo.extend({method:Ie("ping"),params:ms.optional()}),YYe=k({progress:ce(),total:qr(ce()),message:qr(T())}),JYe=k({...Ws.shape,...YYe.shape,progressToken:Yte}),vI=Ks.extend({method:Ie("notifications/progress"),params:JYe}),XYe=ms.extend({cursor:Jte.optional()}),gE=xo.extend({params:XYe.optional()}),_E=Ao.extend({nextCursor:Jte.optional()}),QYe=Ze(["working","input_required","completed","failed","cancelled"]),vE=k({taskId:T(),status:QYe,ttl:br([ce(),Bh()]),createdAt:T(),lastUpdatedAt:T(),pollInterval:qr(ce()),statusMessage:qr(T())}),Kh=Ao.extend({task:vE}),e7e=Ws.merge(vE),SE=Ks.extend({method:Ie("notifications/tasks/status"),params:e7e}),SI=xo.extend({method:Ie("tasks/get"),params:ms.extend({taskId:T()})}),yI=Ao.merge(vE),EI=xo.extend({method:Ie("tasks/result"),params:ms.extend({taskId:T()})}),SYt=Ao.loose(),TI=gE.extend({method:Ie("tasks/list")}),bI=_E.extend({tasks:H(vE)}),xI=xo.extend({method:Ie("tasks/cancel"),params:ms.extend({taskId:T()})}),nre=Ao.merge(vE),ore=k({uri:T(),mimeType:qr(T()),_meta:ir(T(),Pr()).optional()}),ire=ore.extend({text:T()}),o2=T().refine(t=>{try{return atob(t),!0}catch{return!1}},{message:"Invalid Base64 string"}),sre=ore.extend({blob:o2}),yE=Ze(["user","assistant"]),Zh=k({audience:H(yE).optional(),priority:ce().min(0).max(1).optional(),lastModified:hp.datetime({offset:!0}).optional()}),are=k({...Hh.shape,...mE.shape,uri:T(),description:qr(T()),mimeType:qr(T()),annotations:Zh.optional(),_meta:qr(wn({}))}),t7e=k({...Hh.shape,...mE.shape,uriTemplate:T(),description:qr(T()),mimeType:qr(T()),annotations:Zh.optional(),_meta:qr(wn({}))}),AI=gE.extend({method:Ie("resources/list")}),r7e=_E.extend({resources:H(are)}),wI=gE.extend({method:Ie("resources/templates/list")}),n7e=_E.extend({resourceTemplates:H(t7e)}),i2=ms.extend({uri:T()}),o7e=i2,RI=xo.extend({method:Ie("resources/read"),params:o7e}),i7e=Ao.extend({contents:H(br([ire,sre]))}),s7e=Ks.extend({method:Ie("notifications/resources/list_changed"),params:Ws.optional()}),a7e=i2,c7e=xo.extend({method:Ie("resources/subscribe"),params:a7e}),u7e=i2,l7e=xo.extend({method:Ie("resources/unsubscribe"),params:u7e}),p7e=Ws.extend({uri:T()}),d7e=Ks.extend({method:Ie("notifications/resources/updated"),params:p7e}),f7e=k({name:T(),description:qr(T()),required:qr(ue())}),m7e=k({...Hh.shape,...mE.shape,description:qr(T()),arguments:qr(H(f7e)),_meta:qr(wn({}))}),PI=gE.extend({method:Ie("prompts/list")}),h7e=_E.extend({prompts:H(m7e)}),g7e=ms.extend({name:T(),arguments:ir(T(),T()).optional()}),II=xo.extend({method:Ie("prompts/get"),params:g7e}),s2=k({type:Ie("text"),text:T(),annotations:Zh.optional(),_meta:ir(T(),Pr()).optional()}),a2=k({type:Ie("image"),data:o2,mimeType:T(),annotations:Zh.optional(),_meta:ir(T(),Pr()).optional()}),c2=k({type:Ie("audio"),data:o2,mimeType:T(),annotations:Zh.optional(),_meta:ir(T(),Pr()).optional()}),_7e=k({type:Ie("tool_use"),name:T(),id:T(),input:ir(T(),Pr()),_meta:ir(T(),Pr()).optional()}),v7e=k({type:Ie("resource"),resource:br([ire,sre]),annotations:Zh.optional(),_meta:ir(T(),Pr()).optional()}),S7e=are.extend({type:Ie("resource_link")}),u2=br([s2,a2,c2,S7e,v7e]),y7e=k({role:yE,content:u2}),E7e=Ao.extend({description:T().optional(),messages:H(y7e)}),T7e=Ks.extend({method:Ie("notifications/prompts/list_changed"),params:Ws.optional()}),b7e=k({title:T().optional(),readOnlyHint:ue().optional(),destructiveHint:ue().optional(),idempotentHint:ue().optional(),openWorldHint:ue().optional()}),x7e=k({taskSupport:Ze(["required","optional","forbidden"]).optional()}),cre=k({...Hh.shape,...mE.shape,description:T().optional(),inputSchema:k({type:Ie("object"),properties:ir(T(),bo).optional(),required:H(T()).optional()}).catchall(Pr()),outputSchema:k({type:Ie("object"),properties:ir(T(),bo).optional(),required:H(T()).optional()}).catchall(Pr()).optional(),annotations:b7e.optional(),execution:x7e.optional(),_meta:ir(T(),Pr()).optional()}),OI=gE.extend({method:Ie("tools/list")}),A7e=_E.extend({tools:H(cre)}),NI=Ao.extend({content:H(u2).default([]),structuredContent:ir(T(),Pr()).optional(),isError:ue().optional()}),yYt=NI.or(Ao.extend({toolResult:Pr()})),w7e=fE.extend({name:T(),arguments:ir(T(),Pr()).optional()}),Yh=xo.extend({method:Ie("tools/call"),params:w7e}),R7e=Ks.extend({method:Ie("notifications/tools/list_changed"),params:Ws.optional()}),EYt=k({autoRefresh:ue().default(!0),debounceMs:ce().int().nonnegative().default(300)}),EE=Ze(["debug","info","notice","warning","error","critical","alert","emergency"]),P7e=ms.extend({level:EE}),TE=xo.extend({method:Ie("logging/setLevel"),params:P7e}),I7e=Ws.extend({level:EE,logger:T().optional(),data:Pr()}),O7e=Ks.extend({method:Ie("notifications/message"),params:I7e}),N7e=k({name:T().optional()}),C7e=k({hints:H(N7e).optional(),costPriority:ce().min(0).max(1).optional(),speedPriority:ce().min(0).max(1).optional(),intelligencePriority:ce().min(0).max(1).optional()}),$7e=k({mode:Ze(["auto","required","none"]).optional()}),k7e=k({type:Ie("tool_result"),toolUseId:T().describe("The unique identifier for the corresponding tool call."),content:H(u2).default([]),structuredContent:k({}).loose().optional(),isError:ue().optional(),_meta:ir(T(),Pr()).optional()}),M7e=sE("type",[s2,a2,c2]),lI=sE("type",[s2,a2,c2,_7e,k7e]),D7e=k({role:yE,content:br([lI,H(lI)]),_meta:ir(T(),Pr()).optional()}),L7e=fE.extend({messages:H(D7e),modelPreferences:C7e.optional(),systemPrompt:T().optional(),includeContext:Ze(["none","thisServer","allServers"]).optional(),temperature:ce().optional(),maxTokens:ce().int(),stopSequences:H(T()).optional(),metadata:bo.optional(),tools:H(cre).optional(),toolChoice:$7e.optional()}),U7e=xo.extend({method:Ie("sampling/createMessage"),params:L7e}),l2=Ao.extend({model:T(),stopReason:qr(Ze(["endTurn","stopSequence","maxTokens"]).or(T())),role:yE,content:M7e}),p2=Ao.extend({model:T(),stopReason:qr(Ze(["endTurn","stopSequence","maxTokens","toolUse"]).or(T())),role:yE,content:br([lI,H(lI)])}),j7e=k({type:Ie("boolean"),title:T().optional(),description:T().optional(),default:ue().optional()}),z7e=k({type:Ie("string"),title:T().optional(),description:T().optional(),minLength:ce().optional(),maxLength:ce().optional(),format:Ze(["email","uri","date","date-time"]).optional(),default:T().optional()}),F7e=k({type:Ze(["number","integer"]),title:T().optional(),description:T().optional(),minimum:ce().optional(),maximum:ce().optional(),default:ce().optional()}),q7e=k({type:Ie("string"),title:T().optional(),description:T().optional(),enum:H(T()),default:T().optional()}),B7e=k({type:Ie("string"),title:T().optional(),description:T().optional(),oneOf:H(k({const:T(),title:T()})),default:T().optional()}),G7e=k({type:Ie("string"),title:T().optional(),description:T().optional(),enum:H(T()),enumNames:H(T()).optional(),default:T().optional()}),V7e=br([q7e,B7e]),H7e=k({type:Ie("array"),title:T().optional(),description:T().optional(),minItems:ce().optional(),maxItems:ce().optional(),items:k({type:Ie("string"),enum:H(T())}),default:H(T()).optional()}),W7e=k({type:Ie("array"),title:T().optional(),description:T().optional(),minItems:ce().optional(),maxItems:ce().optional(),items:k({anyOf:H(k({const:T(),title:T()}))}),default:H(T()).optional()}),K7e=br([H7e,W7e]),Z7e=br([G7e,V7e,K7e]),Y7e=br([Z7e,j7e,z7e,F7e]),J7e=fE.extend({mode:Ie("form").optional(),message:T(),requestedSchema:k({type:Ie("object"),properties:ir(T(),Y7e),required:H(T()).optional()})}),X7e=fE.extend({mode:Ie("url"),message:T(),elicitationId:T(),url:T().url()}),Q7e=br([J7e,X7e]),eJe=xo.extend({method:Ie("elicitation/create"),params:Q7e}),tJe=Ws.extend({elicitationId:T()}),rJe=Ks.extend({method:Ie("notifications/elicitation/complete"),params:tJe}),CI=Ao.extend({action:Ze(["accept","decline","cancel"]),content:Bt(t=>t===null?void 0:t,ir(T(),br([T(),ce(),ue(),H(T())])).optional())}),nJe=k({type:Ie("ref/resource"),uri:T()});var oJe=k({type:Ie("ref/prompt"),name:T()}),iJe=ms.extend({ref:br([oJe,nJe]),argument:k({name:T(),value:T()}),context:k({arguments:ir(T(),T()).optional()}).optional()}),$I=xo.extend({method:Ie("completion/complete"),params:iJe});function ure(t){if(t.params.ref.type!=="ref/prompt")throw new TypeError(`Expected CompleteRequestPrompt, but got ${t.params.ref.type}`)}function lre(t){if(t.params.ref.type!=="ref/resource")throw new TypeError(`Expected CompleteRequestResourceTemplate, but got ${t.params.ref.type}`)}var sJe=Ao.extend({completion:wn({values:H(T()).max(100),total:qr(ce().int()),hasMore:qr(ue())})}),aJe=k({uri:T().startsWith("file://"),name:T().optional(),_meta:ir(T(),Pr()).optional()}),cJe=xo.extend({method:Ie("roots/list"),params:ms.optional()}),d2=Ao.extend({roots:H(aJe)}),uJe=Ks.extend({method:Ie("notifications/roots/list_changed"),params:Ws.optional()}),TYt=br([_I,gI,$I,TE,II,PI,AI,wI,RI,c7e,l7e,Yh,OI,SI,EI,TI,xI]),bYt=br([hI,vI,n2,uJe,SE]),xYt=br([mI,l2,p2,CI,d2,yI,bI,Kh]),AYt=br([_I,U7e,eJe,cJe,SI,EI,TI,xI]),wYt=br([hI,vI,O7e,d7e,s7e,R7e,T7e,SE,rJe]),RYt=br([mI,ZYe,sJe,E7e,h7e,r7e,n7e,i7e,NI,A7e,yI,bI,Kh]),Ue=class t extends Error{constructor(e,r,n){super(`MCP error ${e}: ${r}`),this.code=e,this.data=n,this.name="McpError"}static fromError(e,r,n){if(e===We.UrlElicitationRequired&&n){let o=n;if(o.elicitations)return new X4(o.elicitations,r)}return new t(e,r,n)}},X4=class extends Ue{constructor(e,r=`URL elicitation${e.length>1?"s":""} required`){super(We.UrlElicitationRequired,r,{elicitations:e})}get elicitations(){return this.data?.elicitations??[]}};var JF=W(require("node:process"),1);De();var Mce={devices:async()=>(await Promise.resolve().then(()=>(Ire(),Pre))).default,doctor:async()=>(await Promise.resolve().then(()=>(Nce(),Oce))).default,simulators:async()=>(await Promise.resolve().then(()=>(kce(),$ce))).default};async function Itt(){let t=new Map;for(let[e,r]of Object.entries(Mce))try{let n=await r();if(!n.uri||!n.handler||typeof n.handler!="function")throw new Error(`Invalid resource structure for ${e}`);t.set(n.uri,n),A("info",`Loaded resource: ${e} (${n.uri})`)}catch(n){A("error",`Failed to load resource ${e}: ${n instanceof Error?n.message:String(n)}`)}return t}async function Dce(t){let e=await Itt();for(let[r,n]of Array.from(e)){let o=async i=>({contents:(await n.handler(i)).contents.map(a=>({uri:i.toString(),text:a.text,mimeType:n.mimeType}))});t.resource(n.name,r,{mimeType:n.mimeType,title:n.description},o),A("info",`Registered resource: ${n.name} at ${r}`)}return A("info",`Registered ${e.size} resources`),!0}Go();WF();Go();KF();ZF();async function YF(t,e=[]){let r=await sg(),n=YI(r,e),o=0,i=new Set,s=new Set;for(let a of n.selectedWorkflows){s.add(a.directoryName);for(let c of a.tools)i.has(c.name)||(t.registerTool(c.name,{description:c.description??"",inputSchema:c.schema,annotations:c.annotations},u=>c.handler(u)),i.add(c.name),o+=1)}yce({enabledWorkflows:[...s],enabledTools:[...i]}),n.selectedNames?A("info",`\u2705 Registered ${o} tools from workflows: ${n.selectedNames.join(", ")}`):A("info",`\u2705 Registered ${o} tools in static mode.`)}function Ott(t){return t.split(",").map(e=>e.trim()).filter(Boolean)}async function Lce(t,e={}){t.server.setRequestHandler(TE,async n=>{let{level:o}=n.params;return mre(o),A("info",`Client requested log level: ${o}`),{}});let r=e.enabledWorkflows?.length?e.enabledWorkflows:JF.default.env.XCODEBUILDMCP_ENABLED_WORKFLOWS?Ott(JF.default.env.XCODEBUILDMCP_ENABLED_WORKFLOWS):[];r.length>0?(A("info",`\u{1F680} Initializing server with selected workflows: ${r.join(", ")}`),await YF(t,r)):(A("info","\u{1F680} Initializing server with all tools..."),await YF(t)),await Dce(t)}var Wt;(function(t){t.assertEqual=o=>{};function e(o){}t.assertIs=e;function r(o){throw new Error}t.assertNever=r,t.arrayToEnum=o=>{let i={};for(let s of o)i[s]=s;return i},t.getValidEnumValues=o=>{let i=t.objectKeys(o).filter(a=>typeof o[o[a]]!="number"),s={};for(let a of i)s[a]=o[a];return t.objectValues(s)},t.objectValues=o=>t.objectKeys(o).map(function(i){return o[i]}),t.objectKeys=typeof Object.keys=="function"?o=>Object.keys(o):o=>{let i=[];for(let s in o)Object.prototype.hasOwnProperty.call(o,s)&&i.push(s);return i},t.find=(o,i)=>{for(let s of o)if(i(s))return s},t.isInteger=typeof Number.isInteger=="function"?o=>Number.isInteger(o):o=>typeof o=="number"&&Number.isFinite(o)&&Math.floor(o)===o;function n(o,i=" | "){return o.map(s=>typeof s=="string"?`'${s}'`:s).join(i)}t.joinValues=n,t.jsonStringifyReplacer=(o,i)=>typeof i=="bigint"?i.toString():i})(Wt||(Wt={}));var Uce;(function(t){t.mergeShapes=(e,r)=>({...e,...r})})(Uce||(Uce={}));var be=Wt.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),el=t=>{switch(typeof t){case"undefined":return be.undefined;case"string":return be.string;case"number":return Number.isNaN(t)?be.nan:be.number;case"boolean":return be.boolean;case"function":return be.function;case"bigint":return be.bigint;case"symbol":return be.symbol;case"object":return Array.isArray(t)?be.array:t===null?be.null:t.then&&typeof t.then=="function"&&t.catch&&typeof t.catch=="function"?be.promise:typeof Map<"u"&&t instanceof Map?be.map:typeof Set<"u"&&t instanceof Set?be.set:typeof Date<"u"&&t instanceof Date?be.date:be.object;default:return be.unknown}};var ne=Wt.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]);var Ss=class t extends Error{get errors(){return this.issues}constructor(e){super(),this.issues=[],this.addIssue=n=>{this.issues=[...this.issues,n]},this.addIssues=(n=[])=>{this.issues=[...this.issues,...n]};let r=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,r):this.__proto__=r,this.name="ZodError",this.issues=e}format(e){let r=e||function(i){return i.message},n={_errors:[]},o=i=>{for(let s of i.issues)if(s.code==="invalid_union")s.unionErrors.map(o);else if(s.code==="invalid_return_type")o(s.returnTypeError);else if(s.code==="invalid_arguments")o(s.argumentsError);else if(s.path.length===0)n._errors.push(r(s));else{let a=n,c=0;for(;cr.message){let r=Object.create(null),n=[];for(let o of this.issues)if(o.path.length>0){let i=o.path[0];r[i]=r[i]||[],r[i].push(e(o))}else n.push(e(o));return{formErrors:n,fieldErrors:r}}get formErrors(){return this.flatten()}};Ss.create=t=>new Ss(t);var Ntt=(t,e)=>{let r;switch(t.code){case ne.invalid_type:t.received===be.undefined?r="Required":r=`Expected ${t.expected}, received ${t.received}`;break;case ne.invalid_literal:r=`Invalid literal value, expected ${JSON.stringify(t.expected,Wt.jsonStringifyReplacer)}`;break;case ne.unrecognized_keys:r=`Unrecognized key(s) in object: ${Wt.joinValues(t.keys,", ")}`;break;case ne.invalid_union:r="Invalid input";break;case ne.invalid_union_discriminator:r=`Invalid discriminator value. Expected ${Wt.joinValues(t.options)}`;break;case ne.invalid_enum_value:r=`Invalid enum value. Expected ${Wt.joinValues(t.options)}, received '${t.received}'`;break;case ne.invalid_arguments:r="Invalid function arguments";break;case ne.invalid_return_type:r="Invalid function return type";break;case ne.invalid_date:r="Invalid date";break;case ne.invalid_string:typeof t.validation=="object"?"includes"in t.validation?(r=`Invalid input: must include "${t.validation.includes}"`,typeof t.validation.position=="number"&&(r=`${r} at one or more positions greater than or equal to ${t.validation.position}`)):"startsWith"in t.validation?r=`Invalid input: must start with "${t.validation.startsWith}"`:"endsWith"in t.validation?r=`Invalid input: must end with "${t.validation.endsWith}"`:Wt.assertNever(t.validation):t.validation!=="regex"?r=`Invalid ${t.validation}`:r="Invalid";break;case ne.too_small:t.type==="array"?r=`Array must contain ${t.exact?"exactly":t.inclusive?"at least":"more than"} ${t.minimum} element(s)`:t.type==="string"?r=`String must contain ${t.exact?"exactly":t.inclusive?"at least":"over"} ${t.minimum} character(s)`:t.type==="number"?r=`Number must be ${t.exact?"exactly equal to ":t.inclusive?"greater than or equal to ":"greater than "}${t.minimum}`:t.type==="bigint"?r=`Number must be ${t.exact?"exactly equal to ":t.inclusive?"greater than or equal to ":"greater than "}${t.minimum}`:t.type==="date"?r=`Date must be ${t.exact?"exactly equal to ":t.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(t.minimum))}`:r="Invalid input";break;case ne.too_big:t.type==="array"?r=`Array must contain ${t.exact?"exactly":t.inclusive?"at most":"less than"} ${t.maximum} element(s)`:t.type==="string"?r=`String must contain ${t.exact?"exactly":t.inclusive?"at most":"under"} ${t.maximum} character(s)`:t.type==="number"?r=`Number must be ${t.exact?"exactly":t.inclusive?"less than or equal to":"less than"} ${t.maximum}`:t.type==="bigint"?r=`BigInt must be ${t.exact?"exactly":t.inclusive?"less than or equal to":"less than"} ${t.maximum}`:t.type==="date"?r=`Date must be ${t.exact?"exactly":t.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(t.maximum))}`:r="Invalid input";break;case ne.custom:r="Invalid input";break;case ne.invalid_intersection_types:r="Intersection results could not be merged";break;case ne.not_multiple_of:r=`Number must be a multiple of ${t.multipleOf}`;break;case ne.not_finite:r="Number must be finite";break;default:r=e.defaultError,Wt.assertNever(t)}return{message:r}},bp=Ntt;var Ctt=bp;function ZE(){return Ctt}var XI=t=>{let{data:e,path:r,errorMaps:n,issueData:o}=t,i=[...r,...o.path||[]],s={...o,path:i};if(o.message!==void 0)return{...o,path:i,message:o.message};let a="",c=n.filter(u=>!!u).slice().reverse();for(let u of c)a=u(s,{data:e,defaultError:a}).message;return{...o,path:i,message:a}};function Se(t,e){let r=ZE(),n=XI({issueData:e,data:t.data,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,r,r===bp?void 0:bp].filter(o=>!!o)});t.common.issues.push(n)}var Vo=class t{constructor(){this.value="valid"}dirty(){this.value==="valid"&&(this.value="dirty")}abort(){this.value!=="aborted"&&(this.value="aborted")}static mergeArray(e,r){let n=[];for(let o of r){if(o.status==="aborted")return tt;o.status==="dirty"&&e.dirty(),n.push(o.value)}return{status:e.value,value:n}}static async mergeObjectAsync(e,r){let n=[];for(let o of r){let i=await o.key,s=await o.value;n.push({key:i,value:s})}return t.mergeObjectSync(e,n)}static mergeObjectSync(e,r){let n={};for(let o of r){let{key:i,value:s}=o;if(i.status==="aborted"||s.status==="aborted")return tt;i.status==="dirty"&&e.dirty(),s.status==="dirty"&&e.dirty(),i.value!=="__proto__"&&(typeof s.value<"u"||o.alwaysSet)&&(n[i.value]=s.value)}return{status:e.value,value:n}}},tt=Object.freeze({status:"aborted"}),ag=t=>({status:"dirty",value:t}),_i=t=>({status:"valid",value:t}),XF=t=>t.status==="aborted",QF=t=>t.status==="dirty",If=t=>t.status==="valid",YE=t=>typeof Promise<"u"&&t instanceof Promise;var $e;(function(t){t.errToObj=e=>typeof e=="string"?{message:e}:e||{},t.toString=e=>typeof e=="string"?e:e?.message})($e||($e={}));var Xs=class{constructor(e,r,n,o){this._cachedPath=[],this.parent=e,this.data=r,this._path=n,this._key=o}get path(){return this._cachedPath.length||(Array.isArray(this._key)?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}},jce=(t,e)=>{if(If(e))return{success:!0,data:e.value};if(!t.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;let r=new Ss(t.common.issues);return this._error=r,this._error}}};function St(t){if(!t)return{};let{errorMap:e,invalid_type_error:r,required_error:n,description:o}=t;if(e&&(r||n))throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);return e?{errorMap:e,description:o}:{errorMap:(s,a)=>{let{message:c}=t;return s.code==="invalid_enum_value"?{message:c??a.defaultError}:typeof a.data>"u"?{message:c??n??a.defaultError}:s.code!=="invalid_type"?{message:a.defaultError}:{message:c??r??a.defaultError}},description:o}}var $t=class{get description(){return this._def.description}_getType(e){return el(e.data)}_getOrReturnCtx(e,r){return r||{common:e.parent.common,data:e.data,parsedType:el(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new Vo,ctx:{common:e.parent.common,data:e.data,parsedType:el(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){let r=this._parse(e);if(YE(r))throw new Error("Synchronous parse encountered promise.");return r}_parseAsync(e){let r=this._parse(e);return Promise.resolve(r)}parse(e,r){let n=this.safeParse(e,r);if(n.success)return n.data;throw n.error}safeParse(e,r){let n={common:{issues:[],async:r?.async??!1,contextualErrorMap:r?.errorMap},path:r?.path||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:el(e)},o=this._parseSync({data:e,path:n.path,parent:n});return jce(n,o)}"~validate"(e){let r={common:{issues:[],async:!!this["~standard"].async},path:[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:el(e)};if(!this["~standard"].async)try{let n=this._parseSync({data:e,path:[],parent:r});return If(n)?{value:n.value}:{issues:r.common.issues}}catch(n){n?.message?.toLowerCase()?.includes("encountered")&&(this["~standard"].async=!0),r.common={issues:[],async:!0}}return this._parseAsync({data:e,path:[],parent:r}).then(n=>If(n)?{value:n.value}:{issues:r.common.issues})}async parseAsync(e,r){let n=await this.safeParseAsync(e,r);if(n.success)return n.data;throw n.error}async safeParseAsync(e,r){let n={common:{issues:[],contextualErrorMap:r?.errorMap,async:!0},path:r?.path||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:el(e)},o=this._parse({data:e,path:n.path,parent:n}),i=await(YE(o)?o:Promise.resolve(o));return jce(n,i)}refine(e,r){let n=o=>typeof r=="string"||typeof r>"u"?{message:r}:typeof r=="function"?r(o):r;return this._refinement((o,i)=>{let s=e(o),a=()=>i.addIssue({code:ne.custom,...n(o)});return typeof Promise<"u"&&s instanceof Promise?s.then(c=>c?!0:(a(),!1)):s?!0:(a(),!1)})}refinement(e,r){return this._refinement((n,o)=>e(n)?!0:(o.addIssue(typeof r=="function"?r(n,o):r),!1))}_refinement(e){return new Qa({schema:this,typeName:ie.ZodEffects,effect:{type:"refinement",refinement:e}})}superRefine(e){return this._refinement(e)}constructor(e){this.spa=this.safeParseAsync,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this),this["~standard"]={version:1,vendor:"zod",validate:r=>this["~validate"](r)}}optional(){return Xa.create(this,this._def)}nullable(){return nl.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return Ap.create(this)}promise(){return Of.create(this,this._def)}or(e){return dg.create([this,e],this._def)}and(e){return fg.create(this,e,this._def)}transform(e){return new Qa({...St(this._def),schema:this,typeName:ie.ZodEffects,effect:{type:"transform",transform:e}})}default(e){let r=typeof e=="function"?e:()=>e;return new vg({...St(this._def),innerType:this,defaultValue:r,typeName:ie.ZodDefault})}brand(){return new QI({typeName:ie.ZodBranded,type:this,...St(this._def)})}catch(e){let r=typeof e=="function"?e:()=>e;return new Sg({...St(this._def),innerType:this,catchValue:r,typeName:ie.ZodCatch})}describe(e){let r=this.constructor;return new r({...this._def,description:e})}pipe(e){return eO.create(this,e)}readonly(){return yg.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}},$tt=/^c[^\s-]{8,}$/i,ktt=/^[0-9a-z]+$/,Mtt=/^[0-9A-HJKMNP-TV-Z]{26}$/i,Dtt=/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i,Ltt=/^[a-z0-9_-]{21}$/i,Utt=/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/,jtt=/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/,ztt=/^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i,Ftt="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$",eq,qtt=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,Btt=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/,Gtt=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,Vtt=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,Htt=/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,Wtt=/^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,zce="((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))",Ktt=new RegExp(`^${zce}$`);function Fce(t){let e="[0-5]\\d";t.precision?e=`${e}\\.\\d{${t.precision}}`:t.precision==null&&(e=`${e}(\\.\\d+)?`);let r=t.precision?"+":"?";return`([01]\\d|2[0-3]):[0-5]\\d(:${e})${r}`}function Ztt(t){return new RegExp(`^${Fce(t)}$`)}function Ytt(t){let e=`${zce}T${Fce(t)}`,r=[];return r.push(t.local?"Z?":"Z"),t.offset&&r.push("([+-]\\d{2}:?\\d{2})"),e=`${e}(${r.join("|")})`,new RegExp(`^${e}$`)}function Jtt(t,e){return!!((e==="v4"||!e)&&qtt.test(t)||(e==="v6"||!e)&&Gtt.test(t))}function Xtt(t,e){if(!Utt.test(t))return!1;try{let[r]=t.split(".");if(!r)return!1;let n=r.replace(/-/g,"+").replace(/_/g,"/").padEnd(r.length+(4-r.length%4)%4,"="),o=JSON.parse(atob(n));return!(typeof o!="object"||o===null||"typ"in o&&o?.typ!=="JWT"||!o.alg||e&&o.alg!==e)}catch{return!1}}function Qtt(t,e){return!!((e==="v4"||!e)&&Btt.test(t)||(e==="v6"||!e)&&Vtt.test(t))}var ug=class t extends $t{_parse(e){if(this._def.coerce&&(e.data=String(e.data)),this._getType(e)!==be.string){let i=this._getOrReturnCtx(e);return Se(i,{code:ne.invalid_type,expected:be.string,received:i.parsedType}),tt}let n=new Vo,o;for(let i of this._def.checks)if(i.kind==="min")e.data.lengthi.value&&(o=this._getOrReturnCtx(e,o),Se(o,{code:ne.too_big,maximum:i.value,type:"string",inclusive:!0,exact:!1,message:i.message}),n.dirty());else if(i.kind==="length"){let s=e.data.length>i.value,a=e.data.lengthe.test(o),{validation:r,code:ne.invalid_string,...$e.errToObj(n)})}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...$e.errToObj(e)})}url(e){return this._addCheck({kind:"url",...$e.errToObj(e)})}emoji(e){return this._addCheck({kind:"emoji",...$e.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...$e.errToObj(e)})}nanoid(e){return this._addCheck({kind:"nanoid",...$e.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...$e.errToObj(e)})}cuid2(e){return this._addCheck({kind:"cuid2",...$e.errToObj(e)})}ulid(e){return this._addCheck({kind:"ulid",...$e.errToObj(e)})}base64(e){return this._addCheck({kind:"base64",...$e.errToObj(e)})}base64url(e){return this._addCheck({kind:"base64url",...$e.errToObj(e)})}jwt(e){return this._addCheck({kind:"jwt",...$e.errToObj(e)})}ip(e){return this._addCheck({kind:"ip",...$e.errToObj(e)})}cidr(e){return this._addCheck({kind:"cidr",...$e.errToObj(e)})}datetime(e){return typeof e=="string"?this._addCheck({kind:"datetime",precision:null,offset:!1,local:!1,message:e}):this._addCheck({kind:"datetime",precision:typeof e?.precision>"u"?null:e?.precision,offset:e?.offset??!1,local:e?.local??!1,...$e.errToObj(e?.message)})}date(e){return this._addCheck({kind:"date",message:e})}time(e){return typeof e=="string"?this._addCheck({kind:"time",precision:null,message:e}):this._addCheck({kind:"time",precision:typeof e?.precision>"u"?null:e?.precision,...$e.errToObj(e?.message)})}duration(e){return this._addCheck({kind:"duration",...$e.errToObj(e)})}regex(e,r){return this._addCheck({kind:"regex",regex:e,...$e.errToObj(r)})}includes(e,r){return this._addCheck({kind:"includes",value:e,position:r?.position,...$e.errToObj(r?.message)})}startsWith(e,r){return this._addCheck({kind:"startsWith",value:e,...$e.errToObj(r)})}endsWith(e,r){return this._addCheck({kind:"endsWith",value:e,...$e.errToObj(r)})}min(e,r){return this._addCheck({kind:"min",value:e,...$e.errToObj(r)})}max(e,r){return this._addCheck({kind:"max",value:e,...$e.errToObj(r)})}length(e,r){return this._addCheck({kind:"length",value:e,...$e.errToObj(r)})}nonempty(e){return this.min(1,$e.errToObj(e))}trim(){return new t({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}toLowerCase(){return new t({...this._def,checks:[...this._def.checks,{kind:"toLowerCase"}]})}toUpperCase(){return new t({...this._def,checks:[...this._def.checks,{kind:"toUpperCase"}]})}get isDatetime(){return!!this._def.checks.find(e=>e.kind==="datetime")}get isDate(){return!!this._def.checks.find(e=>e.kind==="date")}get isTime(){return!!this._def.checks.find(e=>e.kind==="time")}get isDuration(){return!!this._def.checks.find(e=>e.kind==="duration")}get isEmail(){return!!this._def.checks.find(e=>e.kind==="email")}get isURL(){return!!this._def.checks.find(e=>e.kind==="url")}get isEmoji(){return!!this._def.checks.find(e=>e.kind==="emoji")}get isUUID(){return!!this._def.checks.find(e=>e.kind==="uuid")}get isNANOID(){return!!this._def.checks.find(e=>e.kind==="nanoid")}get isCUID(){return!!this._def.checks.find(e=>e.kind==="cuid")}get isCUID2(){return!!this._def.checks.find(e=>e.kind==="cuid2")}get isULID(){return!!this._def.checks.find(e=>e.kind==="ulid")}get isIP(){return!!this._def.checks.find(e=>e.kind==="ip")}get isCIDR(){return!!this._def.checks.find(e=>e.kind==="cidr")}get isBase64(){return!!this._def.checks.find(e=>e.kind==="base64")}get isBase64url(){return!!this._def.checks.find(e=>e.kind==="base64url")}get minLength(){let e=null;for(let r of this._def.checks)r.kind==="min"&&(e===null||r.value>e)&&(e=r.value);return e}get maxLength(){let e=null;for(let r of this._def.checks)r.kind==="max"&&(e===null||r.valuenew ug({checks:[],typeName:ie.ZodString,coerce:t?.coerce??!1,...St(t)});function ert(t,e){let r=(t.toString().split(".")[1]||"").length,n=(e.toString().split(".")[1]||"").length,o=r>n?r:n,i=Number.parseInt(t.toFixed(o).replace(".","")),s=Number.parseInt(e.toFixed(o).replace(".",""));return i%s/10**o}var JE=class t extends $t{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){if(this._def.coerce&&(e.data=Number(e.data)),this._getType(e)!==be.number){let i=this._getOrReturnCtx(e);return Se(i,{code:ne.invalid_type,expected:be.number,received:i.parsedType}),tt}let n,o=new Vo;for(let i of this._def.checks)i.kind==="int"?Wt.isInteger(e.data)||(n=this._getOrReturnCtx(e,n),Se(n,{code:ne.invalid_type,expected:"integer",received:"float",message:i.message}),o.dirty()):i.kind==="min"?(i.inclusive?e.datai.value:e.data>=i.value)&&(n=this._getOrReturnCtx(e,n),Se(n,{code:ne.too_big,maximum:i.value,type:"number",inclusive:i.inclusive,exact:!1,message:i.message}),o.dirty()):i.kind==="multipleOf"?ert(e.data,i.value)!==0&&(n=this._getOrReturnCtx(e,n),Se(n,{code:ne.not_multiple_of,multipleOf:i.value,message:i.message}),o.dirty()):i.kind==="finite"?Number.isFinite(e.data)||(n=this._getOrReturnCtx(e,n),Se(n,{code:ne.not_finite,message:i.message}),o.dirty()):Wt.assertNever(i);return{status:o.value,value:e.data}}gte(e,r){return this.setLimit("min",e,!0,$e.toString(r))}gt(e,r){return this.setLimit("min",e,!1,$e.toString(r))}lte(e,r){return this.setLimit("max",e,!0,$e.toString(r))}lt(e,r){return this.setLimit("max",e,!1,$e.toString(r))}setLimit(e,r,n,o){return new t({...this._def,checks:[...this._def.checks,{kind:e,value:r,inclusive:n,message:$e.toString(o)}]})}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:$e.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:$e.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:$e.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:$e.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:$e.toString(e)})}multipleOf(e,r){return this._addCheck({kind:"multipleOf",value:e,message:$e.toString(r)})}finite(e){return this._addCheck({kind:"finite",message:$e.toString(e)})}safe(e){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:$e.toString(e)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:$e.toString(e)})}get minValue(){let e=null;for(let r of this._def.checks)r.kind==="min"&&(e===null||r.value>e)&&(e=r.value);return e}get maxValue(){let e=null;for(let r of this._def.checks)r.kind==="max"&&(e===null||r.valuee.kind==="int"||e.kind==="multipleOf"&&Wt.isInteger(e.value))}get isFinite(){let e=null,r=null;for(let n of this._def.checks){if(n.kind==="finite"||n.kind==="int"||n.kind==="multipleOf")return!0;n.kind==="min"?(r===null||n.value>r)&&(r=n.value):n.kind==="max"&&(e===null||n.valuenew JE({checks:[],typeName:ie.ZodNumber,coerce:t?.coerce||!1,...St(t)});var XE=class t extends $t{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(e){if(this._def.coerce)try{e.data=BigInt(e.data)}catch{return this._getInvalidInput(e)}if(this._getType(e)!==be.bigint)return this._getInvalidInput(e);let n,o=new Vo;for(let i of this._def.checks)i.kind==="min"?(i.inclusive?e.datai.value:e.data>=i.value)&&(n=this._getOrReturnCtx(e,n),Se(n,{code:ne.too_big,type:"bigint",maximum:i.value,inclusive:i.inclusive,message:i.message}),o.dirty()):i.kind==="multipleOf"?e.data%i.value!==BigInt(0)&&(n=this._getOrReturnCtx(e,n),Se(n,{code:ne.not_multiple_of,multipleOf:i.value,message:i.message}),o.dirty()):Wt.assertNever(i);return{status:o.value,value:e.data}}_getInvalidInput(e){let r=this._getOrReturnCtx(e);return Se(r,{code:ne.invalid_type,expected:be.bigint,received:r.parsedType}),tt}gte(e,r){return this.setLimit("min",e,!0,$e.toString(r))}gt(e,r){return this.setLimit("min",e,!1,$e.toString(r))}lte(e,r){return this.setLimit("max",e,!0,$e.toString(r))}lt(e,r){return this.setLimit("max",e,!1,$e.toString(r))}setLimit(e,r,n,o){return new t({...this._def,checks:[...this._def.checks,{kind:e,value:r,inclusive:n,message:$e.toString(o)}]})}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}positive(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:$e.toString(e)})}negative(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:$e.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:$e.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:$e.toString(e)})}multipleOf(e,r){return this._addCheck({kind:"multipleOf",value:e,message:$e.toString(r)})}get minValue(){let e=null;for(let r of this._def.checks)r.kind==="min"&&(e===null||r.value>e)&&(e=r.value);return e}get maxValue(){let e=null;for(let r of this._def.checks)r.kind==="max"&&(e===null||r.valuenew XE({checks:[],typeName:ie.ZodBigInt,coerce:t?.coerce??!1,...St(t)});var QE=class extends $t{_parse(e){if(this._def.coerce&&(e.data=!!e.data),this._getType(e)!==be.boolean){let n=this._getOrReturnCtx(e);return Se(n,{code:ne.invalid_type,expected:be.boolean,received:n.parsedType}),tt}return _i(e.data)}};QE.create=t=>new QE({typeName:ie.ZodBoolean,coerce:t?.coerce||!1,...St(t)});var eT=class t extends $t{_parse(e){if(this._def.coerce&&(e.data=new Date(e.data)),this._getType(e)!==be.date){let i=this._getOrReturnCtx(e);return Se(i,{code:ne.invalid_type,expected:be.date,received:i.parsedType}),tt}if(Number.isNaN(e.data.getTime())){let i=this._getOrReturnCtx(e);return Se(i,{code:ne.invalid_date}),tt}let n=new Vo,o;for(let i of this._def.checks)i.kind==="min"?e.data.getTime()i.value&&(o=this._getOrReturnCtx(e,o),Se(o,{code:ne.too_big,message:i.message,inclusive:!0,exact:!1,maximum:i.value,type:"date"}),n.dirty()):Wt.assertNever(i);return{status:n.value,value:new Date(e.data.getTime())}}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}min(e,r){return this._addCheck({kind:"min",value:e.getTime(),message:$e.toString(r)})}max(e,r){return this._addCheck({kind:"max",value:e.getTime(),message:$e.toString(r)})}get minDate(){let e=null;for(let r of this._def.checks)r.kind==="min"&&(e===null||r.value>e)&&(e=r.value);return e!=null?new Date(e):null}get maxDate(){let e=null;for(let r of this._def.checks)r.kind==="max"&&(e===null||r.valuenew eT({checks:[],coerce:t?.coerce||!1,typeName:ie.ZodDate,...St(t)});var tT=class extends $t{_parse(e){if(this._getType(e)!==be.symbol){let n=this._getOrReturnCtx(e);return Se(n,{code:ne.invalid_type,expected:be.symbol,received:n.parsedType}),tt}return _i(e.data)}};tT.create=t=>new tT({typeName:ie.ZodSymbol,...St(t)});var lg=class extends $t{_parse(e){if(this._getType(e)!==be.undefined){let n=this._getOrReturnCtx(e);return Se(n,{code:ne.invalid_type,expected:be.undefined,received:n.parsedType}),tt}return _i(e.data)}};lg.create=t=>new lg({typeName:ie.ZodUndefined,...St(t)});var pg=class extends $t{_parse(e){if(this._getType(e)!==be.null){let n=this._getOrReturnCtx(e);return Se(n,{code:ne.invalid_type,expected:be.null,received:n.parsedType}),tt}return _i(e.data)}};pg.create=t=>new pg({typeName:ie.ZodNull,...St(t)});var rT=class extends $t{constructor(){super(...arguments),this._any=!0}_parse(e){return _i(e.data)}};rT.create=t=>new rT({typeName:ie.ZodAny,...St(t)});var xp=class extends $t{constructor(){super(...arguments),this._unknown=!0}_parse(e){return _i(e.data)}};xp.create=t=>new xp({typeName:ie.ZodUnknown,...St(t)});var jc=class extends $t{_parse(e){let r=this._getOrReturnCtx(e);return Se(r,{code:ne.invalid_type,expected:be.never,received:r.parsedType}),tt}};jc.create=t=>new jc({typeName:ie.ZodNever,...St(t)});var nT=class extends $t{_parse(e){if(this._getType(e)!==be.undefined){let n=this._getOrReturnCtx(e);return Se(n,{code:ne.invalid_type,expected:be.void,received:n.parsedType}),tt}return _i(e.data)}};nT.create=t=>new nT({typeName:ie.ZodVoid,...St(t)});var Ap=class t extends $t{_parse(e){let{ctx:r,status:n}=this._processInputParams(e),o=this._def;if(r.parsedType!==be.array)return Se(r,{code:ne.invalid_type,expected:be.array,received:r.parsedType}),tt;if(o.exactLength!==null){let s=r.data.length>o.exactLength.value,a=r.data.lengtho.maxLength.value&&(Se(r,{code:ne.too_big,maximum:o.maxLength.value,type:"array",inclusive:!0,exact:!1,message:o.maxLength.message}),n.dirty()),r.common.async)return Promise.all([...r.data].map((s,a)=>o.type._parseAsync(new Xs(r,s,r.path,a)))).then(s=>Vo.mergeArray(n,s));let i=[...r.data].map((s,a)=>o.type._parseSync(new Xs(r,s,r.path,a)));return Vo.mergeArray(n,i)}get element(){return this._def.type}min(e,r){return new t({...this._def,minLength:{value:e,message:$e.toString(r)}})}max(e,r){return new t({...this._def,maxLength:{value:e,message:$e.toString(r)}})}length(e,r){return new t({...this._def,exactLength:{value:e,message:$e.toString(r)}})}nonempty(e){return this.min(1,e)}};Ap.create=(t,e)=>new Ap({type:t,minLength:null,maxLength:null,exactLength:null,typeName:ie.ZodArray,...St(e)});function cg(t){if(t instanceof ys){let e={};for(let r in t.shape){let n=t.shape[r];e[r]=Xa.create(cg(n))}return new ys({...t._def,shape:()=>e})}else return t instanceof Ap?new Ap({...t._def,type:cg(t.element)}):t instanceof Xa?Xa.create(cg(t.unwrap())):t instanceof nl?nl.create(cg(t.unwrap())):t instanceof rl?rl.create(t.items.map(e=>cg(e))):t}var ys=class t extends $t{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(this._cached!==null)return this._cached;let e=this._def.shape(),r=Wt.objectKeys(e);return this._cached={shape:e,keys:r},this._cached}_parse(e){if(this._getType(e)!==be.object){let u=this._getOrReturnCtx(e);return Se(u,{code:ne.invalid_type,expected:be.object,received:u.parsedType}),tt}let{status:n,ctx:o}=this._processInputParams(e),{shape:i,keys:s}=this._getCached(),a=[];if(!(this._def.catchall instanceof jc&&this._def.unknownKeys==="strip"))for(let u in o.data)s.includes(u)||a.push(u);let c=[];for(let u of s){let p=i[u],f=o.data[u];c.push({key:{status:"valid",value:u},value:p._parse(new Xs(o,f,o.path,u)),alwaysSet:u in o.data})}if(this._def.catchall instanceof jc){let u=this._def.unknownKeys;if(u==="passthrough")for(let p of a)c.push({key:{status:"valid",value:p},value:{status:"valid",value:o.data[p]}});else if(u==="strict")a.length>0&&(Se(o,{code:ne.unrecognized_keys,keys:a}),n.dirty());else if(u!=="strip")throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{let u=this._def.catchall;for(let p of a){let f=o.data[p];c.push({key:{status:"valid",value:p},value:u._parse(new Xs(o,f,o.path,p)),alwaysSet:p in o.data})}}return o.common.async?Promise.resolve().then(async()=>{let u=[];for(let p of c){let f=await p.key,m=await p.value;u.push({key:f,value:m,alwaysSet:p.alwaysSet})}return u}).then(u=>Vo.mergeObjectSync(n,u)):Vo.mergeObjectSync(n,c)}get shape(){return this._def.shape()}strict(e){return $e.errToObj,new t({...this._def,unknownKeys:"strict",...e!==void 0?{errorMap:(r,n)=>{let o=this._def.errorMap?.(r,n).message??n.defaultError;return r.code==="unrecognized_keys"?{message:$e.errToObj(e).message??o}:{message:o}}}:{}})}strip(){return new t({...this._def,unknownKeys:"strip"})}passthrough(){return new t({...this._def,unknownKeys:"passthrough"})}extend(e){return new t({...this._def,shape:()=>({...this._def.shape(),...e})})}merge(e){return new t({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>({...this._def.shape(),...e._def.shape()}),typeName:ie.ZodObject})}setKey(e,r){return this.augment({[e]:r})}catchall(e){return new t({...this._def,catchall:e})}pick(e){let r={};for(let n of Wt.objectKeys(e))e[n]&&this.shape[n]&&(r[n]=this.shape[n]);return new t({...this._def,shape:()=>r})}omit(e){let r={};for(let n of Wt.objectKeys(this.shape))e[n]||(r[n]=this.shape[n]);return new t({...this._def,shape:()=>r})}deepPartial(){return cg(this)}partial(e){let r={};for(let n of Wt.objectKeys(this.shape)){let o=this.shape[n];e&&!e[n]?r[n]=o:r[n]=o.optional()}return new t({...this._def,shape:()=>r})}required(e){let r={};for(let n of Wt.objectKeys(this.shape))if(e&&!e[n])r[n]=this.shape[n];else{let i=this.shape[n];for(;i instanceof Xa;)i=i._def.innerType;r[n]=i}return new t({...this._def,shape:()=>r})}keyof(){return qce(Wt.objectKeys(this.shape))}};ys.create=(t,e)=>new ys({shape:()=>t,unknownKeys:"strip",catchall:jc.create(),typeName:ie.ZodObject,...St(e)});ys.strictCreate=(t,e)=>new ys({shape:()=>t,unknownKeys:"strict",catchall:jc.create(),typeName:ie.ZodObject,...St(e)});ys.lazycreate=(t,e)=>new ys({shape:t,unknownKeys:"strip",catchall:jc.create(),typeName:ie.ZodObject,...St(e)});var dg=class extends $t{_parse(e){let{ctx:r}=this._processInputParams(e),n=this._def.options;function o(i){for(let a of i)if(a.result.status==="valid")return a.result;for(let a of i)if(a.result.status==="dirty")return r.common.issues.push(...a.ctx.common.issues),a.result;let s=i.map(a=>new Ss(a.ctx.common.issues));return Se(r,{code:ne.invalid_union,unionErrors:s}),tt}if(r.common.async)return Promise.all(n.map(async i=>{let s={...r,common:{...r.common,issues:[]},parent:null};return{result:await i._parseAsync({data:r.data,path:r.path,parent:s}),ctx:s}})).then(o);{let i,s=[];for(let c of n){let u={...r,common:{...r.common,issues:[]},parent:null},p=c._parseSync({data:r.data,path:r.path,parent:u});if(p.status==="valid")return p;p.status==="dirty"&&!i&&(i={result:p,ctx:u}),u.common.issues.length&&s.push(u.common.issues)}if(i)return r.common.issues.push(...i.ctx.common.issues),i.result;let a=s.map(c=>new Ss(c));return Se(r,{code:ne.invalid_union,unionErrors:a}),tt}}get options(){return this._def.options}};dg.create=(t,e)=>new dg({options:t,typeName:ie.ZodUnion,...St(e)});var tl=t=>t instanceof mg?tl(t.schema):t instanceof Qa?tl(t.innerType()):t instanceof hg?[t.value]:t instanceof gg?t.options:t instanceof _g?Wt.objectValues(t.enum):t instanceof vg?tl(t._def.innerType):t instanceof lg?[void 0]:t instanceof pg?[null]:t instanceof Xa?[void 0,...tl(t.unwrap())]:t instanceof nl?[null,...tl(t.unwrap())]:t instanceof QI||t instanceof yg?tl(t.unwrap()):t instanceof Sg?tl(t._def.innerType):[],tq=class t extends $t{_parse(e){let{ctx:r}=this._processInputParams(e);if(r.parsedType!==be.object)return Se(r,{code:ne.invalid_type,expected:be.object,received:r.parsedType}),tt;let n=this.discriminator,o=r.data[n],i=this.optionsMap.get(o);return i?r.common.async?i._parseAsync({data:r.data,path:r.path,parent:r}):i._parseSync({data:r.data,path:r.path,parent:r}):(Se(r,{code:ne.invalid_union_discriminator,options:Array.from(this.optionsMap.keys()),path:[n]}),tt)}get discriminator(){return this._def.discriminator}get options(){return this._def.options}get optionsMap(){return this._def.optionsMap}static create(e,r,n){let o=new Map;for(let i of r){let s=tl(i.shape[e]);if(!s.length)throw new Error(`A discriminator value for key \`${e}\` could not be extracted from all schema options`);for(let a of s){if(o.has(a))throw new Error(`Discriminator property ${String(e)} has duplicate value ${String(a)}`);o.set(a,i)}}return new t({typeName:ie.ZodDiscriminatedUnion,discriminator:e,options:r,optionsMap:o,...St(n)})}};function rq(t,e){let r=el(t),n=el(e);if(t===e)return{valid:!0,data:t};if(r===be.object&&n===be.object){let o=Wt.objectKeys(e),i=Wt.objectKeys(t).filter(a=>o.indexOf(a)!==-1),s={...t,...e};for(let a of i){let c=rq(t[a],e[a]);if(!c.valid)return{valid:!1};s[a]=c.data}return{valid:!0,data:s}}else if(r===be.array&&n===be.array){if(t.length!==e.length)return{valid:!1};let o=[];for(let i=0;i{if(XF(i)||XF(s))return tt;let a=rq(i.value,s.value);return a.valid?((QF(i)||QF(s))&&r.dirty(),{status:r.value,value:a.data}):(Se(n,{code:ne.invalid_intersection_types}),tt)};return n.common.async?Promise.all([this._def.left._parseAsync({data:n.data,path:n.path,parent:n}),this._def.right._parseAsync({data:n.data,path:n.path,parent:n})]).then(([i,s])=>o(i,s)):o(this._def.left._parseSync({data:n.data,path:n.path,parent:n}),this._def.right._parseSync({data:n.data,path:n.path,parent:n}))}};fg.create=(t,e,r)=>new fg({left:t,right:e,typeName:ie.ZodIntersection,...St(r)});var rl=class t extends $t{_parse(e){let{status:r,ctx:n}=this._processInputParams(e);if(n.parsedType!==be.array)return Se(n,{code:ne.invalid_type,expected:be.array,received:n.parsedType}),tt;if(n.data.lengththis._def.items.length&&(Se(n,{code:ne.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),r.dirty());let i=[...n.data].map((s,a)=>{let c=this._def.items[a]||this._def.rest;return c?c._parse(new Xs(n,s,n.path,a)):null}).filter(s=>!!s);return n.common.async?Promise.all(i).then(s=>Vo.mergeArray(r,s)):Vo.mergeArray(r,i)}get items(){return this._def.items}rest(e){return new t({...this._def,rest:e})}};rl.create=(t,e)=>{if(!Array.isArray(t))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new rl({items:t,typeName:ie.ZodTuple,rest:null,...St(e)})};var nq=class t extends $t{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){let{status:r,ctx:n}=this._processInputParams(e);if(n.parsedType!==be.object)return Se(n,{code:ne.invalid_type,expected:be.object,received:n.parsedType}),tt;let o=[],i=this._def.keyType,s=this._def.valueType;for(let a in n.data)o.push({key:i._parse(new Xs(n,a,n.path,a)),value:s._parse(new Xs(n,n.data[a],n.path,a)),alwaysSet:a in n.data});return n.common.async?Vo.mergeObjectAsync(r,o):Vo.mergeObjectSync(r,o)}get element(){return this._def.valueType}static create(e,r,n){return r instanceof $t?new t({keyType:e,valueType:r,typeName:ie.ZodRecord,...St(n)}):new t({keyType:ug.create(),valueType:e,typeName:ie.ZodRecord,...St(r)})}},oT=class extends $t{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){let{status:r,ctx:n}=this._processInputParams(e);if(n.parsedType!==be.map)return Se(n,{code:ne.invalid_type,expected:be.map,received:n.parsedType}),tt;let o=this._def.keyType,i=this._def.valueType,s=[...n.data.entries()].map(([a,c],u)=>({key:o._parse(new Xs(n,a,n.path,[u,"key"])),value:i._parse(new Xs(n,c,n.path,[u,"value"]))}));if(n.common.async){let a=new Map;return Promise.resolve().then(async()=>{for(let c of s){let u=await c.key,p=await c.value;if(u.status==="aborted"||p.status==="aborted")return tt;(u.status==="dirty"||p.status==="dirty")&&r.dirty(),a.set(u.value,p.value)}return{status:r.value,value:a}})}else{let a=new Map;for(let c of s){let u=c.key,p=c.value;if(u.status==="aborted"||p.status==="aborted")return tt;(u.status==="dirty"||p.status==="dirty")&&r.dirty(),a.set(u.value,p.value)}return{status:r.value,value:a}}}};oT.create=(t,e,r)=>new oT({valueType:e,keyType:t,typeName:ie.ZodMap,...St(r)});var iT=class t extends $t{_parse(e){let{status:r,ctx:n}=this._processInputParams(e);if(n.parsedType!==be.set)return Se(n,{code:ne.invalid_type,expected:be.set,received:n.parsedType}),tt;let o=this._def;o.minSize!==null&&n.data.sizeo.maxSize.value&&(Se(n,{code:ne.too_big,maximum:o.maxSize.value,type:"set",inclusive:!0,exact:!1,message:o.maxSize.message}),r.dirty());let i=this._def.valueType;function s(c){let u=new Set;for(let p of c){if(p.status==="aborted")return tt;p.status==="dirty"&&r.dirty(),u.add(p.value)}return{status:r.value,value:u}}let a=[...n.data.values()].map((c,u)=>i._parse(new Xs(n,c,n.path,u)));return n.common.async?Promise.all(a).then(c=>s(c)):s(a)}min(e,r){return new t({...this._def,minSize:{value:e,message:$e.toString(r)}})}max(e,r){return new t({...this._def,maxSize:{value:e,message:$e.toString(r)}})}size(e,r){return this.min(e,r).max(e,r)}nonempty(e){return this.min(1,e)}};iT.create=(t,e)=>new iT({valueType:t,minSize:null,maxSize:null,typeName:ie.ZodSet,...St(e)});var oq=class t extends $t{constructor(){super(...arguments),this.validate=this.implement}_parse(e){let{ctx:r}=this._processInputParams(e);if(r.parsedType!==be.function)return Se(r,{code:ne.invalid_type,expected:be.function,received:r.parsedType}),tt;function n(a,c){return XI({data:a,path:r.path,errorMaps:[r.common.contextualErrorMap,r.schemaErrorMap,ZE(),bp].filter(u=>!!u),issueData:{code:ne.invalid_arguments,argumentsError:c}})}function o(a,c){return XI({data:a,path:r.path,errorMaps:[r.common.contextualErrorMap,r.schemaErrorMap,ZE(),bp].filter(u=>!!u),issueData:{code:ne.invalid_return_type,returnTypeError:c}})}let i={errorMap:r.common.contextualErrorMap},s=r.data;if(this._def.returns instanceof Of){let a=this;return _i(async function(...c){let u=new Ss([]),p=await a._def.args.parseAsync(c,i).catch(h=>{throw u.addIssue(n(c,h)),u}),f=await Reflect.apply(s,this,p);return await a._def.returns._def.type.parseAsync(f,i).catch(h=>{throw u.addIssue(o(f,h)),u})})}else{let a=this;return _i(function(...c){let u=a._def.args.safeParse(c,i);if(!u.success)throw new Ss([n(c,u.error)]);let p=Reflect.apply(s,this,u.data),f=a._def.returns.safeParse(p,i);if(!f.success)throw new Ss([o(p,f.error)]);return f.data})}}parameters(){return this._def.args}returnType(){return this._def.returns}args(...e){return new t({...this._def,args:rl.create(e).rest(xp.create())})}returns(e){return new t({...this._def,returns:e})}implement(e){return this.parse(e)}strictImplement(e){return this.parse(e)}static create(e,r,n){return new t({args:e||rl.create([]).rest(xp.create()),returns:r||xp.create(),typeName:ie.ZodFunction,...St(n)})}},mg=class extends $t{get schema(){return this._def.getter()}_parse(e){let{ctx:r}=this._processInputParams(e);return this._def.getter()._parse({data:r.data,path:r.path,parent:r})}};mg.create=(t,e)=>new mg({getter:t,typeName:ie.ZodLazy,...St(e)});var hg=class extends $t{_parse(e){if(e.data!==this._def.value){let r=this._getOrReturnCtx(e);return Se(r,{received:r.data,code:ne.invalid_literal,expected:this._def.value}),tt}return{status:"valid",value:e.data}}get value(){return this._def.value}};hg.create=(t,e)=>new hg({value:t,typeName:ie.ZodLiteral,...St(e)});function qce(t,e){return new gg({values:t,typeName:ie.ZodEnum,...St(e)})}var gg=class t extends $t{_parse(e){if(typeof e.data!="string"){let r=this._getOrReturnCtx(e),n=this._def.values;return Se(r,{expected:Wt.joinValues(n),received:r.parsedType,code:ne.invalid_type}),tt}if(this._cache||(this._cache=new Set(this._def.values)),!this._cache.has(e.data)){let r=this._getOrReturnCtx(e),n=this._def.values;return Se(r,{received:r.data,code:ne.invalid_enum_value,options:n}),tt}return _i(e.data)}get options(){return this._def.values}get enum(){let e={};for(let r of this._def.values)e[r]=r;return e}get Values(){let e={};for(let r of this._def.values)e[r]=r;return e}get Enum(){let e={};for(let r of this._def.values)e[r]=r;return e}extract(e,r=this._def){return t.create(e,{...this._def,...r})}exclude(e,r=this._def){return t.create(this.options.filter(n=>!e.includes(n)),{...this._def,...r})}};gg.create=qce;var _g=class extends $t{_parse(e){let r=Wt.getValidEnumValues(this._def.values),n=this._getOrReturnCtx(e);if(n.parsedType!==be.string&&n.parsedType!==be.number){let o=Wt.objectValues(r);return Se(n,{expected:Wt.joinValues(o),received:n.parsedType,code:ne.invalid_type}),tt}if(this._cache||(this._cache=new Set(Wt.getValidEnumValues(this._def.values))),!this._cache.has(e.data)){let o=Wt.objectValues(r);return Se(n,{received:n.data,code:ne.invalid_enum_value,options:o}),tt}return _i(e.data)}get enum(){return this._def.values}};_g.create=(t,e)=>new _g({values:t,typeName:ie.ZodNativeEnum,...St(e)});var Of=class extends $t{unwrap(){return this._def.type}_parse(e){let{ctx:r}=this._processInputParams(e);if(r.parsedType!==be.promise&&r.common.async===!1)return Se(r,{code:ne.invalid_type,expected:be.promise,received:r.parsedType}),tt;let n=r.parsedType===be.promise?r.data:Promise.resolve(r.data);return _i(n.then(o=>this._def.type.parseAsync(o,{path:r.path,errorMap:r.common.contextualErrorMap})))}};Of.create=(t,e)=>new Of({type:t,typeName:ie.ZodPromise,...St(e)});var Qa=class extends $t{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===ie.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(e){let{status:r,ctx:n}=this._processInputParams(e),o=this._def.effect||null,i={addIssue:s=>{Se(n,s),s.fatal?r.abort():r.dirty()},get path(){return n.path}};if(i.addIssue=i.addIssue.bind(i),o.type==="preprocess"){let s=o.transform(n.data,i);if(n.common.async)return Promise.resolve(s).then(async a=>{if(r.value==="aborted")return tt;let c=await this._def.schema._parseAsync({data:a,path:n.path,parent:n});return c.status==="aborted"?tt:c.status==="dirty"?ag(c.value):r.value==="dirty"?ag(c.value):c});{if(r.value==="aborted")return tt;let a=this._def.schema._parseSync({data:s,path:n.path,parent:n});return a.status==="aborted"?tt:a.status==="dirty"?ag(a.value):r.value==="dirty"?ag(a.value):a}}if(o.type==="refinement"){let s=a=>{let c=o.refinement(a,i);if(n.common.async)return Promise.resolve(c);if(c instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return a};if(n.common.async===!1){let a=this._def.schema._parseSync({data:n.data,path:n.path,parent:n});return a.status==="aborted"?tt:(a.status==="dirty"&&r.dirty(),s(a.value),{status:r.value,value:a.value})}else return this._def.schema._parseAsync({data:n.data,path:n.path,parent:n}).then(a=>a.status==="aborted"?tt:(a.status==="dirty"&&r.dirty(),s(a.value).then(()=>({status:r.value,value:a.value}))))}if(o.type==="transform")if(n.common.async===!1){let s=this._def.schema._parseSync({data:n.data,path:n.path,parent:n});if(!If(s))return tt;let a=o.transform(s.value,i);if(a instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:r.value,value:a}}else return this._def.schema._parseAsync({data:n.data,path:n.path,parent:n}).then(s=>If(s)?Promise.resolve(o.transform(s.value,i)).then(a=>({status:r.value,value:a})):tt);Wt.assertNever(o)}};Qa.create=(t,e,r)=>new Qa({schema:t,typeName:ie.ZodEffects,effect:e,...St(r)});Qa.createWithPreprocess=(t,e,r)=>new Qa({schema:e,effect:{type:"preprocess",transform:t},typeName:ie.ZodEffects,...St(r)});var Xa=class extends $t{_parse(e){return this._getType(e)===be.undefined?_i(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}};Xa.create=(t,e)=>new Xa({innerType:t,typeName:ie.ZodOptional,...St(e)});var nl=class extends $t{_parse(e){return this._getType(e)===be.null?_i(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}};nl.create=(t,e)=>new nl({innerType:t,typeName:ie.ZodNullable,...St(e)});var vg=class extends $t{_parse(e){let{ctx:r}=this._processInputParams(e),n=r.data;return r.parsedType===be.undefined&&(n=this._def.defaultValue()),this._def.innerType._parse({data:n,path:r.path,parent:r})}removeDefault(){return this._def.innerType}};vg.create=(t,e)=>new vg({innerType:t,typeName:ie.ZodDefault,defaultValue:typeof e.default=="function"?e.default:()=>e.default,...St(e)});var Sg=class extends $t{_parse(e){let{ctx:r}=this._processInputParams(e),n={...r,common:{...r.common,issues:[]}},o=this._def.innerType._parse({data:n.data,path:n.path,parent:{...n}});return YE(o)?o.then(i=>({status:"valid",value:i.status==="valid"?i.value:this._def.catchValue({get error(){return new Ss(n.common.issues)},input:n.data})})):{status:"valid",value:o.status==="valid"?o.value:this._def.catchValue({get error(){return new Ss(n.common.issues)},input:n.data})}}removeCatch(){return this._def.innerType}};Sg.create=(t,e)=>new Sg({innerType:t,typeName:ie.ZodCatch,catchValue:typeof e.catch=="function"?e.catch:()=>e.catch,...St(e)});var sT=class extends $t{_parse(e){if(this._getType(e)!==be.nan){let n=this._getOrReturnCtx(e);return Se(n,{code:ne.invalid_type,expected:be.nan,received:n.parsedType}),tt}return{status:"valid",value:e.data}}};sT.create=t=>new sT({typeName:ie.ZodNaN,...St(t)});var Aor=Symbol("zod_brand"),QI=class extends $t{_parse(e){let{ctx:r}=this._processInputParams(e),n=r.data;return this._def.type._parse({data:n,path:r.path,parent:r})}unwrap(){return this._def.type}},eO=class t extends $t{_parse(e){let{status:r,ctx:n}=this._processInputParams(e);if(n.common.async)return(async()=>{let i=await this._def.in._parseAsync({data:n.data,path:n.path,parent:n});return i.status==="aborted"?tt:i.status==="dirty"?(r.dirty(),ag(i.value)):this._def.out._parseAsync({data:i.value,path:n.path,parent:n})})();{let o=this._def.in._parseSync({data:n.data,path:n.path,parent:n});return o.status==="aborted"?tt:o.status==="dirty"?(r.dirty(),{status:"dirty",value:o.value}):this._def.out._parseSync({data:o.value,path:n.path,parent:n})}}static create(e,r){return new t({in:e,out:r,typeName:ie.ZodPipeline})}},yg=class extends $t{_parse(e){let r=this._def.innerType._parse(e),n=o=>(If(o)&&(o.value=Object.freeze(o.value)),o);return YE(r)?r.then(o=>n(o)):n(r)}unwrap(){return this._def.innerType}};yg.create=(t,e)=>new yg({innerType:t,typeName:ie.ZodReadonly,...St(e)});var wor={object:ys.lazycreate},ie;(function(t){t.ZodString="ZodString",t.ZodNumber="ZodNumber",t.ZodNaN="ZodNaN",t.ZodBigInt="ZodBigInt",t.ZodBoolean="ZodBoolean",t.ZodDate="ZodDate",t.ZodSymbol="ZodSymbol",t.ZodUndefined="ZodUndefined",t.ZodNull="ZodNull",t.ZodAny="ZodAny",t.ZodUnknown="ZodUnknown",t.ZodNever="ZodNever",t.ZodVoid="ZodVoid",t.ZodArray="ZodArray",t.ZodObject="ZodObject",t.ZodUnion="ZodUnion",t.ZodDiscriminatedUnion="ZodDiscriminatedUnion",t.ZodIntersection="ZodIntersection",t.ZodTuple="ZodTuple",t.ZodRecord="ZodRecord",t.ZodMap="ZodMap",t.ZodSet="ZodSet",t.ZodFunction="ZodFunction",t.ZodLazy="ZodLazy",t.ZodLiteral="ZodLiteral",t.ZodEnum="ZodEnum",t.ZodEffects="ZodEffects",t.ZodNativeEnum="ZodNativeEnum",t.ZodOptional="ZodOptional",t.ZodNullable="ZodNullable",t.ZodDefault="ZodDefault",t.ZodCatch="ZodCatch",t.ZodPromise="ZodPromise",t.ZodBranded="ZodBranded",t.ZodPipeline="ZodPipeline",t.ZodReadonly="ZodReadonly"})(ie||(ie={}));var Ror=ug.create,Por=JE.create,Ior=sT.create,Oor=XE.create,Nor=QE.create,Cor=eT.create,$or=tT.create,kor=lg.create,Mor=pg.create,Dor=rT.create,Lor=xp.create,Uor=jc.create,jor=nT.create,zor=Ap.create,Bce=ys.create,For=ys.strictCreate,qor=dg.create,Bor=tq.create,Gor=fg.create,Vor=rl.create,Hor=nq.create,Wor=oT.create,Kor=iT.create,Zor=oq.create,Yor=mg.create,Jor=hg.create,Xor=gg.create,Qor=_g.create,eir=Of.create,tir=Qa.create,rir=Xa.create,nir=nl.create,oir=Qa.createWithPreprocess,iir=eO.create;mn();mn();mn();mn();var Gce=D("ZodMiniType",(t,e)=>{if(!t._zod)throw new Error("Uninitialized schema in ZodMiniType.");nt.init(t,e),t.def=e,t.type=e.type,t.parse=(r,n)=>af(t,r,n,{callee:t.parse}),t.safeParse=(r,n)=>sp(t,r,n),t.parseAsync=async(r,n)=>cf(t,r,n,{callee:t.parseAsync}),t.safeParseAsync=async(r,n)=>ap(t,r,n),t.check=(...r)=>t.clone({...e,checks:[...e.checks??[],...r.map(n=>typeof n=="function"?{_zod:{check:n,def:{check:"custom"},onattach:[]}}:n)]},{parent:!0}),t.with=t.check,t.refine=(r,n)=>t.check(srt(r,n)),t.clone=(r,n)=>oo(t,r,n),t.brand=()=>t,t.register=((r,n)=>(r.add(t,n),t)),t.apply=r=>r(t)});var ort=D("ZodMiniObject",(t,e)=>{pR.init(t,e),Gce.init(t,e),te.defineLazy(t,"shape",()=>e.shape)});function iq(t,e){let r={type:"object",shape:t??{},...te.normalizeParams(e)};return new ort(r)}var irt=D("ZodMiniCustom",(t,e)=>{Sy.init(t,e),Gce.init(t,e)});function srt(t,e={}){return Ky(irt,t,e)}mn();mn();Dh();jR();mn();mn();function Qs(t){return!!t._zod}function Nf(t){let e=Object.values(t);if(e.length===0)return iq({});let r=e.every(Qs),n=e.every(o=>!Qs(o));if(r)return iq(t);if(n)return Bce(t);throw new Error("Mixed Zod versions detected in object shape.")}function wp(t,e){return Qs(t)?sp(t,e):t.safeParse(e)}async function tO(t,e){return Qs(t)?await ap(t,e):await t.safeParseAsync(e)}function Rp(t){if(!t)return;let e;if(Qs(t)?e=t._zod?.def?.shape:e=t.shape,!!e){if(typeof e=="function")try{return e()}catch{return}return e}}function Eg(t){if(t){if(typeof t=="object"){let e=t,r=t;if(!e._def&&!r._zod){let n=Object.values(t);if(n.length>0&&n.every(o=>typeof o=="object"&&o!==null&&(o._def!==void 0||o._zod!==void 0||typeof o.parse=="function")))return Nf(t)}}if(Qs(t)){let r=t._zod?.def;if(r&&(r.type==="object"||r.shape!==void 0))return t}else if(t.shape!==void 0)return t}}function rO(t){if(t&&typeof t=="object"){if("message"in t&&typeof t.message=="string")return t.message;if("issues"in t&&Array.isArray(t.issues)&&t.issues.length>0){let e=t.issues[0];if(e&&typeof e=="object"&&"message"in e)return String(e.message)}try{return JSON.stringify(t)}catch{return String(t)}}return String(t)}function Wce(t){return t.description}function Kce(t){if(Qs(t))return t._zod?.def?.type==="optional";let e=t;return typeof t.isOptional=="function"?t.isOptional():e._def?.typeName==="ZodOptional"}function nO(t){if(Qs(t)){let i=t._zod?.def;if(i){if(i.value!==void 0)return i.value;if(Array.isArray(i.values)&&i.values.length>0)return i.values[0]}}let r=t._def;if(r){if(r.value!==void 0)return r.value;if(Array.isArray(r.values)&&r.values.length>0)return r.values[0]}let n=t.value;if(n!==void 0)return n}function Pp(t){return t==="completed"||t==="failed"||t==="cancelled"}var Yce=Symbol("Let zodToJsonSchema decide on which parser to use");var Zce={name:void 0,$refStrategy:"root",basePath:["#"],effectStrategy:"input",pipeStrategy:"all",dateStrategy:"format:date-time",mapStrategy:"entries",removeAdditionalStrategy:"passthrough",allowedAdditionalProperties:!0,rejectedAdditionalProperties:!1,definitionPath:"definitions",target:"jsonSchema7",strictUnions:!1,definitions:{},errorMessages:!1,markdownDescription:!1,patternStrategy:"escape",applyRegexFlags:!1,emailStrategy:"format:email",base64Strategy:"contentEncoding:base64",nameStrategy:"ref",openAiAnyTypeName:"OpenAiAnyType"},Jce=t=>typeof t=="string"?{...Zce,name:t}:{...Zce,...t};var Xce=t=>{let e=Jce(t),r=e.name!==void 0?[...e.basePath,e.definitionPath,e.name]:e.basePath;return{...e,flags:{hasReferencedOpenAiAnyType:!1},currentPath:r,propertyPath:void 0,seen:new Map(Object.entries(e.definitions).map(([n,o])=>[o._def,{def:o._def,path:[...e.basePath,e.definitionPath,n],jsonSchema:void 0}]))}};function sq(t,e,r,n){n?.errorMessages&&r&&(t.errorMessage={...t.errorMessage,[e]:r})}function Jt(t,e,r,n,o){t[e]=r,sq(t,e,n,o)}var oO=(t,e)=>{let r=0;for(;rrt(t.innerType._def,e);function aq(t,e,r){let n=r??e.dateStrategy;if(Array.isArray(n))return{anyOf:n.map((o,i)=>aq(t,e,o))};switch(n){case"string":case"format:date-time":return{type:"string",format:"date-time"};case"format:date":return{type:"string",format:"date"};case"integer":return lrt(t,e)}}var lrt=(t,e)=>{let r={type:"integer",format:"unix-time"};if(e.target==="openApi3")return r;for(let n of t.checks)switch(n.kind){case"min":Jt(r,"minimum",n.value,n.message,e);break;case"max":Jt(r,"maximum",n.value,n.message,e);break}return r};function nue(t,e){return{...rt(t.innerType._def,e),default:t.defaultValue()}}function oue(t,e){return e.effectStrategy==="input"?rt(t.schema._def,e):tn(e)}function iue(t){return{type:"string",enum:Array.from(t.values)}}var prt=t=>"type"in t&&t.type==="string"?!1:"allOf"in t;function sue(t,e){let r=[rt(t.left._def,{...e,currentPath:[...e.currentPath,"allOf","0"]}),rt(t.right._def,{...e,currentPath:[...e.currentPath,"allOf","1"]})].filter(i=>!!i),n=e.target==="jsonSchema2019-09"?{unevaluatedProperties:!1}:void 0,o=[];return r.forEach(i=>{if(prt(i))o.push(...i.allOf),i.unevaluatedProperties===void 0&&(n=void 0);else{let s=i;if("additionalProperties"in i&&i.additionalProperties===!1){let{additionalProperties:a,...c}=i;s=c}else n=void 0;o.push(s)}}),o.length?{allOf:o,...n}:void 0}function aue(t,e){let r=typeof t.value;return r!=="bigint"&&r!=="number"&&r!=="boolean"&&r!=="string"?{type:Array.isArray(t.value)?"array":"object"}:e.target==="openApi3"?{type:r==="bigint"?"integer":r,enum:[t.value]}:{type:r==="bigint"?"integer":r,const:t.value}}var cq,ec={cuid:/^[cC][^\s-]{8,}$/,cuid2:/^[0-9a-z]+$/,ulid:/^[0-9A-HJKMNP-TV-Z]{26}$/,email:/^(?!\.)(?!.*\.\.)([a-zA-Z0-9_'+\-\.]*)[a-zA-Z0-9_+-]@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/,emoji:()=>(cq===void 0&&(cq=RegExp("^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$","u")),cq),uuid:/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/,ipv4:/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,ipv4Cidr:/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/,ipv6:/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/,ipv6Cidr:/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,base64:/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,base64url:/^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,nanoid:/^[a-zA-Z0-9_-]{21}$/,jwt:/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/};function sO(t,e){let r={type:"string"};if(t.checks)for(let n of t.checks)switch(n.kind){case"min":Jt(r,"minLength",typeof r.minLength=="number"?Math.max(r.minLength,n.value):n.value,n.message,e);break;case"max":Jt(r,"maxLength",typeof r.maxLength=="number"?Math.min(r.maxLength,n.value):n.value,n.message,e);break;case"email":switch(e.emailStrategy){case"format:email":tc(r,"email",n.message,e);break;case"format:idn-email":tc(r,"idn-email",n.message,e);break;case"pattern:zod":vi(r,ec.email,n.message,e);break}break;case"url":tc(r,"uri",n.message,e);break;case"uuid":tc(r,"uuid",n.message,e);break;case"regex":vi(r,n.regex,n.message,e);break;case"cuid":vi(r,ec.cuid,n.message,e);break;case"cuid2":vi(r,ec.cuid2,n.message,e);break;case"startsWith":vi(r,RegExp(`^${uq(n.value,e)}`),n.message,e);break;case"endsWith":vi(r,RegExp(`${uq(n.value,e)}$`),n.message,e);break;case"datetime":tc(r,"date-time",n.message,e);break;case"date":tc(r,"date",n.message,e);break;case"time":tc(r,"time",n.message,e);break;case"duration":tc(r,"duration",n.message,e);break;case"length":Jt(r,"minLength",typeof r.minLength=="number"?Math.max(r.minLength,n.value):n.value,n.message,e),Jt(r,"maxLength",typeof r.maxLength=="number"?Math.min(r.maxLength,n.value):n.value,n.message,e);break;case"includes":{vi(r,RegExp(uq(n.value,e)),n.message,e);break}case"ip":{n.version!=="v6"&&tc(r,"ipv4",n.message,e),n.version!=="v4"&&tc(r,"ipv6",n.message,e);break}case"base64url":vi(r,ec.base64url,n.message,e);break;case"jwt":vi(r,ec.jwt,n.message,e);break;case"cidr":{n.version!=="v6"&&vi(r,ec.ipv4Cidr,n.message,e),n.version!=="v4"&&vi(r,ec.ipv6Cidr,n.message,e);break}case"emoji":vi(r,ec.emoji(),n.message,e);break;case"ulid":{vi(r,ec.ulid,n.message,e);break}case"base64":{switch(e.base64Strategy){case"format:binary":{tc(r,"binary",n.message,e);break}case"contentEncoding:base64":{Jt(r,"contentEncoding","base64",n.message,e);break}case"pattern:zod":{vi(r,ec.base64,n.message,e);break}}break}case"nanoid":vi(r,ec.nanoid,n.message,e);case"toLowerCase":case"toUpperCase":case"trim":break;default:}return r}function uq(t,e){return e.patternStrategy==="escape"?frt(t):t}var drt=new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");function frt(t){let e="";for(let r=0;ro.format)?(t.anyOf||(t.anyOf=[]),t.format&&(t.anyOf.push({format:t.format,...t.errorMessage&&n.errorMessages&&{errorMessage:{format:t.errorMessage.format}}}),delete t.format,t.errorMessage&&(delete t.errorMessage.format,Object.keys(t.errorMessage).length===0&&delete t.errorMessage)),t.anyOf.push({format:e,...r&&n.errorMessages&&{errorMessage:{format:r}}})):Jt(t,"format",e,r,n)}function vi(t,e,r,n){t.pattern||t.allOf?.some(o=>o.pattern)?(t.allOf||(t.allOf=[]),t.pattern&&(t.allOf.push({pattern:t.pattern,...t.errorMessage&&n.errorMessages&&{errorMessage:{pattern:t.errorMessage.pattern}}}),delete t.pattern,t.errorMessage&&(delete t.errorMessage.pattern,Object.keys(t.errorMessage).length===0&&delete t.errorMessage)),t.allOf.push({pattern:cue(e,n),...r&&n.errorMessages&&{errorMessage:{pattern:r}}})):Jt(t,"pattern",cue(e,n),r,n)}function cue(t,e){if(!e.applyRegexFlags||!t.flags)return t.source;let r={i:t.flags.includes("i"),m:t.flags.includes("m"),s:t.flags.includes("s")},n=r.i?t.source.toLowerCase():t.source,o="",i=!1,s=!1,a=!1;for(let c=0;c({...n,[o]:rt(t.valueType._def,{...e,currentPath:[...e.currentPath,"properties",o]})??tn(e)}),{}),additionalProperties:e.rejectedAdditionalProperties};let r={type:"object",additionalProperties:rt(t.valueType._def,{...e,currentPath:[...e.currentPath,"additionalProperties"]})??e.allowedAdditionalProperties};if(e.target==="openApi3")return r;if(t.keyType?._def.typeName===ie.ZodString&&t.keyType._def.checks?.length){let{type:n,...o}=sO(t.keyType._def,e);return{...r,propertyNames:o}}else{if(t.keyType?._def.typeName===ie.ZodEnum)return{...r,propertyNames:{enum:t.keyType._def.values}};if(t.keyType?._def.typeName===ie.ZodBranded&&t.keyType._def.type._def.typeName===ie.ZodString&&t.keyType._def.type._def.checks?.length){let{type:n,...o}=iO(t.keyType._def,e);return{...r,propertyNames:o}}}return r}function uue(t,e){if(e.mapStrategy==="record")return aO(t,e);let r=rt(t.keyType._def,{...e,currentPath:[...e.currentPath,"items","items","0"]})||tn(e),n=rt(t.valueType._def,{...e,currentPath:[...e.currentPath,"items","items","1"]})||tn(e);return{type:"array",maxItems:125,items:{type:"array",items:[r,n],minItems:2,maxItems:2}}}function lue(t){let e=t.values,n=Object.keys(t.values).filter(i=>typeof e[e[i]]!="number").map(i=>e[i]),o=Array.from(new Set(n.map(i=>typeof i)));return{type:o.length===1?o[0]==="string"?"string":"number":["string","number"],enum:n}}function pue(t){return t.target==="openAi"?void 0:{not:tn({...t,currentPath:[...t.currentPath,"not"]})}}function due(t){return t.target==="openApi3"?{enum:["null"],nullable:!0}:{type:"null"}}var aT={ZodString:"string",ZodNumber:"number",ZodBigInt:"integer",ZodBoolean:"boolean",ZodNull:"null"};function mue(t,e){if(e.target==="openApi3")return fue(t,e);let r=t.options instanceof Map?Array.from(t.options.values()):t.options;if(r.every(n=>n._def.typeName in aT&&(!n._def.checks||!n._def.checks.length))){let n=r.reduce((o,i)=>{let s=aT[i._def.typeName];return s&&!o.includes(s)?[...o,s]:o},[]);return{type:n.length>1?n:n[0]}}else if(r.every(n=>n._def.typeName==="ZodLiteral"&&!n.description)){let n=r.reduce((o,i)=>{let s=typeof i._def.value;switch(s){case"string":case"number":case"boolean":return[...o,s];case"bigint":return[...o,"integer"];case"object":if(i._def.value===null)return[...o,"null"];case"symbol":case"undefined":case"function":default:return o}},[]);if(n.length===r.length){let o=n.filter((i,s,a)=>a.indexOf(i)===s);return{type:o.length>1?o:o[0],enum:r.reduce((i,s)=>i.includes(s._def.value)?i:[...i,s._def.value],[])}}}else if(r.every(n=>n._def.typeName==="ZodEnum"))return{type:"string",enum:r.reduce((n,o)=>[...n,...o._def.values.filter(i=>!n.includes(i))],[])};return fue(t,e)}var fue=(t,e)=>{let r=(t.options instanceof Map?Array.from(t.options.values()):t.options).map((n,o)=>rt(n._def,{...e,currentPath:[...e.currentPath,"anyOf",`${o}`]})).filter(n=>!!n&&(!e.strictUnions||typeof n=="object"&&Object.keys(n).length>0));return r.length?{anyOf:r}:void 0};function hue(t,e){if(["ZodString","ZodNumber","ZodBigInt","ZodBoolean","ZodNull"].includes(t.innerType._def.typeName)&&(!t.innerType._def.checks||!t.innerType._def.checks.length))return e.target==="openApi3"?{type:aT[t.innerType._def.typeName],nullable:!0}:{type:[aT[t.innerType._def.typeName],"null"]};if(e.target==="openApi3"){let n=rt(t.innerType._def,{...e,currentPath:[...e.currentPath]});return n&&"$ref"in n?{allOf:[n],nullable:!0}:n&&{...n,nullable:!0}}let r=rt(t.innerType._def,{...e,currentPath:[...e.currentPath,"anyOf","0"]});return r&&{anyOf:[r,{type:"null"}]}}function gue(t,e){let r={type:"number"};if(!t.checks)return r;for(let n of t.checks)switch(n.kind){case"int":r.type="integer",sq(r,"type",n.message,e);break;case"min":e.target==="jsonSchema7"?n.inclusive?Jt(r,"minimum",n.value,n.message,e):Jt(r,"exclusiveMinimum",n.value,n.message,e):(n.inclusive||(r.exclusiveMinimum=!0),Jt(r,"minimum",n.value,n.message,e));break;case"max":e.target==="jsonSchema7"?n.inclusive?Jt(r,"maximum",n.value,n.message,e):Jt(r,"exclusiveMaximum",n.value,n.message,e):(n.inclusive||(r.exclusiveMaximum=!0),Jt(r,"maximum",n.value,n.message,e));break;case"multipleOf":Jt(r,"multipleOf",n.value,n.message,e);break}return r}function _ue(t,e){let r=e.target==="openAi",n={type:"object",properties:{}},o=[],i=t.shape();for(let a in i){let c=i[a];if(c===void 0||c._def===void 0)continue;let u=hrt(c);u&&r&&(c._def.typeName==="ZodOptional"&&(c=c._def.innerType),c.isNullable()||(c=c.nullable()),u=!1);let p=rt(c._def,{...e,currentPath:[...e.currentPath,"properties",a],propertyPath:[...e.currentPath,"properties",a]});p!==void 0&&(n.properties[a]=p,u||o.push(a))}o.length&&(n.required=o);let s=mrt(t,e);return s!==void 0&&(n.additionalProperties=s),n}function mrt(t,e){if(t.catchall._def.typeName!=="ZodNever")return rt(t.catchall._def,{...e,currentPath:[...e.currentPath,"additionalProperties"]});switch(t.unknownKeys){case"passthrough":return e.allowedAdditionalProperties;case"strict":return e.rejectedAdditionalProperties;case"strip":return e.removeAdditionalStrategy==="strict"?e.allowedAdditionalProperties:e.rejectedAdditionalProperties}}function hrt(t){try{return t.isOptional()}catch{return!0}}var vue=(t,e)=>{if(e.currentPath.toString()===e.propertyPath?.toString())return rt(t.innerType._def,e);let r=rt(t.innerType._def,{...e,currentPath:[...e.currentPath,"anyOf","1"]});return r?{anyOf:[{not:tn(e)},r]}:tn(e)};var Sue=(t,e)=>{if(e.pipeStrategy==="input")return rt(t.in._def,e);if(e.pipeStrategy==="output")return rt(t.out._def,e);let r=rt(t.in._def,{...e,currentPath:[...e.currentPath,"allOf","0"]}),n=rt(t.out._def,{...e,currentPath:[...e.currentPath,"allOf",r?"1":"0"]});return{allOf:[r,n].filter(o=>o!==void 0)}};function yue(t,e){return rt(t.type._def,e)}function Eue(t,e){let n={type:"array",uniqueItems:!0,items:rt(t.valueType._def,{...e,currentPath:[...e.currentPath,"items"]})};return t.minSize&&Jt(n,"minItems",t.minSize.value,t.minSize.message,e),t.maxSize&&Jt(n,"maxItems",t.maxSize.value,t.maxSize.message,e),n}function Tue(t,e){return t.rest?{type:"array",minItems:t.items.length,items:t.items.map((r,n)=>rt(r._def,{...e,currentPath:[...e.currentPath,"items",`${n}`]})).reduce((r,n)=>n===void 0?r:[...r,n],[]),additionalItems:rt(t.rest._def,{...e,currentPath:[...e.currentPath,"additionalItems"]})}:{type:"array",minItems:t.items.length,maxItems:t.items.length,items:t.items.map((r,n)=>rt(r._def,{...e,currentPath:[...e.currentPath,"items",`${n}`]})).reduce((r,n)=>n===void 0?r:[...r,n],[])}}function bue(t){return{not:tn(t)}}function xue(t){return tn(t)}var Aue=(t,e)=>rt(t.innerType._def,e);var wue=(t,e,r)=>{switch(e){case ie.ZodString:return sO(t,r);case ie.ZodNumber:return gue(t,r);case ie.ZodObject:return _ue(t,r);case ie.ZodBigInt:return eue(t,r);case ie.ZodBoolean:return tue();case ie.ZodDate:return aq(t,r);case ie.ZodUndefined:return bue(r);case ie.ZodNull:return due(r);case ie.ZodArray:return Qce(t,r);case ie.ZodUnion:case ie.ZodDiscriminatedUnion:return mue(t,r);case ie.ZodIntersection:return sue(t,r);case ie.ZodTuple:return Tue(t,r);case ie.ZodRecord:return aO(t,r);case ie.ZodLiteral:return aue(t,r);case ie.ZodEnum:return iue(t);case ie.ZodNativeEnum:return lue(t);case ie.ZodNullable:return hue(t,r);case ie.ZodOptional:return vue(t,r);case ie.ZodMap:return uue(t,r);case ie.ZodSet:return Eue(t,r);case ie.ZodLazy:return()=>t.getter()._def;case ie.ZodPromise:return yue(t,r);case ie.ZodNaN:case ie.ZodNever:return pue(r);case ie.ZodEffects:return oue(t,r);case ie.ZodAny:return tn(r);case ie.ZodUnknown:return xue(r);case ie.ZodDefault:return nue(t,r);case ie.ZodBranded:return iO(t,r);case ie.ZodReadonly:return Aue(t,r);case ie.ZodCatch:return rue(t,r);case ie.ZodPipeline:return Sue(t,r);case ie.ZodFunction:case ie.ZodVoid:case ie.ZodSymbol:return;default:return(n=>{})(e)}};function rt(t,e,r=!1){let n=e.seen.get(t);if(e.override){let a=e.override?.(t,e,n,r);if(a!==Yce)return a}if(n&&!r){let a=grt(n,e);if(a!==void 0)return a}let o={def:t,path:e.currentPath,jsonSchema:void 0};e.seen.set(t,o);let i=wue(t,t.typeName,e),s=typeof i=="function"?rt(i(),e):i;if(s&&_rt(t,e,s),e.postProcess){let a=e.postProcess(s,t,e);return o.jsonSchema=s,a}return o.jsonSchema=s,s}var grt=(t,e)=>{switch(e.$refStrategy){case"root":return{$ref:t.path.join("/")};case"relative":return{$ref:oO(e.currentPath,t.path)};case"none":case"seen":return t.path.lengthe.currentPath[n]===r)?(console.warn(`Recursive reference detected at ${e.currentPath.join("/")}! Defaulting to any`),tn(e)):e.$refStrategy==="seen"?tn(e):void 0}},_rt=(t,e,r)=>(t.description&&(r.description=t.description,e.markdownDescription&&(r.markdownDescription=t.description)),r);var lq=(t,e)=>{let r=Xce(e),n=typeof e=="object"&&e.definitions?Object.entries(e.definitions).reduce((c,[u,p])=>({...c,[u]:rt(p._def,{...r,currentPath:[...r.basePath,r.definitionPath,u]},!0)??tn(r)}),{}):void 0,o=typeof e=="string"?e:e?.nameStrategy==="title"?void 0:e?.name,i=rt(t._def,o===void 0?r:{...r,currentPath:[...r.basePath,r.definitionPath,o]},!1)??tn(r),s=typeof e=="object"&&e.name!==void 0&&e.nameStrategy==="title"?e.name:void 0;s!==void 0&&(i.title=s),r.flags.hasReferencedOpenAiAnyType&&(n||(n={}),n[r.openAiAnyTypeName]||(n[r.openAiAnyTypeName]={type:["string","number","integer","boolean","array","null"],items:{$ref:r.$refStrategy==="relative"?"1":[...r.basePath,r.definitionPath,r.openAiAnyTypeName].join("/")}}));let a=o===void 0?n?{...i,[r.definitionPath]:n}:i:{$ref:[...r.$refStrategy==="relative"?[]:r.basePath,r.definitionPath,o].join("/"),[r.definitionPath]:{...n,[o]:i}};return r.target==="jsonSchema7"?a.$schema="http://json-schema.org/draft-07/schema#":(r.target==="jsonSchema2019-09"||r.target==="openAi")&&(a.$schema="https://json-schema.org/draft/2019-09/schema#"),r.target==="openAi"&&("anyOf"in a||"oneOf"in a||"allOf"in a||"type"in a&&Array.isArray(a.type))&&console.warn("Warning: OpenAI may not support schemas with unions as roots! Try wrapping it in an object property."),a};function vrt(t){return!t||t==="jsonSchema7"||t==="draft-7"?"draft-7":t==="jsonSchema2019-09"||t==="draft-2020-12"?"draft-2020-12":"draft-7"}function pq(t,e){return Qs(t)?gi(t,{target:vrt(e?.target),io:e?.pipeStrategy??"input"}):lq(t,{strictUnions:e?.strictUnions??!0,pipeStrategy:e?.pipeStrategy??"input"})}function dq(t){let r=Rp(t)?.method;if(!r)throw new Error("Schema is missing a method literal");let n=nO(r);if(typeof n!="string")throw new Error("Schema method literal must be a string");return n}function fq(t,e){let r=wp(t,e);if(!r.success)throw r.error;return r.data}var Srt=6e4,cO=class{constructor(e){this._options=e,this._requestMessageId=0,this._requestHandlers=new Map,this._requestHandlerAbortControllers=new Map,this._notificationHandlers=new Map,this._responseHandlers=new Map,this._progressHandlers=new Map,this._timeoutInfo=new Map,this._pendingDebouncedNotifications=new Set,this._taskProgressTokens=new Map,this._requestResolvers=new Map,this.setNotificationHandler(hI,r=>{this._oncancel(r)}),this.setNotificationHandler(vI,r=>{this._onprogress(r)}),this.setRequestHandler(_I,r=>({})),this._taskStore=e?.taskStore,this._taskMessageQueue=e?.taskMessageQueue,this._taskStore&&(this.setRequestHandler(SI,async(r,n)=>{let o=await this._taskStore.getTask(r.params.taskId,n.sessionId);if(!o)throw new Ue(We.InvalidParams,"Failed to retrieve task: Task not found");return{...o}}),this.setRequestHandler(EI,async(r,n)=>{let o=async()=>{let i=r.params.taskId;if(this._taskMessageQueue){let a;for(;a=await this._taskMessageQueue.dequeue(i,n.sessionId);){if(a.type==="response"||a.type==="error"){let c=a.message,u=c.id,p=this._requestResolvers.get(u);if(p)if(this._requestResolvers.delete(u),a.type==="response")p(c);else{let f=c,m=new Ue(f.error.code,f.error.message,f.error.data);p(m)}else{let f=a.type==="response"?"Response":"Error";this._onerror(new Error(`${f} handler missing for request ${u}`))}continue}await this._transport?.send(a.message,{relatedRequestId:n.requestId})}}let s=await this._taskStore.getTask(i,n.sessionId);if(!s)throw new Ue(We.InvalidParams,`Task not found: ${i}`);if(!Pp(s.status))return await this._waitForTaskUpdate(i,n.signal),await o();if(Pp(s.status)){let a=await this._taskStore.getTaskResult(i,n.sessionId);return this._clearTaskQueue(i),{...a,_meta:{...a._meta,[_p]:{taskId:i}}}}return await o()};return await o()}),this.setRequestHandler(TI,async(r,n)=>{try{let{tasks:o,nextCursor:i}=await this._taskStore.listTasks(r.params?.cursor,n.sessionId);return{tasks:o,nextCursor:i,_meta:{}}}catch(o){throw new Ue(We.InvalidParams,`Failed to list tasks: ${o instanceof Error?o.message:String(o)}`)}}),this.setRequestHandler(xI,async(r,n)=>{try{let o=await this._taskStore.getTask(r.params.taskId,n.sessionId);if(!o)throw new Ue(We.InvalidParams,`Task not found: ${r.params.taskId}`);if(Pp(o.status))throw new Ue(We.InvalidParams,`Cannot cancel task in terminal status: ${o.status}`);await this._taskStore.updateTaskStatus(r.params.taskId,"cancelled","Client cancelled task execution.",n.sessionId),this._clearTaskQueue(r.params.taskId);let i=await this._taskStore.getTask(r.params.taskId,n.sessionId);if(!i)throw new Ue(We.InvalidParams,`Task not found after cancellation: ${r.params.taskId}`);return{_meta:{},...i}}catch(o){throw o instanceof Ue?o:new Ue(We.InvalidRequest,`Failed to cancel task: ${o instanceof Error?o.message:String(o)}`)}}))}async _oncancel(e){if(!e.params.requestId)return;this._requestHandlerAbortControllers.get(e.params.requestId)?.abort(e.params.reason)}_setupTimeout(e,r,n,o,i=!1){this._timeoutInfo.set(e,{timeoutId:setTimeout(o,r),startTime:Date.now(),timeout:r,maxTotalTimeout:n,resetTimeoutOnProgress:i,onTimeout:o})}_resetTimeout(e){let r=this._timeoutInfo.get(e);if(!r)return!1;let n=Date.now()-r.startTime;if(r.maxTotalTimeout&&n>=r.maxTotalTimeout)throw this._timeoutInfo.delete(e),Ue.fromError(We.RequestTimeout,"Maximum total timeout exceeded",{maxTotalTimeout:r.maxTotalTimeout,totalElapsed:n});return clearTimeout(r.timeoutId),r.timeoutId=setTimeout(r.onTimeout,r.timeout),!0}_cleanupTimeout(e){let r=this._timeoutInfo.get(e);r&&(clearTimeout(r.timeoutId),this._timeoutInfo.delete(e))}async connect(e){this._transport=e;let r=this.transport?.onclose;this._transport.onclose=()=>{r?.(),this._onclose()};let n=this.transport?.onerror;this._transport.onerror=i=>{n?.(i),this._onerror(i)};let o=this._transport?.onmessage;this._transport.onmessage=(i,s)=>{o?.(i,s),Ju(i)||Wh(i)?this._onresponse(i):vp(i)?this._onrequest(i,s):tre(i)?this._onnotification(i):this._onerror(new Error(`Unknown message type: ${JSON.stringify(i)}`))},await this._transport.start()}_onclose(){let e=this._responseHandlers;this._responseHandlers=new Map,this._progressHandlers.clear(),this._taskProgressTokens.clear(),this._pendingDebouncedNotifications.clear();let r=Ue.fromError(We.ConnectionClosed,"Connection closed");this._transport=void 0,this.onclose?.();for(let n of e.values())n(r)}_onerror(e){this.onerror?.(e)}_onnotification(e){let r=this._notificationHandlers.get(e.method)??this.fallbackNotificationHandler;r!==void 0&&Promise.resolve().then(()=>r(e)).catch(n=>this._onerror(new Error(`Uncaught error in notification handler: ${n}`)))}_onrequest(e,r){let n=this._requestHandlers.get(e.method)??this.fallbackRequestHandler,o=this._transport,i=e.params?._meta?.[_p]?.taskId;if(n===void 0){let p={jsonrpc:"2.0",id:e.id,error:{code:We.MethodNotFound,message:"Method not found"}};i&&this._taskMessageQueue?this._enqueueTaskMessage(i,{type:"error",message:p,timestamp:Date.now()},o?.sessionId).catch(f=>this._onerror(new Error(`Failed to enqueue error response: ${f}`))):o?.send(p).catch(f=>this._onerror(new Error(`Failed to send an error response: ${f}`)));return}let s=new AbortController;this._requestHandlerAbortControllers.set(e.id,s);let a=Xte(e.params)?e.params.task:void 0,c=this._taskStore?this.requestTaskStore(e,o?.sessionId):void 0,u={signal:s.signal,sessionId:o?.sessionId,_meta:e.params?._meta,sendNotification:async p=>{let f={relatedRequestId:e.id};i&&(f.relatedTask={taskId:i}),await this.notification(p,f)},sendRequest:async(p,f,m)=>{let h={...m,relatedRequestId:e.id};i&&!h.relatedTask&&(h.relatedTask={taskId:i});let _=h.relatedTask?.taskId??i;return _&&c&&await c.updateTaskStatus(_,"input_required"),await this.request(p,f,h)},authInfo:r?.authInfo,requestId:e.id,requestInfo:r?.requestInfo,taskId:i,taskStore:c,taskRequestedTtl:a?.ttl,closeSSEStream:r?.closeSSEStream,closeStandaloneSSEStream:r?.closeStandaloneSSEStream};Promise.resolve().then(()=>{a&&this.assertTaskHandlerCapability(e.method)}).then(()=>n(e,u)).then(async p=>{if(s.signal.aborted)return;let f={result:p,jsonrpc:"2.0",id:e.id};i&&this._taskMessageQueue?await this._enqueueTaskMessage(i,{type:"response",message:f,timestamp:Date.now()},o?.sessionId):await o?.send(f)},async p=>{if(s.signal.aborted)return;let f={jsonrpc:"2.0",id:e.id,error:{code:Number.isSafeInteger(p.code)?p.code:We.InternalError,message:p.message??"Internal error",...p.data!==void 0&&{data:p.data}}};i&&this._taskMessageQueue?await this._enqueueTaskMessage(i,{type:"error",message:f,timestamp:Date.now()},o?.sessionId):await o?.send(f)}).catch(p=>this._onerror(new Error(`Failed to send response: ${p}`))).finally(()=>{this._requestHandlerAbortControllers.delete(e.id)})}_onprogress(e){let{progressToken:r,...n}=e.params,o=Number(r),i=this._progressHandlers.get(o);if(!i){this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(e)}`));return}let s=this._responseHandlers.get(o),a=this._timeoutInfo.get(o);if(a&&s&&a.resetTimeoutOnProgress)try{this._resetTimeout(o)}catch(c){this._responseHandlers.delete(o),this._progressHandlers.delete(o),this._cleanupTimeout(o),s(c);return}i(n)}_onresponse(e){let r=Number(e.id),n=this._requestResolvers.get(r);if(n){if(this._requestResolvers.delete(r),Ju(e))n(e);else{let s=new Ue(e.error.code,e.error.message,e.error.data);n(s)}return}let o=this._responseHandlers.get(r);if(o===void 0){this._onerror(new Error(`Received a response for an unknown message ID: ${JSON.stringify(e)}`));return}this._responseHandlers.delete(r),this._cleanupTimeout(r);let i=!1;if(Ju(e)&&e.result&&typeof e.result=="object"){let s=e.result;if(s.task&&typeof s.task=="object"){let a=s.task;typeof a.taskId=="string"&&(i=!0,this._taskProgressTokens.set(a.taskId,r))}}if(i||this._progressHandlers.delete(r),Ju(e))o(e);else{let s=Ue.fromError(e.error.code,e.error.message,e.error.data);o(s)}}get transport(){return this._transport}async close(){await this._transport?.close()}async*requestStream(e,r,n){let{task:o}=n??{};if(!o){try{yield{type:"result",result:await this.request(e,r,n)}}catch(s){yield{type:"error",error:s instanceof Ue?s:new Ue(We.InternalError,String(s))}}return}let i;try{let s=await this.request(e,Kh,n);if(s.task)i=s.task.taskId,yield{type:"taskCreated",task:s.task};else throw new Ue(We.InternalError,"Task creation did not return a task");for(;;){let a=await this.getTask({taskId:i},n);if(yield{type:"taskStatus",task:a},Pp(a.status)){a.status==="completed"?yield{type:"result",result:await this.getTaskResult({taskId:i},r,n)}:a.status==="failed"?yield{type:"error",error:new Ue(We.InternalError,`Task ${i} failed`)}:a.status==="cancelled"&&(yield{type:"error",error:new Ue(We.InternalError,`Task ${i} was cancelled`)});return}if(a.status==="input_required"){yield{type:"result",result:await this.getTaskResult({taskId:i},r,n)};return}let c=a.pollInterval??this._options?.defaultTaskPollInterval??1e3;await new Promise(u=>setTimeout(u,c)),n?.signal?.throwIfAborted()}}catch(s){yield{type:"error",error:s instanceof Ue?s:new Ue(We.InternalError,String(s))}}}request(e,r,n){let{relatedRequestId:o,resumptionToken:i,onresumptiontoken:s,task:a,relatedTask:c}=n??{};return new Promise((u,p)=>{let f=w=>{p(w)};if(!this._transport){f(new Error("Not connected"));return}if(this._options?.enforceStrictCapabilities===!0)try{this.assertCapabilityForMethod(e.method),a&&this.assertTaskCapability(e.method)}catch(w){f(w);return}n?.signal?.throwIfAborted();let m=this._requestMessageId++,h={...e,jsonrpc:"2.0",id:m};n?.onprogress&&(this._progressHandlers.set(m,n.onprogress),h.params={...e.params,_meta:{...e.params?._meta||{},progressToken:m}}),a&&(h.params={...h.params,task:a}),c&&(h.params={...h.params,_meta:{...h.params?._meta||{},[_p]:c}});let _=w=>{this._responseHandlers.delete(m),this._progressHandlers.delete(m),this._cleanupTimeout(m),this._transport?.send({jsonrpc:"2.0",method:"notifications/cancelled",params:{requestId:m,reason:String(w)}},{relatedRequestId:o,resumptionToken:i,onresumptiontoken:s}).catch(N=>this._onerror(new Error(`Failed to send cancellation: ${N}`)));let I=w instanceof Ue?w:new Ue(We.RequestTimeout,String(w));p(I)};this._responseHandlers.set(m,w=>{if(!n?.signal?.aborted){if(w instanceof Error)return p(w);try{let I=wp(r,w.result);I.success?u(I.data):p(I.error)}catch(I){p(I)}}}),n?.signal?.addEventListener("abort",()=>{_(n?.signal?.reason)});let v=n?.timeout??Srt,E=()=>_(Ue.fromError(We.RequestTimeout,"Request timed out",{timeout:v}));this._setupTimeout(m,v,n?.maxTotalTimeout,E,n?.resetTimeoutOnProgress??!1);let x=c?.taskId;if(x){let w=I=>{let N=this._responseHandlers.get(m);N?N(I):this._onerror(new Error(`Response handler missing for side-channeled request ${m}`))};this._requestResolvers.set(m,w),this._enqueueTaskMessage(x,{type:"request",message:h,timestamp:Date.now()}).catch(I=>{this._cleanupTimeout(m),p(I)})}else this._transport.send(h,{relatedRequestId:o,resumptionToken:i,onresumptiontoken:s}).catch(w=>{this._cleanupTimeout(m),p(w)})})}async getTask(e,r){return this.request({method:"tasks/get",params:e},yI,r)}async getTaskResult(e,r,n){return this.request({method:"tasks/result",params:e},r,n)}async listTasks(e,r){return this.request({method:"tasks/list",params:e},bI,r)}async cancelTask(e,r){return this.request({method:"tasks/cancel",params:e},nre,r)}async notification(e,r){if(!this._transport)throw new Error("Not connected");this.assertNotificationCapability(e.method);let n=r?.relatedTask?.taskId;if(n){let a={...e,jsonrpc:"2.0",params:{...e.params,_meta:{...e.params?._meta||{},[_p]:r.relatedTask}}};await this._enqueueTaskMessage(n,{type:"notification",message:a,timestamp:Date.now()});return}if((this._options?.debouncedNotificationMethods??[]).includes(e.method)&&!e.params&&!r?.relatedRequestId&&!r?.relatedTask){if(this._pendingDebouncedNotifications.has(e.method))return;this._pendingDebouncedNotifications.add(e.method),Promise.resolve().then(()=>{if(this._pendingDebouncedNotifications.delete(e.method),!this._transport)return;let a={...e,jsonrpc:"2.0"};r?.relatedTask&&(a={...a,params:{...a.params,_meta:{...a.params?._meta||{},[_p]:r.relatedTask}}}),this._transport?.send(a,r).catch(c=>this._onerror(c))});return}let s={...e,jsonrpc:"2.0"};r?.relatedTask&&(s={...s,params:{...s.params,_meta:{...s.params?._meta||{},[_p]:r.relatedTask}}}),await this._transport.send(s,r)}setRequestHandler(e,r){let n=dq(e);this.assertRequestHandlerCapability(n),this._requestHandlers.set(n,(o,i)=>{let s=fq(e,o);return Promise.resolve(r(s,i))})}removeRequestHandler(e){this._requestHandlers.delete(e)}assertCanSetRequestHandler(e){if(this._requestHandlers.has(e))throw new Error(`A request handler for ${e} already exists, which would be overridden`)}setNotificationHandler(e,r){let n=dq(e);this._notificationHandlers.set(n,o=>{let i=fq(e,o);return Promise.resolve(r(i))})}removeNotificationHandler(e){this._notificationHandlers.delete(e)}_cleanupTaskProgressHandler(e){let r=this._taskProgressTokens.get(e);r!==void 0&&(this._progressHandlers.delete(r),this._taskProgressTokens.delete(e))}async _enqueueTaskMessage(e,r,n){if(!this._taskStore||!this._taskMessageQueue)throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");let o=this._options?.maxTaskQueueSize;await this._taskMessageQueue.enqueue(e,r,n,o)}async _clearTaskQueue(e,r){if(this._taskMessageQueue){let n=await this._taskMessageQueue.dequeueAll(e,r);for(let o of n)if(o.type==="request"&&vp(o.message)){let i=o.message.id,s=this._requestResolvers.get(i);s?(s(new Ue(We.InternalError,"Task cancelled or completed")),this._requestResolvers.delete(i)):this._onerror(new Error(`Resolver missing for request ${i} during task ${e} cleanup`))}}}async _waitForTaskUpdate(e,r){let n=this._options?.defaultTaskPollInterval??1e3;try{let o=await this._taskStore?.getTask(e);o?.pollInterval&&(n=o.pollInterval)}catch{}return new Promise((o,i)=>{if(r.aborted){i(new Ue(We.InvalidRequest,"Request cancelled"));return}let s=setTimeout(o,n);r.addEventListener("abort",()=>{clearTimeout(s),i(new Ue(We.InvalidRequest,"Request cancelled"))},{once:!0})})}requestTaskStore(e,r){let n=this._taskStore;if(!n)throw new Error("No task store configured");return{createTask:async o=>{if(!e)throw new Error("No request provided");return await n.createTask(o,e.id,{method:e.method,params:e.params},r)},getTask:async o=>{let i=await n.getTask(o,r);if(!i)throw new Ue(We.InvalidParams,"Failed to retrieve task: Task not found");return i},storeTaskResult:async(o,i,s)=>{await n.storeTaskResult(o,i,s,r);let a=await n.getTask(o,r);if(a){let c=SE.parse({method:"notifications/tasks/status",params:a});await this.notification(c),Pp(a.status)&&this._cleanupTaskProgressHandler(o)}},getTaskResult:o=>n.getTaskResult(o,r),updateTaskStatus:async(o,i,s)=>{let a=await n.getTask(o,r);if(!a)throw new Ue(We.InvalidParams,`Task "${o}" not found - it may have been cleaned up`);if(Pp(a.status))throw new Ue(We.InvalidParams,`Cannot update task "${o}" from terminal status "${a.status}" to "${i}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);await n.updateTaskStatus(o,i,s,r);let c=await n.getTask(o,r);if(c){let u=SE.parse({method:"notifications/tasks/status",params:c});await this.notification(u),Pp(c.status)&&this._cleanupTaskProgressHandler(o)}},listTasks:o=>n.listTasks(o,r)}}};function Rue(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function Pue(t,e){let r={...t};for(let n in e){let o=n,i=e[o];if(i===void 0)continue;let s=r[o];Rue(s)&&Rue(i)?r[o]={...s,...i}:r[o]=i}return r}var Pme=W(ede(),1),Ime=W(Rme(),1);function nft(){let t=new Pme.default({strict:!1,validateFormats:!0,validateSchema:!1,allErrors:!0});return(0,Ime.default)(t),t}var TN=class{constructor(e){this._ajv=e??nft()}getValidator(e){let r="$id"in e&&typeof e.$id=="string"?this._ajv.getSchema(e.$id)??this._ajv.compile(e):this._ajv.compile(e);return n=>r(n)?{valid:!0,data:n,errorMessage:void 0}:{valid:!1,data:void 0,errorMessage:this._ajv.errorsText(r.errors)}}};var bN=class{constructor(e){this._server=e}requestStream(e,r,n){return this._server.requestStream(e,r,n)}async getTask(e,r){return this._server.getTask({taskId:e},r)}async getTaskResult(e,r,n){return this._server.getTaskResult({taskId:e},r,n)}async listTasks(e,r){return this._server.listTasks(e?{cursor:e}:void 0,r)}async cancelTask(e,r){return this._server.cancelTask({taskId:e},r)}};function Ome(t,e,r){if(!t)throw new Error(`${r} does not support task creation (required for ${e})`);switch(e){case"tools/call":if(!t.tools?.call)throw new Error(`${r} does not support task creation for tools/call (required for ${e})`);break;default:break}}function Nme(t,e,r){if(!t)throw new Error(`${r} does not support task creation (required for ${e})`);switch(e){case"sampling/createMessage":if(!t.sampling?.createMessage)throw new Error(`${r} does not support task creation for sampling/createMessage (required for ${e})`);break;case"elicitation/create":if(!t.elicitation?.create)throw new Error(`${r} does not support task creation for elicitation/create (required for ${e})`);break;default:break}}var xN=class extends cO{constructor(e,r){super(r),this._serverInfo=e,this._loggingLevels=new Map,this.LOG_LEVEL_SEVERITY=new Map(EE.options.map((n,o)=>[n,o])),this.isMessageIgnored=(n,o)=>{let i=this._loggingLevels.get(o);return i?this.LOG_LEVEL_SEVERITY.get(n)this._oninitialize(n)),this.setNotificationHandler(n2,()=>this.oninitialized?.()),this._capabilities.logging&&this.setRequestHandler(TE,async(n,o)=>{let i=o.sessionId||o.requestInfo?.headers["mcp-session-id"]||void 0,{level:s}=n.params,a=EE.safeParse(s);return a.success&&this._loggingLevels.set(i,a.data),{}})}get experimental(){return this._experimental||(this._experimental={tasks:new bN(this)}),this._experimental}registerCapabilities(e){if(this.transport)throw new Error("Cannot register capabilities after connecting to transport");this._capabilities=Pue(this._capabilities,e)}setRequestHandler(e,r){let o=Rp(e)?.method;if(!o)throw new Error("Schema is missing a method literal");let i;if(Qs(o)){let a=o;i=a._zod?.def?.value??a.value}else{let a=o;i=a._def?.value??a.value}if(typeof i!="string")throw new Error("Schema method literal must be a string");if(i==="tools/call"){let a=async(c,u)=>{let p=wp(Yh,c);if(!p.success){let _=p.error instanceof Error?p.error.message:String(p.error);throw new Ue(We.InvalidParams,`Invalid tools/call request: ${_}`)}let{params:f}=p.data,m=await Promise.resolve(r(c,u));if(f.task){let _=wp(Kh,m);if(!_.success){let v=_.error instanceof Error?_.error.message:String(_.error);throw new Ue(We.InvalidParams,`Invalid task creation result: ${v}`)}return _.data}let h=wp(NI,m);if(!h.success){let _=h.error instanceof Error?h.error.message:String(h.error);throw new Ue(We.InvalidParams,`Invalid tools/call result: ${_}`)}return h.data};return super.setRequestHandler(e,a)}return super.setRequestHandler(e,r)}assertCapabilityForMethod(e){switch(e){case"sampling/createMessage":if(!this._clientCapabilities?.sampling)throw new Error(`Client does not support sampling (required for ${e})`);break;case"elicitation/create":if(!this._clientCapabilities?.elicitation)throw new Error(`Client does not support elicitation (required for ${e})`);break;case"roots/list":if(!this._clientCapabilities?.roots)throw new Error(`Client does not support listing roots (required for ${e})`);break;case"ping":break}}assertNotificationCapability(e){switch(e){case"notifications/message":if(!this._capabilities.logging)throw new Error(`Server does not support logging (required for ${e})`);break;case"notifications/resources/updated":case"notifications/resources/list_changed":if(!this._capabilities.resources)throw new Error(`Server does not support notifying about resources (required for ${e})`);break;case"notifications/tools/list_changed":if(!this._capabilities.tools)throw new Error(`Server does not support notifying of tool list changes (required for ${e})`);break;case"notifications/prompts/list_changed":if(!this._capabilities.prompts)throw new Error(`Server does not support notifying of prompt list changes (required for ${e})`);break;case"notifications/elicitation/complete":if(!this._clientCapabilities?.elicitation?.url)throw new Error(`Client does not support URL elicitation (required for ${e})`);break;case"notifications/cancelled":break;case"notifications/progress":break}}assertRequestHandlerCapability(e){if(this._capabilities)switch(e){case"completion/complete":if(!this._capabilities.completions)throw new Error(`Server does not support completions (required for ${e})`);break;case"logging/setLevel":if(!this._capabilities.logging)throw new Error(`Server does not support logging (required for ${e})`);break;case"prompts/get":case"prompts/list":if(!this._capabilities.prompts)throw new Error(`Server does not support prompts (required for ${e})`);break;case"resources/list":case"resources/templates/list":case"resources/read":if(!this._capabilities.resources)throw new Error(`Server does not support resources (required for ${e})`);break;case"tools/call":case"tools/list":if(!this._capabilities.tools)throw new Error(`Server does not support tools (required for ${e})`);break;case"tasks/get":case"tasks/list":case"tasks/result":case"tasks/cancel":if(!this._capabilities.tasks)throw new Error(`Server does not support tasks capability (required for ${e})`);break;case"ping":case"initialize":break}}assertTaskCapability(e){Nme(this._clientCapabilities?.tasks?.requests,e,"Client")}assertTaskHandlerCapability(e){this._capabilities&&Ome(this._capabilities.tasks?.requests,e,"Server")}async _oninitialize(e){let r=e.params.protocolVersion;return this._clientCapabilities=e.params.capabilities,this._clientVersion=e.params.clientInfo,{protocolVersion:dE.includes(r)?r:Q4,capabilities:this.getCapabilities(),serverInfo:this._serverInfo,...this._instructions&&{instructions:this._instructions}}}getClientCapabilities(){return this._clientCapabilities}getClientVersion(){return this._clientVersion}getCapabilities(){return this._capabilities}async ping(){return this.request({method:"ping"},mI)}async createMessage(e,r){if((e.tools||e.toolChoice)&&!this._clientCapabilities?.sampling?.tools)throw new Error("Client does not support sampling tools capability.");if(e.messages.length>0){let n=e.messages[e.messages.length-1],o=Array.isArray(n.content)?n.content:[n.content],i=o.some(u=>u.type==="tool_result"),s=e.messages.length>1?e.messages[e.messages.length-2]:void 0,a=s?Array.isArray(s.content)?s.content:[s.content]:[],c=a.some(u=>u.type==="tool_use");if(i){if(o.some(u=>u.type!=="tool_result"))throw new Error("The last message must contain only tool_result content if any is present");if(!c)throw new Error("tool_result blocks are not matching any tool_use from the previous message")}if(c){let u=new Set(a.filter(f=>f.type==="tool_use").map(f=>f.id)),p=new Set(o.filter(f=>f.type==="tool_result").map(f=>f.toolUseId));if(u.size!==p.size||![...u].every(f=>p.has(f)))throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match")}}return e.tools?this.request({method:"sampling/createMessage",params:e},p2,r):this.request({method:"sampling/createMessage",params:e},l2,r)}async elicitInput(e,r){switch(e.mode??"form"){case"url":{if(!this._clientCapabilities?.elicitation?.url)throw new Error("Client does not support url elicitation.");let o=e;return this.request({method:"elicitation/create",params:o},CI,r)}case"form":{if(!this._clientCapabilities?.elicitation?.form)throw new Error("Client does not support form elicitation.");let o=e.mode==="form"?e:{...e,mode:"form"},i=await this.request({method:"elicitation/create",params:o},CI,r);if(i.action==="accept"&&i.content&&o.requestedSchema)try{let a=this._jsonSchemaValidator.getValidator(o.requestedSchema)(i.content);if(!a.valid)throw new Ue(We.InvalidParams,`Elicitation response content does not match requested schema: ${a.errorMessage}`)}catch(s){throw s instanceof Ue?s:new Ue(We.InternalError,`Error validating elicitation response: ${s instanceof Error?s.message:String(s)}`)}return i}}}createElicitationCompletionNotifier(e,r){if(!this._clientCapabilities?.elicitation?.url)throw new Error("Client does not support URL elicitation (required for notifications/elicitation/complete)");return()=>this.notification({method:"notifications/elicitation/complete",params:{elicitationId:e}},r)}async listRoots(e,r){return this.request({method:"roots/list",params:e},d2,r)}async sendLoggingMessage(e,r){if(this._capabilities.logging&&!this.isMessageIgnored(e.level,r))return this.notification({method:"notifications/message",params:e})}async sendResourceUpdated(e){return this.notification({method:"notifications/resources/updated",params:e})}async sendResourceListChanged(){return this.notification({method:"notifications/resources/list_changed"})}async sendToolListChanged(){return this.notification({method:"notifications/tools/list_changed"})}async sendPromptListChanged(){return this.notification({method:"notifications/prompts/list_changed"})}};var $me=Symbol.for("mcp.completable");function LG(t){return!!t&&typeof t=="object"&&$me in t}function kme(t){return t[$me]?.complete}var Cme;(function(t){t.Completable="McpCompletable"})(Cme||(Cme={}));var oft=/^[A-Za-z0-9._-]{1,128}$/;function ift(t){let e=[];if(t.length===0)return{isValid:!1,warnings:["Tool name cannot be empty"]};if(t.length>128)return{isValid:!1,warnings:[`Tool name exceeds maximum length of 128 characters (current: ${t.length})`]};if(t.includes(" ")&&e.push("Tool name contains spaces, which may cause parsing issues"),t.includes(",")&&e.push("Tool name contains commas, which may cause parsing issues"),(t.startsWith("-")||t.endsWith("-"))&&e.push("Tool name starts or ends with a dash, which may cause parsing issues in some contexts"),(t.startsWith(".")||t.endsWith("."))&&e.push("Tool name starts or ends with a dot, which may cause parsing issues in some contexts"),!oft.test(t)){let r=t.split("").filter(n=>!/[A-Za-z0-9._-]/.test(n)).filter((n,o,i)=>i.indexOf(n)===o);return e.push(`Tool name contains invalid characters: ${r.map(n=>`"${n}"`).join(", ")}`,"Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)"),{isValid:!1,warnings:e}}return{isValid:!0,warnings:e}}function sft(t,e){if(e.length>0){console.warn(`Tool name validation warning for "${t}":`);for(let r of e)console.warn(` - ${r}`);console.warn("Tool registration will proceed, but this may cause compatibility issues."),console.warn("Consider updating the tool name to conform to the MCP tool naming standard."),console.warn("See SEP: Specify Format for Tool Names (https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986) for more details.")}}function UG(t){let e=ift(t);return sft(t,e.warnings),e.isValid}var AN=class{constructor(e){this._mcpServer=e}registerToolTask(e,r,n){let o={taskSupport:"required",...r.execution};if(o.taskSupport==="forbidden")throw new Error(`Cannot register task-based tool '${e}' with taskSupport 'forbidden'. Use registerTool() instead.`);return this._mcpServer._createRegisteredTool(e,r.title,r.description,r.inputSchema,r.outputSchema,r.annotations,o,r._meta,n)}};Te();var wN=class{constructor(e,r){this._registeredResources={},this._registeredResourceTemplates={},this._registeredTools={},this._registeredPrompts={},this._toolHandlersInitialized=!1,this._completionHandlerInitialized=!1,this._resourceHandlersInitialized=!1,this._promptHandlersInitialized=!1,this.server=new xN(e,r)}get experimental(){return this._experimental||(this._experimental={tasks:new AN(this)}),this._experimental}async connect(e){return await this.server.connect(e)}async close(){await this.server.close()}setToolRequestHandlers(){this._toolHandlersInitialized||(this.server.assertCanSetRequestHandler(Gp(OI)),this.server.assertCanSetRequestHandler(Gp(Yh)),this.server.registerCapabilities({tools:{listChanged:!0}}),this.server.setRequestHandler(OI,()=>({tools:Object.entries(this._registeredTools).filter(([,e])=>e.enabled).map(([e,r])=>{let n={name:e,title:r.title,description:r.description,inputSchema:(()=>{let o=Eg(r.inputSchema);return o?pq(o,{strictUnions:!0,pipeStrategy:"input"}):aft})(),annotations:r.annotations,execution:r.execution,_meta:r._meta};if(r.outputSchema){let o=Eg(r.outputSchema);o&&(n.outputSchema=pq(o,{strictUnions:!0,pipeStrategy:"output"}))}return n})})),this.server.setRequestHandler(Yh,async(e,r)=>{try{let n=this._registeredTools[e.params.name];if(!n)throw new Ue(We.InvalidParams,`Tool ${e.params.name} not found`);if(!n.enabled)throw new Ue(We.InvalidParams,`Tool ${e.params.name} disabled`);let o=!!e.params.task,i=n.execution?.taskSupport,s="createTask"in n.handler;if((i==="required"||i==="optional")&&!s)throw new Ue(We.InternalError,`Tool ${e.params.name} has taskSupport '${i}' but was not registered with registerToolTask`);if(i==="required"&&!o)throw new Ue(We.MethodNotFound,`Tool ${e.params.name} requires task augmentation (taskSupport: 'required')`);if(i==="optional"&&!o&&s)return await this.handleAutomaticTaskPolling(n,e,r);let a=await this.validateToolInput(n,e.params.arguments,e.params.name),c=await this.executeToolHandler(n,a,r);return o||await this.validateToolOutput(n,c,e.params.name),c}catch(n){if(n instanceof Ue&&n.code===We.UrlElicitationRequired)throw n;return this.createToolError(n instanceof Error?n.message:String(n))}}),this._toolHandlersInitialized=!0)}createToolError(e){return{content:[{type:"text",text:e}],isError:!0}}async validateToolInput(e,r,n){if(!e.inputSchema)return;let i=Eg(e.inputSchema)??e.inputSchema,s=await tO(i,r);if(!s.success){let a="error"in s?s.error:"Unknown error",c=rO(a);throw new Ue(We.InvalidParams,`Input validation error: Invalid arguments for tool ${n}: ${c}`)}return s.data}async validateToolOutput(e,r,n){if(!e.outputSchema||!("content"in r)||r.isError)return;if(!r.structuredContent)throw new Ue(We.InvalidParams,`Output validation error: Tool ${n} has an output schema but no structured content was provided`);let o=Eg(e.outputSchema),i=await tO(o,r.structuredContent);if(!i.success){let s="error"in i?i.error:"Unknown error",a=rO(s);throw new Ue(We.InvalidParams,`Output validation error: Invalid structured content for tool ${n}: ${a}`)}}async executeToolHandler(e,r,n){let o=e.handler;if("createTask"in o){if(!n.taskStore)throw new Error("No task store provided.");let s={...n,taskStore:n.taskStore};if(e.inputSchema){let a=o;return await Promise.resolve(a.createTask(r,s))}else{let a=o;return await Promise.resolve(a.createTask(s))}}if(e.inputSchema){let s=o;return await Promise.resolve(s(r,n))}else{let s=o;return await Promise.resolve(s(n))}}async handleAutomaticTaskPolling(e,r,n){if(!n.taskStore)throw new Error("No task store provided for task-capable tool.");let o=await this.validateToolInput(e,r.params.arguments,r.params.name),i=e.handler,s={...n,taskStore:n.taskStore},a=o?await Promise.resolve(i.createTask(o,s)):await Promise.resolve(i.createTask(s)),c=a.task.taskId,u=a.task,p=u.pollInterval??5e3;for(;u.status!=="completed"&&u.status!=="failed"&&u.status!=="cancelled";){await new Promise(m=>setTimeout(m,p));let f=await n.taskStore.getTask(c);if(!f)throw new Ue(We.InternalError,`Task ${c} not found during polling`);u=f}return await n.taskStore.getTaskResult(c)}setCompletionRequestHandler(){this._completionHandlerInitialized||(this.server.assertCanSetRequestHandler(Gp($I)),this.server.registerCapabilities({completions:{}}),this.server.setRequestHandler($I,async e=>{switch(e.params.ref.type){case"ref/prompt":return ure(e),this.handlePromptCompletion(e,e.params.ref);case"ref/resource":return lre(e),this.handleResourceCompletion(e,e.params.ref);default:throw new Ue(We.InvalidParams,`Invalid completion reference: ${e.params.ref}`)}}),this._completionHandlerInitialized=!0)}async handlePromptCompletion(e,r){let n=this._registeredPrompts[r.name];if(!n)throw new Ue(We.InvalidParams,`Prompt ${r.name} not found`);if(!n.enabled)throw new Ue(We.InvalidParams,`Prompt ${r.name} disabled`);if(!n.argsSchema)return pb;let i=Rp(n.argsSchema)?.[e.params.argument.name];if(!LG(i))return pb;let s=kme(i);if(!s)return pb;let a=await s(e.params.argument.value,e.params.context);return Dme(a)}async handleResourceCompletion(e,r){let n=Object.values(this._registeredResourceTemplates).find(s=>s.resourceTemplate.uriTemplate.toString()===r.uri);if(!n){if(this._registeredResources[r.uri])return pb;throw new Ue(We.InvalidParams,`Resource template ${e.params.ref.uri} not found`)}let o=n.resourceTemplate.completeCallback(e.params.argument.name);if(!o)return pb;let i=await o(e.params.argument.value,e.params.context);return Dme(i)}setResourceRequestHandlers(){this._resourceHandlersInitialized||(this.server.assertCanSetRequestHandler(Gp(AI)),this.server.assertCanSetRequestHandler(Gp(wI)),this.server.assertCanSetRequestHandler(Gp(RI)),this.server.registerCapabilities({resources:{listChanged:!0}}),this.server.setRequestHandler(AI,async(e,r)=>{let n=Object.entries(this._registeredResources).filter(([i,s])=>s.enabled).map(([i,s])=>({uri:i,name:s.name,...s.metadata})),o=[];for(let i of Object.values(this._registeredResourceTemplates)){if(!i.resourceTemplate.listCallback)continue;let s=await i.resourceTemplate.listCallback(r);for(let a of s.resources)o.push({...i.metadata,...a})}return{resources:[...n,...o]}}),this.server.setRequestHandler(wI,async()=>({resourceTemplates:Object.entries(this._registeredResourceTemplates).map(([r,n])=>({name:r,uriTemplate:n.resourceTemplate.uriTemplate.toString(),...n.metadata}))})),this.server.setRequestHandler(RI,async(e,r)=>{let n=new URL(e.params.uri),o=this._registeredResources[n.toString()];if(o){if(!o.enabled)throw new Ue(We.InvalidParams,`Resource ${n} disabled`);return o.readCallback(n,r)}for(let i of Object.values(this._registeredResourceTemplates)){let s=i.resourceTemplate.uriTemplate.match(n.toString());if(s)return i.readCallback(n,s,r)}throw new Ue(We.InvalidParams,`Resource ${n} not found`)}),this._resourceHandlersInitialized=!0)}setPromptRequestHandlers(){this._promptHandlersInitialized||(this.server.assertCanSetRequestHandler(Gp(PI)),this.server.assertCanSetRequestHandler(Gp(II)),this.server.registerCapabilities({prompts:{listChanged:!0}}),this.server.setRequestHandler(PI,()=>({prompts:Object.entries(this._registeredPrompts).filter(([,e])=>e.enabled).map(([e,r])=>({name:e,title:r.title,description:r.description,arguments:r.argsSchema?uft(r.argsSchema):void 0}))})),this.server.setRequestHandler(II,async(e,r)=>{let n=this._registeredPrompts[e.params.name];if(!n)throw new Ue(We.InvalidParams,`Prompt ${e.params.name} not found`);if(!n.enabled)throw new Ue(We.InvalidParams,`Prompt ${e.params.name} disabled`);if(n.argsSchema){let o=Eg(n.argsSchema),i=await tO(o,e.params.arguments);if(!i.success){let c="error"in i?i.error:"Unknown error",u=rO(c);throw new Ue(We.InvalidParams,`Invalid arguments for prompt ${e.params.name}: ${u}`)}let s=i.data,a=n.callback;return await Promise.resolve(a(s,r))}else{let o=n.callback;return await Promise.resolve(o(r))}}),this._promptHandlersInitialized=!0)}resource(e,r,...n){let o;typeof n[0]=="object"&&(o=n.shift());let i=n[0];if(typeof r=="string"){if(this._registeredResources[r])throw new Error(`Resource ${r} is already registered`);let s=this._createRegisteredResource(e,void 0,r,o,i);return this.setResourceRequestHandlers(),this.sendResourceListChanged(),s}else{if(this._registeredResourceTemplates[e])throw new Error(`Resource template ${e} is already registered`);let s=this._createRegisteredResourceTemplate(e,void 0,r,o,i);return this.setResourceRequestHandlers(),this.sendResourceListChanged(),s}}registerResource(e,r,n,o){if(typeof r=="string"){if(this._registeredResources[r])throw new Error(`Resource ${r} is already registered`);let i=this._createRegisteredResource(e,n.title,r,n,o);return this.setResourceRequestHandlers(),this.sendResourceListChanged(),i}else{if(this._registeredResourceTemplates[e])throw new Error(`Resource template ${e} is already registered`);let i=this._createRegisteredResourceTemplate(e,n.title,r,n,o);return this.setResourceRequestHandlers(),this.sendResourceListChanged(),i}}_createRegisteredResource(e,r,n,o,i){let s={name:e,title:r,metadata:o,readCallback:i,enabled:!0,disable:()=>s.update({enabled:!1}),enable:()=>s.update({enabled:!0}),remove:()=>s.update({uri:null}),update:a=>{typeof a.uri<"u"&&a.uri!==n&&(delete this._registeredResources[n],a.uri&&(this._registeredResources[a.uri]=s)),typeof a.name<"u"&&(s.name=a.name),typeof a.title<"u"&&(s.title=a.title),typeof a.metadata<"u"&&(s.metadata=a.metadata),typeof a.callback<"u"&&(s.readCallback=a.callback),typeof a.enabled<"u"&&(s.enabled=a.enabled),this.sendResourceListChanged()}};return this._registeredResources[n]=s,s}_createRegisteredResourceTemplate(e,r,n,o,i){let s={resourceTemplate:n,title:r,metadata:o,readCallback:i,enabled:!0,disable:()=>s.update({enabled:!1}),enable:()=>s.update({enabled:!0}),remove:()=>s.update({name:null}),update:u=>{typeof u.name<"u"&&u.name!==e&&(delete this._registeredResourceTemplates[e],u.name&&(this._registeredResourceTemplates[u.name]=s)),typeof u.title<"u"&&(s.title=u.title),typeof u.template<"u"&&(s.resourceTemplate=u.template),typeof u.metadata<"u"&&(s.metadata=u.metadata),typeof u.callback<"u"&&(s.readCallback=u.callback),typeof u.enabled<"u"&&(s.enabled=u.enabled),this.sendResourceListChanged()}};this._registeredResourceTemplates[e]=s;let a=n.uriTemplate.variableNames;return Array.isArray(a)&&a.some(u=>!!n.completeCallback(u))&&this.setCompletionRequestHandler(),s}_createRegisteredPrompt(e,r,n,o,i){let s={title:r,description:n,argsSchema:o===void 0?void 0:Nf(o),callback:i,enabled:!0,disable:()=>s.update({enabled:!1}),enable:()=>s.update({enabled:!0}),remove:()=>s.update({name:null}),update:a=>{typeof a.name<"u"&&a.name!==e&&(delete this._registeredPrompts[e],a.name&&(this._registeredPrompts[a.name]=s)),typeof a.title<"u"&&(s.title=a.title),typeof a.description<"u"&&(s.description=a.description),typeof a.argsSchema<"u"&&(s.argsSchema=Nf(a.argsSchema)),typeof a.callback<"u"&&(s.callback=a.callback),typeof a.enabled<"u"&&(s.enabled=a.enabled),this.sendPromptListChanged()}};return this._registeredPrompts[e]=s,o&&Object.values(o).some(c=>{let u=c instanceof Vh?c._def?.innerType:c;return LG(u)})&&this.setCompletionRequestHandler(),s}_createRegisteredTool(e,r,n,o,i,s,a,c,u){UG(e);let p={title:r,description:n,inputSchema:Mme(o),outputSchema:Mme(i),annotations:s,execution:a,_meta:c,handler:u,enabled:!0,disable:()=>p.update({enabled:!1}),enable:()=>p.update({enabled:!0}),remove:()=>p.update({name:null}),update:f=>{typeof f.name<"u"&&f.name!==e&&(typeof f.name=="string"&&UG(f.name),delete this._registeredTools[e],f.name&&(this._registeredTools[f.name]=p)),typeof f.title<"u"&&(p.title=f.title),typeof f.description<"u"&&(p.description=f.description),typeof f.paramsSchema<"u"&&(p.inputSchema=Nf(f.paramsSchema)),typeof f.outputSchema<"u"&&(p.outputSchema=Nf(f.outputSchema)),typeof f.callback<"u"&&(p.handler=f.callback),typeof f.annotations<"u"&&(p.annotations=f.annotations),typeof f._meta<"u"&&(p._meta=f._meta),typeof f.enabled<"u"&&(p.enabled=f.enabled),this.sendToolListChanged()}};return this._registeredTools[e]=p,this.setToolRequestHandlers(),this.sendToolListChanged(),p}tool(e,...r){if(this._registeredTools[e])throw new Error(`Tool ${e} is already registered`);let n,o,i,s;if(typeof r[0]=="string"&&(n=r.shift()),r.length>1){let c=r[0];jG(c)?(o=r.shift(),r.length>1&&typeof r[0]=="object"&&r[0]!==null&&!jG(r[0])&&(s=r.shift())):typeof c=="object"&&c!==null&&(s=r.shift())}let a=r[0];return this._createRegisteredTool(e,void 0,n,o,i,s,{taskSupport:"forbidden"},void 0,a)}registerTool(e,r,n){if(this._registeredTools[e])throw new Error(`Tool ${e} is already registered`);let{title:o,description:i,inputSchema:s,outputSchema:a,annotations:c,_meta:u}=r;return this._createRegisteredTool(e,o,i,s,a,c,{taskSupport:"forbidden"},u,n)}prompt(e,...r){if(this._registeredPrompts[e])throw new Error(`Prompt ${e} is already registered`);let n;typeof r[0]=="string"&&(n=r.shift());let o;r.length>1&&(o=r.shift());let i=r[0],s=this._createRegisteredPrompt(e,void 0,n,o,i);return this.setPromptRequestHandlers(),this.sendPromptListChanged(),s}registerPrompt(e,r,n){if(this._registeredPrompts[e])throw new Error(`Prompt ${e} is already registered`);let{title:o,description:i,argsSchema:s}=r,a=this._createRegisteredPrompt(e,o,i,s,n);return this.setPromptRequestHandlers(),this.sendPromptListChanged(),a}isConnected(){return this.server.transport!==void 0}async sendLoggingMessage(e,r){return this.server.sendLoggingMessage(e,r)}sendResourceListChanged(){this.isConnected()&&this.server.sendResourceListChanged()}sendToolListChanged(){this.isConnected()&&this.server.sendToolListChanged()}sendPromptListChanged(){this.isConnected()&&this.server.sendPromptListChanged()}};var aft={type:"object",properties:{}};function Lme(t){return t!==null&&typeof t=="object"&&"parse"in t&&typeof t.parse=="function"&&"safeParse"in t&&typeof t.safeParse=="function"}function cft(t){return"_def"in t||"_zod"in t||Lme(t)}function jG(t){return typeof t!="object"||t===null||cft(t)?!1:Object.keys(t).length===0?!0:Object.values(t).some(Lme)}function Mme(t){if(t)return jG(t)?Nf(t):t}function uft(t){let e=Rp(t);return e?Object.entries(e).map(([r,n])=>{let o=Wce(n),i=Kce(n);return{name:r,description:o,required:!i}}):[]}function Gp(t){let r=Rp(t)?.method;if(!r)throw new Error("Schema is missing a method literal");let n=nO(r);if(typeof n=="string")return n;throw new Error("Schema method literal must be a string")}function Dme(t){return{completion:{values:t.slice(0,100),total:t.length,hasMore:t.length>100}}}var pb={completion:{values:[],hasMore:!1}};Go();bE();pe();var INe=W(vAe(),1);var Ee=typeof __SENTRY_DEBUG__>"u"||__SENTRY_DEBUG__;var ft=globalThis;var xr="10.5.0";function wi(){return nd(ft),ft}function nd(t){let e=t.__SENTRY__=t.__SENTRY__||{};return e.version=e.version||xr,e[xr]=e[xr]||{}}function gl(t,e,r=ft){let n=r.__SENTRY__=r.__SENTRY__||{},o=n[xr]=n[xr]||{};return o[t]||(o[t]=e())}var Kb=["debug","info","warn","error","log","assert","trace"],zPt="Sentry Logger ",v_={};function _n(t){if(!("console"in ft))return t();let e=ft.console,r={},n=Object.keys(v_);n.forEach(o=>{let i=v_[o];r[o]=e[o],e[o]=i});try{return t()}finally{n.forEach(o=>{e[o]=r[o]})}}function FPt(){q8().enabled=!0}function qPt(){q8().enabled=!1}function SAe(){return q8().enabled}function BPt(...t){F8("log",...t)}function GPt(...t){F8("warn",...t)}function VPt(...t){F8("error",...t)}function F8(t,...e){Ee&&SAe()&&_n(()=>{ft.console[t](`${zPt}[${t}]:`,...e)})}function q8(){return Ee?gl("loggerSettings",()=>({enabled:!1})):{enabled:!1}}var j={enable:FPt,disable:qPt,isEnabled:SAe,log:BPt,warn:GPt,error:VPt};var yAe=/\(error: (.*)\)/,EAe=/captureMessage|captureException/;function KC(...t){let e=t.sort((r,n)=>r[0]-n[0]).map(r=>r[1]);return(r,n=0,o=0)=>{let i=[],s=r.split(` -`);for(let a=n;a1024&&(c=c.slice(0,1024));let u=yAe.test(c)?c.replace(yAe,"$1"):c;if(!u.match(/\S*Error: /)){for(let p of e){let f=p(u);if(f){i.push(f);break}}if(i.length>=50+o)break}}return TAe(i.slice(o))}}function G8(t){return Array.isArray(t)?KC(...t):t}function TAe(t){if(!t.length)return[];let e=Array.from(t);return/sentryWrapped/.test(WC(e).function||"")&&e.pop(),e.reverse(),EAe.test(WC(e).function||"")&&(e.pop(),EAe.test(WC(e).function||"")&&e.pop()),e.slice(0,50).map(r=>({...r,filename:r.filename||WC(e).filename,function:r.function||"?"}))}function WC(t){return t[t.length-1]||{}}var B8="";function Zb(t){try{return!t||typeof t!="function"?B8:t.name||B8}catch{return B8}}var ZC={},bAe={};function S_(t,e){ZC[t]=ZC[t]||[],ZC[t].push(e)}function y_(t,e){if(!bAe[t]){bAe[t]=!0;try{e()}catch(r){Ee&&j.error(`Error while instrumenting ${t}`,r)}}}function E_(t,e){let r=t&&ZC[t];if(r)for(let n of r)try{n(e)}catch(o){Ee&&j.error(`Error while triggering instrumentation handler. -Type: ${t} -Name: ${Zb(n)} -Error:`,o)}}var V8=null;function xAe(t){let e="error";S_(e,t),y_(e,HPt)}function HPt(){V8=ft.onerror,ft.onerror=function(t,e,r,n,o){return E_("error",{column:n,error:o,line:r,msg:t,url:e}),V8?V8.apply(this,arguments):!1},ft.onerror.__SENTRY_INSTRUMENTED__=!0}var H8=null;function AAe(t){let e="unhandledrejection";S_(e,t),y_(e,WPt)}function WPt(){H8=ft.onunhandledrejection,ft.onunhandledrejection=function(t){return E_("unhandledrejection",t),H8?H8.apply(this,arguments):!0},ft.onunhandledrejection.__SENTRY_INSTRUMENTED__=!0}var wAe=Object.prototype.toString;function od(t){switch(wAe.call(t)){case"[object Error]":case"[object Exception]":case"[object DOMException]":case"[object WebAssembly.Exception]":return!0;default:return ru(t,Error)}}function YC(t,e){return wAe.call(t)===`[object ${e}]`}function W8(t){return YC(t,"ErrorEvent")}function id(t){return YC(t,"String")}function T_(t){return typeof t=="object"&&t!==null&&"__sentry_template_string__"in t&&"__sentry_template_values__"in t}function b_(t){return t===null||T_(t)||typeof t!="object"&&typeof t!="function"}function _l(t){return YC(t,"Object")}function K8(t){return typeof Event<"u"&&ru(t,Event)}function Z8(t){return typeof Element<"u"&&ru(t,Element)}function Y8(t){return YC(t,"RegExp")}function mc(t){return!!(t?.then&&typeof t.then=="function")}function J8(t){return _l(t)&&"nativeEvent"in t&&"preventDefault"in t&&"stopPropagation"in t}function ru(t,e){try{return t instanceof e}catch{return!1}}function Yb(t){return!!(typeof t=="object"&&t!==null&&(t.__isVue||t._isVue))}var KPt=ft,ZPt=80;function RAe(t,e={}){if(!t)return"";try{let r=t,n=5,o=[],i=0,s=0,a=" > ",c=a.length,u,p=Array.isArray(e)?e:e.keyAttrs,f=!Array.isArray(e)&&e.maxStringLength||ZPt;for(;r&&i++1&&s+o.length*c+u.length>=f));)o.push(u),s+=u.length,r=r.parentNode;return o.reverse().join(a)}catch{return""}}function YPt(t,e){let r=t,n=[];if(!r?.tagName)return"";if(KPt.HTMLElement&&r instanceof HTMLElement&&r.dataset){if(r.dataset.sentryComponent)return r.dataset.sentryComponent;if(r.dataset.sentryElement)return r.dataset.sentryElement}n.push(r.tagName.toLowerCase());let o=e?.length?e.filter(s=>r.getAttribute(s)).map(s=>[s,r.getAttribute(s)]):null;if(o?.length)o.forEach(s=>{n.push(`[${s[0]}="${s[1]}"]`)});else{r.id&&n.push(`#${r.id}`);let s=r.className;if(s&&id(s)){let a=s.split(/\s+/);for(let c of a)n.push(`.${c}`)}}let i=["aria-label","type","name","title","alt"];for(let s of i){let a=r.getAttribute(s);a&&n.push(`[${s}="${a}"]`)}return n.join("")}function nu(t,e=0){return typeof t!="string"||e===0||t.length<=e?t:`${t.slice(0,e)}...`}function X8(t,e){let r=t,n=r.length;if(n<=150)return r;e>n&&(e=n);let o=Math.max(e-60,0);o<5&&(o=0);let i=Math.min(o+140,n);return i>n-5&&(i=n),i===n&&(o=Math.max(i-140,0)),r=r.slice(o,i),o>0&&(r=`'{snip} ${r}`),ix_(t,n,r))}function fa(t,e,r){if(!(e in t))return;let n=t[e];if(typeof n!="function")return;let o=r(n);typeof o=="function"&&OAe(o,n);try{t[e]=o}catch{Ee&&j.log(`Failed to replace method "${e}" in object`,t)}}function zn(t,e,r){try{Object.defineProperty(t,e,{value:r,writable:!0,configurable:!0})}catch{Ee&&j.log(`Failed to add non-enumerable property "${e}" to object`,t)}}function OAe(t,e){try{let r=e.prototype||{};t.prototype=e.prototype=r,zn(t,"__sentry_original__",e)}catch{}}function eV(t){return t.__sentry_original__}function JC(t){if(od(t))return{message:t.message,name:t.name,stack:t.stack,...IAe(t)};if(K8(t)){let e={type:t.type,target:PAe(t.target),currentTarget:PAe(t.currentTarget),...IAe(t)};return typeof CustomEvent<"u"&&ru(t,CustomEvent)&&(e.detail=t.detail),e}else return t}function PAe(t){try{return Z8(t)?RAe(t):Object.prototype.toString.call(t)}catch{return""}}function IAe(t){if(typeof t=="object"&&t!==null){let e={};for(let r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e}else return{}}function tV(t,e=40){let r=Object.keys(JC(t));r.sort();let n=r[0];if(!n)return"[object has no keys]";if(n.length>=e)return nu(n,e);for(let o=r.length;o>0;o--){let i=r.slice(0,o).join(", ");if(!(i.length>e))return o===r.length?i:nu(i,e)}return""}function JPt(){let t=ft;return t.crypto||t.msCrypto}function Yn(t=JPt()){let e=()=>Math.random()*16;try{if(t?.randomUUID)return t.randomUUID().replace(/-/g,"");t?.getRandomValues&&(e=()=>{let r=new Uint8Array(1);return t.getRandomValues(r),r[0]})}catch{}return("10000000100040008000"+1e11).replace(/[018]/g,r=>(r^(e()&15)>>r/4).toString(16))}function NAe(t){return t.exception?.values?.[0]}function ad(t){let{message:e,event_id:r}=t;if(e)return e;let n=NAe(t);return n?n.type&&n.value?`${n.type}: ${n.value}`:n.type||n.value||r||"":r||""}function nV(t,e,r){let n=t.exception=t.exception||{},o=n.values=n.values||[],i=o[0]=o[0]||{};i.value||(i.value=e||""),i.type||(i.type=r||"Error")}function A_(t,e){let r=NAe(t);if(!r)return;let n={type:"generic",handled:!0},o=r.mechanism;if(r.mechanism={...n,...o,...e},e&&"data"in e){let i={...o?.data,...e.data};r.mechanism.data=i}}var XPt=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;function rV(t){return parseInt(t||"",10)}function oV(t){let e=t.match(XPt)||[],r=rV(e[1]),n=rV(e[2]),o=rV(e[3]);return{buildmetadata:e[5],major:isNaN(r)?void 0:r,minor:isNaN(n)?void 0:n,patch:isNaN(o)?void 0:o,prerelease:e[4]}}function XC(t){if(QPt(t))return!0;try{zn(t,"__sentry_captured__",!0)}catch{}return!1}function QPt(t){try{return t.__sentry_captured__}catch{}}var $Ae=1e3;function vl(){return Date.now()/$Ae}function eIt(){let{performance:t}=ft;if(!t?.now||!t.timeOrigin)return vl;let e=t.timeOrigin;return()=>(e+t.now())/$Ae}var CAe;function cd(){return(CAe??(CAe=eIt()))()}function kAe(t){let e=cd(),r={sid:Yn(),init:!0,timestamp:e,started:e,duration:0,status:"ok",errors:0,ignoreDuration:!1,toJSON:()=>tIt(r)};return t&&Sl(r,t),r}function Sl(t,e={}){if(e.user&&(!t.ipAddress&&e.user.ip_address&&(t.ipAddress=e.user.ip_address),!t.did&&!e.did&&(t.did=e.user.id||e.user.email||e.user.username)),t.timestamp=e.timestamp||cd(),e.abnormal_mechanism&&(t.abnormal_mechanism=e.abnormal_mechanism),e.ignoreDuration&&(t.ignoreDuration=e.ignoreDuration),e.sid&&(t.sid=e.sid.length===32?e.sid:Yn()),e.init!==void 0&&(t.init=e.init),!t.did&&e.did&&(t.did=`${e.did}`),typeof e.started=="number"&&(t.started=e.started),t.ignoreDuration)t.duration=void 0;else if(typeof e.duration=="number")t.duration=e.duration;else{let r=t.timestamp-t.started;t.duration=r>=0?r:0}e.release&&(t.release=e.release),e.environment&&(t.environment=e.environment),!t.ipAddress&&e.ipAddress&&(t.ipAddress=e.ipAddress),!t.userAgent&&e.userAgent&&(t.userAgent=e.userAgent),typeof e.errors=="number"&&(t.errors=e.errors),e.status&&(t.status=e.status)}function MAe(t,e){let r={};e?r={status:e}:t.status==="ok"&&(r={status:"exited"}),Sl(t,r)}function tIt(t){return{sid:`${t.sid}`,init:t.init,started:new Date(t.started*1e3).toISOString(),timestamp:new Date(t.timestamp*1e3).toISOString(),status:t.status,errors:t.errors,did:typeof t.did=="number"||typeof t.did=="string"?`${t.did}`:void 0,duration:t.duration,abnormal_mechanism:t.abnormal_mechanism,attrs:{release:t.release,environment:t.environment,ip_address:t.ipAddress,user_agent:t.userAgent}}}function ud(t,e,r=2){if(!e||typeof e!="object"||r<=0)return e;if(t&&Object.keys(e).length===0)return t;let n={...t};for(let o in e)Object.prototype.hasOwnProperty.call(e,o)&&(n[o]=ud(n[o],e[o],r-1));return n}function hc(){return Yn()}function xs(){return Yn().substring(16)}var iV="_sentrySpan";function pm(t,e){e?zn(t,iV,e):delete t[iV]}function dm(t){return t[iV]}var rIt=100,Ji=class t{constructor(){this._notifyingListeners=!1,this._scopeListeners=[],this._eventProcessors=[],this._breadcrumbs=[],this._attachments=[],this._user={},this._tags={},this._extra={},this._contexts={},this._sdkProcessingMetadata={},this._propagationContext={traceId:hc(),sampleRand:Math.random()}}clone(){let e=new t;return e._breadcrumbs=[...this._breadcrumbs],e._tags={...this._tags},e._extra={...this._extra},e._contexts={...this._contexts},this._contexts.flags&&(e._contexts.flags={values:[...this._contexts.flags.values]}),e._user=this._user,e._level=this._level,e._session=this._session,e._transactionName=this._transactionName,e._fingerprint=this._fingerprint,e._eventProcessors=[...this._eventProcessors],e._attachments=[...this._attachments],e._sdkProcessingMetadata={...this._sdkProcessingMetadata},e._propagationContext={...this._propagationContext},e._client=this._client,e._lastEventId=this._lastEventId,pm(e,dm(this)),e}setClient(e){this._client=e}setLastEventId(e){this._lastEventId=e}getClient(){return this._client}lastEventId(){return this._lastEventId}addScopeListener(e){this._scopeListeners.push(e)}addEventProcessor(e){return this._eventProcessors.push(e),this}setUser(e){return this._user=e||{email:void 0,id:void 0,ip_address:void 0,username:void 0},this._session&&Sl(this._session,{user:e}),this._notifyScopeListeners(),this}getUser(){return this._user}setTags(e){return this._tags={...this._tags,...e},this._notifyScopeListeners(),this}setTag(e,r){return this._tags={...this._tags,[e]:r},this._notifyScopeListeners(),this}setExtras(e){return this._extra={...this._extra,...e},this._notifyScopeListeners(),this}setExtra(e,r){return this._extra={...this._extra,[e]:r},this._notifyScopeListeners(),this}setFingerprint(e){return this._fingerprint=e,this._notifyScopeListeners(),this}setLevel(e){return this._level=e,this._notifyScopeListeners(),this}setTransactionName(e){return this._transactionName=e,this._notifyScopeListeners(),this}setContext(e,r){return r===null?delete this._contexts[e]:this._contexts[e]=r,this._notifyScopeListeners(),this}setSession(e){return e?this._session=e:delete this._session,this._notifyScopeListeners(),this}getSession(){return this._session}update(e){if(!e)return this;let r=typeof e=="function"?e(this):e,n=r instanceof t?r.getScopeData():_l(r)?e:void 0,{tags:o,extra:i,user:s,contexts:a,level:c,fingerprint:u=[],propagationContext:p}=n||{};return this._tags={...this._tags,...o},this._extra={...this._extra,...i},this._contexts={...this._contexts,...a},s&&Object.keys(s).length&&(this._user=s),c&&(this._level=c),u.length&&(this._fingerprint=u),p&&(this._propagationContext=p),this}clear(){return this._breadcrumbs=[],this._tags={},this._extra={},this._user={},this._contexts={},this._level=void 0,this._transactionName=void 0,this._fingerprint=void 0,this._session=void 0,pm(this,void 0),this._attachments=[],this.setPropagationContext({traceId:hc(),sampleRand:Math.random()}),this._notifyScopeListeners(),this}addBreadcrumb(e,r){let n=typeof r=="number"?r:rIt;if(n<=0)return this;let o={timestamp:vl(),...e,message:e.message?nu(e.message,2048):e.message};return this._breadcrumbs.push(o),this._breadcrumbs.length>n&&(this._breadcrumbs=this._breadcrumbs.slice(-n),this._client?.recordDroppedEvent("buffer_overflow","log_item")),this._notifyScopeListeners(),this}getLastBreadcrumb(){return this._breadcrumbs[this._breadcrumbs.length-1]}clearBreadcrumbs(){return this._breadcrumbs=[],this._notifyScopeListeners(),this}addAttachment(e){return this._attachments.push(e),this}clearAttachments(){return this._attachments=[],this}getScopeData(){return{breadcrumbs:this._breadcrumbs,attachments:this._attachments,contexts:this._contexts,tags:this._tags,extra:this._extra,user:this._user,level:this._level,fingerprint:this._fingerprint||[],eventProcessors:this._eventProcessors,propagationContext:this._propagationContext,sdkProcessingMetadata:this._sdkProcessingMetadata,transactionName:this._transactionName,span:dm(this)}}setSDKProcessingMetadata(e){return this._sdkProcessingMetadata=ud(this._sdkProcessingMetadata,e,2),this}setPropagationContext(e){return this._propagationContext=e,this}getPropagationContext(){return this._propagationContext}captureException(e,r){let n=r?.event_id||Yn();if(!this._client)return Ee&&j.warn("No client configured on scope - will not capture exception!"),n;let o=new Error("Sentry syntheticException");return this._client.captureException(e,{originalException:e,syntheticException:o,...r,event_id:n},this),n}captureMessage(e,r,n){let o=n?.event_id||Yn();if(!this._client)return Ee&&j.warn("No client configured on scope - will not capture message!"),o;let i=new Error(e);return this._client.captureMessage(e,r,{originalException:e,syntheticException:i,...n,event_id:o},this),o}captureEvent(e,r){let n=r?.event_id||Yn();return this._client?(this._client.captureEvent(e,{...r,event_id:n},this),n):(Ee&&j.warn("No client configured on scope - will not capture event!"),n)}_notifyScopeListeners(){this._notifyingListeners||(this._notifyingListeners=!0,this._scopeListeners.forEach(e=>{e(this)}),this._notifyingListeners=!1)}};function w_(){return gl("defaultCurrentScope",()=>new Ji)}function ou(){return gl("defaultIsolationScope",()=>new Ji)}var sV=class{constructor(e,r){let n;e?n=e:n=new Ji;let o;r?o=r:o=new Ji,this._stack=[{scope:n}],this._isolationScope=o}withScope(e){let r=this._pushScope(),n;try{n=e(r)}catch(o){throw this._popScope(),o}return mc(n)?n.then(o=>(this._popScope(),o),o=>{throw this._popScope(),o}):(this._popScope(),n)}getClient(){return this.getStackTop().client}getScope(){return this.getStackTop().scope}getIsolationScope(){return this._isolationScope}getStackTop(){return this._stack[this._stack.length-1]}_pushScope(){let e=this.getScope().clone();return this._stack.push({client:this.getClient(),scope:e}),e}_popScope(){return this._stack.length<=1?!1:!!this._stack.pop()}};function R_(){let t=wi(),e=nd(t);return e.stack=e.stack||new sV(w_(),ou())}function nIt(t){return R_().withScope(t)}function oIt(t,e){let r=R_();return r.withScope(()=>(r.getStackTop().scope=t,e(t)))}function DAe(t){return R_().withScope(()=>t(R_().getIsolationScope()))}function LAe(){return{withIsolationScope:DAe,withScope:nIt,withSetScope:oIt,withSetIsolationScope:(t,e)=>DAe(e),getCurrentScope:()=>R_().getScope(),getIsolationScope:()=>R_().getIsolationScope()}}function aV(t){let e=wi(),r=nd(e);r.acs=t}function gc(t){let e=nd(t);return e.acs?e.acs:LAe()}function It(){let t=wi();return gc(t).getCurrentScope()}function nr(){let t=wi();return gc(t).getIsolationScope()}function QC(){return gl("globalScope",()=>new Ji)}function iu(...t){let e=wi(),r=gc(e);if(t.length===2){let[n,o]=t;return n?r.withSetScope(n,o):r.withScope(o)}return r.withScope(t[0])}function fm(...t){let e=wi(),r=gc(e);if(t.length===2){let[n,o]=t;return n?r.withSetIsolationScope(n,o):r.withIsolationScope(o)}return r.withIsolationScope(t[0])}function Ce(){return It().getClient()}function P_(t){let e=t.getPropagationContext(),{traceId:r,parentSpanId:n,propagationSpanId:o}=e,i={trace_id:r,span_id:o||xs()};return n&&(i.parent_span_id=n),i}var Nn="sentry.source",_c="sentry.sample_rate",cV="sentry.previous_trace_sample_rate",lt="sentry.op",dr="sentry.origin";var uV="sentry.measurement_unit",lV="sentry.measurement_value",su="sentry.custom_span_name",I_="sentry.profile_id",O_="sentry.exclusive_time",pV="cache.hit",dV="cache.key",fV="cache.item_size";function e1(t){if(t<400&&t>=100)return{code:1};if(t>=400&&t<500)switch(t){case 401:return{code:2,message:"unauthenticated"};case 403:return{code:2,message:"permission_denied"};case 404:return{code:2,message:"not_found"};case 409:return{code:2,message:"already_exists"};case 413:return{code:2,message:"failed_precondition"};case 429:return{code:2,message:"resource_exhausted"};case 499:return{code:2,message:"cancelled"};default:return{code:2,message:"invalid_argument"}}if(t>=500&&t<600)switch(t){case 501:return{code:2,message:"unimplemented"};case 503:return{code:2,message:"unavailable"};case 504:return{code:2,message:"deadline_exceeded"};default:return{code:2,message:"internal_error"}}return{code:2,message:"unknown_error"}}var UAe="_sentryScope",jAe="_sentryIsolationScope";function Jb(t,e,r){t&&(zn(t,jAe,r),zn(t,UAe,e))}function vc(t){return{scope:t[UAe],isolationScope:t[jAe]}}var Xb="sentry-",FAe=/^sentry-/,qAe=8192;function yl(t){let e=hm(t);if(!e)return;let r=Object.entries(e).reduce((n,[o,i])=>{if(o.match(FAe)){let s=o.slice(Xb.length);n[s]=i}return n},{});if(Object.keys(r).length>0)return r}function mm(t){if(!t)return;let e=Object.entries(t).reduce((r,[n,o])=>(o&&(r[`${Xb}${n}`]=o),r),{});return t1(e)}function hm(t){if(!(!t||!id(t)&&!Array.isArray(t)))return Array.isArray(t)?t.reduce((e,r)=>{let n=zAe(r);return Object.entries(n).forEach(([o,i])=>{e[o]=i}),e},{}):zAe(t)}function zAe(t){return t.split(",").map(e=>e.split("=").map(r=>{try{return decodeURIComponent(r.trim())}catch{return}})).reduce((e,[r,n])=>(r&&n&&(e[r]=n),e),{})}function t1(t){if(Object.keys(t).length!==0)return Object.entries(t).reduce((e,[r,n],o)=>{let i=`${encodeURIComponent(r)}=${encodeURIComponent(n)}`,s=o===0?i:`${e},${i}`;return s.length>qAe?(Ee&&j.warn(`Not adding key: ${r} with val: ${n} to baggage header due to exceeding baggage size limits.`),e):s},"")}var iIt=/^o(\d+)\./,sIt=/^(?:(\w+):)\/\/(?:(\w+)(?::(\w+)?)?@)([\w.-]+)(?::(\d+))?\/(.+)/;function aIt(t){return t==="http"||t==="https"}function Sc(t,e=!1){let{host:r,path:n,pass:o,port:i,projectId:s,protocol:a,publicKey:c}=t;return`${a}://${c}${e&&o?`:${o}`:""}@${r}${i?`:${i}`:""}/${n&&`${n}/`}${s}`}function cIt(t){let e=sIt.exec(t);if(!e){_n(()=>{console.error(`Invalid Sentry Dsn: ${t}`)});return}let[r,n,o="",i="",s="",a=""]=e.slice(1),c="",u=a,p=u.split("/");if(p.length>1&&(c=p.slice(0,-1).join("/"),u=p.pop()),u){let f=u.match(/^\d+/);f&&(u=f[0])}return BAe({host:i,pass:o,path:c,projectId:u,port:s,protocol:r,publicKey:n})}function BAe(t){return{protocol:t.protocol,publicKey:t.publicKey||"",pass:t.pass||"",host:t.host,port:t.port||"",path:t.path||"",projectId:t.projectId}}function uIt(t){if(!Ee)return!0;let{port:e,projectId:r,protocol:n}=t;return["protocol","publicKey","host","projectId"].find(s=>t[s]?!1:(j.error(`Invalid Sentry Dsn: ${s} missing`),!0))?!1:r.match(/^\d+$/)?aIt(n)?e&&isNaN(parseInt(e,10))?(j.error(`Invalid Sentry Dsn: Invalid port ${e}`),!1):!0:(j.error(`Invalid Sentry Dsn: Invalid protocol ${n}`),!1):(j.error(`Invalid Sentry Dsn: Invalid projectId ${r}`),!1)}function lIt(t){return t.match(iIt)?.[1]}function r1(t){let e=t.getOptions(),{host:r}=t.getDsn()||{},n;return e.orgId?n=String(e.orgId):r&&(n=lIt(r)),n}function GAe(t){let e=typeof t=="string"?cIt(t):BAe(t);if(!(!e||!uIt(e)))return e}function As(t){if(typeof t=="boolean")return Number(t);let e=typeof t=="string"?parseFloat(t):t;if(!(typeof e!="number"||isNaN(e)||e<0||e>1))return e}var n1=new RegExp("^[ \\t]*([0-9a-f]{32})?-?([0-9a-f]{16})?-?([01])?[ \\t]*$");function VAe(t){if(!t)return;let e=t.match(n1);if(!e)return;let r;return e[3]==="1"?r=!0:e[3]==="0"&&(r=!1),{traceId:e[1],parentSampled:r,parentSpanId:e[2]}}function Qb(t,e){let r=VAe(t),n=yl(e);if(!r?.traceId)return{traceId:hc(),sampleRand:Math.random()};let o=pIt(r,n);n&&(n.sample_rand=o.toString());let{traceId:i,parentSpanId:s,parentSampled:a}=r;return{traceId:i,parentSpanId:s,sampled:a,dsc:n||{},sampleRand:o}}function ld(t=hc(),e=xs(),r){let n="";return r!==void 0&&(n=r?"-1":"-0"),`${t}-${e}${n}`}function pIt(t,e){let r=As(e?.sample_rand);if(r!==void 0)return r;let n=As(e?.sample_rate);return n&&t?.parentSampled!==void 0?t.parentSampled?Math.random()*n:n+Math.random()*(1-n):Math.random()}function mV(t,e){let r=r1(t);return e&&r&&e!==r?(j.log(`Won't continue trace because org IDs don't match (incoming baggage: ${e}, SDK options: ${r})`),!1):(t.getOptions().strictTraceContinuation||!1)&&(e&&!r||!e&&r)?(j.log(`Starting a new trace because strict trace continuation is enabled but one org ID is missing (incoming baggage: ${e}, Sentry client: ${r})`),!1):!0}var i1=0,s1=1,HAe=!1;function KAe(t){let{spanId:e,traceId:r}=t.spanContext(),{data:n,op:o,parent_span_id:i,status:s,origin:a,links:c}=Ye(t);return{parent_span_id:i,span_id:e,trace_id:r,data:n,op:o,status:s,origin:a,links:c}}function pd(t){let{spanId:e,traceId:r,isRemote:n}=t.spanContext(),o=n?e:Ye(t).parent_span_id,i=vc(t).scope,s=n?i?.getPropagationContext().propagationSpanId||xs():e;return{parent_span_id:o,span_id:s,trace_id:r}}function a1(t){let{traceId:e,spanId:r}=t.spanContext(),n=au(t);return ld(e,r,n)}function gm(t){if(t&&t.length>0)return t.map(({context:{spanId:e,traceId:r,traceFlags:n,...o},attributes:i})=>({span_id:e,trace_id:r,sampled:n===s1,attributes:i,...o}))}function Xi(t){return typeof t=="number"?WAe(t):Array.isArray(t)?t[0]+t[1]/1e9:t instanceof Date?WAe(t.getTime()):cd()}function WAe(t){return t>9999999999?t/1e3:t}function Ye(t){if(mIt(t))return t.getSpanJSON();let{spanId:e,traceId:r}=t.spanContext();if(fIt(t)){let{attributes:n,startTime:o,name:i,endTime:s,status:a,links:c}=t,u="parentSpanId"in t?t.parentSpanId:"parentSpanContext"in t?t.parentSpanContext?.spanId:void 0;return{span_id:e,trace_id:r,data:n,description:i,parent_span_id:u,start_timestamp:Xi(o),timestamp:Xi(s)||void 0,status:_m(a),op:n[lt],origin:n[dr],links:gm(c)}}return{span_id:e,trace_id:r,start_timestamp:0,data:{}}}function fIt(t){let e=t;return!!e.attributes&&!!e.startTime&&!!e.name&&!!e.endTime&&!!e.status}function mIt(t){return typeof t.getSpanJSON=="function"}function au(t){let{traceFlags:e}=t.spanContext();return e===s1}function _m(t){if(!(!t||t.code===0))return t.code===1?"ok":t.message||"unknown_error"}var ex="_sentryChildSpans",hV="_sentryRootSpan";function N_(t,e){let r=t[hV]||t;zn(e,hV,r),t[ex]?t[ex].add(e):zn(t,ex,new Set([e]))}function c1(t){let e=new Set;function r(n){if(!e.has(n)&&au(n)){e.add(n);let o=n[ex]?Array.from(n[ex]):[];for(let i of o)r(i)}}return r(t),Array.from(e)}function vn(t){return t[hV]||t}function ma(){let t=wi(),e=gc(t);return e.getActiveSpan?e.getActiveSpan():dm(It())}function tx(){HAe||(_n(()=>{console.warn("[Sentry] Returning null from `beforeSendSpan` is disallowed. To drop certain spans, configure the respective integrations directly.")}),HAe=!0)}var ZAe=!1;function YAe(){if(ZAe)return;function t(){let e=ma(),r=e&&vn(e);if(r){let n="internal_error";Ee&&j.log(`[Tracing] Root span: ${n} -> Global error occurred`),r.setStatus({code:2,message:n})}}t.tag="sentry_tracingErrorCallback",ZAe=!0,xAe(t),AAe(t)}function fo(t){if(typeof __SENTRY_TRACING__=="boolean"&&!__SENTRY_TRACING__)return!1;let e=t||Ce()?.getOptions();return!!e&&(e.tracesSampleRate!=null||!!e.tracesSampler)}var C_="production";var JAe="_frozenDsc";function u1(t,e){zn(t,JAe,e)}function gV(t,e){let r=e.getOptions(),{publicKey:n}=e.getDsn()||{},o={environment:r.environment||C_,release:r.release,public_key:n,trace_id:t,org_id:r1(e)};return e.emit("createDsc",o),o}function El(t,e){let r=e.getPropagationContext();return r.dsc||gV(r.traceId,t)}function Cn(t){let e=Ce();if(!e)return{};let r=vn(t),n=Ye(r),o=n.data,i=r.spanContext().traceState,s=i?.get("sentry.sample_rate")??o[_c]??o[cV];function a(_){return(typeof s=="number"||typeof s=="string")&&(_.sample_rate=`${s}`),_}let c=r[JAe];if(c)return a(c);let u=i?.get("sentry.dsc"),p=u&&yl(u);if(p)return a(p);let f=gV(t.spanContext().traceId,e),m=o[Nn],h=n.description;return m!=="url"&&h&&(f.transaction=h),fo()&&(f.sampled=String(au(r)),f.sample_rand=i?.get("sentry.sample_rand")??vc(r).scope?.getPropagationContext().sampleRand.toString()),a(f),e.emit("createDsc",f,r),f}var dd=class{constructor(e={}){this._traceId=e.traceId||hc(),this._spanId=e.spanId||xs()}spanContext(){return{spanId:this._spanId,traceId:this._traceId,traceFlags:i1}}end(e){}setAttribute(e,r){return this}setAttributes(e){return this}setStatus(e){return this}updateName(e){return this}isRecording(){return!1}addEvent(e,r,n){return this}addLink(e){return this}addLinks(e){return this}recordException(e,r){}};function yc(t,e=100,r=1/0){try{return _V("",t,e,r)}catch(n){return{ERROR:`**non-serializable** (${n})`}}}function vV(t,e=3,r=100*1024){let n=yc(t,e);return vIt(n)>r?vV(t,e-1,r):n}function _V(t,e,r=1/0,n=1/0,o=SIt()){let[i,s]=o;if(e==null||["boolean","string"].includes(typeof e)||typeof e=="number"&&Number.isFinite(e))return e;let a=hIt(t,e);if(!a.startsWith("[object "))return a;if(e.__sentry_skip_normalization__)return e;let c=typeof e.__sentry_override_normalization_depth__=="number"?e.__sentry_override_normalization_depth__:r;if(c===0)return a.replace("object ","");if(i(e))return"[Circular ~]";let u=e;if(u&&typeof u.toJSON=="function")try{let h=u.toJSON();return _V("",h,c-1,n,o)}catch{}let p=Array.isArray(e)?[]:{},f=0,m=JC(e);for(let h in m){if(!Object.prototype.hasOwnProperty.call(m,h))continue;if(f>=n){p[h]="[MaxProperties ~]";break}let _=m[h];p[h]=_V(h,_,c-1,n,o),f++}return s(e),p}function hIt(t,e){try{if(t==="domain"&&e&&typeof e=="object"&&e._events)return"[Domain]";if(t==="domainEmitter")return"[DomainEmitter]";if(typeof global<"u"&&e===global)return"[Global]";if(typeof window<"u"&&e===window)return"[Window]";if(typeof document<"u"&&e===document)return"[Document]";if(Yb(e))return"[VueViewModel]";if(J8(e))return"[SyntheticEvent]";if(typeof e=="number"&&!Number.isFinite(e))return`[${e}]`;if(typeof e=="function")return`[Function: ${Zb(e)}]`;if(typeof e=="symbol")return`[${String(e)}]`;if(typeof e=="bigint")return`[BigInt: ${String(e)}]`;let r=gIt(e);return/^HTML(\w*)Element$/.test(r)?`[HTMLElement: ${r}]`:`[object ${r}]`}catch(r){return`**non-serializable** (${r})`}}function gIt(t){let e=Object.getPrototypeOf(t);return e?.constructor?e.constructor.name:"null prototype"}function _It(t){return~-encodeURI(t).split(/%..|./).length}function vIt(t){return _It(JSON.stringify(t))}function SIt(){let t=new WeakSet;function e(n){return t.has(n)?!0:(t.add(n),!1)}function r(n){t.delete(n)}return[e,r]}function ws(t,e=[]){return[t,e]}function yV(t,e){let[r,n]=t;return[r,[...n,e]]}function l1(t,e){let r=t[1];for(let n of r){let o=n[0].type;if(e(n,o))return!0}return!1}function SV(t){let e=nd(ft);return e.encodePolyfill?e.encodePolyfill(t):new TextEncoder().encode(t)}function rx(t){let[e,r]=t,n=JSON.stringify(e);function o(i){typeof n=="string"?n=typeof i=="string"?n+i:[SV(n),i]:n.push(typeof i=="string"?SV(i):i)}for(let i of r){let[s,a]=i;if(o(` -${JSON.stringify(s)} -`),typeof a=="string"||a instanceof Uint8Array)o(a);else{let c;try{c=JSON.stringify(a)}catch{c=JSON.stringify(yc(a))}o(c)}}return typeof n=="string"?n:yIt(n)}function yIt(t){let e=t.reduce((o,i)=>o+i.length,0),r=new Uint8Array(e),n=0;for(let o of t)r.set(o,n),n+=o.length;return r}function EV(t){return[{type:"span"},t]}function TV(t){let e=typeof t.data=="string"?SV(t.data):t.data;return[{type:"attachment",length:e.length,filename:t.filename,content_type:t.contentType,attachment_type:t.attachmentType},e]}var EIt={session:"session",sessions:"session",attachment:"attachment",transaction:"transaction",event:"error",client_report:"internal",user_report:"default",profile:"profile",profile_chunk:"profile",replay_event:"replay",replay_recording:"replay",check_in:"monitor",feedback:"feedback",span:"span",raw_security:"security",log:"log_item"};function p1(t){return EIt[t]}function d1(t){if(!t?.sdk)return;let{name:e,version:r}=t.sdk;return{name:e,version:r}}function bV(t,e,r,n){let o=t.sdkProcessingMetadata?.dynamicSamplingContext;return{event_id:t.event_id,sent_at:new Date().toISOString(),...e&&{sdk:e},...!!r&&n&&{dsn:Sc(n)},...o&&{trace:o}}}function nx(t,e){if(!e?.length||!t.description)return!1;for(let r of e){if(TIt(r)){if(x_(t.description,r))return!0;continue}if(!r.name&&!r.op)continue;let n=r.name?x_(t.description,r.name):!0,o=r.op?t.op&&x_(t.op,r.op):!0;if(n&&o)return!0}return!1}function XAe(t,e){let r=e.parent_span_id,n=e.span_id;if(r)for(let o of t)o.parent_span_id===n&&(o.parent_span_id=r)}function TIt(t){return typeof t=="string"||t instanceof RegExp}function bIt(t,e){if(!e)return t;let r=t.sdk||{};return t.sdk={...r,name:r.name||e.name,version:r.version||e.version,integrations:[...t.sdk?.integrations||[],...e.integrations||[]],packages:[...t.sdk?.packages||[],...e.packages||[]],settings:t.sdk?.settings||e.settings?{...t.sdk?.settings,...e.settings}:void 0},t}function QAe(t,e,r,n){let o=d1(r),i={sent_at:new Date().toISOString(),...o&&{sdk:o},...!!n&&e&&{dsn:Sc(e)}},s="aggregates"in t?[{type:"sessions"},t]:[{type:"session"},t.toJSON()];return ws(i,[s])}function ewe(t,e,r,n){let o=d1(r),i=t.type&&t.type!=="replay_event"?t.type:"event";bIt(t,r?.sdk);let s=bV(t,o,n,e);return delete t.sdkProcessingMetadata,ws(s,[[{type:i},t]])}function twe(t,e){function r(h){return!!h.trace_id&&!!h.public_key}let n=Cn(t[0]),o=e?.getDsn(),i=e?.getOptions().tunnel,s={sent_at:new Date().toISOString(),...r(n)&&{trace:n},...!!i&&o&&{dsn:Sc(o)}},{beforeSendSpan:a,ignoreSpans:c}=e?.getOptions()||{},u=c?.length?t.filter(h=>!nx(Ye(h),c)):t,p=t.length-u.length;p&&e?.recordDroppedEvent("before_send","span",p);let f=a?h=>{let _=Ye(h),v=a(_);return v||(tx(),_)}:Ye,m=[];for(let h of u){let _=f(h);_&&m.push(EV(_))}return ws(s,m)}function ox(t){if(!Ee)return;let{description:e="< unknown name >",op:r="< unknown op >",parent_span_id:n}=Ye(t),{spanId:o}=t.spanContext(),i=au(t),s=vn(t),a=s===t,c=`[Tracing] Starting ${i?"sampled":"unsampled"} ${a?"root ":""}span`,u=[`op: ${r}`,`name: ${e}`,`ID: ${o}`];if(n&&u.push(`parent ID: ${n}`),!a){let{op:p,description:f}=Ye(s);u.push(`root ID: ${s.spanContext().spanId}`),p&&u.push(`root op: ${p}`),f&&u.push(`root description: ${f}`)}j.log(`${c} - ${u.join(` - `)}`)}function ix(t){if(!Ee)return;let{description:e="< unknown name >",op:r="< unknown op >"}=Ye(t),{spanId:n}=t.spanContext(),i=vn(t)===t,s=`[Tracing] Finishing "${r}" ${i?"root ":""}span "${e}" with ID ${n}`;j.log(s)}function vm(t){if(!t||t.length===0)return;let e={};return t.forEach(r=>{let n=r.attributes||{},o=n[uV],i=n[lV];typeof o=="string"&&typeof i=="number"&&(e[r.name]={value:i,unit:o})}),e}var rwe=1e3,$_=class{constructor(e={}){this._traceId=e.traceId||hc(),this._spanId=e.spanId||xs(),this._startTime=e.startTimestamp||cd(),this._links=e.links,this._attributes={},this.setAttributes({[dr]:"manual",[lt]:e.op,...e.attributes}),this._name=e.name,e.parentSpanId&&(this._parentSpanId=e.parentSpanId),"sampled"in e&&(this._sampled=e.sampled),e.endTimestamp&&(this._endTime=e.endTimestamp),this._events=[],this._isStandaloneSpan=e.isStandalone,this._endTime&&this._onSpanEnded()}addLink(e){return this._links?this._links.push(e):this._links=[e],this}addLinks(e){return this._links?this._links.push(...e):this._links=e,this}recordException(e,r){}spanContext(){let{_spanId:e,_traceId:r,_sampled:n}=this;return{spanId:e,traceId:r,traceFlags:n?s1:i1}}setAttribute(e,r){return r===void 0?delete this._attributes[e]:this._attributes[e]=r,this}setAttributes(e){return Object.keys(e).forEach(r=>this.setAttribute(r,e[r])),this}updateStartTime(e){this._startTime=Xi(e)}setStatus(e){return this._status=e,this}updateName(e){return this._name=e,this.setAttribute(Nn,"custom"),this}end(e){this._endTime||(this._endTime=Xi(e),ix(this),this._onSpanEnded())}getSpanJSON(){return{data:this._attributes,description:this._name,op:this._attributes[lt],parent_span_id:this._parentSpanId,span_id:this._spanId,start_timestamp:this._startTime,status:_m(this._status),timestamp:this._endTime,trace_id:this._traceId,origin:this._attributes[dr],profile_id:this._attributes[I_],exclusive_time:this._attributes[O_],measurements:vm(this._events),is_segment:this._isStandaloneSpan&&vn(this)===this||void 0,segment_id:this._isStandaloneSpan?vn(this).spanContext().spanId:void 0,links:gm(this._links)}}isRecording(){return!this._endTime&&!!this._sampled}addEvent(e,r,n){Ee&&j.log("[Tracing] Adding an event to span:",e);let o=nwe(r)?r:n||cd(),i=nwe(r)?{}:r||{},s={name:e,time:Xi(o),attributes:i};return this._events.push(s),this}isStandaloneSpan(){return!!this._isStandaloneSpan}_onSpanEnded(){let e=Ce();if(e&&e.emit("spanEnd",this),!(this._isStandaloneSpan||this===vn(this)))return;if(this._isStandaloneSpan){this._sampled?AIt(twe([this],e)):(Ee&&j.log("[Tracing] Discarding standalone span because its trace was not chosen to be sampled."),e&&e.recordDroppedEvent("sample_rate","span"));return}let n=this._convertSpanToTransaction();n&&(vc(this).scope||It()).captureEvent(n)}_convertSpanToTransaction(){if(!owe(Ye(this)))return;this._name||(Ee&&j.warn("Transaction has no name, falling back to ``."),this._name="");let{scope:e,isolationScope:r}=vc(this),n=e?.getScopeData().sdkProcessingMetadata?.normalizedRequest;if(this._sampled!==!0)return;let i=c1(this).filter(p=>p!==this&&!xIt(p)).map(p=>Ye(p)).filter(owe),s=this._attributes[Nn];delete this._attributes[su],i.forEach(p=>{delete p.data[su]});let a={contexts:{trace:KAe(this)},spans:i.length>rwe?i.sort((p,f)=>p.start_timestamp-f.start_timestamp).slice(0,rwe):i,start_timestamp:this._startTime,timestamp:this._endTime,transaction:this._name,type:"transaction",sdkProcessingMetadata:{capturedSpanScope:e,capturedSpanIsolationScope:r,dynamicSamplingContext:Cn(this)},request:n,...s&&{transaction_info:{source:s}}},c=vm(this._events);return c&&Object.keys(c).length&&(Ee&&j.log("[Measurements] Adding measurements to transaction event",JSON.stringify(c,void 0,2)),a.measurements=c),a}};function nwe(t){return t&&typeof t=="number"||t instanceof Date||Array.isArray(t)}function owe(t){return!!t.start_timestamp&&!!t.timestamp&&!!t.span_id&&!!t.trace_id}function xIt(t){return t instanceof $_&&t.isStandaloneSpan()}function AIt(t){let e=Ce();if(!e)return;let r=t[1];if(!r||r.length===0){e.recordDroppedEvent("before_send","span");return}e.sendEnvelope(t)}function Tl(t,e,r=()=>{}){let n;try{n=t()}catch(o){throw e(o),r(),o}return wIt(n,e,r)}function wIt(t,e,r){return mc(t)?t.then(n=>(r(),n),n=>{throw e(n),r(),n}):(r(),t)}function sx(t,e,r){if(!fo(t))return[!1];let n,o;typeof t.tracesSampler=="function"?(o=t.tracesSampler({...e,inheritOrSampleWith:a=>typeof e.parentSampleRate=="number"?e.parentSampleRate:typeof e.parentSampled=="boolean"?Number(e.parentSampled):a}),n=!0):e.parentSampled!==void 0?o=e.parentSampled:typeof t.tracesSampleRate<"u"&&(o=t.tracesSampleRate,n=!0);let i=As(o);if(i===void 0)return Ee&&j.warn(`[Tracing] Discarding root span because of invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(o)} of type ${JSON.stringify(typeof o)}.`),[!1];if(!i)return Ee&&j.log(`[Tracing] Discarding transaction because ${typeof t.tracesSampler=="function"?"tracesSampler returned 0 or false":"a negative sampling decision was inherited or tracesSampleRate is set to 0"}`),[!1,i,n];let s=rswe(i)(()=>{let u=It(),p=wV(u,i),m=t.onlyIfParent&&!p?new dd:xV({parentSpan:p,spanArguments:n,forceTransaction:o,scope:u});return pm(u,m),Tl(()=>e(m),()=>{let{status:h}=Ye(m);m.isRecording()&&(!h||h==="ok")&&m.setStatus({code:2,message:"internal_error"})},()=>{m.end()})}))}function M_(t,e){let r=ax();if(r.startSpanManual)return r.startSpanManual(t,e);let n=AV(t),{forceTransaction:o,parentSpan:i,scope:s}=t,a=s?.clone();return iu(a,()=>swe(i)(()=>{let u=It(),p=wV(u,i),m=t.onlyIfParent&&!p?new dd:xV({parentSpan:p,spanArguments:n,forceTransaction:o,scope:u});return pm(u,m),Tl(()=>e(m,()=>m.end()),()=>{let{status:h}=Ye(m);m.isRecording()&&(!h||h==="ok")&&m.setStatus({code:2,message:"internal_error"})})}))}function m1(t){let e=ax();if(e.startInactiveSpan)return e.startInactiveSpan(t);let r=AV(t),{forceTransaction:n,parentSpan:o}=t;return(t.scope?s=>iu(t.scope,s):o!==void 0?s=>fd(o,s):s=>s())(()=>{let s=It(),a=wV(s,o);return t.onlyIfParent&&!a?new dd:xV({parentSpan:a,spanArguments:r,forceTransaction:n,scope:s})})}function fd(t,e){let r=ax();return r.withActiveSpan?r.withActiveSpan(t,e):iu(n=>(pm(n,t||void 0),e(n)))}function D_(t){let e=ax();return e.suppressTracing?e.suppressTracing(t):iu(r=>{r.setSDKProcessingMetadata({[f1]:!0});let n=t();return r.setSDKProcessingMetadata({[f1]:void 0}),n})}function xV({parentSpan:t,spanArguments:e,forceTransaction:r,scope:n}){if(!fo()){let s=new dd;if(r||!t){let a={sampled:"false",sample_rate:"0",transaction:e.name,...Cn(s)};u1(s,a)}return s}let o=nr(),i;if(t&&!r)i=RIt(t,n,e),N_(t,i);else if(t){let s=Cn(t),{traceId:a,spanId:c}=t.spanContext(),u=au(t);i=iwe({traceId:a,parentSpanId:c,...e},n,u),u1(i,s)}else{let{traceId:s,dsc:a,parentSpanId:c,sampled:u}={...o.getPropagationContext(),...n.getPropagationContext()};i=iwe({traceId:s,parentSpanId:c,...e},n,u),a&&u1(i,a)}return ox(i),Jb(i,n,o),i}function AV(t){let r={isStandalone:(t.experimental||{}).standalone,...t};if(t.startTime){let n={...r};return n.startTimestamp=Xi(t.startTime),delete n.startTime,n}return r}function ax(){let t=wi();return gc(t)}function iwe(t,e,r){let n=Ce(),o=n?.getOptions()||{},{name:i=""}=t,s={spanAttributes:{...t.attributes},spanName:i,parentSampled:r};n?.emit("beforeSampling",s,{decision:!1});let a=s.parentSampled??r,c=s.spanAttributes,u=e.getPropagationContext(),[p,f,m]=e.getScopeData().sdkProcessingMetadata[f1]?[!1]:sx(o,{name:i,parentSampled:a,attributes:c,parentSampleRate:As(u.dsc?.sample_rate)},u.sampleRand),h=new $_({...t,attributes:{[Nn]:"custom",[_c]:f!==void 0&&m?f:void 0,...c},sampled:p});return!p&&n&&(Ee&&j.log("[Tracing] Discarding root span because its trace was not chosen to be sampled."),n.recordDroppedEvent("sample_rate","transaction")),n&&n.emit("spanStart",h),h}function RIt(t,e,r){let{spanId:n,traceId:o}=t.spanContext(),i=e.getScopeData().sdkProcessingMetadata[f1]?!1:au(t),s=i?new $_({...r,parentSpanId:n,traceId:o,sampled:i}):new dd({traceId:o});N_(t,s);let a=Ce();return a&&(a.emit("spanStart",s),r.endTimestamp&&a.emit("spanEnd",s)),s}function wV(t,e){if(e)return e;if(e===null)return;let r=dm(t);if(!r)return;let n=Ce();return(n?n.getOptions():{}).parentSpanIsAlwaysRootSpan?vn(r):r}function swe(t){return t!==void 0?e=>fd(t,e):e=>e()}var RV=0,awe=1,cwe=2;function Ec(t){return new cu(e=>{e(t)})}function h1(t){return new cu((e,r)=>{r(t)})}var cu=class t{constructor(e){this._state=RV,this._handlers=[],this._runExecutor(e)}then(e,r){return new t((n,o)=>{this._handlers.push([!1,i=>{if(!e)n(i);else try{n(e(i))}catch(s){o(s)}},i=>{if(!r)o(i);else try{n(r(i))}catch(s){o(s)}}]),this._executeHandlers()})}catch(e){return this.then(r=>r,e)}finally(e){return new t((r,n)=>{let o,i;return this.then(s=>{i=!1,o=s,e&&e()},s=>{i=!0,o=s,e&&e()}).then(()=>{if(i){n(o);return}r(o)})})}_executeHandlers(){if(this._state===RV)return;let e=this._handlers.slice();this._handlers=[],e.forEach(r=>{r[0]||(this._state===awe&&r[1](this._value),this._state===cwe&&r[2](this._value),r[0]=!0)})}_runExecutor(e){let r=(i,s)=>{if(this._state===RV){if(mc(s)){s.then(n,o);return}this._state=i,this._value=s,this._executeHandlers()}},n=i=>{r(awe,i)},o=i=>{r(cwe,i)};try{e(n,o)}catch(i){o(i)}}};function g1(t,e,r,n=0){return new cu((o,i)=>{let s=t[n];if(e===null||typeof s!="function")o(e);else{let a=s({...e},r);Ee&&s.id&&a===null&&j.log(`Event processor "${s.id}" dropped event`),mc(a)?a.then(c=>g1(t,c,r,n+1).then(o)).then(null,i):g1(t,a,r,n+1).then(o).then(null,i)}})}function uwe(t,e){let{fingerprint:r,span:n,breadcrumbs:o,sdkProcessingMetadata:i}=e;PIt(t,e),n&&NIt(t,n),CIt(t,r),IIt(t,o),OIt(t,i)}function PV(t,e){let{extra:r,tags:n,user:o,contexts:i,level:s,sdkProcessingMetadata:a,breadcrumbs:c,fingerprint:u,eventProcessors:p,attachments:f,propagationContext:m,transactionName:h,span:_}=e;_1(t,"extra",r),_1(t,"tags",n),_1(t,"user",o),_1(t,"contexts",i),t.sdkProcessingMetadata=ud(t.sdkProcessingMetadata,a,2),s&&(t.level=s),h&&(t.transactionName=h),_&&(t.span=_),c.length&&(t.breadcrumbs=[...t.breadcrumbs,...c]),u.length&&(t.fingerprint=[...t.fingerprint,...u]),p.length&&(t.eventProcessors=[...t.eventProcessors,...p]),f.length&&(t.attachments=[...t.attachments,...f]),t.propagationContext={...t.propagationContext,...m}}function _1(t,e,r){t[e]=ud(t[e],r,1)}function PIt(t,e){let{extra:r,tags:n,user:o,contexts:i,level:s,transactionName:a}=e;Object.keys(r).length&&(t.extra={...r,...t.extra}),Object.keys(n).length&&(t.tags={...n,...t.tags}),Object.keys(o).length&&(t.user={...o,...t.user}),Object.keys(i).length&&(t.contexts={...i,...t.contexts}),s&&(t.level=s),a&&t.type!=="transaction"&&(t.transaction=a)}function IIt(t,e){let r=[...t.breadcrumbs||[],...e];t.breadcrumbs=r.length?r:void 0}function OIt(t,e){t.sdkProcessingMetadata={...t.sdkProcessingMetadata,...e}}function NIt(t,e){t.contexts={trace:pd(e),...t.contexts},t.sdkProcessingMetadata={dynamicSamplingContext:Cn(e),...t.sdkProcessingMetadata};let r=vn(e),n=Ye(r).description;n&&!t.transaction&&t.type==="transaction"&&(t.transaction=n)}function CIt(t,e){t.fingerprint=t.fingerprint?Array.isArray(t.fingerprint)?t.fingerprint:[t.fingerprint]:[],e&&(t.fingerprint=t.fingerprint.concat(e)),t.fingerprint.length||delete t.fingerprint}var v1,lwe,S1;function pwe(t){let e=ft._sentryDebugIds;if(!e)return{};let r=Object.keys(e);return S1&&r.length===lwe||(lwe=r.length,S1=r.reduce((n,o)=>{v1||(v1={});let i=v1[o];if(i)n[i[0]]=i[1];else{let s=t(o);for(let a=s.length-1;a>=0;a--){let u=s[a]?.filename,p=e[o];if(u&&p){n[u]=p,v1[o]=[u,p];break}}}return n},{})),S1}function dwe(t,e,r,n,o,i){let{normalizeDepth:s=3,normalizeMaxBreadth:a=1e3}=t,c={...e,event_id:e.event_id||r.event_id||Yn(),timestamp:e.timestamp||vl()},u=r.integrations||t.integrations.map(E=>E.name);$It(c,t),DIt(c,u),o&&o.emit("applyFrameMetadata",e),e.type===void 0&&kIt(c,t.stackParser);let p=UIt(n,r.captureContext);r.mechanism&&A_(c,r.mechanism);let f=o?o.getEventProcessors():[],m=QC().getScopeData();if(i){let E=i.getScopeData();PV(m,E)}if(p){let E=p.getScopeData();PV(m,E)}let h=[...r.attachments||[],...m.attachments];h.length&&(r.attachments=h),uwe(c,m);let _=[...f,...m.eventProcessors];return g1(_,c,r).then(E=>(E&&MIt(E),typeof s=="number"&&s>0?LIt(E,s,a):E))}function $It(t,e){let{environment:r,release:n,dist:o,maxValueLength:i=250}=e;t.environment=t.environment||r||C_,!t.release&&n&&(t.release=n),!t.dist&&o&&(t.dist=o);let s=t.request;s?.url&&(s.url=nu(s.url,i))}function kIt(t,e){let r=pwe(e);t.exception?.values?.forEach(n=>{n.stacktrace?.frames?.forEach(o=>{o.filename&&(o.debug_id=r[o.filename])})})}function MIt(t){let e={};if(t.exception?.values?.forEach(n=>{n.stacktrace?.frames?.forEach(o=>{o.debug_id&&(o.abs_path?e[o.abs_path]=o.debug_id:o.filename&&(e[o.filename]=o.debug_id),delete o.debug_id)})}),Object.keys(e).length===0)return;t.debug_meta=t.debug_meta||{},t.debug_meta.images=t.debug_meta.images||[];let r=t.debug_meta.images;Object.entries(e).forEach(([n,o])=>{r.push({type:"sourcemap",code_file:n,debug_id:o})})}function DIt(t,e){e.length>0&&(t.sdk=t.sdk||{},t.sdk.integrations=[...t.sdk.integrations||[],...e])}function LIt(t,e,r){if(!t)return null;let n={...t,...t.breadcrumbs&&{breadcrumbs:t.breadcrumbs.map(o=>({...o,...o.data&&{data:yc(o.data,e,r)}}))},...t.user&&{user:yc(t.user,e,r)},...t.contexts&&{contexts:yc(t.contexts,e,r)},...t.extra&&{extra:yc(t.extra,e,r)}};return t.contexts?.trace&&n.contexts&&(n.contexts.trace=t.contexts.trace,t.contexts.trace.data&&(n.contexts.trace.data=yc(t.contexts.trace.data,e,r))),t.spans&&(n.spans=t.spans.map(o=>({...o,...o.data&&{data:yc(o.data,e,r)}}))),t.contexts?.flags&&n.contexts&&(n.contexts.flags=yc(t.contexts.flags,3,r)),n}function UIt(t,e){if(!e)return t;let r=t?t.clone():new Ji;return r.update(e),r}function fwe(t){if(t)return jIt(t)?{captureContext:t}:FIt(t)?{captureContext:t}:t}function jIt(t){return t instanceof Ji||typeof t=="function"}var zIt=["user","level","extra","contexts","tags","fingerprint","propagationContext"];function FIt(t){return Object.keys(t).some(e=>zIt.includes(e))}function Jn(t,e){return It().captureException(t,fwe(e))}function y1(t,e){return It().captureEvent(t,e)}function E1(t){nr().setTags(t)}function T1(){let t=Ce();return t?.getOptions().enabled!==!1&&!!t?.getTransport()}function b1(t){let e=nr(),r=It(),{userAgent:n}=ft.navigator||{},o=kAe({user:r.getUser()||e.getUser(),...n&&{userAgent:n},...t}),i=e.getSession();return i?.status==="ok"&&Sl(i,{status:"exited"}),cx(),e.setSession(o),o}function cx(){let t=nr(),r=It().getSession()||t.getSession();r&&MAe(r),qIt(),t.setSession()}function qIt(){let t=nr(),e=Ce(),r=t.getSession();r&&e&&e.captureSession(r)}var BIt="7";function GIt(t){let e=t.protocol?`${t.protocol}:`:"",r=t.port?`:${t.port}`:"";return`${e}//${t.host}${r}${t.path?`/${t.path}`:""}/api/`}function VIt(t){return`${GIt(t)}${t.projectId}/envelope/`}function HIt(t,e){let r={sentry_version:BIt};return t.publicKey&&(r.sentry_key=t.publicKey),e&&(r.sentry_client=`${e.name}/${e.version}`),new URLSearchParams(r).toString()}function mwe(t,e,r){return e||`${VIt(t)}?${HIt(t,r)}`}var hwe=[];function WIt(t){let e={};return t.forEach(r=>{let{name:n}=r,o=e[n];o&&!o.isDefaultInstance&&r.isDefaultInstance||(e[n]=r)}),Object.values(e)}function IV(t){let e=t.defaultIntegrations||[],r=t.integrations;e.forEach(o=>{o.isDefaultInstance=!0});let n;if(Array.isArray(r))n=[...e,...r];else if(typeof r=="function"){let o=r(e);n=Array.isArray(o)?o:[o]}else n=e;return WIt(n)}function gwe(t,e){let r={};return e.forEach(n=>{n&&NV(t,n,r)}),r}function OV(t,e){for(let r of e)r?.afterAllSetup&&r.afterAllSetup(t)}function NV(t,e,r){if(r[e.name]){Ee&&j.log(`Integration skipped because it was already installed: ${e.name}`);return}if(r[e.name]=e,hwe.indexOf(e.name)===-1&&typeof e.setupOnce=="function"&&(e.setupOnce(),hwe.push(e.name)),e.setup&&typeof e.setup=="function"&&e.setup(t),typeof e.preprocessEvent=="function"){let n=e.preprocessEvent.bind(e);t.on("preprocessEvent",(o,i)=>n(o,i,t))}if(typeof e.processEvent=="function"){let n=e.processEvent.bind(e),o=Object.assign((i,s)=>n(i,s,t),{id:e.name});t.addEventProcessor(o)}Ee&&j.log(`Integration installed: ${e.name}`)}function _we(t,e,r){let n=[{type:"client_report"},{timestamp:r||vl(),discarded_events:t}];return ws(e?{dsn:e}:{},[n])}function x1(t){let e=[];t.message&&e.push(t.message);try{let r=t.exception.values[t.exception.values.length-1];r?.value&&(e.push(r.value),r.type&&e.push(`${r.type}: ${r.value}`))}catch{}return e}function vwe(t){let{trace_id:e,parent_span_id:r,span_id:n,status:o,origin:i,data:s,op:a}=t.contexts?.trace??{};return{data:s??{},description:t.transaction,op:a,parent_span_id:r,span_id:n??"",start_timestamp:t.start_timestamp??0,status:o,timestamp:t.timestamp,trace_id:e??"",origin:i,profile_id:s?.[I_],exclusive_time:s?.[O_],measurements:t.measurements,is_segment:!0}}function Swe(t){return{type:"transaction",timestamp:t.timestamp,start_timestamp:t.start_timestamp,transaction:t.description,contexts:{trace:{trace_id:t.trace_id,span_id:t.span_id,parent_span_id:t.parent_span_id,op:t.op,status:t.status,origin:t.origin,data:{...t.data,...t.profile_id&&{[I_]:t.profile_id},...t.exclusive_time&&{[O_]:t.exclusive_time}}}},measurements:t.measurements}}var ywe="Not capturing exception because it's already been captured.",Ewe="Discarded session because of missing or non-string release",xwe=Symbol.for("SentryInternalError"),Awe=Symbol.for("SentryDoNotSendEventError");function A1(t){return{message:t,[xwe]:!0}}function CV(t){return{message:t,[Awe]:!0}}function Twe(t){return!!t&&typeof t=="object"&&xwe in t}function bwe(t){return!!t&&typeof t=="object"&&Awe in t}var w1=class{constructor(e){if(this._options=e,this._integrations={},this._numProcessing=0,this._outcomes={},this._hooks={},this._eventProcessors=[],e.dsn?this._dsn=GAe(e.dsn):Ee&&j.warn("No DSN provided, client will not send events."),this._dsn){let r=mwe(this._dsn,e.tunnel,e._metadata?e._metadata.sdk:void 0);this._transport=e.transport({tunnel:this._options.tunnel,recordDroppedEvent:this.recordDroppedEvent.bind(this),...e.transportOptions,url:r})}}captureException(e,r,n){let o=Yn();if(XC(e))return Ee&&j.log(ywe),o;let i={event_id:o,...r};return this._process(this.eventFromException(e,i).then(s=>this._captureEvent(s,i,n))),i.event_id}captureMessage(e,r,n,o){let i={event_id:Yn(),...n},s=T_(e)?e:String(e),a=b_(e)?this.eventFromMessage(s,r,i):this.eventFromException(e,i);return this._process(a.then(c=>this._captureEvent(c,i,o))),i.event_id}captureEvent(e,r,n){let o=Yn();if(r?.originalException&&XC(r.originalException))return Ee&&j.log(ywe),o;let i={event_id:o,...r},s=e.sdkProcessingMetadata||{},a=s.capturedSpanScope,c=s.capturedSpanIsolationScope;return this._process(this._captureEvent(e,i,a||n,c)),i.event_id}captureSession(e){this.sendSession(e),Sl(e,{init:!1})}getDsn(){return this._dsn}getOptions(){return this._options}getSdkMetadata(){return this._options._metadata}getTransport(){return this._transport}flush(e){let r=this._transport;return r?(this.emit("flush"),this._isClientDoneProcessing(e).then(n=>r.flush(e).then(o=>n&&o))):Ec(!0)}close(e){return this.flush(e).then(r=>(this.getOptions().enabled=!1,this.emit("close"),r))}getEventProcessors(){return this._eventProcessors}addEventProcessor(e){this._eventProcessors.push(e)}init(){(this._isEnabled()||this._options.integrations.some(({name:e})=>e.startsWith("Spotlight")))&&this._setupIntegrations()}getIntegrationByName(e){return this._integrations[e]}addIntegration(e){let r=this._integrations[e.name];NV(this,e,this._integrations),r||OV(this,[e])}sendEvent(e,r={}){this.emit("beforeSendEvent",e,r);let n=ewe(e,this._dsn,this._options._metadata,this._options.tunnel);for(let i of r.attachments||[])n=yV(n,TV(i));let o=this.sendEnvelope(n);o&&o.then(i=>this.emit("afterSendEvent",e,i),null)}sendSession(e){let{release:r,environment:n=C_}=this._options;if("aggregates"in e){let i=e.attrs||{};if(!i.release&&!r){Ee&&j.warn(Ewe);return}i.release=i.release||r,i.environment=i.environment||n,e.attrs=i}else{if(!e.release&&!r){Ee&&j.warn(Ewe);return}e.release=e.release||r,e.environment=e.environment||n}this.emit("beforeSendSession",e);let o=QAe(e,this._dsn,this._options._metadata,this._options.tunnel);this.sendEnvelope(o)}recordDroppedEvent(e,r,n=1){if(this._options.sendClientReports){let o=`${e}:${r}`;Ee&&j.log(`Recording outcome: "${o}"${n>1?` (${n} times)`:""}`),this._outcomes[o]=(this._outcomes[o]||0)+n}}on(e,r){let n=this._hooks[e]=this._hooks[e]||[];return n.push(r),()=>{let o=n.indexOf(r);o>-1&&n.splice(o,1)}}emit(e,...r){let n=this._hooks[e];n&&n.forEach(o=>o(...r))}sendEnvelope(e){return this.emit("beforeEnvelope",e),this._isEnabled()&&this._transport?this._transport.send(e).then(null,r=>(Ee&&j.error("Error while sending envelope:",r),r)):(Ee&&j.error("Transport disabled"),Ec({}))}_setupIntegrations(){let{integrations:e}=this._options;this._integrations=gwe(this,e),OV(this,e)}_updateSessionFromEvent(e,r){let n=r.level==="fatal",o=!1,i=r.exception?.values;if(i){o=!0;for(let c of i)if(c.mechanism?.handled===!1){n=!0;break}}let s=e.status==="ok";(s&&e.errors===0||s&&n)&&(Sl(e,{...n&&{status:"crashed"},errors:e.errors||Number(o||n)}),this.captureSession(e))}_isClientDoneProcessing(e){return new cu(r=>{let n=0,o=1,i=setInterval(()=>{this._numProcessing==0?(clearInterval(i),r(!0)):(n+=o,e&&n>=e&&(clearInterval(i),r(!1)))},o)})}_isEnabled(){return this.getOptions().enabled!==!1&&this._transport!==void 0}_prepareEvent(e,r,n,o){let i=this.getOptions(),s=Object.keys(this._integrations);return!r.integrations&&s?.length&&(r.integrations=s),this.emit("preprocessEvent",e,r),e.type||o.setLastEventId(e.event_id||r.event_id),dwe(i,e,r,n,this,o).then(a=>{if(a===null)return a;this.emit("postprocessEvent",a,r),a.contexts={trace:P_(n),...a.contexts};let c=El(this,n);return a.sdkProcessingMetadata={dynamicSamplingContext:c,...a.sdkProcessingMetadata},a})}_captureEvent(e,r={},n=It(),o=nr()){return Ee&&$V(e)&&j.log(`Captured error event \`${x1(e)[0]||""}\``),this._processEvent(e,r,n,o).then(i=>i.event_id,i=>{Ee&&(bwe(i)?j.log(i.message):Twe(i)?j.warn(i.message):j.warn(i))})}_processEvent(e,r,n,o){let i=this.getOptions(),{sampleRate:s}=i,a=wwe(e),c=$V(e),u=e.type||"error",p=`before send for type \`${u}\``,f=typeof s>"u"?void 0:As(s);if(c&&typeof f=="number"&&Math.random()>f)return this.recordDroppedEvent("sample_rate","error"),h1(CV(`Discarding event because it's not included in the random sample (sampling rate = ${s})`));let m=u==="replay_event"?"replay":u;return this._prepareEvent(e,r,n,o).then(h=>{if(h===null)throw this.recordDroppedEvent("event_processor",m),CV("An event processor returned `null`, will not send event.");if(r.data&&r.data.__sentry__===!0)return h;let v=ZIt(this,i,h,r);return KIt(v,p)}).then(h=>{if(h===null){if(this.recordDroppedEvent("before_send",m),a){let x=1+(e.spans||[]).length;this.recordDroppedEvent("before_send","span",x)}throw CV(`${p} returned \`null\`, will not send event.`)}let _=n.getSession()||o.getSession();if(c&&_&&this._updateSessionFromEvent(_,h),a){let E=h.sdkProcessingMetadata?.spanCountBeforeProcessing||0,x=h.spans?h.spans.length:0,w=E-x;w>0&&this.recordDroppedEvent("before_send","span",w)}let v=h.transaction_info;if(a&&v&&h.transaction!==e.transaction){let E="custom";h.transaction_info={...v,source:E}}return this.sendEvent(h,r),h}).then(null,h=>{throw bwe(h)||Twe(h)?h:(this.captureException(h,{mechanism:{handled:!1,type:"internal"},data:{__sentry__:!0},originalException:h}),A1(`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event. -Reason: ${h}`))})}_process(e){this._numProcessing++,e.then(r=>(this._numProcessing--,r),r=>(this._numProcessing--,r))}_clearOutcomes(){let e=this._outcomes;return this._outcomes={},Object.entries(e).map(([r,n])=>{let[o,i]=r.split(":");return{reason:o,category:i,quantity:n}})}_flushOutcomes(){Ee&&j.log("Flushing outcomes...");let e=this._clearOutcomes();if(e.length===0){Ee&&j.log("No outcomes to send");return}if(!this._dsn){Ee&&j.log("No dsn provided, will not send outcomes");return}Ee&&j.log("Sending outcomes:",e);let r=_we(e,this._options.tunnel&&Sc(this._dsn));this.sendEnvelope(r)}};function KIt(t,e){let r=`${e} must return \`null\` or a valid event.`;if(mc(t))return t.then(n=>{if(!_l(n)&&n!==null)throw A1(r);return n},n=>{throw A1(`${e} rejected with ${n}`)});if(!_l(t)&&t!==null)throw A1(r);return t}function ZIt(t,e,r,n){let{beforeSend:o,beforeSendTransaction:i,beforeSendSpan:s,ignoreSpans:a}=e,c=r;if($V(c)&&o)return o(c,n);if(wwe(c)){if(s||a){let u=vwe(c);if(a?.length&&nx(u,a))return null;if(s){let p=s(u);p?c=ud(r,Swe(p)):tx()}if(c.spans){let p=[],f=c.spans;for(let h of f){if(a?.length&&nx(h,a)){XAe(f,h);continue}if(s){let _=s(h);_?p.push(_):(tx(),p.push(h))}else p.push(h)}let m=c.spans.length-p.length;m&&t.recordDroppedEvent("before_send","span",m),c.spans=p}}if(i){if(c.spans){let u=c.spans.length;c.sdkProcessingMetadata={...r.sdkProcessingMetadata,spanCountBeforeProcessing:u}}return i(c,n)}}return c}function $V(t){return t.type===void 0}function wwe(t){return t.type==="transaction"}function Rwe(t,e){return e?iu(e,()=>{let r=ma(),n=r?pd(r):P_(e);return[r?Cn(r):El(t,e),n]}):[void 0,void 0]}function Pwe(t,e,r,n,o){let i={sent_at:new Date().toISOString()};r?.sdk&&(i.sdk={name:r.sdk.name,version:r.sdk.version}),n&&o&&(i.dsn=Sc(o)),e&&(i.trace=e);let s=YIt(t);return ws(i,[s])}function YIt(t){return[{type:"check_in"},t]}function JIt(t){return[{type:"log",item_count:t.length,content_type:"application/vnd.sentry.items.log+json"},{items:t}]}function Iwe(t,e,r,n){let o={};return e?.sdk&&(o.sdk={name:e.sdk.name,version:e.sdk.version}),r&&n&&(o.dsn=Sc(n)),ws(o,[JIt(t)])}function Sm(t,e){let r=e??XIt(t)??[];if(r.length===0)return;let n=t.getOptions(),o=Iwe(r,n._metadata,n.tunnel,t.getDsn());Owe().set(t,[]),t.emit("flushLogs"),t.sendEnvelope(o)}function XIt(t){return Owe().get(t)}function Owe(){return gl("clientToLogBufferMap",()=>new WeakMap)}function Nwe(t,e){return t(e.stack||"",1)}function kV(t,e){let r={type:e.name||e.constructor.name,value:e.message},n=Nwe(t,e);return n.length&&(r.stacktrace={frames:n}),r}function QIt(t){for(let e in t)if(Object.prototype.hasOwnProperty.call(t,e)){let r=t[e];if(r instanceof Error)return r}}function eOt(t){if("name"in t&&typeof t.name=="string"){let n=`'${t.name}' captured as exception`;return"message"in t&&typeof t.message=="string"&&(n+=` with message '${t.message}'`),n}else if("message"in t&&typeof t.message=="string")return t.message;let e=tV(t);if(W8(t))return`Event \`ErrorEvent\` captured as exception with message \`${t.message}\``;let r=tOt(t);return`${r&&r!=="Object"?`'${r}'`:"Object"} captured as exception with keys: ${e}`}function tOt(t){try{let e=Object.getPrototypeOf(t);return e?e.constructor.name:void 0}catch{}}function rOt(t,e,r,n){if(od(r))return[r,void 0];if(e.synthetic=!0,_l(r)){let i=t?.getOptions().normalizeDepth,s={__serialized__:vV(r,i)},a=QIt(r);if(a)return[a,s];let c=eOt(r),u=n?.syntheticException||new Error(c);return u.message=c,[u,s]}let o=n?.syntheticException||new Error(r);return o.message=`${r}`,[o,void 0]}function Cwe(t,e,r,n){let i=n?.data&&n.data.mechanism||{handled:!0,type:"generic"},[s,a]=rOt(t,i,r,n),c={exception:{values:[kV(e,s)]}};return a&&(c.extra=a),nV(c,void 0,void 0),A_(c,i),{...c,event_id:n?.event_id}}function $we(t,e,r="info",n,o){let i={event_id:n?.event_id,level:r};if(o&&n?.syntheticException){let s=Nwe(t,n.syntheticException);s.length&&(i.exception={values:[{value:e,stacktrace:{frames:s}}]},A_(i,{synthetic:!0}))}if(T_(e)){let{__sentry_template_string__:s,__sentry_template_values__:a}=e;return i.logentry={message:s,params:a},i}return i.message=e,i}var nOt=5e3,ux=class extends w1{constructor(e){if(YAe(),super(e),this._logWeight=0,this._options.enableLogs){let r=this;r.on("flushLogs",()=>{r._logWeight=0,clearTimeout(r._logFlushIdleTimeout)}),r.on("afterCaptureLog",n=>{r._logWeight+=oOt(n),r._logWeight>=8e5?Sm(r):r._logFlushIdleTimeout=setTimeout(()=>{Sm(r)},nOt)}),r.on("flush",()=>{Sm(r)})}}eventFromException(e,r){let n=Cwe(this,this._options.stackParser,e,r);return n.level="error",Ec(n)}eventFromMessage(e,r="info",n){return Ec($we(this._options.stackParser,e,r,n,this._options.attachStacktrace))}captureException(e,r,n){return kwe(r),super.captureException(e,r,n)}captureEvent(e,r,n){return!e.type&&e.exception?.values&&e.exception.values.length>0&&kwe(r),super.captureEvent(e,r,n)}captureCheckIn(e,r,n){let o="checkInId"in e&&e.checkInId?e.checkInId:Yn();if(!this._isEnabled())return Ee&&j.warn("SDK not enabled, will not capture check-in."),o;let i=this.getOptions(),{release:s,environment:a,tunnel:c}=i,u={check_in_id:o,monitor_slug:e.monitorSlug,status:e.status,release:s,environment:a};"duration"in e&&(u.duration=e.duration),r&&(u.monitor_config={schedule:r.schedule,checkin_margin:r.checkinMargin,max_runtime:r.maxRuntime,timezone:r.timezone,failure_issue_threshold:r.failureIssueThreshold,recovery_threshold:r.recoveryThreshold});let[p,f]=Rwe(this,n);f&&(u.contexts={trace:f});let m=Pwe(u,p,this.getSdkMetadata(),c,this.getDsn());return Ee&&j.log("Sending checkin:",e.monitorSlug,e.status),this.sendEnvelope(m),o}_prepareEvent(e,r,n,o){return this._options.platform&&(e.platform=e.platform||this._options.platform),this._options.runtime&&(e.contexts={...e.contexts,runtime:e.contexts?.runtime||this._options.runtime}),this._options.serverName&&(e.server_name=e.server_name||this._options.serverName),super._prepareEvent(e,r,n,o)}};function kwe(t){let e=nr().getScopeData().sdkProcessingMetadata.requestSession;if(e){let r=t?.mechanism?.handled??!0;r&&e.status!=="crashed"?e.status="errored":r||(e.status="crashed")}}function oOt(t){let e=0;return t.message&&(e+=t.message.length*2),t.attributes&&Object.values(t.attributes).forEach(r=>{Array.isArray(r)?e+=r.length*Mwe(r[0]):b_(r)?e+=Mwe(r):e+=100}),e}function Mwe(t){return typeof t=="string"?t.length*2:typeof t=="number"?8:typeof t=="boolean"?4:0}var MV=Symbol.for("SentryBufferFullError");function Dwe(t){let e=[];function r(){return t===void 0||e.lengthn(a)).then(null,()=>n(a).then(null,()=>{})),a}function i(s){return new cu((a,c)=>{let u=e.length;if(!u)return a(!0);let p=setTimeout(()=>{s&&s>0&&a(!1)},s);e.forEach(f=>{Ec(f).then(()=>{--u||(clearTimeout(p),a(!0))},c)})})}return{$:e,add:o,drain:i}}function iOt(t,e=Date.now()){let r=parseInt(`${t}`,10);if(!isNaN(r))return r*1e3;let n=Date.parse(`${t}`);return isNaN(n)?6e4:n-e}function sOt(t,e){return t[e]||t.all||0}function Lwe(t,e,r=Date.now()){return sOt(t,e)>r}function Uwe(t,{statusCode:e,headers:r},n=Date.now()){let o={...t},i=r?.["x-sentry-rate-limits"],s=r?.["retry-after"];if(i)for(let a of i.trim().split(",")){let[c,u,,,p]=a.split(":",5),f=parseInt(c,10),m=(isNaN(f)?60:f)*1e3;if(!u)o.all=n+m;else for(let h of u.split(";"))h==="metric_bucket"?(!p||p.split(";").includes("custom"))&&(o[h]=n+m):o[h]=n+m}else s?o.all=n+iOt(s,n):e===429&&(o.all=n+60*1e3);return o}var aOt=64;function lx(t,e,r=Dwe(t.bufferSize||aOt)){let n={},o=s=>r.drain(s);function i(s){let a=[];if(l1(s,(f,m)=>{let h=p1(m);Lwe(n,h)?t.recordDroppedEvent("ratelimit_backoff",h):a.push(f)}),a.length===0)return Ec({});let c=ws(s[0],a),u=f=>{l1(c,(m,h)=>{t.recordDroppedEvent(f,p1(h))})},p=()=>e({body:rx(c)}).then(f=>(f.statusCode!==void 0&&(f.statusCode<200||f.statusCode>=300)&&Ee&&j.warn(`Sentry responded with status code ${f.statusCode} to sent event.`),n=Uwe(n,f),f),f=>{throw u("network_error"),Ee&&j.error("Encountered error running transport request:",f),f});return r.add(p).then(f=>f,f=>{if(f===MV)return Ee&&j.error("Skipped sending event because buffer is full."),u("queue_overflow"),Ec({});throw f})}return{send:i,flush:o}}function ym(t,e,r=[e],n="npm"){let o=t._metadata||{};o.sdk||(o.sdk={name:`sentry.javascript.${e}`,packages:r.map(i=>({name:`${n}:@sentry/${i}`,version:xr})),version:xr}),t._metadata=o}function L_(t={}){let e=t.client||Ce();if(!T1()||!e)return{};let r=wi(),n=gc(r);if(n.getTraceData)return n.getTraceData(t);let o=t.scope||It(),i=t.span||ma(),s=i?a1(i):cOt(o),a=i?Cn(i):El(e,o),c=mm(a);return n1.test(s)?{"sentry-trace":s,baggage:c}:(j.warn("Invalid sentry-trace data. Cannot generate trace data"),{})}function cOt(t){let{traceId:e,sampled:r,propagationSpanId:n}=t.getPropagationContext();return ld(e,n,r)}function DV(t,e,r){let n,o,i,s=r?.maxWait?Math.max(r.maxWait,e):0,a=r?.setTimeoutImpl||setTimeout;function c(){return u(),n=t(),n}function u(){o!==void 0&&clearTimeout(o),i!==void 0&&clearTimeout(i),o=i=void 0}function p(){return o!==void 0||i!==void 0?c():n}function f(){return o&&clearTimeout(o),o=a(c,e),s&&i===void 0&&(i=a(c,s)),n}return f.cancel=u,f.flush=p,f}function jwe(t){let e=Object.create(null);try{Object.entries(t).forEach(([r,n])=>{typeof n=="string"&&(e[r]=n)})}catch{}return e}function R1(t){let e=t.headers||{},n=(typeof e["x-forwarded-host"]=="string"?e["x-forwarded-host"]:void 0)||(typeof e.host=="string"?e.host:void 0),i=(typeof e["x-forwarded-proto"]=="string"?e["x-forwarded-proto"]:void 0)||t.protocol||(t.socket?.encrypted?"https":"http"),s=t.url||"",a=uOt({url:s,host:n,protocol:i}),c=t.body||void 0,u=t.cookies;return{url:a,method:t.method,query_string:zwe(s),headers:jwe(e),cookies:u,data:c}}function uOt({url:t,protocol:e,host:r}){if(t?.startsWith("http"))return t;if(t&&r)return`${e}://${r}${t}`}function zwe(t){if(t)try{let e=new URL(t,"http://s.io").search.slice(1);return e.length?e:void 0}catch{return}}var lOt=100;function ha(t,e){let r=Ce(),n=nr();if(!r)return;let{beforeBreadcrumb:o=null,maxBreadcrumbs:i=lOt}=r.getOptions();if(i<=0)return;let a={timestamp:vl(),...t},c=o?_n(()=>o(a,e)):a;c!==null&&(r.emit&&r.emit("beforeAddBreadcrumb",c,e),n.addBreadcrumb(c,i))}var Fwe,pOt="FunctionToString",qwe=new WeakMap,dOt=(()=>({name:pOt,setupOnce(){Fwe=Function.prototype.toString;try{Function.prototype.toString=function(...t){let e=eV(this),r=qwe.has(Ce())&&e!==void 0?e:this;return Fwe.apply(r,t)}}catch{}},setup(t){qwe.set(t,!0)}})),P1=dOt;var fOt=[/^Script error\.?$/,/^Javascript error: Script error\.? on line 0$/,/^ResizeObserver loop completed with undelivered notifications.$/,/^Cannot redefine property: googletag$/,/^Can't find variable: gmo$/,/^undefined is not an object \(evaluating 'a\.[A-Z]'\)$/,`can't redefine non-configurable property "solana"`,"vv().getRestrictions is not a function. (In 'vv().getRestrictions(1,a)', 'vv().getRestrictions' is undefined)","Can't find variable: _AutofillCallbackHandler",/^Non-Error promise rejection captured with value: Object Not Found Matching Id:\d+, MethodName:simulateEvent, ParamCount:\d+$/,/^Java exception was raised during method invocation$/],mOt="EventFilters",LV=(t={})=>{let e;return{name:mOt,setup(r){let n=r.getOptions();e=Bwe(t,n)},processEvent(r,n,o){if(!e){let i=o.getOptions();e=Bwe(t,i)}return hOt(r,e)?null:r}}},O1=((t={})=>({...LV(t),name:"InboundFilters"}));function Bwe(t={},e={}){return{allowUrls:[...t.allowUrls||[],...e.allowUrls||[]],denyUrls:[...t.denyUrls||[],...e.denyUrls||[]],ignoreErrors:[...t.ignoreErrors||[],...e.ignoreErrors||[],...t.disableErrorDefaults?[]:fOt],ignoreTransactions:[...t.ignoreTransactions||[],...e.ignoreTransactions||[]]}}function hOt(t,e){if(t.type){if(t.type==="transaction"&&_Ot(t,e.ignoreTransactions))return Ee&&j.warn(`Event dropped due to being matched by \`ignoreTransactions\` option. -Event: ${ad(t)}`),!0}else{if(gOt(t,e.ignoreErrors))return Ee&&j.warn(`Event dropped due to being matched by \`ignoreErrors\` option. -Event: ${ad(t)}`),!0;if(EOt(t))return Ee&&j.warn(`Event dropped due to not having an error message, error type or stacktrace. -Event: ${ad(t)}`),!0;if(vOt(t,e.denyUrls))return Ee&&j.warn(`Event dropped due to being matched by \`denyUrls\` option. -Event: ${ad(t)}. -Url: ${I1(t)}`),!0;if(!SOt(t,e.allowUrls))return Ee&&j.warn(`Event dropped due to not being matched by \`allowUrls\` option. -Event: ${ad(t)}. -Url: ${I1(t)}`),!0}return!1}function gOt(t,e){return e?.length?x1(t).some(r=>sd(r,e)):!1}function _Ot(t,e){if(!e?.length)return!1;let r=t.transaction;return r?sd(r,e):!1}function vOt(t,e){if(!e?.length)return!1;let r=I1(t);return r?sd(r,e):!1}function SOt(t,e){if(!e?.length)return!0;let r=I1(t);return r?sd(r,e):!0}function yOt(t=[]){for(let e=t.length-1;e>=0;e--){let r=t[e];if(r&&r.filename!==""&&r.filename!=="[native code]")return r.filename||null}return null}function I1(t){try{let r=[...t.exception?.values??[]].reverse().find(n=>n.mechanism?.parent_id===void 0&&n.stacktrace?.frames?.length)?.stacktrace?.frames;return r?yOt(r):null}catch{return Ee&&j.error(`Cannot extract url for event ${ad(t)}`),null}}function EOt(t){return t.exception?.values?.length?!t.message&&!t.exception.values.some(e=>e.stacktrace||e.type&&e.type!=="Error"||e.value):!1}function Hwe(t,e,r,n,o,i){if(!o.exception?.values||!i||!ru(i.originalException,Error))return;let s=o.exception.values.length>0?o.exception.values[o.exception.values.length-1]:void 0;s&&(o.exception.values=UV(t,e,n,i.originalException,r,o.exception.values,s,0))}function UV(t,e,r,n,o,i,s,a){if(i.length>=r+1)return i;let c=[...i];if(ru(n[o],Error)){Gwe(s,a);let u=t(e,n[o]),p=c.length;Vwe(u,o,p,a),c=UV(t,e,r,n[o],o,[u,...c],u,p)}return Array.isArray(n.errors)&&n.errors.forEach((u,p)=>{if(ru(u,Error)){Gwe(s,a);let f=t(e,u),m=c.length;Vwe(f,`errors[${p}]`,m,a),c=UV(t,e,r,u,o,[f,...c],f,m)}}),c}function Gwe(t,e){t.mechanism=t.mechanism||{type:"generic",handled:!0},t.mechanism={...t.mechanism,...t.type==="AggregateError"&&{is_exception_group:!0},exception_id:e}}function Vwe(t,e,r,n){t.mechanism=t.mechanism||{type:"generic",handled:!0},t.mechanism={...t.mechanism,type:"chained",source:e,exception_id:r,parent_id:n}}var TOt="cause",bOt=5,xOt="LinkedErrors",AOt=((t={})=>{let e=t.limit||bOt,r=t.key||TOt;return{name:xOt,preprocessEvent(n,o,i){let s=i.getOptions();Hwe(kV,s.stackParser,r,e,n,o)}}}),N1=AOt;function Wwe(t){let e={},r=0;for(;r{let i=t[o],s=Array.isArray(i)?i.join(";"):i;return o==="Forwarded"?wOt(s):s?.split(",").map(a=>a.trim())}).reduce((o,i)=>i?o.concat(i):o,[]).find(o=>o!==null&&ROt(o))||null}function wOt(t){if(!t)return null;for(let e of t.split(";"))if(e.startsWith("for="))return e.slice(4);return null}function ROt(t){return/(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$)/.test(t)}var POt={cookies:!0,data:!0,headers:!0,query_string:!0,url:!0},IOt="RequestData",OOt=((t={})=>{let e={...POt,...t.include};return{name:IOt,processEvent(r,n,o){let{sdkProcessingMetadata:i={}}=r,{normalizedRequest:s,ipAddress:a}=i,c={...e,ip:e.ip??o.getOptions().sendDefaultPii};return s&&NOt(r,s,{ipAddress:a},c),r}}}),C1=OOt;function NOt(t,e,r,n){if(t.request={...t.request,...COt(e,n)},n.ip){let o=e.headers&&Kwe(e.headers)||r.ipAddress;o&&(t.user={...t.user,ip_address:o})}}function COt(t,e){let r={},n={...t.headers};if(e.headers&&(r.headers=n,e.cookies||delete n.cookie,e.ip||jV.forEach(o=>{delete n[o]})),r.method=t.method,e.url&&(r.url=t.url),e.cookies){let o=t.cookies||(n?.cookie?Wwe(n.cookie):void 0);r.cookies=o||{}}return e.query_string&&(r.query_string=t.query_string),e.data&&(r.data=t.data),r}function Zwe(t){let e="console";S_(e,t),y_(e,$Ot)}function $Ot(){"console"in ft&&Kb.forEach(function(t){t in ft.console&&fa(ft.console,t,function(e){return v_[t]=e,function(...r){E_("console",{args:r,level:t}),v_[t]?.apply(ft.console,r)}})})}function Ywe(t){return t==="warn"?"warning":["fatal","error","warning","log","info","debug"].includes(t)?t:"log"}var kOt=/^(\S+:\\|\/?)([\s\S]*?)((?:\.{1,2}|[^/\\]+?|)(\.[^./\\]*|))(?:[/\\]*)$/;function MOt(t){let e=t.length>1024?`${t.slice(-1024)}`:t,r=kOt.exec(e);return r?r.slice(1):[]}function zV(t){let e=MOt(t),r=e[0]||"",n=e[1];return!r&&!n?".":(n&&(n=n.slice(0,n.length-1)),r+n)}var DOt="Console",$1=(t={})=>{let e=new Set(t.levels||Kb);return{name:DOt,setup(r){Zwe(({args:n,level:o})=>{Ce()!==r||!e.has(o)||LOt(o,n)})}}};function LOt(t,e){let r={category:"console",data:{arguments:e,logger:"console"},level:Ywe(t),message:Jwe(e)};if(t==="assert")if(e[0]===!1){let n=e.slice(1);r.message=n.length>0?`Assertion failed: ${Jwe(n)}`:"Assertion failed",r.data.arguments=n}else return;ha(r,{input:e,level:t})}function Jwe(t){return"util"in ft&&typeof ft.util.format=="function"?ft.util.format(...t):Q8(t," ")}var UOt="thismessage:/";function FV(t){return"isRelative"in t}function qV(t,e){let r=t.indexOf("://")<=0&&t.indexOf("//")!==0,n=e??(r?UOt:void 0);try{if("canParse"in URL&&!URL.canParse(t,n))return;let o=new URL(t,n);return r?{isRelative:r,pathname:o.pathname,search:o.search,hash:o.hash}:o}catch{}}function md(t){if(!t)return{};let e=t.match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);if(!e)return{};let r=e[6]||"",n=e[8]||"";return{host:e[4],path:e[5],protocol:e[2],search:r,hash:n,relative:e[5]+r+n}}function hd(t){return t.split(/[?#]/,1)[0]}function gd(t){let{protocol:e,host:r,path:n}=t,o=r?.replace(/^.*@/,"[filtered]:[filtered]@").replace(/(:80)$/,"").replace(/(:443)$/,"")||"";return`${e?`${e}://`:""}${o}${n}`}function bl(t,e,r){try{if(!Ce())return;let o=ma();o?.isRecording()&&o.setStatus({code:2,message:"internal_error"}),Jn(t,{mechanism:{type:"mcp_server",handled:!1,data:{error_type:e||"handler_execution",...r}}})}catch{}}function BV(t,e){fa(t,e,r=>function(n,...o){let i=o[o.length-1];if(typeof i!="function")return r.call(this,n,...o);let s=jOt(i,e,n);return r.call(this,n,...o.slice(0,-1),s)})}function jOt(t,e,r){return function(...n){try{return zOt.call(this,t,e,r,n)}catch(o){return Ee&&j.warn("MCP handler wrapping failed:",o),t.apply(this,n)}}}function zOt(t,e,r,n){try{let o=t.apply(this,n);return o&&typeof o=="object"&&typeof o.then=="function"?Promise.resolve(o).catch(i=>{throw Xwe(i,e,r),i}):o}catch(o){throw Xwe(o,e,r),o}}function Xwe(t,e,r){try{let n={};e==="tool"?(n.tool_name=r,t.name==="ProtocolValidationError"||t.message.includes("validation")||t.message.includes("protocol")?bl(t,"validation",n):t.name==="ServerTimeoutError"||t.message.includes("timed out")||t.message.includes("timeout")?bl(t,"timeout",n):bl(t,"tool_execution",n)):e==="resource"?(n.resource_uri=r,bl(t,"resource_execution",n)):e==="prompt"&&(n.prompt_name=r,bl(t,"prompt_execution",n))}catch{}}function FOt(t){BV(t,"tool")}function qOt(t){BV(t,"resource")}function BOt(t){BV(t,"prompt")}function Qwe(t){FOt(t),qOt(t),BOt(t)}var GV="mcp.method.name",eRe="mcp.request.id",tRe="mcp.session.id",rRe="mcp.transport",nRe="mcp.server.name",oRe="mcp.server.title",iRe="mcp.server.version",sRe="mcp.protocol.version",aRe="mcp.tool.name",_d="mcp.resource.uri",cRe="mcp.prompt.name",uRe="mcp.tool.result.is_error",lRe="mcp.tool.result.content_count",pRe="mcp.tool.result.content",dRe="mcp.tool.result",k1="mcp.prompt.result.description",fRe="mcp.prompt.result.message_count",mRe="mcp.prompt.result.message_content",hRe="mcp.prompt.result",U_="mcp.request.argument",gRe="mcp.logging.level",_Re="mcp.logging.logger",vRe="mcp.logging.data_type",px="mcp.logging.message",SRe="network.transport",yRe="network.protocol.version",M1="client.address",D1="client.port",VV="mcp.server",ERe="mcp.notification.client_to_server",TRe="mcp.notification.server_to_client",bRe="auto.function.mcp_server",HV="auto.mcp.notification",xRe="route";var GOt=new Set([M1,D1,px,k1,mRe,_d,pRe]);function VOt(t){return!!(GOt.has(t)||t.startsWith(`${U_}.`)||(t.startsWith(`${dRe}.`)||t.startsWith(`${hRe}.`))&&!t.endsWith("_count")&&!t.endsWith("_error")&&!t.endsWith(".is_error"))}function j_(t,e){return e?t:Object.entries(t).reduce((r,[n,o])=>(VOt(n)||(r[n]=o),r),{})}function ARe(t){return typeof t=="object"&&t!==null&&"jsonrpc"in t&&t.jsonrpc==="2.0"&&"method"in t&&"id"in t}function WV(t){return typeof t=="object"&&t!==null&&"jsonrpc"in t&&t.jsonrpc==="2.0"&&"method"in t&&!("id"in t)}function wRe(t){return typeof t=="object"&&t!==null&&"jsonrpc"in t&&t.jsonrpc==="2.0"&&"id"in t&&("result"in t||"error"in t)}function RRe(t){return typeof t=="object"&&t!==null&&"resource"in t&&"tool"in t&&"prompt"in t&&"connect"in t?!0:(Ee&&j.warn("Did not patch MCP server. Interface is incompatible."),!1)}function Rs(t){return t!=null&&typeof t=="object"}function HOt(t){let e={[lRe]:t.length};for(let[r,n]of t.entries()){if(!Rs(n))continue;let o=t.length===1?"mcp.tool.result":`mcp.tool.result.${r}`,i=(a,c)=>{typeof c=="string"&&(e[`${o}.${a}`]=c)};i("content_type",n.type),i("mime_type",n.mimeType),i("uri",n.uri),i("name",n.name),typeof n.text=="string"&&(e[`${o}.content`]=n.text),typeof n.data=="string"&&(e[`${o}.data_size`]=n.data.length);let s=n.resource;Rs(s)&&(i("resource_uri",s.uri),i("resource_mime_type",s.mimeType))}return e}function PRe(t){if(!Rs(t))return{};let e=Array.isArray(t.content)?HOt(t.content):{};return typeof t.isError=="boolean"&&(e[uRe]=t.isError),e}function IRe(t){let e={};if(!Rs(t))return e;if(typeof t.description=="string"&&(e[k1]=t.description),Array.isArray(t.messages)){e[fRe]=t.messages.length;let r=t.messages;for(let[n,o]of r.entries()){if(!Rs(o))continue;let i=r.length===1?"mcp.prompt.result":`mcp.prompt.result.${n}`;if(((a,c)=>{if(typeof c=="string"){let u=r.length===1?`${i}.message_${a}`:`${i}.${a}`;e[u]=c}})("role",o.role),Rs(o.content)){let a=o.content;if(typeof a.text=="string"){let c=r.length===1?`${i}.message_content`:`${i}.content`;e[c]=a.text}}}}return e}var KV=new WeakMap;function ORe(t){let e=KV.get(t);return e||(e=new Map,KV.set(t,e)),e}function NRe(t,e,r,n){ORe(t).set(e,{span:r,method:n,startTime:Date.now()})}function CRe(t,e,r){let n=ORe(t),o=n.get(e);if(o){let{span:i,method:s}=o;if(s==="tools/call"){let a=PRe(r),u=!!Ce()?.getOptions().sendDefaultPii,p=j_(a,u);i.setAttributes(p)}else if(s==="prompts/get"){let a=IRe(r),u=!!Ce()?.getOptions().sendDefaultPii,p=j_(a,u);i.setAttributes(p)}i.end(),n.delete(e)}}function $Re(t){let e=KV.get(t);if(e){for(let[,r]of e)r.span.setStatus({code:2,message:"cancelled"}),r.span.end();e.clear()}}var Em=new WeakMap;function kRe(t,e){t.sessionId&&Em.set(t,e)}function MRe(t,e){if(t.sessionId){let r=Em.get(t)||{};Em.set(t,{...r,...e})}}function DRe(t){return Em.get(t)?.clientInfo}function LRe(t){return Em.get(t)?.protocolVersion}function URe(t){return Em.get(t)}function jRe(t){Em.delete(t)}function zRe(t){let e={};return Rs(t)&&(typeof t.name=="string"&&(e.name=t.name),typeof t.title=="string"&&(e.title=t.title),typeof t.version=="string"&&(e.version=t.version)),e}function FRe(t){let e={};return Rs(t.params)&&(typeof t.params.protocolVersion=="string"&&(e.protocolVersion=t.params.protocolVersion),t.params.clientInfo&&(e.clientInfo=zRe(t.params.clientInfo))),e}function qRe(t){let e={};return Rs(t)&&(typeof t.protocolVersion=="string"&&(e.protocolVersion=t.protocolVersion),t.serverInfo&&(e.serverInfo=zRe(t.serverInfo))),e}function WOt(t){let e=DRe(t),r={};return e?.name&&(r["mcp.client.name"]=e.name),e?.title&&(r["mcp.client.title"]=e.title),e?.version&&(r["mcp.client.version"]=e.version),r}function KOt(t){let e=URe(t)?.serverInfo,r={};return e?.name&&(r[nRe]=e.name),e?.title&&(r[oRe]=e.title),e?.version&&(r[iRe]=e.version),r}function ZOt(t){return{address:t?.requestInfo?.remoteAddress||t?.clientAddress||t?.request?.ip||t?.request?.connection?.remoteAddress,port:t?.requestInfo?.remotePort||t?.clientPort||t?.request?.connection?.remotePort}}function YOt(t){if(!t?.constructor)return{mcpTransport:"unknown",networkTransport:"unknown"};let e=typeof t.constructor?.name=="string"?t.constructor.name:"unknown",r="unknown",n=e.toLowerCase();return n.includes("stdio")?r="pipe":(n.includes("http")||n.includes("sse"))&&(r="tcp"),{mcpTransport:e,networkTransport:r}}function ZV(t,e){let r=t&&"sessionId"in t?t.sessionId:void 0,n=e?ZOt(e):{},{mcpTransport:o,networkTransport:i}=YOt(t),s=WOt(t),a=KOt(t),c=LRe(t);return{...r&&{[tRe]:r},...n.address&&{[M1]:n.address},...n.port&&{[D1]:n.port},[rRe]:o,[SRe]:i,[yRe]:"2.0",...c&&{[sRe]:c},...s,...a}}var BRe={"tools/call":{targetField:"name",targetAttribute:aRe,captureArguments:!0,argumentsField:"arguments"},"resources/read":{targetField:"uri",targetAttribute:_d,captureUri:!0},"resources/subscribe":{targetField:"uri",targetAttribute:_d},"resources/unsubscribe":{targetField:"uri",targetAttribute:_d},"prompts/get":{targetField:"name",targetAttribute:cRe,captureName:!0,captureArguments:!0,argumentsField:"arguments"}};function dx(t,e){let r=BRe[t];if(!r)return{attributes:{}};let n=r.targetField&&typeof e?.[r.targetField]=="string"?e[r.targetField]:void 0;return{target:n,attributes:n&&r.targetAttribute?{[r.targetAttribute]:n}:{}}}function GRe(t,e){let r={},n=BRe[t];if(!n)return r;if(n.captureArguments&&n.argumentsField&&e?.[n.argumentsField]){let o=e[n.argumentsField];if(typeof o=="object"&&o!==null)for(let[i,s]of Object.entries(o))r[`${U_}.${i.toLowerCase()}`]=JSON.stringify(s)}return n.captureUri&&e?.uri&&(r[`${U_}.uri`]=JSON.stringify(e.uri)),n.captureName&&e?.name&&(r[`${U_}.name`]=JSON.stringify(e.name)),r}function JOt(t,e){let r={};switch(t){case"notifications/cancelled":e?.requestId&&(r["mcp.cancelled.request_id"]=String(e.requestId)),e?.reason&&(r["mcp.cancelled.reason"]=String(e.reason));break;case"notifications/message":e?.level&&(r[gRe]=String(e.level)),e?.logger&&(r[_Re]=String(e.logger)),e?.data!==void 0&&(r[vRe]=typeof e.data,typeof e.data=="string"?r[px]=e.data:r[px]=JSON.stringify(e.data));break;case"notifications/progress":e?.progressToken&&(r["mcp.progress.token"]=String(e.progressToken)),typeof e?.progress=="number"&&(r["mcp.progress.current"]=e.progress),typeof e?.total=="number"&&(r["mcp.progress.total"]=e.total,typeof e?.progress=="number"&&(r["mcp.progress.percentage"]=e.progress/e.total*100)),e?.message&&(r["mcp.progress.message"]=String(e.message));break;case"notifications/resources/updated":if(e?.uri){r[_d]=String(e.uri);let n=qV(String(e.uri));n&&!FV(n)&&(r["mcp.resource.protocol"]=n.protocol.replace(":",""))}break;case"notifications/initialized":r["mcp.lifecycle.phase"]="initialization_complete",r["mcp.protocol.ready"]=1;break}return r}function YV(t,e,r){if(t==="request"){let n=e,o=dx(n.method,r||{});return{...n.id!==void 0&&{[eRe]:String(n.id)},...o.attributes,...GRe(n.method,r||{})}}return JOt(e.method,r||{})}function VRe(t,e){return e?`${t} ${e}`:t}function HRe(t){let e,r;switch(t){case"request":e=VV,r=bRe;break;case"notification-incoming":e=ERe,r=HV;break;case"notification-outgoing":e=TRe,r=HV;break}return{[lt]:e,[dr]:r,[Nn]:xRe}}function WRe(t){let{type:e,message:r,transport:n,extra:o,callback:i}=t,{method:s}=r,a=r.params,c;if(e==="request"){let h=dx(s,a||{});c=VRe(s,h.target)}else c=s;let u={...ZV(n,o),[GV]:s,...YV(e,r,a),...HRe(e)},f=!!Ce()?.getOptions().sendDefaultPii,m=j_(u,f);return k_({name:c,forceTransaction:!0,attributes:m},i)}function KRe(t,e,r,n){return WRe({type:"notification-incoming",message:t,transport:e,extra:r,callback:n})}function ZRe(t,e,r){return WRe({type:"notification-outgoing",message:t,transport:e,callback:r})}function YRe(t,e,r){let{method:n}=t,o=t.params,i=dx(n,o||{}),s=VRe(n,i.target),a={...ZV(e,r),[GV]:n,...YV("request",t,o),...HRe("request")},u=!!Ce()?.getOptions().sendDefaultPii,p=j_(a,u);return{name:s,op:VV,forceTransaction:!0,attributes:p}}function JRe(t){t.onmessage&&fa(t,"onmessage",e=>function(r,n){if(ARe(r)){if(r.method==="initialize")try{let i=FRe(r);kRe(this,i)}catch{}let o=nr().clone();return fm(o,()=>{let i=YRe(r,this,n),s=m1(i);return NRe(this,r.id,s,r.method),fd(s,()=>e.call(this,r,n))})}return WV(r)?KRe(r,this,n,()=>e.call(this,r,n)):e.call(this,r,n)})}function XRe(t){t.send&&fa(t,"send",e=>async function(...r){let[n]=r;if(WV(n))return ZRe(n,this,()=>e.call(this,...r));if(wRe(n)&&n.id!==null&&n.id!==void 0){if(n.error&&XOt(n.error),Rs(n.result)&&(n.result.protocolVersion||n.result.serverInfo))try{let o=qRe(n.result);MRe(this,o)}catch{}CRe(this,n.id,n.result)}return e.call(this,...r)})}function QRe(t){t.onclose&&fa(t,"onclose",e=>function(...r){return $Re(this),jRe(this),e.call(this,...r)})}function ePe(t){t.onerror&&fa(t,"onerror",e=>function(r){return QOt(r),e.call(this,r)})}function XOt(t){try{if(t&&typeof t=="object"&&"code"in t&&"message"in t){let e=t;if(e.code===-32603||e.code>=-32099&&e.code<=-32e3){let n=new Error(e.message);n.name=`JsonRpcError_${e.code}`,bl(n,"protocol")}}}catch{}}function QOt(t){try{bl(t,"transport")}catch{}}var tPe=new WeakSet;function L1(t){if(tPe.has(t)||!RRe(t))return t;let e=t;return fa(e,"connect",r=>async function(n,...o){let i=await r.call(this,n,...o);return JRe(n),XRe(n),QRe(n),ePe(n),i}),Qwe(e),tPe.add(t),t}var JV="ai.prompt",rPe="ai.response.object",nPe="ai.response.text",oPe="ai.response.toolCalls",iPe="ai.prompt.messages",sPe="ai.prompt.tools",uu="ai.model.id",aPe="ai.model.provider",cPe="ai.response.providerMetadata",uPe="ai.telemetry.functionId",lPe="ai.usage.completionTokens",pPe="ai.usage.promptTokens",XV="gen_ai.response.model",z_="gen_ai.usage.input_tokens",F_="gen_ai.usage.output_tokens",QV="ai.toolCall.name",eH="ai.toolCall.id",dPe="ai.toolCall.args",fPe="ai.toolCall.result";function mPe(t,e){t.setAttribute(dr,e)}function eNt(t){let{data:e,description:r}=Ye(t);if(!r)return;if(e[QV]&&e[eH]&&r==="ai.toolCall"){nNt(t,e);return}let n=e[uu],o=e[aPe];typeof n!="string"||typeof o!="string"||!n||!o||oNt(t,r,e)}function tNt(t){if(t.type==="transaction"&&t.spans){let e=new Map;for(let r of t.spans)rNt(r),iNt(r,e);for(let r of t.spans)r.op==="gen_ai.invoke_agent"&&sNt(r,e)}return t}function rNt(t){let{data:e,origin:r}=t;if(r==="auto.vercelai.otel"){ga(e,lPe,F_),ga(e,pPe,z_),typeof e[F_]=="number"&&typeof e[z_]=="number"&&(e["gen_ai.usage.total_tokens"]=e[F_]+e[z_]),ga(e,iPe,"gen_ai.request.messages"),ga(e,nPe,"gen_ai.response.text"),ga(e,oPe,"gen_ai.response.tool_calls"),ga(e,rPe,"gen_ai.response.object"),ga(e,sPe,"gen_ai.request.available_tools"),ga(e,dPe,"gen_ai.tool.input"),ga(e,fPe,"gen_ai.tool.output"),aNt(e);for(let n of Object.keys(e))n.startsWith("ai.")&&ga(e,n,`vercel.${n}`)}}function ga(t,e,r){t[e]!=null&&(t[r]=t[e],delete t[e])}function nNt(t,e){mPe(t,"auto.vercelai.otel"),t.setAttribute(lt,"gen_ai.execute_tool"),ga(e,QV,"gen_ai.tool.name"),ga(e,eH,"gen_ai.tool.call.id"),e["gen_ai.tool.type"]||t.setAttribute("gen_ai.tool.type","function");let r=e["gen_ai.tool.name"];r&&t.updateName(`execute_tool ${r}`)}function oNt(t,e,r){mPe(t,"auto.vercelai.otel");let n=e.replace("ai.","");t.setAttribute("ai.pipeline.name",n),t.updateName(n);let o=r[uPe];if(o&&typeof o=="string"&&e.split(".").length-1===1&&(t.updateName(`${n} ${o}`),t.setAttribute("gen_ai.function_id",o)),r[JV]&&t.setAttribute("gen_ai.prompt",r[JV]),r[uu]&&!r[XV]&&t.setAttribute(XV,r[uu]),t.setAttribute("ai.streaming",e.includes("stream")),e==="ai.generateText"){t.setAttribute(lt,"gen_ai.invoke_agent");return}if(e==="ai.generateText.doGenerate"){t.setAttribute(lt,"gen_ai.generate_text"),t.updateName(`generate_text ${r[uu]}`);return}if(e==="ai.streamText"){t.setAttribute(lt,"gen_ai.invoke_agent");return}if(e==="ai.streamText.doStream"){t.setAttribute(lt,"gen_ai.stream_text"),t.updateName(`stream_text ${r[uu]}`);return}if(e==="ai.generateObject"){t.setAttribute(lt,"gen_ai.invoke_agent");return}if(e==="ai.generateObject.doGenerate"){t.setAttribute(lt,"gen_ai.generate_object"),t.updateName(`generate_object ${r[uu]}`);return}if(e==="ai.streamObject"){t.setAttribute(lt,"gen_ai.invoke_agent");return}if(e==="ai.streamObject.doStream"){t.setAttribute(lt,"gen_ai.stream_object"),t.updateName(`stream_object ${r[uu]}`);return}if(e==="ai.embed"){t.setAttribute(lt,"gen_ai.invoke_agent");return}if(e==="ai.embed.doEmbed"){t.setAttribute(lt,"gen_ai.embed"),t.updateName(`embed ${r[uu]}`);return}if(e==="ai.embedMany"){t.setAttribute(lt,"gen_ai.invoke_agent");return}if(e==="ai.embedMany.doEmbed"){t.setAttribute(lt,"gen_ai.embed_many"),t.updateName(`embed_many ${r[uu]}`);return}if(e.startsWith("ai.stream")){t.setAttribute(lt,"ai.run");return}}function U1(t){t.on("spanStart",eNt),t.addEventProcessor(Object.assign(tNt,{id:"VercelAiEventProcessor"}))}function iNt(t,e){let r=t.parent_span_id;if(!r)return;let n=t.data[z_],o=t.data[F_];if(typeof n=="number"||typeof o=="number"){let i=e.get(r)||{inputTokens:0,outputTokens:0};typeof n=="number"&&(i.inputTokens+=n),typeof o=="number"&&(i.outputTokens+=o),e.set(r,i)}}function sNt(t,e){let r=e.get(t.span_id);r&&(r.inputTokens>0&&(t.data[z_]=r.inputTokens),r.outputTokens>0&&(t.data[F_]=r.outputTokens),(r.inputTokens>0||r.outputTokens>0)&&(t.data["gen_ai.usage.total_tokens"]=r.inputTokens+r.outputTokens))}function aNt(t){let e=t[cPe];if(e)try{let r=JSON.parse(e);r.openai&&(Tc(t,"gen_ai.usage.input_tokens.cached",r.openai.cachedPromptTokens),Tc(t,"gen_ai.usage.output_tokens.reasoning",r.openai.reasoningTokens),Tc(t,"gen_ai.usage.output_tokens.prediction_accepted",r.openai.acceptedPredictionTokens),Tc(t,"gen_ai.usage.output_tokens.prediction_rejected",r.openai.rejectedPredictionTokens),Tc(t,"gen_ai.conversation.id",r.openai.responseId)),r.anthropic&&(Tc(t,"gen_ai.usage.input_tokens.cached",r.anthropic.cacheReadInputTokens),Tc(t,"gen_ai.usage.input_tokens.cache_write",r.anthropic.cacheCreationInputTokens)),r.bedrock?.usage&&(Tc(t,"gen_ai.usage.input_tokens.cached",r.bedrock.usage.cacheReadInputTokens),Tc(t,"gen_ai.usage.input_tokens.cache_write",r.bedrock.usage.cacheWriteInputTokens)),r.deepseek&&(Tc(t,"gen_ai.usage.input_tokens.cached",r.deepseek.promptCacheHitTokens),Tc(t,"gen_ai.usage.input_tokens.cache_miss",r.deepseek.promptCacheMissTokens))}catch{}}function Tc(t,e,r){r!=null&&(t[e]=r)}var hPe="gen_ai.system",j1="gen_ai.request.model",gPe="gen_ai.request.stream",_Pe="gen_ai.request.temperature",vPe="gen_ai.request.frequency_penalty",SPe="gen_ai.request.presence_penalty",yPe="gen_ai.request.top_p",fx="gen_ai.response.finish_reasons",EPe="gen_ai.response.model",TPe="gen_ai.response.id",bPe="gen_ai.usage.input_tokens",xPe="gen_ai.usage.output_tokens",APe="gen_ai.usage.total_tokens",wPe="gen_ai.operation.name",tH="gen_ai.request.messages",mx="gen_ai.response.text",RPe="gen_ai.request.available_tools",PPe="gen_ai.response.streaming",hx="gen_ai.response.tool_calls",IPe="openai.response.id",OPe="openai.response.model",NPe="openai.response.timestamp",CPe="openai.usage.completion_tokens",$Pe="openai.usage.prompt_tokens",rH={CHAT:"chat",RESPONSES:"responses"};var vd="OpenAI",kPe=["responses.create","chat.completions.create"],cNt=["response.output_item.added","response.function_call_arguments.delta","response.function_call_arguments.done","response.output_item.done"],MPe=["response.created","response.in_progress","response.failed","response.completed","response.incomplete","response.queued","response.output_text.delta",...cNt];function z1(t){return t.includes("chat.completions")?rH.CHAT:t.includes("responses")?rH.RESPONSES:t.split(".").pop()||"unknown"}function nH(t){return`gen_ai.${z1(t)}`}function DPe(t){return kPe.includes(t)}function LPe(t,e){return t?`${t}.${e}`:e}function UPe(t){return t!==null&&typeof t=="object"&&"object"in t&&t.object==="chat.completion"}function jPe(t){return t!==null&&typeof t=="object"&&"object"in t&&t.object==="response"}function zPe(t){return t!==null&&typeof t=="object"&&"type"in t&&typeof t.type=="string"&&t.type.startsWith("response.")}function FPe(t){return t!==null&&typeof t=="object"&&"object"in t&&t.object==="chat.completion.chunk"}function gx(t,e,r,n){e!==void 0&&t.setAttributes({[$Pe]:e,[bPe]:e}),r!==void 0&&t.setAttributes({[CPe]:r,[xPe]:r}),n!==void 0&&t.setAttributes({[APe]:n})}function _x(t,e,r,n){t.setAttributes({[IPe]:e,[TPe]:e}),t.setAttributes({[OPe]:r,[EPe]:r}),t.setAttributes({[NPe]:new Date(n*1e3).toISOString()})}function uNt(t,e){for(let r of t){let n=r.index;if(!(n===void 0||!r.function))if(!(n in e.chatCompletionToolCalls))e.chatCompletionToolCalls[n]={...r,function:{name:r.function.name,arguments:r.function.arguments||""}};else{let o=e.chatCompletionToolCalls[n];r.function.arguments&&o?.function&&(o.function.arguments+=r.function.arguments)}}}function lNt(t,e,r){e.responseId=t.id??e.responseId,e.responseModel=t.model??e.responseModel,e.responseTimestamp=t.created??e.responseTimestamp,t.usage&&(e.promptTokens=t.usage.prompt_tokens,e.completionTokens=t.usage.completion_tokens,e.totalTokens=t.usage.total_tokens);for(let n of t.choices??[])r&&(n.delta?.content&&e.responseTexts.push(n.delta.content),n.delta?.tool_calls&&uNt(n.delta.tool_calls,e)),n.finish_reason&&e.finishReasons.push(n.finish_reason)}function pNt(t,e,r,n){if(!(t&&typeof t=="object")){e.eventTypes.push("unknown:non-object");return}if(t instanceof Error){n.setStatus({code:2,message:"internal_error"}),Jn(t,{mechanism:{handled:!1}});return}if(!("type"in t))return;let o=t;if(!MPe.includes(o.type)){e.eventTypes.push(o.type);return}if(r&&(o.type==="response.output_item.done"&&"item"in o&&e.responsesApiToolCalls.push(o.item),o.type==="response.output_text.delta"&&"delta"in o&&o.delta)){e.responseTexts.push(o.delta);return}if("response"in o){let{response:i}=o;e.responseId=i.id??e.responseId,e.responseModel=i.model??e.responseModel,e.responseTimestamp=i.created_at??e.responseTimestamp,i.usage&&(e.promptTokens=i.usage.input_tokens,e.completionTokens=i.usage.output_tokens,e.totalTokens=i.usage.total_tokens),i.status&&e.finishReasons.push(i.status),r&&i.output_text&&e.responseTexts.push(i.output_text)}}async function*qPe(t,e,r){let n={eventTypes:[],responseTexts:[],finishReasons:[],responseId:"",responseModel:"",responseTimestamp:0,promptTokens:void 0,completionTokens:void 0,totalTokens:void 0,chatCompletionToolCalls:{},responsesApiToolCalls:[]};try{for await(let o of t)FPe(o)?lNt(o,n,r):zPe(o)&&pNt(o,n,r,e),yield o}finally{_x(e,n.responseId,n.responseModel,n.responseTimestamp),gx(e,n.promptTokens,n.completionTokens,n.totalTokens),e.setAttributes({[PPe]:!0}),n.finishReasons.length&&e.setAttributes({[fx]:JSON.stringify(n.finishReasons)}),r&&n.responseTexts.length&&e.setAttributes({[mx]:n.responseTexts.join("")});let i=[...Object.values(n.chatCompletionToolCalls),...n.responsesApiToolCalls];i.length>0&&e.setAttributes({[hx]:JSON.stringify(i)}),e.end()}}function dNt(t,e){let r={[hPe]:"openai",[wPe]:z1(e)};if(t.length>0&&typeof t[0]=="object"&&t[0]!==null){let n=t[0],o=Array.isArray(n.tools)?n.tools:[],s=n.web_search_options&&typeof n.web_search_options=="object"?[{type:"web_search_options",...n.web_search_options}]:[],a=[...o,...s];a.length>0&&(r[RPe]=JSON.stringify(a))}if(t.length>0&&typeof t[0]=="object"&&t[0]!==null){let n=t[0];r[j1]=n.model??"unknown","temperature"in n&&(r[_Pe]=n.temperature),"top_p"in n&&(r[yPe]=n.top_p),"frequency_penalty"in n&&(r[vPe]=n.frequency_penalty),"presence_penalty"in n&&(r[SPe]=n.presence_penalty),"stream"in n&&(r[gPe]=n.stream)}else r[j1]="unknown";return r}function fNt(t,e,r){if(_x(t,e.id,e.model,e.created),e.usage&&gx(t,e.usage.prompt_tokens,e.usage.completion_tokens,e.usage.total_tokens),Array.isArray(e.choices)){let n=e.choices.map(o=>o.finish_reason).filter(o=>o!==null);if(n.length>0&&t.setAttributes({[fx]:JSON.stringify(n)}),r){let o=e.choices.map(i=>i.message?.tool_calls).filter(i=>Array.isArray(i)&&i.length>0).flat();o.length>0&&t.setAttributes({[hx]:JSON.stringify(o)})}}}function mNt(t,e,r){if(_x(t,e.id,e.model,e.created_at),e.status&&t.setAttributes({[fx]:JSON.stringify([e.status])}),e.usage&&gx(t,e.usage.input_tokens,e.usage.output_tokens,e.usage.total_tokens),r){let n=e;if(Array.isArray(n.output)&&n.output.length>0){let o=n.output.filter(i=>typeof i=="object"&&i!==null&&i.type==="function_call");o.length>0&&t.setAttributes({[hx]:JSON.stringify(o)})}}}function hNt(t,e,r){if(!e||typeof e!="object")return;let n=e;if(UPe(n)){if(fNt(t,n,r),r&&n.choices?.length){let o=n.choices.map(i=>i.message?.content||"");t.setAttributes({[mx]:JSON.stringify(o)})}}else jPe(n)&&(mNt(t,n,r),r&&n.output_text&&t.setAttributes({[mx]:n.output_text}))}function BPe(t,e){"messages"in e&&t.setAttributes({[tH]:JSON.stringify(e.messages)}),"input"in e&&t.setAttributes({[tH]:JSON.stringify(e.input)})}function gNt(){let e=It().getClient(),r=e?.getIntegrationByName(vd),n=r?!!e?.getOptions().sendDefaultPii:!1;return{recordInputs:r?.options?.recordInputs??n,recordOutputs:r?.options?.recordOutputs??n}}function _Nt(t,e,r,n){return async function(...i){let s=n||gNt(),a=dNt(i,e),c=a[j1]||"unknown",u=z1(e),p=i[0];return p&&typeof p=="object"&&p.stream===!0?M_({name:`${u} ${c} stream-response`,op:nH(e),attributes:a},async m=>{try{s.recordInputs&&i[0]&&typeof i[0]=="object"&&BPe(m,i[0]);let h=await t.apply(r,i);return qPe(h,m,s.recordOutputs??!1)}catch(h){throw m.setStatus({code:2,message:"internal_error"}),Jn(h,{mechanism:{handled:!1}}),m.end(),h}}):k_({name:`${u} ${c}`,op:nH(e),attributes:a},async m=>{try{s.recordInputs&&i[0]&&typeof i[0]=="object"&&BPe(m,i[0]);let h=await t.apply(r,i);return hNt(m,h,s.recordOutputs),h}catch(h){throw Jn(h),h}})}}function GPe(t,e="",r){return new Proxy(t,{get(n,o){let i=n[o],s=LPe(e,String(o));return typeof i=="function"&&DPe(s)?_Nt(i,s,n,r):typeof i=="function"?i.bind(n):i&&typeof i=="object"?GPe(i,s,r):i}})}function oH(t,e){return GPe(t,"",e)}function vx(t){if(t!==void 0)return t>=400&&t<500?"warning":t>=500?"error":void 0}function HPe(t,e=!1){return!(e||t&&!t.startsWith("/")&&!t.match(/^[A-Z]:/)&&!t.startsWith(".")&&!t.match(/^[a-zA-Z]([a-zA-Z0-9.\-+])*:\/\//))&&t!==void 0&&!t.includes("node_modules/")}function WPe(t){let e=/^\s*[-]{4,}$/,r=/at (?:async )?(?:(.+?)\s+\()?(?:(.+):(\d+):(\d+)?|([^)]+))\)?/,n=/at (?:async )?(.+?) \(data:(.*?),/;return o=>{let i=o.match(n);if(i)return{filename:``,function:i[1]};let s=o.match(r);if(s){let a,c,u,p,f;if(s[1]){u=s[1];let _=u.lastIndexOf(".");if(u[_-1]==="."&&_--,_>0){a=u.slice(0,_),c=u.slice(_+1);let v=a.indexOf(".Module");v>0&&(u=u.slice(v+1),a=a.slice(0,v))}p=void 0}c&&(p=a,f=c),c===""&&(f=void 0,u=void 0),u===void 0&&(f=f||"?",u=p?`${p}.${f}`:f);let m=s[2]?.startsWith("file://")?s[2].slice(7):s[2],h=s[5]==="native";return m?.match(/\/[A-Z]:/)&&(m=m.slice(1)),!m&&s[5]&&!h&&(m=s[5]),{filename:m?decodeURI(m):void 0,module:t?t(m):void 0,function:u,lineno:VPe(s[3]),colno:VPe(s[4]),in_app:HPe(m||"",h)}}if(o.match(e))return{filename:o}}}function iH(t){return[90,WPe(t)]}function VPe(t){return parseInt(t||"",10)||void 0}var Qi=class{constructor(e){this._maxSize=e,this._cache=new Map}get size(){return this._cache.size}get(e){let r=this._cache.get(e);if(r!==void 0)return this._cache.delete(e),this._cache.set(e,r),r}set(e,r){this._cache.size>=this._maxSize&&this._cache.delete(this._cache.keys().next().value),this._cache.set(e,r)}remove(e){let r=this._cache.get(e);return r&&this._cache.delete(e),r}clear(){this._cache.clear()}keys(){return Array.from(this._cache.keys())}values(){let e=[];return this._cache.forEach(r=>e.push(r)),e}};var sH=W(Ft(),1),Sx={};function Ge(t,e,r){return r?FNt(t,e,r):zNt(t,e)}function zNt(t,e){return Object.assign(r=>{let n=Sx[t];if(n)return r&&n.setConfig(r),n;let o=e(r);return Sx[t]=o,(0,sH.registerInstrumentations)({instrumentations:[o]}),o},{id:t})}function FNt(t,e,r){return Object.assign(n=>{let o=r(n),i=Sx[t];if(i)return i.setConfig(o),i;let s=new e(o);return Sx[t]=s,(0,sH.registerInstrumentations)({instrumentations:[s]}),s},{id:t})}function yx(t){let e=!1,r=[];if(!qNt(t))e=!0;else{let o=t._wrap;t._wrap=(...i)=>(e=!0,r.forEach(s=>s()),r=[],o(...i))}return o=>{e?o():r.push(o)}}function qNt(t){return typeof t._wrap=="function"}var gu=require("node:diagnostics_channel");pe();var AOe=W(_r(),1),Nx=W(Ft(),1);var Kt=typeof __SENTRY_DEBUG__>"u"||__SENTRY_DEBUG__;function Ex(t){let e=t.protocol||"",r=t.hostname||t.host||"",n=!t.port||t.port===80||t.port===443||/^(.*):(\d+)$/.test(r)?"":`:${t.port}`,o=t.path?t.path:"/";return`${e}//${r}${n}${o}`}var ri="@sentry/instrumentation-http";pe();var aH=new Map;function KPe(t,{ignoreIncomingRequestBody:e,maxIncomingRequestBodySize:r="medium",trackIncomingRequestsAsSessions:n=!0,sessionFlushingDelayMS:o}){let i=t.emit;if(i.__sentry_patched__)return;let s=new Proxy(i,{apply(a,c,u){if(u[0]!=="request")return a.apply(c,u);Kt&&j.log(ri,"Handling incoming request");let p=nr().clone(),f=u[1],m=u[2],h=R1(f),_=f.ip||f.socket?.remoteAddress,v=f.url||"/";!e?.(v,f)&&r!=="none"&&VNt(f,p,r),p.setSDKProcessingMetadata({normalizedRequest:h,ipAddress:_});let E=(f.method||"GET").toUpperCase(),x=hd(v),w=`${E} ${x}`;return p.setTransactionName(w),n!==!1&&GNt({requestIsolationScope:p,response:m,sessionFlushingDelayMS:o??6e4}),fm(p,()=>{It().getPropagationContext().propagationSpanId=xs();let I=bi.extract(Je.active(),h.headers);return Je.with(I,()=>a.apply(c,u))})}});zn(s,"__sentry_patched__",!0),t.emit=s}function GNt({requestIsolationScope:t,response:e,sessionFlushingDelayMS:r}){t.setSDKProcessingMetadata({requestSession:{status:"ok"}}),e.once("close",()=>{let n=Ce(),o=t.getScopeData().sdkProcessingMetadata.requestSession;if(n&&o){Kt&&j.log(`Recorded request session with status: ${o.status}`);let i=new Date;i.setSeconds(0,0);let s=i.toISOString(),a=aH.get(n),c=a?.[s]||{exited:0,crashed:0,errored:0};if(c[{ok:"exited",crashed:"crashed",errored:"errored"}[o.status]]++,a)a[s]=c;else{Kt&&j.log("Opened new request session aggregate.");let u={[s]:c};aH.set(n,u);let p=()=>{clearTimeout(m),f(),aH.delete(n);let h=Object.entries(u).map(([_,v])=>({started:_,exited:v.exited,errored:v.errored,crashed:v.crashed}));n.sendSession({aggregates:h})},f=n.on("flush",()=>{Kt&&j.log("Sending request session aggregate due to client flush"),p()}),m=setTimeout(()=>{Kt&&j.log("Sending request session aggregate due to flushing schedule"),p()},r).unref()}}})}function VNt(t,e,r){let n=0,o=[];Kt&&j.log(ri,"Patching request.on");let i=new WeakMap,s=r==="small"?1e3:r==="medium"?1e4:1048576;try{t.on=new Proxy(t.on,{apply:(a,c,u)=>{let[p,f,...m]=u;if(p==="data"){Kt&&j.log(ri,`Handling request.on("data") with maximum body size of ${s}b`);let h=new Proxy(f,{apply:(_,v,E)=>{try{let x=E[0],w=Buffer.from(x);n{let[,p]=u,f=i.get(p);if(f){i.delete(p);let m=u.slice();return m[1]=f,Reflect.apply(a,c,m)}return Reflect.apply(a,c,u)}}),t.on("end",()=>{try{let a=Buffer.concat(o).toString("utf-8");if(a){let u=Buffer.byteLength(a,"utf-8")>s?`${Buffer.from(a).subarray(0,s-3).toString("utf-8")}...`:a;e.setSDKProcessingMetadata({normalizedRequest:{data:u}})}}catch(a){Kt&&j.error(ri,"Error building captured request body",a)}})}catch(a){Kt&&j.error(ri,"Error patching request to capture body",a)}}er();pe();pe();var Sa=W(_r(),1),Sd=W(WH(),1),r3="sentry.parentIsRemote",X_="sentry.graphql.operation";function n3(t){if("parentSpanId"in t)return t.parentSpanId;if("parentSpanContext"in t)return t.parentSpanContext?.spanId}function o3(t){let e=t;return!!e.attributes&&typeof e.attributes=="object"}function S1t(t){return typeof t.kind=="number"}function y1t(t){return!!t.status}function JIe(t){return!!t.name}function E1t(t){if(!o3(t))return{};let e=t.attributes[t_]||t.attributes[e_],r={url:e,"http.method":t.attributes[om]||t.attributes[Eb]};!r["http.method"]&&r.url&&(r["http.method"]="GET");try{if(typeof e=="string"){let n=md(e);r.url=gd(n),n.search&&(r["http.query"]=n.search),n.hash&&(r["http.fragment"]=n.hash)}}catch{}return r}function T1t(t){return S1t(t)?t.kind:Zn.INTERNAL}var KH="sentry-trace",ZH="baggage",i3="sentry.dsc",s3="sentry.sampled_not_recording",XIe="sentry.url",b1t="sentry.sample_rand",x1t="sentry.sample_rate",QIe=pc("sentry_scopes"),YH=pc("sentry_fork_isolation_scope"),JH=pc("sentry_fork_set_scope"),XH=pc("sentry_fork_set_isolation_scope"),eOe="_scopeContext";function Q_(t){return t.getValue(QIe)}function tOe(t,e){return t.setValue(QIe,e)}function A1t(t,e){zn(t,eOe,e)}function Px(t){return t[eOe]}function J_(t){let{traceFlags:e,traceState:r}=t,n=r?r.get(s3)==="1":!1;if(e===ca.SAMPLED)return!0;if(n)return!1;let o=r?r.get(i3):void 0,i=o?yl(o):void 0;if(i?.sampled==="true")return!0;if(i?.sampled==="false")return!1}function rOe(t,e,r){let n=e[om]||e[Eb];if(n)return R1t({attributes:e,name:t,kind:r},n);let o=e[S5],i=typeof e[lt]=="string"&&e[lt].startsWith("cache.");if(o&&!i)return w1t({attributes:e,name:t});let s=e[Nn]==="custom"?"custom":"route";if(e[A5])return{...Rx(t,e,"route"),op:"rpc"};if(e[x5])return{...Rx(t,e,s),op:"message"};let u=e[E5];return u?{...Rx(t,e,s),op:u.toString()}:{op:void 0,description:t,source:"custom"}}function nOe(t){let e=o3(t)?t.attributes:{},r=JIe(t)?t.name:"",n=T1t(t);return rOe(r,e,n)}function w1t({attributes:t,name:e}){let r=t[su];if(typeof r=="string")return{op:"db",description:r,source:t[Nn]||"custom"};if(t[Nn]==="custom")return{op:"db",description:e,source:"custom"};let n=t[y5];return{op:"db",description:n?n.toString():e,source:"task"}}function R1t({name:t,kind:e,attributes:r},n){let o=["http"];switch(e){case Zn.CLIENT:o.push("client");break;case Zn.SERVER:o.push("server");break}r["sentry.http.prefetch"]&&o.push("prefetch");let{urlPath:i,url:s,query:a,fragment:c,hasRoute:u}=I1t(r,e);if(!i)return{...Rx(t,r),op:o.join(".")};let p=r[X_],f=`${n} ${i}`,m=p?`${f} (${P1t(p)})`:f,h=u||i==="/"?"route":"url",_={};s&&(_.url=s),a&&(_["http.query"]=a),c&&(_["http.fragment"]=c);let v=e===Zn.CLIENT||e===Zn.SERVER,x=!`${r[dr]||"manual"}`.startsWith("auto"),w=r[Nn]==="custom",I=r[su],N=!w&&I==null&&(v||!x),{description:$,source:B}=N?{description:m,source:h}:Rx(t,r);return{op:o.join("."),description:$,source:B,data:_}}function P1t(t){if(Array.isArray(t)){let e=t.slice().sort();return e.length<=5?e.join(", "):`${e.slice(0,5).join(", ")}, +${e.length-5}`}return`${t}`}function I1t(t,e){let r=t[T5],n=t[e_]||t[t_],o=t[Qc],i=typeof n=="string"?md(n):void 0,s=i?gd(i):void 0,a=i?.search||void 0,c=i?.hash||void 0;return typeof o=="string"?{urlPath:o,url:s,query:a,fragment:c,hasRoute:!0}:e===Zn.SERVER&&typeof r=="string"?{urlPath:hd(r),url:s,query:a,fragment:c,hasRoute:!1}:i?{urlPath:s,url:s,query:a,fragment:c,hasRoute:!1}:typeof r=="string"?{urlPath:hd(r),url:s,query:a,fragment:c,hasRoute:!1}:{urlPath:void 0,url:s,query:a,fragment:c,hasRoute:!1}}function Rx(t,e,r="custom"){let n=e[Nn]||r,o=e[su];return o&&typeof o=="string"?{description:o,source:n}:{description:t,source:n}}function oOe(t){t.on("createDsc",(e,r)=>{if(!r)return;let i=Ye(r).data[Nn],{description:s}=JIe(r)?nOe(r):{description:void 0};if(i!=="url"&&s&&(e.transaction=s),fo()){let a=J_(r.spanContext());e.sampled=a==null?void 0:String(a)}})}function iOe(){return yt.getActiveSpan()}var hu=typeof __SENTRY_DEBUG__>"u"||__SENTRY_DEBUG__;function sOe({dsc:t,sampled:e}){let r=t?mm(t):void 0,n=new Sa.TraceState,o=r?n.set(i3,r):n;return e===!1?o.set(s3,"1"):o}var aOe=new Set;function cOe(){return Array.from(aOe)}function M$(t){aOe.add(t)}var C$=class extends Sa.W3CBaggagePropagator{constructor(){super(),M$("SentryPropagator"),this._urlMatchesTargetsMap=new Qi(100)}inject(e,r,n){if((0,Sa.isTracingSuppressed)(e)){hu&&j.log("[Tracing] Not injecting trace data for url because tracing is suppressed.");return}let o=yt.getSpan(e),i=o&&C1t(o),s=Ce()?.getOptions()?.tracePropagationTargets;if(!Ix(i,s,this._urlMatchesTargetsMap)){hu&&j.log("[Tracing] Not injecting trace data for url because it does not match tracePropagationTargets:",i);return}let a=N1t(r),c=bi.getBaggage(e)||bi.createBaggage({}),{dynamicSamplingContext:u,traceId:p,spanId:f,sampled:m}=uOe(e);if(a){let h=hm(a);h&&Object.entries(h).forEach(([_,v])=>{c=c.setEntry(_,{value:v})})}u&&(c=Object.entries(u).reduce((h,[_,v])=>v?h.setEntry(`${Xb}${_}`,{value:v}):h,c)),p&&p!==tm&&n.set(r,KH,ld(p,f,m)),super.inject(bi.setBaggage(e,c),r,n)}extract(e,r,n){let o=n.get(r,KH),i=n.get(r,ZH),s=o?Array.isArray(o)?o[0]:o:void 0;return pOe(lOe(e,{sentryTrace:s,baggage:i}))}fields(){return[KH,ZH]}},WIe="[Tracing] Not injecting trace data for url because it does not match tracePropagationTargets:";function Ix(t,e,r){if(typeof t!="string"||!e)return!0;let n=r?.get(t);if(n!==void 0)return hu&&!n&&j.log(WIe,t),n;let o=sd(t,e);return r?.set(t,o),hu&&!o&&j.log(WIe,t),o}function uOe(t,e={}){let r=yt.getSpan(t);if(r?.spanContext().isRemote){let a=r.spanContext();return{dynamicSamplingContext:Cn(r),traceId:a.traceId,spanId:void 0,sampled:J_(a)}}if(r){let a=r.spanContext();return{dynamicSamplingContext:Cn(r),traceId:a.traceId,spanId:a.spanId,sampled:J_(a)}}let n=e.scope||Q_(t)?.scope||It(),o=e.client||Ce(),i=n.getPropagationContext();return{dynamicSamplingContext:o?El(o,n):void 0,traceId:i.traceId,spanId:i.propagationSpanId,sampled:i.sampled}}function lOe(t,{sentryTrace:e,baggage:r}){let n=Qb(e,r),{traceId:o,parentSpanId:i,sampled:s,dsc:a}=n,c=Ce(),u=yl(r);if(!i||c&&!mV(c,u?.org_id))return t;let p=$1t({traceId:o,spanId:i,sampled:s,dsc:a});return yt.setSpanContext(t,p)}function O1t(t,e,r){let n=pOe(lOe(t,e));return Je.with(n,r)}function pOe(t){let e=Q_(t),r={scope:e?e.scope:It().clone(),isolationScope:e?e.isolationScope:nr()};return tOe(t,r)}function N1t(t){try{let e=t[ZH];return Array.isArray(e)?e.join(","):e}catch{return}}function C1t(t){let e=Ye(t).data,r=e[e_]||e[t_];if(typeof r=="string")return r;let n=t.spanContext().traceState?.get(XIe);if(n)return n}function $1t({spanId:t,traceId:e,sampled:r,dsc:n}){let o=sOe({dsc:n,sampled:r});return{traceId:e,spanId:t,isRemote:!0,traceFlags:r?ca.SAMPLED:ca.NONE,traceState:o}}function k1t(t,e){let r=a3(),{name:n,parentSpan:o}=t;return l3(o)(()=>{let s=u3(t.scope,t.forceTransaction),c=t.onlyIfParent&&!yt.getSpan(s)?(0,Sa.suppressTracing)(s):s,u=c3(t);return r.startActiveSpan(n,u,c,p=>Tl(()=>e(p),()=>{Ye(p).status===void 0&&p.setStatus({code:hn.ERROR})},()=>p.end()))})}function M1t(t,e){let r=a3(),{name:n,parentSpan:o}=t;return l3(o)(()=>{let s=u3(t.scope,t.forceTransaction),c=t.onlyIfParent&&!yt.getSpan(s)?(0,Sa.suppressTracing)(s):s,u=c3(t);return r.startActiveSpan(n,u,c,p=>Tl(()=>e(p,()=>p.end()),()=>{Ye(p).status===void 0&&p.setStatus({code:hn.ERROR})}))})}function D1t(t){let e=a3(),{name:r,parentSpan:n}=t;return l3(n)(()=>{let i=u3(t.scope,t.forceTransaction),a=t.onlyIfParent&&!yt.getSpan(i)?(0,Sa.suppressTracing)(i):i,c=c3(t);return e.startSpan(r,c,a)})}function dOe(t,e){let r=t?yt.setSpan(Je.active(),t):yt.deleteSpan(Je.active());return Je.with(r,()=>e(It()))}function a3(){return Ce()?.tracer||yt.getTracer("@sentry/opentelemetry",xr)}function c3(t){let{startTime:e,attributes:r,kind:n,op:o,links:i}=t,s=typeof e=="number"?L1t(e):e;return{attributes:o?{[lt]:o,...r}:r,kind:n,links:i,startTime:s}}function L1t(t){return t<9999999999?t*1e3:t}function u3(t,e){let r=U1t(t),n=yt.getSpan(r);if(!n||!e)return r;let o=yt.deleteSpan(r),{spanId:i,traceId:s}=n.spanContext(),a=J_(n.spanContext()),c=vn(n),u=Cn(c),p=sOe({dsc:u,sampled:a}),f={traceId:s,spanId:i,isRemote:!0,traceFlags:a?ca.SAMPLED:ca.NONE,traceState:p};return yt.setSpanContext(o,f)}function U1t(t){if(t){let e=Px(t);if(e)return e}return Je.active()}function j1t(t,e){return O1t(Je.active(),t,e)}function fOe(t,e){let r=Px(e),n=r&&yt.getSpan(r),o=n?pd(n):P_(e);return[n?Cn(n):El(t,e),o]}function l3(t){return t!==void 0?e=>dOe(t,e):e=>e()}function z1t(t){let e=(0,Sa.suppressTracing)(Je.active());return Je.with(e,t)}function mOe(t){t.on("preprocessEvent",e=>{let r=iOe();if(!r||e.type==="transaction")return;e.contexts={trace:pd(r),...e.contexts};let n=vn(r);return e.sdkProcessingMetadata={dynamicSamplingContext:Cn(n),...e.sdkProcessingMetadata},e})}function F1t({span:t,scope:e,client:r}={}){let n=(e&&Px(e))??Je.active();if(t){let{scope:c}=vc(t);n=c&&Px(c)||yt.setSpan(Je.active(),t)}let{traceId:o,spanId:i,sampled:s,dynamicSamplingContext:a}=uOe(n,{scope:e,client:r});return{"sentry-trace":ld(o,i,s),baggage:mm(a)}}function hOe(){function t(){let a=Je.active(),c=Q_(a);return c||{scope:w_(),isolationScope:ou()}}function e(a){let c=Je.active();return Je.with(c,()=>a(i()))}function r(a,c){let u=Px(a)||Je.active();return Je.with(u.setValue(JH,a),()=>c(a))}function n(a){let c=Je.active();return Je.with(c.setValue(YH,!0),()=>a(s()))}function o(a,c){let u=Je.active();return Je.with(u.setValue(XH,a),()=>c(s()))}function i(){return t().scope}function s(){return t().isolationScope}aV({withScope:e,withSetScope:r,withSetIsolationScope:o,withIsolationScope:n,getCurrentScope:i,getIsolationScope:s,startSpan:k1t,startSpanManual:M1t,startInactiveSpan:D1t,getActiveSpan:iOe,suppressTracing:z1t,getTraceData:F1t,continueTrace:j1t,withActiveSpan:dOe})}function gOe(t){class e extends t{constructor(...n){super(...n),M$("SentryContextManager")}with(n,o,i,...s){let a=Q_(n),c=a?.scope||It(),u=a?.isolationScope||nr(),p=n.getValue(YH)===!0,f=n.getValue(JH),m=n.getValue(XH),h=f||c.clone(),_=m||(p?u.clone():u),x=tOe(n,{scope:h,isolationScope:_}).deleteValue(YH).deleteValue(JH).deleteValue(XH);return A1t(h,x),super.with(x,o,i,...s)}}return e}function q1t(t){let e=new Map;for(let r of t)B1t(e,r);return Array.from(e,function([r,n]){return n})}function _Oe(t){return t.attributes[r3]===!0?void 0:n3(t)}function B1t(t,e){let r=e.spanContext().spanId,n=_Oe(e);if(!n){QH(t,{id:r,span:e,children:[]});return}let o=G1t(t,n),i=QH(t,{id:r,span:e,parentNode:o,children:[]});o.children.push(i)}function G1t(t,e){let r=t.get(e);return r||QH(t,{id:e,children:[]})}function QH(t,e){let r=t.get(e.id);return r?.span?r:r&&!r.span?(r.span=e.span,r.parentNode=e.parentNode,r):(t.set(e.id,e),e)}var vOe={1:"cancelled",2:"unknown_error",3:"invalid_argument",4:"deadline_exceeded",5:"not_found",6:"already_exists",7:"permission_denied",8:"resource_exhausted",9:"failed_precondition",10:"aborted",11:"out_of_range",12:"unimplemented",13:"internal_error",14:"unavailable",15:"data_loss",16:"unauthenticated"},V1t=t=>Object.values(vOe).includes(t);function SOe(t){let e=o3(t)?t.attributes:{},r=y1t(t)?t.status:void 0;if(r){if(r.code===hn.OK)return{code:1};if(r.code===hn.ERROR){if(typeof r.message>"u"){let o=KIe(e);if(o)return o}return r.message&&V1t(r.message)?{code:2,message:r.message}:{code:2,message:"unknown_error"}}}let n=KIe(e);return n||(r?.code===hn.UNSET?{code:1}:{code:2,message:"unknown_error"})}function KIe(t){let e=t[Zp]||t[WN],r=t[w5],n=typeof e=="number"?e:typeof e=="string"?parseInt(e):void 0;if(typeof n=="number")return e1(n);if(typeof r=="string")return{code:2,message:vOe[r]||"unknown_error"}}var ZIe=1e3,YIe=300,e3=class{constructor(e){this._finishedSpanBucketSize=e?.timeout||YIe,this._finishedSpanBuckets=new Array(this._finishedSpanBucketSize).fill(void 0),this._lastCleanupTimestampInS=Math.floor(Date.now()/1e3),this._spansToBucketEntry=new WeakMap,this._sentSpans=new Map,this._debouncedFlush=DV(this.flush.bind(this),1,{maxWait:100})}export(e){let r=Math.floor(Date.now()/1e3);if(this._lastCleanupTimestampInS!==r){let s=0;this._finishedSpanBuckets.forEach((a,c)=>{a&&a.timestampInS<=r-this._finishedSpanBucketSize&&(s+=a.spans.size,this._finishedSpanBuckets[c]=void 0)}),s>0&&hu&&j.log(`SpanExporter dropped ${s} spans because they were pending for more than ${this._finishedSpanBucketSize} seconds.`),this._lastCleanupTimestampInS=r}let n=r%this._finishedSpanBucketSize,o=this._finishedSpanBuckets[n]||{timestampInS:r,spans:new Set};this._finishedSpanBuckets[n]=o,o.spans.add(e),this._spansToBucketEntry.set(e,o);let i=_Oe(e);(!i||this._sentSpans.has(i))&&this._debouncedFlush()}flush(){let e=this._finishedSpanBuckets.flatMap(s=>s?Array.from(s.spans):[]);this._flushSentSpanCache();let r=this._maybeSend(e),n=r.size,o=e.length-n;hu&&j.log(`SpanExporter exported ${n} spans, ${o} spans are waiting for their parent spans to finish`);let i=Date.now()+YIe*1e3;for(let s of r){this._sentSpans.set(s.spanContext().spanId,i);let a=this._spansToBucketEntry.get(s);a&&a.spans.delete(s)}this._debouncedFlush.cancel()}clear(){this._finishedSpanBuckets=this._finishedSpanBuckets.fill(void 0),this._sentSpans.clear(),this._debouncedFlush.cancel()}_maybeSend(e){let r=q1t(e),n=new Set,o=this._getCompletedRootNodes(r);for(let i of o){let s=i.span;n.add(s);let a=W1t(s);if(i.parentNode&&this._sentSpans.has(i.parentNode.id)){let p=a.contexts?.trace?.data;p&&(p["sentry.parent_span_already_sent"]=!0)}let c=a.spans||[];for(let p of i.children)t3(p,c,n);a.spans=c.length>ZIe?c.sort((p,f)=>p.start_timestamp-f.start_timestamp).slice(0,ZIe):c;let u=vm(s.events);u&&(a.measurements=u),y1(a)}return n}_flushSentSpanCache(){let e=Date.now();for(let[r,n]of this._sentSpans.entries())n<=e&&this._sentSpans.delete(r)}_nodeIsCompletedRootNodeOrHasSentParent(e){return!!e.span&&(!e.parentNode||this._sentSpans.has(e.parentNode.id))}_getCompletedRootNodes(e){return e.filter(r=>this._nodeIsCompletedRootNodeOrHasSentParent(r))}};function H1t(t){let e=t.attributes,r=e[dr],n=e[lt],o=e[Nn];return{origin:r,op:n,source:o}}function W1t(t){let{op:e,description:r,data:n,origin:o="manual",source:i}=yOe(t),s=vc(t),a=t.attributes[_c],c={[Nn]:i,[_c]:a,[lt]:e,[dr]:o,...n,...EOe(t.attributes)},{links:u}=t,{traceId:p,spanId:f}=t.spanContext(),m=n3(t),h=SOe(t),_={parent_span_id:m,span_id:f,trace_id:p,data:c,origin:o,op:e,status:_m(h),links:gm(u)},v=c[Zp],E=typeof v=="number"?{response:{status_code:v}}:void 0;return{contexts:{trace:_,otel:{resource:t.resource.attributes},...E},spans:[],start_timestamp:Xi(t.startTime),timestamp:Xi(t.endTime),transaction:r,type:"transaction",sdkProcessingMetadata:{capturedSpanScope:s.scope,capturedSpanIsolationScope:s.isolationScope,sampleRate:a,dynamicSamplingContext:Cn(t)},...i&&{transaction_info:{source:i}}}}function t3(t,e,r){let n=t.span;if(n&&r.add(n),!n){t.children.forEach(I=>{t3(I,e,r)});return}let i=n.spanContext().spanId,s=n.spanContext().traceId,a=n3(n),{attributes:c,startTime:u,endTime:p,links:f}=n,{op:m,description:h,data:_,origin:v="manual"}=yOe(n),E={[dr]:v,[lt]:m,...EOe(c),..._},x=SOe(n),w={span_id:i,trace_id:s,data:E,description:h,parent_span_id:a,start_timestamp:Xi(u),timestamp:Xi(p)||void 0,status:_m(x),op:m,origin:v,measurements:vm(n.events),links:gm(f)};e.push(w),t.children.forEach(I=>{t3(I,e,r)})}function yOe(t){let{op:e,source:r,origin:n}=H1t(t),{op:o,description:i,source:s,data:a}=nOe(t),c=e||o,u=r||s,p={...a,...K1t(t)};return{op:c,description:i,source:u,origin:n,data:p}}function EOe(t){let e={...t};return delete e[_c],delete e[r3],delete e[su],e}function K1t(t){let e=t.attributes,r={};t.kind!==Zn.INTERNAL&&(r["otel.kind"]=Zn[t.kind]);let n=e[WN];n&&(r[Zp]=n);let o=E1t(t);return o.url&&(r.url=o.url),o["http.query"]&&(r["http.query"]=o["http.query"].slice(1)),o["http.fragment"]&&(r["http.fragment"]=o["http.fragment"].slice(1)),r}function Z1t(t,e){let r=yt.getSpan(e),n=Q_(e);r&&!r.spanContext().isRemote&&N_(r,t),r?.spanContext().isRemote&&t.setAttribute(r3,!0),e===Xg&&(n={scope:w_(),isolationScope:ou()}),n&&Jb(t,n.scope,n.isolationScope),ox(t),Ce()?.emit("spanStart",t)}function Y1t(t){ix(t),Ce()?.emit("spanEnd",t)}var $$=class{constructor(e){M$("SentrySpanProcessor"),this._exporter=new e3(e)}async forceFlush(){this._exporter.flush()}async shutdown(){this._exporter.clear()}onStart(e,r){Z1t(e,r)}onEnd(e){Y1t(e),this._exporter.export(e)}},k$=class{constructor(e){this._client=e,M$("SentrySampler")}shouldSample(e,r,n,o,i,s){let a=this._client.getOptions(),c=Q1t(e),u=c?.spanContext();if(!fo(a))return Y_({decision:void 0,context:e,spanAttributes:i});let p=i[Eb]||i[om];if(o===Zn.CLIENT&&p&&(!c||u?.isRemote))return Y_({decision:void 0,context:e,spanAttributes:i});let f=c?J1t(c,r,n):void 0;if(!(!c||u?.isRemote))return Y_({decision:f?Sd.SamplingDecision.RECORD_AND_SAMPLED:Sd.SamplingDecision.NOT_RECORD,context:e,spanAttributes:i});let{description:h,data:_,op:v}=rOe(n,i,o),E={..._,...i};v&&(E[lt]=v);let x={decision:!0};if(this._client.emit("beforeSampling",{spanAttributes:E,spanName:h,parentSampled:f,parentContext:u},x),!x.decision)return Y_({decision:void 0,context:e,spanAttributes:i});let{isolationScope:w}=Q_(e)??{},I=u?.traceState?u.traceState.get(i3):void 0,N=I?yl(I):void 0,$=As(N?.sample_rand)??Math.random(),[B,G,he]=sx(a,{name:h,attributes:E,normalizedRequest:w?.getScopeData().sdkProcessingMetadata.normalizedRequest,parentSampled:f,parentSampleRate:As(N?.sample_rate)},$),Q=`${p}`.toUpperCase();return Q==="OPTIONS"||Q==="HEAD"?(hu&&j.log(`[Tracing] Not sampling span because HTTP method is '${Q}' for ${n}`),Y_({decision:Sd.SamplingDecision.NOT_RECORD,context:e,spanAttributes:i,sampleRand:$,downstreamTraceSampleRate:0})):(!B&&f===void 0&&(hu&&j.log("[Tracing] Discarding root span because its trace was not chosen to be sampled."),this._client.recordDroppedEvent("sample_rate","transaction")),{...Y_({decision:B?Sd.SamplingDecision.RECORD_AND_SAMPLED:Sd.SamplingDecision.NOT_RECORD,context:e,spanAttributes:i,sampleRand:$,downstreamTraceSampleRate:he?G:void 0}),attributes:{[_c]:he?G:void 0}})}toString(){return"SentrySampler"}};function J1t(t,e,r){let n=t.spanContext();if(ml(n)&&n.traceId===e){if(n.isRemote){let i=J_(t.spanContext());return hu&&j.log(`[Tracing] Inheriting remote parent's sampled decision for ${r}: ${i}`),i}let o=J_(n);return hu&&j.log(`[Tracing] Inheriting parent's sampled decision for ${r}: ${o}`),o}}function Y_({decision:t,context:e,spanAttributes:r,sampleRand:n,downstreamTraceSampleRate:o}){let i=X1t(e,r);return o!==void 0&&(i=i.set(x1t,`${o}`)),n!==void 0&&(i=i.set(b1t,`${n}`)),t==null?{decision:Sd.SamplingDecision.NOT_RECORD,traceState:i}:t===Sd.SamplingDecision.NOT_RECORD?{decision:t,traceState:i.set(s3,"1")}:{decision:t,traceState:i}}function X1t(t,e){let o=yt.getSpan(t)?.spanContext()?.traceState||new Sa.TraceState,i=e[e_]||e[t_];return i&&typeof i=="string"&&(o=o.set(XIe,i)),o}function Q1t(t){let e=yt.getSpan(t);return e&&ml(e.spanContext())?e:void 0}function Ox(t,e){if(!t)return e;let r=hm(t),n=hm(e);if(!n)return t;let o={...r};return Object.entries(n).forEach(([i,s])=>{o[i]||(o[i]=s)}),t1(o)}function TOe(t,e){let r=e$t(t),n=e?.statusCode,o=vx(n);ha({category:"http",data:{status_code:n,...r},type:"http",level:o},{event:"response",request:t,response:e})}function bOe(t,e){let r=t$t(t),n=Ce()?.getOptions().tracePropagationTargets,o=Ix(r,n,e)?L_():void 0;if(!o)return;let{"sentry-trace":i,baggage:s}=o;if(i&&!t.getHeader("sentry-trace"))try{t.setHeader("sentry-trace",i),Kt&&j.log(ri,"Added sentry-trace header to outgoing request")}catch(a){Kt&&j.error(ri,"Failed to add sentry-trace header to outgoing request:",od(a)?a.message:"Unknown error")}if(s){let a=Ox(t.getHeader("baggage"),s);if(a)try{t.setHeader("baggage",a),Kt&&j.log(ri,"Added baggage header to outgoing request")}catch(c){Kt&&j.error(ri,"Failed to add baggage header to outgoing request:",od(c)?c.message:"Unknown error")}}}function e$t(t){try{let e=t.getHeader("host")||t.host,r=new URL(t.path,`${t.protocol}//${e}`),n=md(r.toString()),o={url:gd(n),"http.method":t.method||"GET"};return n.search&&(o["http.query"]=n.search),n.hash&&(o["http.fragment"]=n.hash),o}catch{return{}}}function xOe(t){return{method:t.method,protocol:t.protocol,host:t.host,hostname:t.host,path:t.path,headers:t.getHeaders()}}function t$t(t){let e=t.getHeader("host")||t.host,r=t.protocol,n=t.path;return`${r}//${e}${n}`}var wm=class extends Nx.InstrumentationBase{constructor(e={}){super(ri,xr,e),this._propagationDecisionMap=new Qi(100),this._ignoreOutgoingRequestsMap=new WeakMap}init(){let e=!1,r=(c=>{KPe(c.server,{ignoreIncomingRequestBody:this.getConfig().ignoreIncomingRequestBody,maxIncomingRequestBodySize:this.getConfig().maxIncomingRequestBodySize,trackIncomingRequestsAsSessions:this.getConfig().trackIncomingRequestsAsSessions,sessionFlushingDelayMS:this.getConfig().sessionFlushingDelayMS??6e4})}),n=(c=>{let u=c;this._onOutgoingRequestFinish(u.request,u.response)}),o=(c=>{let u=c;this._onOutgoingRequestFinish(u.request,void 0)}),i=(c=>{let u=c;this._onOutgoingRequestCreated(u.request)}),s=c=>(e||(e=!0,(0,gu.subscribe)("http.server.request.start",r),(0,gu.subscribe)("http.client.response.finish",n),(0,gu.subscribe)("http.client.request.error",o),this.getConfig().propagateTraceInOutgoingRequests&&(0,gu.subscribe)("http.client.request.created",i)),c),a=()=>{(0,gu.unsubscribe)("http.server.request.start",r),(0,gu.unsubscribe)("http.client.response.finish",n),(0,gu.unsubscribe)("http.client.request.error",o),(0,gu.unsubscribe)("http.client.request.created",i)};return[new Nx.InstrumentationNodeModuleDefinition("http",["*"],s,a),new Nx.InstrumentationNodeModuleDefinition("https",["*"],s,a)]}_onOutgoingRequestFinish(e,r){Kt&&j.log(ri,"Handling finished outgoing request");let n=this.getConfig().breadcrumbs,o=typeof n>"u"?!0:n,i=this._ignoreOutgoingRequestsMap.get(e)??this._shouldIgnoreOutgoingRequest(e);this._ignoreOutgoingRequestsMap.set(e,i),o&&!i&&TOe(e,r)}_onOutgoingRequestCreated(e){let r=this._ignoreOutgoingRequestsMap.get(e)??this._shouldIgnoreOutgoingRequest(e);this._ignoreOutgoingRequestsMap.set(e,r),!r&&bOe(e,this._propagationDecisionMap)}_shouldIgnoreOutgoingRequest(e){if((0,AOe.isTracingSuppressed)(Je.active()))return!0;let r=this.getConfig().ignoreOutgoingRequests;if(!r)return!1;let n=xOe(e),o=Ex(e);return r(o,n)}};var wOe="Http",r$t=Ge(`${wOe}.sentry`,t=>new wm(t)),ROe=(t={})=>{let e=t.dropSpansForIncomingRequestStatusCodes??[[401,404],[300,399]];return{name:wOe,setupOnce(){r$t({...t,extractIncomingTraceFromHeader:!0,propagateTraceInOutgoingRequests:!0})},processEvent(r){if(r.type==="transaction"){let n=r.contexts?.trace?.data?.["http.response.status_code"];if(typeof n=="number"&&e.some(o=>{if(typeof o=="number")return o===n;let[i,s]=o;return n>=i&&n<=s}))return null}return r}}};pe();var OOe=W(_r(),1),NOe=W(Ft(),1);var ev=W(require("diagnostics_channel"),1);var Rm=oV(process.versions.node),Cx=Rm.major,POe=Rm.minor;var D$="sentry-trace",p3="baggage",IOe=/baggage: (.*)\r\n/,Pm=class extends NOe.InstrumentationBase{constructor(e={}){super("@sentry/instrumentation-node-fetch",xr,e),this._channelSubs=[],this._propagationDecisionMap=new Qi(100),this._ignoreOutgoingRequestsMap=new WeakMap}init(){}disable(){super.disable(),this._channelSubs.forEach(e=>e.unsubscribe()),this._channelSubs=[]}enable(){super.enable(),this._channelSubs=this._channelSubs||[],!(this._channelSubs.length>0)&&(this._subscribeToChannel("undici:request:create",this._onRequestCreated.bind(this)),this._subscribeToChannel("undici:request:headers",this._onResponseHeaders.bind(this)))}_onRequestCreated({request:e}){if(!(this.getConfig().enabled!==!1))return;let o=this._shouldIgnoreOutgoingRequest(e);if(this._ignoreOutgoingRequestsMap.set(e,o),o)return;let i=d3(e.origin,e.path),s=Ce()?.getOptions().tracePropagationTargets,a=Ix(i,s,this._propagationDecisionMap)?L_():void 0;if(!a)return;let{"sentry-trace":c,baggage:u}=a;if(Array.isArray(e.headers)){let p=e.headers;c&&!p.includes(D$)&&p.push(D$,c);let f=p.findIndex(m=>m===p3);if(u&&f===-1)p.push(p3,u);else if(u){let m=p[f+1],h=Ox(m,u);h&&(p[f+1]=h)}}else{let p=e.headers;c&&!p.includes(`${D$}:`)&&(e.headers+=`${D$}: ${c}\r -`);let f=e.headers.match(IOe)?.[1];if(u&&!f)e.headers+=`${p3}: ${u}\r -`;else if(u){let m=Ox(f,u);m&&(e.headers=e.headers.replace(IOe,`baggage: ${m}\r -`))}}}_onResponseHeaders({request:e,response:r}){let n=this.getConfig();if(!(n.enabled!==!1))return;let i=n.breadcrumbs,s=typeof i>"u"?!0:i,a=this._ignoreOutgoingRequestsMap.get(e);s&&!a&&n$t(e,r)}_subscribeToChannel(e,r){let n=Cx>18||Cx===18&&POe>=19,o;if(n)ev.subscribe?.(e,r),o=()=>ev.unsubscribe?.(e,r);else{let i=ev.channel(e);i.subscribe(r),o=()=>i.unsubscribe(r)}this._channelSubs.push({name:e,unsubscribe:o})}_shouldIgnoreOutgoingRequest(e){if((0,OOe.isTracingSuppressed)(Je.active()))return!0;let r=d3(e.origin,e.path),n=this.getConfig().ignoreOutgoingRequests;return typeof n!="function"||!r?!1:n(r)}};function n$t(t,e){let r=o$t(t),n=e.statusCode,o=vx(n);ha({category:"http",data:{status_code:n,...r},type:"http",level:o},{event:"response",request:t,response:e})}function o$t(t){try{let e=d3(t.origin,t.path),r=md(e),n={url:gd(r),"http.method":t.method||"GET"};return r.search&&(n["http.query"]=r.search),r.hash&&(n["http.fragment"]=r.hash),n}catch{return{}}}function d3(t,e="/"){try{return new URL(e,t).toString()}catch{let r=`${t}`;return r.endsWith("/")&&e.startsWith("/")?`${r}${e.slice(1)}`:!r.endsWith("/")&&!e.startsWith("/")?`${r}/${e.slice(1)}`:`${r}${e}`}}var i$t="NodeFetch",s$t=Ge(`${i$t}.sentry`,Pm,t=>t),a$t=((t={})=>({name:"NodeFetch",setupOnce(){s$t(t)}})),COe=a$t;var kOe=require("node:child_process"),L$=require("node:fs"),mo=W(require("node:os"),1),MOe=require("node:path"),f3=require("node:util");var c$t=(0,f3.promisify)(L$.readFile),u$t=(0,f3.promisify)(L$.readdir),l$t="Context",p$t=((t={})=>{let e,r={app:!0,os:!0,device:!0,culture:!0,cloudResource:!0,...t};async function n(i){e===void 0&&(e=o());let s=d$t(await e);return i.contexts={...i.contexts,app:{...s.app,...i.contexts?.app},os:{...s.os,...i.contexts?.os},device:{...s.device,...i.contexts?.device},culture:{...s.culture,...i.contexts?.culture},cloud_resource:{...s.cloud_resource,...i.contexts?.cloud_resource}},i}async function o(){let i={};if(r.os&&(i.os=await f$t()),r.app&&(i.app=h$t()),r.device&&(i.device=g$t(r.device)),r.culture){let s=m$t();s&&(i.culture=s)}return r.cloudResource&&(i.cloud_resource=T$t()),i}return{name:l$t,processEvent(i){return n(i)}}}),DOe=p$t;function d$t(t){if(t.app?.app_memory&&(t.app.app_memory=process.memoryUsage().rss),t.app?.free_memory&&typeof process.availableMemory=="function"){let e=process.availableMemory?.();e!=null&&(t.app.free_memory=e)}return t.device?.free_memory&&(t.device.free_memory=mo.freemem()),t}async function f$t(){let t=mo.platform();switch(t){case"darwin":return y$t();case"linux":return E$t();default:return{name:_$t[t]||t,version:mo.release()}}}function m$t(){try{if(typeof process.versions.icu!="string")return;let t=new Date(9e8);if(new Intl.DateTimeFormat("es",{month:"long"}).format(t)==="enero"){let r=Intl.DateTimeFormat().resolvedOptions();return{locale:r.locale,timezone:r.timeZone}}}catch{}}function h$t(){let t=process.memoryUsage().rss,r={app_start_time:new Date(Date.now()-process.uptime()*1e3).toISOString(),app_memory:t};if(typeof process.availableMemory=="function"){let n=process.availableMemory?.();n!=null&&(r.free_memory=n)}return r}function g$t(t){let e={},r;try{r=mo.uptime()}catch{}if(typeof r=="number"&&(e.boot_time=new Date(Date.now()-r*1e3).toISOString()),e.arch=mo.arch(),(t===!0||t.memory)&&(e.memory_size=mo.totalmem(),e.free_memory=mo.freemem()),t===!0||t.cpu){let n=mo.cpus(),o=n?.[0];o&&(e.processor_count=n.length,e.cpu_description=o.model,e.processor_frequency=o.speed)}return e}var _$t={aix:"IBM AIX",freebsd:"FreeBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows"},v$t=[{name:"fedora-release",distros:["Fedora"]},{name:"redhat-release",distros:["Red Hat Linux","Centos"]},{name:"redhat_version",distros:["Red Hat Linux"]},{name:"SuSE-release",distros:["SUSE Linux"]},{name:"lsb-release",distros:["Ubuntu Linux","Arch Linux"]},{name:"debian_version",distros:["Debian"]},{name:"debian_release",distros:["Debian"]},{name:"arch-release",distros:["Arch Linux"]},{name:"gentoo-release",distros:["Gentoo Linux"]},{name:"novell-release",distros:["SUSE Linux"]},{name:"alpine-release",distros:["Alpine Linux"]}],S$t={alpine:t=>t,arch:t=>_u(/distrib_release=(.*)/,t),centos:t=>_u(/release ([^ ]+)/,t),debian:t=>t,fedora:t=>_u(/release (..)/,t),mint:t=>_u(/distrib_release=(.*)/,t),red:t=>_u(/release ([^ ]+)/,t),suse:t=>_u(/VERSION = (.*)\n/,t),ubuntu:t=>_u(/distrib_release=(.*)/,t)};function _u(t,e){let r=t.exec(e);return r?r[1]:void 0}async function y$t(){let t={kernel_version:mo.release(),name:"Mac OS X",version:`10.${Number(mo.release().split(".")[0])-4}`};try{let e=await new Promise((r,n)=>{(0,kOe.execFile)("/usr/bin/sw_vers",(o,i)=>{if(o){n(o);return}r(i)})});t.name=_u(/^ProductName:\s+(.*)$/m,e),t.version=_u(/^ProductVersion:\s+(.*)$/m,e),t.build=_u(/^BuildVersion:\s+(.*)$/m,e)}catch{}return t}function $Oe(t){return t.split(" ")[0].toLowerCase()}async function E$t(){let t={kernel_version:mo.release(),name:"Linux"};try{let e=await u$t("/etc"),r=v$t.find(a=>e.includes(a.name));if(!r)return t;let n=(0,MOe.join)("/etc",r.name),o=(await c$t(n,{encoding:"utf-8"})).toLowerCase(),{distros:i}=r;t.name=i.find(a=>o.indexOf($Oe(a))>=0)||i[0];let s=$Oe(t.name);t.version=S$t[s]?.(o)}catch{}return t}function T$t(){return process.env.VERCEL?{"cloud.provider":"vercel","cloud.region":process.env.VERCEL_REGION}:process.env.AWS_REGION?{"cloud.provider":"aws","cloud.region":process.env.AWS_REGION,"cloud.platform":process.env.AWS_EXECUTION_ENV}:process.env.GCP_PROJECT?{"cloud.provider":"gcp"}:process.env.ALIYUN_REGION_ID?{"cloud.provider":"alibaba_cloud","cloud.region":process.env.ALIYUN_REGION_ID}:process.env.WEBSITE_SITE_NAME&&process.env.REGION_NAME?{"cloud.provider":"azure","cloud.region":process.env.REGION_NAME}:process.env.IBM_CLOUD_REGION?{"cloud.provider":"ibm_cloud","cloud.region":process.env.IBM_CLOUD_REGION}:process.env.TENCENTCLOUD_REGION?{"cloud.provider":"tencent_cloud","cloud.region":process.env.TENCENTCLOUD_REGION,"cloud.account.id":process.env.TENCENTCLOUD_APPID,"cloud.availability_zone":process.env.TENCENTCLOUD_ZONE}:process.env.NETLIFY?{"cloud.provider":"netlify"}:process.env.FLY_REGION?{"cloud.provider":"fly.io","cloud.region":process.env.FLY_REGION}:process.env.DYNO?{"cloud.provider":"heroku"}:void 0}var jOe=require("node:fs"),zOe=require("node:readline");var m3=new Qi(10),FOe=new Qi(20),b$t=7,x$t="ContextLines",A$t=1e3,w$t=1e4;function R$t(t,e,r){let n=t.get(e);return n===void 0?(t.set(e,r),r):n}function P$t(t){return!!(t.startsWith("node:")||t.endsWith(".min.js")||t.endsWith(".min.cjs")||t.endsWith(".min.mjs")||t.startsWith("data:"))}function I$t(t){return t.lineno!==void 0&&t.lineno>w$t||t.colno!==void 0&&t.colno>A$t}function O$t(t,e){let r=m3.get(t);if(r===void 0)return!1;for(let n=e[0];n<=e[1];n++)if(r[n]===void 0)return!1;return!0}function N$t(t,e){if(!t.length)return[];let r=0,n=t[0];if(typeof n!="number")return[];let o=UOe(n,e),i=[];for(;;){if(r===t.length-1){i.push(o);break}let s=t[r+1];if(typeof s!="number")break;s<=o[1]?o[1]=s+e:(i.push(o),o=UOe(s,e)),r++}return i}function C$t(t,e,r){return new Promise((n,o)=>{let i=(0,jOe.createReadStream)(t),s=(0,zOe.createInterface)({input:i});function a(){i.destroy(),n()}let c=0,u=0,p=e[u];if(p===void 0){a();return}let f=p[0],m=p[1];function h(_){FOe.set(t,1),Kt&&j.error(`Failed to read file: ${t}. Error: ${_}`),s.close(),s.removeAllListeners(),a()}i.on("error",h),s.on("error",h),s.on("close",a),s.on("line",_=>{if(c++,!(c=m)){if(u===e.length-1){s.close(),s.removeAllListeners();return}u++;let v=e[u];if(v===void 0){s.close(),s.removeAllListeners();return}f=v[0],m=v[1]}})})}async function $$t(t,e){let r={};if(e>0&&t.exception?.values){for(let i of t.exception.values)if(i.stacktrace?.frames?.length)for(let s=i.stacktrace.frames.length-1;s>=0;s--){let a=i.stacktrace.frames[s],c=a?.filename;if(!a||typeof c!="string"||typeof a.lineno!="number"||P$t(c)||I$t(a))continue;r[c]||(r[c]=[]),r[c].push(a.lineno)}}let n=Object.keys(r);if(n.length==0)return t;let o=[];for(let i of n){if(FOe.get(i))continue;let s=r[i];if(!s)continue;s.sort((u,p)=>u-p);let a=N$t(s,e);if(a.every(u=>O$t(i,u)))continue;let c=R$t(m3,i,{});o.push(C$t(i,a,c))}if(await Promise.all(o).catch(()=>{Kt&&j.log("Failed to read one or more source files and resolve context lines")}),e>0&&t.exception?.values)for(let i of t.exception.values)i.stacktrace?.frames&&i.stacktrace.frames.length>0&&k$t(i.stacktrace.frames,e,m3);return t}function k$t(t,e,r){for(let n of t)if(n.filename&&n.context_line===void 0&&typeof n.lineno=="number"){let o=r.get(n.filename);if(o===void 0)continue;M$t(n.lineno,n,e,o)}}function LOe(t){delete t.pre_context,delete t.context_line,delete t.post_context}function M$t(t,e,r,n){if(e.lineno===void 0||n===void 0){Kt&&j.error("Cannot resolve context for frame with no lineno or file contents");return}e.pre_context=[];for(let i=qOe(t,r);i{let e=t.frameContextLines!==void 0?t.frameContextLines:b$t;return{name:x$t,processEvent(r){return $$t(r,e)}}}),GOe=D$t;var KOe=require("node:worker_threads");var U$;async function j$(){if(U$===void 0)try{U$=!!(await import("node:inspector")).url()}catch{U$=!1}return U$}var $x="__SENTRY_ERROR_LOCAL_VARIABLES__";function HOe(t,e,r){let n=0,o=5,i=0;return setInterval(()=>{i===0?n>t&&(o*=2,r(o),o>86400&&(o=86400),i=o):(i-=1,i===0&&e()),n=0},1e3).unref(),()=>{n+=1}}function VOe(t){return t!==void 0&&(t.length===0||t==="?"||t==="")}function z$(t,e){return t===e||`Object.${t}`===e||t===`Object.${e}`||VOe(t)&&VOe(e)}var L$t="LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjUuMCAoNTc3ZmJlZikgfCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0ICovCmltcG9ydHtTZXNzaW9uIGFzIGV9ZnJvbSJub2RlOmluc3BlY3Rvci9wcm9taXNlcyI7aW1wb3J0e3dvcmtlckRhdGEgYXMgdH1mcm9tIm5vZGU6d29ya2VyX3RocmVhZHMiO2NvbnN0IG49Z2xvYmFsVGhpcyxpPXt9O2NvbnN0IG89Il9fU0VOVFJZX0VSUk9SX0xPQ0FMX1ZBUklBQkxFU19fIjtjb25zdCBhPXQ7ZnVuY3Rpb24gcyguLi5lKXthLmRlYnVnJiZmdW5jdGlvbihlKXtpZighKCJjb25zb2xlImluIG4pKXJldHVybiBlKCk7Y29uc3QgdD1uLmNvbnNvbGUsbz17fSxhPU9iamVjdC5rZXlzKGkpO2EuZm9yRWFjaChlPT57Y29uc3Qgbj1pW2VdO29bZV09dFtlXSx0W2VdPW59KTt0cnl7cmV0dXJuIGUoKX1maW5hbGx5e2EuZm9yRWFjaChlPT57dFtlXT1vW2VdfSl9fSgoKT0+Y29uc29sZS5sb2coIltMb2NhbFZhcmlhYmxlcyBXb3JrZXJdIiwuLi5lKSl9YXN5bmMgZnVuY3Rpb24gYyhlLHQsbixpKXtjb25zdCBvPWF3YWl0IGUucG9zdCgiUnVudGltZS5nZXRQcm9wZXJ0aWVzIix7b2JqZWN0SWQ6dCxvd25Qcm9wZXJ0aWVzOiEwfSk7aVtuXT1vLnJlc3VsdC5maWx0ZXIoZT0+Imxlbmd0aCIhPT1lLm5hbWUmJiFpc05hTihwYXJzZUludChlLm5hbWUsMTApKSkuc29ydCgoZSx0KT0+cGFyc2VJbnQoZS5uYW1lLDEwKS1wYXJzZUludCh0Lm5hbWUsMTApKS5tYXAoZT0+ZS52YWx1ZT8udmFsdWUpfWFzeW5jIGZ1bmN0aW9uIHIoZSx0LG4saSl7Y29uc3Qgbz1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO2lbbl09by5yZXN1bHQubWFwKGU9PltlLm5hbWUsZS52YWx1ZT8udmFsdWVdKS5yZWR1Y2UoKGUsW3Qsbl0pPT4oZVt0XT1uLGUpLHt9KX1mdW5jdGlvbiB1KGUsdCl7ZS52YWx1ZSYmKCJ2YWx1ZSJpbiBlLnZhbHVlP3ZvaWQgMD09PWUudmFsdWUudmFsdWV8fG51bGw9PT1lLnZhbHVlLnZhbHVlP3RbZS5uYW1lXT1gPCR7ZS52YWx1ZS52YWx1ZX0+YDp0W2UubmFtZV09ZS52YWx1ZS52YWx1ZToiZGVzY3JpcHRpb24iaW4gZS52YWx1ZSYmImZ1bmN0aW9uIiE9PWUudmFsdWUudHlwZT90W2UubmFtZV09YDwke2UudmFsdWUuZGVzY3JpcHRpb259PmA6InVuZGVmaW5lZCI9PT1lLnZhbHVlLnR5cGUmJih0W2UubmFtZV09Ijx1bmRlZmluZWQ+IikpfWFzeW5jIGZ1bmN0aW9uIGwoZSx0KXtjb25zdCBuPWF3YWl0IGUucG9zdCgiUnVudGltZS5nZXRQcm9wZXJ0aWVzIix7b2JqZWN0SWQ6dCxvd25Qcm9wZXJ0aWVzOiEwfSksaT17fTtmb3IoY29uc3QgdCBvZiBuLnJlc3VsdClpZih0LnZhbHVlPy5vYmplY3RJZCYmIkFycmF5Ij09PXQudmFsdWUuY2xhc3NOYW1lKXtjb25zdCBuPXQudmFsdWUub2JqZWN0SWQ7YXdhaXQgYyhlLG4sdC5uYW1lLGkpfWVsc2UgaWYodC52YWx1ZT8ub2JqZWN0SWQmJiJPYmplY3QiPT09dC52YWx1ZS5jbGFzc05hbWUpe2NvbnN0IG49dC52YWx1ZS5vYmplY3RJZDthd2FpdCByKGUsbix0Lm5hbWUsaSl9ZWxzZSB0LnZhbHVlJiZ1KHQsaSk7cmV0dXJuIGl9bGV0IGY7KGFzeW5jIGZ1bmN0aW9uKCl7Y29uc3QgdD1uZXcgZTt0LmNvbm5lY3RUb01haW5UaHJlYWQoKSxzKCJDb25uZWN0ZWQgdG8gbWFpbiB0aHJlYWQiKTtsZXQgbj0hMTt0Lm9uKCJEZWJ1Z2dlci5yZXN1bWVkIiwoKT0+e249ITF9KSx0Lm9uKCJEZWJ1Z2dlci5wYXVzZWQiLGU9PntuPSEwLGFzeW5jIGZ1bmN0aW9uKGUse3JlYXNvbjp0LGRhdGE6e29iamVjdElkOm59LGNhbGxGcmFtZXM6aX0pe2lmKCJleGNlcHRpb24iIT09dCYmInByb21pc2VSZWplY3Rpb24iIT09dClyZXR1cm47aWYoZj8uKCksbnVsbD09bilyZXR1cm47Y29uc3QgYT1bXTtmb3IobGV0IHQ9MDt0PGkubGVuZ3RoO3QrKyl7Y29uc3R7c2NvcGVDaGFpbjpuLGZ1bmN0aW9uTmFtZTpvLHRoaXM6c309aVt0XSxjPW4uZmluZChlPT4ibG9jYWwiPT09ZS50eXBlKSxyPSJnbG9iYWwiIT09cy5jbGFzc05hbWUmJnMuY2xhc3NOYW1lP2Ake3MuY2xhc3NOYW1lfS4ke299YDpvO2lmKHZvaWQgMD09PWM/Lm9iamVjdC5vYmplY3RJZClhW3RdPXtmdW5jdGlvbjpyfTtlbHNle2NvbnN0IG49YXdhaXQgbChlLGMub2JqZWN0Lm9iamVjdElkKTthW3RdPXtmdW5jdGlvbjpyLHZhcnM6bn19fWF3YWl0IGUucG9zdCgiUnVudGltZS5jYWxsRnVuY3Rpb25PbiIse2Z1bmN0aW9uRGVjbGFyYXRpb246YGZ1bmN0aW9uKCkgeyB0aGlzLiR7b30gPSB0aGlzLiR7b30gfHwgJHtKU09OLnN0cmluZ2lmeShhKX07IH1gLHNpbGVudDohMCxvYmplY3RJZDpufSksYXdhaXQgZS5wb3N0KCJSdW50aW1lLnJlbGVhc2VPYmplY3QiLHtvYmplY3RJZDpufSl9KHQsZS5wYXJhbXMpLnRoZW4oYXN5bmMoKT0+e24mJmF3YWl0IHQucG9zdCgiRGVidWdnZXIucmVzdW1lIil9LGFzeW5jIGU9PntuJiZhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpfSl9KSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLmVuYWJsZSIpO2NvbnN0IGk9ITEhPT1hLmNhcHR1cmVBbGxFeGNlcHRpb25zO2lmKGF3YWl0IHQucG9zdCgiRGVidWdnZXIuc2V0UGF1c2VPbkV4Y2VwdGlvbnMiLHtzdGF0ZTppPyJhbGwiOiJ1bmNhdWdodCJ9KSxpKXtjb25zdCBlPWEubWF4RXhjZXB0aW9uc1BlclNlY29uZHx8NTA7Zj1mdW5jdGlvbihlLHQsbil7bGV0IGk9MCxvPTUsYT0wO3JldHVybiBzZXRJbnRlcnZhbCgoKT0+ezA9PT1hP2k+ZSYmKG8qPTIsbihvKSxvPjg2NDAwJiYobz04NjQwMCksYT1vKTooYS09MSwwPT09YSYmdCgpKSxpPTB9LDFlMykudW5yZWYoKSwoKT0+e2krPTF9fShlLGFzeW5jKCk9PntzKCJSYXRlLWxpbWl0IGxpZnRlZC4iKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6ImFsbCJ9KX0sYXN5bmMgZT0+e3MoYFJhdGUtbGltaXQgZXhjZWVkZWQuIERpc2FibGluZyBjYXB0dXJpbmcgb2YgY2F1Z2h0IGV4Y2VwdGlvbnMgZm9yICR7ZX0gc2Vjb25kcy5gKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6InVuY2F1Z2h0In0pfSl9fSkoKS5jYXRjaChlPT57cygiRmFpbGVkIHRvIHN0YXJ0IGRlYnVnZ2VyIixlKX0pLHNldEludGVydmFsKCgpPT57fSwxZTQpOw==";function WOe(...t){j.log("[LocalVariables]",...t)}var ZOe=((t={})=>{function e(i,s){let a=(i.stacktrace?.frames||[]).filter(c=>c.function!=="new Promise");for(let c=0;c{s.terminate()}),s.once("error",a=>{WOe("Worker error",a)}),s.once("exit",a=>{WOe("Worker exit",a)}),s.unref()}return{name:"LocalVariablesAsync",async setup(i){if(!i.getOptions().includeLocalVariables)return;if(await j$()){j.warn("Local variables capture has been disabled because the debugger was already enabled");return}let a={...t,debug:j.isEnabled()};n().then(()=>{try{o(a)}catch(c){j.error("Failed to start worker",c)}},c=>{j.error("Failed to start inspector",c)})},processEvent(i,s){return r(i,s)}}});function YOe(t){if(t!==void 0)return t.slice(-10).reduce((e,r)=>`${e},${r.function},${r.lineno},${r.colno}`,"")}function U$t(t,e){if(e!==void 0)return YOe(t(e,1))}function JOe(t){let e=[],r=!1;function n(s){e=[],!r&&(r=!0,t(s))}e.push(n);function o(s){e.push(s)}function i(s){let a=e.pop()||n;try{a(s)}catch{n(s)}}return{add:o,next:i}}var h3=class t{constructor(e){this._session=e}static async create(e){if(e)return e;let r=await import("node:inspector");return new t(new r.Session)}configureAndConnect(e,r){this._session.connect(),this._session.on("Debugger.paused",n=>{e(n,()=>{this._session.post("Debugger.resume")})}),this._session.post("Debugger.enable"),this._session.post("Debugger.setPauseOnExceptions",{state:r?"all":"uncaught"})}setPauseOnExceptions(e){this._session.post("Debugger.setPauseOnExceptions",{state:e?"all":"uncaught"})}getLocalVariables(e,r){this._getProperties(e,n=>{let{add:o,next:i}=JOe(r);for(let s of n)if(s.value?.objectId&&s.value.className==="Array"){let a=s.value.objectId;o(c=>this._unrollArray(a,s.name,c,i))}else if(s.value?.objectId&&s.value.className==="Object"){let a=s.value.objectId;o(c=>this._unrollObject(a,s.name,c,i))}else s.value&&o(a=>this._unrollOther(s,a,i));i({})})}_getProperties(e,r){this._session.post("Runtime.getProperties",{objectId:e,ownProperties:!0},(n,o)=>{r(n?[]:o.result)})}_unrollArray(e,r,n,o){this._getProperties(e,i=>{n[r]=i.filter(s=>s.name!=="length"&&!isNaN(parseInt(s.name,10))).sort((s,a)=>parseInt(s.name,10)-parseInt(a.name,10)).map(s=>s.value?.value),o(n)})}_unrollObject(e,r,n,o){this._getProperties(e,i=>{n[r]=i.map(s=>[s.name,s.value?.value]).reduce((s,[a,c])=>(s[a]=c,s),{}),o(n)})}_unrollOther(e,r,n){e.value&&("value"in e.value?e.value.value===void 0||e.value.value===null?r[e.name]=`<${e.value.value}>`:r[e.name]=e.value.value:"description"in e.value&&e.value.type!=="function"?r[e.name]=`<${e.value.description}>`:e.value.type==="undefined"&&(r[e.name]="")),n(r)}},j$t="LocalVariables",z$t=((t={},e)=>{let r=new Qi(20),n,o=!1;function i(a){let c=YOe(a.stacktrace?.frames);if(c===void 0)return;let u=r.remove(c);if(u===void 0)return;let p=(a.stacktrace?.frames||[]).filter(f=>f.function!=="new Promise");for(let f=0;f= v18.");return}if(await j$()){j.warn("Local variables capture has been disabled because the debugger was already enabled");return}h3.create(e).then(p=>{function f(h,{params:{reason:_,data:v,callFrames:E}},x){if(_!=="exception"&&_!=="promiseRejection"){x();return}n?.();let w=U$t(h,v.description);if(w==null){x();return}let{add:I,next:N}=JOe($=>{r.set(w,$),x()});for(let $=0;$J.type==="local"),me=he.className==="global"||!he.className?G:`${he.className}.${G}`;if(Q?.object.objectId===void 0)I(J=>{J[$]={function:me},N(J)});else{let J=Q.object.objectId;I(Oe=>p.getLocalVariables(J,pt=>{Oe[$]={function:me,vars:pt},N(Oe)}))}}N([])}let m=t.captureAllExceptions!==!1;if(p.configureAndConnect((h,_)=>f(c.stackParser,h,_),m),m){let h=t.maxExceptionsPerSecond||50;n=HOe(h,()=>{j.log("Local variables rate-limit lifted."),p.setPauseOnExceptions(!0)},_=>{j.log(`Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${_} seconds.`),p.setPauseOnExceptions(!1)})}o=!0},p=>{j.log("The `LocalVariables` integration failed to start.",p)})},processEvent(a){return o?s(a):a},_getCachedFramesCount(){return r.size},_getFirstCachedFrame(){return r.values()[0]}}}),XOe=z$t;var QOe=(t={})=>Rm.major<19?XOe(t):ZOe(t);var kx=require("node:fs"),Mx=require("node:path");function tv(){try{return typeof module<"u"&&typeof module.exports<"u"}catch{return!1}}var g3,F$t="Modules",q$t=typeof __SENTRY_SERVER_MODULES__>"u"?{}:__SENTRY_SERVER_MODULES__,B$t=(()=>({name:F$t,processEvent(t){return t.modules={...t.modules,...eNe()},t},getModules:eNe})),tNe=B$t;function G$t(){try{return require.cache?Object.keys(require.cache):[]}catch{return[]}}function V$t(){return{...q$t,...K$t(),...tv()?H$t():{}}}function H$t(){let t=require.main?.paths||[],e=G$t(),r={},n=new Set;return e.forEach(o=>{let i=o,s=()=>{let a=i;if(i=(0,Mx.dirname)(a),!i||a===i||n.has(a))return;if(t.indexOf(i)<0)return s();let c=(0,Mx.join)(a,"package.json");if(n.add(a),!(0,kx.existsSync)(c))return s();try{let u=JSON.parse((0,kx.readFileSync)(c,"utf8"));r[u.name]=u.version}catch{}};s()}),r}function eNe(){return g3||(g3=V$t()),g3}function W$t(){try{let t=(0,Mx.join)(process.cwd(),"package.json");return JSON.parse((0,kx.readFileSync)(t,"utf8"))}catch{return{}}}function K$t(){let t=W$t();return{...t.dependencies,...t.devDependencies}}var Z$t=2e3;function Dx(t){_n(()=>{console.error(t)});let e=Ce();if(e===void 0){Kt&&j.warn("No NodeClient was defined, we are exiting the process now."),global.process.exit(1);return}let r=e.getOptions(),n=r?.shutdownTimeout&&r.shutdownTimeout>0?r.shutdownTimeout:Z$t;e.close(n).then(o=>{o||Kt&&j.warn("We reached the timeout for emptying the request buffer, still exiting now!"),global.process.exit(1)},o=>{Kt&&j.error(o)})}var Y$t="OnUncaughtException",rNe=(t={})=>{let e={exitEvenIfOtherHandlersAreRegistered:!1,...t};return{name:Y$t,setup(r){global.process.on("uncaughtException",J$t(r,e))}}};function J$t(t,e){let n=!1,o=!1,i=!1,s,a=t.getOptions();return Object.assign(c=>{let u=Dx;e.onFatalError?u=e.onFatalError:a.onFatalError&&(u=a.onFatalError);let f=global.process.listeners("uncaughtException").filter(h=>h.name!=="domainUncaughtExceptionClear"&&h.tag!=="sentry_tracingErrorCallback"&&h._errorHandler!==!0).length===0,m=e.exitEvenIfOtherHandlersAreRegistered||f;n?m&&(i?(Kt&&j.warn("uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown"),Dx(c)):o||(o=!0,setTimeout(()=>{i||(i=!0,u(s,c))},2e3))):(s=c,n=!0,Ce()===t&&Jn(c,{originalException:c,captureContext:{level:"fatal"},mechanism:{handled:!1,type:"onuncaughtexception"}}),!i&&m&&(i=!0,u(c)))},{_errorHandler:!0})}var X$t="OnUnhandledRejection",Q$t=((t={})=>{let e={mode:"warn",...t};return{name:X$t,setup(r){global.process.on("unhandledRejection",ekt(r,e))}}}),nNe=Q$t;function ekt(t,e){return function(n,o){if(Ce()!==t)return;let i=e.mode==="strict"?"fatal":"error",s=n&&typeof n=="object"?n._sentry_active_span:void 0;(s?c=>fd(s,c):c=>c())(()=>{Jn(n,{originalException:o,captureContext:{extra:{unhandledPromiseRejection:!0},level:i},mechanism:{handled:!1,type:"onunhandledrejection"}})}),tkt(n,e.mode)}}function tkt(t,e){let r="This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:";e==="warn"?_n(()=>{console.warn(r),console.error(t&&typeof t=="object"&&"stack"in t?t.stack:t)}):e==="strict"&&(_n(()=>{console.warn(r)}),Dx(t))}var oNe=W(require("node:http"),1);var _3="Spotlight",rkt=((t={})=>{let e={sidecarUrl:t.sidecarUrl||"http://localhost:8969/stream"};return{name:_3,setup(r){try{j.warn("[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?")}catch{}nkt(r,e)}}}),iNe=rkt;function nkt(t,e){let r=okt(e.sidecarUrl);if(!r)return;let n=0;t.on("beforeEnvelope",o=>{if(n>3){j.warn("[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests");return}let i=rx(o);D_(()=>{let s=oNe.request({method:"POST",path:r.pathname,hostname:r.hostname,port:r.port,headers:{"Content-Type":"application/x-sentry-envelope"}},a=>{a.statusCode&&a.statusCode>=200&&a.statusCode<400&&(n=0),a.on("data",()=>{}),a.on("end",()=>{}),a.setEncoding("utf8")});s.on("error",()=>{n++,j.warn("[Spotlight] Failed to send envelope to Spotlight Sidecar")}),s.write(i),s.end()})})}function okt(t){try{return new URL(`${t}`)}catch{j.warn(`[Spotlight] Invalid sidecar URL: ${t}`);return}}var sNe=W(require("node:util"),1);var ikt="NodeSystemError";function skt(t){return!(t instanceof Error)||!("errno"in t)||typeof t.errno!="number"?!1:sNe.getSystemErrorMap().has(t.errno)}var aNe=(t={})=>({name:ikt,processEvent:(e,r,n)=>{if(!skt(r.originalException))return e;let o=r.originalException,i={...o};!n.getOptions().sendDefaultPii&&t.includePaths!==!0&&(delete i.path,delete i.dest),e.contexts={...e.contexts,node_system_error:i};for(let s of e.exception?.values||[])s.value&&(o.path&&s.value.includes(o.path)&&(s.value=s.value.replace(`'${o.path}'`,"").trim()),o.dest&&s.value.includes(o.dest)&&(s.value=s.value.replace(`'${o.dest}'`,"").trim()));return e}});var v3=W(require("node:diagnostics_channel"),1);var akt="ChildProcess",cNe=(t={})=>({name:akt,setup(){v3.channel("child_process").subscribe(e=>{e&&typeof e=="object"&&"process"in e&&ckt(e.process,t)}),v3.channel("worker_threads").subscribe(e=>{e&&typeof e=="object"&&"worker"in e&&ukt(e.worker,t)})}});function ckt(t,e){let r=!1,n;t.on("spawn",()=>{if(t.spawnfile==="/usr/bin/sw_vers"){r=!0;return}n={spawnfile:t.spawnfile},e.includeChildProcessArgs&&(n.spawnargs=t.spawnargs)}).on("exit",o=>{r||(r=!0,o!==null&&o!==0&&ha({category:"child_process",message:`Child process exited with code '${o}'`,level:o===0?"info":"warning",data:n}))}).on("error",o=>{r||(r=!0,ha({category:"child_process",message:`Child process errored with '${o.message}'`,level:"error",data:n}))})}function ukt(t,e){let r;t.on("online",()=>{r=t.threadId}).on("error",n=>{e.captureWorkerErrors!==!1?Jn(n,{mechanism:{type:"instrument",handled:!1,data:{threadId:String(r)}}}):ha({category:"worker_thread",message:`Worker thread errored with '${n.message}'`,level:"error",data:{threadId:r}})})}var dNe=W(pNe(),1);var b3=gOe(dNe.AsyncLocalStorageContextManager);pe();function x3(){ua.disable(),ua.setLogger({error:j.error,warn:j.warn,info:j.log,debug:j.log,verbose:j.log},Kn.DEBUG)}var ykt="ProcessSession",fNe=()=>({name:ykt,setupOnce(){b1(),process.on("beforeExit",()=>{nr().getSession()?.status!=="ok"&&cx()})}});var Tkt=W(require("node:http"),1),bkt=W(require("node:https"),1),gNe=require("node:stream"),_Ne=require("node:zlib");var Al=W(require("node:net"),1),w3=W(require("node:tls"),1);var A3=W(require("node:http"),1);var vu=Symbol("AgentBaseInternalState"),G$=class extends A3.Agent{constructor(e){super(e),this[vu]={}}isSecureEndpoint(e){if(e){if(typeof e.secureEndpoint=="boolean")return e.secureEndpoint;if(typeof e.protocol=="string")return e.protocol==="https:"}let{stack:r}=new Error;return typeof r!="string"?!1:r.split(` -`).some(n=>n.indexOf("(https.js:")!==-1||n.indexOf("node:https:")!==-1)}createSocket(e,r,n){let o={...r,secureEndpoint:this.isSecureEndpoint(r)};Promise.resolve().then(()=>this.connect(e,o)).then(i=>{if(i instanceof A3.Agent)return i.addRequest(e,o);this[vu].currentSocket=i,super.createSocket(e,r,n)},n)}createConnection(){let e=this[vu].currentSocket;if(this[vu].currentSocket=void 0,!e)throw new Error("No socket was returned in the `connect()` function");return e}get defaultPort(){return this[vu].defaultPort??(this.protocol==="https:"?443:80)}set defaultPort(e){this[vu]&&(this[vu].defaultPort=e)}get protocol(){return this[vu].protocol??(this.isSecureEndpoint()?"https:":"http:")}set protocol(e){this[vu]&&(this[vu].protocol=e)}};function V$(...t){j.log("[https-proxy-agent:parse-proxy-response]",...t)}function mNe(t){return new Promise((e,r)=>{let n=0,o=[];function i(){let p=t.read();p?u(p):t.once("readable",i)}function s(){t.removeListener("end",a),t.removeListener("error",c),t.removeListener("readable",i)}function a(){s(),V$("onend"),r(new Error("Proxy connection ended before receiving CONNECT response"))}function c(p){s(),V$("onerror %o",p),r(p)}function u(p){o.push(p),n+=p.length;let f=Buffer.concat(o,n),m=f.indexOf(`\r -\r -`);if(m===-1){V$("have not received end of HTTP headers yet..."),i();return}let h=f.subarray(0,m).toString("ascii").split(`\r -`),_=h.shift();if(!_)return t.destroy(),r(new Error("No header received from proxy CONNECT response"));let v=_.split(" "),E=+(v[1]||0),x=v.slice(2).join(" "),w={};for(let I of h){if(!I)continue;let N=I.indexOf(":");if(N===-1)return t.destroy(),r(new Error(`Invalid header from proxy CONNECT response: "${I}"`));let $=I.slice(0,N).toLowerCase(),B=I.slice(N+1).trimStart(),G=w[$];typeof G=="string"?w[$]=[G,B]:Array.isArray(G)?G.push(B):w[$]=B}V$("got proxy server response: %o %o",_,w),s(),e({connect:{statusCode:E,statusText:x,headers:w},buffered:f})}t.on("error",c),t.on("end",a),i()})}function Lx(...t){j.log("[https-proxy-agent]",...t)}var Ux=class extends G${static __initStatic(){this.protocols=["http","https"]}constructor(e,r){super(r),this.options={},this.proxy=typeof e=="string"?new URL(e):e,this.proxyHeaders=r?.headers??{},Lx("Creating new HttpsProxyAgent instance: %o",this.proxy.href);let n=(this.proxy.hostname||this.proxy.host).replace(/^\[|\]$/g,""),o=this.proxy.port?parseInt(this.proxy.port,10):this.proxy.protocol==="https:"?443:80;this.connectOpts={ALPNProtocols:["http/1.1"],...r?hNe(r,"headers"):null,host:n,port:o}}async connect(e,r){let{proxy:n}=this;if(!r.host)throw new TypeError('No "host" provided');let o;if(n.protocol==="https:"){Lx("Creating `tls.Socket`: %o",this.connectOpts);let m=this.connectOpts.servername||this.connectOpts.host;o=w3.connect({...this.connectOpts,servername:m&&Al.isIP(m)?void 0:m})}else Lx("Creating `net.Socket`: %o",this.connectOpts),o=Al.connect(this.connectOpts);let i=typeof this.proxyHeaders=="function"?this.proxyHeaders():{...this.proxyHeaders},s=Al.isIPv6(r.host)?`[${r.host}]`:r.host,a=`CONNECT ${s}:${r.port} HTTP/1.1\r -`;if(n.username||n.password){let m=`${decodeURIComponent(n.username)}:${decodeURIComponent(n.password)}`;i["Proxy-Authorization"]=`Basic ${Buffer.from(m).toString("base64")}`}i.Host=`${s}:${r.port}`,i["Proxy-Connection"]||(i["Proxy-Connection"]=this.keepAlive?"Keep-Alive":"close");for(let m of Object.keys(i))a+=`${m}: ${i[m]}\r -`;let c=mNe(o);o.write(`${a}\r -`);let{connect:u,buffered:p}=await c;if(e.emit("proxyConnect",u),this.emit("proxyConnect",u,e),u.statusCode===200){if(e.once("socket",Ekt),r.secureEndpoint){Lx("Upgrading socket connection to TLS");let m=r.servername||r.host;return w3.connect({...hNe(r,"host","path","port"),socket:o,servername:Al.isIP(m)?void 0:m})}return o}o.destroy();let f=new Al.Socket({writable:!1});return f.readable=!0,e.once("socket",m=>{Lx("Replaying proxy buffer for failed request"),m.push(p),m.push(null)}),f}};Ux.__initStatic();function Ekt(t){t.resume()}function hNe(t,...e){let r={},n;for(n in t)e.includes(n)||(r[n]=t[n]);return r}var xkt=1024*32;function Akt(t){return new gNe.Readable({read(){this.push(t),this.push(null)}})}function vNe(t){let e;try{e=new URL(t.url)}catch{return _n(()=>{console.warn("[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.")}),lx(t,()=>Promise.resolve({}))}let r=e.protocol==="https:",n=wkt(e,t.proxy||(r?process.env.https_proxy:void 0)||process.env.http_proxy),o=r?bkt:Tkt,i=t.keepAlive===void 0?!1:t.keepAlive,s=n?new Ux(n):new o.Agent({keepAlive:i,maxSockets:30,timeout:2e3}),a=Rkt(t,t.httpModule??o,s);return lx(t,a)}function wkt(t,e){let{no_proxy:r}=process.env;if(!r?.split(",").some(o=>t.host.endsWith(o)||t.hostname.endsWith(o)))return e}function Rkt(t,e,r){let{hostname:n,pathname:o,port:i,protocol:s,search:a}=new URL(t.url);return function(u){return new Promise((p,f)=>{D_(()=>{let m=Akt(u.body),h={...t.headers};u.body.length>xkt&&(h["content-encoding"]="gzip",m=m.pipe((0,_Ne.createGzip)()));let _=e.request({method:"POST",agent:r,headers:h,hostname:n,path:`${o}${a}`,port:i,protocol:s,ca:t.caCerts},v=>{v.on("data",()=>{}),v.on("end",()=>{}),v.setEncoding("utf8");let E=v.headers["retry-after"]??null,x=v.headers["x-sentry-rate-limits"]??null;p({statusCode:v.statusCode,headers:{"retry-after":E,"x-sentry-rate-limits":Array.isArray(x)?x[0]||null:x}})});_.on("error",f),m.pipe(_)})})}}var Pkt=new Set(["false","f","n","no","off","0"]),Ikt=new Set(["true","t","y","yes","on","1"]);function H$(t,e){let r=String(t).toLowerCase();return Pkt.has(r)?!1:Ikt.has(r)?!0:e?.strict?null:!!t}var W$=require("node:path");function SNe(t){return t.replace(/^[A-Z]:/,"").replace(/\\/g,"/")}function yNe(t=process.argv[1]?zV(process.argv[1]):process.cwd(),e=W$.sep==="\\"){let r=e?SNe(t):t;return n=>{if(!n)return;let o=e?SNe(n):n,{dir:i,base:s,ext:a}=W$.posix.parse(o);(a===".js"||a===".mjs"||a===".cjs")&&(s=s.slice(0,a.length*-1));let c=decodeURIComponent(s);i||(i=".");let u=i.lastIndexOf("/node_modules");if(u>-1)return`${i.slice(u+14).replace(/\//g,".")}:${c}`;if(i.startsWith(r)){let p=i.slice(r.length+1).replace(/\//g,".");return p?`${p}:${c}`:c}return c}}function ENe(t){if(process.env.SENTRY_RELEASE)return process.env.SENTRY_RELEASE;if(ft.SENTRY_RELEASE?.id)return ft.SENTRY_RELEASE.id;let e=process.env.GITHUB_SHA||process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA||process.env.CI_BUILD_REF||process.env.CI_COMMIT_SHA||process.env.BITBUCKET_COMMIT,r=process.env.APPVEYOR_PULL_REQUEST_HEAD_COMMIT||process.env.APPVEYOR_REPO_COMMIT||process.env.CODEBUILD_RESOLVED_SOURCE_VERSION||process.env.AWS_COMMIT_ID||process.env.BUILD_SOURCEVERSION||process.env.GIT_CLONE_COMMIT_HASH||process.env.BUDDY_EXECUTION_REVISION||process.env.BUILDKITE_COMMIT||process.env.CIRCLE_SHA1||process.env.CIRRUS_CHANGE_IN_REPO||process.env.CF_REVISION||process.env.CM_COMMIT||process.env.CF_PAGES_COMMIT_SHA||process.env.DRONE_COMMIT_SHA||process.env.FC_GIT_COMMIT_SHA||process.env.HEROKU_TEST_RUN_COMMIT_VERSION||process.env.HEROKU_SLUG_COMMIT||process.env.RAILWAY_GIT_COMMIT_SHA||process.env.RENDER_GIT_COMMIT||process.env.SEMAPHORE_GIT_SHA||process.env.TRAVIS_PULL_REQUEST_SHA||process.env.VERCEL_GIT_COMMIT_SHA||process.env.VERCEL_GITHUB_COMMIT_SHA||process.env.VERCEL_GITLAB_COMMIT_SHA||process.env.VERCEL_BITBUCKET_COMMIT_SHA||process.env.ZEIT_GITHUB_COMMIT_SHA||process.env.ZEIT_GITLAB_COMMIT_SHA||process.env.ZEIT_BITBUCKET_COMMIT_SHA,n=process.env.CI_COMMIT_ID||process.env.SOURCE_COMMIT||process.env.SOURCE_VERSION||process.env.GIT_COMMIT||process.env.COMMIT_REF||process.env.BUILD_VCS_NUMBER||process.env.CI_COMMIT_SHA;return e||r||n||t}var TNe=KC(iH(yNe()));var bNe=W(require("node:os"),1);pe();var xNe=W(Ft(),1);var Z$=require("worker_threads");var Okt=6e4,K$=class extends ux{constructor(e){let r=e.includeServerName===!1?void 0:e.serverName||global.process.env.SENTRY_NAME||bNe.hostname(),n={...e,platform:"node",runtime:{name:"node",version:global.process.version},serverName:r};e.openTelemetryInstrumentations&&(0,xNe.registerInstrumentations)({instrumentations:e.openTelemetryInstrumentations}),ym(n,"node"),j.log(`Initializing Sentry: process: ${process.pid}, thread: ${Z$.isMainThread?"main":`worker-${Z$.threadId}`}.`),super(n),this.getOptions().enableLogs&&(this._logOnExitFlushListener=()=>{Sm(this)},r&&this.on("beforeCaptureLog",o=>{o.attributes={...o.attributes,"server.address":r}}),process.on("beforeExit",this._logOnExitFlushListener))}get tracer(){if(this._tracer)return this._tracer;let e="@sentry/node",r=xr,n=yt.getTracer(e,r);return this._tracer=n,n}async flush(e){return await this.traceProvider?.forceFlush(),this.getOptions().sendClientReports&&this._flushOutcomes(),super.flush(e)}close(e){return this._clientReportInterval&&clearInterval(this._clientReportInterval),this._clientReportOnExitFlushListener&&process.off("beforeExit",this._clientReportOnExitFlushListener),this._logOnExitFlushListener&&process.off("beforeExit",this._logOnExitFlushListener),super.close(e)}startClientReportTracking(){let e=this.getOptions();e.sendClientReports&&(this._clientReportOnExitFlushListener=()=>{this._flushOutcomes()},this._clientReportInterval=setInterval(()=>{Kt&&j.log("Flushing client reports based on interval."),this._flushOutcomes()},e.clientReportFlushInterval??Okt).unref(),process.on("beforeExit",this._clientReportOnExitFlushListener))}_getTraceInfoFromScope(e){return e?fOe(this,e):[void 0,void 0]}};var ANe=W(qb(),1),wNe=W(require("module"),1),Nkt={};function RNe(){let[t=0,e=0]=process.versions.node.split(".").map(Number);if(t>=21||t===20&&e>=6||t===18&&e>=19){if(!ft._sentryEsmLoaderHookRegistered)try{let{addHookMessagePort:r}=(0,ANe.createAddHookMessageChannel)();wNe.default.register("import-in-the-middle/hook.mjs",Nkt.url,{data:{addHookMessagePort:r,include:[]},transferList:[r]})}catch(r){j.warn("Failed to register ESM hook",r)}}else _n(()=>{console.warn(`[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.`)})}function Y$(){return[O1(),P1(),N1(),C1(),aNe(),$1(),ROe(),COe(),rNe(),nNe(),GOe(),QOe(),DOe(),cNe(),fNe(),tNe()]}function R3(t={}){return Ckt(t,Y$)}function Ckt(t={},e){let r=$kt(t,e);r.debug===!0&&(Kt?j.enable():_n(()=>{console.warn("[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.")})),!tv()&&r.registerEsmLoaderHooks!==!1&&RNe(),hOe(),It().update(r.initialScope),r.spotlight&&!r.integrations.some(({name:i})=>i===_3)&&r.integrations.push(iNe({sidecarUrl:typeof r.spotlight=="string"?r.spotlight:void 0})),ym(r,"node-core");let o=new K$(r);return It().setClient(o),o.init(),j.log(`Running in ${tv()?"CommonJS":"ESM"} mode.`),o.startClientReportTracking(),Dkt(),oOe(o),mOe(o),o}function P3(){if(!Kt)return;let t=cOe(),e=["SentryContextManager","SentryPropagator"];fo()&&e.push("SentrySpanProcessor");for(let r of e)t.includes(r)||j.error(`You have to set up the ${r}. Without this, the OpenTelemetry & Sentry integration will not work properly.`);t.includes("SentrySampler")||j.warn("You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`.")}function $kt(t,e){let r=kkt(t.release),n=t.spotlight??H$(process.env.SENTRY_SPOTLIGHT,{strict:!0})??process.env.SENTRY_SPOTLIGHT,o=Mkt(t.tracesSampleRate),i={...t,dsn:t.dsn??process.env.SENTRY_DSN,environment:t.environment??process.env.SENTRY_ENVIRONMENT,sendClientReports:t.sendClientReports??!0,transport:t.transport??vNe,stackParser:G8(t.stackParser||TNe),release:r,tracesSampleRate:o,spotlight:n,debug:H$(t.debug??process.env.SENTRY_DEBUG)},s=t.integrations,a=t.defaultIntegrations??e(i);return{...i,integrations:IV({defaultIntegrations:a,integrations:s})}}function kkt(t){if(t!==void 0)return t;let e=ENe();if(e!==void 0)return e}function Mkt(t){if(t!==void 0)return t;let e=process.env.SENTRY_TRACES_SAMPLE_RATE;if(!e)return;let r=parseFloat(e);return isFinite(r)?r:void 0}function Dkt(){if(H$(process.env.SENTRY_USE_ENVIRONMENT)!==!1){let t=process.env.SENTRY_TRACE,e=process.env.SENTRY_BAGGAGE,r=Qb(t,e);It().setPropagationContext(r)}}function zr(t,e){t.setAttribute(dr,e)}var I3="Http",PNe="@opentelemetry_sentry-patched/instrumentation-http",Lkt=Ge(`${I3}.sentry`,t=>new wm(t)),Ukt=Ge(I3,t=>{let e=new INe.HttpInstrumentation(t);try{e._diag=ua.createComponentLogger({namespace:PNe}),e.instrumentationName=PNe}catch{}return e});function jkt(t,e={}){return typeof t.spans=="boolean"?t.spans:!(e.skipOpenTelemetrySetup||!fo(e)&&Rm.major>=22)}var ONe=(t={})=>{let e=t.dropSpansForIncomingRequestStatusCodes??[[401,404],[300,399]];return{name:I3,setupOnce(){let r=jkt(t,Ce()?.getOptions());if(Lkt({...t,extractIncomingTraceFromHeader:!r,propagateTraceInOutgoingRequests:!r}),r){let n=qkt(t);Ukt(n)}},processEvent(r){if(r.type==="transaction"){let n=r.contexts?.trace?.data?.["http.response.status_code"];if(typeof n=="number"&&e.some(o=>{if(typeof o=="number")return o===n;let[i,s]=o;return n>=i&&n<=s}))return null}return r}}};function zkt(t){return"outputData"in t&&"outputSize"in t&&!("client"in t)&&!("statusCode"in t)}function Fkt(t){return t.headers["next-router-prefetch"]==="1"}function qkt(t={}){return{...t.instrumentation?._experimentalConfig,disableIncomingRequestInstrumentation:t.disableIncomingRequestSpans,ignoreOutgoingRequestHook:r=>{let n=Ex(r);if(!n)return!1;let o=t.ignoreOutgoingRequests;return!!o?.(n,r)},ignoreIncomingRequestHook:r=>{let n=r.url,o=r.method?.toUpperCase();if(o==="OPTIONS"||o==="HEAD"||t.ignoreStaticAssets!==!1&&o==="GET"&&n&&Bkt(n))return!0;let i=t.ignoreIncomingRequests;return!!(n&&i?.(n,r))},requireParentforOutgoingSpans:!1,requireParentforIncomingSpans:!1,requestHook:(r,n)=>{zr(r,"auto.http.otel.http"),!zkt(n)&&Fkt(n)&&r.setAttribute("sentry.http.prefetch",!0),t.instrumentation?.requestHook?.(r,n)},responseHook:(r,n)=>{t.instrumentation?.responseHook?.(r,n)},applyCustomAttributesOnSpan:(r,n,o)=>{t.instrumentation?.applyCustomAttributesOnSpan?.(r,n,o)}}}function Bkt(t){let e=hd(t);return!!(e.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)||e.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/))}var DNe=W(MNe(),1);var LNe="NodeFetch",Hkt=Ge(LNe,DNe.UndiciInstrumentation,t=>Jkt(t)),Wkt=Ge(`${LNe}.sentry`,Pm,t=>t),Kkt=((t={})=>({name:"NodeFetch",setupOnce(){Ykt(t,Ce()?.getOptions())&&Hkt(t),Wkt(t)}})),UNe=Kkt;function Zkt(t,e="/"){let r=`${t}`;return r.endsWith("/")&&e.startsWith("/")?`${r}${e.slice(1)}`:!r.endsWith("/")&&!e.startsWith("/")?`${r}/${e.slice(1)}`:`${r}${e}`}function Ykt(t,e={}){return typeof t.spans=="boolean"?t.spans:!e.skipOpenTelemetrySetup&&fo(e)}function Jkt(t={}){return{requireParentforSpans:!1,ignoreRequestHook:r=>{let n=Zkt(r.origin,r.path),o=t.ignoreOutgoingRequests;return!!(o&&n&&o(n))},startSpanHook:()=>({[dr]:"auto.http.otel.node_fetch"})}}var ZNe=W(KNe(),1);var yu=typeof __SENTRY_DEBUG__>"u"||__SENTRY_DEBUG__;var YNe="Express";function fMt(t){zr(t,"auto.http.otel.express");let e=Ye(t).data,r=e["express.type"];r&&t.setAttribute(lt,`${r}.express`);let n=e["express.name"];typeof n=="string"&&t.updateName(n)}function mMt(t,e){if(nr()===ou())return yu&&j.warn("Isolation scope is still default isolation scope - skipping setting transactionName"),e;if(t.layerType==="request_handler"){let r=t.request,n=r.method?r.method.toUpperCase():"GET";nr().setTransactionName(`${n} ${t.route}`)}return e}var JNe=Ge(YNe,()=>new ZNe.ExpressInstrumentation({requestHook:t=>fMt(t),spanNameHook:(t,e)=>mMt(t,e)})),hMt=(()=>({name:YNe,setupOnce(){JNe()}})),XNe=hMt;var q3=W(require("node:diagnostics_channel"),1);var z3=W(require("node:diagnostics_channel"),1);pe();var ck=W(_r(),1),PCe=W(Ft(),1);er();var SCe=W(pCe(),1);var Bx=t=>{if(typeof t!="string")throw new TypeError("invalid pattern");if(t.length>65536)throw new TypeError("pattern is too long")};var bMt={"[:alnum:]":["\\p{L}\\p{Nl}\\p{Nd}",!0],"[:alpha:]":["\\p{L}\\p{Nl}",!0],"[:ascii:]":["\\x00-\\x7f",!1],"[:blank:]":["\\p{Zs}\\t",!0],"[:cntrl:]":["\\p{Cc}",!0],"[:digit:]":["\\p{Nd}",!0],"[:graph:]":["\\p{Z}\\p{C}",!0,!0],"[:lower:]":["\\p{Ll}",!0],"[:print:]":["\\p{C}",!0],"[:punct:]":["\\p{P}",!0],"[:space:]":["\\p{Z}\\t\\r\\n\\v\\f",!0],"[:upper:]":["\\p{Lu}",!0],"[:word:]":["\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}",!0],"[:xdigit:]":["A-Fa-f0-9",!1]},Gx=t=>t.replace(/[[\]\\-]/g,"\\$&"),xMt=t=>t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),dCe=t=>t.join(""),fCe=(t,e)=>{let r=e;if(t.charAt(r)!=="[")throw new Error("not in a brace expression");let n=[],o=[],i=r+1,s=!1,a=!1,c=!1,u=!1,p=r,f="";e:for(;if?n.push(Gx(f)+"-"+Gx(v)):v===f&&n.push(Gx(v)),f="",i++;continue}if(t.startsWith("-]",i+1)){n.push(Gx(v+"-")),i+=2;continue}if(t.startsWith("-",i+1)){f=v,i+=2;continue}n.push(Gx(v)),i++}if(pe?t.replace(/\[([^\/\\])\]/g,"$1"):t.replace(/((?!\\).|^)\[([^\/\\])\]/g,"$1$2").replace(/\\([^\/])/g,"$1");var AMt=new Set(["!","?","+","*","@"]),mCe=t=>AMt.has(t),wMt="(?!(?:^|/)\\.\\.?(?:$|/))",ik="(?!\\.)",RMt=new Set(["[","."]),PMt=new Set(["..","."]),IMt=new Set("().*{}+?[]^$\\!"),OMt=t=>t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),U3="[^/]",hCe=U3+"*?",gCe=U3+"+?",cv=class t{type;#t;#e;#c=!1;#r=[];#n;#i;#s;#u=!1;#o;#a;#p=!1;constructor(e,r,n={}){this.type=e,e&&(this.#e=!0),this.#n=r,this.#t=this.#n?this.#n.#t:this,this.#o=this.#t===this?n:this.#t.#o,this.#s=this.#t===this?[]:this.#t.#s,e==="!"&&!this.#t.#u&&this.#s.push(this),this.#i=this.#n?this.#n.#r.length:0}get hasMagic(){if(this.#e!==void 0)return this.#e;for(let e of this.#r)if(typeof e!="string"&&(e.type||e.hasMagic))return this.#e=!0;return this.#e}toString(){return this.#a!==void 0?this.#a:this.type?this.#a=this.type+"("+this.#r.map(e=>String(e)).join("|")+")":this.#a=this.#r.map(e=>String(e)).join("")}#f(){if(this!==this.#t)throw new Error("should only call on root");if(this.#u)return this;this.toString(),this.#u=!0;let e;for(;e=this.#s.pop();){if(e.type!=="!")continue;let r=e,n=r.#n;for(;n;){for(let o=r.#i+1;!n.type&&otypeof r=="string"?r:r.toJSON()):[this.type,...this.#r.map(r=>r.toJSON())];return this.isStart()&&!this.type&&e.unshift([]),this.isEnd()&&(this===this.#t||this.#t.#u&&this.#n?.type==="!")&&e.push({}),e}isStart(){if(this.#t===this)return!0;if(!this.#n?.isStart())return!1;if(this.#i===0)return!0;let e=this.#n;for(let r=0;r{let[_,v,E,x]=typeof h=="string"?t.#m(h,this.#e,c):h.toRegExpSource(e);return this.#e=this.#e||E,this.#c=this.#c||x,_}).join(""),p="";if(this.isStart()&&typeof this.#r[0]=="string"&&!(this.#r.length===1&&PMt.has(this.#r[0]))){let _=RMt,v=r&&_.has(u.charAt(0))||u.startsWith("\\.")&&_.has(u.charAt(2))||u.startsWith("\\.\\.")&&_.has(u.charAt(4)),E=!r&&!e&&_.has(u.charAt(0));p=v?wMt:E?ik:""}let f="";return this.isEnd()&&this.#t.#u&&this.#n?.type==="!"&&(f="(?:$|\\/)"),[p+u+f,Ed(u),this.#e=!!this.#e,this.#c]}let n=this.type==="*"||this.type==="+",o=this.type==="!"?"(?:(?!(?:":"(?:",i=this.#d(r);if(this.isStart()&&this.isEnd()&&!i&&this.type!=="!"){let c=this.toString();return this.#r=[c],this.type=null,this.#e=void 0,[c,Ed(this.toString()),!1,!1]}let s=!n||e||r||!ik?"":this.#d(!0);s===i&&(s=""),s&&(i=`(?:${i})(?:${s})*?`);let a="";if(this.type==="!"&&this.#p)a=(this.isStart()&&!r?ik:"")+gCe;else{let c=this.type==="!"?"))"+(this.isStart()&&!r&&!e?ik:"")+hCe+")":this.type==="@"?")":this.type==="?"?")?":this.type==="+"&&s?")":this.type==="*"&&s?")?":`)${this.type}`;a=o+i+c}return[a,Ed(i),this.#e=!!this.#e,this.#c]}#d(e){return this.#r.map(r=>{if(typeof r=="string")throw new Error("string type in extglob ast??");let[n,o,i,s]=r.toRegExpSource(e);return this.#c=this.#c||s,n}).filter(r=>!(this.isStart()&&this.isEnd())||!!r).join("|")}static#m(e,r,n=!1){let o=!1,i="",s=!1;for(let a=0;ae?t.replace(/[?*()[\]]/g,"[$&]"):t.replace(/[?*()[\]\\]/g,"\\$&");var ko=(t,e,r={})=>(Bx(e),!r.nocomment&&e.charAt(0)==="#"?!1:new uv(e,r).match(t)),NMt=/^\*+([^+@!?\*\[\(]*)$/,CMt=t=>e=>!e.startsWith(".")&&e.endsWith(t),$Mt=t=>e=>e.endsWith(t),kMt=t=>(t=t.toLowerCase(),e=>!e.startsWith(".")&&e.toLowerCase().endsWith(t)),MMt=t=>(t=t.toLowerCase(),e=>e.toLowerCase().endsWith(t)),DMt=/^\*+\.\*+$/,LMt=t=>!t.startsWith(".")&&t.includes("."),UMt=t=>t!=="."&&t!==".."&&t.includes("."),jMt=/^\.\*+$/,zMt=t=>t!=="."&&t!==".."&&t.startsWith("."),FMt=/^\*+$/,qMt=t=>t.length!==0&&!t.startsWith("."),BMt=t=>t.length!==0&&t!=="."&&t!=="..",GMt=/^\?+([^+@!?\*\[\(]*)?$/,VMt=([t,e=""])=>{let r=yCe([t]);return e?(e=e.toLowerCase(),n=>r(n)&&n.toLowerCase().endsWith(e)):r},HMt=([t,e=""])=>{let r=ECe([t]);return e?(e=e.toLowerCase(),n=>r(n)&&n.toLowerCase().endsWith(e)):r},WMt=([t,e=""])=>{let r=ECe([t]);return e?n=>r(n)&&n.endsWith(e):r},KMt=([t,e=""])=>{let r=yCe([t]);return e?n=>r(n)&&n.endsWith(e):r},yCe=([t])=>{let e=t.length;return r=>r.length===e&&!r.startsWith(".")},ECe=([t])=>{let e=t.length;return r=>r.length===e&&r!=="."&&r!==".."},TCe=typeof process=="object"&&process?typeof process.env=="object"&&process.env&&process.env.__MINIMATCH_TESTING_PLATFORM__||process.platform:"posix",_Ce={win32:{sep:"\\"},posix:{sep:"/"}},ZMt=TCe==="win32"?_Ce.win32.sep:_Ce.posix.sep;ko.sep=ZMt;var Ea=Symbol("globstar **");ko.GLOBSTAR=Ea;var YMt="[^/]",JMt=YMt+"*?",XMt="(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?",QMt="(?:(?!(?:\\/|^)\\.).)*?",eDt=(t,e={})=>r=>ko(r,t,e);ko.filter=eDt;var ya=(t,e={})=>Object.assign({},t,e),tDt=t=>{if(!t||typeof t!="object"||!Object.keys(t).length)return ko;let e=ko;return Object.assign((n,o,i={})=>e(n,o,ya(t,i)),{Minimatch:class extends e.Minimatch{constructor(o,i={}){super(o,ya(t,i))}static defaults(o){return e.defaults(ya(t,o)).Minimatch}},AST:class extends e.AST{constructor(o,i,s={}){super(o,i,ya(t,s))}static fromGlob(o,i={}){return e.AST.fromGlob(o,ya(t,i))}},unescape:(n,o={})=>e.unescape(n,ya(t,o)),escape:(n,o={})=>e.escape(n,ya(t,o)),filter:(n,o={})=>e.filter(n,ya(t,o)),defaults:n=>e.defaults(ya(t,n)),makeRe:(n,o={})=>e.makeRe(n,ya(t,o)),braceExpand:(n,o={})=>e.braceExpand(n,ya(t,o)),match:(n,o,i={})=>e.match(n,o,ya(t,i)),sep:e.sep,GLOBSTAR:Ea})};ko.defaults=tDt;var bCe=(t,e={})=>(Bx(t),e.nobrace||!/\{(?:(?!\{).)*\}/.test(t)?[t]:(0,SCe.default)(t));ko.braceExpand=bCe;var rDt=(t,e={})=>new uv(t,e).makeRe();ko.makeRe=rDt;var nDt=(t,e,r={})=>{let n=new uv(e,r);return t=t.filter(o=>n.match(o)),n.options.nonull&&!t.length&&t.push(e),t};ko.match=nDt;var vCe=/[?*]|[+@!]\(.*?\)|\[|\]/,oDt=t=>t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),uv=class{options;set;pattern;windowsPathsNoEscape;nonegate;negate;comment;empty;preserveMultipleSlashes;partial;globSet;globParts;nocase;isWindows;platform;windowsNoMagicRoot;regexp;constructor(e,r={}){Bx(e),r=r||{},this.options=r,this.pattern=e,this.platform=r.platform||TCe,this.isWindows=this.platform==="win32",this.windowsPathsNoEscape=!!r.windowsPathsNoEscape||r.allowWindowsEscape===!1,this.windowsPathsNoEscape&&(this.pattern=this.pattern.replace(/\\/g,"/")),this.preserveMultipleSlashes=!!r.preserveMultipleSlashes,this.regexp=null,this.negate=!1,this.nonegate=!!r.nonegate,this.comment=!1,this.empty=!1,this.partial=!!r.partial,this.nocase=!!this.options.nocase,this.windowsNoMagicRoot=r.windowsNoMagicRoot!==void 0?r.windowsNoMagicRoot:!!(this.isWindows&&this.nocase),this.globSet=[],this.globParts=[],this.set=[],this.make()}hasMagic(){if(this.options.magicalBraces&&this.set.length>1)return!0;for(let e of this.set)for(let r of e)if(typeof r!="string")return!0;return!1}debug(...e){}make(){let e=this.pattern,r=this.options;if(!r.nocomment&&e.charAt(0)==="#"){this.comment=!0;return}if(!e){this.empty=!0;return}this.parseNegate(),this.globSet=[...new Set(this.braceExpand())],r.debug&&(this.debug=(...i)=>console.error(...i)),this.debug(this.pattern,this.globSet);let n=this.globSet.map(i=>this.slashSplit(i));this.globParts=this.preprocess(n),this.debug(this.pattern,this.globParts);let o=this.globParts.map((i,s,a)=>{if(this.isWindows&&this.windowsNoMagicRoot){let c=i[0]===""&&i[1]===""&&(i[2]==="?"||!vCe.test(i[2]))&&!vCe.test(i[3]),u=/^[a-z]:/i.test(i[0]);if(c)return[...i.slice(0,4),...i.slice(4).map(p=>this.parse(p))];if(u)return[i[0],...i.slice(1).map(p=>this.parse(p))]}return i.map(c=>this.parse(c))});if(this.debug(this.pattern,o),this.set=o.filter(i=>i.indexOf(!1)===-1),this.isWindows)for(let i=0;i=2?(e=this.firstPhasePreProcess(e),e=this.secondPhasePreProcess(e)):r>=1?e=this.levelOneOptimize(e):e=this.adjascentGlobstarOptimize(e),e}adjascentGlobstarOptimize(e){return e.map(r=>{let n=-1;for(;(n=r.indexOf("**",n+1))!==-1;){let o=n;for(;r[o+1]==="**";)o++;o!==n&&r.splice(n,o-n)}return r})}levelOneOptimize(e){return e.map(r=>(r=r.reduce((n,o)=>{let i=n[n.length-1];return o==="**"&&i==="**"?n:o===".."&&i&&i!==".."&&i!=="."&&i!=="**"?(n.pop(),n):(n.push(o),n)},[]),r.length===0?[""]:r))}levelTwoFileOptimize(e){Array.isArray(e)||(e=this.slashSplit(e));let r=!1;do{if(r=!1,!this.preserveMultipleSlashes){for(let o=1;oo&&n.splice(o+1,s-o);let a=n[o+1],c=n[o+2],u=n[o+3];if(a!==".."||!c||c==="."||c===".."||!u||u==="."||u==="..")continue;r=!0,n.splice(o,1);let p=n.slice(0);p[o]="**",e.push(p),o--}if(!this.preserveMultipleSlashes){for(let s=1;sr.length)}partsMatch(e,r,n=!1){let o=0,i=0,s=[],a="";for(;oI?r=r.slice(N):I>N&&(e=e.slice(I)))}}let{optimizationLevel:i=1}=this.options;i>=2&&(e=this.levelTwoFileOptimize(e)),this.debug("matchOne",this,{file:e,pattern:r}),this.debug("matchOne",e.length,r.length);for(var s=0,a=0,c=e.length,u=r.length;s>> no match, partial?`,e,m,r,h),m===c))}let v;if(typeof p=="string"?(v=f===p,this.debug("string match",p,f,v)):(v=p.test(f),this.debug("pattern match",p,f,v)),!v)return!1}if(s===c&&a===u)return!0;if(s===c)return n;if(a===u)return s===c-1&&e[s]==="";throw new Error("wtf?")}braceExpand(){return bCe(this.pattern,this.options)}parse(e){Bx(e);let r=this.options;if(e==="**")return Ea;if(e==="")return"";let n,o=null;(n=e.match(FMt))?o=r.dot?BMt:qMt:(n=e.match(NMt))?o=(r.nocase?r.dot?MMt:kMt:r.dot?$Mt:CMt)(n[1]):(n=e.match(GMt))?o=(r.nocase?r.dot?HMt:VMt:r.dot?WMt:KMt)(n):(n=e.match(DMt))?o=r.dot?UMt:LMt:(n=e.match(jMt))&&(o=zMt);let i=cv.fromGlob(e,this.options).toMMPattern();return o&&typeof i=="object"&&Reflect.defineProperty(i,"test",{value:o}),i}makeRe(){if(this.regexp||this.regexp===!1)return this.regexp;let e=this.set;if(!e.length)return this.regexp=!1,this.regexp;let r=this.options,n=r.noglobstar?JMt:r.dot?XMt:QMt,o=new Set(r.nocase?["i"]:[]),i=e.map(c=>{let u=c.map(p=>{if(p instanceof RegExp)for(let f of p.flags.split(""))o.add(f);return typeof p=="string"?oDt(p):p===Ea?Ea:p._src});return u.forEach((p,f)=>{let m=u[f+1],h=u[f-1];p!==Ea||h===Ea||(h===void 0?m!==void 0&&m!==Ea?u[f+1]="(?:\\/|"+n+"\\/)?"+m:u[f]=n:m===void 0?u[f-1]=h+"(?:\\/|"+n+")?":m!==Ea&&(u[f-1]=h+"(?:\\/|\\/"+n+"\\/)"+m,u[f+1]=Ea))}),u.filter(p=>p!==Ea).join("/")}).join("|"),[s,a]=e.length>1?["(?:",")"]:["",""];i="^"+s+i+a+"$",this.negate&&(i="^(?!"+i+").+$");try{this.regexp=new RegExp(i,[...o].join(""))}catch{this.regexp=!1}return this.regexp}slashSplit(e){return this.preserveMultipleSlashes?e.split("/"):this.isWindows&&/^\/\/[^\/]+/.test(e)?["",...e.split(/\/+/)]:e.split(/\/+/)}match(e,r=this.partial){if(this.debug("match",e,this.pattern),this.comment)return!1;if(this.empty)return e==="";if(e==="/"&&r)return!0;let n=this.options;this.isWindows&&(e=e.split("\\").join("/"));let o=this.slashSplit(e);this.debug(this.pattern,"split",o);let i=this.set;this.debug(this.pattern,"set",i);let s=o[o.length-1];if(!s)for(let a=o.length-2;!s&&a>=0;a--)s=o[a];for(let a=0;atypeof r=="function"?r(o):n(o.url,r)}}enable(){if(this._handleInitialization===void 0&&this.getConfig().registerOnInitialization){let e=this.plugin();this._handleInitialization=r=>{r.fastify.register(e)},z3.default.subscribe("fastify.initialization",this._handleInitialization)}return super.enable()}disable(){return this._handleInitialization&&(z3.default.unsubscribe("fastify.initialization",this._handleInitialization),this._handleInitialization=void 0),super.disable()}init(){return[]}plugin(){let e=this;return r[Symbol.for("skip-override")]=!0,r[Symbol.for("fastify.display-name")]="@fastify/otel",r[Symbol.for("plugin-meta")]={fastify:aDt,name:"@fastify/otel"},r;function r(n,o,i){n.decorate(Pi,e),n.decorate(wCe,n.addHook),n.decorate(RCe,n.setNotFoundHandler),n.decorateRequest("opentelemetry",function(){let m=this[Vx];return{span:this[bd],tracer:e.tracer,context:m,inject:(_,v)=>bi.inject(m,_,v),extract:(_,v)=>bi.extract(m,_,v)}}),n.decorateRequest(bd,null),n.decorateRequest(Vx,null),n.addHook("onRoute",function(f){if(e[sk]?.(f)===!0){e._logger.debug(`Ignoring route instrumentation ${f.method} ${f.url} because it matches the ignore path`);return}for(let m of ACe)if(f[m]!=null){let h=f[m];if(typeof h=="function")f[m]=p(h,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - route -> ${m}`,[Jr.FASTIFY_TYPE]:Td.ROUTE,[Qc]:f.url,[Jr.HOOK_CALLBACK_NAME]:h.name?.length>0?h.name:wl});else if(Array.isArray(h)){let _=[];for(let v of h)_.push(p(v,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - route -> ${m}`,[Jr.FASTIFY_TYPE]:Td.ROUTE,[Qc]:f.url,[Jr.HOOK_CALLBACK_NAME]:v.name?.length>0?v.name:wl}));f[m]=_}}f.onSend!=null?f.onSend=Array.isArray(f.onSend)?[...f.onSend,s]:[f.onSend,s]:f.onSend=s,f.onError!=null?f.onError=Array.isArray(f.onError)?[...f.onError,a]:[f.onError,a]:f.onError=a,f.handler=p(f.handler,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - route-handler`,[Jr.FASTIFY_TYPE]:Td.HANDLER,[Qc]:f.url,[Jr.HOOK_CALLBACK_NAME]:f.handler.name.length>0?f.handler.name:wl})}),n.addHook("onRequest",function(f,m,h){if(this[Pi].isEnabled()===!1)return h();if(this[Pi][sk]?.({url:f.url,method:f.method})===!0)return this[Pi]._logger.debug(`Ignoring request ${f.method} ${f.url} because it matches the ignore path`),h();let _=Je.active();yt.getSpan(_)==null&&(_=bi.extract(_,f.headers));let v=(0,ck.getRPCMetadata)(_);f.routeOptions.url!=null&&v?.type===ck.RPCType.HTTP&&(v.route=f.routeOptions.url);let E=this[Pi].tracer.startSpan("request",{attributes:{[Ts]:n[Pi].servername,[Jr.ROOT]:"@fastify/otel",[Qc]:f.url,[om]:f.method}},_);f[Vx]=yt.setSpan(_,E),f[bd]=E,Je.with(f[Vx],()=>{h()})}),n.addHook("onResponse",function(f,m,h){let _=f[bd];_!=null&&(_.setStatus({code:hn.OK,message:"OK"}),_.setAttributes({[Zp]:404}),_.end()),f[bd]=null,h()}),n.addHook=c,n.setNotFoundHandler=u,i();function s(f,m,h,_){let v=f[bd];v!=null&&(m.statusCode<500&&v.setStatus({code:hn.OK,message:"OK"}),v.setAttributes({[Zp]:m.statusCode}),v.end()),f[bd]=null,_(null,h)}function a(f,m,h,_){let v=f[bd];v!=null&&(v.setStatus({code:hn.ERROR,message:h.message}),v.recordException(h)),_()}function c(f,m){let h=this[wCe];return ACe.includes(f)?h.call(this,f,p(m,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - ${f}`,[Jr.FASTIFY_TYPE]:Td.INSTANCE,[Jr.HOOK_CALLBACK_NAME]:m.name?.length>0?m.name:wl})):h.call(this,f,m)}function u(f,m){let h=this[RCe];typeof f=="function"?(m=p(f,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - not-found-handler`,[Jr.FASTIFY_TYPE]:Td.INSTANCE,[Jr.HOOK_CALLBACK_NAME]:f.name?.length>0?f.name:wl}),h.call(this,m)):(f.preValidation!=null&&(f.preValidation=p(f.preValidation,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - not-found-handler - preValidation`,[Jr.FASTIFY_TYPE]:Td.INSTANCE,[Jr.HOOK_CALLBACK_NAME]:f.preValidation.name?.length>0?f.preValidation.name:wl})),f.preHandler!=null&&(f.preHandler=p(f.preHandler,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - not-found-handler - preHandler`,[Jr.FASTIFY_TYPE]:Td.INSTANCE,[Jr.HOOK_CALLBACK_NAME]:f.preHandler.name?.length>0?f.preHandler.name:wl})),m=p(m,{[Ts]:n[Pi].servername,[Jr.HOOK_NAME]:`${this.pluginName} - not-found-handler`,[Jr.FASTIFY_TYPE]:Td.INSTANCE,[Jr.HOOK_CALLBACK_NAME]:m.name?.length>0?m.name:wl}),h.call(this,f,m))}function p(f,m={}){return function(..._){let v=this[Pi],[E]=_;if(v.isEnabled()===!1)return f.call(this,..._);let x=E[Vx]??Je.active(),w=v.tracer.startSpan(`handler - ${f.name?.length>0?f.name:this.pluginName??wl}`,{attributes:m},x);return Je.with(yt.setSpan(x,w),function(){try{let I=f.call(this,..._);return typeof I?.then=="function"?I.then(N=>(w.end(),N),N=>(w.setStatus({code:hn.ERROR,message:N.message}),w.recordException(N),w.end(),Promise.reject(N))):(w.end(),I)}catch(I){throw w.setStatus({code:hn.ERROR,message:I.message}),w.recordException(I),w.end(),I}},this)}}}}};pe();var pk=W(_r(),1),Om=W(Ft(),1);er();var Rl;(function(t){let e="fastify.name";t.FASTIFY_NAME=e;let r="fastify.type";t.FASTIFY_TYPE=r;let n="hook.name";t.HOOK_NAME=n;let o="plugin.name";t.PLUGIN_NAME=o})(Rl||(Rl={}));var Hx;(function(t){let e="middleware";t.MIDDLEWARE=e;let r="request_handler";t.REQUEST_HANDLER=r})(Hx||(Hx={}));var Wx;(function(t){let e="middleware";t.MIDDLEWARE=e;let r="request handler";t.REQUEST_HANDLER=r})(Wx||(Wx={}));pe();var Kx=Symbol("opentelemetry.instrumentation.fastify.request_active_span");function F3(t,e,r,n={}){let o=e.startSpan(r,{attributes:n}),i=t[Kx]||[];return i.push(o),Object.defineProperty(t,Kx,{enumerable:!1,configurable:!0,value:i}),o}function uk(t,e){let r=t[Kx]||[];r.length&&(r.forEach(n=>{e&&(n.setStatus({code:hn.ERROR,message:e.message}),n.recordException(e)),n.end()}),delete t[Kx])}function OCe(t,e,r){let n,o;try{o=t(),ICe(o)&&o.then(i=>e(void 0,i),i=>e(i))}catch(i){n=i}finally{if(!ICe(o)&&(e(n,o),n))throw n;return o}}function ICe(t){return typeof t=="object"&&t&&typeof Object.getOwnPropertyDescriptor(t,"then")?.value=="function"||!1}var cDt="0.1.0",uDt="@sentry/instrumentation-fastify-v3",NCe="anonymous",lDt=new Set(["onTimeout","onRequest","preParsing","preValidation","preSerialization","preHandler","onSend","onResponse","onError"]),lk=class extends Om.InstrumentationBase{constructor(e={}){super(uDt,cDt,e)}init(){return[new Om.InstrumentationNodeModuleDefinition("fastify",[">=3.0.0 <4"],e=>this._patchConstructor(e))]}_hookOnRequest(){let e=this;return function(n,o,i){if(!e.isEnabled())return i();e._wrap(o,"send",e._patchSend());let s=n,a=(0,pk.getRPCMetadata)(Je.active()),c=s.routeOptions?s.routeOptions.url:n.routerPath;c&&a?.type===pk.RPCType.HTTP&&(a.route=c);let u=n.method||"GET";nr().setTransactionName(`${u} ${c}`),i()}}_wrapHandler(e,r,n,o){let i=this;return this._diag.debug("Patching fastify route.handler function"),function(...s){if(!i.isEnabled())return n.apply(this,s);let a=n.name||e||NCe,c=`${Wx.MIDDLEWARE} - ${a}`,u=s[1],p=F3(u,i.tracer,c,{[Rl.FASTIFY_TYPE]:Hx.MIDDLEWARE,[Rl.PLUGIN_NAME]:e,[Rl.HOOK_NAME]:r}),f=o&&s[s.length-1];return f&&(s[s.length-1]=function(...m){uk(u),f.apply(this,m)}),Je.with(yt.setSpan(Je.active(),p),()=>OCe(()=>n.apply(this,s),m=>{m instanceof Error&&(p.setStatus({code:hn.ERROR,message:m.message}),p.recordException(m)),o||uk(u)}))}}_wrapAddHook(){let e=this;return this._diag.debug("Patching fastify server.addHook function"),function(r){return function(...o){let i=o[0],s=o[1],a=this.pluginName;if(!lDt.has(i))return r.apply(this,o);let c=typeof o[o.length-1]=="function"&&s.constructor.name!=="AsyncFunction";return r.apply(this,[i,e._wrapHandler(a,i,s,c)])}}}_patchConstructor(e){let r=this;function n(...o){let i=e.fastify.apply(this,o);return i.addHook("onRequest",r._hookOnRequest()),i.addHook("preHandler",r._hookPreHandler()),pDt(),r._wrap(i,"addHook",r._wrapAddHook()),i}return e.errorCodes!==void 0&&(n.errorCodes=e.errorCodes),n.fastify=n,n.default=n,n}_patchSend(){let e=this;return this._diag.debug("Patching fastify reply.send function"),function(n){return function(...i){let s=i[0];return e.isEnabled()?(0,Om.safeExecuteInTheMiddle)(()=>n.apply(this,i),a=>{!a&&s instanceof Error&&(a=s),uk(this,a)}):n.apply(this,i)}}}_hookPreHandler(){let e=this;return this._diag.debug("Patching fastify preHandler function"),function(n,o,i){if(!e.isEnabled())return i();let s=n,a=s.routeOptions?.handler||s.context?.handler,c=a?.name.startsWith("bound ")?a.name.substring(6):a?.name,u=`${Wx.REQUEST_HANDLER} - ${c||this.pluginName||NCe}`,p={[Rl.PLUGIN_NAME]:this.pluginName,[Rl.FASTIFY_TYPE]:Hx.REQUEST_HANDLER,[b5]:s.routeOptions?s.routeOptions.url:n.routerPath};c&&(p[Rl.FASTIFY_NAME]=c);let f=F3(o,e.tracer,u,p);CCe(f);let{requestHook:m}=e.getConfig();return m&&(0,Om.safeExecuteInTheMiddle)(()=>m(f,{request:n}),h=>{h&&e._diag.error("request hook failed",h)},!0),Je.with(yt.setSpan(Je.active(),f),()=>{i()})}}};function pDt(){let t=Ce();t&&t.on("spanStart",e=>{CCe(e)})}function CCe(t){let e=Ye(t).data,r=e["fastify.type"];if(e[lt]||!r)return;t.setAttributes({[dr]:"auto.http.otel.fastify",[lt]:`${r}.fastify`});let n=e["fastify.name"]||e["plugin.name"]||e["hook.name"];if(typeof n=="string"){let o=n.replace(/^fastify -> /,"").replace(/^@fastify\/otel -> /,"");t.updateName(o)}}var kCe="Fastify",dDt="Fastify-V5",fDt="Fastify-V3",MCe=Ge(fDt,()=>new lk);function mDt(){let t=Ce();if(t)return t.getIntegrationByName(kCe)}function $Ce(t,e,r,n){let o=mDt()?.getShouldHandleError()||UCe;if(n==="diagnostics-channel"&&(this.diagnosticsChannelExists=!0),this.diagnosticsChannelExists&&n==="onError-hook"){yu&&j.warn("Fastify error handler was already registered via diagnostics channel.","You can safely remove `setupFastifyErrorHandler` call and set `shouldHandleError` on the integration options.");return}o(t,e,r)&&Jn(t,{mechanism:{handled:!1,type:"fastify"}})}var DCe=Ge(dDt,()=>{let t=new ak,e=t.plugin();return q3.subscribe("fastify.initialization",r=>{let n=r.fastify;n?.register(e).after(o=>{o?yu&&j.error("Failed to setup Fastify instrumentation",o):(gDt(),n&&_Dt(n))})}),q3.subscribe("tracing:fastify.request.handler:error",r=>{let{error:n,request:o,reply:i}=r;$Ce.call($Ce,n,o,i,"diagnostics-channel")}),t}),hDt=(({shouldHandleError:t})=>{let e;return{name:kCe,setupOnce(){e=t||UCe,MCe(),DCe()},getShouldHandleError(){return e},setShouldHandleError(r){e=r}}}),LCe=(t={})=>hDt(t);function UCe(t,e,r){let n=r.statusCode;return n>=500||n<=299}function jCe(t){let e=Ye(t),r=e.description,n=e.data,o=n["fastify.type"],i=o==="hook",s=o===r?.startsWith("handler -"),a=r==="request"||o==="request-handler";if(n[lt]||!s&&!a&&!i)return;let c=i?"hook":s?"middleware":a?"request-handler":"";t.setAttributes({[dr]:"auto.http.otel.fastify",[lt]:`${c}.fastify`});let u=n["fastify.name"]||n["plugin.name"]||n["hook.name"];if(typeof u=="string"){let p=u.replace(/^fastify -> /,"").replace(/^@fastify\/otel -> /,"");t.updateName(p)}}function gDt(){let t=Ce();t&&t.on("spanStart",e=>{jCe(e)})}function _Dt(t){t.addHook("onRequest",async(e,r)=>{if(e.opentelemetry){let{span:i}=e.opentelemetry();i&&jCe(i)}let n=e.routeOptions?.url,o=e.method||"GET";nr().setTransactionName(`${o} ${n}`)})}pe();var o1e=W(n1e(),1);var i1e="Graphql",s1e=Ge(i1e,o1e.GraphQLInstrumentation,t=>{let e=c1e(t);return{...e,responseHook(r,n){zr(r,"auto.graphql.otel.graphql"),n.errors?.length&&!Ye(r).status&&r.setStatus({code:hn.ERROR});let i=Ye(r).data,s=i["graphql.operation.type"],a=i["graphql.operation.name"];if(e.useOperationNameForRootSpan&&s){let c=vn(r),p=Ye(c).data[X_]||[],f=a?`${s} ${a}`:`${s}`;Array.isArray(p)?(p.push(f),c.setAttribute(X_,p)):typeof p=="string"?c.setAttribute(X_,[p,f]):c.setAttribute(X_,f),Ye(c).data["original-description"]||c.setAttribute("original-description",Ye(c).description),c.updateName(`${Ye(c).data["original-description"]} (${DDt(p)})`)}}}}),MDt=((t={})=>({name:i1e,setupOnce(){s1e(c1e(t))}})),a1e=MDt;function c1e(t){return{ignoreResolveSpans:!0,ignoreTrivialResolveSpans:!0,useOperationNameForRootSpan:!0,...t}}function DDt(t){if(Array.isArray(t)){let e=t.slice().sort();return e.length<=5?e.join(", "):`${e.slice(0,5).join(", ")}, +${e.length-5}`}return`${t}`}var y1e=W(S1e(),1);var E1e="Kafka",T1e=Ge(E1e,()=>new y1e.KafkaJsInstrumentation({consumerHook(t){zr(t,"auto.kafkajs.otel.consumer")},producerHook(t){zr(t,"auto.kafkajs.otel.producer")}})),UDt=(()=>({name:E1e,setupOnce(){T1e()}})),b1e=UDt;var O1e=W(I1e(),1);var N1e="LruMemoizer",C1e=Ge(N1e,()=>new O1e.LruMemoizerInstrumentation),zDt=(()=>({name:N1e,setupOnce(){C1e()}})),$1e=zDt;var F1e=W(z1e(),1);var q1e="Mongo",B1e=Ge(q1e,()=>new F1e.MongoDBInstrumentation({dbStatementSerializer:VDt,responseHook(t){zr(t,"auto.db.otel.mongo")}}));function VDt(t){let e=J3(t);return JSON.stringify(e)}function J3(t){if(Array.isArray(t))return t.map(e=>J3(e));if(HDt(t)){let e={};return Object.entries(t).map(([r,n])=>[r,J3(n)]).reduce((r,n)=>(KDt(n)&&(r[n[0]]=n[1]),r),e)}return"?"}function HDt(t){return typeof t=="object"&&t!==null&&!WDt(t)}function WDt(t){let e=!1;return typeof Buffer<"u"&&(e=Buffer.isBuffer(t)),e}function KDt(t){return Array.isArray(t)}var ZDt=(()=>({name:q1e,setupOnce(){B1e()}})),G1e=ZDt;var t$e=W(e$e(),1);var r$e="Mongoose",n$e=Ge(r$e,()=>new t$e.MongooseInstrumentation({responseHook(t){zr(t,"auto.db.otel.mongoose")}})),iLt=(()=>({name:r$e,setupOnce(){n$e()}})),o$e=iLt;var d$e=W(p$e(),1);var f$e="Mysql",m$e=Ge(f$e,()=>new d$e.MySQLInstrumentation({})),hLt=(()=>({name:f$e,setupOnce(){m$e()}})),h$e=hLt;var x$e=W(b$e(),1);var A$e="Mysql2",w$e=Ge(A$e,()=>new x$e.MySQL2Instrumentation({responseHook(t){zr(t,"auto.db.otel.mysql2")}})),RLt=(()=>({name:A$e,setupOnce(){w$e()}})),R$e=RLt;var J$e=W(k$e(),1),X$e=W(H$e(),1);var HLt=["get","set","setex"],f9=["get","mget"],WLt=["set","setex"];function s0(t,e){return t.includes(e.toLowerCase())}function m9(t){return s0(f9,t)?"cache.get":s0(WLt,t)?"cache.put":void 0}function KLt(t,e){return e.some(r=>t.startsWith(r))}function K$e(t,e){try{if(e.length===0)return;let r=o=>typeof o=="string"||typeof o=="number"||Buffer.isBuffer(o)?[o.toString()]:Array.isArray(o)?W$e(o.map(i=>r(i))):[""],n=e[0];return s0(HLt,t)&&n!=null?r(n):W$e(e.map(o=>r(o)))}catch{return}}function Z$e(t,e,r){if(!m9(t))return!1;for(let n of e)if(KLt(n,r))return!0;return!1}function Y$e(t){let e=r=>{try{return Buffer.isBuffer(r)?r.byteLength:typeof r=="string"?r.length:typeof r=="number"?r.toString().length:r==null?0:JSON.stringify(r).length}catch{return}};return Array.isArray(t)?t.reduce((r,n)=>{let o=e(n);return typeof o=="number"?r!==void 0?r+o:o:r},0):e(t)}function W$e(t){let e=[],r=n=>{n.forEach(o=>{Array.isArray(o)?r(o):e.push(o)})};return r(t),e}var Q$e="Redis",h9={},eke=(t,e,r,n)=>{t.setAttribute(dr,"auto.db.otel.redis");let o=K$e(e,r),i=m9(e);if(!o||!i||!h9.cachePrefixes||!Z$e(e,o,h9.cachePrefixes))return;let s=Ye(t).data["net.peer.name"],a=Ye(t).data["net.peer.port"];a&&s&&t.setAttributes({"network.peer.address":s,"network.peer.port":a});let c=Y$e(n);c&&t.setAttribute(fV,c),s0(f9,e)&&c!==void 0&&t.setAttribute(pV,c>0),t.setAttributes({[lt]:i,[dV]:o});let u=o.join(", ");t.updateName(nu(u,1024))},ZLt=Ge("IORedis",()=>new J$e.IORedisInstrumentation({responseHook:eke})),YLt=Ge("Redis",()=>new X$e.RedisInstrumentation({responseHook:eke})),tke=Object.assign(()=>{ZLt(),YLt()},{id:Q$e}),JLt=((t={})=>({name:Q$e,setupOnce(){h9=t,tke()}})),rke=JLt;var yke=W(Ske(),1);var Eke="Postgres",Tke=Ge(Eke,()=>new yke.PgInstrumentation({requireParentSpan:!0,requestHook(t){zr(t,"auto.db.otel.postgres")}})),fUt=(()=>({name:Eke,setupOnce(){Tke()}})),bke=fUt;pe();var $l=W(Ft(),1);er();var T9="PostgresJs",xke=[">=3.0.0 <4"],Ake=Ge(T9,t=>new E9({requireParentSpan:t?.requireParentSpan??!0,requestHook:t?.requestHook})),E9=class extends $l.InstrumentationBase{constructor(e){super("sentry-postgres-js",xr,e)}init(){let e=new $l.InstrumentationNodeModuleDefinition("postgres",xke);return["src","cf/src","cjs/src"].forEach(r=>{e.files.push(new $l.InstrumentationNodeModuleFile(`postgres/${r}/connection.js`,["*"],this._patchConnection.bind(this),this._unwrap.bind(this))),e.files.push(new $l.InstrumentationNodeModuleFile(`postgres/${r}/query.js`,xke,this._patchQuery.bind(this),this._unwrap.bind(this)))}),[e]}_shouldCreateSpans(){let e=this.getConfig();return yt.getSpan(Je.active())!==void 0||!e.requireParentSpan}_patchReject(e,r){return new Proxy(e,{apply:(n,o,i)=>{r.setStatus({code:2,message:i?.[0]?.message||"unknown_error"});let s=Reflect.apply(n,o,i);return r.setAttribute(O5,i?.[0]?.code||"Unknown error"),r.setAttribute(N5,i?.[0]?.name||"Unknown error"),r.end(),s}})}_patchResolve(e,r){return new Proxy(e,{apply:(n,o,i)=>{let s=Reflect.apply(n,o,i),a=i?.[0]?.command;return a&&r.setAttribute(bb,a),r.end(),s}})}_patchQuery(e){return e.Query.prototype.handle=new Proxy(e.Query.prototype.handle,{apply:async(r,n,o)=>{if(!this._shouldCreateSpans())return Reflect.apply(r,n,o);let i=this._sanitizeSqlQuery(n.strings?.[0]);return M_({name:i||"postgresjs.query",op:"db"},s=>{let c=It().getScopeData().contexts.postgresjsConnection;zr(s,"auto.db.otel.postgres");let{requestHook:u}=this.getConfig();u&&(0,$l.safeExecuteInTheMiddle)(()=>u(s,i,c),h=>{h&&j.error(`Error in requestHook for ${T9} integration:`,h)});let p=c?.ATTR_DB_NAMESPACE||"",f=c?.ATTR_SERVER_ADDRESS||"",m=c?.ATTR_SERVER_PORT||"";s.setAttribute(xb,"postgres"),s.setAttribute(Tb,p),s.setAttribute(Ab,f),s.setAttribute(wb,m),s.setAttribute(I5,i),n.resolve=this._patchResolve(n.resolve,s),n.reject=this._patchReject(n.reject,s);try{return Reflect.apply(r,n,o)}catch(h){throw s.setStatus({code:2}),s.end(),h}})}}),e}_patchConnection(e){return new Proxy(e,{apply:(r,n,o)=>{let i=o[0]?.database||"",s=o[0]?.host?.[0]||"",a=o[0]?.port?.[0]||"";return It().setContext("postgresjsConnection",{ATTR_DB_NAMESPACE:i,ATTR_SERVER_ADDRESS:s,ATTR_SERVER_PORT:a}),Reflect.apply(r,n,o)}})}_sanitizeSqlQuery(e){return e?e.replace(/\s+/g," ").trim().substring(0,1024).replace(/--.*?(\r?\n|$)/g,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/;\s*$/,"").replace(/\b\d+\b/g,"?").replace(/\s+/g," ").replace(/\bIN\b\s*\(\s*\?(?:\s*,\s*\?)*\s*\)/g,"IN (?)"):"Unknown SQL Query"}},mUt=(()=>({name:T9,setupOnce(){Ake()}})),wke=mUt;pe();var RM=W(GDe(),1);pe();var X4t=process.env.PRISMA_SHOW_ALL_TRACES==="true",Q4t="00-10-10-00";function e2t(t){switch(t){case"client":return Zn.CLIENT;case"internal":default:return Zn.INTERNAL}}var t2t=class{traceMiddleware;tracerProvider;ignoreSpanTypes;constructor({traceMiddleware:t,tracerProvider:e,ignoreSpanTypes:r}){this.traceMiddleware=t,this.tracerProvider=e,this.ignoreSpanTypes=r}isEnabled(){return!0}getTraceParent(t){let e=yt.getSpanContext(t??Je.active());return e?`00-${e.traceId}-${e.spanId}-0${e.traceFlags}`:Q4t}dispatchEngineSpans(t){let e=this.tracerProvider.getTracer("prisma"),r=new Map,n=t.filter(o=>o.parentId===null);for(let o of n)WDe(e,o,t,r,this.ignoreSpanTypes)}getActiveContext(){return Je.active()}runInChildSpan(t,e){if(typeof t=="string"&&(t={name:t}),t.internal&&!X4t||t.middleware&&!this.traceMiddleware)return e();let r=this.tracerProvider.getTracer("prisma"),n=t.context??this.getActiveContext(),o=`prisma:client:${t.name}`;if(KDe(o,this.ignoreSpanTypes))return e();if(t.active===!1){let i=r.startSpan(o,t,n);return VDe(i,e(i,n))}return r.startActiveSpan(o,t,i=>VDe(i,e(i,n)))}};function WDe(t,e,r,n,o){if(KDe(e.name,o))return;let i={attributes:e.attributes,kind:e2t(e.kind),startTime:e.startTime};t.startActiveSpan(e.name,i,s=>{n.set(e.id,s.spanContext().spanId),e.links&&s.addLinks(e.links.flatMap(c=>{let u=n.get(c);return u?{context:{spanId:u,traceId:s.spanContext().traceId,traceFlags:s.spanContext().traceFlags}}:[]}));let a=r.filter(c=>c.parentId===e.id);for(let c of a)WDe(t,c,r,n,o);s.end(e.endTime)})}function VDe(t,e){return r2t(e)?e.then(r=>(t.end(),r),r=>{throw t.end(),r}):(t.end(),e)}function r2t(t){return t!=null&&typeof t.then=="function"}function KDe(t,e){return e.some(r=>typeof r=="string"?r===t:r.test(t))}var ZDe={name:"@prisma/instrumentation",version:"6.13.0",description:"OpenTelemetry compliant instrumentation for Prisma Client",main:"dist/index.js",module:"dist/index.mjs",types:"dist/index.d.ts",exports:{".":{require:{types:"./dist/index.d.ts",default:"./dist/index.js"},import:{types:"./dist/index.d.ts",default:"./dist/index.mjs"}}},license:"Apache-2.0",homepage:"https://www.prisma.io",repository:{type:"git",url:"https://github.com/prisma/prisma.git",directory:"packages/instrumentation"},bugs:"https://github.com/prisma/prisma/issues",devDependencies:{"@prisma/internals":"workspace:*","@swc/core":"1.11.5","@types/jest":"29.5.14","@types/node":"18.19.76","@opentelemetry/api":"1.9.0",jest:"29.7.0","jest-junit":"16.0.0",typescript:"5.4.5"},dependencies:{"@opentelemetry/instrumentation":"^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0"},peerDependencies:{"@opentelemetry/api":"^1.8"},files:["dist"],keywords:["prisma","instrumentation","opentelemetry","otel"],scripts:{dev:"DEV=true tsx helpers/build.ts",build:"tsx helpers/build.ts",prepublishOnly:"pnpm run build",test:"jest"},sideEffects:!1},cW=ZDe.version,n2t=cW.split(".")[0],HDe="PRISMA_INSTRUMENTATION",aW=`V${n2t}_PRISMA_INSTRUMENTATION`,o2t=ZDe.name,i2t="@prisma/client",YDe=class extends RM.InstrumentationBase{tracerProvider;constructor(t={}){super(o2t,cW,t)}setTracerProvider(t){this.tracerProvider=t}init(){return[new RM.InstrumentationNodeModuleDefinition(i2t,[cW])]}enable(){let t=this._config,e={helper:new t2t({traceMiddleware:t.middleware??!1,tracerProvider:this.tracerProvider??yt.getTracerProvider(),ignoreSpanTypes:t.ignoreSpanTypes??[]})};global[HDe]=e,global[aW]=e}disable(){delete global[HDe],delete global[aW]}isEnabled(){return!!global[aW]}};var JDe="Prisma";function s2t(t){return!!t&&typeof t=="object"&&"dispatchEngineSpans"in t}function XDe(){let t=globalThis.PRISMA_INSTRUMENTATION;return t&&typeof t=="object"&&"helper"in t?t.helper:void 0}var uW=class extends YDe{constructor(){super()}enable(){super.enable();let e=XDe(),r=!1;s2t(e)&&(e.createEngineSpan=()=>{_n(()=>{r||(r=!0,console.warn("[Sentry] The Sentry SDK supports tracing with Prisma version 5 only with limited capabilities. For full tracing capabilities pass `prismaInstrumentation` for version 5 to the Sentry `prismaIntegration`. Read more: https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/"))})})}},a2t=Ge(JDe,t=>t?.prismaInstrumentation?t.prismaInstrumentation:new uW),QDe=({prismaInstrumentation:t}={})=>({name:JDe,setupOnce(){a2t({prismaInstrumentation:t})},setup(e){XDe()&&e.on("spanStart",r=>{let n=Ye(r);n.description?.startsWith("prisma:")&&r.setAttribute(dr,"auto.db.otel.prisma"),n.description==="prisma:engine:db_query"&&n.data["db.query.text"]&&r.updateName(n.data["db.query.text"]),n.description==="prisma:engine:db_query"&&!n.data["db.system"]&&r.setAttribute("db.system","prisma")})}});var uLe=W(cLe(),1);var lLe="Hapi",pLe=Ge(lLe,()=>new uLe.HapiInstrumentation),y2t=(()=>({name:lLe,setupOnce(){pLe()}})),dLe=y2t;var xLe=W(bLe(),1);er();var ALe="Koa",wLe=Ge(ALe,xLe.KoaInstrumentation,(t={})=>({ignoreLayersType:t.ignoreLayersType,requestHook(e,r){zr(e,"auto.http.otel.koa");let n=Ye(e).data,o=n["koa.type"];o&&e.setAttribute(lt,`${o}.koa`);let i=n["koa.name"];if(typeof i=="string"&&e.updateName(i||"< unknown >"),nr()===ou()){yu&&j.warn("Isolation scope is default isolation scope - skipping setting transactionName");return}let s=n[Qc],a=r.context?.request?.method?.toUpperCase()||"GET";s&&nr().setTransactionName(`${a} ${s}`)}})),I2t=((t={})=>({name:ALe,setupOnce(){wLe(t)}})),RLe=I2t;var DLe=W(MLe(),1);var LLe="Connect",ULe=Ge(LLe,()=>new DLe.ConnectInstrumentation),j2t=(()=>({name:LLe,setupOnce(){ULe()}})),jLe=j2t;var KLe=W(WLe(),1);var G2t=new Set(["callProcedure","execSql","execSqlBatch","execBulkLoad","prepare","execute"]),ZLe="Tedious",YLe=Ge(ZLe,()=>new KLe.TediousInstrumentation({})),V2t=(()=>{let t;return{name:ZLe,setupOnce(){let e=YLe();t=yx(e)},setup(e){t?.(()=>e.on("spanStart",r=>{let{description:n,data:o}=Ye(r);if(!n||o["db.system"]!=="mssql")return;let i=n.split(" ")[0]||"";G2t.has(i)&&r.setAttribute(dr,"auto.db.otel.tedious")}))}}}),JLe=V2t;var rUe=W(tUe(),1);var nUe="GenericPool",oUe=Ge(nUe,()=>new rUe.GenericPoolInstrumentation({})),W2t=(()=>{let t;return{name:nUe,setupOnce(){let e=oUe();t=yx(e)},setup(e){t?.(()=>e.on("spanStart",r=>{let o=Ye(r).description;(o==="generic-pool.aquire"||o==="generic-pool.acquire")&&r.setAttribute(dr,"auto.db.otel.generic_pool")}))}}}),iUe=W2t;var hUe=W(mUe(),1);var gUe="Amqplib",nFt={consumeEndHook:t=>{zr(t,"auto.amqplib.otel.consumer")},publishHook:t=>{zr(t,"auto.amqplib.otel.publisher")}},_Ue=Ge(gUe,()=>new hUe.AmqplibInstrumentation(nFt)),oFt=(()=>({name:gUe,setupOnce(){_Ue()}})),vUe=oFt;var I0="VercelAI";var HM=W(Ft(),1);var SUe=["generateText","streamText","generateObject","streamObject","embed","embedMany"];function iFt(t,e,r,n){let o=t?.recordInputs!==void 0?t.recordInputs:e.recordInputs!==void 0?e.recordInputs:r===!0?!0:n,i=t?.recordOutputs!==void 0?t.recordOutputs:e.recordOutputs!==void 0?e.recordOutputs:r===!0?!0:n;return{recordInputs:o,recordOutputs:i}}var VM=class t extends HM.InstrumentationBase{__init(){this._isPatched=!1}__init2(){this._callbacks=[]}constructor(e={}){super("@sentry/instrumentation-vercel-ai",xr,e),t.prototype.__init.call(this),t.prototype.__init2.call(this)}init(){return new HM.InstrumentationNodeModuleDefinition("ai",[">=3.0.0 <5"],this._patch.bind(this))}callWhenPatched(e){this._isPatched?e():this._callbacks.push(e)}_patch(e){this._isPatched=!0,this._callbacks.forEach(n=>n()),this._callbacks=[];function r(n){return(...o)=>{let i=o[0].experimental_telemetry||{},s=i.isEnabled,a=It().getClient(),c=a?.getIntegrationByName(I0),u=c?.options,p=c?!!a?.getOptions().sendDefaultPii:!1,{recordInputs:f,recordOutputs:m}=iFt(u,i,s,p);return o[0].experimental_telemetry={...i,isEnabled:s!==void 0?s:!0,recordInputs:f,recordOutputs:m},Tl(()=>n.apply(this,o),h=>{h&&typeof h=="object"&&zn(h,"_sentry_active_span",ma())})}}if(Object.prototype.toString.call(e)==="[object Module]"){for(let n of SUe)e[n]=r(e[n]);return e}else{let n=SUe.reduce((o,i)=>(o[i]=r(e[i]),o),{});return{...e,...n}}}};var yUe=Ge(I0,()=>new VM({}));function sFt(t){return!!t.getIntegrationByName("Modules")?.getModules?.()?.ai}var aFt=((t={})=>{let e;return{name:I0,options:t,setupOnce(){e=yUe()},afterAllSetup(r){t.force??sFt(r)?U1(r):e?.callWhenPatched(()=>U1(r))}}}),EUe=aFt;var KM=W(Ft(),1);var cFt=[">=4.0.0 <6"];function uFt(t,e){let r=t?.recordInputs??e,n=t?.recordOutputs??e;return{recordInputs:r,recordOutputs:n}}var WM=class extends KM.InstrumentationBase{constructor(e={}){super("@sentry/instrumentation-openai",xr,e)}init(){return new KM.InstrumentationNodeModuleDefinition("openai",cFt,this._patch.bind(this))}_patch(e){let r=e.OpenAI,n=function(...o){let i=Reflect.construct(r,o),s=It().getClient(),c=s?.getIntegrationByName(vd)?.options,u=!!s?.getOptions().sendDefaultPii,{recordInputs:p,recordOutputs:f}=uFt(c,u);return oH(i,{recordInputs:p,recordOutputs:f})};Object.setPrototypeOf(n,r),Object.setPrototypeOf(n.prototype,r.prototype);for(let o of Object.getOwnPropertyNames(r))if(!["length","name","prototype"].includes(o)){let i=Object.getOwnPropertyDescriptor(r,o);i&&Object.defineProperty(n,o,i)}try{e.OpenAI=n}catch{Object.defineProperty(e,"OpenAI",{value:n,writable:!0,configurable:!0,enumerable:!0})}if(e.default===r)try{e.default=n}catch{Object.defineProperty(e,"default",{value:n,writable:!0,configurable:!0,enumerable:!0})}return e}};var TUe=Ge(vd,()=>new WM({})),lFt=((t={})=>({name:vd,options:t,setupOnce(){TUe()}})),bUe=lFt;var IUe=W(Ft(),1);var AUe=W(require("node:net"),1);pe();var Dl=W(Ft(),1);er();function wUe(t,e,r,n,o){let s=()=>{},a=o.firestoreSpanCreationHook;typeof a=="function"&&(s=p=>{(0,Dl.safeExecuteInTheMiddle)(()=>a(p),f=>{f&&ua.error(f?.message)},!0)});let c=new Dl.InstrumentationNodeModuleDefinition("@firebase/firestore",e,p=>xUe(p,r,n,t,s)),u=["@firebase/firestore/dist/lite/index.node.cjs.js","@firebase/firestore/dist/lite/index.node.mjs.js","@firebase/firestore/dist/lite/index.rn.esm2017.js","@firebase/firestore/dist/lite/index.cjs.js"];for(let p of u)c.files.push(new Dl.InstrumentationNodeModuleFile(p,e,f=>xUe(f,r,n,t,s),f=>RUe(f,n)));return c}function xUe(t,e,r,n,o){return RUe(t,r),e(t,"addDoc",pFt(n,o)),e(t,"getDocs",fFt(n,o)),e(t,"setDoc",mFt(n,o)),e(t,"deleteDoc",dFt(n,o)),t}function RUe(t,e){for(let r of["addDoc","getDocs","setDoc","deleteDoc"])(0,Dl.isWrapped)(t[r])&&e(t,r);return t}function pFt(t,e){return function(n){return function(o,i){let s=YM(t,"addDoc",o);return e(s),ZM(s,()=>n(o,i))}}}function dFt(t,e){return function(n){return function(o){let i=YM(t,"deleteDoc",o.parent||o);return e(i),ZM(i,()=>n(o))}}}function fFt(t,e){return function(n){return function(o){let i=YM(t,"getDocs",o);return e(i),ZM(i,()=>n(o))}}}function mFt(t,e){return function(n){return function(o,i,s){let a=YM(t,"setDoc",o.parent||o);return e(a),ZM(a,()=>typeof s<"u"?n(o,i,s):n(o,i))}}}function ZM(t,e){return Je.with(yt.setSpan(Je.active(),t),()=>(0,Dl.safeExecuteInTheMiddle)(()=>e(),r=>{r&&t.recordException(r),t.end()},!0))}function YM(t,e,r){let n=t.startSpan(`${e} ${r.path}`,{kind:Zn.CLIENT});return gFt(n,r),n.setAttribute(bb,e),n}function hFt(t){let e,r;if(typeof t.host=="string")if(t.host.startsWith("[")){if(t.host.endsWith("]"))e=t.host.replace(/^\[|\]$/g,"");else if(t.host.includes("]:")){let n=t.host.lastIndexOf(":");n!==-1&&(e=t.host.slice(1,n).replace(/^\[|\]$/g,""),r=t.host.slice(n+1))}}else if(AUe.isIPv6(t.host))e=t.host;else{let n=t.host.lastIndexOf(":");n!==-1?(e=t.host.slice(0,n),r=t.host.slice(n+1)):e=t.host}return{address:e,port:r?parseInt(r,10):void 0}}function gFt(t,e){let r=e.firestore.app,n=r.options,i=(e.firestore.toJSON()||{}).settings||{},s={[P5]:e.path,[Tb]:r.name,[xb]:"firebase.firestore","firebase.firestore.type":e.type,"firebase.firestore.options.projectId":n.projectId,"firebase.firestore.options.appId":n.appId,"firebase.firestore.options.messagingSenderId":n.messagingSenderId,"firebase.firestore.options.storageBucket":n.storageBucket},{address:a,port:c}=hFt(i);a&&(s[Ab]=a),c&&(s[wb]=c),t.setAttributes(s)}var PUe={},_Ft=[">=3.0.0 <5"],JM=class extends IUe.InstrumentationBase{constructor(e=PUe){super("@sentry/instrumentation-firebase",xr,e)}setConfig(e={}){super.setConfig({...PUe,...e})}init(){let e=[];return e.push(wUe(this.tracer,_Ft,this._wrap,this._unwrap,this.getConfig())),e}};var OUe="Firebase",vFt={firestoreSpanCreationHook:t=>{zr(t,"auto.firebase.otel.firestore"),t.setAttribute(lt,"db.query")}},NUe=Ge(OUe,()=>new JM(vFt)),SFt=(()=>({name:OUe,setupOnce(){NUe()}})),CUe=SFt;function $Ue(){return[XNe(),LCe(),a1e(),G1e(),o$e(),h$e(),R$e(),rke(),bke(),QDe(),dLe(),RLe(),jLe(),JLe(),iUe(),b1e(),vUe(),$1e(),EUe(),bUe(),wke(),CUe()]}pe();var XM=W(TH(),1),kUe=W(WH(),1);er();var yFt=W(qb(),1);var wW=1e6;function MUe(t,e={}){t.getOptions().debug&&x3();let r=EFt(t,e);t.traceProvider=r}function EFt(t,e={}){let r=new kUe.BasicTracerProvider({sampler:new k$(t),resource:(0,XM.defaultResource)().merge((0,XM.resourceFromAttributes)({[Ts]:"node",[R5]:"sentry",[C5]:xr})),forceFlushTimeoutMillis:500,spanProcessors:[new $$({timeout:TFt(t.getOptions().maxSpanWaitDuration)}),...e.spanProcessors||[]]});return yt.setGlobalTracerProvider(r),bi.setGlobalPropagator(new C$),Je.setGlobalContextManager(new b3),r}function TFt(t){if(t!=null){if(t>wW)return yu&&j.warn(`\`maxSpanWaitDuration\` is too high, using the maximum value of ${wW}`),wW;if(t<=0||Number.isNaN(t)){yu&&j.warn("`maxSpanWaitDuration` must be a positive number, using default value instead.");return}return t}}function DUe(){return Y$().filter(e=>e.name!=="Http"&&e.name!=="NodeFetch").concat(ONe(),UNe())}function LUe(t){return[...DUe(),...fo(t)?$Ue():[]]}function RW(t={}){return bFt(t,LUe)}function bFt(t={},e){ym(t,"node");let r=R3({...t,defaultIntegrations:t.defaultIntegrations??e(t)});return r&&!t.skipOpenTelemetrySetup&&(MUe(r,{spanProcessors:t.openTelemetrySpanProcessors}),P3()),r}function jUe(){let t=new wN({name:"xcodebuildmcp",version:Ep},{capabilities:{tools:{listChanged:!0},resources:{subscribe:!0,listChanged:!0},logging:{}}}),e=L1(t);return A("info",`Server initialized with Sentry MCP instrumentation (version ${Ep})`),e}Go();var Xv=require("child_process");bE();function xFt(){try{let e=(0,Xv.execSync)("xcodebuild -version",{encoding:"utf8"}).trim().split(` -`).slice(0,2).join(" - "),r=(0,Xv.execSync)("xcode-select -p",{encoding:"utf8"}).trim(),n=(0,Xv.execSync)("xcrun --find xcodebuild",{encoding:"utf8"}).trim();return{version:e,path:r,selectedXcode:n}}catch(t){return{version:"Not available",path:"Not available",selectedXcode:"Not available",error:t instanceof Error?t.message:String(t)}}}function AFt(){let t=["INCREMENTAL_BUILDS_ENABLED","PATH","DEVELOPER_DIR","HOME","USER","TMPDIR","NODE_ENV","SENTRY_DISABLED"],e={};return t.forEach(r=>{e[r]=process.env[r]??""}),Object.keys(process.env).forEach(r=>{r.startsWith("XCODEBUILDMCP_")&&(e[r]=process.env[r]??"")}),e}function zUe(t){try{(0,Xv.execSync)(`which ${t}`,{stdio:"ignore"})}catch{return{available:!1}}let e,r={axe:"axe --version",mise:"mise --version"};if(t in r)try{e=(0,Xv.execSync)(r[t],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{}return{available:!0,version:e}}var FUe=!1;function wFt(){return process.env.SENTRY_DISABLED==="true"||process.env.XCODEBUILDMCP_SENTRY_DISABLED==="true"}function RFt(){return process.env.VITEST==="true"||!1}function qUe(){if(FUe||wFt()||RFt())return;FUe=!0,RW({dsn:process.env.SENTRY_DSN??"https://798607831167c7b9fe2f2912f5d3c665@o4509258288332800.ingest.de.sentry.io/4509258293837904",sendDefaultPii:!0,release:`xcodebuildmcp@${Ep}`,environment:"production",tracesSampleRate:1});let t=zUe("axe"),e=zUe("mise"),r=AFt(),n=xFt(),o={nodeVersion:process.version,platform:process.platform,arch:process.arch,axeAvailable:t.available?"true":"false",axeVersion:t.version??"Unknown",miseAvailable:e.available?"true":"false",miseVersion:e.version??"Unknown",...Object.fromEntries(Object.entries(r).map(([i,s])=>[`env_${i}`,s??""])),xcodeVersion:n.version??"Unknown",xcodePath:n.path??"Unknown"};E1(o)}var PFt=Yu.object({incrementalBuildsEnabled:Yu.boolean().default(!1).describe("Enable incremental builds via xcodemake (true/false)."),enabledWorkflows:Yu.string().default("").describe("Comma-separated list of workflows to load at startup."),sentryDisabled:Yu.boolean().default(!1).describe("Disable Sentry error reporting."),debug:Yu.boolean().default(!1).describe("Enable debug logging.")});function IFt(t){return process.env.INCREMENTAL_BUILDS_ENABLED=t.incrementalBuildsEnabled?"1":"0",process.env.XCODEBUILDMCP_ENABLED_WORKFLOWS=t.enabledWorkflows,process.env.XCODEBUILDMCP_SENTRY_DISABLED=t.sentryDisabled?"true":"false",process.env.XCODEBUILDMCP_DEBUG=t.debug?"true":"false",t.enabledWorkflows.split(",").map(e=>e.trim()).filter(Boolean)}function BUe({config:t}){let e=IFt(t);qUe();let r=jUe(),n=Lce(r,{enabledWorkflows:e}).catch(i=>{throw A("error",`Failed to bootstrap Smithery server: ${i instanceof Error?i.message:String(i)}`),i}),o={get(i,s,a){return s==="connect"?async(...c)=>(await n,i.connect.bind(i)(...c)):Reflect.get(i,s,a)}};return new Proxy(r,o)}var HUe=W(GUe(),1);function IW(t){return{ok:!0,value:t}}function VUe(t,e){return e!==void 0?{ok:!1,error:{type:t,...e}}:{ok:!1,error:t}}Te();function QM(t,e){let r=OFt(Object.entries(t.query));if(e){let n=e.safeParse(r);if(!n.success){let o=gi(e),i=n.error.issues.map(s=>{let a=r;for(let c of s.path){let u=String(c);if(a&&typeof a=="object"&&u in a)a=a[u];else{a=void 0;break}}return{param:s.path.join(".")||"root",pointer:`/${s.path.join("/")}`,reason:s.message,received:a}});return VUe({title:"Invalid configuration parameters",status:422,detail:"One or more config parameters are invalid.",instance:t.originalUrl,configSchema:o,errors:i,help:"Pass config as URL query params. Example: /mcp?param1=value1¶m2=value2"})}return IW(n.data)}return IW(r)}function OFt(t){let e={};for(let[r,n]of t){if(r==="api_key"||r==="profile")continue;let o=r.split("."),i=Array.isArray(n)?n[0]:n;if(typeof i!="string")continue;let s=i;try{s=JSON.parse(i)}catch{}HUe.default.set(e,o,s)}return e}var KUe=require("http2"),eD=require("http2"),OW=require("stream"),QUe=W(require("crypto"),1),Lm=class extends Error{constructor(t,e){super(t,e),this.name="RequestError"}},NFt=t=>t instanceof Lm?t:new Lm(t.message,{cause:t}),CFt=global.Request,N0=class extends CFt{constructor(t,e){typeof t=="object"&&rS in t&&(t=t[rS]()),typeof e?.body?.getReader<"u"&&(e.duplex??="half"),super(t,e)}},$Ft=t=>{let e=[],r=t.rawHeaders;for(let n=0;n{let i={method:t,headers:r,signal:o.signal};if(t==="TRACE"){i.method="GET";let s=new N0(e,i);return Object.defineProperty(s,"method",{get(){return"TRACE"}}),s}if(!(t==="GET"||t==="HEAD"))if("rawBody"in n&&n.rawBody instanceof Buffer)i.body=new ReadableStream({start(s){s.enqueue(n.rawBody),s.close()}});else if(n[ZUe]){let s;i.body=new ReadableStream({async pull(a){try{s||=OW.Readable.toWeb(n).getReader();let{done:c,value:u}=await s.read();c?a.close():a.enqueue(u)}catch(c){a.error(c)}}})}else i.body=OW.Readable.toWeb(n);return new N0(e,i)},rS=Symbol("getRequestCache"),MFt=Symbol("requestCache"),tD=Symbol("incomingKey"),rD=Symbol("urlKey"),DFt=Symbol("headersKey"),tS=Symbol("abortControllerKey"),LFt=Symbol("getAbortController"),nD={get method(){return this[tD].method||"GET"},get url(){return this[rD]},get headers(){return this[DFt]||=$Ft(this[tD])},[LFt](){return this[rS](),this[tS]},[rS](){return this[tS]||=new AbortController,this[MFt]||=kFt(this.method,this[rD],this.headers,this[tD],this[tS])}};["body","bodyUsed","cache","credentials","destination","integrity","mode","redirect","referrer","referrerPolicy","signal","keepalive"].forEach(t=>{Object.defineProperty(nD,t,{get(){return this[rS]()[t]}})});["arrayBuffer","blob","clone","formData","json","text"].forEach(t=>{Object.defineProperty(nD,t,{value:function(){return this[rS]()[t]()}})});Object.setPrototypeOf(nD,N0.prototype);var UFt=(t,e)=>{let r=Object.create(nD);r[tD]=t;let n=t.url||"";if(n[0]!=="/"&&(n.startsWith("http://")||n.startsWith("https://"))){if(t instanceof eD.Http2ServerRequest)throw new Lm("Absolute URL for :path is not allowed in HTTP/2");try{let a=new URL(n);r[rD]=a.href}catch(a){throw new Lm("Invalid absolute URL",{cause:a})}return r}let o=(t instanceof eD.Http2ServerRequest?t.authority:t.headers.host)||e;if(!o)throw new Lm("Missing host header");let i;if(t instanceof eD.Http2ServerRequest){if(i=t.scheme,!(i==="http"||i==="https"))throw new Lm("Unsupported scheme")}else i=t.socket&&t.socket.encrypted?"https":"http";let s=new URL(`${i}://${o}${n}`);if(s.hostname.length!==o.length&&s.hostname!==o.replace(/:\d+$/,""))throw new Lm("Invalid host header");return r[rD]=s.href,r},WUe=Symbol("responseCache"),eS=Symbol("getResponseCache"),Um=Symbol("cache"),CW=global.Response,C0=class YUe{#t;#e;[eS](){return delete this[Um],this[WUe]||=new CW(this.#t,this.#e)}constructor(e,r){let n;if(this.#t=e,r instanceof YUe){let o=r[WUe];if(o){this.#e=o,this[eS]();return}else this.#e=r.#e,n=new Headers(r.#e.headers)}else this.#e=r;(typeof e=="string"||typeof e?.getReader<"u"||e instanceof Blob||e instanceof Uint8Array)&&(n||=r?.headers||{"content-type":"text/plain; charset=UTF-8"},this[Um]=[r?.status||200,e,n])}get headers(){let e=this[Um];return e?(e[2]instanceof Headers||(e[2]=new Headers(e[2])),e[2]):this[eS]().headers}get status(){return this[Um]?.[0]??this[eS]().status}get ok(){let e=this.status;return e>=200&&e<300}};["body","bodyUsed","redirected","statusText","trailers","type","url"].forEach(t=>{Object.defineProperty(C0.prototype,t,{get(){return this[eS]()[t]}})});["arrayBuffer","blob","clone","formData","json","text"].forEach(t=>{Object.defineProperty(C0.prototype,t,{value:function(){return this[eS]()[t]()}})});Object.setPrototypeOf(C0,CW);Object.setPrototypeOf(C0.prototype,CW.prototype);async function jFt(t){return Promise.race([t,Promise.resolve().then(()=>Promise.resolve(void 0))])}function JUe(t,e,r){let n=a=>{t.cancel(a).catch(()=>{})};return e.on("close",n),e.on("error",n),(r??t.read()).then(s,o),t.closed.finally(()=>{e.off("close",n),e.off("error",n)});function o(a){a&&e.destroy(a)}function i(){t.read().then(s,o)}function s({done:a,value:c}){try{if(a)e.end();else if(!e.write(c))e.once("drain",i);else return t.read().then(s,o)}catch(u){o(u)}}}function zFt(t,e){if(t.locked)throw new TypeError("ReadableStream is locked.");return e.destroyed?void 0:JUe(t.getReader(),e)}var XUe=t=>{let e={};t instanceof Headers||(t=new Headers(t??void 0));let r=[];for(let[n,o]of t)n==="set-cookie"?r.push(o):e[n]=o;return r.length>0&&(e["set-cookie"]=r),e["content-type"]??="text/plain; charset=UTF-8",e},FFt="x-hono-already-sent",qFt=global.fetch;typeof global.crypto>"u"&&(global.crypto=QUe.default);global.fetch=(t,e)=>(e={compress:!1,...e},qFt(t,e));var $W=Symbol("outgoingEnded"),BFt=()=>new Response(null,{status:400}),eje=t=>new Response(null,{status:t instanceof Error&&(t.name==="TimeoutError"||t.constructor.name==="TimeoutError")?504:500}),NW=(t,e)=>{let r=t instanceof Error?t:new Error("unknown error",{cause:t});r.code==="ERR_STREAM_PREMATURE_CLOSE"?console.info("The user aborted a request."):(console.error(t),e.headersSent||e.writeHead(500,{"Content-Type":"text/plain"}),e.end(`Error: ${r.message}`),e.destroy(r))},tje=t=>{"flushHeaders"in t&&t.writable&&t.flushHeaders()},rje=async(t,e)=>{let[r,n,o]=t[Um];o instanceof Headers&&(o=XUe(o)),typeof n=="string"?o["Content-Length"]=Buffer.byteLength(n):n instanceof Uint8Array?o["Content-Length"]=n.byteLength:n instanceof Blob&&(o["Content-Length"]=n.size),e.writeHead(r,o),typeof n=="string"||n instanceof Uint8Array?e.end(n):n instanceof Blob?e.end(new Uint8Array(await n.arrayBuffer())):(tje(e),await zFt(n,e)?.catch(i=>NW(i,e))),e[$W]?.()},GFt=t=>typeof t.then=="function",VFt=async(t,e,r={})=>{if(GFt(t))if(r.errorHandler)try{t=await t}catch(o){let i=await r.errorHandler(o);if(!i)return;t=i}else t=await t.catch(eje);if(Um in t)return rje(t,e);let n=XUe(t.headers);if(t.body){let o=t.body.getReader(),i=[],s=!1,a;if(n["transfer-encoding"]!=="chunked"){let c=2;for(let u=0;u{console.error(f),s=!0});if(!p){if(u===1){await new Promise(f=>setTimeout(f)),c=3;continue}break}if(a=void 0,p.value&&i.push(p.value),p.done){s=!0;break}}s&&!("content-length"in n)&&(n["content-length"]=i.reduce((u,p)=>u+p.length,0))}e.writeHead(t.status,n),i.forEach(c=>{e.write(c)}),s?e.end():(i.length===0&&tje(e),await JUe(o,e,a))}else n[FFt]||(e.writeHead(t.status,n),e.end());e[$W]?.()},kW=(t,e={})=>{let r=e.autoCleanupIncoming??!0;return e.overrideGlobalObjects!==!1&&global.Request!==N0&&(Object.defineProperty(global,"Request",{value:N0}),Object.defineProperty(global,"Response",{value:C0})),async(n,o)=>{let i,s;try{s=UFt(n,e.hostname);let a=!r||n.method==="GET"||n.method==="HEAD";if(a||(n[ZUe]=!0,n.on("end",()=>{a=!0}),n instanceof KUe.Http2ServerRequest&&(o[$W]=()=>{a||setTimeout(()=>{a||setTimeout(()=>{n.destroy(),o.destroy()})})})),o.on("close",()=>{s[tS]&&(n.errored?s[tS].abort(n.errored.toString()):o.writableFinished||s[tS].abort("Client connection prematurely closed.")),a||setTimeout(()=>{a||setTimeout(()=>{n.destroy()})})}),i=t(s,{incoming:n,outgoing:o}),Um in i)return rje(i,o)}catch(a){if(i)return NW(a,o);if(e.errorHandler){if(i=await e.errorHandler(s?a:NFt(a)),!i)return}else s?i=eje(a):i=BFt()}try{return await VFt(i,o,e)}catch(a){return NW(a,o)}}};var oD=class{constructor(e={}){this._started=!1,this._streamMapping=new Map,this._requestToStreamMapping=new Map,this._requestResponseMap=new Map,this._initialized=!1,this._enableJsonResponse=!1,this._standaloneSseStreamId="_GET_stream",this.sessionIdGenerator=e.sessionIdGenerator,this._enableJsonResponse=e.enableJsonResponse??!1,this._eventStore=e.eventStore,this._onsessioninitialized=e.onsessioninitialized,this._onsessionclosed=e.onsessionclosed,this._allowedHosts=e.allowedHosts,this._allowedOrigins=e.allowedOrigins,this._enableDnsRebindingProtection=e.enableDnsRebindingProtection??!1,this._retryInterval=e.retryInterval}async start(){if(this._started)throw new Error("Transport already started");this._started=!0}createJsonErrorResponse(e,r,n,o){let i={code:r,message:n};return o?.data!==void 0&&(i.data=o.data),new Response(JSON.stringify({jsonrpc:"2.0",error:i,id:null}),{status:e,headers:{"Content-Type":"application/json",...o?.headers}})}validateRequestHeaders(e){if(this._enableDnsRebindingProtection){if(this._allowedHosts&&this._allowedHosts.length>0){let r=e.headers.get("host");if(!r||!this._allowedHosts.includes(r)){let n=`Invalid Host header: ${r}`;return this.onerror?.(new Error(n)),this.createJsonErrorResponse(403,-32e3,n)}}if(this._allowedOrigins&&this._allowedOrigins.length>0){let r=e.headers.get("origin");if(r&&!this._allowedOrigins.includes(r)){let n=`Invalid Origin header: ${r}`;return this.onerror?.(new Error(n)),this.createJsonErrorResponse(403,-32e3,n)}}}}async handleRequest(e,r){let n=this.validateRequestHeaders(e);if(n)return n;switch(e.method){case"POST":return this.handlePostRequest(e,r);case"GET":return this.handleGetRequest(e);case"DELETE":return this.handleDeleteRequest(e);default:return this.handleUnsupportedRequest()}}async writePrimingEvent(e,r,n,o){if(!this._eventStore||o<"2025-11-25")return;let i=await this._eventStore.storeEvent(n,{}),s=`id: ${i} -data: - -`;this._retryInterval!==void 0&&(s=`id: ${i} -retry: ${this._retryInterval} -data: - -`),e.enqueue(r.encode(s))}async handleGetRequest(e){if(!e.headers.get("accept")?.includes("text/event-stream"))return this.createJsonErrorResponse(406,-32e3,"Not Acceptable: Client must accept text/event-stream");let n=this.validateSession(e);if(n)return n;let o=this.validateProtocolVersion(e);if(o)return o;if(this._eventStore){let u=e.headers.get("last-event-id");if(u)return this.replayEvents(u)}if(this._streamMapping.get(this._standaloneSseStreamId)!==void 0)return this.createJsonErrorResponse(409,-32e3,"Conflict: Only one SSE stream is allowed per session");let i=new TextEncoder,s,a=new ReadableStream({start:u=>{s=u},cancel:()=>{this._streamMapping.delete(this._standaloneSseStreamId)}}),c={"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive"};return this.sessionId!==void 0&&(c["mcp-session-id"]=this.sessionId),this._streamMapping.set(this._standaloneSseStreamId,{controller:s,encoder:i,cleanup:()=>{this._streamMapping.delete(this._standaloneSseStreamId);try{s.close()}catch{}}}),new Response(a,{headers:c})}async replayEvents(e){if(!this._eventStore)return this.createJsonErrorResponse(400,-32e3,"Event store not configured");try{let r;if(this._eventStore.getStreamIdForEventId){if(r=await this._eventStore.getStreamIdForEventId(e),!r)return this.createJsonErrorResponse(400,-32e3,"Invalid event ID format");if(this._streamMapping.get(r)!==void 0)return this.createJsonErrorResponse(409,-32e3,"Conflict: Stream already has an active connection")}let n={"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive"};this.sessionId!==void 0&&(n["mcp-session-id"]=this.sessionId);let o=new TextEncoder,i,s=new ReadableStream({start:c=>{i=c},cancel:()=>{}}),a=await this._eventStore.replayEventsAfter(e,{send:async(c,u)=>{if(!this.writeSSEEvent(i,o,u,c)){this.onerror?.(new Error("Failed replay events"));try{i.close()}catch{}}}});return this._streamMapping.set(a,{controller:i,encoder:o,cleanup:()=>{this._streamMapping.delete(a);try{i.close()}catch{}}}),new Response(s,{headers:n})}catch(r){return this.onerror?.(r),this.createJsonErrorResponse(500,-32e3,"Error replaying events")}}writeSSEEvent(e,r,n,o){try{let i=`event: message -`;return o&&(i+=`id: ${o} -`),i+=`data: ${JSON.stringify(n)} - -`,e.enqueue(r.encode(i)),!0}catch{return!1}}handleUnsupportedRequest(){return new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}),{status:405,headers:{Allow:"GET, POST, DELETE","Content-Type":"application/json"}})}async handlePostRequest(e,r){try{let n=e.headers.get("accept");if(!n?.includes("application/json")||!n.includes("text/event-stream"))return this.createJsonErrorResponse(406,-32e3,"Not Acceptable: Client must accept both application/json and text/event-stream");let o=e.headers.get("content-type");if(!o||!o.includes("application/json"))return this.createJsonErrorResponse(415,-32e3,"Unsupported Media Type: Content-Type must be application/json");let i={headers:Object.fromEntries(e.headers.entries())},s;if(r?.parsedBody!==void 0)s=r.parsedBody;else try{s=await e.json()}catch{return this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON")}let a;try{Array.isArray(s)?a=s.map(x=>fI.parse(x)):a=[fI.parse(s)]}catch{return this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON-RPC message")}let c=a.some(hE);if(c){if(this._initialized&&this.sessionId!==void 0)return this.createJsonErrorResponse(400,-32600,"Invalid Request: Server already initialized");if(a.length>1)return this.createJsonErrorResponse(400,-32600,"Invalid Request: Only one initialization request is allowed");this.sessionId=this.sessionIdGenerator?.(),this._initialized=!0,this.sessionId&&this._onsessioninitialized&&await Promise.resolve(this._onsessioninitialized(this.sessionId))}if(!c){let x=this.validateSession(e);if(x)return x;let w=this.validateProtocolVersion(e);if(w)return w}if(!a.some(vp)){for(let x of a)this.onmessage?.(x,{authInfo:r?.authInfo,requestInfo:i});return new Response(null,{status:202})}let p=crypto.randomUUID(),f=a.find(x=>hE(x)),m=f?f.params.protocolVersion:e.headers.get("mcp-protocol-version")??Zte;if(this._enableJsonResponse)return new Promise(x=>{this._streamMapping.set(p,{resolveJson:x,cleanup:()=>{this._streamMapping.delete(p)}});for(let w of a)vp(w)&&this._requestToStreamMapping.set(w.id,p);for(let w of a)this.onmessage?.(w,{authInfo:r?.authInfo,requestInfo:i})});let h=new TextEncoder,_,v=new ReadableStream({start:x=>{_=x},cancel:()=>{this._streamMapping.delete(p)}}),E={"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"};this.sessionId!==void 0&&(E["mcp-session-id"]=this.sessionId);for(let x of a)vp(x)&&(this._streamMapping.set(p,{controller:_,encoder:h,cleanup:()=>{this._streamMapping.delete(p);try{_.close()}catch{}}}),this._requestToStreamMapping.set(x.id,p));await this.writePrimingEvent(_,h,p,m);for(let x of a){let w,I;vp(x)&&this._eventStore&&m>="2025-11-25"&&(w=()=>{this.closeSSEStream(x.id)},I=()=>{this.closeStandaloneSSEStream()}),this.onmessage?.(x,{authInfo:r?.authInfo,requestInfo:i,closeSSEStream:w,closeStandaloneSSEStream:I})}return new Response(v,{status:200,headers:E})}catch(n){return this.onerror?.(n),this.createJsonErrorResponse(400,-32700,"Parse error",{data:String(n)})}}async handleDeleteRequest(e){let r=this.validateSession(e);if(r)return r;let n=this.validateProtocolVersion(e);return n||(await Promise.resolve(this._onsessionclosed?.(this.sessionId)),await this.close(),new Response(null,{status:200}))}validateSession(e){if(this.sessionIdGenerator===void 0)return;if(!this._initialized)return this.createJsonErrorResponse(400,-32e3,"Bad Request: Server not initialized");let r=e.headers.get("mcp-session-id");if(!r)return this.createJsonErrorResponse(400,-32e3,"Bad Request: Mcp-Session-Id header is required");if(r!==this.sessionId)return this.createJsonErrorResponse(404,-32001,"Session not found")}validateProtocolVersion(e){let r=e.headers.get("mcp-protocol-version");if(r!==null&&!dE.includes(r))return this.createJsonErrorResponse(400,-32e3,`Bad Request: Unsupported protocol version: ${r} (supported versions: ${dE.join(", ")})`)}async close(){this._streamMapping.forEach(({cleanup:e})=>{e()}),this._streamMapping.clear(),this._requestResponseMap.clear(),this.onclose?.()}closeSSEStream(e){let r=this._requestToStreamMapping.get(e);if(!r)return;let n=this._streamMapping.get(r);n&&n.cleanup()}closeStandaloneSSEStream(){let e=this._streamMapping.get(this._standaloneSseStreamId);e&&e.cleanup()}async send(e,r){let n=r?.relatedRequestId;if((Ju(e)||Wh(e))&&(n=e.id),n===void 0){if(Ju(e)||Wh(e))throw new Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");let s;this._eventStore&&(s=await this._eventStore.storeEvent(this._standaloneSseStreamId,e));let a=this._streamMapping.get(this._standaloneSseStreamId);if(a===void 0)return;a.controller&&a.encoder&&this.writeSSEEvent(a.controller,a.encoder,e,s);return}let o=this._requestToStreamMapping.get(n);if(!o)throw new Error(`No connection established for request ID: ${String(n)}`);let i=this._streamMapping.get(o);if(!this._enableJsonResponse&&i?.controller&&i?.encoder){let s;this._eventStore&&(s=await this._eventStore.storeEvent(o,e)),this.writeSSEEvent(i.controller,i.encoder,e,s)}if(Ju(e)||Wh(e)){this._requestResponseMap.set(n,e);let s=Array.from(this._requestToStreamMapping.entries()).filter(([c,u])=>u===o).map(([c])=>c);if(s.every(c=>this._requestResponseMap.has(c))){if(!i)throw new Error(`No connection established for request ID: ${String(n)}`);if(this._enableJsonResponse&&i.resolveJson){let c={"Content-Type":"application/json"};this.sessionId!==void 0&&(c["mcp-session-id"]=this.sessionId);let u=s.map(p=>this._requestResponseMap.get(p));u.length===1?i.resolveJson(new Response(JSON.stringify(u[0]),{status:200,headers:c})):i.resolveJson(new Response(JSON.stringify(u),{status:200,headers:c}))}else i.cleanup();for(let c of s)this._requestResponseMap.delete(c),this._requestToStreamMapping.delete(c)}}}};var nS=class{constructor(e={}){this._requestContext=new WeakMap,this._webStandardTransport=new oD(e),this._requestListener=kW(async r=>{let n=this._requestContext.get(r);return this._webStandardTransport.handleRequest(r,{authInfo:n?.authInfo,parsedBody:n?.parsedBody})})}get sessionId(){return this._webStandardTransport.sessionId}set onclose(e){this._webStandardTransport.onclose=e}get onclose(){return this._webStandardTransport.onclose}set onerror(e){this._webStandardTransport.onerror=e}get onerror(){return this._webStandardTransport.onerror}set onmessage(e){this._webStandardTransport.onmessage=e}get onmessage(){return this._webStandardTransport.onmessage}async start(){return this._webStandardTransport.start()}async close(){return this._webStandardTransport.close()}async send(e,r){return this._webStandardTransport.send(e,r)}async handleRequest(e,r,n){let o=e.auth;await kW(async s=>this._webStandardTransport.handleRequest(s,{authInfo:o,parsedBody:n}))(e,r)}closeSSEStream(e){this._webStandardTransport.closeSSEStream(e)}closeStandaloneSSEStream(){this._webStandardTransport.closeStandaloneSSEStream()}};var JZ=W(Pc(),1),q6e=require("node:crypto");Te();var O6e=(t=1e3)=>{let e=new Map;return{get:r=>{let n=e.get(r);if(n)return e.delete(r),e.set(r,n),n},set:(r,n)=>{if(e.has(r))e.delete(r);else if(e.size>=t){let[o,i]=e.entries().next().value;i.close?.(),e.delete(o)}e.set(r,n)},delete:r=>e.delete(r)}};var N6e=(t=0)=>e=>`\x1B[${e+t}m`,C6e=(t=0)=>e=>`\x1B[${38+t};5;${e}m`,$6e=(t=0)=>(e,r,n)=>`\x1B[${38+t};2;${e};${r};${n}m`,nn={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],overline:[53,55],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],gray:[90,39],grey:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgGray:[100,49],bgGrey:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}},RDr=Object.keys(nn.modifier),z3t=Object.keys(nn.color),F3t=Object.keys(nn.bgColor),PDr=[...z3t,...F3t];function q3t(){let t=new Map;for(let[e,r]of Object.entries(nn)){for(let[n,o]of Object.entries(r))nn[n]={open:`\x1B[${o[0]}m`,close:`\x1B[${o[1]}m`},r[n]=nn[n],t.set(o[0],o[1]);Object.defineProperty(nn,e,{value:r,enumerable:!1})}return Object.defineProperty(nn,"codes",{value:t,enumerable:!1}),nn.color.close="\x1B[39m",nn.bgColor.close="\x1B[49m",nn.color.ansi=N6e(),nn.color.ansi256=C6e(),nn.color.ansi16m=$6e(),nn.bgColor.ansi=N6e(10),nn.bgColor.ansi256=C6e(10),nn.bgColor.ansi16m=$6e(10),Object.defineProperties(nn,{rgbToAnsi256:{value(e,r,n){return e===r&&r===n?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(r/255*5)+Math.round(n/255*5)},enumerable:!1},hexToRgb:{value(e){let r=/[a-f\d]{6}|[a-f\d]{3}/i.exec(e.toString(16));if(!r)return[0,0,0];let[n]=r;n.length===3&&(n=[...n].map(i=>i+i).join(""));let o=Number.parseInt(n,16);return[o>>16&255,o>>8&255,o&255]},enumerable:!1},hexToAnsi256:{value:e=>nn.rgbToAnsi256(...nn.hexToRgb(e)),enumerable:!1},ansi256ToAnsi:{value(e){if(e<8)return 30+e;if(e<16)return 90+(e-8);let r,n,o;if(e>=232)r=((e-232)*10+8)/255,n=r,o=r;else{e-=16;let a=e%36;r=Math.floor(e/36)/5,n=Math.floor(a/6)/5,o=a%6/5}let i=Math.max(r,n,o)*2;if(i===0)return 30;let s=30+(Math.round(o)<<2|Math.round(n)<<1|Math.round(r));return i===2&&(s+=60),s},enumerable:!1},rgbToAnsi:{value:(e,r,n)=>nn.ansi256ToAnsi(nn.rgbToAnsi256(e,r,n)),enumerable:!1},hexToAnsi:{value:e=>nn.ansi256ToAnsi(nn.hexToAnsi256(e)),enumerable:!1}}),nn}var B3t=q3t(),Ic=B3t;var cL=W(require("node:process"),1),M6e=W(require("node:os"),1),WZ=W(require("node:tty"),1);function Da(t,e=globalThis.Deno?globalThis.Deno.args:cL.default.argv){let r=t.startsWith("-")?"":t.length===1?"-":"--",n=e.indexOf(r+t),o=e.indexOf("--");return n!==-1&&(o===-1||n=2,has16m:t>=3}}function H3t(t,{streamIsTTY:e,sniffFlags:r=!0}={}){let n=G3t();n!==void 0&&(aL=n);let o=r?aL:n;if(o===0)return 0;if(r){if(Da("color=16m")||Da("color=full")||Da("color=truecolor"))return 3;if(Da("color=256"))return 2}if("TF_BUILD"in on&&"AGENT_NAME"in on)return 1;if(t&&!e&&o===void 0)return 0;let i=o||0;if(on.TERM==="dumb")return i;if(cL.default.platform==="win32"){let s=M6e.default.release().split(".");return Number(s[0])>=10&&Number(s[2])>=10586?Number(s[2])>=14931?3:2:1}if("CI"in on)return["GITHUB_ACTIONS","GITEA_ACTIONS","CIRCLECI"].some(s=>s in on)?3:["TRAVIS","APPVEYOR","GITLAB_CI","BUILDKITE","DRONE"].some(s=>s in on)||on.CI_NAME==="codeship"?1:i;if("TEAMCITY_VERSION"in on)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(on.TEAMCITY_VERSION)?1:0;if(on.COLORTERM==="truecolor"||on.TERM==="xterm-kitty"||on.TERM==="xterm-ghostty"||on.TERM==="wezterm")return 3;if("TERM_PROGRAM"in on){let s=Number.parseInt((on.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(on.TERM_PROGRAM){case"iTerm.app":return s>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(on.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(on.TERM)||"COLORTERM"in on?1:i}function k6e(t,e={}){let r=H3t(t,{streamIsTTY:t&&t.isTTY,...e});return V3t(r)}var W3t={stdout:k6e({isTTY:WZ.default.isatty(1)}),stderr:k6e({isTTY:WZ.default.isatty(2)})},D6e=W3t;function L6e(t,e,r){let n=t.indexOf(e);if(n===-1)return t;let o=e.length,i=0,s="";do s+=t.slice(i,n)+e+r,i=n+o,n=t.indexOf(e,i);while(n!==-1);return s+=t.slice(i),s}function U6e(t,e,r,n){let o=0,i="";do{let s=t[n-1]==="\r";i+=t.slice(o,s?n-1:n)+e+(s?`\r -`:` -`)+r,o=n+1,n=t.indexOf(` -`,o)}while(n!==-1);return i+=t.slice(o),i}var{stdout:j6e,stderr:z6e}=D6e,KZ=Symbol("GENERATOR"),bS=Symbol("STYLER"),tA=Symbol("IS_EMPTY"),F6e=["ansi","ansi","ansi256","ansi16m"],xS=Object.create(null),K3t=(t,e={})=>{if(e.level&&!(Number.isInteger(e.level)&&e.level>=0&&e.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let r=j6e?j6e.level:0;t.level=e.level===void 0?r:e.level};var Z3t=t=>{let e=(...r)=>r.join(" ");return K3t(e,t),Object.setPrototypeOf(e,rA.prototype),e};function rA(t){return Z3t(t)}Object.setPrototypeOf(rA.prototype,Function.prototype);for(let[t,e]of Object.entries(Ic))xS[t]={get(){let r=uL(this,YZ(e.open,e.close,this[bS]),this[tA]);return Object.defineProperty(this,t,{value:r}),r}};xS.visible={get(){let t=uL(this,this[bS],!0);return Object.defineProperty(this,"visible",{value:t}),t}};var ZZ=(t,e,r,...n)=>t==="rgb"?e==="ansi16m"?Ic[r].ansi16m(...n):e==="ansi256"?Ic[r].ansi256(Ic.rgbToAnsi256(...n)):Ic[r].ansi(Ic.rgbToAnsi(...n)):t==="hex"?ZZ("rgb",e,r,...Ic.hexToRgb(...n)):Ic[r][t](...n),Y3t=["rgb","hex","ansi256"];for(let t of Y3t){xS[t]={get(){let{level:r}=this;return function(...n){let o=YZ(ZZ(t,F6e[r],"color",...n),Ic.color.close,this[bS]);return uL(this,o,this[tA])}}};let e="bg"+t[0].toUpperCase()+t.slice(1);xS[e]={get(){let{level:r}=this;return function(...n){let o=YZ(ZZ(t,F6e[r],"bgColor",...n),Ic.bgColor.close,this[bS]);return uL(this,o,this[tA])}}}}var J3t=Object.defineProperties(()=>{},{...xS,level:{enumerable:!0,get(){return this[KZ].level},set(t){this[KZ].level=t}}}),YZ=(t,e,r)=>{let n,o;return r===void 0?(n=t,o=e):(n=r.openAll+t,o=e+r.closeAll),{open:t,close:e,openAll:n,closeAll:o,parent:r}},uL=(t,e,r)=>{let n=(...o)=>X3t(n,o.length===1?""+o[0]:o.join(" "));return Object.setPrototypeOf(n,J3t),n[KZ]=t,n[bS]=e,n[tA]=r,n},X3t=(t,e)=>{if(t.level<=0||!e)return t[tA]?"":e;let r=t[bS];if(r===void 0)return e;let{openAll:n,closeAll:o}=r;if(e.includes("\x1B"))for(;r!==void 0;)e=L6e(e,r.close,r.open),r=r.parent;let i=e.indexOf(` -`);return i!==-1&&(e=U6e(e,o,n,i)),n+e+o};Object.defineProperties(rA.prototype,xS);var Q3t=rA(),MDr=rA({level:z6e?z6e.level:0});var Km=Q3t;function e9t(t,e=3){let r=0,n=new WeakSet;try{return JSON.stringify(t,(o,i)=>{if(o===""?r=0:typeof i=="object"&&i!==null&&r++,r>e)return"[Object]";if(typeof i=="object"&&i!==null){if(n.has(i))return"[Circular]";n.add(i)}return typeof i=="function"?"[Function]":typeof i=="bigint"?`${i}n`:i instanceof Error?{name:i.name,message:i.message}:i instanceof Date?i.toISOString():i},2)}catch{return String(t)}}function lL(t="info"){let r={debug:0,info:1,warn:2,error:3}[t],n=(o,i,s,a)=>{let c=new Date().toISOString().split("T")[1].split(".")[0],u=Km.dim(c),p=i(o);if(typeof s=="string")return`${u} ${p} ${s}`;let f=a||"",m=e9t(s,3);return`${u} ${p} ${f} -${Km.dim(m)}`};return{debug:(o,i)=>{r<=0&&console.error(n("DEBUG",Km.cyan,o,i))},info:(o,i)=>{r<=1&&console.error(n("INFO",Km.blue,o,i))},warn:(o,i)=>{r<=2&&console.error(n("WARN",Km.yellow,o,i))},error:(o,i)=>{r<=3&&console.error(n("ERROR",Km.red,o,i))}}}function XZ(t,e){let r=e?.app??(0,JZ.default)();r.use("/mcp",JZ.default.json());let n=e?.sessionStore??O6e(),o=lL(e?.logLevel??"info");return r.post("/mcp",async(i,s)=>{o.debug({method:i.body.method,id:i.body.id,sessionId:i.headers["mcp-session-id"]},"MCP Request");let a=i.headers["mcp-session-id"],c;if(a&&n.get(a))c=n.get(a);else if(!a&&hE(i.body)){let u=(0,q6e.randomUUID)();c=new nS({sessionIdGenerator:()=>u,onsessioninitialized:m=>{n.set(m,c)}}),c.onclose=()=>{c.sessionId&&n.delete?.(c.sessionId)};let p=QM(i,e?.schema);if(!p.ok){let m=p.error.status||400;o.error({error:p.error,sessionId:u},"Config validation failed"),s.status(m).json(p.error);return}let f=p.value;try{o.info({sessionId:u},"Creating new session"),await t({sessionId:u,config:f,auth:i.auth,logger:o}).connect(c)}catch(m){o.error({error:m,sessionId:u},"Error initializing server"),s.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Error initializing server."},id:null});return}}else{o.warn({sessionId:a},"Session not found or expired"),s.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Session not found or expired"},id:null});return}await c.handleRequest(i,s,i.body),o.debug({method:i.body.method,id:i.body.id,sessionId:i.headers["mcp-session-id"]},"MCP Response sent")}),r.get("/.well-known/mcp-config",(i,s)=>{s.set("Content-Type","application/schema+json; charset=utf-8");let a=(e?.schema??k({})).meta({title:"MCP Session Configuration",description:"Schema for the /mcp endpoint configuration","x-query-style":"dot+bracket"}),c={...gi(a,{target:"draft-2020-12"}),$id:`${i.protocol}://${i.get("host")}/.well-known/mcp-config`};s.json(c)}),r.get("/mcp",async(i,s)=>{let a=i.headers["mcp-session-id"];if(!a||!n.get(a)){s.status(400).send("Invalid or expired session ID");return}await n.get(a).handleRequest(i,s)}),r.delete("/mcp",async(i,s)=>{let a=i.headers["mcp-session-id"];if(!a){o.warn("Session termination request missing session ID"),s.status(400).json({jsonrpc:"2.0",error:{code:-32600,message:"Missing mcp-session-id header"},id:null});return}let c=n.get(a);if(!c){o.warn({sessionId:a},"Session termination failed - not found"),s.status(404).json({jsonrpc:"2.0",error:{code:-32e3,message:"Session not found or expired"},id:null});return}c.close?.(),o.info({sessionId:a},"Session terminated"),s.status(204).end()}),{app:r}}var QZ=W(Pc(),1);Te();function eY(t,e){let r=e?.app??(0,QZ.default)(),n=lL(e?.logLevel??"info");return r.use("/mcp",QZ.default.json()),r.post("/mcp",async(o,i)=>{try{n.debug({method:o.body.method,id:o.body.id,params:o.body.params},"MCP Request");let s=QM(o,e?.schema);if(!s.ok){let p=s.error.status||400;n.error({error:s.error},"Config validation failed"),i.status(p).json(s.error);return}let a=s.value,c=t({config:a,auth:o.auth,logger:n}),u=new nS({sessionIdGenerator:void 0});i.on("close",()=>{u.close(),c.close()}),await c.connect(u),await u.handleRequest(o,i,o.body),n.debug({method:o.body.method,id:o.body.id},"MCP Response sent")}catch(s){n.error({error:s},"Error handling MCP request"),i.headersSent||i.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}}),r.get("/mcp",async(o,i)=>{i.status(405).json({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null})}),r.delete("/mcp",async(o,i)=>{i.status(405).json({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null})}),r.get("/.well-known/mcp-config",(o,i)=>{i.set("Content-Type","application/schema+json; charset=utf-8");let s=(e?.schema??k({})).meta({title:"MCP Session Configuration",description:"Schema for the /mcp endpoint configuration","x-query-style":"dot+bracket"}),a={...gi(s,{target:"draft-2020-12"}),$id:`${o.protocol}://${o.get("host")}/.well-known/mcp-config`};i.json(a)}),{app:r}}var mY=W(Pc(),1);var nA=new TextEncoder,Gl=new TextDecoder,ZDr=2**32;function B6e(...t){let e=t.reduce((o,{length:i})=>o+i,0),r=new Uint8Array(e),n=0;for(let o of t)r.set(o,n),n+=o.length;return r}function pL(t){let e=new Uint8Array(t.length);for(let r=0;r127)throw new TypeError("non-ASCII string encountered in encode()");e[r]=n}return e}function G6e(t){if(Uint8Array.fromBase64)return Uint8Array.fromBase64(t);let e=atob(t),r=new Uint8Array(e.length);for(let n=0;nnew TypeError(`CryptoKey does not support this operation, its ${e} must be ${t}`),RS=(t,e)=>t.name===e;function tY(t){return parseInt(t.name.slice(4),10)}function t9t(t){switch(t){case"ES256":return"P-256";case"ES384":return"P-384";case"ES512":return"P-521";default:throw new Error("unreachable")}}function r9t(t,e){if(e&&!t.usages.includes(e))throw new TypeError(`CryptoKey does not support this operation, its usages must include ${e}.`)}function V6e(t,e,r){switch(e){case"HS256":case"HS384":case"HS512":{if(!RS(t.algorithm,"HMAC"))throw Cu("HMAC");let n=parseInt(e.slice(2),10);if(tY(t.algorithm.hash)!==n)throw Cu(`SHA-${n}`,"algorithm.hash");break}case"RS256":case"RS384":case"RS512":{if(!RS(t.algorithm,"RSASSA-PKCS1-v1_5"))throw Cu("RSASSA-PKCS1-v1_5");let n=parseInt(e.slice(2),10);if(tY(t.algorithm.hash)!==n)throw Cu(`SHA-${n}`,"algorithm.hash");break}case"PS256":case"PS384":case"PS512":{if(!RS(t.algorithm,"RSA-PSS"))throw Cu("RSA-PSS");let n=parseInt(e.slice(2),10);if(tY(t.algorithm.hash)!==n)throw Cu(`SHA-${n}`,"algorithm.hash");break}case"Ed25519":case"EdDSA":{if(!RS(t.algorithm,"Ed25519"))throw Cu("Ed25519");break}case"ML-DSA-44":case"ML-DSA-65":case"ML-DSA-87":{if(!RS(t.algorithm,e))throw Cu(e);break}case"ES256":case"ES384":case"ES512":{if(!RS(t.algorithm,"ECDSA"))throw Cu("ECDSA");let n=t9t(e);if(t.algorithm.namedCurve!==n)throw Cu(n,"algorithm.namedCurve");break}default:throw new TypeError("CryptoKey does not support this operation")}r9t(t,r)}function H6e(t,e,...r){if(r=r.filter(Boolean),r.length>2){let n=r.pop();t+=`one of type ${r.join(", ")}, or ${n}.`}else r.length===2?t+=`one of type ${r[0]} or ${r[1]}.`:t+=`of type ${r[0]}.`;return e==null?t+=` Received ${e}`:typeof e=="function"&&e.name?t+=` Received function ${e.name}`:typeof e=="object"&&e!=null&&e.constructor?.name&&(t+=` Received an instance of ${e.constructor.name}`),t}var W6e=(t,...e)=>H6e("Key must be ",t,...e),rY=(t,e,...r)=>H6e(`Key for the ${t} algorithm must be `,e,...r);var nY=t=>{if(t?.[Symbol.toStringTag]==="CryptoKey")return!0;try{return t instanceof CryptoKey}catch{return!1}},oY=t=>t?.[Symbol.toStringTag]==="KeyObject",iY=t=>nY(t)||oY(t);function K6e(...t){let e=t.filter(Boolean);if(e.length===0||e.length===1)return!0;let r;for(let n of e){let o=Object.keys(n);if(!r||r.size===0){r=new Set(o);continue}for(let i of o){if(r.has(i))return!1;r.add(i)}}return!0}var n9t=t=>typeof t=="object"&&t!==null;function is(t){if(!n9t(t)||Object.prototype.toString.call(t)!=="[object Object]")return!1;if(Object.getPrototypeOf(t)===null)return!0;let e=t;for(;Object.getPrototypeOf(e)!==null;)e=Object.getPrototypeOf(e);return Object.getPrototypeOf(t)===e}function Z6e(t,e){if(t.startsWith("RS")||t.startsWith("PS")){let{modulusLength:r}=e.algorithm;if(typeof r!="number"||r<2048)throw new TypeError(`${t} requires key modulusLength to be 2048 bits or larger`)}}function o9t(t){let e,r;switch(t.kty){case"AKP":{switch(t.alg){case"ML-DSA-44":case"ML-DSA-65":case"ML-DSA-87":e={name:t.alg},r=t.priv?["sign"]:["verify"];break;default:throw new jo('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"RSA":{switch(t.alg){case"PS256":case"PS384":case"PS512":e={name:"RSA-PSS",hash:`SHA-${t.alg.slice(-3)}`},r=t.d?["sign"]:["verify"];break;case"RS256":case"RS384":case"RS512":e={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${t.alg.slice(-3)}`},r=t.d?["sign"]:["verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":e={name:"RSA-OAEP",hash:`SHA-${parseInt(t.alg.slice(-3),10)||1}`},r=t.d?["decrypt","unwrapKey"]:["encrypt","wrapKey"];break;default:throw new jo('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"EC":{switch(t.alg){case"ES256":e={name:"ECDSA",namedCurve:"P-256"},r=t.d?["sign"]:["verify"];break;case"ES384":e={name:"ECDSA",namedCurve:"P-384"},r=t.d?["sign"]:["verify"];break;case"ES512":e={name:"ECDSA",namedCurve:"P-521"},r=t.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":e={name:"ECDH",namedCurve:t.crv},r=t.d?["deriveBits"]:[];break;default:throw new jo('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"OKP":{switch(t.alg){case"Ed25519":case"EdDSA":e={name:"Ed25519"},r=t.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":e={name:t.crv},r=t.d?["deriveBits"]:[];break;default:throw new jo('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}default:throw new jo('Invalid or unsupported JWK "kty" (Key Type) Parameter value')}return{algorithm:e,keyUsages:r}}async function PS(t){if(!t.alg)throw new TypeError('"alg" argument is required when "jwk.alg" is not present');let{algorithm:e,keyUsages:r}=o9t(t),n={...t};return n.kty!=="AKP"&&delete n.alg,delete n.use,crypto.subtle.importKey("jwk",n,e,t.ext??!(t.d||t.priv),t.key_ops??r)}async function Y6e(t,e,r){if(!is(t))throw new TypeError("JWK must be an object");let n;switch(e??=t.alg,n??=r?.extractable??t.ext,t.kty){case"oct":if(typeof t.k!="string"||!t.k)throw new TypeError('missing "k" (Key Value) Parameter value');return Hd(t.k);case"RSA":if("oth"in t&&t.oth!==void 0)throw new jo('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');return PS({...t,alg:e,ext:n});case"AKP":{if(typeof t.alg!="string"||!t.alg)throw new TypeError('missing "alg" (Algorithm) Parameter value');if(e!==void 0&&e!==t.alg)throw new TypeError("JWK alg and alg option value mismatch");return PS({...t,ext:n})}case"EC":case"OKP":return PS({...t,alg:e,ext:n});default:throw new jo('Unsupported "kty" (Key Type) Parameter value')}}function J6e(t,e,r,n,o){if(o.crit!==void 0&&n?.crit===void 0)throw new t('"crit" (Critical) Header Parameter MUST be integrity protected');if(!n||n.crit===void 0)return new Set;if(!Array.isArray(n.crit)||n.crit.length===0||n.crit.some(s=>typeof s!="string"||s.length===0))throw new t('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');let i;r!==void 0?i=new Map([...Object.entries(r),...e.entries()]):i=e;for(let s of n.crit){if(!i.has(s))throw new jo(`Extension Header Parameter "${s}" is not recognized`);if(o[s]===void 0)throw new t(`Extension Header Parameter "${s}" is missing`);if(i.get(s)&&n[s]===void 0)throw new t(`Extension Header Parameter "${s}" MUST be integrity protected`)}return new Set(n.crit)}function X6e(t,e){if(e!==void 0&&(!Array.isArray(e)||e.some(r=>typeof r!="string")))throw new TypeError(`"${t}" option must be an array of strings`);if(e)return new Set(e)}var sA=t=>is(t)&&typeof t.kty=="string",Q6e=t=>t.kty!=="oct"&&(t.kty==="AKP"&&typeof t.priv=="string"||typeof t.d=="string"),eBe=t=>t.kty!=="oct"&&t.d===void 0&&t.priv===void 0,tBe=t=>t.kty==="oct"&&typeof t.k=="string";var IS,rBe=async(t,e,r,n=!1)=>{IS||=new WeakMap;let o=IS.get(t);if(o?.[r])return o[r];let i=await PS({...e,alg:r});return n&&Object.freeze(t),o?o[r]=i:IS.set(t,{[r]:i}),i},s9t=(t,e)=>{IS||=new WeakMap;let r=IS.get(t);if(r?.[e])return r[e];let n=t.type==="public",o=!!n,i;if(t.asymmetricKeyType==="x25519"){switch(e){case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":break;default:throw new TypeError("given KeyObject instance cannot be used for this algorithm")}i=t.toCryptoKey(t.asymmetricKeyType,o,n?[]:["deriveBits"])}if(t.asymmetricKeyType==="ed25519"){if(e!=="EdDSA"&&e!=="Ed25519")throw new TypeError("given KeyObject instance cannot be used for this algorithm");i=t.toCryptoKey(t.asymmetricKeyType,o,[n?"verify":"sign"])}switch(t.asymmetricKeyType){case"ml-dsa-44":case"ml-dsa-65":case"ml-dsa-87":{if(e!==t.asymmetricKeyType.toUpperCase())throw new TypeError("given KeyObject instance cannot be used for this algorithm");i=t.toCryptoKey(t.asymmetricKeyType,o,[n?"verify":"sign"])}}if(t.asymmetricKeyType==="rsa"){let s;switch(e){case"RSA-OAEP":s="SHA-1";break;case"RS256":case"PS256":case"RSA-OAEP-256":s="SHA-256";break;case"RS384":case"PS384":case"RSA-OAEP-384":s="SHA-384";break;case"RS512":case"PS512":case"RSA-OAEP-512":s="SHA-512";break;default:throw new TypeError("given KeyObject instance cannot be used for this algorithm")}if(e.startsWith("RSA-OAEP"))return t.toCryptoKey({name:"RSA-OAEP",hash:s},o,n?["encrypt"]:["decrypt"]);i=t.toCryptoKey({name:e.startsWith("PS")?"RSA-PSS":"RSASSA-PKCS1-v1_5",hash:s},o,[n?"verify":"sign"])}if(t.asymmetricKeyType==="ec"){let a=new Map([["prime256v1","P-256"],["secp384r1","P-384"],["secp521r1","P-521"]]).get(t.asymmetricKeyDetails?.namedCurve);if(!a)throw new TypeError("given KeyObject instance cannot be used for this algorithm");e==="ES256"&&a==="P-256"&&(i=t.toCryptoKey({name:"ECDSA",namedCurve:a},o,[n?"verify":"sign"])),e==="ES384"&&a==="P-384"&&(i=t.toCryptoKey({name:"ECDSA",namedCurve:a},o,[n?"verify":"sign"])),e==="ES512"&&a==="P-521"&&(i=t.toCryptoKey({name:"ECDSA",namedCurve:a},o,[n?"verify":"sign"])),e.startsWith("ECDH-ES")&&(i=t.toCryptoKey({name:"ECDH",namedCurve:a},o,n?[]:["deriveBits"]))}if(!i)throw new TypeError("given KeyObject instance cannot be used for this algorithm");return r?r[e]=i:IS.set(t,{[e]:i}),i};async function nBe(t,e){if(t instanceof Uint8Array||nY(t))return t;if(oY(t)){if(t.type==="secret")return t.export();if("toCryptoKey"in t&&typeof t.toCryptoKey=="function")try{return s9t(t,e)}catch(n){if(n instanceof TypeError)throw n}let r=t.export({format:"jwk"});return rBe(t,r,e)}if(sA(t))return t.k?Hd(t.k):rBe(t,t,e,!0);throw new Error("unreachable")}var OS=t=>t?.[Symbol.toStringTag],sY=(t,e,r)=>{if(e.use!==void 0){let n;switch(r){case"sign":case"verify":n="sig";break;case"encrypt":case"decrypt":n="enc";break}if(e.use!==n)throw new TypeError(`Invalid key for this operation, its "use" must be "${n}" when present`)}if(e.alg!==void 0&&e.alg!==t)throw new TypeError(`Invalid key for this operation, its "alg" must be "${t}" when present`);if(Array.isArray(e.key_ops)){let n;switch(!0){case(r==="sign"||r==="verify"):case t==="dir":case t.includes("CBC-HS"):n=r;break;case t.startsWith("PBES2"):n="deriveBits";break;case/^A\d{3}(?:GCM)?(?:KW)?$/.test(t):!t.includes("GCM")&&t.endsWith("KW")?n=r==="encrypt"?"wrapKey":"unwrapKey":n=r;break;case(r==="encrypt"&&t.startsWith("RSA")):n="wrapKey";break;case r==="decrypt":n=t.startsWith("RSA")?"unwrapKey":"deriveBits";break}if(n&&e.key_ops?.includes?.(n)===!1)throw new TypeError(`Invalid key for this operation, its "key_ops" must include "${n}" when present`)}return!0},a9t=(t,e,r)=>{if(!(e instanceof Uint8Array)){if(sA(e)){if(tBe(e)&&sY(t,e,r))return;throw new TypeError('JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present')}if(!iY(e))throw new TypeError(rY(t,e,"CryptoKey","KeyObject","JSON Web Key","Uint8Array"));if(e.type!=="secret")throw new TypeError(`${OS(e)} instances for symmetric algorithms must be of type "secret"`)}},c9t=(t,e,r)=>{if(sA(e))switch(r){case"decrypt":case"sign":if(Q6e(e)&&sY(t,e,r))return;throw new TypeError("JSON Web Key for this operation must be a private JWK");case"encrypt":case"verify":if(eBe(e)&&sY(t,e,r))return;throw new TypeError("JSON Web Key for this operation must be a public JWK")}if(!iY(e))throw new TypeError(rY(t,e,"CryptoKey","KeyObject","JSON Web Key"));if(e.type==="secret")throw new TypeError(`${OS(e)} instances for asymmetric algorithms must not be of type "secret"`);if(e.type==="public")switch(r){case"sign":throw new TypeError(`${OS(e)} instances for asymmetric algorithm signing must be of type "private"`);case"decrypt":throw new TypeError(`${OS(e)} instances for asymmetric algorithm decryption must be of type "private"`)}if(e.type==="private")switch(r){case"verify":throw new TypeError(`${OS(e)} instances for asymmetric algorithm verifying must be of type "public"`);case"encrypt":throw new TypeError(`${OS(e)} instances for asymmetric algorithm encryption must be of type "public"`)}};function oBe(t,e,r){switch(t.substring(0,2)){case"A1":case"A2":case"di":case"HS":case"PB":a9t(t,e,r);break;default:c9t(t,e,r)}}function iBe(t,e){let r=`SHA-${t.slice(-3)}`;switch(t){case"HS256":case"HS384":case"HS512":return{hash:r,name:"HMAC"};case"PS256":case"PS384":case"PS512":return{hash:r,name:"RSA-PSS",saltLength:parseInt(t.slice(-3),10)>>3};case"RS256":case"RS384":case"RS512":return{hash:r,name:"RSASSA-PKCS1-v1_5"};case"ES256":case"ES384":case"ES512":return{hash:r,name:"ECDSA",namedCurve:e.namedCurve};case"Ed25519":case"EdDSA":return{name:"Ed25519"};case"ML-DSA-44":case"ML-DSA-65":case"ML-DSA-87":return{name:t};default:throw new jo(`alg ${t} is not supported either by JOSE or your javascript runtime`)}}async function sBe(t,e,r){if(e instanceof Uint8Array){if(!t.startsWith("HS"))throw new TypeError(W6e(e,"CryptoKey","KeyObject","JSON Web Key"));return crypto.subtle.importKey("raw",e,{hash:`SHA-${t.slice(-3)}`,name:"HMAC"},!1,[r])}return V6e(e,t,r),e}async function aBe(t,e,r,n){let o=await sBe(t,e,"verify");Z6e(t,o);let i=iBe(t,o.algorithm);try{return await crypto.subtle.verify(i,o,r,n)}catch{return!1}}async function cBe(t,e,r){if(!is(t))throw new Mn("Flattened JWS must be an object");if(t.protected===void 0&&t.header===void 0)throw new Mn('Flattened JWS must have either of the "protected" or "header" members');if(t.protected!==void 0&&typeof t.protected!="string")throw new Mn("JWS Protected Header incorrect type");if(t.payload===void 0)throw new Mn("JWS Payload missing");if(typeof t.signature!="string")throw new Mn("JWS Signature missing or incorrect type");if(t.header!==void 0&&!is(t.header))throw new Mn("JWS Unprotected Header incorrect type");let n={};if(t.protected)try{let E=Hd(t.protected);n=JSON.parse(Gl.decode(E))}catch{throw new Mn("JWS Protected Header is invalid")}if(!K6e(n,t.header))throw new Mn("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");let o={...n,...t.header},i=J6e(Mn,new Map([["b64",!0]]),r?.crit,n,o),s=!0;if(i.has("b64")&&(s=n.b64,typeof s!="boolean"))throw new Mn('The "b64" (base64url-encode payload) Header Parameter must be a boolean');let{alg:a}=o;if(typeof a!="string"||!a)throw new Mn('JWS "alg" (Algorithm) Header Parameter missing or invalid');let c=r&&X6e("algorithms",r.algorithms);if(c&&!c.has(a))throw new dL('"alg" (Algorithm) Header Parameter value not allowed');if(s){if(typeof t.payload!="string")throw new Mn("JWS Payload must be a string")}else if(typeof t.payload!="string"&&!(t.payload instanceof Uint8Array))throw new Mn("JWS Payload must be a string or an Uint8Array instance");let u=!1;typeof e=="function"&&(e=await e(n,t),u=!0),oBe(a,e,"verify");let p=B6e(t.protected!==void 0?pL(t.protected):new Uint8Array,pL("."),typeof t.payload=="string"?s?pL(t.payload):nA.encode(t.payload):t.payload),f;try{f=Hd(t.signature)}catch{throw new Mn("Failed to base64url decode the signature")}let m=await nBe(e,a);if(!await aBe(a,m,f,p))throw new hL;let _;if(s)try{_=Hd(t.payload)}catch{throw new Mn("Failed to base64url decode the payload")}else typeof t.payload=="string"?_=nA.encode(t.payload):_=t.payload;let v={payload:_};return t.protected!==void 0&&(v.protectedHeader=n),t.header!==void 0&&(v.unprotectedHeader=t.header),u?{...v,key:m}:v}async function uBe(t,e,r){if(t instanceof Uint8Array&&(t=Gl.decode(t)),typeof t!="string")throw new Mn("Compact JWS must be a string or Uint8Array");let{0:n,1:o,2:i,length:s}=t.split(".");if(s!==3)throw new Mn("Invalid Compact JWS");let a=await cBe({payload:o,protected:n,signature:i},e,r),c={payload:a.payload,protectedHeader:a.protectedHeader};return typeof e=="function"?{...c,key:a.key}:c}var u9t=t=>Math.floor(t.getTime()/1e3),dBe=60,fBe=dBe*60,aY=fBe*24,l9t=aY*7,p9t=aY*365.25,d9t=/^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;function lBe(t){let e=d9t.exec(t);if(!e||e[4]&&e[1])throw new TypeError("Invalid time period format");let r=parseFloat(e[2]),n=e[3].toLowerCase(),o;switch(n){case"sec":case"secs":case"second":case"seconds":case"s":o=Math.round(r);break;case"minute":case"minutes":case"min":case"mins":case"m":o=Math.round(r*dBe);break;case"hour":case"hours":case"hr":case"hrs":case"h":o=Math.round(r*fBe);break;case"day":case"days":case"d":o=Math.round(r*aY);break;case"week":case"weeks":case"w":o=Math.round(r*l9t);break;default:o=Math.round(r*p9t);break}return e[1]==="-"||e[4]==="ago"?-o:o}var pBe=t=>t.includes("/")?t.toLowerCase():`application/${t.toLowerCase()}`,f9t=(t,e)=>typeof t=="string"?e.includes(t):Array.isArray(t)?e.some(Set.prototype.has.bind(new Set(t))):!1;function mBe(t,e,r={}){let n;try{n=JSON.parse(Gl.decode(e))}catch{}if(!is(n))throw new AS("JWT Claims Set must be a top-level JSON object");let{typ:o}=r;if(o&&(typeof t.typ!="string"||pBe(t.typ)!==pBe(o)))throw new Cs('unexpected "typ" JWT header value',n,"typ","check_failed");let{requiredClaims:i=[],issuer:s,subject:a,audience:c,maxTokenAge:u}=r,p=[...i];u!==void 0&&p.push("iat"),c!==void 0&&p.push("aud"),a!==void 0&&p.push("sub"),s!==void 0&&p.push("iss");for(let _ of new Set(p.reverse()))if(!(_ in n))throw new Cs(`missing required "${_}" claim`,n,_,"missing");if(s&&!(Array.isArray(s)?s:[s]).includes(n.iss))throw new Cs('unexpected "iss" claim value',n,"iss","check_failed");if(a&&n.sub!==a)throw new Cs('unexpected "sub" claim value',n,"sub","check_failed");if(c&&!f9t(n.aud,typeof c=="string"?[c]:c))throw new Cs('unexpected "aud" claim value',n,"aud","check_failed");let f;switch(typeof r.clockTolerance){case"string":f=lBe(r.clockTolerance);break;case"number":f=r.clockTolerance;break;case"undefined":f=0;break;default:throw new TypeError("Invalid clockTolerance option type")}let{currentDate:m}=r,h=u9t(m||new Date);if((n.iat!==void 0||u)&&typeof n.iat!="number")throw new Cs('"iat" claim must be a number',n,"iat","invalid");if(n.nbf!==void 0){if(typeof n.nbf!="number")throw new Cs('"nbf" claim must be a number',n,"nbf","invalid");if(n.nbf>h+f)throw new Cs('"nbf" claim timestamp check failed',n,"nbf","check_failed")}if(n.exp!==void 0){if(typeof n.exp!="number")throw new Cs('"exp" claim must be a number',n,"exp","invalid");if(n.exp<=h-f)throw new oA('"exp" claim timestamp check failed',n,"exp","check_failed")}if(u){let _=h-n.iat,v=typeof u=="number"?u:lBe(u);if(_-f>v)throw new oA('"iat" claim timestamp check failed (too far in the past)',n,"iat","check_failed");if(_<0-f)throw new Cs('"iat" claim timestamp check failed (it should be in the past)',n,"iat","check_failed")}return n}async function cY(t,e,r){let n=await uBe(t,e,r);if(n.protectedHeader.crit?.includes("b64")&&n.protectedHeader.b64===!1)throw new AS("JWTs MUST NOT use unencoded payload");let i={payload:mBe(n.protectedHeader,n.payload,r),protectedHeader:n.protectedHeader};return typeof e=="function"?{...i,key:n.key}:i}function m9t(t){switch(typeof t=="string"&&t.slice(0,2)){case"RS":case"PS":return"RSA";case"ES":return"EC";case"Ed":return"OKP";case"ML":return"AKP";default:throw new jo('Unsupported "alg" value for a JSON Web Key Set')}}function h9t(t){return t&&typeof t=="object"&&Array.isArray(t.keys)&&t.keys.every(g9t)}function g9t(t){return is(t)}var uY=class{#t;#e=new WeakMap;constructor(e){if(!h9t(e))throw new iA("JSON Web Key Set malformed");this.#t=structuredClone(e)}jwks(){return this.#t}async getKey(e,r){let{alg:n,kid:o}={...e,...r?.header},i=m9t(n),s=this.#t.keys.filter(u=>{let p=i===u.kty;if(p&&typeof o=="string"&&(p=o===u.kid),p&&(typeof u.alg=="string"||i==="AKP")&&(p=n===u.alg),p&&typeof u.use=="string"&&(p=u.use==="sig"),p&&Array.isArray(u.key_ops)&&(p=u.key_ops.includes("verify")),p)switch(n){case"ES256":p=u.crv==="P-256";break;case"ES384":p=u.crv==="P-384";break;case"ES512":p=u.crv==="P-521";break;case"Ed25519":case"EdDSA":p=u.crv==="Ed25519";break}return p}),{0:a,length:c}=s;if(c===0)throw new wS;if(c!==1){let u=new fL,p=this.#e;throw u[Symbol.asyncIterator]=async function*(){for(let f of s)try{yield await hBe(p,f,n)}catch{}},u}return hBe(this.#e,a,n)}};async function hBe(t,e,r){let n=t.get(e)||t.set(e,{}).get(e);if(n[r]===void 0){let o=await Y6e({...e,ext:!0},r);if(o instanceof Uint8Array||o.type!=="public")throw new iA("JSON Web Key Set members must be public keys");n[r]=o}return n[r]}function lY(t){let e=new uY(t),r=async(n,o)=>e.getKey(n,o);return Object.defineProperties(r,{jwks:{value:()=>structuredClone(e.jwks()),enumerable:!1,configurable:!1,writable:!1}}),r}function _9t(){return typeof WebSocketPair<"u"||typeof navigator<"u"&&navigator.userAgent==="Cloudflare-Workers"||typeof EdgeRuntime<"u"&&EdgeRuntime==="vercel"}var pY;(typeof navigator>"u"||!navigator.userAgent?.startsWith?.("Mozilla/5.0 "))&&(pY="jose/v6.1.3");var gBe=Symbol();async function v9t(t,e,r,n=fetch){let o=await n(t,{method:"GET",signal:r,redirect:"manual",headers:e}).catch(i=>{throw i.name==="TimeoutError"?new mL:i});if(o.status!==200)throw new ci("Expected 200 OK from the JSON Web Key Set HTTP response");try{return await o.json()}catch{throw new ci("Failed to parse the JSON Web Key Set HTTP response as JSON")}}var gL=Symbol();function S9t(t,e){return!(typeof t!="object"||t===null||!("uat"in t)||typeof t.uat!="number"||Date.now()-t.uat>=e||!("jwks"in t)||!is(t.jwks)||!Array.isArray(t.jwks.keys)||!Array.prototype.every.call(t.jwks.keys,is))}var dY=class{#t;#e;#c;#r;#n;#i;#s;#u;#o;#a;constructor(e,r){if(!(e instanceof URL))throw new TypeError("url must be an instance of URL");this.#t=new URL(e.href),this.#e=typeof r?.timeoutDuration=="number"?r?.timeoutDuration:5e3,this.#c=typeof r?.cooldownDuration=="number"?r?.cooldownDuration:3e4,this.#r=typeof r?.cacheMaxAge=="number"?r?.cacheMaxAge:6e5,this.#s=new Headers(r?.headers),pY&&!this.#s.has("User-Agent")&&this.#s.set("User-Agent",pY),this.#s.has("accept")||(this.#s.set("accept","application/json"),this.#s.append("accept","application/jwk-set+json")),this.#u=r?.[gBe],r?.[gL]!==void 0&&(this.#a=r?.[gL],S9t(r?.[gL],this.#r)&&(this.#n=this.#a.uat,this.#o=lY(this.#a.jwks)))}pendingFetch(){return!!this.#i}coolingDown(){return typeof this.#n=="number"?Date.now(){this.#o=lY(e),this.#a&&(this.#a.uat=Date.now(),this.#a.jwks=e),this.#n=Date.now(),this.#i=void 0}).catch(e=>{throw this.#i=void 0,e}),await this.#i}};function fY(t,e){let r=new dY(t,e),n=async(o,i)=>r.getKey(o,i);return Object.defineProperties(n,{coolingDown:{get:()=>r.coolingDown(),enumerable:!0,configurable:!1},fresh:{get:()=>r.fresh(),enumerable:!0,configurable:!1},reload:{value:()=>r.reload(),enumerable:!0,configurable:!1,writable:!1},reloading:{get:()=>r.pendingFetch(),enumerable:!0,configurable:!1},jwks:{value:()=>r.jwks(),enumerable:!0,configurable:!1,writable:!1}}),n}function y9t(t){let e=t??"/";return e.endsWith("/")?e:`${e}/`}function E9t(t){let e=y9t(t.basePath),r=t.issuer??"https://server.smithery.ai",n=new URL(t.jwksUrl??"https://server.smithery.ai/.well-known/jwks.json"),o=typeof t.tokenPath=="string"&&t.tokenPath.length>0?t.tokenPath:`${e}token`,i=fY(n),s=mY.default.Router();return s.use(mY.default.urlencoded({extended:!1})),s.post(o,async(a,c,u)=>{try{if((typeof a.body?.grant_type=="string"?a.body.grant_type:void 0)!=="urn:ietf:params:oauth:grant-type:jwt-bearer")return u();let f=typeof a.body?.assertion=="string"?a.body.assertion:void 0;if(!f){c.status(400).json({error:"invalid_request",error_description:"Missing assertion"});return}let h=`https://${a.get("host")??"localhost"}${o}`,{payload:_}=await cY(f,i,{issuer:r,audience:h,algorithms:["RS256"]}),v=await t.handleJwtGrant(_,a);if(!v)return u();c.json(v)}catch(p){console.error(p),c.status(400).json({error:"invalid_grant"})}}),s}function _Be(t,e){t.use(E9t(e))}var hY=W(Pc(),1);var EBe=require("node:buffer"),TBe=require("node:crypto"),bBe=require("node:net"),vBe=["draft-6","draft-7","draft-8"],vL=(t,e)=>{let r;if(t){let n=Math.ceil((t.getTime()-Date.now())/1e3);r=Math.max(0,n)}else e&&(r=Math.ceil(e/1e3));return r},T9t=t=>{let e=(0,TBe.createHash)("sha256");e.update(t);let r=e.digest("hex").slice(0,12);return EBe.Buffer.from(r).toString("base64")},b9t=(t,e)=>{t.headersSent||(t.setHeader("X-RateLimit-Limit",e.limit.toString()),t.setHeader("X-RateLimit-Remaining",e.remaining.toString()),e.resetTime instanceof Date&&(t.setHeader("Date",new Date().toUTCString()),t.setHeader("X-RateLimit-Reset",Math.ceil(e.resetTime.getTime()/1e3).toString())))},x9t=(t,e,r)=>{if(t.headersSent)return;let n=Math.ceil(r/1e3),o=vL(e.resetTime);t.setHeader("RateLimit-Policy",`${e.limit};w=${n}`),t.setHeader("RateLimit-Limit",e.limit.toString()),t.setHeader("RateLimit-Remaining",e.remaining.toString()),o&&t.setHeader("RateLimit-Reset",o.toString())},A9t=(t,e,r)=>{if(t.headersSent)return;let n=Math.ceil(r/1e3),o=vL(e.resetTime,r);t.setHeader("RateLimit-Policy",`${e.limit};w=${n}`),t.setHeader("RateLimit",`limit=${e.limit}, remaining=${e.remaining}, reset=${o}`)},w9t=(t,e,r,n,o)=>{if(t.headersSent)return;let i=Math.ceil(r/1e3),s=vL(e.resetTime,r),a=T9t(o),c=`q=${e.limit}; w=${i}; pk=:${a}:`,u=`r=${e.remaining}; t=${s}`;t.append("RateLimit-Policy",`"${n}"; ${c}`),t.append("RateLimit",`"${n}"; ${u}`)},R9t=(t,e,r)=>{if(t.headersSent)return;let n=vL(e.resetTime,r);t.setHeader("Retry-After",n.toString())},$s=class extends Error{constructor(t,e){let r=`https://express-rate-limit.github.io/${t}/`;super(`${e} See ${r} for more information.`),this.name=this.constructor.name,this.code=t,this.help=r}},_L=class extends $s{},SBe=new Set,yBe=new WeakMap,P9t={enabled:{default:!0},disable(){for(let t of Object.keys(this.enabled))this.enabled[t]=!1},ip(t){if(t===void 0)throw new $s("ERR_ERL_UNDEFINED_IP_ADDRESS","An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.");if(!(0,bBe.isIP)(t))throw new $s("ERR_ERL_INVALID_IP_ADDRESS",`An invalid 'request.ip' (${t}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`)},trustProxy(t){if(t.app.get("trust proxy")===!0)throw new $s("ERR_ERL_PERMISSIVE_TRUST_PROXY","The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.")},xForwardedForHeader(t){if(t.headers["x-forwarded-for"]&&t.app.get("trust proxy")===!1)throw new $s("ERR_ERL_UNEXPECTED_X_FORWARDED_FOR","The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.")},positiveHits(t){if(typeof t!="number"||t<1||t!==Math.round(t))throw new $s("ERR_ERL_INVALID_HITS",`The totalHits value returned from the store must be a positive integer, got ${t}`)},unsharedStore(t){if(SBe.has(t)){let e=t?.localKeys?"":" (with a unique prefix)";throw new $s("ERR_ERL_STORE_REUSE",`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${t.constructor.name}${e} for each limiter instead.`)}SBe.add(t)},singleCount(t,e,r){let n=yBe.get(t);n||(n=new Map,yBe.set(t,n));let o=e.localKeys?e:e.constructor.name,i=n.get(o);i||(i=[],n.set(o,i));let s=`${e.prefix??""}${r}`;if(i.includes(s))throw new $s("ERR_ERL_DOUBLE_COUNT",`The hit count for ${r} was incremented more than once for a single request.`);i.push(s)},limit(t){if(t===0)throw new _L("WRN_ERL_MAX_ZERO","Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7")},draftPolliHeaders(t){if(t)throw new _L("WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS","The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.")},onLimitReached(t){if(t)throw new _L("WRN_ERL_DEPRECATED_ON_LIMIT_REACHED","The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.")},headersDraftVersion(t){if(typeof t!="string"||!vBe.includes(t)){let e=vBe.join(", ");throw new $s("ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",`standardHeaders: only the following versions of the IETF draft specification are supported: ${e}.`)}},headersResetTime(t){if(!t)throw new $s("ERR_ERL_HEADERS_NO_RESET","standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.")},validationsConfig(){let t=Object.keys(this).filter(e=>!["enabled","disable"].includes(e));t.push("default");for(let e of Object.keys(this.enabled))if(!t.includes(e))throw new $s("ERR_ERL_UNKNOWN_VALIDATION",`options.validate.${e} is not recognized. Supported validate options are: ${t.join(", ")}.`)},creationStack(t){let{stack:e}=new Error("express-rate-limit validation check (set options.validate.creationStack=false to disable)");if(e?.includes("Layer.handle [as handle_request]"))throw t.localKeys?new $s("ERR_ERL_CREATED_IN_REQUEST_HANDLER","express-rate-limit instance should be created at app initialization, not when responding to a request."):new $s("ERR_ERL_CREATED_IN_REQUEST_HANDLER","express-rate-limit instance should *usually* be created at app initialization, not when responding to a request.")}},I9t=t=>{let e;typeof t=="boolean"?e={default:t}:e={default:!0,...t};let r={enabled:e};for(let[n,o]of Object.entries(P9t))typeof o=="function"&&(r[n]=(...i)=>{if(e[n]??e.default)try{o.apply(r,i)}catch(s){s instanceof _L?console.warn(s):console.error(s)}});return r},O9t=class{constructor(){this.previous=new Map,this.current=new Map,this.localKeys=!0}init(t){this.windowMs=t.windowMs,this.interval&&clearInterval(this.interval),this.interval=setInterval(()=>{this.clearExpired()},this.windowMs),this.interval.unref&&this.interval.unref()}async get(t){return this.current.get(t)??this.previous.get(t)}async increment(t){let e=this.getClient(t),r=Date.now();return e.resetTime.getTime()<=r&&this.resetClient(e,r),e.totalHits++,e}async decrement(t){let e=this.getClient(t);e.totalHits>0&&e.totalHits--}async resetKey(t){this.current.delete(t),this.previous.delete(t)}async resetAll(){this.current.clear(),this.previous.clear()}shutdown(){clearInterval(this.interval),this.resetAll()}resetClient(t,e=Date.now()){return t.totalHits=0,t.resetTime.setTime(e+this.windowMs),t}getClient(t){if(this.current.has(t))return this.current.get(t);let e;return this.previous.has(t)?(e=this.previous.get(t),this.previous.delete(t)):(e={totalHits:0,resetTime:new Date},this.resetClient(e)),this.current.set(t,e),e}clearExpired(){this.previous=this.current,this.current=new Map}},N9t=t=>typeof t.incr=="function"&&typeof t.increment!="function",C9t=t=>{if(!N9t(t))return t;let e=t;class r{async increment(o){return new Promise((i,s)=>{e.incr(o,(a,c,u)=>{a&&s(a),i({totalHits:c,resetTime:u})})})}async decrement(o){return e.decrement(o)}async resetKey(o){return e.resetKey(o)}async resetAll(){if(typeof e.resetAll=="function")return e.resetAll()}}return new r},$9t=t=>{let{validations:e,...r}=t;return{...r,validate:e.enabled}},k9t=t=>{let e={};for(let r of Object.keys(t)){let n=r;t[n]!==void 0&&(e[n]=t[n])}return e},M9t=t=>{let e=k9t(t),r=I9t(e?.validate??!0);r.validationsConfig(),r.draftPolliHeaders(e.draft_polli_ratelimit_headers),r.onLimitReached(e.onLimitReached);let n=e.standardHeaders??!1;n===!0&&(n="draft-6");let o={windowMs:60*1e3,limit:t.max??5,message:"Too many requests, please try again later.",statusCode:429,legacyHeaders:t.headers??!0,identifier(i,s){let a="",c=o.requestPropertyName,{limit:u}=i[c],p=o.windowMs/1e3,f=o.windowMs/(1e3*60),m=o.windowMs/(1e3*60*60),h=o.windowMs/(1e3*60*60*24);return p<60?a=`${p}sec`:f<60?a=`${f}min`:m<24?a=`${m}hr${m>1?"s":""}`:a=`${h}day${h>1?"s":""}`,`${u}-in-${a}`},requestPropertyName:"rateLimit",skipFailedRequests:!1,skipSuccessfulRequests:!1,requestWasSuccessful:(i,s)=>s.statusCode<400,skip:(i,s)=>!1,keyGenerator(i,s){return r.ip(i.ip),r.trustProxy(i),r.xForwardedForHeader(i),i.ip},async handler(i,s,a,c){s.status(o.statusCode);let u=typeof o.message=="function"?await o.message(i,s):o.message;s.writableEnded||s.send(u)},passOnStoreError:!1,...e,standardHeaders:n,store:C9t(e.store??new O9t),validations:r};if(typeof o.store.increment!="function"||typeof o.store.decrement!="function"||typeof o.store.resetKey!="function"||o.store.resetAll!==void 0&&typeof o.store.resetAll!="function"||o.store.init!==void 0&&typeof o.store.init!="function")throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");return o},D9t=t=>async(e,r,n)=>{try{await Promise.resolve(t(e,r,n)).catch(n)}catch(o){n(o)}},L9t=t=>{let e=M9t(t??{}),r=$9t(e);e.validations.creationStack(e.store),e.validations.unsharedStore(e.store),typeof e.store.init=="function"&&e.store.init(r);let n=D9t(async(i,s,a)=>{if(await e.skip(i,s)){a();return}let u=i,p=await e.keyGenerator(i,s),f=0,m;try{let E=await e.store.increment(p);f=E.totalHits,m=E.resetTime}catch(E){if(e.passOnStoreError){console.error("express-rate-limit: error from store, allowing request without rate-limiting.",E),a();return}throw E}e.validations.positiveHits(f),e.validations.singleCount(i,e.store,p);let _=await(typeof e.limit=="function"?e.limit(i,s):e.limit);e.validations.limit(_);let v={limit:_,used:f,remaining:Math.max(_-f,0),resetTime:m};if(Object.defineProperty(v,"current",{configurable:!1,enumerable:!1,value:f}),u[e.requestPropertyName]=v,e.legacyHeaders&&!s.headersSent&&b9t(s,v),e.standardHeaders&&!s.headersSent)switch(e.standardHeaders){case"draft-6":{x9t(s,v,e.windowMs);break}case"draft-7":{e.validations.headersResetTime(v.resetTime),A9t(s,v,e.windowMs);break}case"draft-8":{let x=await(typeof e.identifier=="function"?e.identifier(i,s):e.identifier);e.validations.headersResetTime(v.resetTime),w9t(s,v,e.windowMs,x,p);break}default:{e.validations.headersDraftVersion(e.standardHeaders);break}}if(e.skipFailedRequests||e.skipSuccessfulRequests){let E=!1,x=async()=>{E||(await e.store.decrement(p),E=!0)};e.skipFailedRequests&&(s.on("finish",async()=>{await e.requestWasSuccessful(i,s)||await x()}),s.on("close",async()=>{s.writableEnded||await x()}),s.on("error",async()=>{await x()})),e.skipSuccessfulRequests&&s.on("finish",async()=>{await e.requestWasSuccessful(i,s)&&await x()})}if(e.validations.disable(),f>_){(e.legacyHeaders||e.standardHeaders)&&R9t(s,v,e.windowMs),e.handler(i,s,a,r);return}a()}),o=()=>{throw new Error("The current store does not support the get/getKey method")};return n.resetKey=e.store.resetKey.bind(e.store),n.getKey=typeof e.store.get=="function"?e.store.get.bind(e.store):o,n},Wd=L9t;var fr=class extends Error{constructor(e,r){super(e),this.errorUri=r,this.name=this.constructor.name}toResponseObject(){let e={error:this.errorCode,error_description:this.message};return this.errorUri&&(e.error_uri=this.errorUri),e}get errorCode(){return this.constructor.errorCode}},yo=class extends fr{};yo.errorCode="invalid_request";var La=class extends fr{};La.errorCode="invalid_client";var Zm=class extends fr{};Zm.errorCode="invalid_grant";var aA=class extends fr{};aA.errorCode="unauthorized_client";var Ym=class extends fr{};Ym.errorCode="unsupported_grant_type";var cA=class extends fr{};cA.errorCode="invalid_scope";var uA=class extends fr{};uA.errorCode="access_denied";var Vr=class extends fr{};Vr.errorCode="server_error";var lA=class extends fr{};lA.errorCode="temporarily_unavailable";var pA=class extends fr{};pA.errorCode="unsupported_response_type";var dA=class extends fr{};dA.errorCode="unsupported_token_type";var Oc=class extends fr{};Oc.errorCode="invalid_token";var Jm=class extends fr{};Jm.errorCode="method_not_allowed";var ks=class extends fr{};ks.errorCode="too_many_requests";var Xm=class extends fr{};Xm.errorCode="invalid_client_metadata";var Kd=class extends fr{};Kd.errorCode="insufficient_scope";var fA=class extends fr{};fA.errorCode="invalid_target";var AUr={[yo.errorCode]:yo,[La.errorCode]:La,[Zm.errorCode]:Zm,[aA.errorCode]:aA,[Ym.errorCode]:Ym,[cA.errorCode]:cA,[uA.errorCode]:uA,[Vr.errorCode]:Vr,[lA.errorCode]:lA,[pA.errorCode]:pA,[dA.errorCode]:dA,[Oc.errorCode]:Oc,[Jm.errorCode]:Jm,[ks.errorCode]:ks,[Xm.errorCode]:Xm,[Kd.errorCode]:Kd,[fA.errorCode]:fA};function $u(t){return(e,r,n)=>{if(t.includes(e.method)){n();return}let o=new Jm(`The method ${e.method} is not allowed for this endpoint`);r.status(405).set("Allow",t.join(", ")).json(o.toResponseObject())}}var U9t=k({client_id:T(),redirect_uri:T().optional().refine(t=>t===void 0||URL.canParse(t),{message:"redirect_uri must be a valid URL"})}),j9t=k({response_type:Ie("code"),code_challenge:T(),code_challenge_method:Ie("S256"),scope:T().optional(),state:T().optional(),resource:T().url().optional()});function gY({provider:t,rateLimit:e}){let r=hY.default.Router();return r.use($u(["GET","POST"])),r.use(hY.default.urlencoded({extended:!1})),e!==!1&&r.use(Wd({windowMs:900*1e3,max:100,standardHeaders:!0,legacyHeaders:!1,message:new ks("You have exceeded the rate limit for authorization requests").toResponseObject(),...e})),r.all("/",async(n,o)=>{o.setHeader("Cache-Control","no-store");let i,s,a;try{let u=U9t.safeParse(n.method==="POST"?n.body:n.query);if(!u.success)throw new yo(u.error.message);if(i=u.data.client_id,s=u.data.redirect_uri,a=await t.clientsStore.getClient(i),!a)throw new La("Invalid client_id");if(s!==void 0){if(!a.redirect_uris.includes(s))throw new yo("Unregistered redirect_uri")}else if(a.redirect_uris.length===1)s=a.redirect_uris[0];else throw new yo("redirect_uri must be specified when client has multiple registered URIs")}catch(u){if(u instanceof fr){let p=u instanceof Vr?500:400;o.status(p).json(u.toResponseObject())}else{let p=new Vr("Internal Server Error");o.status(500).json(p.toResponseObject())}return}let c;try{let u=j9t.safeParse(n.method==="POST"?n.body:n.query);if(!u.success)throw new yo(u.error.message);let{scope:p,code_challenge:f,resource:m}=u.data;c=u.data.state;let h=[];p!==void 0&&(h=p.split(" ")),await t.authorize(a,{state:c,scopes:h,redirectUri:s,codeChallenge:f,resource:m?new URL(m):void 0},o)}catch(u){if(u instanceof fr)o.redirect(302,xBe(s,u,c));else{let p=new Vr("Internal Server Error");o.redirect(302,xBe(s,p,c))}}}),r}function xBe(t,e,r){let n=new URL(t);return n.searchParams.set("error",e.errorCode),n.searchParams.set("error_description",e.message),e.errorUri&&n.searchParams.set("error_uri",e.errorUri),r&&n.searchParams.set("state",r),n.href}var IBe=W(Pc(),1),OBe=W(NS(),1);function CS(t){let e=IBe.default.Router();return e.use((0,OBe.default)()),e.use($u(["GET","OPTIONS"])),e.get("/",(r,n)=>{n.status(200).json(t)}),e}var vY=W(Pc(),1);var zo=zP().superRefine((t,e)=>{if(!URL.canParse(t))return e.addIssue({code:gp.custom,message:"URL must be parseable",fatal:!0}),mh}).refine(t=>{let e=new URL(t);return e.protocol!=="javascript:"&&e.protocol!=="data:"&&e.protocol!=="vbscript:"},{message:"URL cannot use javascript:, data:, or vbscript: scheme"}),LUr=wn({resource:T().url(),authorization_servers:H(zo).optional(),jwks_uri:T().url().optional(),scopes_supported:H(T()).optional(),bearer_methods_supported:H(T()).optional(),resource_signing_alg_values_supported:H(T()).optional(),resource_name:T().optional(),resource_documentation:T().optional(),resource_policy_uri:T().url().optional(),resource_tos_uri:T().url().optional(),tls_client_certificate_bound_access_tokens:ue().optional(),authorization_details_types_supported:H(T()).optional(),dpop_signing_alg_values_supported:H(T()).optional(),dpop_bound_access_tokens_required:ue().optional()}),G9t=wn({issuer:T(),authorization_endpoint:zo,token_endpoint:zo,registration_endpoint:zo.optional(),scopes_supported:H(T()).optional(),response_types_supported:H(T()),response_modes_supported:H(T()).optional(),grant_types_supported:H(T()).optional(),token_endpoint_auth_methods_supported:H(T()).optional(),token_endpoint_auth_signing_alg_values_supported:H(T()).optional(),service_documentation:zo.optional(),revocation_endpoint:zo.optional(),revocation_endpoint_auth_methods_supported:H(T()).optional(),revocation_endpoint_auth_signing_alg_values_supported:H(T()).optional(),introspection_endpoint:T().optional(),introspection_endpoint_auth_methods_supported:H(T()).optional(),introspection_endpoint_auth_signing_alg_values_supported:H(T()).optional(),code_challenge_methods_supported:H(T()).optional(),client_id_metadata_document_supported:ue().optional()}),V9t=wn({issuer:T(),authorization_endpoint:zo,token_endpoint:zo,userinfo_endpoint:zo.optional(),jwks_uri:zo,registration_endpoint:zo.optional(),scopes_supported:H(T()).optional(),response_types_supported:H(T()),response_modes_supported:H(T()).optional(),grant_types_supported:H(T()).optional(),acr_values_supported:H(T()).optional(),subject_types_supported:H(T()),id_token_signing_alg_values_supported:H(T()),id_token_encryption_alg_values_supported:H(T()).optional(),id_token_encryption_enc_values_supported:H(T()).optional(),userinfo_signing_alg_values_supported:H(T()).optional(),userinfo_encryption_alg_values_supported:H(T()).optional(),userinfo_encryption_enc_values_supported:H(T()).optional(),request_object_signing_alg_values_supported:H(T()).optional(),request_object_encryption_alg_values_supported:H(T()).optional(),request_object_encryption_enc_values_supported:H(T()).optional(),token_endpoint_auth_methods_supported:H(T()).optional(),token_endpoint_auth_signing_alg_values_supported:H(T()).optional(),display_values_supported:H(T()).optional(),claim_types_supported:H(T()).optional(),claims_supported:H(T()).optional(),service_documentation:T().optional(),claims_locales_supported:H(T()).optional(),ui_locales_supported:H(T()).optional(),claims_parameter_supported:ue().optional(),request_parameter_supported:ue().optional(),request_uri_parameter_supported:ue().optional(),require_request_uri_registration:ue().optional(),op_policy_uri:zo.optional(),op_tos_uri:zo.optional(),client_id_metadata_document_supported:ue().optional()}),UUr=k({...V9t.shape,...G9t.pick({code_challenge_methods_supported:!0}).shape}),jUr=k({access_token:T(),id_token:T().optional(),token_type:T(),expires_in:uE.number().optional(),scope:T().optional(),refresh_token:T().optional()}).strip(),zUr=k({error:T(),error_description:T().optional(),error_uri:T().optional()}),NBe=zo.optional().or(Ie("").transform(()=>{})),_Y=k({redirect_uris:H(zo),token_endpoint_auth_method:T().optional(),grant_types:H(T()).optional(),response_types:H(T()).optional(),client_name:T().optional(),client_uri:zo.optional(),logo_uri:NBe,scope:T().optional(),contacts:H(T()).optional(),tos_uri:NBe,policy_uri:T().optional(),jwks_uri:zo.optional(),jwks:nI().optional(),software_id:T().optional(),software_version:T().optional(),software_statement:T().optional()}).strip(),H9t=k({client_id:T(),client_secret:T().optional(),client_id_issued_at:ce().optional(),client_secret_expires_at:ce().optional()}).strip(),FUr=_Y.merge(H9t),qUr=k({error:T(),error_description:T().optional()}).strip(),CBe=k({token:T(),token_type_hint:T().optional()}).strip();var SY=W(require("node:crypto"),1),$Be=W(NS(),1);var W9t=720*60*60;function yY({clientsStore:t,clientSecretExpirySeconds:e=W9t,rateLimit:r,clientIdGeneration:n=!0}){if(!t.registerClient)throw new Error("Client registration store does not support registering clients");let o=vY.default.Router();return o.use((0,$Be.default)()),o.use($u(["POST"])),o.use(vY.default.json()),r!==!1&&o.use(Wd({windowMs:3600*1e3,max:20,standardHeaders:!0,legacyHeaders:!1,message:new ks("You have exceeded the rate limit for client registration requests").toResponseObject(),...r})),o.post("/",async(i,s)=>{s.setHeader("Cache-Control","no-store");try{let a=_Y.safeParse(i.body);if(!a.success)throw new Xm(a.error.message);let c=a.data,u=c.token_endpoint_auth_method==="none",p=u?void 0:SY.default.randomBytes(32).toString("hex"),f=Math.floor(Date.now()/1e3),h=e>0?f+e:0,v={...c,client_secret:p,client_secret_expires_at:u?void 0:h};n&&(v.client_id=SY.default.randomUUID(),v.client_id_issued_at=f),v=await t.registerClient(v),s.status(201).json(v)}catch(a){if(a instanceof fr){let c=a instanceof Vr?500:400;s.status(c).json(a.toResponseObject())}else{let c=new Vr("Internal Server Error");s.status(500).json(c.toResponseObject())}}}),o}var EY=W(Pc(),1),kBe=W(NS(),1);var K9t=k({client_id:T(),client_secret:T().optional()});function SL({clientsStore:t}){return async(e,r,n)=>{try{let o=K9t.safeParse(e.body);if(!o.success)throw new yo(String(o.error));let{client_id:i,client_secret:s}=o.data,a=await t.getClient(i);if(!a)throw new La("Invalid client_id");if(a.client_secret){if(!s)throw new La("Client secret is required");if(a.client_secret!==s)throw new La("Invalid client_secret");if(a.client_secret_expires_at&&a.client_secret_expires_at{o.setHeader("Cache-Control","no-store");try{let i=CBe.safeParse(n.body);if(!i.success)throw new yo(i.error.message);let s=n.client;if(!s)throw new Vr("Internal Server Error");await t.revokeToken(s,i.data),o.status(200).json({})}catch(i){if(i instanceof fr){let s=i instanceof Vr?500:400;o.status(s).json(i.toResponseObject())}else{let s=new Vr("Internal Server Error");o.status(500).json(s.toResponseObject())}}}),r}var bY=W(Pc(),1),LBe=W(NS(),1);var MBe;MBe=globalThis.crypto?.webcrypto??globalThis.crypto??import("node:crypto").then(t=>t.webcrypto);async function Z9t(t){let e=await(await MBe).subtle.digest("SHA-256",new TextEncoder().encode(t));return btoa(String.fromCharCode(...new Uint8Array(e))).replace(/\//g,"_").replace(/\+/g,"-").replace(/=/g,"")}async function DBe(t,e){return await Z9t(t)===e}var Y9t=k({grant_type:T()}),J9t=k({code:T(),code_verifier:T(),redirect_uri:T().optional(),resource:T().url().optional()}),X9t=k({refresh_token:T(),scope:T().optional(),resource:T().url().optional()});function xY({provider:t,rateLimit:e}){let r=bY.default.Router();return r.use((0,LBe.default)()),r.use($u(["POST"])),r.use(bY.default.urlencoded({extended:!1})),e!==!1&&r.use(Wd({windowMs:900*1e3,max:50,standardHeaders:!0,legacyHeaders:!1,message:new ks("You have exceeded the rate limit for token requests").toResponseObject(),...e})),r.use(SL({clientsStore:t.clientsStore})),r.post("/",async(n,o)=>{o.setHeader("Cache-Control","no-store");try{let i=Y9t.safeParse(n.body);if(!i.success)throw new yo(i.error.message);let{grant_type:s}=i.data,a=n.client;if(!a)throw new Vr("Internal Server Error");switch(s){case"authorization_code":{let c=J9t.safeParse(n.body);if(!c.success)throw new yo(c.error.message);let{code:u,code_verifier:p,redirect_uri:f,resource:m}=c.data,h=t.skipLocalPkceValidation;if(!h){let v=await t.challengeForAuthorizationCode(a,u);if(!await DBe(p,v))throw new Zm("code_verifier does not match the challenge")}let _=await t.exchangeAuthorizationCode(a,u,h?p:void 0,f,m?new URL(m):void 0);o.status(200).json(_);break}case"refresh_token":{let c=X9t.safeParse(n.body);if(!c.success)throw new yo(c.error.message);let{refresh_token:u,scope:p,resource:f}=c.data,m=p?.split(" "),h=await t.exchangeRefreshToken(a,u,m,f?new URL(f):void 0);o.status(200).json(h);break}case"client_credentials":default:throw new Ym("The grant type is not supported by this authorization server.")}}catch(i){if(i instanceof fr){let s=i instanceof Vr?500:400;o.status(s).json(i.toResponseObject())}else{let s=new Vr("Internal Server Error");o.status(500).json(s.toResponseObject())}}}),r}function UBe({verifier:t,requiredScopes:e=[],resourceMetadataUrl:r}){return async(n,o,i)=>{try{let s=n.headers.authorization;if(!s)throw new Oc("Missing Authorization header");let[a,c]=s.split(" ");if(a.toLowerCase()!=="bearer"||!c)throw new Oc("Invalid Authorization header format, expected 'Bearer TOKEN'");let u=await t.verifyAccessToken(c);if(e.length>0&&!e.every(f=>u.scopes.includes(f)))throw new Kd("Insufficient scope");if(typeof u.expiresAt!="number"||isNaN(u.expiresAt))throw new Oc("Token has no expiration time");if(u.expiresAt{let p=`Bearer error="${c}", error_description="${u}"`;return e.length>0&&(p+=`, scope="${e.join(" ")}"`),r&&(p+=`, resource_metadata="${r}"`),p};if(s instanceof Oc)o.set("WWW-Authenticate",a(s.errorCode,s.message)),o.status(401).json(s.toResponseObject());else if(s instanceof Kd)o.set("WWW-Authenticate",a(s.errorCode,s.message)),o.status(403).json(s.toResponseObject());else if(s instanceof Vr)o.status(500).json(s.toResponseObject());else if(s instanceof fr)o.status(400).json(s.toResponseObject());else{let c=new Vr("Internal Server Error");o.status(500).json(c.toResponseObject())}}}}var jBe=W(Pc(),1);var zBe=process.env.MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL==="true"||process.env.MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL==="1";zBe&&console.warn("MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL is enabled - HTTP issuer URLs are allowed. Do not use in production.");var FBe=t=>{if(t.protocol!=="https:"&&t.hostname!=="localhost"&&t.hostname!=="127.0.0.1"&&!zBe)throw new Error("Issuer URL must be HTTPS");if(t.hash)throw new Error(`Issuer URL must not have a fragment: ${t}`);if(t.search)throw new Error(`Issuer URL must not have a query string: ${t}`)},AY=t=>{let e=t.issuerUrl,r=t.baseUrl;FBe(e);let n="/authorize",o="/token",i=t.provider.clientsStore.registerClient?"/register":void 0,s=t.provider.revokeToken?"/revoke":void 0;return{issuer:e.href,service_documentation:t.serviceDocumentationUrl?.href,authorization_endpoint:new URL(n,r||e).href,response_types_supported:["code"],code_challenge_methods_supported:["S256"],token_endpoint:new URL(o,r||e).href,token_endpoint_auth_methods_supported:["client_secret_post","none"],grant_types_supported:["authorization_code","refresh_token"],scopes_supported:t.scopesSupported,revocation_endpoint:s?new URL(s,r||e).href:void 0,revocation_endpoint_auth_methods_supported:s?["client_secret_post"]:void 0,registration_endpoint:i?new URL(i,r||e).href:void 0}};function qBe(t){FBe(new URL(t.oauthMetadata.issuer));let e=jBe.default.Router(),r={resource:t.resourceServerUrl.href,authorization_servers:[t.oauthMetadata.issuer],scopes_supported:t.scopesSupported,resource_name:t.resourceName,resource_documentation:t.serviceDocumentationUrl?.href},n=new URL(t.resourceServerUrl.href).pathname;return e.use(`/.well-known/oauth-protected-resource${n==="/"?"":n}`,CS(r)),e.use("/.well-known/oauth-authorization-server",CS(t.oauthMetadata)),e}function yL(t){return!!t&&"authorize"in t}function BBe(t,e){let r=e.provider,o=yL(r)?r.basePath??"/":e.identity?.basePath??"/",i=o.endsWith("/")?o:`${o}/`,s,a,c,u;if(yL(r)){let p=new URL("https://localhost"),f=new URL(i,p),m=AY({provider:r,issuerUrl:p,baseUrl:f});s=new URL(m.authorization_endpoint).pathname,a=new URL(m.token_endpoint).pathname,m.registration_endpoint&&(c=new URL(m.registration_endpoint).pathname),m.revocation_endpoint&&(u=new URL(m.revocation_endpoint).pathname)}if(yL(r)?t.use((p,f,m)=>{if(!p.path.startsWith("/.well-known/"))return m();let h=p.get("host")??"localhost";p.protocol!=="https"&&console.warn("Detected http but using https for issuer URL in OAuth metadata since it will fail otherwise.");let _=new URL(`https://${h}`),v=new URL(i,_),E=AY({provider:r,issuerUrl:_,baseUrl:v});e.identity&&(E.grant_types_supported=Array.from(new Set([...E.grant_types_supported??[],"urn:ietf:params:oauth:grant-type:jwt-bearer"])));let x=new URL("/mcp",_);return qBe({oauthMetadata:E,resourceServerUrl:x})(p,f,m)}):e.identity&&(t.use("/.well-known/oauth-protected-resource",(p,f,m)=>{let h=p.get("host")??"localhost",_=new URL(`https://${h}`),v={resource:new URL("/mcp",_).href,authorization_servers:[_.href]};return CS(v)(p,f,m)}),t.use("/.well-known/oauth-authorization-server",(p,f,m)=>{let h=p.get("host")??"localhost",_=new URL(`https://${h}`),v={issuer:_.href,token_endpoint:new URL(`${i}token`,_).href,grant_types_supported:["urn:ietf:params:oauth:grant-type:jwt-bearer"]};return CS(v)(p,f,m)})),e.identity){let p={...e.identity,basePath:i,tokenPath:a??`${i}token`};_Be(t,p)}if(yL(r)){let p=s??`${i}authorize`;t.use(p,gY({provider:r}));let f=a??`${i}token`;if(t.use(f,xY({provider:r})),r.clientsStore?.registerClient){let h=c??`${i}register`;t.use(h,yY({clientsStore:r.clientsStore}))}if(r.revokeToken){let h=u??`${i}revoke`;t.use(h,TY({provider:r}))}let m=r.handleOAuthCallback?.bind(r);if(m){let h=r.callbackPath??"/callback";t.get(h,async(_,v)=>{let E=typeof _.query.code=="string"?_.query.code:void 0,x=typeof _.query.state=="string"?_.query.state:void 0;if(!E){v.status(400).send("Invalid request parameters");return}try{let w=await m(E,x,v);v.redirect(w.toString())}catch(w){console.error(w),v.status(500).send("Error during authentication callback")}})}}r&&t.use("/mcp",(p,f,m)=>UBe({verifier:r,requiredScopes:r.requiredScopes,resourceMetadataUrl:r.resourceMetadataUrl})(p,f,m))}var Zd=W(_Ge()),vGe=W(NS()),SGe=W(Pc());Te();var Vjr=process.env.REGISTRY_ENDPOINT,Hjr=process.env.ANALYTICS_ENDPOINT,EWt=8081,to=PW;async function TWt(){try{let t=process.env.PORT||EWt.toString(),e,r=to.initApp?to.initApp():(0,SGe.default)();if(console.log(`${Zd.default.dim("> Injecting cors middleware")}`),r.use((0,vGe.default)({exposedHeaders:["mcp-session-id"]})),to.oauth&&console.log(Zd.default.dim("> OAuth detected. Mounting auth routes.")),to.identity&&console.log(Zd.default.dim("> Identity detected. Mounting identity routes.")),(to.oauth||to.identity)&&BBe(r,{provider:to.oauth,identity:to.identity}),to.default&&typeof to.default=="function"){if(console.log(Zd.default.dim(`> Setting up ${to.stateless?"stateless":"stateful"} server`)),to.configSchema)try{let i=gi(to.configSchema),s=Object.keys(i.properties||{}).length,a=(i.required||[]).length;s>0&&console.log(Zd.default.dim(`> Config schema: ${s} field${s===1?"":"s"} (${a} required)`))}catch{console.log(Zd.default.dim("> Config schema detected"))}let n=to.oauth,o=process.env.LOG_LEVEL||"info";to.stateless?e=eY(to.default,{app:r,schema:to.configSchema,logLevel:o}):e=XZ(to.default,{app:r,schema:to.configSchema,logLevel:o})}else throw new Error(`No valid server export found. Please export: -- export default function({ sessionId, config }) { ... } (stateful) -- export default function({ config }) { ... } (stateless)`);e.app.listen(Number.parseInt(t,10))}catch(t){console.error(`${Zd.default.red("\u2717 Failed to start MCP server:")}`,t),process.exit(1)}}TWt(); -/*! Bundled license information: - -lodash/lodash.js: - (** - * @license - * Lodash - * Copyright OpenJS Foundation and other contributors - * Released under MIT license - * Based on Underscore.js 1.8.3 - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - *) - -depd/index.js: - (*! - * depd - * Copyright(c) 2014-2018 Douglas Christopher Wilson - * MIT Licensed - *) - -statuses/index.js: - (*! - * statuses - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2016 Douglas Christopher Wilson - * MIT Licensed - *) - -toidentifier/index.js: - (*! - * toidentifier - * Copyright(c) 2016 Douglas Christopher Wilson - * MIT Licensed - *) - -http-errors/index.js: - (*! - * http-errors - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2016 Douglas Christopher Wilson - * MIT Licensed - *) - -bytes/index.js: - (*! - * bytes - * Copyright(c) 2012-2014 TJ Holowaychuk - * Copyright(c) 2015 Jed Watson - * MIT Licensed - *) - -unpipe/index.js: - (*! - * unpipe - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - *) - -raw-body/index.js: - (*! - * raw-body - * Copyright(c) 2013-2014 Jonathan Ong - * Copyright(c) 2014-2022 Douglas Christopher Wilson - * MIT Licensed - *) - -ee-first/index.js: - (*! - * ee-first - * Copyright(c) 2014 Jonathan Ong - * MIT Licensed - *) - -on-finished/index.js: - (*! - * on-finished - * Copyright(c) 2013 Jonathan Ong - * Copyright(c) 2014 Douglas Christopher Wilson - * MIT Licensed - *) - -content-type/index.js: - (*! - * content-type - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - *) - -mime-db/index.js: - (*! - * mime-db - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2015-2022 Douglas Christopher Wilson - * MIT Licensed - *) - -mime-types/index.js: - (*! - * mime-types - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - *) - -media-typer/index.js: - (*! - * media-typer - * Copyright(c) 2014-2017 Douglas Christopher Wilson - * MIT Licensed - *) - -type-is/index.js: - (*! - * type-is - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - *) - -body-parser/lib/read.js: -body-parser/lib/types/raw.js: -body-parser/lib/types/text.js: -body-parser/index.js: - (*! - * body-parser - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - *) - -body-parser/lib/types/json.js: -body-parser/lib/types/urlencoded.js: - (*! - * body-parser - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - *) - -encodeurl/index.js: - (*! - * encodeurl - * Copyright(c) 2016 Douglas Christopher Wilson - * MIT Licensed - *) - -escape-html/index.js: - (*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - *) - -parseurl/index.js: - (*! - * parseurl - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2014-2017 Douglas Christopher Wilson - * MIT Licensed - *) - -finalhandler/index.js: - (*! - * finalhandler - * Copyright(c) 2014-2022 Douglas Christopher Wilson - * MIT Licensed - *) - -express/lib/view.js: -express/lib/application.js: -express/lib/request.js: -express/lib/express.js: -express/index.js: - (*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - *) - -etag/index.js: - (*! - * etag - * Copyright(c) 2014-2016 Douglas Christopher Wilson - * MIT Licensed - *) - -forwarded/index.js: - (*! - * forwarded - * Copyright(c) 2014-2017 Douglas Christopher Wilson - * MIT Licensed - *) - -proxy-addr/index.js: - (*! - * proxy-addr - * Copyright(c) 2014-2016 Douglas Christopher Wilson - * MIT Licensed - *) - -express/lib/utils.js: -express/lib/response.js: - (*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - *) - -router/lib/layer.js: -router/lib/route.js: -router/index.js: - (*! - * router - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2022 Douglas Christopher Wilson - * MIT Licensed - *) - -negotiator/index.js: - (*! - * negotiator - * Copyright(c) 2012 Federico Romero - * Copyright(c) 2012-2014 Isaac Z. Schlueter - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - *) - -accepts/index.js: - (*! - * accepts - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - *) - -fresh/index.js: - (*! - * fresh - * Copyright(c) 2012 TJ Holowaychuk - * Copyright(c) 2016-2017 Douglas Christopher Wilson - * MIT Licensed - *) - -range-parser/index.js: - (*! - * range-parser - * Copyright(c) 2012-2014 TJ Holowaychuk - * Copyright(c) 2015-2016 Douglas Christopher Wilson - * MIT Licensed - *) - -safe-buffer/index.js: - (*! safe-buffer. MIT License. Feross Aboukhadijeh *) - -content-disposition/index.js: - (*! - * content-disposition - * Copyright(c) 2014-2017 Douglas Christopher Wilson - * MIT Licensed - *) - -cookie/index.js: - (*! - * cookie - * Copyright(c) 2012-2014 Roman Shtylman - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - *) - -send/index.js: - (*! - * send - * Copyright(c) 2012 TJ Holowaychuk - * Copyright(c) 2014-2022 Douglas Christopher Wilson - * MIT Licensed - *) - -vary/index.js: - (*! - * vary - * Copyright(c) 2014-2017 Douglas Christopher Wilson - * MIT Licensed - *) - -serve-static/index.js: - (*! - * serve-static - * Copyright(c) 2010 Sencha Inc. - * Copyright(c) 2011 TJ Holowaychuk - * Copyright(c) 2014-2016 Douglas Christopher Wilson - * MIT Licensed - *) - -object-assign/index.js: - (* - object-assign - (c) Sindre Sorhus - @license MIT - *) -*/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 59daa7a0..a74981ad 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,8 +1,6 @@ { "recommendations": [ - "dbaeumer.vscode-eslint" - ], - "unwantedRecommendations": [ + "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index fb633852..b0dcd07c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -27,7 +27,8 @@ "type": "node", "request": "launch", "name": "Launch MCP Server Dev", - "program": "${workspaceFolder}/build/index.js", + "program": "${workspaceFolder}/build/cli.js", + "args": ["mcp"], "cwd": "${workspaceFolder}", "runtimeArgs": [ "--inspect=9999" diff --git a/.vscode/mcp.json b/.vscode/mcp.json index de9ab67b..ac5ac9c3 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -20,7 +20,8 @@ "args": [ "--inspect=9999", "--trace-warnings", - "${workspaceFolder}/build/index.js" + "${workspaceFolder}/build/cli.js", + "mcp" ], "env": { "XCODEBUILDMCP_DEBUG": "true", diff --git a/.vscode/settings.json b/.vscode/settings.json index 93934668..915880e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,6 @@ "javascript", "typescript" ], - "eslint.runtime": "/opt/homebrew/bin/node", "eslint.format.enable": true, "eslint.nodePath": "${workspaceFolder}/node_modules", "eslint.workingDirectories": [ @@ -21,27 +20,23 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "editor.defaultFormatter": "vscode.typescript-language-features", + "editor.defaultFormatter": "esbenp.prettier-vscode", "[typescript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "terminal.integrated.shellIntegration.decorationsEnabled": "never", - "vitest.nodeExecutable": "/opt/homebrew/bin/node", "[json]": { "editor.defaultFormatter": "vscode.json-language-features" }, "[jsonc]": { "editor.defaultFormatter": "vscode.json-language-features" }, - "chat.mcp.serverSampling": { - "XcodeBuildMCP/.vscode/mcp.json: XcodeBuildMCP-Dev": { - "allowedDuringChat": true, - "allowedModels": [ - "copilot/gpt-5.2" - ] - } - }, + "vitest.shellType": "child_process", + "vitest.nodeExecutable": "/usr/bin/env", + "vitest.nodeExecArgs": [ + "node" + ] } \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index e7881092..8bc172d4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,210 +1,53 @@ -This file provides guidance to AI assisants (Claude Code, Cursor etc) when working with code in this repository. - -## Project Overview - -XcodeBuildMCP is a Model Context Protocol (MCP) server providing standardized tools for AI assistants to interact with Xcode projects, iOS simulators, devices, and Apple development workflows. It's a TypeScript/Node.js project that runs as a stdio-based MCP server. - -## Common Commands - -### Build & Development -```bash -npm run build # Compile TypeScript with tsup, generates version info -npm run dev # Watch mode development -npm run bundle:axe # Bundle axe CLI tool for simulator automation (needed when using local MCP server) -npm run test # Run complete Vitest test suite -npm run test:watch # Watch mode testing -npm run lint # ESLint code checking -npm run lint:fix # ESLint code checking and fixing -npm run format:check # Prettier code checking -npm run format # Prettier code formatting -npm run typecheck # TypeScript type checking -npm run inspect # Run interactive MCP protocol inspector -npm run doctor # Doctor CLI -``` - -### Development with Reloaderoo - -**Reloaderoo** (v1.1.2+) provides CLI-based testing and hot-reload capabilities for XcodeBuildMCP without requiring MCP client configuration. - -#### Quick Start - -**CLI Mode (Testing & Development):** -```bash -# List all tools -npx reloaderoo inspect list-tools -- node build/index.js - -# Call any tool -npx reloaderoo inspect call-tool list_devices --params '{}' -- node build/index.js - -# Get server information -npx reloaderoo inspect server-info -- node build/index.js - -# List and read resources -npx reloaderoo inspect list-resources -- node build/index.js -npx reloaderoo inspect read-resource "xcodebuildmcp://devices" -- node build/index.js -``` - -**Proxy Mode (MCP Client Integration):** -```bash -# Start persistent server for MCP clients -npx reloaderoo proxy -- node build/index.js - -# With debug logging -npx reloaderoo proxy --log-level debug -- node build/index.js - -# Then ask AI: "Please restart the MCP server to load my changes" -``` - -#### All CLI Inspect Commands - -Reloaderoo provides 8 inspect subcommands for comprehensive MCP server testing: - -```bash -# Server capabilities and information -npx reloaderoo inspect server-info -- node build/index.js - -# Tool management -npx reloaderoo inspect list-tools -- node build/index.js -npx reloaderoo inspect call-tool --params '' -- node build/index.js - -# Resource access -npx reloaderoo inspect list-resources -- node build/index.js -npx reloaderoo inspect read-resource "" -- node build/index.js - -# Prompt management -npx reloaderoo inspect list-prompts -- node build/index.js -npx reloaderoo inspect get-prompt --args '' -- node build/index.js - -# Connectivity testing -npx reloaderoo inspect ping -- node build/index.js -``` - -#### Advanced Options - -```bash -# Custom working directory -npx reloaderoo inspect list-tools --working-dir /custom/path -- node build/index.js - -# Timeout configuration -npx reloaderoo inspect call-tool slow_tool --timeout 60000 --params '{}' -- node build/index.js - -# Use timeout configuration if needed -npx reloaderoo inspect server-info --timeout 60000 -- node build/index.js - -# Debug logging (use proxy mode for detailed logging) -npx reloaderoo proxy --log-level debug -- node build/index.js -``` - -#### Key Benefits - -- ✅ **No MCP Client Setup**: Direct CLI access to all tools -- ✅ **Raw JSON Output**: Perfect for AI agents and programmatic use -- ✅ **Hot-Reload Support**: `restart_server` tool for MCP client development -- ✅ **Claude Code Compatible**: Automatic content block consolidation -- ✅ **8 Inspect Commands**: Complete MCP protocol testing capabilities -- ✅ **Universal Compatibility**: Works on any system via npx - -For complete documentation, examples, and troubleshooting, see @docs/RELOADEROO.md - -## Architecture Overview - -### Plugin-Based MCP architecture - -XcodeBuildMCP uses the concept of configuration by convention for MCP exposing and running MCP capabilities like tools and resources. This means to add a new tool or resource, you simply create a new file in the appropriate directory and it will be automatically loaded and exposed to MCP clients. - -#### Tools - -Tools are the core of the MCP server and are the primary way to interact with the server. They are organized into directories by their functionality and are automatically loaded and exposed to MCP clients. - -For more information see @docs/PLUGIN_DEVELOPMENT.md - -#### Resources - -Resources are the secondary way to interact with the server. They are used to provide data to tools and are organized into directories by their functionality and are automatically loaded and exposed to MCP clients. - -For more information see @docs/PLUGIN_DEVELOPMENT.md - -### Tool Registration - -XcodeBuildMCP loads tools at startup. To limit the toolset, set `XCODEBUILDMCP_ENABLED_WORKFLOWS` to a comma-separated list of workflow directory names (for example: `simulator,project-discovery`). The `session-management` workflow is always auto-included since other tools depend on it. - -#### Claude Code Compatibility Workaround -- **Detection**: Automatic detection when running under Claude Code. -- **Purpose**: Workaround for Claude Code's MCP specification violation where it only displays the first content block in tool responses. -- **Behavior**: When Claude Code is detected, multiple content blocks are automatically consolidated into a single text response, separated by `---` dividers. This ensures all information (including test results and stderr warnings) is visible to Claude Code users. - -### Core Architecture Layers -1. **MCP Transport**: stdio protocol communication -2. **Plugin Discovery**: Automatic tool AND resource registration system -3. **MCP Resources**: URI-based data access (e.g., `xcodebuildmcp://simulators`) -4. **Tool Implementation**: Self-contained workflow modules -5. **Shared Utilities**: Command execution, build management, validation -6. **Types**: Shared interfaces and Zod schemas - -For more information see @docs/ARCHITECTURE.md - -## Testing - -The project enforces a strict **Dependency Injection (DI)** testing philosophy. - -- **NO Vitest Mocking**: The use of `vi.mock()`, `vi.fn()`, `vi.spyOn()`, etc., is **completely banned**. -- **Executors**: All external interactions (like running commands or accessing the file system) are handled through injectable "executors". - - `CommandExecutor`: For running shell commands. - - `FileSystemExecutor`: For file system operations. -- **Testing Logic**: Tests import the core `...Logic` function from a tool file and pass in a mock executor (`createMockExecutor` or `createMockFileSystemExecutor`) to simulate different outcomes. - -This approach ensures that tests are robust, easy to maintain, and verify the actual integration between components without being tightly coupled to implementation details. - -For complete guidelines, refer to @docs/TESTING.md. - -## TypeScript Import Standards - -This project uses **TypeScript file extensions** (`.ts`) for all relative imports to ensure compatibility with native TypeScript runtimes. - -### Import Rules - -- ✅ **Use `.ts` extensions**: `import { tool } from './tool.ts'` -- ✅ **Use `.ts` for re-exports**: `export { default } from '../shared/tool.ts'` -- ✅ **External packages use `.js`**: `import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'` -- ❌ **Never use `.js` for internal files**: `import { tool } from './tool.js'` ← ESLint error - -### Benefits - -1. **Future-proof**: Compatible with native TypeScript runtimes (Bun, Deno, Node.js --loader) -2. **IDE Experience**: Direct navigation to source TypeScript files -3. **Consistency**: Import path matches the actual file you're editing -4. **Modern Standard**: Aligns with TypeScript 4.7+ `allowImportingTsExtensions` - -### ESLint Enforcement - -The project automatically enforces this standard: - -```bash -npm run lint # Will catch .js imports for internal files -``` - -This ensures all new code follows the `.ts` import pattern and maintains compatibility with both current and future TypeScript execution environments. - -## Release Process - -Follow standardized development workflow with feature branches, structured pull requests, and linear commit history. **Never push to main directly or force push without permission.** - -For complete guidelines, refer to @docs/RELEASE_PROCESS.md - -## Useful external resources - -### Model Context Protocol - -https://modelcontextprotocol.io/llms-full.txt - -### MCP Specification - -https://modelcontextprotocol.io/specification - -### MCP Inspector - -https://github.com/modelcontextprotocol/inspector - -### MCP Client SDKs - -https://github.com/modelcontextprotocol/typescript-sdk +# Development Rules + +## Code Quality +- No `any` types unless absolutely necessary +- Check node_modules for external API type definitions instead of guessing +- **NEVER use inline imports** - no `await import("./foo.js")`, no `import("pkg").Type` in type positions, no dynamic imports for types. Always use standard top-level imports. +- NEVER remove or downgrade code to fix type errors from outdated dependencies; upgrade the dependency instead +- Always ask before removing functionality or code that appears to be intentional +- Follow TypeScript best practices + +## Commands +- NEVER commit unless user asks + +## GitHub +When reading issues: +- Always read all comments on the issue +- +## Tools +- GitHub CLI for issues/PRs +- +## Style +- Keep answers short and concise +- No emojis in commits, issues, PR comments, or code +- No fluff or cheerful filler text +- Technical prose only, be kind but direct (e.g., "Thanks @user" not "Thanks so much @user!") + +## Docs +- If modifying or adding/removing tools run `npm run docs:update` to update the TOOLS.md file, never edit this file directly. +- +### Changelog +Location: `CHANGELOG.md` + +#### Format +Use these sections under `## [Unreleased]`: +- `### Added` - New features +- `### Changed` - Changes to existing functionality +- `### Fixed` - Bug fixes +- `### Removed` - Removed features +- +#### Rules +- Before adding entries, read the full `[Unreleased]` section to see which subsections already exist +- New entries ALWAYS go under `## [Unreleased]` section +- Append to existing subsections (e.g., `### Fixed`), do not create duplicates +- NEVER modify already-released version sections (e.g., `## [0.12.2]`) +- Each version section is immutable once released +- +#### Attribution +- **Internal changes (from issues)**: `Fixed foo bar ([#123](https://github.com/cameroncook/XcodeBuildMCP/issues/123))` +- **External contributions**: `Added feature X ([#456](https://github.com/cameroncook/XcodeBuildMCP/pull/456) by [@username](https://github.com/username))` + +## **CRITICAL** Tool Usage Rules **CRITICAL** +- NEVER use sed/cat to read a file or a range of a file. Always use the native read tool. +- You MUST read every file you modify in full before editing. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b0a3f59..b04cc542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,107 @@ # Changelog -## [1.16.0] - 2025-12-30 -- Remove dynamic tool discovery (`discover_tools`) and `XCODEBUILDMCP_DYNAMIC_TOOLS`. Use `XCODEBUILDMCP_ENABLED_WORKFLOWS` to limit startup tool registration. -- Add MCP tool annotations to all tools. +## [Unreleased] + +### Changed + +- Changed MCP `xcode-ide` integration to expose manifest-managed gateway tools (`xcode_ide_list_tools`, `xcode_ide_call_tool`) while keeping CLI dynamic `xcode_tools_*` behavior unchanged ([#210](https://github.com/getsentry/XcodeBuildMCP/issues/210)) +- Deferred non-critical Xcode IDE state synchronization and Sentry enrichment work until after MCP connect to reduce handshake-path latency ([#210](https://github.com/getsentry/XcodeBuildMCP/issues/210)) + +### Fixed + +- Removed startup dependency on handshake-time Xcode bridge `tools/list` sync for MCP tool registration, preventing bridge list latency from delaying initial connect ([#210](https://github.com/getsentry/XcodeBuildMCP/issues/210)) +- Fixed Sentry telemetry scope to capture only internal XcodeBuildMCP runtime failures, removing broad MCP wrapping, PII-heavy tags, and default per-error log capture ([#204](https://github.com/getsentry/XcodeBuildMCP/issues/204)) + +## [2.0.7] + +### Changed + +- XcodeBuildMCP has moved to the [getsentry](https://github.com/getsentry) GitHub organization. Homebrew users should switch to the new tap: `brew untap cameroncooke/xcodebuildmcp && brew tap getsentry/xcodebuildmcp`. npm and npx users are unaffected. + +## [2.0.5] - 2026-02-10 + +### Added + +- Homebrew installation (`brew tap getsentry/xcodebuildmcp && brew install xcodebuildmcp`) — installs a self-contained binary with no Node.js dependency. + +### Removed + +- Smithery distribution channel. + +## [2.0.0] + +### New! CLI + +XcodeBuildMCP now includes a first-class CLI for direct terminal usage, scripting, and CI workflows. All the same tools available via MCP are accessible from the command line. + +```bash +npm install -g xcodebuildmcp@beta +xcodebuildmcp tools # List available tools +xcodebuildmcp simulator build-and-run --scheme MyApp --project-path ./MyApp.xcodeproj +``` + +Stateful operations (log capture, debugging, video recording) are backed by a per-workspace background process that starts automatically and shuts down after idle. See [docs/CLI.md](docs/CLI.md) for full documentation. + +### New! Configuration File + +Project-level configuration via `.xcodebuildmcp/config.yaml` replaces the need for environment variables. Set your project path, scheme, simulator, enabled workflows, debug settings, and more in one place. Environment variables still work but the config file takes precedence. + +```yaml +schemaVersion: 1 +enabledWorkflows: + - simulator + - ui-automation + - debugging +sessionDefaults: + scheme: MyApp + projectPath: ./MyApp.xcodeproj + simulatorName: iPhone 16 +``` + +See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for the full reference. + +### New! Xcode IDE Integration + +XcodeBuildMCP can now proxy tools from Xcode 26.3's built-in MCP bridge, giving your agent access to Xcode IDE capabilities like Preview rendering, the Issue Navigator, and documentation search. Enable the `xcode-ide` workflow to use this. Setup instructions for both Codex Agent and Claude Code Agent in Xcode are included. See [docs/XCODE_IDE_MCPBRIDGE.md](docs/XCODE_IDE_MCPBRIDGE.md) for details. + +### Added + +- **LLDB Debugging**: Attach a debugger to simulator apps, set breakpoints, inspect variables, view the call stack, and run LLDB commands — all through your agent. Supports both DAP and LLDB-CLI backends. See [docs/TOOLS.md](docs/TOOLS.md) for the debugging tools. +- **Session default persistence**: Session defaults can now be saved to the config file with `persist: true`, so your preferred project, scheme, and simulator are remembered across sessions. +- **Log subsystem filtering**: Filter simulator log capture by subsystem — choose `app` (default), `all`, `swiftui` (for `Self._printChanges()` output), or a custom list of subsystems. +- **Agent skills**: Optional skill files that prime your agent with usage instructions for the MCP server or CLI. Install via the provided shell script or manually. See [docs/SKILLS.md](docs/SKILLS.md). +- **MCP tool annotations**: All tools now include MCP-standard annotations (read-only vs. destructive, idempotent, etc.) for clients that support them. +- **Simulator name resolution**: Session defaults now accept a simulator name and automatically resolve it to a device ID. +- **Launch environment variables**: Launch tools now accept an optional `env` object so you can pass runtime environment variables when starting apps on simulator or device. + +### Changed + +- Simulator tools are now the default workflow. Previously all workflows loaded by default, increasing context usage. +- Bundled AXe updated to 1.3.0. +- Landscape screenshots now orient correctly. +- Simulator platform detection and default refresh behavior are more reliable, so simulator commands stay aligned with your current defaults as they change. + +### Fixed + +- Fixed incremental builds corrupting arguments when strings contained substrings matching build flags. +- Fixed build path handling so relative project, workspace, and derived data paths resolve correctly even when commands run from different working directories. +- Fixed working-directory leakage in incremental build setup that could affect concurrent requests. +- Fixed simulator screenshot matching for similarly named devices (for example, `iPhone 15` and `iPhone 15 Pro`). + +## [1.15.1] - 2025-12-20 + +### Changed +- Add suppressWarnings to suppress warnings from the build tools. +- Update AXe to 1.2.0 +- Update tap tool to accept label/id as tap targets. + +## [1.15.0] - 2025-12-15 + +### Added +- Add support for in-memory session defaults. + +### Changed +- Various bug fixes and improvements ## [1.14.0] - 2025-09-22 - Add video capture tool for simulators @@ -185,3 +284,4 @@ Please note that the UI automation features are an early preview and currently i ## [v1.0.1] - 2025-04-02 - Initial release of XcodeBuildMCP - Basic support for building iOS and macOS applications + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 57c6600b..b4bafb44 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,128 +1 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible 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. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders 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, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -xcodebuildmcp@cameroncooke.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. +See https://open.sentry.io/code-of-conduct/ diff --git a/NEXT_STEPS_MIGRATION_TODO.md b/NEXT_STEPS_MIGRATION_TODO.md new file mode 100644 index 00000000..abd76a08 --- /dev/null +++ b/NEXT_STEPS_MIGRATION_TODO.md @@ -0,0 +1,27 @@ +# Next Steps Migration TODO + +Generated: 2026-02-11 19:12:27 UTC + +## Remaining tool files with inline nextSteps +- [x] src/mcp/tools/debugging/debug_attach_sim.ts:148 +- [x] src/mcp/tools/device/get_device_app_path.ts:140 +- [x] src/mcp/tools/device/launch_app_device.ts:135 +- [x] src/mcp/tools/device/list_devices.ts:391 +- [x] src/mcp/tools/logging/start_device_log_cap.ts:665 +- [x] src/mcp/tools/logging/start_sim_log_cap.ts:81 +- [x] src/mcp/tools/macos/get_mac_app_path.ts:167 +- [x] src/mcp/tools/project-discovery/get_app_bundle_id.ts:91 +- [x] src/mcp/tools/project-discovery/get_mac_bundle_id.ts:88 +- [x] src/mcp/tools/project-discovery/list_schemes.ts:83 +- [x] src/mcp/tools/project-discovery/show_build_settings.ts:88 +- [x] src/mcp/tools/project-scaffolding/scaffold_ios_project.ts:366 +- [x] src/mcp/tools/project-scaffolding/scaffold_macos_project.ts:340 +- [x] src/mcp/tools/simulator/boot_sim.ts:69 +- [x] src/mcp/tools/simulator/build_run_sim.ts:486 +- [x] src/mcp/tools/simulator/get_sim_app_path.ts:244 +- [x] src/mcp/tools/simulator/install_app_sim.ts:93 +- [x] src/mcp/tools/simulator/launch_app_logs_sim.ts:100 +- [x] src/mcp/tools/simulator/launch_app_sim.ts:127 +- [x] src/mcp/tools/simulator/list_sims.ts:196 +- [x] src/mcp/tools/simulator/open_sim.ts:42 +- [x] src/mcp/tools/simulator/record_sim_video.ts:130 diff --git a/README.md b/README.md index a2841221..8412563f 100644 --- a/README.md +++ b/README.md @@ -1,354 +1,352 @@ XcodeBuild MCP -A Model Context Protocol (MCP) server that provides Xcode-related tools for integration with AI assistants and other MCP clients. - -[![CI](https://github.com/cameroncooke/XcodeBuildMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/cameroncooke/XcodeBuildMCP/actions/workflows/ci.yml) -[![npm version](https://badge.fury.io/js/xcodebuildmcp.svg)](https://badge.fury.io/js/xcodebuildmcp) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/node->=18.x-brightgreen.svg)](https://nodejs.org/) [![Xcode 16](https://img.shields.io/badge/Xcode-16-blue.svg)](https://developer.apple.com/xcode/) [![macOS](https://img.shields.io/badge/platform-macOS-lightgrey.svg)](https://www.apple.com/macos/) [![MCP](https://img.shields.io/badge/MCP-Compatible-green.svg)](https://modelcontextprotocol.io/) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/cameroncooke/XcodeBuildMCP) - -## Table of contents - -- [Overview](#overview) -- [Why?](#why) -- [Features](#features) - - [Xcode project management](#xcode-project-management) - - [Swift Package Manager](#swift-package-manager) - - [Simulator management](#simulator-management) - - [Device management](#device-management) - - [App utilities](#app-utilities) - - [MCP Resources](#mcp-resources) -- [Getting started](#getting-started) - - [Prerequisites](#prerequisites) - - [One click install](#one-click-install) - - [General installation](#general-installation) - - [Specific client installation instructions](#specific-client-installation-instructions) - - [OpenAI Codex CLI](#openai-codex-cli) - - [Claude Code CLI](#claude-code-cli) - - [Smithery](#smithery) - - [MCP Compatibility](#mcp-compatibility) -- [Incremental build support](#incremental-build-support) -- [Workflow Selection](#workflow-selection) -- [Session-aware opt-out](#session-aware-opt-out) -- [Code Signing for Device Deployment](#code-signing-for-device-deployment) -- [Troubleshooting](#troubleshooting) - - [Doctor Tool](#doctor-tool) -- [Privacy](#privacy) - - [What is sent to Sentry?](#what-is-sent-to-sentry) - - [Opting Out of Sentry](#opting-out-of-sentry) -- [Demos](#demos) - - [Autonomously fixing build errors in Cursor](#autonomously-fixing-build-errors-in-cursor) - - [Utilising the new UI automation and screen capture features](#utilising-the-new-ui-automation-and-screen-capture-features) - - [Building and running iOS app in Claude Desktop](#building-and-running-ios-app-in-claude-desktop) -- [Contributing](#contributing) -- [Licence](#licence) - -## Overview - -XcodeBuildMCP is a Model Context Protocol (MCP) server that exposes Xcode operations as tools and resources for AI assistants and other MCP clients. Built with a modern plugin architecture, it provides a comprehensive set of self-contained tools organized into workflow-based directories, plus MCP resources for efficient data access, enabling programmatic interaction with Xcode projects, simulators, devices, and Swift packages through a standardized interface. - -![xcodebuildmcp2](https://github.com/user-attachments/assets/8961d5db-f7ed-4e60-bbb8-48bfd0bc1353) -Using Cursor to build, install, and launch an app on the iOS simulator while capturing logs at run-time. - -## Why? - -The XcodeBuild MCP tool exists primarily to streamline and standardise interaction between AI agents and Xcode projects. By providing dedicated tools for common Xcode operations, it removes reliance on manual or potentially incorrect command-line invocations. - -This ensures a reliable and efficient development process, allowing agents to seamlessly leverage Xcode's capabilities while reducing the risk of configuration errors. - -Critically, this MCP enables AI agents to independently validate code changes by building projects, inspecting errors, and iterating autonomously. In contrast to user-driven tools like Sweetpad, XcodeBuild MCP empowers agents to automate these workflows effectively. - -## Features - -The XcodeBuildMCP server provides the following tool capabilities: - -### Xcode project management -- **Discover Projects**: Xcode projects and workspaces discovery -- **Build Operations**: Platform-specific build tools for macOS, iOS simulator, and iOS device targets -- **Project Information**: Tools to list schemes and show build settings for Xcode projects and workspaces -- **Clean Operations**: Clean build products using xcodebuild's native clean action -- **Incremental build support**: Lightning fast builds using incremental build support (experimental, opt-in required) -- **Project Scaffolding**: Create new iOS and macOS projects from modern templates with workspace + SPM package architecture, customizable bundle identifiers, deployment targets, and device families - -### Swift Package Manager -- **Build Packages**: Build Swift packages with configuration and architecture options -- **Run Tests**: Execute Swift package test suites with filtering and parallel execution -- **Run Executables**: Execute package binaries with timeout handling and background execution support -- **Process Management**: List and stop long-running executables started with Swift Package tools -- **Clean Artifacts**: Remove build artifacts and derived data for fresh builds - -### Simulator management -- **Simulator Control**: List, boot, and open simulators -- **App Lifecycle**: Complete app management - install, launch, and stop apps on simulators -- **Log Capture**: Capture run-time logs from a simulator -- **UI Automation**: Interact with simulator UI elements -- **Screenshot**: Capture screenshots from a simulator -- **Video Capture**: Start/stop simulator video capture to MP4 (AXe v1.1.0+) - -### Device management -- **Device Discovery**: List connected physical Apple devices over USB or Wi-Fi -- **App Lifecycle**: Complete app management - build, install, launch, and stop apps on physical devices -- **Testing**: Run test suites on physical devices with detailed results and cross-platform support -- **Log Capture**: Capture console output from apps running on physical Apple devices -- **Wireless Connectivity**: Support for devices connected over Wi-Fi networks - -### App utilities -- **Bundle ID Extraction**: Extract bundle identifiers from app bundles across all Apple platforms -- **App Lifecycle Management**: Complete app lifecycle control across all platforms - - Launch apps on simulators, physical devices, and macOS - - Stop running apps with process ID or bundle ID management - - Process monitoring and control for comprehensive app management - -### MCP Resources - -For clients that support MCP resources XcodeBuildMCP provides efficient URI-based data access: - -- **Simulators Resource** (`xcodebuildmcp://simulators`): Direct access to available iOS simulators with UUIDs and states -- **Devices Resource** (`xcodebuildmcp://devices`): Direct access to connected physical Apple devices with UDIDs and states -- **Doctor Resource** (`xcodebuildmcp://doctor`): Direct access to environment information such as Xcode version, macOS version, and Node.js version - -## Getting started - -### Prerequisites +A Model Context Protocol (MCP) server and CLI that provides tools for agent use when working on iOS and macOS projects. -- macOS 14.5 or later -- Xcode 16.x or later -- Node 18.x or later - -> Video capture requires the bundled AXe binary (v1.1.0+). Run `npm run bundle:axe` once locally before using `record_sim_video`. This is not required for unit tests. - -Configure your MCP client - -#### One click install +[![CI](https://github.com/getsentry/XcodeBuildMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/getsentry/XcodeBuildMCP/actions/workflows/ci.yml) +[![npm version](https://badge.fury.io/js/xcodebuildmcp.svg)](https://badge.fury.io/js/xcodebuildmcp) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/node->=18.x-brightgreen.svg)](https://nodejs.org/) [![Xcode 16](https://img.shields.io/badge/Xcode-16-blue.svg)](https://developer.apple.com/xcode/) [![macOS](https://img.shields.io/badge/platform-macOS-lightgrey.svg)](https://www.apple.com/macos/) [![MCP](https://img.shields.io/badge/MCP-Compatible-green.svg)](https://modelcontextprotocol.io/) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/getsentry/XcodeBuildMCP) [![AgentAudit Security](https://img.shields.io/badge/AgentAudit-Safe-brightgreen?logo=data:image/svg%2Bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAxTDMgNXY2YzAgNS41NSAzLjg0IDEwLjc0IDkgMTIgNS4xNi0xLjI2IDktNi40NSA5LTEyVjVsLTktNHoiLz48L3N2Zz4=)](https://www.agentaudit.dev/skills/xcodebuildmcp) -For a quick install, you can use the following links: +## Installation -[![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=XcodeBuildMCP&config=eyJ0eXBlIjoic3RkaW8iLCJjb21tYW5kIjoibnB4IC15IHhjb2RlYnVpbGRtY3BAbGF0ZXN0IiwiZW52Ijp7IklOQ1JFTUVOVEFMX0JVSUxEU19FTkFCTEVEIjoiZmFsc2UiLCJYQ09ERUJVSUxETUNQX1NFTlRSWV9ESVNBQkxFRCI6ImZhbHNlIn19) +XcodeBuildMCP ships as a single package with two modes: a **CLI** for direct terminal use and an **MCP server** for AI coding agents. Both installation methods give you both modes. -[Install in VS Code](https://insiders.vscode.dev/redirect/mcp/install?name=XcodeBuildMCP&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22xcodebuildmcp%40latest%22%5D%7D) +### Option A — Homebrew -[Install in VS Code Insiders](https://insiders.vscode.dev/redirect/mcp/install?name=XcodeBuildMCP&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22xcodebuildmcp%40latest%22%5D%7D&quality=insiders) - -#### General installation +```bash +brew tap getsentry/xcodebuildmcp +brew install xcodebuildmcp +``` -Most MCP clients (Cursor, VS Code, Windsurf, Claude Desktop etc) have standardised on the following JSON configuration format, just add the the following to your client's JSON configuration's `mcpServers` object: +Use the CLI: +```bash +xcodebuildmcp --help +``` +MCP client config: ```json "XcodeBuildMCP": { - "command": "npx", - "args": [ - "-y", - "xcodebuildmcp@latest" - ] + "command": "xcodebuildmcp", + "args": ["mcp"] } ``` -#### Specific client installation instructions - -##### OpenAI Codex CLI - -Codex uses a toml configuration file to configure MCP servers. To configure XcodeBuildMCP with [OpenAI's Codex CLI](https://github.com/openai/codex), add the following configuration to your Codex CLI config file: +Upgrade later with `brew update && brew upgrade xcodebuildmcp`. -```toml -[mcp_servers.XcodeBuildMCP] -command = "npx" -args = ["-y", "xcodebuildmcp@latest"] -env = { "INCREMENTAL_BUILDS_ENABLED" = "false", "XCODEBUILDMCP_SENTRY_DISABLED" = "false" } -``` - -If you see tool calls timing out in Codex (for example, `timed out awaiting tools/call after 60s`), increase the Codex tool timeout in your config: - -```toml -tool_timeout_sec = 600 -``` - -For more information see [OpenAI Codex MCP Server Configuration](https://github.com/openai/codex/blob/main/docs/config.md#connecting-to-mcp-servers) documentation. - -##### Claude Code CLI - -To use XcodeBuildMCP with [Claude Code](https://code.anthropic.com), you can add it via the command line: +### Option B — npm / npx (Node.js 18+) +**For CLI use**, install globally: ```bash -# Add XcodeBuildMCP server to Claude Code -claude mcp add XcodeBuildMCP npx xcodebuildmcp@latest - -# Or with environment variables -claude mcp add XcodeBuildMCP npx xcodebuildmcp@latest -e INCREMENTAL_BUILDS_ENABLED=false -e XCODEBUILDMCP_SENTRY_DISABLED=false +npm install -g xcodebuildmcp@latest +xcodebuildmcp --help ``` -##### Smithery - -XcodeBuildMCP runs as a local (stdio) server when installed via [Smithery](https://smithery.ai/server/@cameroncooke/XcodeBuildMCP). You can configure it in the Smithery session UI or via environment variables. - -```bash -# Claude Desktop / Claude Code -npx -y @smithery/cli install @cameroncooke/XcodeBuildMCP --client claude - -# Cursor -npx -y @smithery/cli install @cameroncooke/XcodeBuildMCP --client cursor - -# VS Code -npx -y @smithery/cli install @cameroncooke/XcodeBuildMCP --client vscode +**For MCP server only**, no global install needed — add directly to your client config: +```json +"XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] +} ``` -If your client isn't listed, run `npx smithery install --help` to see supported values for `--client`. - -> [!IMPORTANT] -> Please note that XcodeBuildMCP will request xcodebuild to skip macro validation. This is to avoid errors when building projects that use Swift Macros. - -#### MCP Compatibility - -XcodeBuildMCP supports both MCP tools, resources and sampling. At time of writing the following editors have varying levels of MCP feature support: - -| Editor | Tools | Resources | Samplng | -|--------|-------|-----------|---------| -| **VS Code** | ✅ | ✅ | ✅ | -| **Cursor** | ✅ | ❌ | ❌ | -| **Windsurf** | ✅ | ❌ | ❌ | -| **Claude Code** | ✅ | ✅ | ❌ | -| **Claude Desktop** | ✅ | ✅ | ❌ | +To pin a specific version, replace `@latest` with an exact version (e.g. `xcodebuildmcp@latest`). -## Incremental build support +### Client-specific setup -XcodeBuildMCP includes experimental support for incremental builds. This feature is disabled by default and can be enabled by setting the `INCREMENTAL_BUILDS_ENABLED` environment variable to `true`: +The examples below use npx (Option B). If you installed via Homebrew, replace the command with `"command": "xcodebuildmcp", "args": ["mcp"]` instead. -To enable incremental builds, set the `INCREMENTAL_BUILDS_ENABLED` environment variable to `true`: +
+ Cursor +
-Example MCP configuration: -```json -"XcodeBuildMCP": { - ... - "env": { - "INCREMENTAL_BUILDS_ENABLED": "true" + Recommended (project-scoped): add `.cursor/mcp.json` in your workspace root: + ```json + { + "mcpServers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } } -} -``` + ``` + + For global Cursor config (`~/.cursor/mcp.json`), use this variant so startup is aligned with the active workspace: + ```json + { + "mcpServers": { + "XcodeBuildMCP": { + "command": "/bin/zsh", + "args": [ + "-lc", + "cd \"${workspaceFolder}\" && exec npx -y xcodebuildmcp@latest mcp" + ] + } + } + } + ``` + + Or use the quick install link: + +[![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)](https://cursor.com/en-US/install-mcp?name=XcodeBuildMCP&config=eyJjb21tYW5kIjoibnB4IC15IHhjb2RlYnVpbGRtY3BAbGF0ZXN0IG1jcCJ9) +
+
+ +
+ Claude Code +
+ + Run: + ```bash + claude mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp + ``` +
+
+ +
+ Codex CLI +
+ + Run: + ```bash + codex mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp + ``` + + Or add to `~/.codex/config.toml`: + ```toml + [mcp_servers.XcodeBuildMCP] + command = "npx" + args = ["-y", "xcodebuildmcp@latest", "mcp"] + ``` +
+
+ +
+ Claude Desktop +
+ + Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: + ```json + { + "mcpServers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } + } + ``` +
+
+ +
+ VS Code / VS Code Insiders +
+ + Add to your VS Code settings JSON: + ```json + "mcp": { + "servers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } + } + ``` + + Or use the quick install links: + + [![Install in VS Code](https://img.shields.io/badge/VS_Code-XcodeBuildMCP-0098FF?style=flat&logo=visualstudiocode&logoColor=ffffff)](vscode:mcp/install?%7B%22name%22%3A%22XcodeBuildMCP%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22xcodebuildmcp%40latest%22%2C%22mcp%22%5D%7D) + [![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-XcodeBuildMCP-24bfa5?style=flat&logo=visualstudiocode&logoColor=ffffff)](vscode-insiders:mcp/install?%7B%22name%22%3A%22XcodeBuildMCP%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22xcodebuildmcp%40latest%22%2C%22mcp%22%5D%7D) +
+
+ +
+ Kiro / Kiro CLI +
+ + **Workspace-level** (applies only to the current workspace): add `.kiro/settings/mcp.json` in your project root: + ```json + { + "mcpServers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } + } + ``` + + **User-level** (applies globally across all workspaces): add to `~/.kiro/settings/mcp.json`: + ```json + { + "mcpServers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } + } + ``` +
+
+ +
+ Windsurf +
+ + Add to `~/.codeium/windsurf/mcp_config.json`: + ```json + { + "mcpServers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } + } + ``` +
+
+ +
+ Trae +
+ + Add to `~/Library/Application Support/Trae/User/mcp.json`: + ```json + { + "mcpServers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } + } + ``` +
+
-> [!IMPORTANT] -> Please note that incremental builds support is currently highly experimental and your mileage may vary. Please report any issues you encounter to the [issue tracker](https://github.com/cameroncooke/XcodeBuildMCP/issues). +
+ Xcode (Codex Agent) +
-## Workflow Selection + Requires Xcode 26.3 or later. Codex agent must be installed and configured in Xcode Settings -> Intelligence -> Open AI. -By default, XcodeBuildMCP loads all tools at startup. If you want a smaller tool surface for a specific workflow, set `XCODEBUILDMCP_ENABLED_WORKFLOWS` to a comma-separated list of workflow directory names. The `session-management` workflow is always auto-included since other tools depend on it. + The only way at the time of writing to add an MCP server is to use a project scoped `.codex/config.toml` file in the root of your project workspace: + `/path/to/your/project/.codex/config.toml` -Example MCP client configuration: -```json -"XcodeBuildMCP": { - ... - "env": { - "XCODEBUILDMCP_ENABLED_WORKFLOWS": "simulator,device,project-discovery" + ```toml + [mcp_servers.XcodeBuildMCP] + args = [ + "-lc", + "PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"; nvm use --silent >/dev/null 2>&1 || true; npx -y xcodebuildmcp@latest mcp" + ] + command = "/bin/zsh" + enabled = true + tool_timeout_sec = 10000 + ``` + + > **NOTE**: + > Codex Agent when running in Xcode has a limited PATH by default. The above example should work for most users but if you find the server doesn't start or is not available, it's likely because npx is not found so you might have to adjust the above configuration accordingly. + +
+
+ +
+ Xcode (Claude Code Agent) +
+ + Requires Xcode 26.3 or later. Claude Code agent must be installed and configured in Xcode Settings -> Intelligence -> Anthropic. + + Add to the end or replace the existing `mcpServers` object in Xcode's Claude Code agent config at: + `~/Library/Developer/Xcode/CodingAssistant/ClaudeAgentConfig/.claude.json` + + ```json + // ... rest of file ... + "mcpServers": { + "XcodeBuildMCP": { + "command": "/bin/zsh", + "args": [ + "-lc", + "PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"; nvm use --silent >/dev/null 2>&1 || true; npx -y xcodebuildmcp@latest mcp" + ] + } + } } -} -``` + ``` -**Available Workflows:** -- `device` (7 tools) - iOS Device Development -- `simulator` (12 tools) - iOS Simulator Development -- `simulator-management` (5 tools) - Simulator Management -- `swift-package` (6 tools) - Swift Package Manager -- `project-discovery` (5 tools) - Project Discovery -- `macos` (6 tools) - macOS Development -- `ui-testing` (11 tools) - UI Testing & Automation -- `logging` (4 tools) - Log Capture & Management -- `project-scaffolding` (2 tools) - Project Scaffolding -- `utilities` (1 tool) - Project Utilities -- `doctor` (1 tool) - System Doctor + > **NOTE**: + > Claude Code Agent when running in Xcode has a limited PATH by default. The above example should work for most users but if you find the server doesn't start or is not available, it's likely because npx is not found so you might have to adjust the above configuration accordingly. -## Session-aware opt-out +
+
-By default, XcodeBuildMCP uses a session-aware mode: the LLM (or client) sets shared defaults once (simulator, device, project/workspace, scheme, etc.), and all tools reuse them—similar to choosing a scheme and simulator in Xcode’s UI so you don’t repeat them on every action. This cuts context bloat not just in each call payload, but also in the tool schemas themselves (those parameters don’t have to be described on every tool). +
-If you prefer the older, explicit style where each tool requires its own parameters, set `XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS=true`. This restores the legacy schemas with per-call parameters while still honoring any session defaults you choose to set. +For other installation options see [Getting Started](docs/GETTING_STARTED.md). -Example MCP client configuration: -```json -"XcodeBuildMCP": { - ... - "env": { - "XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS": "true" - } -} -``` - -Leave this unset for the streamlined session-aware experience; enable it to force explicit parameters on each tool call. - -## Code Signing for Device Deployment +## Requirements -For device deployment features to work, code signing must be properly configured in Xcode **before** using XcodeBuildMCP device tools: +- macOS 14.5 or later +- Xcode 16.x or later +- Node.js 18.x or later (not required for Homebrew installation) -1. Open your project in Xcode -2. Select your project target -3. Go to "Signing & Capabilities" tab -4. Configure "Automatically manage signing" and select your development team -5. Ensure a valid provisioning profile is selected +## Skills -> **Note**: XcodeBuildMCP cannot configure code signing automatically. This initial setup must be done once in Xcode, after which the MCP device tools can build, install, and test apps on physical devices. +XcodeBuildMCP now includes two optional agent skills: -## Troubleshooting +- **MCP Skill**: Primes the agent with instructions on how to use the MCP server's tools (optional when using the MCP server). -If you encounter issues with XcodeBuildMCP, the doctor tool can help identify the problem by providing detailed information about your environment and dependencies. +- **CLI Skill**: Primes the agent with instructions on how to navigate the CLI (recommended when using the CLI). -### Doctor Tool -The doctor tool is a standalone utility that checks your system configuration and reports on the status of all dependencies required by XcodeBuildMCP. It's particularly useful when reporting issues. +To install, copy and paste the command below into a terminal and follow the on-screen instructions. ```bash -# Run the doctor tool using npx -npx --package xcodebuildmcp@latest xcodebuildmcp-doctor +curl -fsSL https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v2.0.7/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh ``` -The doctor tool will output comprehensive information about: +For further information on how to install the skill, see: [docs/SKILLS.md](docs/SKILLS.md) -- System and Node.js environment -- Xcode installation and configuration -- Required dependencies (xcodebuild, AXe, etc.) -- Environment variables affecting XcodeBuildMCP -- Feature availability status +## Notes -When reporting issues on GitHub, please include the full output from the doctor tool to help with troubleshooting. +- XcodeBuildMCP requests xcodebuild to skip macro validation to avoid errors when building projects that use Swift Macros. +- Device tools require code signing to be configured in Xcode. See [docs/DEVICE_CODE_SIGNING.md](docs/DEVICE_CODE_SIGNING.md). ## Privacy -This project uses [Sentry](https://sentry.io/) for error monitoring and diagnostics. Sentry helps us track issues, crashes, and unexpected errors to improve the reliability and stability of XcodeBuildMCP. +XcodeBuildMCP uses Sentry for internal runtime error telemetry only. For details and opt-out instructions, see [docs/PRIVACY.md](docs/PRIVACY.md). -### What is sent to Sentry? -- Only error-level logs and diagnostic information are sent to Sentry by default. -- Error logs may include details such as error messages, stack traces, and (in some cases) file paths or project names. You can review the sources in this repository to see exactly what is logged. +## CLI -### Opting Out of Sentry -- If you do not wish to send error logs to Sentry, you can opt out by setting the environment variable `XCODEBUILDMCP_SENTRY_DISABLED=true`. +XcodeBuildMCP provides a unified command-line interface. The `mcp` subcommand starts the MCP server, while all other commands provide direct terminal access to tools: -Example MCP client configuration: -```json -"XcodeBuildMCP": { - ... - "env": { - "XCODEBUILDMCP_SENTRY_DISABLED": "true" - } -} -``` - -## Demos - -### Autonomously fixing build errors in Cursor -![xcodebuildmcp3](https://github.com/user-attachments/assets/173e6450-8743-4379-a76c-de2dd2b678a3) - -### Utilising the new UI automation and screen capture features +```bash +# Install globally +npm install -g xcodebuildmcp@latest -![xcodebuildmcp4](https://github.com/user-attachments/assets/17300a18-f47a-428a-aad3-dc094859c1b2) +# Start the MCP server (for MCP clients) +xcodebuildmcp mcp -### Building and running iOS app in Claude Desktop -https://github.com/user-attachments/assets/e3c08d75-8be6-4857-b4d0-9350b26ef086 +# List available tools +xcodebuildmcp tools -## Contributing +# Build for simulator +xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj +``` -[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/) [![Node.js](https://img.shields.io/badge/node->=18.x-brightgreen.svg)](https://nodejs.org/) +The CLI uses a per-workspace daemon for stateful operations (log capture, debugging, etc.) that auto-starts when needed. See [docs/CLI.md](docs/CLI.md) for full documentation. -Contributions are welcome! Here's how you can help improve XcodeBuildMCP. +## Documentation -See our documentation for development: -- [CONTRIBUTING](docs/CONTRIBUTING.md) - Contribution guidelines and development setup -- [CODE_QUALITY](docs/CODE_QUALITY.md) - Code quality standards, linting, and architectural rules -- [TESTING](docs/TESTING.md) - Testing principles and patterns -- [ARCHITECTURE](docs/ARCHITECTURE.md) - System architecture and design principles +- Getting started: [docs/GETTING_STARTED.md](docs/GETTING_STARTED.md) +- CLI usage: [docs/CLI.md](docs/CLI.md) +- Configuration and options: [docs/CONFIGURATION.md](docs/CONFIGURATION.md) +- Tools reference: [docs/TOOLS.md](docs/TOOLS.md) +- Troubleshooting: [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) +- Privacy: [docs/PRIVACY.md](docs/PRIVACY.md) +- Skills: [docs/SKILLS.md](docs/SKILLS.md) +- Contributing: [docs/dev/CONTRIBUTING.md](docs/dev/CONTRIBUTING.md) ## Licence diff --git a/XcodeBuildMCP.code-workspace b/XcodeBuildMCP.code-workspace index d6f91c86..54025ef2 100644 --- a/XcodeBuildMCP.code-workspace +++ b/XcodeBuildMCP.code-workspace @@ -1,13 +1,13 @@ { - "folders": [ - { - "path": "." - }, - { - "path": "../XcodeBuildMCP-iOS-Template" - }, - { - "path": "../XcodeBuildMCP-macOS-Template" - } - ] -} \ No newline at end of file + "folders": [ + { + "path": ".", + }, + { + "path": "../XcodeBuildMCP-iOS-Template", + }, + { + "path": "../XcodeBuildMCP-macOS-Template", + }, + ], +} diff --git a/build-plugins/plugin-discovery.js b/build-plugins/plugin-discovery.js deleted file mode 100644 index c420f264..00000000 --- a/build-plugins/plugin-discovery.js +++ /dev/null @@ -1,283 +0,0 @@ -import { readdirSync, readFileSync, existsSync } from 'fs'; -import { join } from 'path'; -import path from 'path'; - -export function createPluginDiscoveryPlugin() { - return { - name: 'plugin-discovery', - setup(build) { - // Generate the workflow loaders file before build starts - build.onStart(async () => { - try { - await generateWorkflowLoaders(); - await generateResourceLoaders(); - } catch (error) { - console.error('Failed to generate loaders:', error); - throw error; - } - }); - }, - }; -} - -async function generateWorkflowLoaders() { - const pluginsDir = path.resolve(process.cwd(), 'src/mcp/tools'); - - if (!existsSync(pluginsDir)) { - throw new Error(`Plugins directory not found: ${pluginsDir}`); - } - - // Scan for workflow directories - const workflowDirs = readdirSync(pluginsDir, { withFileTypes: true }) - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => dirent.name); - - const workflowLoaders = {}; - const workflowMetadata = {}; - - for (const dirName of workflowDirs) { - const dirPath = join(pluginsDir, dirName); - const indexPath = join(dirPath, 'index.ts'); - - // Check if workflow has index.ts file - if (!existsSync(indexPath)) { - console.warn(`Skipping ${dirName}: no index.ts file found`); - continue; - } - - // Try to extract workflow metadata from index.ts - try { - const indexContent = readFileSync(indexPath, 'utf8'); - const metadata = extractWorkflowMetadata(indexContent); - - if (metadata) { - // Find all tool files in this workflow directory - const toolFiles = readdirSync(dirPath, { withFileTypes: true }) - .filter((dirent) => dirent.isFile()) - .map((dirent) => dirent.name) - .filter( - (name) => - (name.endsWith('.ts') || name.endsWith('.js')) && - name !== 'index.ts' && - name !== 'index.js' && - !name.endsWith('.test.ts') && - !name.endsWith('.test.js') && - name !== 'active-processes.ts', // Special exclusion for swift-package - ); - - // Generate dynamic loader function that loads workflow and all its tools - workflowLoaders[dirName] = generateWorkflowLoader(dirName, toolFiles); - workflowMetadata[dirName] = metadata; - - console.log( - `✅ Discovered workflow: ${dirName} - ${metadata.name} (${toolFiles.length} tools)`, - ); - } else { - console.warn(`⚠️ Skipping ${dirName}: invalid workflow metadata`); - } - } catch (error) { - console.warn(`⚠️ Error processing ${dirName}:`, error); - } - } - - // Generate the content for generated-plugins.ts - const generatedContent = await generatePluginsFileContent(workflowLoaders, workflowMetadata); - - // Write to the generated file - const outputPath = path.resolve(process.cwd(), 'src/core/generated-plugins.ts'); - - const fs = await import('fs'); - await fs.promises.writeFile(outputPath, generatedContent, 'utf8'); - - console.log(`🔧 Generated workflow loaders for ${Object.keys(workflowLoaders).length} workflows`); -} - -function generateWorkflowLoader(workflowName, toolFiles) { - const toolImports = toolFiles - .map((file, index) => { - const toolName = file.replace(/\.(ts|js)$/, ''); - return `const tool_${index} = await import('../mcp/tools/${workflowName}/${toolName}.js').then(m => m.default)`; - }) - .join(';\n '); - - const toolExports = toolFiles - .map((file, index) => { - const toolName = file.replace(/\.(ts|js)$/, ''); - return `'${toolName}': tool_${index}`; - }) - .join(',\n '); - - return `async () => { - const { workflow } = await import('../mcp/tools/${workflowName}/index.js'); - ${toolImports ? toolImports + ';\n ' : ''} - return { - workflow, - ${toolExports ? toolExports : ''} - }; - }`; -} - -function extractWorkflowMetadata(content) { - try { - // Simple regex to extract workflow export object - const workflowMatch = content.match(/export\s+const\s+workflow\s*=\s*({[\s\S]*?});/); - - if (!workflowMatch) { - return null; - } - - const workflowObj = workflowMatch[1]; - - // Extract name - const nameMatch = workflowObj.match(/name\s*:\s*['"`]([^'"`]+)['"`]/); - if (!nameMatch) return null; - - // Extract description - const descMatch = workflowObj.match(/description\s*:\s*['"`]([\s\S]*?)['"`]/); - if (!descMatch) return null; - - const result = { - name: nameMatch[1], - description: descMatch[1], - }; - - return result; - } catch (error) { - console.warn('Failed to extract workflow metadata:', error); - return null; - } -} - -async function generatePluginsFileContent(workflowLoaders, workflowMetadata) { - const loaderEntries = Object.entries(workflowLoaders) - .map(([key, loader]) => { - // Indent the loader function properly - const indentedLoader = loader - .split('\n') - .map((line, index) => (index === 0 ? ` '${key}': ${line}` : ` ${line}`)) - .join('\n'); - return indentedLoader; - }) - .join(',\n'); - - const metadataEntries = Object.entries(workflowMetadata) - .map(([key, metadata]) => { - const metadataJson = JSON.stringify(metadata, null, 4) - .split('\n') - .map((line) => ` ${line}`) - .join('\n'); - return ` '${key}': ${metadataJson.trim()}`; - }) - .join(',\n'); - - const content = `// AUTO-GENERATED - DO NOT EDIT -// This file is generated by the plugin discovery esbuild plugin - -// Generated based on filesystem scan -export const WORKFLOW_LOADERS = { -${loaderEntries} -}; - -export type WorkflowName = keyof typeof WORKFLOW_LOADERS; - -// Optional: Export workflow metadata for quick access -export const WORKFLOW_METADATA = { -${metadataEntries} -}; -`; - return formatGenerated(content); -} - -async function generateResourceLoaders() { - const resourcesDir = path.resolve(process.cwd(), 'src/mcp/resources'); - - if (!existsSync(resourcesDir)) { - console.log('Resources directory not found, skipping resource generation'); - return; - } - - // Scan for resource files - const resourceFiles = readdirSync(resourcesDir, { withFileTypes: true }) - .filter((dirent) => dirent.isFile()) - .map((dirent) => dirent.name) - .filter( - (name) => - (name.endsWith('.ts') || name.endsWith('.js')) && - !name.endsWith('.test.ts') && - !name.endsWith('.test.js') && - !name.startsWith('__'), // Exclude test directories - ); - - const resourceLoaders = {}; - - for (const fileName of resourceFiles) { - const resourceName = fileName.replace(/\.(ts|js)$/, ''); - - // Generate dynamic loader for this resource - resourceLoaders[resourceName] = `async () => { - const module = await import('../mcp/resources/${resourceName}.js'); - return module.default; - }`; - - console.log(`✅ Discovered resource: ${resourceName}`); - } - - // Generate the content for generated-resources.ts - const generatedContent = await generateResourcesFileContent(resourceLoaders); - - // Write to the generated file - const outputPath = path.resolve(process.cwd(), 'src/core/generated-resources.ts'); - - const fs = await import('fs'); - await fs.promises.writeFile(outputPath, generatedContent, 'utf8'); - - console.log(`🔧 Generated resource loaders for ${Object.keys(resourceLoaders).length} resources`); -} - -async function generateResourcesFileContent(resourceLoaders) { - const loaderEntries = Object.entries(resourceLoaders) - .map(([key, loader]) => ` '${key}': ${loader}`) - .join(',\n'); - - const content = `// AUTO-GENERATED - DO NOT EDIT -// This file is generated by the plugin discovery esbuild plugin - -export const RESOURCE_LOADERS = { -${loaderEntries} -}; - -export type ResourceName = keyof typeof RESOURCE_LOADERS; -`; - return formatGenerated(content); -} - -async function formatGenerated(content) { - try { - const { resolve } = await import('node:path'); - const { pathToFileURL } = await import('node:url'); - const prettier = await import('prettier'); - let config = (await prettier.resolveConfig(process.cwd())) ?? null; - if (!config) { - try { - const configUrl = pathToFileURL(resolve(process.cwd(), '.prettierrc.js')).href; - const configModule = await import(configUrl); - config = configModule.default ?? configModule; - } catch { - config = null; - } - } - const options = { - semi: true, - trailingComma: 'all', - singleQuote: true, - printWidth: 100, - tabWidth: 2, - endOfLine: 'auto', - ...config, - parser: 'typescript', - }; - return prettier.format(content, options); - } catch { - return content; - } -} diff --git a/build-plugins/plugin-discovery.ts b/build-plugins/plugin-discovery.ts deleted file mode 100644 index 4e507f21..00000000 --- a/build-plugins/plugin-discovery.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { Plugin } from 'esbuild'; -import { readdirSync, readFileSync, existsSync } from 'fs'; -import { join } from 'path'; -import path from 'path'; - -export interface WorkflowMetadata { - name: string; - description: string; -} - -export function createPluginDiscoveryPlugin(): Plugin { - return { - name: 'plugin-discovery', - setup(build) { - // Generate the workflow loaders file before build starts - build.onStart(async () => { - try { - await generateWorkflowLoaders(); - await generateResourceLoaders(); - } catch (error) { - console.error('Failed to generate loaders:', error); - throw error; - } - }); - }, - }; -} - -export async function generateWorkflowLoaders(): Promise { - const pluginsDir = path.resolve(process.cwd(), 'src/mcp/tools'); - - if (!existsSync(pluginsDir)) { - throw new Error(`Plugins directory not found: ${pluginsDir}`); - } - - // Scan for workflow directories - const workflowDirs = readdirSync(pluginsDir, { withFileTypes: true }) - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => dirent.name); - - const workflowLoaders: Record = {}; - const workflowMetadata: Record = {}; - - for (const dirName of workflowDirs) { - const dirPath = join(pluginsDir, dirName); - const indexPath = join(dirPath, 'index.ts'); - - // Check if workflow has index.ts file - if (!existsSync(indexPath)) { - console.warn(`Skipping ${dirName}: no index.ts file found`); - continue; - } - - // Try to extract workflow metadata from index.ts - try { - const indexContent = readFileSync(indexPath, 'utf8'); - const metadata = extractWorkflowMetadata(indexContent); - - if (metadata) { - // Find all tool files in this workflow directory - const toolFiles = readdirSync(dirPath, { withFileTypes: true }) - .filter((dirent) => dirent.isFile()) - .map((dirent) => dirent.name) - .filter( - (name) => - (name.endsWith('.ts') || name.endsWith('.js')) && - name !== 'index.ts' && - name !== 'index.js' && - !name.endsWith('.test.ts') && - !name.endsWith('.test.js') && - name !== 'active-processes.ts', - ); - - workflowLoaders[dirName] = generateWorkflowLoader(dirName, toolFiles); - workflowMetadata[dirName] = metadata; - - console.log( - `✅ Discovered workflow: ${dirName} - ${metadata.name} (${toolFiles.length} tools)`, - ); - } else { - console.warn(`⚠️ Skipping ${dirName}: invalid workflow metadata`); - } - } catch (error) { - console.warn(`⚠️ Error processing ${dirName}:`, error); - } - } - - // Generate the content for generated-plugins.ts - const generatedContent = await generatePluginsFileContent(workflowLoaders, workflowMetadata); - - // Write to the generated file - const outputPath = path.resolve(process.cwd(), 'src/core/generated-plugins.ts'); - - const fs = await import('fs'); - await fs.promises.writeFile(outputPath, generatedContent, 'utf8'); - - console.log(`🔧 Generated workflow loaders for ${Object.keys(workflowLoaders).length} workflows`); -} - -function generateWorkflowLoader(workflowName: string, toolFiles: string[]): string { - const toolImports = toolFiles - .map((file, index) => { - const toolName = file.replace(/\.(ts|js)$/, ''); - return `const tool_${index} = await import('../mcp/tools/${workflowName}/${toolName}.js').then(m => m.default)`; - }) - .join(';\n '); - - const toolExports = toolFiles - .map((file, index) => { - const toolName = file.replace(/\.(ts|js)$/, ''); - return `'${toolName}': tool_${index}`; - }) - .join(',\n '); - - return `async () => { - const { workflow } = await import('../mcp/tools/${workflowName}/index.js'); - ${toolImports ? toolImports + ';\n ' : ''} - return { - workflow, - ${toolExports ? toolExports : ''} - }; - }`; -} - -function extractWorkflowMetadata(content: string): WorkflowMetadata | null { - try { - // Simple regex to extract workflow export object - const workflowMatch = content.match(/export\s+const\s+workflow\s*=\s*({[\s\S]*?});/); - - if (!workflowMatch) { - return null; - } - - const workflowObj = workflowMatch[1]; - - // Extract name - const nameMatch = workflowObj.match(/name\s*:\s*['"`]([^'"`]+)['"`]/); - if (!nameMatch) return null; - - // Extract description - const descMatch = workflowObj.match(/description\s*:\s*['"`]([\s\S]*?)['"`]/); - if (!descMatch) return null; - - return { - name: nameMatch[1], - description: descMatch[1], - }; - } catch (error) { - console.warn('Failed to extract workflow metadata:', error); - return null; - } -} - -async function generatePluginsFileContent( - workflowLoaders: Record, - workflowMetadata: Record, -): Promise { - const loaderEntries = Object.entries(workflowLoaders) - .map(([key, loader]) => { - const indentedLoader = loader - .split('\n') - .map((line, index) => (index === 0 ? ` '${key}': ${line}` : ` ${line}`)) - .join('\n'); - return indentedLoader; - }) - .join(',\n'); - - const metadataEntries = Object.entries(workflowMetadata) - .map(([key, metadata]) => { - const metadataJson = JSON.stringify(metadata, null, 4) - .split('\n') - .map((line) => ` ${line}`) - .join('\n'); - return ` '${key}': ${metadataJson.trim()}`; - }) - .join(',\n'); - - const content = `// AUTO-GENERATED - DO NOT EDIT -// This file is generated by the plugin discovery esbuild plugin - -// Generated based on filesystem scan -export const WORKFLOW_LOADERS = { -${loaderEntries} -}; - -export type WorkflowName = keyof typeof WORKFLOW_LOADERS; - -// Optional: Export workflow metadata for quick access -export const WORKFLOW_METADATA = { -${metadataEntries} -}; -`; - return formatGenerated(content); -} - -export async function generateResourceLoaders(): Promise { - const resourcesDir = path.resolve(process.cwd(), 'src/mcp/resources'); - - if (!existsSync(resourcesDir)) { - console.log('Resources directory not found, skipping resource generation'); - return; - } - - const resourceFiles = readdirSync(resourcesDir, { withFileTypes: true }) - .filter((dirent) => dirent.isFile()) - .map((dirent) => dirent.name) - .filter( - (name) => - (name.endsWith('.ts') || name.endsWith('.js')) && - !name.endsWith('.test.ts') && - !name.endsWith('.test.js') && - !name.startsWith('__'), - ); - - const resourceLoaders: Record = {}; - - for (const fileName of resourceFiles) { - const resourceName = fileName.replace(/\.(ts|js)$/, ''); - resourceLoaders[resourceName] = `async () => { - const module = await import('../mcp/resources/${resourceName}.js'); - return module.default; - }`; - - console.log(`✅ Discovered resource: ${resourceName}`); - } - - const generatedContent = await generateResourcesFileContent(resourceLoaders); - const outputPath = path.resolve(process.cwd(), 'src/core/generated-resources.ts'); - - const fs = await import('fs'); - await fs.promises.writeFile(outputPath, generatedContent, 'utf8'); - - console.log(`🔧 Generated resource loaders for ${Object.keys(resourceLoaders).length} resources`); -} - -async function generateResourcesFileContent( - resourceLoaders: Record, -): Promise { - const loaderEntries = Object.entries(resourceLoaders) - .map(([key, loader]) => ` '${key}': ${loader}`) - .join(',\n'); - - const content = `// AUTO-GENERATED - DO NOT EDIT -// This file is generated by the plugin discovery esbuild plugin - -export const RESOURCE_LOADERS = { -${loaderEntries} -}; - -export type ResourceName = keyof typeof RESOURCE_LOADERS; -`; - return formatGenerated(content); -} - -async function formatGenerated(content: string): Promise { - try { - const { resolve } = await import('node:path'); - const { pathToFileURL } = await import('node:url'); - const prettier = await import('prettier'); - let config = (await prettier.resolveConfig(process.cwd())) ?? null; - if (!config) { - try { - const configUrl = pathToFileURL(resolve(process.cwd(), '.prettierrc.js')).href; - const configModule = await import(configUrl); - config = (configModule as { default?: unknown }).default ?? configModule; - } catch { - config = null; - } - } - const options = { - semi: true, - trailingComma: 'all' as const, - singleQuote: true, - printWidth: 100, - tabWidth: 2, - endOfLine: 'auto' as const, - ...(config as Record | null), - parser: 'typescript', - }; - return prettier.format(content, options); - } catch { - return content; - } -} diff --git a/build-plugins/tsconfig.json b/build-plugins/tsconfig.json deleted file mode 100644 index c7c2ce0a..00000000 --- a/build-plugins/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "module": "ESNext", - "target": "ES2022", - "outDir": "../build-plugins-dist", - "rootDir": ".", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true - }, - "include": ["**/*.ts"], - "exclude": ["node_modules", "dist"] -} \ No newline at end of file diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 00000000..60df0c0a --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,20 @@ +schemaVersion: 1 +enabledWorkflows: ['simulator', 'ui-automation', 'debugging'] +experimentalWorkflowDiscovery: false +disableSessionDefaults: false +incrementalBuildsEnabled: false +sessionDefaults: + projectPath: './MyApp.xcodeproj' # xor workspacePath + workspacePath: './MyApp.xcworkspace' # xor projectPath + scheme: 'MyApp' + configuration: 'Debug' + simulatorName: 'iPhone 16' # xor simulatorId + simulatorId: '' # xor simulatorName + deviceId: '' + useLatestOS: true + arch: 'arm64' + suppressWarnings: false + derivedDataPath: './.derivedData' + preferXcodebuild: false + platform: 'iOS' + bundleId: 'io.sentry.myapp' diff --git a/docs/CLI.md b/docs/CLI.md new file mode 100644 index 00000000..51d227b3 --- /dev/null +++ b/docs/CLI.md @@ -0,0 +1,263 @@ +# XcodeBuildMCP CLI + +`xcodebuildmcp` is a unified command-line interface that provides both an MCP server and direct tool access via a first-class CLI. + +Use `xcodebuildmcp` CLI to invoke tools or start the MCP server by passing the `mcp` argument. + +## Installation + +```bash +# Install globally +npm install -g xcodebuildmcp@beta + +# Or run via npx +npx xcodebuildmcp@beta --help +``` + +## Quick Start + +```bash +# List available tools +xcodebuildmcp tools + +# View CLI help +xcodebuildmcp --help + +# View tool help +xcodebuildmcp --help +``` + +## Tool Options + +Each tool supports `--help` for detailed options: + +```bash +xcodebuildmcp simulator build --help +``` + +Common patterns: + +```bash +# Pass options as flags +xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj + +# Pass complex options as JSON +xcodebuildmcp simulator build --json '{"scheme": "MyApp", "projectPath": "./MyApp.xcodeproj"}' + +# Control output format +xcodebuildmcp simulator list --output json +``` + +## Examples + +### Build and Run Workflow + +```bash +# Discover projects +xcodebuildmcp simulator discover-projects + +# List schemes +xcodebuildmcp simulator list-schemes --project-path ./MyApp.xcodeproj + +# Build +xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj + +# Boot simulator +xcodebuildmcp simulator boot --simulator-name "iPhone 17 Pro" + +# Install and launch +xcodebuildmcp simulator install --simulator-id --app-path ./build/MyApp.app + +xcodebuildmcp simulator launch-app --simulator-id --bundle-id io.sentry.MyApp + +# Or... build and run in a single command +xcodebuildmcp simulator build-and-run --scheme MyApp --project-path ./MyApp.xcodeproj +``` + +### Log Capture Workflow + +```bash +# Start log capture +xcodebuildmcp logging start-simulator-log-capture --simulator-id --bundle-id io.sentry.MyApp + +> Log capture started successfully. Session ID: 51e2142a-1a99-442a-af01-0586540043df. + +# Stop and retrieve logs +xcodebuildmcp logging stop-simulator-log-capture --session-id +``` + +### Testing + +```bash +# Run all tests +xcodebuildmcp simulator test --scheme MyAppTests --project-path ./MyApp.xcodeproj + +# Run with specific simulator +xcodebuildmcp simulator test --scheme MyAppTests --simulator-name "iPhone 17 Pro" +``` + +For a full list of workflows and tools, see [TOOLS-CLI.md](TOOLS-CLI.md). + +## Configuration + +The CLI respects the same configuration as the MCP server: + +```yaml +# .xcodebuildmcp/config.yaml +sessionDefaults: + scheme: MyApp + projectPath: ./MyApp.xcodeproj + simulatorName: iPhone 17 Pro + +enabledWorkflows: + - simulator + - project-discovery +``` + +See [CONFIGURATION.md](CONFIGURATION.md) for the full schema. + +## Environment Variables + +| Variable | Description | +|----------|-------------| +| `XCODEBUILDMCP_SOCKET` | Override socket path for all commands | +| `XCODEBUILDMCP_DAEMON_IDLE_TIMEOUT_MS` | Daemon idle timeout in ms (default `600000`, set `0` to disable) | +| `XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS` | Disable session defaults | + +## CLI vs MCP Mode + +| Feature | CLI (`xcodebuildmcp `) | MCP (`xcodebuildmcp mcp`) | +|---------|------------------------------|---------------------------| +| Invocation | Direct terminal | MCP client (Claude, etc.) | +| Session state | Stateless direct + daemon for stateful tools | In-process | +| Use case | Scripts, CI, manual | AI-assisted development | +| Configuration | Same config.yaml | Same config.yaml | + +Both share the same underlying tool implementations. + +## Per-Workspace Daemon + +The CLI uses a per-workspace daemon architecture only when needed: + +- Stateless tools run directly in the CLI process. +- Stateful tools route through the daemon (auto-started as needed). +- Dynamic `xcode-ide` bridge tools are a special-case daemon-backed path for persistent bridge sessions. + +### How It Works + +- **Workspace identity**: The workspace root is determined by the location of `.xcodebuildmcp/config.yaml`, or falls back to the current directory. +- **Socket location**: Each daemon runs on a Unix socket at `~/.xcodebuildmcp/daemons//daemon.sock` +- **Auto-start**: The daemon starts automatically when you invoke a stateful tool - no manual setup required. +- **Auto-shutdown**: The daemon exits after 10 minutes of inactivity, but only when there are no active stateful sessions (log capture, debugging, video capture, background swift-package processes). + +### Daemon Commands + +```bash +# Check daemon status for current workspace +xcodebuildmcp daemon status + +# Manually start the daemon +xcodebuildmcp daemon start + +# Stop the daemon +xcodebuildmcp daemon stop + +# Restart the daemon +xcodebuildmcp daemon restart + +# List all daemons across workspaces +xcodebuildmcp daemon list + +# List in JSON format +xcodebuildmcp daemon list --json +``` + +### Daemon Status Output + +``` +Daemon Status: Running + PID: 12345 + Workspace: /Users/you/Projects/MyApp + Socket: /Users/you/.xcodebuildmcp/daemons/c5da0cbe19a7/daemon.sock + Started: 2024-01-15T10:30:00.000Z + Tools: 94 + Workflows: (default) +``` + +### Daemon List Output + +``` +Daemons: + + [running] c5da0cbe19a7 + Workspace: /Users/you/Projects/MyApp + PID: 12345 + Started: 2024-01-15T10:30:00.000Z + Version: 1.15.0 + + [stale] a1b2c3d4e5f6 + Workspace: /Users/you/Projects/OldProject + PID: 99999 + Started: 2024-01-14T08:00:00.000Z + Version: 1.14.0 + +Total: 2 (1 running, 1 stale) +``` + +## Stateful vs Stateless Tools + +### Stateless Tools (run in-process) +Most tools run directly without the daemon: +- `build`, `test`, `clean` +- `list`, `list-schemes`, `discover-projects` +- `boot`, `install`, `launch-app` etc. + +### Stateful Tools (require daemon) +Some tools maintain state and route through the daemon: +- Log capture: `start-simulator-log-capture`, `stop-simulator-log-capture` +- Video recording: `record-video` +- Debugging: `attach`, `continue`, etc. +- Background processes: `run`, `stop` + +When you invoke a stateful tool, the daemon auto-starts if needed. + +## Global Options + +| Option | Description | +|--------|-------------| +| `--socket ` | Override the daemon socket path (hidden) | +| `-h, --help` | Show help | +| `-v, --version` | Show version | + +## Troubleshooting + +### Daemon won't start + +```bash +# Check for stale sockets +xcodebuildmcp daemon list + +# Force restart +xcodebuildmcp daemon restart + +# Run in foreground to see logs +xcodebuildmcp daemon start --foreground +``` + +### Tool timeout + +Increase the daemon startup timeout: + +```bash +# Default is 5 seconds +export XCODEBUILDMCP_STARTUP_TIMEOUT_MS=10000 +``` + +### Socket permission errors + +The socket directory (`~/.xcodebuildmcp/daemons/`) should have mode 0700. If you encounter permission issues: + +```bash +chmod 700 ~/.xcodebuildmcp +chmod -R 700 ~/.xcodebuildmcp/daemons +``` diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 00000000..4adcf620 --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,334 @@ +# Configuration + +XcodeBuildMCP reads configuration from a project config file. The config file is optional but provides deterministic, repo-scoped behavior for every session. + +## Contents + +- [Config file](#config-file) +- [Session defaults](#session-defaults) +- [Workflow selection](#workflow-selection) +- [Build settings](#build-settings) +- [Debugging and logging](#debugging-and-logging) +- [UI automation](#ui-automation) +- [Templates](#templates) +- [Telemetry](#telemetry) +- [Quick reference](#quick-reference) +- [Environment variables (legacy)](#environment-variables-legacy) + +--- + +## Config file + +Create a config file at your workspace root: + +``` +/.xcodebuildmcp/config.yaml +``` + +Minimal example: + +```yaml +schemaVersion: 1 +enabledWorkflows: ["simulator", "ui-automation", "debugging"] +``` + +Full example options: + +```yaml +schemaVersion: 1 + +# Workflow selection +enabledWorkflows: ["simulator", "ui-automation", "debugging"] +experimentalWorkflowDiscovery: false + +# Session defaults +disableSessionDefaults: false +sessionDefaults: + projectPath: "./MyApp.xcodeproj" + scheme: "MyApp" + configuration: "Debug" + simulatorName: "iPhone 16" + platform: "iOS" + useLatestOS: true + arch: "arm64" + suppressWarnings: true + derivedDataPath: "./.derivedData" + preferXcodebuild: false + bundleId: "io.sentry.myapp" + +# Build settings +incrementalBuildsEnabled: false + +# Debugging +debug: false +debuggerBackend: "dap" +dapRequestTimeoutMs: 30000 +dapLogEvents: false +launchJsonWaitMs: 8000 + +# UI automation +uiDebuggerGuardMode: "error" +axePath: "/opt/axe/bin/axe" + +# Templates +iosTemplatePath: "/path/to/ios/templates" +iosTemplateVersion: "v1.2.3" +macosTemplatePath: "/path/to/macos/templates" +macosTemplateVersion: "v1.2.3" +``` + +The `schemaVersion` field is required and currently only supports `1`. + +--- + +## Session defaults + +Session defaults allow you to set shared values once (project, scheme, simulator, etc.) that all tools reuse automatically. This reduces token usage and ensures consistent behavior. + +### Enabling and disabling + +Session defaults are enabled by default. To disable them: + +```yaml +disableSessionDefaults: true +``` + +When disabled, agents must pass explicit parameters on every tool call (legacy behavior). + +### Setting defaults from config + +Seed defaults at server startup by adding a `sessionDefaults` block: + +```yaml +sessionDefaults: + projectPath: "./MyApp.xcodeproj" + scheme: "MyApp" + configuration: "Debug" + simulatorName: "iPhone 16" +``` + +### Setting defaults from an agent + +Agents can call `session_set_defaults` at runtime. By default these are stored in memory only. To persist them to the config file, the agent sets `persist: true`. + +### Session defaults reference + +| Option | Type | Description | +|--------|------|-------------| +| `projectPath` | string | Path to `.xcodeproj` file. Mutually exclusive with `workspacePath`. | +| `workspacePath` | string | Path to `.xcworkspace` file. Takes precedence if both are set. | +| `scheme` | string | Build scheme name. | +| `configuration` | string | Build configuration (e.g., `Debug`, `Release`). | +| `simulatorName` | string | Simulator name (e.g., `iPhone 16`). Mutually exclusive with `simulatorId`. | +| `simulatorId` | string | Simulator UUID. Takes precedence if both are set. | +| `deviceId` | string | Physical device UUID. | +| `platform` | string | Target platform (e.g., `iOS`, `macOS`, `watchOS`, `tvOS`, `visionOS`). | +| `useLatestOS` | boolean | Use the latest available OS version for the simulator. | +| `arch` | string | Build architecture (e.g., `arm64`, `x86_64`). | +| `suppressWarnings` | boolean | Suppress compiler warnings in build output. | +| `derivedDataPath` | string | Custom path for derived data. | +| `preferXcodebuild` | boolean | Use `xcodebuild` instead of the experimental incremental build support (xcodemake). Only applies when incremental builds are enabled. Is designed for agent use to self correct after failed xcodemake build attempts. | +| `bundleId` | string | App bundle identifier. | + +### Mutual exclusivity rules + +- If both `projectPath` and `workspacePath` are set, `workspacePath` wins. +- If both `simulatorId` and `simulatorName` are set, `simulatorId` wins. + +--- + +## Workflow selection + +Workflows determine which tools are available. By default only the `simulator` workflow is loaded. + +```yaml +enabledWorkflows: ["simulator", "ui-automation", "debugging"] +``` + +See [TOOLS.md](TOOLS.md) for available workflows and their tools. + +To access Xcode IDE tools (Xcode 26+ `xcrun mcpbridge`), enable `xcode-ide`. This workflow exposes `xcode_ide_list_tools` and `xcode_ide_call_tool` for MCP clients. See [XCODE_IDE_MCPBRIDGE.md](XCODE_IDE_MCPBRIDGE.md). + +### Experimental workflow discovery + +Enables a `manage-workflows` tool that agents can use to add/remove workflows at runtime. + +```yaml +experimentalWorkflowDiscovery: true +``` + +> [!IMPORTANT] +> Requires client support for tools changed notifications. At the time of writing, Cursor, Claude Code, and Codex do not support this. + +--- + +## Build settings + +### Incremental builds + +Enable incremental builds to speed up repeated builds: + +```yaml +incrementalBuildsEnabled: true +``` + +> [!IMPORTANT] +> Incremental builds are experimental and may not work for all projects. Agents can bypass this setting per-call if needed. + +--- + +## Debugging and logging + +### Debug logging + +Enable debug logging and the doctor diagnostic tool: + +```yaml +debug: true +``` + +### Debugger backend + +Select the debugger backend: + +```yaml +debuggerBackend: "dap" # or "lldb-cli" +``` + +Default is `dap`. Changing this is not generally recommended. + +### DAP settings + +Tune the DAP backend: + +```yaml +dapRequestTimeoutMs: 30000 # default: 30000 +dapLogEvents: true # default: false +``` + +### Device log capture + +Control how long to wait for devicectl JSON output: + +```yaml +launchJsonWaitMs: 8000 # default: 8000 +``` + +--- + +## UI automation + +### Debugger guard + +Block UI automation tools when the debugger is paused to prevent failures: + +```yaml +uiDebuggerGuardMode: "error" # "error" | "warn" | "off" +``` + +Default is `error`. + +### AXe binary + +UI automation and simulator video capture require AXe, which is bundled by default. To use a different version: + +```yaml +axePath: "/opt/axe/bin/axe" +``` + +See the [AXe repository](https://github.com/cameroncooke/axe) for more information. + +--- + +## Templates + +The scaffold tools pull templates from GitHub. Override the default locations and versions: + +```yaml +iosTemplatePath: "/path/to/ios/templates" +iosTemplateVersion: "v1.2.3" +macosTemplatePath: "/path/to/macos/templates" +macosTemplateVersion: "v1.2.3" +``` + +Default templates: +- iOS: [XcodeBuildMCP-iOS-Template](https://github.com/getsentry/XcodeBuildMCP-iOS-Template) +- macOS: [XcodeBuildMCP-macOS-Template](https://github.com/getsentry/XcodeBuildMCP-macOS-Template) + +--- + +## Telemetry + +By default, only internal XcodeBuildMCP runtime failures are sent to Sentry. User-domain errors (such as project build/test/config failures) are not sent. To disable telemetry entirely: + +```yaml +# Environment variable only (no config.yaml option) +# XCODEBUILDMCP_SENTRY_DISABLED=true +``` + +See [PRIVACY.md](PRIVACY.md) for more information. + +Notes: +- Sentry SDK logging is enabled for internal observability. +- Only explicitly opted-in internal logs are forwarded to Sentry; standard console logs are not auto-forwarded. +- Tool arguments and tool outputs are not captured by MCP wrapper telemetry (`recordInputs: false`, `recordOutputs: false`). + +--- + +## Quick reference + +| Option | Type | Default | +|--------|------|---------| +| `schemaVersion` | number | Required (`1`) | +| `enabledWorkflows` | string[] | `["simulator"]` | +| `experimentalWorkflowDiscovery` | boolean | `false` | +| `disableSessionDefaults` | boolean | `false` | +| `sessionDefaults` | object | `{}` | +| `incrementalBuildsEnabled` | boolean | `false` | +| `debug` | boolean | `false` | +| `debuggerBackend` | string | `"dap"` | +| `dapRequestTimeoutMs` | number | `30000` | +| `dapLogEvents` | boolean | `false` | +| `launchJsonWaitMs` | number | `8000` | +| `uiDebuggerGuardMode` | string | `"error"` | +| `axePath` | string | Bundled | +| `iosTemplatePath` | string | GitHub default | +| `iosTemplateVersion` | string | Bundled version | +| `macosTemplatePath` | string | GitHub default | +| `macosTemplateVersion` | string | Bundled version | + +--- + +## Environment variables (legacy) + +Environment variables are supported for backwards compatibility but the config file is preferred. + +| Config option | Environment variable | +|---------------|---------------------| +| `enabledWorkflows` | `XCODEBUILDMCP_ENABLED_WORKFLOWS` (comma-separated) | +| `experimentalWorkflowDiscovery` | `XCODEBUILDMCP_EXPERIMENTAL_WORKFLOW_DISCOVERY` | +| `disableSessionDefaults` | `XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS` | +| `incrementalBuildsEnabled` | `INCREMENTAL_BUILDS_ENABLED` | +| `debug` | `XCODEBUILDMCP_DEBUG` | +| `debuggerBackend` | `XCODEBUILDMCP_DEBUGGER_BACKEND` | +| `dapRequestTimeoutMs` | `XCODEBUILDMCP_DAP_REQUEST_TIMEOUT_MS` | +| `dapLogEvents` | `XCODEBUILDMCP_DAP_LOG_EVENTS` | +| `launchJsonWaitMs` | `XBMCP_LAUNCH_JSON_WAIT_MS` | +| `uiDebuggerGuardMode` | `XCODEBUILDMCP_UI_DEBUGGER_GUARD_MODE` | +| `axePath` | `XCODEBUILDMCP_AXE_PATH` | +| `iosTemplatePath` | `XCODEBUILDMCP_IOS_TEMPLATE_PATH` | +| `iosTemplateVersion` | `XCODEBUILD_MCP_IOS_TEMPLATE_VERSION` | +| `macosTemplatePath` | `XCODEBUILDMCP_MACOS_TEMPLATE_PATH` | +| `macosTemplateVersion` | `XCODEBUILD_MCP_MACOS_TEMPLATE_VERSION` | +| (no config option) | `XCODEBUILDMCP_SENTRY_DISABLED` | + +Config file takes precedence over environment variables when both are set. + +--- + +## Related docs + +- Session defaults: [SESSION_DEFAULTS.md](SESSION_DEFAULTS.md) +- Tools reference: [TOOLS.md](TOOLS.md) +- Privacy and telemetry: [PRIVACY.md](PRIVACY.md) +- Troubleshooting: [TROUBLESHOOTING.md](TROUBLESHOOTING.md) diff --git a/docs/DAP_BACKEND_IMPLEMENTATION_PLAN.md b/docs/DAP_BACKEND_IMPLEMENTATION_PLAN.md new file mode 100644 index 00000000..75b10f82 --- /dev/null +++ b/docs/DAP_BACKEND_IMPLEMENTATION_PLAN.md @@ -0,0 +1,571 @@ +## Goal & constraints (grounded in current code) + +Implement a real **`lldb-dap` Debug Adapter Protocol backend** that plugs into the existing debugger architecture without changing MCP tool names/schemas. The DAP backend remains **opt-in only** via `XCODEBUILDMCP_DEBUGGER_BACKEND=dap` (current selection logic in `src/utils/debugger/debugger-manager.ts`). + +Key integration points already in place: + +- **Backend contract**: `src/utils/debugger/backends/DebuggerBackend.ts` +- **Backend selection & session lifecycle**: `src/utils/debugger/debugger-manager.ts` +- **MCP tool surface area**: `src/mcp/tools/debugging/*` (attach, breakpoints, stack, variables, command, detach) +- **Subprocess patterns**: `src/utils/execution/interactive-process.ts` (interactive, piped stdio, test-safe default spawner) +- **DI/test safety**: defaults throw under Vitest (`getDefaultCommandExecutor`, `getDefaultInteractiveSpawner`) +- **Docs baseline**: `docs/DAP_BACKEND_IMPLEMENTATION_PLAN.md`, `docs/DEBUGGING_ARCHITECTURE.md` + +--- + +## Implementation status (current) + +Implemented modules and behavior (as of this document): + +- DAP protocol and transport: `src/utils/debugger/dap/types.ts`, `src/utils/debugger/dap/transport.ts` +- Adapter discovery: `src/utils/debugger/dap/adapter-discovery.ts` +- Backend implementation: `src/utils/debugger/backends/dap-backend.ts` +- Conditional breakpoints: backend-level support via `DebuggerBackend.addBreakpoint(..., { condition })` +- Tool updates: `src/mcp/tools/debugging/debug_breakpoint_add.ts` passes conditions to backend +- Health check: `doctor` now reports `lldb-dap` availability +- Tests: DAP transport framing, backend mapping, and debugger manager selection tests + +### MCP tool → DAP request mapping (current) + +| MCP tool | DebuggerManager call | DAP requests | +| --- | --- | --- | +| `debug_attach_sim` | `createSession` → `attach` | `initialize` → `attach` → `configurationDone` | +| `debug_lldb_command` | `runCommand` | `evaluate` (context: `repl`) | +| `debug_stack` | `getStack` | `threads` → `stackTrace` | +| `debug_variables` | `getVariables` | `threads` → `stackTrace` → `scopes` → `variables` | +| `debug_breakpoint_add` | `addBreakpoint` | `setBreakpoints` / `setFunctionBreakpoints` | +| `debug_breakpoint_remove` | `removeBreakpoint` | `setBreakpoints` / `setFunctionBreakpoints` | +| `debug_detach` | `detach` | `disconnect` | + +### Breakpoint strategy (current) + +- Breakpoints are stateful: DAP removal re-applies `setBreakpoints`/`setFunctionBreakpoints` with the remaining list. +- Conditions are passed as part of the breakpoint request in both backends: + - DAP: `breakpoints[].condition` or `functionBreakpoints[].condition` + - LLDB CLI: `breakpoint modify -c "" ` + +--- + +## Architectural decisions to make (explicit) + +### 1) Spawn model: one `lldb-dap` process per debug session +**Decision**: Each `DebuggerManager.createSession()` creates a new backend instance, which owns a single `lldb-dap` subprocess for the lifetime of that session. + +- Aligns with current LLDB CLI backend (one long-lived interactive `lldb` per session). +- Keeps multi-session support (`DebuggerManager.sessions: Map`) straightforward. + +### 2) Transport abstraction: DAP framing + request correlation in a dedicated module +**Decision**: Build a dedicated DAP transport that: +- implements `Content-Length` framing +- correlates requests/responses by `seq` +- emits DAP events + +This keeps `DapBackend` focused on **mapping MCP tool operations → DAP requests**. + +### 3) Breakpoint conditions support: move condition handling into the backend API +**Decision**: Extend internal debugger API to support conditional breakpoints *without relying on* “LLDB command follow-ups” (which are CLI-specific). + +This avoids depending on DAP `evaluate` for breakpoint modification and keeps semantics consistent across backends. + +--- + +## Implementation plan (by component / file) + +### A) Add DAP protocol & transport layer + +#### New files + +##### 1) `src/utils/debugger/dap/types.ts` +Define minimal DAP types used by the backend (not a full spec). + +Example types (illustrative, not exhaustive): + +```ts +export type DapRequest = { + seq: number; + type: 'request'; + command: string; + arguments?: C; +}; + +export type DapResponse = { + seq: number; + type: 'response'; + request_seq: number; + success: boolean; + command: string; + message?: string; + body?: B; +}; + +export type DapEvent = { + seq: number; + type: 'event'; + event: string; + body?: B; +}; +``` + +Also define bodies used in mapping: +- `InitializeResponseBody` (capabilities) +- `ThreadsResponseBody` +- `StackTraceResponseBody` +- `ScopesResponseBody` +- `VariablesResponseBody` +- `SetBreakpointsResponseBody` +- `EvaluateResponseBody` +- event bodies: `StoppedEventBody`, `OutputEventBody`, `TerminatedEventBody` + +**Side effects / impact**: none outside debugger subsystem; ensures type safety inside DAP modules. + +--- + +##### 2) `src/utils/debugger/dap/transport.ts` +Implement DAP over stdio. + +**Dependencies / imports** +- `node:events` (EventEmitter) or a small typed emitter pattern +- `src/utils/execution/index.ts` for `InteractiveSpawner` and `InteractiveProcess` types +- `src/utils/logging/index.ts` for `log` +- `src/utils/CommandExecutor.ts` type (for adapter discovery helper if kept here) + +**Core responsibilities** +- Spawn adapter process (or accept an already spawned `InteractiveProcess`) +- Parse stdout stream into discrete DAP messages using `Content-Length` framing +- Maintain: + - `nextSeq: number` + - `pending: Map` keyed by request `seq` +- Expose: + - `sendRequest(command, args, opts?) => Promise` + - event subscription: `onEvent(handler)` or `on('event', ...)` + - lifecycle: `dispose()` (must not throw) + +**Key function signatures** + +```ts +export type DapTransportOptions = { + spawner: InteractiveSpawner; + adapterCommand: string[]; // e.g. ['xcrun', 'lldb-dap'] or [resolvedPath] + env?: Record; + cwd?: string; + logPrefix?: string; +}; + +export class DapTransport { + constructor(opts: DapTransportOptions); + + sendRequest( + command: string, + args?: A, + opts?: { timeoutMs?: number }, + ): Promise; + + onEvent(handler: (evt: DapEvent) => void): () => void; + + dispose(): void; // best-effort, never throw +} +``` + +**Framing logic** +- Maintain an internal `Buffer`/string accumulator for stdout. +- Repeatedly: + - find `\r\n\r\n` + - parse headers for `Content-Length` + - wait until body bytes are available + - `JSON.parse` body into `{ type: 'response' | 'event' | 'request' }` + +**Process failure handling** +- On adapter `exit`/`error`, reject all pending requests with a clear error (and include exit detail). +- Log stderr output at `debug` level; do **not** feed stderr into framing. + +**Concurrency** +- Transport supports multiple in-flight requests concurrently (DAP allows it). +- Backend may still serialize higher-level operations if stateful. + +**Side effects** +- Add a long-lived child process per session. +- Requires careful memory management in the framing buffer (ensure you slice consumed bytes). + +--- + +### B) Adapter discovery (`xcrun --find lldb-dap`) + +#### New helper (recommended) +##### 3) `src/utils/debugger/dap/adapter-discovery.ts` (new) +**Purpose**: centralize resolution and produce actionable errors when DAP is explicitly selected but unavailable. + +**Uses** +- `CommandExecutor` to run `xcrun --find lldb-dap` +- `log` for diagnostics +- throw a `DependencyError` (from `src/utils/errors.ts`) or plain `Error` with a consistent message + +Example signature: + +```ts +import type { CommandExecutor } from '../../execution/index.ts'; + +export async function resolveLldbDapCommand(opts: { + executor: CommandExecutor; +}): Promise; +// returns e.g. ['xcrun', 'lldb-dap'] OR [absolutePath] +``` + +**Design choice** +- Returning `['xcrun','lldb-dap']` is simplest (no dependency on parsing). +- Returning `[absolutePath]` provides a stronger “tool exists” guarantee. + +**Impact** +- Enables a clean error message early in session creation. +- Keeps `DapBackend` simpler. + +--- + +### C) Implement `DapBackend` (current) + +#### Modify file: `src/utils/debugger/backends/dap-backend.ts` + +**Implemented** as a real backend that: +- discovers adapter (`resolveLldbDapCommand`) +- creates `DapTransport` +- performs DAP handshake (`initialize`) +- attaches by PID (`attach`) +- maps backend interface methods to DAP requests + +**Dependencies** +- `DapTransport` +- `resolveLldbDapCommand` +- `getDefaultCommandExecutor` and `getDefaultInteractiveSpawner` (production defaults) +- `log` +- existing backend interface/types + +**Constructor / factory** +Update `createDapBackend()` to accept injectable deps, mirroring the CLI backend’s injection style. + +```ts +export async function createDapBackend(opts?: { + executor?: CommandExecutor; + spawner?: InteractiveSpawner; + requestTimeoutMs?: number; +}): Promise; +``` + +> This is critical for tests because defaults throw under Vitest. + +**Session state to maintain inside `DapBackend`** +- `transport: DapTransport | null` +- `attached: boolean` +- `lastStoppedThreadId: number | null` +- `cachedThreads: { id: number; name?: string }[] | null` (optional) +- breakpoint registry: + - `breakpointsById: Map` + - for DAP “remove breakpoint”, you must re-issue `setBreakpoints`/`setFunctionBreakpoints` with the updated list, so also keep: + - `fileLineBreakpointsByFile: Map>` + - `functionBreakpoints: Array<{ name: string; condition?: string; id?: number }>` +- optional cached stack frames from the last `stackTrace` call (for variables lookup) + +**Backend lifecycle mapping** +- `attach()`: + 1) spawn `lldb-dap` + 2) `initialize` + 3) `attach` with pid (+ waitFor mapping) + 4) `configurationDone` if required by lldb-dap behavior (plan for it even if no-op) + 5) mark attached + +- `detach()` + - send `disconnect` with `terminateDebuggee: false` (do not kill app) + - dispose transport / kill process + +- `dispose()` + - best-effort cleanup; **must not throw** (important because `DebuggerManager.createSession` calls dispose to clean up on attach failure) + +**Method mappings (MCP tools → DebuggerManager → DapBackend)** + +1) `runCommand(command: string, opts?)` +- Map to DAP `evaluate` with `context: 'repl'` +- Return string output from `EvaluateResponse.body.result` and/or `body.output` +- If adapter doesn’t support command-style repl evaluation, return a clear error message suggesting `lldb-cli` backend. + +2) `getStack(opts?: { threadIndex?: number; maxFrames?: number })` +- DAP sequence: + - `threads` + - select thread: + - if a `stopped` event has a `threadId`, prefer that when `threadIndex` is undefined + - else map `threadIndex` to array index (document this) + - `stackTrace({ threadId, startFrame: 0, levels: maxFrames })` +- Format output as readable text (LLDB-like) to keep tool behavior familiar: + - `frame #: at :` +- If stackTrace fails due to running state, return a helpful error: + - “Process is running; pause or hit a breakpoint to fetch stack.” + +3) `getVariables(opts?: { frameIndex?: number })` +- DAP sequence: + - resolve thread as above + - `stackTrace` to get frames + - choose frame by `frameIndex` (default 0) + - `scopes({ frameId })` + - for each scope: `variables({ variablesReference })` +- Format output as text with sections per scope: + - `Locals:\n x = 1\n y = ...` + +4) `addBreakpoint(spec: BreakpointSpec, opts?: { condition?: string })` +- For `file-line`: + - update `fileLineBreakpointsByFile[file]` + - call `setBreakpoints({ source: { path: file }, breakpoints: [{ line, condition }] })` + - parse returned `breakpoints[]` to find matching line and capture `id` +- For `function`: + - update `functionBreakpoints` + - call `setFunctionBreakpoints({ breakpoints: [{ name, condition }] })` +- Return `BreakpointInfo`: + - `id` must be a number (from DAP breakpoint id; if missing, generate a synthetic id and store mapping, but prefer real id) + - `rawOutput` can be a pretty JSON snippet or a short text summary + +5) `removeBreakpoint(id: number)` +- Look up spec in `breakpointsById` +- Remove it from the corresponding registry +- Re-issue `setBreakpoints` or `setFunctionBreakpoints` with the remaining breakpoints +- Return text confirmation + +**Important: DAP vs existing condition flow** +- Today `debug_breakpoint_add` sets condition by issuing an LLDB command after creation. +- With the above, condition becomes part of breakpoint creation and removal logic, backend-agnostic. + +--- + +### D) Internal API adjustment for conditional breakpoints (recommended) + +#### Modify: `src/utils/debugger/backends/DebuggerBackend.ts` +Update signature: + +```ts +addBreakpoint(spec: BreakpointSpec, opts?: { condition?: string }): Promise; +``` + +#### Modify: `src/utils/debugger/debugger-manager.ts` +Update method: + +```ts +async addBreakpoint( + id: string | undefined, + spec: BreakpointSpec, + opts?: { condition?: string }, +): Promise +``` + +Pass `opts` through to `backend.addBreakpoint`. + +**Impact** +- Requires updating both backends + the tool call site. +- Improves cross-backend compatibility and avoids “DAP evaluate must support breakpoint modify”. + +#### Modify: `src/utils/debugger/backends/lldb-cli-backend.ts` +Implement condition via LLDB command internally after breakpoint creation (current behavior, just moved): + +- after parsing breakpoint id: + - if `opts?.condition`, run `breakpoint modify -c "" ` + +This keeps condition support identical for LLDB CLI users. + +--- + +### E) Update MCP tool logic to use new breakpoint API + +#### Modify: `src/mcp/tools/debugging/debug_breakpoint_add.ts` +Change logic to pass `condition` into `ctx.debugger.addBreakpoint(...)` and remove the follow-up `breakpoint modify ...` command. + +**Before** +- call `addBreakpoint()` +- if condition, call `runCommand("breakpoint modify ...")` + +**After** +- call `addBreakpoint(sessionId, spec, { condition })` +- no extra `runCommand` required + +**Impact / side effects** +- Output remains the same shape, but the “rawOutput” content for DAP may differ (acceptable). +- Improves backend portability. + +--- + +### F) Backend selection & opt-in behavior (already mostly correct) + +#### Modify (optional but recommended): `src/utils/debugger/debugger-manager.ts` +Keep selection rules but improve failure clarity: + +- If backend kind is `dap`, and adapter discovery fails, throw an error like: + - `DAP backend selected but lldb-dap not found. Ensure Xcode is installed and xcrun can locate lldb-dap, or set XCODEBUILDMCP_DEBUGGER_BACKEND=lldb-cli.` + +Also ensure that dispose failures do not mask attach failures: +- in `createSession` catch, wrap `dispose()` in its own try/catch (even if backend should not throw). + +--- + +### G) Diagnostics / “doctor” integration (validation surface) + +#### Modify: `src/mcp/tools/doctor/doctor.ts` (not shown in provided contents) +Add a DAP capability line: +- `lldb-dap available: yes/no` +- if env selects dap, include a prominent warning/error section when missing + +Implementation approach: +- reuse `CommandExecutor` and call `xcrun --find lldb-dap` +- do not fail doctor entirely if missing; just report + +**Side effects** +- Improves discoverability and reduces “mystery failures” when users opt into dap. + +--- + +## Concurrency & state management plan + +### Transport-level +- Fully concurrent in-flight DAP requests supported via: + - `seq` generation + - `pending` map keyed by `seq` +- Each request can set its own timeout (`timeoutMs`). + +### Backend-level +Use a serialized queue **only where state mutation occurs**, e.g.: +- updating breakpoint registries +- attach/detach transitions + +Pattern (same as LLDB CLI backend): + +```ts +private queue: Promise = Promise.resolve(); + +private enqueue(work: () => Promise): Promise { ... } +``` + +**Reasoning** +- Prevent races such as: + - addBreakpoint + removeBreakpoint in parallel, reissuing `setBreakpoints` inconsistently. + +--- + +## Error handling & logging strategy + +### Error taxonomy (pragmatic, consistent with current tools) +- Backend throws `Error` with clear messages. +- MCP tools already catch and wrap errors via `createErrorResponse(...)`. + +### Where to log +- `DapTransport`: + - `log('debug', ...)` for raw events (optionally gated by env) + - `log('error', ...)` on process exit while requests are pending +- `DapBackend`: + - minimal `info` logs on attach/detach + - `debug` logs for request mapping (command names, not full payloads unless opted in) + +### New optional env flags (config plan) +Document these (no need to require them): +- `XCODEBUILDMCP_DAP_REQUEST_TIMEOUT_MS` (default to 30_000) +- `XCODEBUILDMCP_DAP_LOG_EVENTS=true` (default false) + +--- + +## Tests (architecture-aware, DI-compliant) + +Even though this is “testing”, it directly impacts design because default spawners/executors throw under Vitest. + +### 1) Add a first-class mock interactive spawner utility +#### Modify: `src/test-utils/mock-executors.ts` +Add: + +```ts +export function createMockInteractiveSpawner(script: { + // map writes -> stdout/stderr emissions, or a programmable fake +}): InteractiveSpawner; +``` + +This avoids ad-hoc manual mocks and matches the project’s “approved mocks live in test-utils” philosophy. + +### 2) DAP framing tests +New: `src/utils/debugger/dap/__tests__/transport-framing.test.ts` +- Feed partial header/body chunks into the transport parser using `PassThrough` streams behind a mock InteractiveProcess. +- Assert: + - correct parsing across chunk boundaries + - multiple messages in one chunk + - invalid Content-Length handling + +### 3) Backend mapping tests (no real lldb-dap) +New: `src/utils/debugger/backends/__tests__/dap-backend.test.ts` +- Use `createMockExecutor()` to fake adapter discovery. +- Use `createMockInteractiveSpawner()` to simulate an adapter that returns scripted DAP responses: + - initialize → success + - attach → success + - threads/stackTrace/scopes/variables → stable fixtures +- Validate: + - `getStack()` formatting + - `getVariables()` formatting + - breakpoint add/remove registry behavior + - `dispose()` never throws + +### 4) DebuggerManager selection test +New: `src/utils/debugger/__tests__/debugger-manager-dap.test.ts` +- Inject a custom `backendFactory` that returns a fake backend (or the scripted DAP backend) and verify: + - env selection + - attach failure triggers dispose + - current session behavior unchanged + +--- + +## Docs updates (grounded in existing docs) + +### 1) Update `docs/DAP_BACKEND_IMPLEMENTATION_PLAN.md` +Replace/extend the existing outline with the following: +- finalized module list (`dap/types.ts`, `dap/transport.ts`, discovery helper) +- breakpoint strategy (stateful re-issue `setBreakpoints`) +- explicit mapping table per MCP tool + +### 2) Update `docs/DEBUGGING_ARCHITECTURE.md` +Add a section “DAP Backend (lldb-dap)”: +- how it’s selected (opt-in) +- differences vs LLDB CLI (structured stack/variables, breakpoint reapplication) +- note about process state (stack/variables usually require stopped context) +- explain that conditional breakpoints are implemented backend-side + +--- + +## Configuration & validation steps (manual / operational) + +### Validation steps (local) +1. Ensure `lldb-dap` is discoverable: + - `xcrun --find lldb-dap` +2. Run server with DAP enabled: + - `XCODEBUILDMCP_DEBUGGER_BACKEND=dap node build/cli.js mcp` +3. Use existing MCP tool flow: + - `debug_attach_sim` (attach by PID or bundleId) + - `debug_breakpoint_add` (with condition) + - trigger breakpoint (or pause via `debug_lldb_command` if implemented via evaluate) + - `debug_stack`, `debug_variables` + - `debug_detach` + +### Expected behavioral constraints to document +- If the target is running and no stop context exists, DAP `stackTrace`/`variables` may fail; return guidance in tool output (“pause or set breakpoint”). + +--- + +## Summary of files modified / added + +### Add +- `src/utils/debugger/dap/types.ts` +- `src/utils/debugger/dap/transport.ts` +- `src/utils/debugger/dap/adapter-discovery.ts` (recommended) + +### Modify +- `src/utils/debugger/backends/dap-backend.ts` (real implementation) +- `src/utils/debugger/backends/DebuggerBackend.ts` (add breakpoint condition option) +- `src/utils/debugger/backends/lldb-cli-backend.ts` (support condition via new opts) +- `src/utils/debugger/debugger-manager.ts` (pass-through opts; optional improved error handling) +- `src/mcp/tools/debugging/debug_breakpoint_add.ts` (use backend-level condition support) +- `src/mcp/tools/doctor/doctor.ts` (report `lldb-dap` availability) +- `docs/DAP_BACKEND_IMPLEMENTATION_PLAN.md` +- `docs/DEBUGGING_ARCHITECTURE.md` +- `src/test-utils/mock-executors.ts` (add mock interactive spawner) + +--- + +## Critical “don’t miss” requirements +- `dispose()` in DAP backend and transport must be **best-effort and never throw** because `DebuggerManager.createSession()` will call dispose on attach failure. +- Avoid any use of default executors/spawners in tests; ensure `createDapBackend()` accepts injected `executor` + `spawner`. +- Breakpoint removal requires stateful re-application with `setBreakpoints` / `setFunctionBreakpoints`; plan for breakpoint registries from day one. diff --git a/docs/DEBUGGING_ARCHITECTURE.md b/docs/DEBUGGING_ARCHITECTURE.md new file mode 100644 index 00000000..88abb1fb --- /dev/null +++ b/docs/DEBUGGING_ARCHITECTURE.md @@ -0,0 +1,276 @@ +# Debugging Architecture + +This document describes how the simulator debugging tools are wired, how sessions are managed, +and how external tools (simctl, Simulator, LLDB, xcodebuild) are invoked. + +## Scope + +- Tools: `src/mcp/tools/debugging/*` +- Debugger subsystem: `src/utils/debugger/*` +- Execution and tool wiring: `src/utils/typed-tool-factory.ts`, `src/utils/execution/*` +- External invocation: `xcrun simctl`, `xcrun lldb`, `xcodebuild` + +## Registration and Wiring + +- Workflow discovery is automatic: `src/core/plugin-registry.ts` loads debugging tools via the + generated workflow loaders (`src/core/generated-plugins.ts`). +- Tool handlers are created with the typed tool factory: + - `createTypedToolWithContext` for standard tools (Zod validation + dependency injection). + - `createSessionAwareToolWithContext` for session-aware tools (merges session defaults and + validates requirements). +- Debugging tools inject a `DebuggerToolContext` that provides: + - `executor`: a `CommandExecutor` used for simctl and other command execution. + - `debugger`: a shared `DebuggerManager` instance. + +## Session Defaults and Validation + +- Session defaults live in `src/utils/session-store.ts` and are merged with user args by the + session-aware tool factory. +- `debug_attach_sim` is session-aware; it can omit `simulatorId`/`simulatorName` in the public + schema and rely on session defaults. +- The `XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS` env flag exposes legacy schemas that include all + parameters (no session default hiding). + +## Debug Session Lifecycle + +`DebuggerManager` owns lifecycle, state, and backend routing: + +Backend selection happens inside `DebuggerManager.createSession`: + +- Selection order: explicit `backend` argument -> `XCODEBUILDMCP_DEBUGGER_BACKEND` -> default `lldb-cli`. +- Env values: `lldb-cli`/`lldb` -> `lldb-cli`, `dap` -> `dap`, anything else throws. +- Backend factory: `defaultBackendFactory` maps `lldb-cli` to `createLldbCliBackend` and `dap` to + `createDapBackend`. A custom factory can be injected for tests or extensions. + +1. `debug_attach_sim` resolves simulator UUID and PID, then calls + `DebuggerManager.createSession`. +2. `DebuggerManager` creates a backend (default `lldb-cli`), attaches to the process, and stores + session metadata (id, simulatorId, pid, timestamps). +3. Debugging tools (`debug_lldb_command`, `debug_stack`, `debug_variables`, + `debug_breakpoint_add/remove`) look up the session (explicit id or current) and route commands + to the backend. +4. `debug_detach` calls `DebuggerManager.detachSession` to detach and dispose the backend. + +## Debug Session + Command Execution Flow + +Session lifecycle flow (text): + +1. Client calls `debug_attach_sim`. +2. `debug_attach_sim` resolves simulator UUID and PID, then calls `DebuggerManager.createSession`. +3. `DebuggerManager.createSession` resolves backend kind (explicit/env/default), instantiates the + backend, and calls `backend.attach`. +4. Command tools (`debug_lldb_command`, `debug_stack`, `debug_variables`) call + `DebuggerManager.runCommand`/`getStack`/`getVariables`, which route to the backend. +5. `debug_detach` calls `DebuggerManager.detachSession`, which invokes `backend.detach` and + `backend.dispose`. + +`LldbCliBackend.runCommand()` flow (text): + +1. Enqueue the command to serialize LLDB access. +2. Await backend readiness (`initialize` completed). +3. Write the command to the interactive process. +4. Write `script print("__XCODEBUILDMCP_DONE__")` to emit the sentinel marker. +5. Buffer stdout/stderr until the sentinel is detected. +6. Trim the buffer to the next prompt, sanitize output, and return the result. + +
+Sequence diagrams (Mermaid) + +```mermaid +sequenceDiagram + participant U as User/Client + participant A as debug_attach_sim + participant M as DebuggerManager + participant F as backendFactory + participant B as DebuggerBackend (lldb-cli|dap) + participant L as LldbCliBackend + participant P as InteractiveProcess (xcrun lldb) + + U->>A: debug_attach_sim(simulator*, bundleId|pid) + A->>A: determineSimulatorUuid(...) + A->>A: resolveSimulatorAppPid(...) (if bundleId) + A->>M: createSession({simulatorId, pid, waitFor}) + M->>M: resolveBackendKind(explicit/env/default) + M->>F: create backend(kind) + F-->>M: backend instance + M->>B: attach({pid, simulatorId, waitFor}) + alt kind == lldb-cli + B-->>L: (is LldbCliBackend) + L->>P: spawn xcrun lldb + initialize prompt/sentinel + else kind == dap + B-->>M: throws DAP_ERROR_MESSAGE + end + M-->>A: DebugSessionInfo {id, backend, ...} + A->>M: setCurrentSession(id) (optional) + U->>M: runCommand(id?, "thread backtrace") + M->>B: runCommand(...) + U->>M: detachSession(id?) + M->>B: detach() + M->>B: dispose() +``` + +```mermaid +sequenceDiagram + participant T as debug_lldb_command + participant M as DebuggerManager + participant L as LldbCliBackend + participant P as InteractiveProcess + participant S as stdout/stderr buffer + + T->>M: runCommand(sessionId?, command, {timeoutMs?}) + M->>L: runCommand(command) + L->>L: enqueue(work) + L->>L: await ready (initialize()) + L->>P: write(command + "\n") + L->>P: write('script print("__XCODEBUILDMCP_DONE__")\n') + P-->>S: stdout/stderr chunks + S-->>L: handleData() appends to buffer + L->>L: checkPending() finds sentinel + L->>L: slice output up to sentinel + L->>L: trim buffer to next prompt (if present) + L->>L: sanitizeOutput() + trimEnd() + L-->>M: output string + M-->>T: output string +``` + +
+ +## LLDB CLI Backend (Default) + +- Backend implementation: `src/utils/debugger/backends/lldb-cli-backend.ts`. +- Uses `InteractiveSpawner` from `src/utils/execution/interactive-process.ts` to keep a single + long-lived `xcrun lldb` process alive. +- Keeps LLDB state (breakpoints, selected frames, target) across tool calls without reattaching. + +### Internals: interactive process model + +- The backend spawns `xcrun lldb --no-lldbinit -o "settings set prompt "`. +- `InteractiveProcess.write()` is used to send commands; stdout and stderr are merged into a single + parse buffer. +- `InteractiveProcess.dispose()` closes stdin, removes listeners, and kills the process. + +### Prompt and sentinel protocol + +The backend uses a prompt + sentinel protocol to detect command completion reliably: + +- `LLDB_PROMPT = "XCODEBUILDMCP_LLDB> "` +- `COMMAND_SENTINEL = "__XCODEBUILDMCP_DONE__"` + +Definitions: + +- Prompt: the LLDB REPL prompt string that indicates LLDB is ready to accept the next command. +- Sentinel: a unique marker explicitly printed after each command to mark the end of that + command's output. + +Protocol flow: + +1. Startup: write `script print("__XCODEBUILDMCP_DONE__")` to prime the prompt parser. +2. For each command: + - Write the command. + - Write `script print("__XCODEBUILDMCP_DONE__")`. + - Read until the sentinel is observed, then trim up to the next prompt. + +The sentinel marks command completion, while the prompt indicates the REPL is ready for the next +command. + +Why both a prompt and a sentinel? + +- The sentinel is the explicit end-of-output marker; LLDB does not provide a reliable boundary for + arbitrary command output otherwise. +- The prompt is used to cleanly align the buffer for the next command after the sentinel is seen. + +Annotated example (simplified): + +1. Backend writes: + - `thread backtrace` + - `script print("__XCODEBUILDMCP_DONE__")` +2. LLDB emits (illustrative): + - `... thread backtrace output ...` + - `__XCODEBUILDMCP_DONE__` + - `XCODEBUILDMCP_LLDB> ` +3. Parser behavior: + - Sentinel marks the end of the command output payload. + - Prompt is used to trim the buffer, so the next command starts cleanly. + +### Output parsing and sanitization + +- `handleData()` appends to an internal buffer, and `checkPending()` scans for the sentinel regex + `/(^|\\r?\\n)__XCODEBUILDMCP_DONE__(\\r?\\n)/`. +- Output is the buffer up to the sentinel. The remainder is trimmed to the next prompt, if present. +- `sanitizeOutput()` removes prompt echoes, sentinel lines, the `script print(...)` lines, and empty + lines, then `runCommand()` returns `trimEnd()` output. + +### Concurrency model (queueing) + +- Commands are serialized through a promise queue to avoid interleaved output. +- `waitForSentinel()` rejects if a pending command exists, acting as a safety check. + +### Timeouts, errors, and disposal + +- Startup timeout: `DEFAULT_STARTUP_TIMEOUT_MS = 10_000`. +- Per-command timeout: `DEFAULT_COMMAND_TIMEOUT_MS = 30_000` (override via `runCommand` opts). +- Timeout failure clears the pending command and rejects the promise. +- `assertNoLldbError()` throws if `/error:/i` appears in output (simple heuristic). +- Process exit triggers `failPending(new Error(...))` so in-flight calls fail promptly. +- `runCommand()` rejects immediately if the backend is already disposed. + +### Testing and injection + +`getDefaultInteractiveSpawner()` throws in test environments to prevent spawning real interactive +processes. Tests should inject a mock `InteractiveSpawner` into `createLldbCliBackend()` or a custom +`DebuggerManager` backend factory. + +## DAP Backend (lldb-dap) + +- Implementation: `src/utils/debugger/backends/dap-backend.ts`, with protocol support in + `src/utils/debugger/dap/transport.ts`, `src/utils/debugger/dap/types.ts`, and adapter discovery in + `src/utils/debugger/dap/adapter-discovery.ts`. +- Selected via backend selection (explicit `backend`, `XCODEBUILDMCP_DEBUGGER_BACKEND=dap`, or default when unset). +- Adapter discovery uses `xcrun --find lldb-dap`; missing adapters raise a clear dependency error. +- One `lldb-dap` process is spawned per session; DAP framing and request correlation are handled + by `DapTransport`. +- Session handshake: `initialize` → `attach` → `configurationDone`. +- Breakpoints are stateful: adding/removing re-issues `setBreakpoints` or + `setFunctionBreakpoints` with the remaining list. Conditions are passed in the request body. +- Stack/variables typically require a stopped thread; the backend returns guidance if the process + is still running. + +## External Tool Invocation + +### simctl and Simulator + +- Simulator UUID resolution uses `xcrun simctl list devices available -j` + (`determineSimulatorUuid` in `src/utils/simulator-utils.ts`). +- PID lookup uses `xcrun simctl spawn launchctl list` + (`resolveSimulatorAppPid` in `src/utils/debugger/simctl.ts`). + +### LLDB + +- Attachment uses `xcrun lldb --no-lldbinit` in the interactive backend. +- Breakpoint conditions are applied internally by the LLDB CLI backend using + `breakpoint modify -c "" ` after creation. + +### xcodebuild (Build/Launch Context) + +- Debugging assumes a running simulator app. +- The typical flow is to build and launch via simulator tools (for example `build_sim`), + which uses `executeXcodeBuildCommand` to invoke `xcodebuild` (or `xcodemake` when enabled). +- After launch, `debug_attach_sim` attaches LLDB to the simulator process. + +## Typical Tool Flow + +1. Build and launch the app on a simulator (`build_sim`, `launch_app_sim`). +2. Attach LLDB (`debug_attach_sim`) using session defaults or explicit simulator + bundle ID. +3. Set breakpoints (`debug_breakpoint_add`), inspect stack/variables (`debug_stack`, + `debug_variables`), and issue arbitrary LLDB commands (`debug_lldb_command`). +4. Detach when done (`debug_detach`). + +## Integration Points Summary + +- Tool entrypoints: `src/mcp/tools/debugging/*` +- Session defaults: `src/utils/session-store.ts` +- Debug session manager: `src/utils/debugger/debugger-manager.ts` +- Backends: `src/utils/debugger/backends/lldb-cli-backend.ts` (default), + `src/utils/debugger/backends/dap-backend.ts` +- Interactive execution: `src/utils/execution/interactive-process.ts` (used by LLDB CLI backend) +- External commands: `xcrun simctl`, `xcrun lldb`, `xcodebuild` diff --git a/docs/DEMOS.md b/docs/DEMOS.md new file mode 100644 index 00000000..80bd1b12 --- /dev/null +++ b/docs/DEMOS.md @@ -0,0 +1,15 @@ +# Demos + +## Autonomously fixing build errors in Cursor +![xcodebuildmcp3](https://github.com/user-attachments/assets/173e6450-8743-4379-a76c-de2dd2b678a3) + +## Utilising the new UI automation and screen capture features +![xcodebuildmcp4](https://github.com/user-attachments/assets/17300a18-f47a-428a-aad3-dc094859c1b2) + +## Building and running iOS app in Claude Desktop +https://github.com/user-attachments/assets/e3c08d75-8be6-4857-b4d0-9350b26ef086 + +## Related docs +- Getting started: [GETTING_STARTED.md](GETTING_STARTED.md) +- Tools reference: [TOOLS.md](TOOLS.md) +- Session defaults: [SESSION_DEFAULTS.md](SESSION_DEFAULTS.md) diff --git a/docs/DEVICE_CODE_SIGNING.md b/docs/DEVICE_CODE_SIGNING.md new file mode 100644 index 00000000..f6bd7292 --- /dev/null +++ b/docs/DEVICE_CODE_SIGNING.md @@ -0,0 +1,18 @@ +# Device Code Signing + +For device deployment features to work, code signing must be configured in Xcode before using XcodeBuildMCP device tools. + +## One-time setup in Xcode +1. Open your project in Xcode. +2. Select your project target. +3. Open the "Signing & Capabilities" tab. +4. Enable "Automatically manage signing" and select your development team. +5. Ensure a valid provisioning profile is selected. + +## What XcodeBuildMCP can and cannot do +- Can build, install, launch, and test once signing is configured. +- Cannot configure code signing automatically. + +## Related docs +- Tools reference: [TOOLS.md](TOOLS.md) +- Troubleshooting: [TROUBLESHOOTING.md](TROUBLESHOOTING.md) diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md new file mode 100644 index 00000000..511259c0 --- /dev/null +++ b/docs/GETTING_STARTED.md @@ -0,0 +1,143 @@ +# Getting Started + +## Prerequisites +- macOS 14.5 or later +- Xcode 16.x or later +- Node.js 18.x or later (not required for Homebrew installation) + +## Choose Your Interface + +XcodeBuildMCP provides a unified CLI with two modes: + +| Command | Use Case | +|---------|----------| +| `xcodebuildmcp mcp` | Start MCP server for AI-assisted development | +| `xcodebuildmcp ` | Direct terminal usage, scripts, CI pipelines | + +Both share the same tools and configuration. + +## Installation + +Both methods give you the CLI and the MCP server. + +### Option A — Homebrew (no Node.js required) + +```bash +brew tap getsentry/xcodebuildmcp +brew install xcodebuildmcp +``` + +Use the CLI: +```bash +xcodebuildmcp --help +``` + +MCP client config: +```json +"XcodeBuildMCP": { + "command": "xcodebuildmcp", + "args": ["mcp"] +} +``` + +Upgrade later with `brew update && brew upgrade xcodebuildmcp`. + +### Option B — npm / npx (Node.js 18+) + +**For CLI use**, install globally: +```bash +npm install -g xcodebuildmcp@latest +xcodebuildmcp --help +``` + +**For MCP server only**, no global install needed — add directly to your client config: +```json +"XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] +} +``` + +Using `@latest` ensures clients resolve the newest version on each run. + +See [CLI.md](CLI.md) for full CLI documentation. + +## Project config (optional) +For deterministic session defaults and runtime configuration, add a config file at: + +```text +/.xcodebuildmcp/config.yaml +``` + +See [CONFIGURATION.md](CONFIGURATION.md) for the full schema and examples. + +## Client-specific configuration + +The examples below use npx (Option B). If you installed via Homebrew, replace the command with `"command": "xcodebuildmcp", "args": ["mcp"]` instead. + +### Cursor +Recommended (project-scoped): create `.cursor/mcp.json` in your project root: + +```json +{ + "mcpServers": { + "XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] + } + } +} +``` + +If you use a global Cursor config at `~/.cursor/mcp.json`, use this variant to align startup with the active workspace: + +```json +{ + "mcpServers": { + "XcodeBuildMCP": { + "command": "/bin/zsh", + "args": [ + "-lc", + "cd \"${workspaceFolder}\" && exec npx -y xcodebuildmcp@latest mcp" + ] + } + } +} +``` + +### OpenAI Codex CLI +Codex uses TOML for MCP configuration. Add this to `~/.codex/config.toml`: + +```toml +[mcp_servers.XcodeBuildMCP] +command = "npx" +args = ["-y", "xcodebuildmcp@latest", "mcp"] +env = { "XCODEBUILDMCP_SENTRY_DISABLED" = "false" } +``` + +If you see tool calls timing out (for example, `timed out awaiting tools/call after 60s`), increase the timeout: + +```toml +tool_timeout_sec = 600 +``` + +For more info see the OpenAI Codex configuration docs: +https://github.com/openai/codex/blob/main/docs/config.md#connecting-to-mcp-servers + +### Claude Code CLI +```bash +# Add XcodeBuildMCP server to Claude Code +claude mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp + +# Or with environment variables +claude mcp add XcodeBuildMCP -e XCODEBUILDMCP_SENTRY_DISABLED=false -- npx -y xcodebuildmcp@latest mcp +``` + +Note: XcodeBuildMCP requests xcodebuild to skip macro validation to avoid Swift Macro build errors. + +## Next steps +- Configuration options: [CONFIGURATION.md](CONFIGURATION.md) +- Session defaults and opt-out: [SESSION_DEFAULTS.md](SESSION_DEFAULTS.md) +- Tools reference: [TOOLS.md](TOOLS.md) +- CLI guide: [CLI.md](CLI.md) +- Troubleshooting: [TROUBLESHOOTING.md](TROUBLESHOOTING.md) diff --git a/docs/MIGRATION_V2.md b/docs/MIGRATION_V2.md new file mode 100644 index 00000000..9994c923 --- /dev/null +++ b/docs/MIGRATION_V2.md @@ -0,0 +1,246 @@ +# Migrating to XcodeBuildMCP v2.0.0 + +This guide covers breaking changes and required actions when upgrading from v1.x to v2.0.0. + +## Quick migration + +There are two breaking changes. Most users only need to do step 1. + +**1. Append `mcp` to your server launch command.** + +The `xcodebuildmcp` binary is now a CLI. To start the MCP server you must pass the `mcp` subcommand. Without it the MCP client will fail to connect. + +``` +# v1.x +npx -y xcodebuildmcp@latest + +# v2.0.0 +npx -y xcodebuildmcp@latest mcp +``` + +If you installed via a CLI command, remove and re-add: + +```bash +# Claude Code +claude mcp remove XcodeBuildMCP +claude mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp + +# Codex CLI +codex mcp remove XcodeBuildMCP +codex mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp +``` + +If you manage a configuration file directly, add `"mcp"` as the last entry in the `args` array. See [section 1](#1-mcp-server-launch-requires-the-mcp-subcommand) for file locations and examples. + +**2. Check your workflow configuration (if needed).** + +v2.0.0 defaults to loading only the `simulator` workflow instead of all workflows. If you already set `enabledWorkflows` or `XCODEBUILDMCP_ENABLED_WORKFLOWS`, nothing changes for you. + +If you relied on the previous default and need additional workflows, add them to `.xcodebuildmcp/config.yaml`: + +```yaml +schemaVersion: 1 +enabledWorkflows: + - simulator + - ui-automation + - debugging +``` + +See [section 2](#2-default-workflows-changed) for the rationale and full list of available workflows. + +--- + +# Detailed reference + +## 1. MCP server launch requires the `mcp` subcommand + +The `xcodebuildmcp` binary is now a CLI first. To start the MCP server, you must pass the `mcp` subcommand at the end of the launch command. Without it, the binary enters CLI mode and the MCP client will fail to connect. + +Wherever your v1.x setup invoked `xcodebuildmcp` (or `npx -y xcodebuildmcp@latest`), append `mcp` so the final token of the command is `mcp`. + +### Option A: Remove and re-add via CLI + +If you originally installed using a CLI command (e.g. `claude mcp add`, `codex mcp add`), remove the existing entry and re-add with the updated command. + +**Claude Code:** + +```bash +claude mcp remove XcodeBuildMCP +claude mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp +``` + +**Codex CLI:** + +```bash +codex mcp remove XcodeBuildMCP +codex mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp +``` + +### Option B: Edit the configuration file directly + +If you manage MCP servers through a configuration file, open it and add `"mcp"` as the last entry in the `args` array. + +Common file locations: + +| Client | Configuration file | +|--------|--------------------| +| Claude Code | `~/.claude.json` | +| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` | +| Cursor (project) | `.cursor/mcp.json` | +| Cursor (global) | `~/.cursor/mcp.json` | +| VS Code (project) | `.vscode/mcp.json` | +| VS Code (global) | `~/Library/Application Support/Code/User/mcp.json` | +| Windsurf | `~/.codeium/windsurf/mcp_config.json` | +| Trae | `~/Library/Application Support/Trae/User/mcp.json` | +| Codex CLI | `~/.codex/config.toml` | + +JSON example: + +```json +"XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] +} +``` + +TOML example (Codex CLI): + +```toml +[mcp_servers.XcodeBuildMCP] +command = "npx" +args = ["-y", "xcodebuildmcp@latest", "mcp"] +``` + +For the full set of client-specific examples, see the [README](../README.md#installation). + +--- + +## 2. Default workflows changed + +### Why this changed + +In v1.x, all workflows loaded by default, exposing 70+ tools. Every tool definition and its schema is sent to the LLM on each turn, consuming context window space throughout the session. While most LLM providers cache these tokens to reduce re-inference cost, they still occupy context that could otherwise be used for your code and conversation. + +v2.0.0 defaults to loading only the **`simulator` workflow** (21 tools). Simulator development is the most common use case, so this covers the majority of users while keeping the token footprint small. + +Other workflows -- `ui-automation`, `debugging`, `device`, `macos`, and more -- are now opt-in. This gives you direct control over the trade-off between tool breadth and token cost. Enable only what you need, when you need it. + +### Who is affected + +- **Already set** `enabledWorkflows` or `XCODEBUILDMCP_ENABLED_WORKFLOWS`? Nothing changes. +- **Relied on the default** (all workflows)? You will now only see simulator tools until you opt in to additional workflows. + +### How to enable additional workflows + +#### Config file (recommended) + +Create or update `.xcodebuildmcp/config.yaml` in your workspace root: + +```yaml +schemaVersion: 1 +enabledWorkflows: + - simulator + - device + - macos + - ui-automation + - debugging + - swift-package +``` + +#### Environment variable + +Set `XCODEBUILDMCP_ENABLED_WORKFLOWS` in your MCP client configuration: + +```json +"XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"], + "env": { + "XCODEBUILDMCP_ENABLED_WORKFLOWS": "simulator,device,macos,ui-automation,debugging,swift-package" + } +} +``` + +### Available workflows + +| Workflow ID | Description | +|-------------|-------------| +| `simulator` | iOS simulator build, run, test, screenshots, logs (default) | +| `device` | Physical device build, deploy, test, logs | +| `macos` | macOS build, run, test | +| `swift-package` | Swift Package Manager build, test, run | +| `ui-automation` | Tap, swipe, text input, UI hierarchy inspection | +| `debugging` | LLDB attach, breakpoints, variable inspection | +| `logging` | Simulator and device log capture | +| `simulator-management` | Boot, list, open simulators | +| `utilities` | Clean build products | +| `project-discovery` | Discover projects and workspaces | +| `project-scaffolding` | Create new projects from templates | +| `session-management` | Session defaults management | +| `doctor` | Diagnostic tool (requires debug mode) | +| `workflow-discovery` | Runtime workflow management (experimental) | +| `xcode-ide` | Xcode IDE MCP bridge proxy (Xcode 26.3+) | + +For full details on configuration options see [CONFIGURATION.md](CONFIGURATION.md). For session defaults (project, scheme, simulator, etc.) see [SESSION_DEFAULTS.md](SESSION_DEFAULTS.md). + +--- + +## 3. CLI and skills + +### xcodebuildmcp is now a CLI + +The `xcodebuildmcp` command can now be used directly in the terminal without an MCP client: + +```bash +# Install globally +npm install -g xcodebuildmcp@latest + +# List available tools +xcodebuildmcp tools + +# Build and run on simulator +xcodebuildmcp simulator build-and-run --scheme MyApp --project-path ./MyApp.xcodeproj + +# Take a screenshot +xcodebuildmcp simulator screenshot + +# Tap a button +xcodebuildmcp ui-automation tap --label "Submit" +``` + +See [CLI.md](CLI.md) for full documentation. + +### MCP vs CLI for coding agents + +**The MCP server is the recommended way to use XcodeBuildMCP with coding agents and will yield the best results.** The CLI is provided as an alternative and for scripting/CI use cases. + +Why MCP is preferred: + +- **Automatic tool discovery** -- tools are registered with the agent at session start, so the agent always knows what is available and how to call it. +- **Session defaults** -- the MCP server maintains stateful defaults (project path, scheme, simulator, etc.) across tool calls, so the agent does not have to recall and re-supply project details on every invocation. This significantly reduces errors. +- **Stateful operations** -- log capture, debugging sessions, and other long-running operations are fully managed by the server. + +The CLI avoids the per-turn context cost of MCP tool definitions since the agent invokes commands directly with no tool schemas to transmit. However, this comes with trade-offs: session defaults are not available in CLI mode, so the agent must pass all parameters explicitly on every call. Agents also tend to consume significant tokens repeatedly calling `--help` to re-discover commands and arguments. The CLI skill helps reduce this discovery overhead, but in practice MCP tools are almost always used more reliably by agents. + +### Agent skills (optional) + +v2.0.0 introduces optional skill files that prime your coding agent with usage instructions: + +- **CLI Skill** -- strongly recommended when using the CLI with a coding agent. +- **MCP Skill** -- optional when using the MCP server; gives the agent better context on available tools. + +Install via the interactive installer: + +```bash +curl -fsSL https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v2.0.0/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh +``` + +See [SKILLS.md](SKILLS.md) for more details. + +--- + +## 4. New project-level configuration file + +v2.0.0 adds support for a YAML config file at `.xcodebuildmcp/config.yaml`. This replaces the need for environment variables and provides deterministic, repo-scoped behavior. Environment variables still work but the config file takes precedence. + +See [CONFIGURATION.md](CONFIGURATION.md) for the full reference. diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md new file mode 100644 index 00000000..2e0262fe --- /dev/null +++ b/docs/OVERVIEW.md @@ -0,0 +1,24 @@ +# Overview + +XcodeBuildMCP is a Model Context Protocol (MCP) server that exposes Xcode operations as tools and resources for AI assistants and other MCP clients. It uses a plugin-based architecture with workflow groups so clients can access Xcode projects, simulators, devices, and Swift packages through a standard interface. + +## Why it exists +- Standardizes Xcode interactions for AI agents instead of ad-hoc command lines. +- Reduces configuration errors by providing purpose-built tools. +- Enables agents to build, inspect errors, and iterate autonomously. + +## What it can do +- Xcode project discovery, build, test, and clean. +- Simulator and device app lifecycle management. +- Swift Package Manager build, test, and run. +- UI automation, screenshots, and video capture. +- Log capture and system diagnostics. +- Debugger attach, breakpoints, stack, variables, and LLDB command execution. + +See the full tool catalog in [TOOLS.md](TOOLS.md). + +## Next steps +- Get started: [GETTING_STARTED.md](GETTING_STARTED.md) +- Configure options: [CONFIGURATION.md](CONFIGURATION.md) +- Tools reference: [TOOLS.md](TOOLS.md) +- Troubleshooting: [TROUBLESHOOTING.md](TROUBLESHOOTING.md) diff --git a/docs/PLUGIN_DEVELOPMENT.md b/docs/PLUGIN_DEVELOPMENT.md deleted file mode 100644 index 9754b6f9..00000000 --- a/docs/PLUGIN_DEVELOPMENT.md +++ /dev/null @@ -1,769 +0,0 @@ -# XcodeBuildMCP Plugin Development Guide - -This guide provides comprehensive instructions for creating new tools and workflow groups in XcodeBuildMCP using the filesystem-based auto-discovery system. - -## Table of Contents - -1. [Overview](#overview) -2. [Plugin Architecture](#plugin-architecture) -3. [Creating New Tools](#creating-new-tools) -4. [Creating New Workflow Groups](#creating-new-workflow-groups) -5. [Creating MCP Resources](#creating-mcp-resources) -6. [Auto-Discovery System](#auto-discovery-system) -7. [Testing Guidelines](#testing-guidelines) -8. [Development Workflow](#development-workflow) -9. [Best Practices](#best-practices) - -## Overview - -XcodeBuildMCP uses a **plugin-based architecture** with **filesystem-based auto-discovery**. Tools are automatically discovered and loaded without manual registration, and can be selectively enabled using `XCODEBUILDMCP_ENABLED_WORKFLOWS`. - -### Key Features - -- **Auto-Discovery**: Tools are automatically found by scanning `src/mcp/tools/` directory -- **Selective Workflow Loading**: Limit startup tool registration with `XCODEBUILDMCP_ENABLED_WORKFLOWS` -- **Dependency Injection**: All tools use testable patterns with mock-friendly executors -- **Workflow Organization**: Tools are grouped into end-to-end development workflows - -## Plugin Architecture - -### Directory Structure - -``` -src/mcp/tools/ -├── simulator-workspace/ # iOS Simulator + Workspace tools -├── simulator-project/ # iOS Simulator + Project tools (re-exports) -├── simulator-shared/ # Shared simulator tools (canonical) -├── device-workspace/ # iOS Device + Workspace tools -├── device-project/ # iOS Device + Project tools (re-exports) -├── device-shared/ # Shared device tools (canonical) -├── macos-workspace/ # macOS + Workspace tools -├── macos-project/ # macOS + Project tools (re-exports) -├── macos-shared/ # Shared macOS tools (canonical) -├── swift-package/ # Swift Package Manager tools -├── ui-testing/ # UI automation tools -├── project-discovery/ # Project analysis tools -├── utilities/ # General utilities -├── doctor/ # System health check tools -└── logging/ # Log capture tools -``` - -### Plugin Tool Types - -1. **Canonical Workflows**: Standalone workflow groups (e.g., `swift-package`, `ui-testing`) defined as folders in the `src/mcp/tools/` directory -2. **Shared Tools**: Common tools in `*-shared` directories (not exposed to clients) -3. **Re-exported Tools**: Share tools to other workflow groups by re-exporting them - -## Creating New Tools - -### 1. Tool File Structure - -Every tool follows this standardized pattern: - -```typescript -// src/mcp/tools/my-workflow/my_tool.ts -import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { log, validateRequiredParam, createTextResponse, createErrorResponse } from '../../../utils/index.js'; - -// 1. Define parameters type for clarity -type MyToolParams = { - requiredParam: string; - optionalParam?: string; -}; - -// 2. Implement the core logic in a separate, testable function -export async function my_toolLogic( - params: MyToolParams, - executor: CommandExecutor, -): Promise { - // 3. Validate required parameters - const requiredValidation = validateRequiredParam('requiredParam', params.requiredParam); - if (!requiredValidation.isValid) { - return requiredValidation.errorResponse; - } - - log('info', `Executing my_tool with param: ${params.requiredParam}`); - - try { - // 4. Build and execute the command using the injected executor - const command = ['my-command', '--param', params.requiredParam]; - if (params.optionalParam) { - command.push('--optional', params.optionalParam); - } - - const result = await executor(command, 'My Tool Operation'); - - if (!result.success) { - return createErrorResponse('My Tool operation failed', result.error); - } - - return createTextResponse(`✅ Success: ${result.output}`); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - log('error', `My Tool execution error: ${errorMessage}`); - return createErrorResponse('Tool execution failed', errorMessage); - } -} - -// 5. Export the tool definition as the default export -export default { - name: 'my_tool', - description: 'A brief description of what my_tool does, with a usage example. e.g. my_tool({ requiredParam: "value" })', - schema: { - requiredParam: z.string().describe('Description of the required parameter.'), - optionalParam: z.string().optional().describe('Description of the optional parameter.'), - }, - // The handler wraps the logic function with the default executor for production use - handler: async (args: Record): Promise => { - return my_toolLogic(args as MyToolParams, getDefaultCommandExecutor()); - }, -}; -``` - -### 2. Required Tool Plugin Properties - -Every tool plugin **must** export a default object with these properties: - -| Property | Type | Description | -|----------|------|-------------| -| `name` | `string` | Tool name (must match filename without extension) | -| `description` | `string` | Clear description with usage examples | -| `schema` | `Record` | Zod validation schema for parameters | -| `handler` | `function` | Async function: `(args) => Promise` | - -### 3. Naming Conventions - -Tools follow the pattern: `{action}_{target}_{specifier}_{projectType}` - -**Examples:** -- `build_sim_id_ws` → Build + Simulator + ID + Workspace -- `build_sim_name_proj` → Build + Simulator + Name + Project -- `test_device_ws` → Test + Device + Workspace -- `swift_package_build` → Swift Package + Build - -**Project Type Suffixes:** -- `_ws` → Works with `.xcworkspace` files -- `_proj` → Works with `.xcodeproj` files -- No suffix → Generic or canonical tools - -### 4. Parameter Validation Patterns - -Use utility functions for consistent validation: - -```typescript -// Required parameter validation -const pathValidation = validateRequiredParam('workspacePath', params.workspacePath); -if (!pathValidation.isValid) return pathValidation.errorResponse; - -// At-least-one parameter validation -const identifierValidation = validateAtLeastOneParam( - 'simulatorId', params.simulatorId, - 'simulatorName', params.simulatorName -); -if (!identifierValidation.isValid) return identifierValidation.errorResponse; - -// File existence validation -const fileValidation = validateFileExists(params.workspacePath as string); -if (!fileValidation.isValid) return fileValidation.errorResponse; -``` - -### 5. Response Patterns - -Use utility functions for consistent responses: - -```typescript -// Success responses -return createTextResponse('✅ Operation succeeded'); -return createTextResponse('Operation completed', false); // Not an error - -// Error responses -return createErrorResponse('Operation failed', errorDetails); -return createErrorResponse('Validation failed', errorMessage, 'ValidationError'); - -// Complex responses -return { - content: [ - { type: 'text', text: '✅ Build succeeded' }, - { type: 'text', text: 'Next steps: Run install_app_sim...' } - ], - isError: false -}; -``` - -## Creating New Workflow Groups - -### 1. Workflow Group Structure - -Each workflow group requires: - -1. **Directory**: Following naming convention -2. **Workflow Metadata**: `index.ts` file with workflow export -3. **Tool Files**: Individual tool implementations -4. **Tests**: Comprehensive test coverage - -### 2. Directory Naming Convention - -``` -[platform]-[projectType]/ # e.g., simulator-workspace, device-project -[platform]-shared/ # e.g., simulator-shared, macos-shared -[workflow-name]/ # e.g., swift-package, ui-testing -``` - -### 3. Workflow Metadata (index.ts) - -**Required for all workflow groups:** - -```typescript -// Example: src/mcp/tools/simulator-workspace/index.ts -export const workflow = { - name: 'iOS Simulator Workspace Development', - description: 'Complete iOS development workflow for .xcworkspace files including build, test, deploy, and debug capabilities', -}; -``` - -**Required Properties:** -- `name`: Human-readable workflow name -- `description`: Clear description of workflow purpose - -### 4. Tool Organization Patterns - -#### Canonical Workflow Groups -Self-contained workflows that don't re-export from other groups: - -``` -swift-package/ -├── index.ts # Workflow metadata -├── swift_package_build.ts # Build tool -├── swift_package_test.ts # Test tool -├── swift_package_run.ts # Run tool -└── __tests__/ # Test directory - ├── index.test.ts # Workflow tests - ├── swift_package_build.test.ts - └── ... -``` - -#### Shared Workflow Groups -Provide canonical tools for re-export by project/workspace variants: - -``` -simulator-shared/ -├── boot_sim.ts # Canonical simulator boot tool -├── install_app_sim.ts # Canonical app install tool -└── __tests__/ # Test directory - ├── boot_sim.test.ts - └── ... -``` - -#### Project/Workspace Workflow Groups -Re-export shared tools and add variant-specific tools: - -``` -simulator-project/ -├── index.ts # Workflow metadata -├── boot_sim.ts # Re-export: export { default } from '../simulator-shared/boot_sim.js'; -├── build_sim_id_proj.ts # Project-specific build tool -└── __tests__/ # Test directory - ├── index.test.ts # Workflow tests - ├── re-exports.test.ts # Re-export validation - └── ... -``` - -### 5. Re-export Implementation - -For project/workspace groups that share tools: - -```typescript -// simulator-project/boot_sim.ts -export { default } from '../simulator-shared/boot_sim.js'; -``` - -**Re-export Rules:** -1. Re-exports come from canonical `-shared` groups -2. No chained re-exports (re-exports from re-exports) -3. Each tool maintains project or workspace specificity -4. Implementation shared, interfaces remain unique - -## Creating MCP Resources - -MCP Resources provide efficient URI-based data access for clients that support the MCP resource specification - -### 1. Resource Structure - -Resources are located in `src/resources/` and follow this pattern: - -```typescript -// src/resources/example.ts -import { log, getDefaultCommandExecutor, CommandExecutor } from '../../utils/index.js'; - -// Testable resource logic separated from MCP handler -export async function exampleResourceLogic( - executor: CommandExecutor, -): Promise<{ contents: Array<{ text: string }> }> { - try { - log('info', 'Processing example resource request'); - - // Use the executor to get data - const result = await executor(['some', 'command'], 'Example Resource Operation'); - - if (!result.success) { - throw new Error(result.error || 'Failed to get resource data'); - } - - return { - contents: [{ text: result.output || 'resource data' }] - }; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - log('error', `Error in example resource handler: ${errorMessage}`); - - return { - contents: [ - { - text: `Error retrieving resource data: ${errorMessage}`, - }, - ], - }; - } -} - -export default { - uri: 'xcodebuildmcp://example', - name: 'example', - description: 'Description of the resource data', - mimeType: 'text/plain', - async handler(_uri: URL): Promise<{ contents: Array<{ text: string }> }> { - return exampleResourceLogic(getDefaultCommandExecutor()); - }, -}; -``` - -### 2. Resource Implementation Guidelines - -**Reuse Existing Logic**: Resources that mirror tools should reuse existing tool logic for consistency: - -```typescript -// src/mcp/resources/simulators.ts (simplified example) -import { list_simsLogic } from '../tools/simulator-shared/list_sims.js'; - -export default { - uri: 'xcodebuildmcp://simulators', - name: 'simulators' - description: 'Available iOS simulators with UUIDs and states', - mimeType: 'text/plain', - async handler(uri: URL): Promise<{ contents: Array<{ text: string }> }> { - const executor = getDefaultCommandExecutor(); - const result = await list_simsLogic({}, executor); - return { - contents: [{ text: result.content[0].text }] - }; - } -}; -``` - -As not all clients support resources it important that resource content that would be ideally be served by resources be mirroed as a tool as well. This ensurew clients that don't support this capability continue to will still have access to that resource data via a simple tool call. - -### 3. Resource Testing - -Create tests in `src/mcp/resources/__tests__/`: - -```typescript -// src/mcp/resources/__tests__/example.test.ts -import exampleResource, { exampleResourceLogic } from '../example.js'; -import { createMockExecutor } from '../../utils/test-common.js'; - -describe('example resource', () => { - describe('Export Field Validation', () => { - it('should export correct uri', () => { - expect(exampleResource.uri).toBe('xcodebuildmcp://example'); - }); - - it('should export correct description', () => { - expect(exampleResource.description).toBe('Description of the resource data'); - }); - - it('should export correct mimeType', () => { - expect(exampleResource.mimeType).toBe('text/plain'); - }); - - it('should export handler function', () => { - expect(typeof exampleResource.handler).toBe('function'); - }); - }); - - describe('Resource Logic Functionality', () => { - it('should return resource data successfully', async () => { - const mockExecutor = createMockExecutor({ - success: true, - output: 'test data' - }); - - // Test the logic function directly, not the handler - const result = await exampleResourceLogic(mockExecutor); - - expect(result.contents).toHaveLength(1); - expect(result.contents[0].text).toContain('expected data'); - }); - - it('should handle command execution errors', async () => { - const mockExecutor = createMockExecutor({ - success: false, - error: 'Command failed' - }); - - const result = await exampleResourceLogic(mockExecutor); - - expect(result.contents[0].text).toContain('Error retrieving'); - }); - }); -}); -``` - -### 4. Auto-Discovery - -Resources are automatically discovered and loaded by the build system. After creating a resource: - -1. Run `npm run build` to regenerate resource loaders -2. The resource will be available at its URI for supported clients - -## Auto-Discovery System - -### How Auto-Discovery Works - -1. **Filesystem Scan**: `loadPlugins()` scans `src/mcp/tools/` directory -2. **Workflow Loading**: Each subdirectory is treated as a potential workflow group -3. **Metadata Validation**: `index.ts` files provide workflow metadata -4. **Tool Discovery**: All `.ts` files (except tests and index) are loaded as tools -5. **Registration**: Tools are automatically registered with the MCP server - -### Discovery Process - -```typescript -// Simplified discovery flow -const plugins = await loadPlugins(); -for (const plugin of plugins.values()) { - server.tool(plugin.name, plugin.description, plugin.schema, plugin.handler); -} -``` - -### Selective Workflow Loading - -To limit which workflows are registered at startup, set `XCODEBUILDMCP_ENABLED_WORKFLOWS` to a comma-separated list of workflow directory names. The `session-management` workflow is always auto-included since other tools depend on it. - -Example: -```bash -XCODEBUILDMCP_ENABLED_WORKFLOWS=simulator,device,project-discovery -``` - -`XCODEBUILDMCP_DEBUG=true` can still be used to increase logging verbosity. - -## Testing Guidelines - -### Test Organization - -``` -__tests__/ -├── index.test.ts # Workflow metadata tests (canonical groups only) -├── re-exports.test.ts # Re-export validation (project/workspace groups) -└── tool_name.test.ts # Individual tool tests -``` - -### Dependency Injection Testing - -**✅ CORRECT Pattern:** -```typescript -import { createMockExecutor } from '../../../utils/test-common.js'; - -describe('build_sim_name_ws', () => { - it('should build successfully', async () => { - const mockExecutor = createMockExecutor({ - success: true, - output: 'BUILD SUCCEEDED' - }); - - const result = await build_sim_name_wsLogic(params, mockExecutor); - expect(result.isError).toBe(false); - }); -}); -``` - -**❌ FORBIDDEN Pattern (Vitest Mocking Banned):** -```typescript -// ❌ ALL VITEST MOCKING IS COMPLETELY BANNED -vi.mock('child_process'); -const mockSpawn = vi.fn(); -``` - -### Three-Dimensional Testing - -Every tool test must cover: - -1. **Input Validation**: Parameter schema validation and error cases -2. **Command Generation**: Verify correct CLI commands are built -3. **Output Processing**: Test response formatting and error handling - -### Test Template - -```typescript -import { describe, it, expect } from 'vitest'; -import { createMockExecutor } from '../../../utils/test-common.js'; -import tool, { toolNameLogic } from '../tool_name.js'; - -describe('tool_name', () => { - describe('Export Validation', () => { - it('should export correct name', () => { - expect(tool.name).toBe('tool_name'); - }); - - it('should export correct description', () => { - expect(tool.description).toContain('Expected description'); - }); - - it('should export handler function', () => { - expect(typeof tool.handler).toBe('function'); - }); - }); - - describe('Parameter Validation', () => { - it('should validate required parameters', async () => { - const mockExecutor = createMockExecutor({ success: true, output: '' }); - - const result = await toolNameLogic({}, mockExecutor); - - expect(result.isError).toBe(true); - expect(result.content[0].text).toContain("Required parameter"); - }); - }); - - describe('Command Generation', () => { - it('should generate correct command', async () => { - const mockExecutor = createMockExecutor({ success: true, output: 'SUCCESS' }); - - await toolNameLogic({ param: 'value' }, mockExecutor); - - expect(mockExecutor).toHaveBeenCalledWith( - expect.arrayContaining(['expected', 'command']), - expect.any(String), - expect.any(Boolean) - ); - }); - }); - - describe('Response Processing', () => { - it('should handle successful execution', async () => { - const mockExecutor = createMockExecutor({ success: true, output: 'SUCCESS' }); - - const result = await toolNameLogic({ param: 'value' }, mockExecutor); - - expect(result.isError).toBe(false); - expect(result.content[0].text).toContain('✅'); - }); - - it('should handle execution errors', async () => { - const mockExecutor = createMockExecutor({ success: false, error: 'Command failed' }); - - const result = await toolNameLogic({ param: 'value' }, mockExecutor); - - expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Command failed'); - }); - }); -}); -``` - -## Development Workflow - -### Adding a New Tool - -1. **Choose Directory**: Select appropriate workflow group or create new one -2. **Create Tool File**: Follow naming convention and structure -3. **Implement Logic**: Use dependency injection pattern -4. **Define Schema**: Add comprehensive Zod validation -5. **Write Tests**: Cover all three dimensions -6. **Test Integration**: Build and verify auto-discovery - -### Step-by-Step Tool Creation - -```bash -# 1. Create tool file -touch src/mcp/tools/simulator-workspace/my_new_tool_ws.ts - -# 2. Implement tool following patterns above - -# 3. Create test file -touch src/mcp/tools/simulator-workspace/__tests__/my_new_tool_ws.test.ts - -# 4. Build project -npm run build - -# 5. Verify tool is discovered (should appear in tools list) -npm run inspect # Use MCP Inspector to verify -``` - -### Adding a New Workflow Group - -1. **Create Directory**: Follow naming convention -2. **Add Workflow Metadata**: Create `index.ts` with workflow export -3. **Implement Tools**: Add tool files following patterns -4. **Create Tests**: Add comprehensive test coverage -5. **Verify Discovery**: Test auto-discovery and tool registration - -### Step-by-Step Workflow Creation - -```bash -# 1. Create workflow directory -mkdir src/mcp/tools/my-new-workflow - -# 2. Create workflow metadata -cat > src/mcp/tools/my-new-workflow/index.ts << 'EOF' -export const workflow = { - name: 'My New Workflow', - description: 'Description of workflow capabilities', -}; -EOF - -# 3. Create tools directory and test directory -mkdir src/mcp/tools/my-new-workflow/__tests__ - -# 4. Implement tools following patterns - -# 5. Build and verify -npm run build -npm run inspect -``` - -## Best Practices - -### Tool Design - -1. **Single Responsibility**: Each tool should have one clear purpose -2. **Descriptive Names**: Follow naming conventions for discoverability -3. **Clear Descriptions**: Include usage examples in tool descriptions -4. **Comprehensive Validation**: Validate all parameters with helpful error messages -5. **Consistent Responses**: Use utility functions for response formatting - -### Error Handling - -1. **Graceful Failures**: Always return ToolResponse, never throw from handlers -2. **Descriptive Errors**: Provide actionable error messages -3. **Error Types**: Use appropriate error types for different scenarios -4. **Logging**: Log important events and errors for debugging - -### Testing - -1. **Dependency Injection**: Always test with mock executors -2. **Complete Coverage**: Test all input, command, and output scenarios -3. **Literal Assertions**: Use exact string expectations to catch changes -4. **Fast Execution**: Tests should complete quickly without real system calls - -### Workflow Organization - -1. **End-to-End Workflows**: Groups should provide complete functionality -2. **Logical Grouping**: Group related tools together -3. **Clear Capabilities**: Document what each workflow can accomplish -4. **Consistent Patterns**: Follow established patterns for maintainability - -### Workflow Metadata Considerations - -1. **Workflow Completeness**: Each group should be self-sufficient -2. **Clear Descriptions**: Keep the `description` concise and user-focused - -## Updating TOOLS.md Documentation - -### Critical Documentation Maintenance - -**Every time you add, change, move, edit, or delete a tool, you MUST review and update the `docs/TOOLS.md` file to reflect the current state of the codebase.** - -### Documentation Update Process - -#### 1. Use Tree CLI for Accurate Discovery - -**Always use the `tree` command to get the actual filesystem representation of tools:** - -```bash -# Get the definitive source of truth for all workflow groups and tools -tree src/mcp/tools/ -I "__tests__" -I "*.test.ts" -``` - -This command: -- Shows ALL workflow directories and their tools -- Excludes test files (`__tests__` directories and `*.test.ts` files) -- Provides the actual proof of what exists in the codebase -- Gives an accurate count of tools per workflow group - -#### 2. Ignore Shared Groups in Documentation - -When updating `docs/TOOLS.md`: - -- **Ignore `*-shared` directories** (e.g., `simulator-shared`, `device-shared`, `macos-shared`) -- These are implementation details, not user-facing workflow groups -- Only document the main workflow groups that users interact with -- The group count should exclude shared groups - -#### 3. List Actual Tool Names - -Instead of using generic descriptions like "Additional Tools: Simulator management, logging, UI testing tools": - -**❌ Wrong:** -```markdown -- **Additional Tools**: Simulator management, logging, UI testing tools -``` - -**✅ Correct:** -```markdown -- `boot_sim`, `install_app_sim`, `launch_app_sim`, `list_sims`, `open_sim` -- `describe_ui`, `screenshot`, `start_sim_log_cap`, `stop_sim_log_cap` -``` - -#### 4. Systematic Documentation Update Steps - -1. **Run the tree command** to get current filesystem state -2. **Identify all non-shared workflow directories** -3. **Count actual tool files** in each directory (exclude `index.ts` and test files) -4. **List all tool names** explicitly in the documentation -5. **Update tool counts** to reflect actual numbers -6. **Verify consistency** between filesystem and documentation - -#### 5. Documentation Formatting Requirements - -**Format: One Tool Per Bullet Point with Description** - -Each tool must be listed individually with its actual description from the tool file: - -```markdown -### 1. My Awesome Workflow (`my-awesome-workflow`) -**Purpose**: A short description of what this workflow is for. (2 tools) -- `my_tool_one` - Description for my_tool_one from its definition file. -- `my_tool_two` - Description for my_tool_two from its definition file. -``` - -**Description Sources:** -- Use the actual `description` field from each tool's TypeScript file -- Descriptions should be concise but informative for end users -- Include platform/context information (iOS, macOS, simulator, device, etc.) -- Mention required parameters when critical for usage - -#### 6. Validation Checklist - -After updating `docs/TOOLS.md`: - -- [ ] Tool counts match actual filesystem counts (from tree command) -- [ ] Each tool has its own bullet point (one tool per line) -- [ ] Each tool includes its actual description from the tool file -- [ ] No generic descriptions like "Additional Tools: X, Y, Z" -- [ ] Descriptions are user-friendly and informative -- [ ] Shared groups (`*-shared`) are not included in main workflow list -- [ ] Workflow group count reflects only user-facing groups (15 groups) -- [ ] Tree command output was used as source of truth -- [ ] Documentation is user-focused, not implementation-focused -- [ ] Tool names are in alphabetical order within each workflow group - -### Why This Process Matters - -1. **Accuracy**: Tree command provides definitive proof of current state -2. **Maintainability**: Systematic process prevents documentation drift -3. **User Experience**: Accurate documentation helps users understand available tools -4. **Development Confidence**: Developers can trust the documentation reflects reality - -**Remember**: The filesystem is the source of truth. Documentation must always reflect the actual codebase structure, and the tree command is the most reliable way to ensure accuracy. diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md new file mode 100644 index 00000000..56573dce --- /dev/null +++ b/docs/PRIVACY.md @@ -0,0 +1,23 @@ +# Privacy + +XcodeBuildMCP uses Sentry for error monitoring and diagnostics. This helps track crashes and unexpected errors to improve reliability. + +## What is sent to Sentry +- Internal XcodeBuildMCP failures only (for example: daemon/MCP runtime faults and unexpected exceptions in server code). +- User-domain errors are not sent (for example: project build/test failures, invalid user config, missing scheme/destination, simulator/app-state errors). +- Tool inputs/outputs are not captured by default, and environment/system variables are not attached as telemetry tags. +- Internal operational logs are sent only when explicitly marked for Sentry (`{ sentry: true }` in server code). Console logging is not automatically forwarded. +- Event payloads are scrubbed before send to remove request/user context and redact user home paths (for example `/Users//...`). + +## Opting out +To disable error telemetry, set: + +```json +"env": { + "XCODEBUILDMCP_SENTRY_DISABLED": "true" +} +``` + +## Related docs +- Configuration options: [CONFIGURATION.md](CONFIGURATION.md) +- Troubleshooting: [TROUBLESHOOTING.md](TROUBLESHOOTING.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..d1b2264d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,27 @@ +# XcodeBuildMCP Documentation + +## Start here +- [Getting started](GETTING_STARTED.md) +- [Configuration and options](CONFIGURATION.md) +- [Tools reference (all workflows/tools)](TOOLS.md) +- [Troubleshooting and doctor](TROUBLESHOOTING.md) +- [Privacy and telemetry](PRIVACY.md) +- [Demos](DEMOS.md) + +## User guides +- [Product overview and rationale](OVERVIEW.md) +- [Session defaults and opt-out](SESSION_DEFAULTS.md) +- [Device code signing notes](DEVICE_CODE_SIGNING.md) +- [CLI reference](CLI.md) + +## Developer docs +- [Developer documentation index](dev/README.md) +- [Contributing guide](dev/CONTRIBUTING.md) +- [Architecture](dev/ARCHITECTURE.md) +- [Testing](dev/TESTING.md) +- [Code quality rules](dev/CODE_QUALITY.md) +- [Plugin development](dev/PLUGIN_DEVELOPMENT.md) +- [Release process](dev/RELEASE_PROCESS.md) + +## Generated docs +- [TOOLS.md](TOOLS.md) is generated by `scripts/update-tools-docs.ts`. diff --git a/docs/RELOADEROO.md b/docs/RELOADEROO.md deleted file mode 100644 index 689425a7..00000000 --- a/docs/RELOADEROO.md +++ /dev/null @@ -1,446 +0,0 @@ -# Reloaderoo Integration Guide - -This guide explains how to use Reloaderoo v1.1.2+ for testing and developing XcodeBuildMCP with both CLI inspection tools and transparent proxy capabilities. - -## Overview - -**Reloaderoo** is a dual-mode MCP development tool that operates as both a CLI inspection tool and a transparent proxy server for the Model Context Protocol (MCP). It provides two distinct operational modes for different development workflows. - -## Installation - -Reloaderoo is available via npm and can be used with npx for universal compatibility. - -```bash -# Use npx to run reloaderoo (works on any system) -npx reloaderoo@latest --help - -# Or install globally if preferred -npm install -g reloaderoo -reloaderoo --help -``` - -## Two Operational Modes - -### 🔍 **CLI Mode** (Inspection & Testing) - -Direct command-line access to MCP servers without client setup - perfect for testing and debugging: - -**Key Benefits:** -- ✅ **One-shot commands** - Test tools, list resources, get server info -- ✅ **No MCP client required** - Perfect for testing and debugging -- ✅ **Raw JSON output** - Ideal for scripts and automation -- ✅ **8 inspection commands** - Complete MCP protocol coverage -- ✅ **AI agent friendly** - Designed for terminal-based AI development workflows - -**Basic Commands:** - -```bash -# List all available tools -npx reloaderoo@latest inspect list-tools -- node build/index.js - -# Call any tool with parameters -npx reloaderoo@latest inspect call-tool --params '' -- node build/index.js - -# Get server information -npx reloaderoo@latest inspect server-info -- node build/index.js - -# List available resources -npx reloaderoo@latest inspect list-resources -- node build/index.js - -# Read a specific resource -npx reloaderoo@latest inspect read-resource "" -- node build/index.js - -# List available prompts -npx reloaderoo@latest inspect list-prompts -- node build/index.js - -# Get a specific prompt -npx reloaderoo@latest inspect get-prompt --args '' -- node build/index.js - -# Check server connectivity -npx reloaderoo@latest inspect ping -- node build/index.js -``` - -**Example Tool Calls:** - -```bash -# List connected devices -npx reloaderoo@latest inspect call-tool list_devices --params '{}' -- node build/index.js - -# Get doctor information -npx reloaderoo@latest inspect call-tool doctor --params '{}' -- node build/index.js - -# List iOS simulators -npx reloaderoo@latest inspect call-tool list_sims --params '{}' -- node build/index.js - -# Read devices resource -npx reloaderoo@latest inspect read-resource "xcodebuildmcp://devices" -- node build/index.js -``` - -### 🔄 **Proxy Mode** (Hot-Reload Development) - -Transparent MCP proxy server that enables seamless hot-reloading during development: - -**Key Benefits:** -- ✅ **Hot-reload MCP servers** without disconnecting your AI client -- ✅ **Session persistence** - Keep your development context intact -- ✅ **Automatic `restart_server` tool** - AI agents can restart servers on demand -- ✅ **Transparent forwarding** - Full MCP protocol passthrough -- ✅ **Process management** - Spawns, monitors, and restarts your server process - -**Usage:** - -```bash -# Start proxy mode (your AI client connects to this) -npx reloaderoo@latest proxy -- node build/index.js - -# With debug logging -npx reloaderoo@latest proxy --log-level debug -- node build/index.js - -# Then in your AI session, request: -# "Please restart the MCP server to load my latest changes" -``` - -The AI agent will automatically call the `restart_server` tool, preserving your session while reloading code changes. - -## MCP Inspection Server Mode - -Start CLI mode as a persistent MCP server for interactive debugging through MCP clients: - -```bash -# Start reloaderoo in CLI mode as an MCP server -npx reloaderoo@latest inspect mcp -- node build/index.js -``` - -This runs CLI mode as a persistent MCP server, exposing 8 debug tools through the MCP protocol: -- `list_tools` - List all server tools -- `call_tool` - Call any server tool -- `list_resources` - List all server resources -- `read_resource` - Read any server resource -- `list_prompts` - List all server prompts -- `get_prompt` - Get any server prompt -- `get_server_info` - Get comprehensive server information -- `ping` - Test server connectivity - -## Claude Code Compatibility - -When running under Claude Code, XcodeBuildMCP automatically detects the environment and consolidates multiple content blocks into single responses with `---` separators. - -**Automatic Detection Methods:** -1. **Environment Variables**: `CLAUDECODE=1` or `CLAUDE_CODE_ENTRYPOINT=cli` -2. **Parent Process Analysis**: Checks if parent process contains 'claude' -3. **Graceful Fallback**: Falls back to environment variables if process detection fails - -**No Configuration Required**: The consolidation happens automatically when Claude Code is detected. - -## Command Reference - -### Command Structure - -```bash -npx reloaderoo@latest [options] [command] - -Two modes, one tool: -• Proxy MCP server that adds support for hot-reloading MCP servers. -• CLI tool for inspecting MCP servers. - -Global Options: - -V, --version Output the version number - -h, --help Display help for command - -Commands: - proxy [options] 🔄 Run as MCP proxy server (default behavior) - inspect 🔍 Inspect and debug MCP servers - info [options] 📊 Display version and configuration information - help [command] ❓ Display help for command -``` - -### 🔄 **Proxy Mode Commands** - -```bash -npx reloaderoo@latest proxy [options] -- [child-args...] - -Options: - -w, --working-dir Working directory for the child process - -l, --log-level Log level (debug, info, notice, warning, error, critical) - -f, --log-file Custom log file path (logs to stderr by default) - -t, --restart-timeout Timeout for restart operations (default: 30000ms) - -m, --max-restarts Maximum restart attempts (0-10, default: 3) - -d, --restart-delay Delay between restart attempts (default: 1000ms) - -q, --quiet Suppress non-essential output - --no-auto-restart Disable automatic restart on crashes - --debug Enable debug mode with verbose logging - --dry-run Validate configuration without starting proxy - -Examples: - npx reloaderoo proxy -- node build/index.js - npx reloaderoo -- node build/index.js # Same as above (proxy is default) - npx reloaderoo proxy --log-level debug -- node build/index.js -``` - -### 🔍 **CLI Mode Commands** - -```bash -npx reloaderoo@latest inspect [subcommand] [options] -- [child-args...] - -Subcommands: - server-info [options] Get server information and capabilities - list-tools [options] List all available tools - call-tool [options] Call a specific tool - list-resources [options] List all available resources - read-resource [options] Read a specific resource - list-prompts [options] List all available prompts - get-prompt [options] Get a specific prompt - ping [options] Check server connectivity - -Examples: - npx reloaderoo@latest inspect list-tools -- node build/index.js - npx reloaderoo@latest inspect call-tool list_devices --params '{}' -- node build/index.js - npx reloaderoo@latest inspect server-info -- node build/index.js -``` - -### **Info Command** - -```bash -npx reloaderoo@latest info [options] - -Options: - -v, --verbose Show detailed information - -h, --help Display help for command - -Examples: - npx reloaderoo@latest info # Show basic system information - npx reloaderoo@latest info --verbose # Show detailed system information -``` - -### Response Format - -All CLI commands return structured JSON: - -```json -{ - "success": true, - "data": { - // Command-specific response data - }, - "metadata": { - "command": "call-tool:list_devices", - "timestamp": "2025-07-25T08:32:47.042Z", - "duration": 1782 - } -} -``` - -### Error Handling - -When commands fail, you'll receive: - -```json -{ - "success": false, - "error": { - "message": "Error description", - "code": "ERROR_CODE" - }, - "metadata": { - "command": "failed-command", - "timestamp": "2025-07-25T08:32:47.042Z", - "duration": 100 - } -} -``` - -## Development Workflow - -### 🔍 **CLI Mode Workflow** (Testing & Debugging) - -Perfect for testing individual tools or debugging server issues without MCP client setup: - -```bash -# 1. Build XcodeBuildMCP -npm run build - -# 2. Test your server quickly -npx reloaderoo@latest inspect list-tools -- node build/index.js - -# 3. Call specific tools to verify behavior -npx reloaderoo@latest inspect call-tool list_devices --params '{}' -- node build/index.js - -# 4. Check server health and resources -npx reloaderoo@latest inspect ping -- node build/index.js -npx reloaderoo@latest inspect list-resources -- node build/index.js -``` - -### 🔄 **Proxy Mode Workflow** (Hot-Reload Development) - -For full development sessions with AI clients that need persistent connections: - -#### 1. **Start Development Session** -Configure your AI client to connect to reloaderoo proxy instead of your server directly: -```bash -npx reloaderoo@latest proxy -- node build/index.js -# or with debug logging: -npx reloaderoo@latest proxy --log-level debug -- node build/index.js -``` - -#### 2. **Develop Your MCP Server** -Work on your XcodeBuildMCP code as usual - make changes, add tools, modify functionality. - -#### 3. **Test Changes Instantly** -```bash -# Rebuild your changes -npm run build - -# Then ask your AI agent to restart the server: -# "Please restart the MCP server to load my latest changes" -``` - -The agent will call the `restart_server` tool automatically. Your new capabilities are immediately available! - -#### 4. **Continue Development** -Your AI session continues with the updated server capabilities. No connection loss, no context reset. - -### 🛠️ **MCP Inspection Server** (Interactive CLI Debugging) - -For interactive debugging through MCP clients: - -```bash -# Start reloaderoo CLI mode as an MCP server -npx reloaderoo@latest inspect mcp -- node build/index.js - -# Then connect with an MCP client to access debug tools -# Available tools: list_tools, call_tool, list_resources, etc. -``` - -## Troubleshooting - -### 🔄 **Proxy Mode Issues** - -**Server won't start in proxy mode:** -```bash -# Check if XcodeBuildMCP runs independently first -node build/index.js - -# Then try with reloaderoo proxy to validate configuration -npx reloaderoo@latest proxy -- node build/index.js -``` - -**Connection problems with MCP clients:** -```bash -# Enable debug logging to see what's happening -npx reloaderoo@latest proxy --log-level debug -- node build/index.js - -# Check system info and configuration -npx reloaderoo@latest info --verbose -``` - -**Restart failures in proxy mode:** -```bash -# Increase restart timeout -npx reloaderoo@latest proxy --restart-timeout 60000 -- node build/index.js - -# Check restart limits -npx reloaderoo@latest proxy --max-restarts 5 -- node build/index.js -``` - -### 🔍 **CLI Mode Issues** - -**CLI commands failing:** -```bash -# Test basic connectivity first -npx reloaderoo@latest inspect ping -- node build/index.js - -# Enable debug logging for CLI commands (via proxy debug mode) -npx reloaderoo@latest proxy --log-level debug -- node build/index.js -``` - -**JSON parsing errors:** -```bash -# Check server information for troubleshooting -npx reloaderoo@latest inspect server-info -- node build/index.js - -# Ensure your server outputs valid JSON -node build/index.js | head -10 -``` - -### **General Issues** - -**Command not found:** -```bash -# Ensure npx can find reloaderoo -npx reloaderoo@latest --help - -# If that fails, try installing globally -npm install -g reloaderoo -``` - -**Parameter validation:** -```bash -# Ensure JSON parameters are properly quoted -npx reloaderoo@latest inspect call-tool list_devices --params '{}' -- node build/index.js -``` - -### **General Debug Mode** - -```bash -# Get detailed information about what's happening -npx reloaderoo@latest proxy --debug -- node build/index.js # For proxy mode -npx reloaderoo@latest proxy --log-level debug -- node build/index.js # For detailed proxy logging - -# View system information -npx reloaderoo@latest info --verbose -``` - -### Debug Tips - -1. **Always build first**: Run `npm run build` before testing -2. **Check tool names**: Use `inspect list-tools` to see exact tool names -3. **Validate JSON**: Ensure parameters are valid JSON strings -4. **Enable debug logging**: Use `--log-level debug` or `--debug` for verbose output -5. **Test connectivity**: Use `inspect ping` to verify server communication - -## Advanced Usage - -### Environment Variables - -Configure reloaderoo behavior via environment variables: - -```bash -# Logging Configuration -export MCPDEV_PROXY_LOG_LEVEL=debug # Log level (debug, info, notice, warning, error, critical) -export MCPDEV_PROXY_LOG_FILE=/path/to/log # Custom log file path (default: stderr) -export MCPDEV_PROXY_DEBUG_MODE=true # Enable debug mode (true/false) - -# Process Management -export MCPDEV_PROXY_RESTART_LIMIT=5 # Maximum restart attempts (0-10, default: 3) -export MCPDEV_PROXY_AUTO_RESTART=true # Enable/disable auto-restart (true/false) -export MCPDEV_PROXY_TIMEOUT=30000 # Operation timeout in milliseconds -export MCPDEV_PROXY_RESTART_DELAY=1000 # Delay between restart attempts in milliseconds -export MCPDEV_PROXY_CWD=/path/to/directory # Default working directory -``` - -### Custom Working Directory - -```bash -npx reloaderoo@latest proxy --working-dir /custom/path -- node build/index.js -npx reloaderoo@latest inspect list-tools --working-dir /custom/path -- node build/index.js -``` - -### Timeout Configuration - -```bash -npx reloaderoo@latest proxy --restart-timeout 60000 -- node build/index.js -``` - -## Integration with XcodeBuildMCP - -Reloaderoo is specifically configured to work with XcodeBuildMCP's: - -- **84+ Tools**: All workflow groups accessible via CLI -- **4 Resources**: Direct access to devices, simulators, environment, swift-packages -- **Claude Code Detection**: Automatic consolidation of multiple content blocks -- **Hot-Reload Support**: Seamless development workflow with `restart_server` - -For more information about XcodeBuildMCP's architecture and capabilities, see: -- [Architecture Guide](ARCHITECTURE.md) -- [Plugin Development Guide](PLUGIN_DEVELOPMENT.md) -- [Testing Guide](TESTING.md) diff --git a/docs/RELOADEROO_FOR_XCODEBUILDMCP.md b/docs/RELOADEROO_FOR_XCODEBUILDMCP.md deleted file mode 100644 index a3ad909a..00000000 --- a/docs/RELOADEROO_FOR_XCODEBUILDMCP.md +++ /dev/null @@ -1,302 +0,0 @@ -# Reloaderoo Usage Guide for XcodeBuildMCP - -This guide explains how to use Reloaderoo for interacting with XcodeBuildMCP as a CLI to save context window space. - -You can use this guide to prompt your agent, but providing the entire document will give you no actual benefits. You will end up using more context than just using MCP server directly. So it's recommended that you curate this document by removing the example commands that you don't need and just keeping the ones that are right for your project. You'll then want to keep this file within your project workspace and then include it in the context window when you need to interact your agent to use XcodeBuildMCP tools. - -> [!IMPORTANT] -> Please remove this introduction before you prompt your agent with this file or any derrived version of it. - -## Installation - -Reloaderoo is available via npm and can be used with npx for universal compatibility. - -```bash -# Use npx to run reloaderoo -npx reloaderoo@latest --help -``` - -**Example Tool Calls:** - -### iOS Device Development - -- **`build_device`**: Builds an app for a physical device. - ```bash - npx reloaderoo@latest inspect call-tool build_device --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -- node build/index.js - ``` -- **`get_device_app_path`**: Gets the `.app` bundle path for a device build. - ```bash - npx reloaderoo@latest inspect call-tool get_device_app_path --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -- node build/index.js - ``` -- **`install_app_device`**: Installs an app on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool install_app_device --params '{"deviceId": "DEVICE_UDID", "appPath": "/path/to/MyApp.app"}' -- node build/index.js - ``` -- **`launch_app_device`**: Launches an app on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool launch_app_device --params '{"deviceId": "DEVICE_UDID", "bundleId": "com.example.MyApp"}' -- node build/index.js - ``` -- **`list_devices`**: Lists connected physical devices. - ```bash - npx reloaderoo@latest inspect call-tool list_devices --params '{}' -- node build/index.js - ``` -- **`stop_app_device`**: Stops an app on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool stop_app_device --params '{"deviceId": "DEVICE_UDID", "processId": 12345}' -- node build/index.js - ``` -- **`test_device`**: Runs tests on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool test_device --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "deviceId": "DEVICE_UDID"}' -- node build/index.js - ``` - -### iOS Simulator Development - -- **`boot_sim`**: Boots a simulator. - ```bash - npx reloaderoo@latest inspect call-tool boot_sim --params '{"simulatorId": "SIMULATOR_UUID"}' -- node build/index.js - ``` -- **`build_run_sim`**: Builds and runs an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool build_run_sim --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "simulatorName": "iPhone 16"}' -- node build/index.js - ``` -- **`build_sim`**: Builds an app for a simulator. - ```bash - npx reloaderoo@latest inspect call-tool build_sim --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "simulatorName": "iPhone 16"}' -- node build/index.js - ``` -- **`get_sim_app_path`**: Gets the `.app` bundle path for a simulator build. - ```bash - npx reloaderoo@latest inspect call-tool get_sim_app_path --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "platform": "iOS Simulator", "simulatorName": "iPhone 16"}' -- node build/index.js - ``` -- **`install_app_sim`**: Installs an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool install_app_sim --params '{"simulatorId": "SIMULATOR_UUID", "appPath": "/path/to/MyApp.app"}' -- node build/index.js - ``` -- **`launch_app_logs_sim`**: Launches an app on a simulator with log capture. - ```bash - npx reloaderoo@latest inspect call-tool launch_app_logs_sim --params '{"simulatorId": "SIMULATOR_UUID", "bundleId": "com.example.MyApp"}' -- node build/index.js - ``` -- **`launch_app_sim`**: Launches an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool launch_app_sim --params '{"simulatorName": "iPhone 16", "bundleId": "com.example.MyApp"}' -- node build/index.js - ``` -- **`list_sims`**: Lists available simulators. - ```bash - npx reloaderoo@latest inspect call-tool list_sims --params '{}' -- node build/index.js - ``` -- **`open_sim`**: Opens the Simulator application. - ```bash - npx reloaderoo@latest inspect call-tool open_sim --params '{}' -- node build/index.js - ``` -- **`stop_app_sim`**: Stops an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool stop_app_sim --params '{"simulatorName": "iPhone 16", "bundleId": "com.example.MyApp"}' -- node build/index.js - ``` -- **`test_sim`**: Runs tests on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool test_sim --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "simulatorName": "iPhone 16"}' -- node build/index.js - ``` - -### Log Capture & Management - -- **`start_device_log_cap`**: Starts log capture for a physical device. - ```bash - npx reloaderoo@latest inspect call-tool start_device_log_cap --params '{"deviceId": "DEVICE_UDID", "bundleId": "com.example.MyApp"}' -- node build/index.js - ``` -- **`start_sim_log_cap`**: Starts log capture for a simulator. - ```bash - npx reloaderoo@latest inspect call-tool start_sim_log_cap --params '{"simulatorUuid": "SIMULATOR_UUID", "bundleId": "com.example.MyApp"}' -- node build/index.js - ``` -- **`stop_device_log_cap`**: Stops log capture for a physical device. - ```bash - npx reloaderoo@latest inspect call-tool stop_device_log_cap --params '{"logSessionId": "SESSION_ID"}' -- node build/index.js - ``` -- **`stop_sim_log_cap`**: Stops log capture for a simulator. - ```bash - npx reloaderoo@latest inspect call-tool stop_sim_log_cap --params '{"logSessionId": "SESSION_ID"}' -- node build/index.js - ``` - -### macOS Development - -- **`build_macos`**: Builds a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool build_macos --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -- node build/index.js - ``` -- **`build_run_macos`**: Builds and runs a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool build_run_macos --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -- node build/index.js - ``` -- **`get_mac_app_path`**: Gets the `.app` bundle path for a macOS build. - ```bash - npx reloaderoo@latest inspect call-tool get_mac_app_path --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -- node build/index.js - ``` -- **`launch_mac_app`**: Launches a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool launch_mac_app --params '{"appPath": "/Applications/Calculator.app"}' -- node build/index.js - ``` -- **`stop_mac_app`**: Stops a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool stop_mac_app --params '{"appName": "Calculator"}' -- node build/index.js - ``` -- **`test_macos`**: Runs tests for a macOS project. - ```bash - npx reloaderoo@latest inspect call-tool test_macos --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -- node build/index.js - ``` - -### Project Discovery - -- **`discover_projs`**: Discovers Xcode projects and workspaces. - ```bash - npx reloaderoo@latest inspect call-tool discover_projs --params '{"workspaceRoot": "/path/to/workspace"}' -- node build/index.js - ``` -- **`get_app_bundle_id`**: Gets an app's bundle identifier. - ```bash - npx reloaderoo@latest inspect call-tool get_app_bundle_id --params '{"appPath": "/path/to/MyApp.app"}' -- node build/index.js - ``` -- **`get_mac_bundle_id`**: Gets a macOS app's bundle identifier. - ```bash - npx reloaderoo@latest inspect call-tool get_mac_bundle_id --params '{"appPath": "/Applications/Calculator.app"}' -- node build/index.js - ``` -- **`list_schemes`**: Lists schemes in a project or workspace. - ```bash - npx reloaderoo@latest inspect call-tool list_schemes --params '{"projectPath": "/path/to/MyProject.xcodeproj"}' -- node build/index.js - ``` -- **`show_build_settings`**: Shows build settings for a scheme. - ```bash - npx reloaderoo@latest inspect call-tool show_build_settings --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -- node build/index.js - ``` - -### Project Scaffolding - -- **`scaffold_ios_project`**: Scaffolds a new iOS project. - ```bash - npx reloaderoo@latest inspect call-tool scaffold_ios_project --params '{"projectName": "MyNewApp", "outputPath": "/path/to/projects"}' -- node build/index.js - ``` -- **`scaffold_macos_project`**: Scaffolds a new macOS project. - ```bash - npx reloaderoo@latest inspect call-tool scaffold_macos_project --params '{"projectName": "MyNewMacApp", "outputPath": "/path/to/projects"}' -- node build/index.js - ``` - -### Project Utilities - -- **`clean`**: Cleans build artifacts. - ```bash - # For a project - npx reloaderoo@latest inspect call-tool clean --params '{"projectPath": "/path/to/MyProject.xcodeproj"}' -- node build/index.js - # For a workspace - npx reloaderoo@latest inspect call-tool clean --params '{"workspacePath": "/path/to/MyWorkspace.xcworkspace", "scheme": "MyScheme"}' -- node build/index.js - ``` - -### Simulator Management - -- **`reset_sim_location`**: Resets a simulator's location. - ```bash - npx reloaderoo@latest inspect call-tool reset_sim_location --params '{"simulatorUuid": "SIMULATOR_UUID"}' -- node build/index.js - ``` -- **`set_sim_appearance`**: Sets a simulator's appearance (dark/light mode). - ```bash - npx reloaderoo@latest inspect call-tool set_sim_appearance --params '{"simulatorUuid": "SIMULATOR_UUID", "mode": "dark"}' -- node build/index.js - ``` -- **`set_sim_location`**: Sets a simulator's GPS location. - ```bash - npx reloaderoo@latest inspect call-tool set_sim_location --params '{"simulatorUuid": "SIMULATOR_UUID", "latitude": 37.7749, "longitude": -122.4194}' -- node build/index.js - ``` -- **`sim_statusbar`**: Overrides a simulator's status bar. - ```bash - npx reloaderoo@latest inspect call-tool sim_statusbar --params '{"simulatorUuid": "SIMULATOR_UUID", "dataNetwork": "wifi"}' -- node build/index.js - ``` - -### Swift Package Manager - -- **`swift_package_build`**: Builds a Swift package. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_build --params '{"packagePath": "/path/to/package"}' -- node build/index.js - ``` -- **`swift_package_clean`**: Cleans a Swift package. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_clean --params '{"packagePath": "/path/to/package"}' -- node build/index.js - ``` -- **`swift_package_list`**: Lists running Swift package processes. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_list --params '{}' -- node build/index.js - ``` -- **`swift_package_run`**: Runs a Swift package executable. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_run --params '{"packagePath": "/path/to/package"}' -- node build/index.js - ``` -- **`swift_package_stop`**: Stops a running Swift package process. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_stop --params '{"pid": 12345}' -- node build/index.js - ``` -- **`swift_package_test`**: Tests a Swift package. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_test --params '{"packagePath": "/path/to/package"}' -- node build/index.js - ``` - -### System Doctor - -- **`doctor`**: Runs system diagnostics. - ```bash - npx reloaderoo@latest inspect call-tool doctor --params '{}' -- node build/index.js - ``` - -### UI Testing & Automation - -- **`button`**: Simulates a hardware button press. - ```bash - npx reloaderoo@latest inspect call-tool button --params '{"simulatorUuid": "SIMULATOR_UUID", "buttonType": "home"}' -- node build/index.js - ``` -- **`describe_ui`**: Gets the UI hierarchy of the current screen. - ```bash - npx reloaderoo@latest inspect call-tool describe_ui --params '{"simulatorUuid": "SIMULATOR_UUID"}' -- node build/index.js - ``` -- **`gesture`**: Performs a pre-defined gesture. - ```bash - npx reloaderoo@latest inspect call-tool gesture --params '{"simulatorUuid": "SIMULATOR_UUID", "preset": "scroll-up"}' -- node build/index.js - ``` -- **`key_press`**: Simulates a key press. - ```bash - npx reloaderoo@latest inspect call-tool key_press --params '{"simulatorUuid": "SIMULATOR_UUID", "keyCode": 40}' -- node build/index.js - ``` -- **`key_sequence`**: Simulates a sequence of key presses. - ```bash - npx reloaderoo@latest inspect call-tool key_sequence --params '{"simulatorUuid": "SIMULATOR_UUID", "keyCodes": [40, 42, 44]}' -- node build/index.js - ``` -- **`long_press`**: Performs a long press at coordinates. - ```bash - npx reloaderoo@latest inspect call-tool long_press --params '{"simulatorUuid": "SIMULATOR_UUID", "x": 100, "y": 200, "duration": 1500}' -- node build/index.js - ``` -- **`screenshot`**: Takes a screenshot. - ```bash - npx reloaderoo@latest inspect call-tool screenshot --params '{"simulatorUuid": "SIMULATOR_UUID"}' -- node build/index.js - ``` -- **`swipe`**: Performs a swipe gesture. - ```bash - npx reloaderoo@latest inspect call-tool swipe --params '{"simulatorUuid": "SIMULATOR_UUID", "x1": 100, "y1": 200, "x2": 100, "y2": 400}' -- node build/index.js - ``` -- **`tap`**: Performs a tap at coordinates. - ```bash - npx reloaderoo@latest inspect call-tool tap --params '{"simulatorUuid": "SIMULATOR_UUID", "x": 100, "y": 200}' -- node build/index.js - ``` -- **`touch`**: Simulates a touch down or up event. - ```bash - npx reloaderoo@latest inspect call-tool touch --params '{"simulatorUuid": "SIMULATOR_UUID", "x": 100, "y": 200, "down": true}' -- node build/index.js - ``` -- **`type_text`**: Types text into the focused element. - ```bash - npx reloaderoo@latest inspect call-tool type_text --params '{"simulatorUuid": "SIMULATOR_UUID", "text": "Hello, World!"}' -- node build/index.js - ``` - -### Resources - -- **Read devices resource**: - ```bash - npx reloaderoo@latest inspect read-resource "xcodebuildmcp://devices" -- node build/index.js - ``` -- **Read simulators resource**: - ```bash - npx reloaderoo@latest inspect read-resource "xcodebuildmcp://simulators" -- node build/index.js - ``` -- **Read doctor resource**: - ```bash - npx reloaderoo@latest inspect read-resource "xcodebuildmcp://doctor" -- node build/index.js - ``` diff --git a/docs/RELOADEROO_XCODEBUILDMCP_PRIMER.md b/docs/RELOADEROO_XCODEBUILDMCP_PRIMER.md deleted file mode 100644 index eefada1d..00000000 --- a/docs/RELOADEROO_XCODEBUILDMCP_PRIMER.md +++ /dev/null @@ -1,325 +0,0 @@ -# Reloaderoo + XcodeBuildMCP: Curated CLI Primer - -Use this primer to drive XcodeBuildMCP entirely through Reloaderoo—treating it like a CLI. It is designed to be included in your agent’s context to show exactly how to invoke the specific tools your project needs. - -Why this file: -- XcodeBuildMCP exposes many tools. Dumping the full tool surface into the context wastes tokens. -- Instead, copy this file into your project and delete everything you don’t need. Keep only the commands relevant to your workflow (e.g., just Simulator tools). -- Your trimmed version becomes a small, project‑specific reference that tells your agent precisely which Reloaderoo tool calls to make. - -How to use this primer: -1. Copy this file into your repo (e.g., docs/xcodebuildmcp_primer.md or AGENTS.md). -2. Remove all sections and commands you don’t use. Keep it minimal. -3. Replace placeholders with your real values (paths, schemes, simulator UUIDs/Names, bundle IDs, etc.). -4. Use the quiet (-q) examples to reduce noise; pipe output to jq when you only need the content. -5. Include your curated file in the agent context whenever you want it to call XcodeBuildMCP via Reloaderoo. - -Conventions in the examples: -- Calls use: npx reloaderoo@latest inspect … -q -- npx xcodebuildmcp@latest -- Parameters are passed as JSON via --params. -- Resources are read with read-resource (e.g., xcodebuildmcp://simulators). -- Use jq -r '.contents[].text' to extract the textual results when needed. - -Keep it small. The smaller your curated primer, the less context your agent needs—and the cheaper, faster, and more reliable your interactions will be. - -## Installation - -Reloaderoo is available via npm and can be used with npx for universal compatibility. - -```bash -# Use npx to run reloaderoo -npx reloaderoo@latest --help -``` - -## Hint - -Use jq to parse the output to get just the content response: - -```bash - npx reloaderoo@latest inspect read-resource "xcodebuildmcp://simulators" -q -- npx xcodebuildmcp@latest | jq -r '.contents[].text' - ``` - -**Example Tool Calls:** - -## iOS Device Development - -- **`build_device`**: Builds an app for a physical device. - ```bash - npx reloaderoo@latest inspect -q call-tool build_device --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` -- **`get_device_app_path`**: Gets the `.app` bundle path for a device build. - ```bash - npx reloaderoo@latest inspect call-tool get_device_app_path --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` -- **`install_app_device`**: Installs an app on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool install_app_device --params '{"deviceId": "DEVICE_UDID", "appPath": "/path/to/MyApp.app"}' -q -- npx xcodebuildmcp@latest - ``` -- **`launch_app_device`**: Launches an app on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool launch_app_device --params '{"deviceId": "DEVICE_UDID", "bundleId": "com.example.MyApp"}' -q -- npx xcodebuildmcp@latest - ``` -- **`list_devices`**: Lists connected physical devices. - ```bash - npx reloaderoo@latest inspect call-tool list_devices --params '{}' -q -- npx xcodebuildmcp@latest - ``` -- **`stop_app_device`**: Stops an app on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool stop_app_device --params '{"deviceId": "DEVICE_UDID", "processId": 12345}' -q -- npx xcodebuildmcp@latest - ``` -- **`test_device`**: Runs tests on a physical device. - ```bash - npx reloaderoo@latest inspect call-tool test_device --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "deviceId": "DEVICE_UDID"}' -q -- npx xcodebuildmcp@latest - ``` - -## iOS Simulator Development - -- **`boot_sim`**: Boots a simulator. - ```bash - npx reloaderoo@latest inspect call-tool boot_sim --params '{"simulatorUuid": "SIMULATOR_UUID"}' -q -- npx xcodebuildmcp@latest - ``` -- **`build_run_sim`**: Builds and runs an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool build_run_sim --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "simulatorName": "iPhone 16"}' -q -- npx xcodebuildmcp@latest - ``` -- **`build_sim`**: Builds an app for a simulator. - ```bash - npx reloaderoo@latest inspect call-tool build_sim --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "simulatorName": "iPhone 16"}' -q -- npx xcodebuildmcp@latest - ``` -- **`get_sim_app_path`**: Gets the `.app` bundle path for a simulator build. - ```bash - npx reloaderoo@latest inspect call-tool get_sim_app_path --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "platform": "iOS Simulator", "simulatorName": "iPhone 16"}' -q -- npx xcodebuildmcp@latest - ``` -- **`install_app_sim`**: Installs an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool install_app_sim --params '{"simulatorUuid": "SIMULATOR_UUID", "appPath": "/path/to/MyApp.app"}' -q -- npx xcodebuildmcp@latest - ``` -- **`launch_app_logs_sim`**: Launches an app on a simulator with log capture. - ```bash - npx reloaderoo@latest inspect call-tool launch_app_logs_sim --params '{"simulatorUuid": "SIMULATOR_UUID", "bundleId": "com.example.MyApp"}' -q -- npx xcodebuildmcp@latest - ``` -- **`launch_app_sim`**: Launches an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool launch_app_sim --params '{"simulatorName": "iPhone 16", "bundleId": "com.example.MyApp"}' -q -- npx xcodebuildmcp@latest - ``` -- **`list_sims`**: Lists available simulators. - ```bash - npx reloaderoo@latest inspect call-tool list_sims --params '{}' -q -- npx xcodebuildmcp@latest - ``` -- **`open_sim`**: Opens the Simulator application. - ```bash - npx reloaderoo@latest inspect call-tool open_sim --params '{}' -q -- npx xcodebuildmcp@latest - ``` -- **`stop_app_sim`**: Stops an app on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool stop_app_sim --params '{"simulatorName": "iPhone 16", "bundleId": "com.example.MyApp"}' -q -- npx xcodebuildmcp@latest - ``` -- **`test_sim`**: Runs tests on a simulator. - ```bash - npx reloaderoo@latest inspect call-tool test_sim --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme", "simulatorName": "iPhone 16"}' -q -- npx xcodebuildmcp@latest - ``` - -## Log Capture & Management - -- **`start_device_log_cap`**: Starts log capture for a physical device. - ```bash - npx reloaderoo@latest inspect call-tool start_device_log_cap --params '{"deviceId": "DEVICE_UDID", "bundleId": "com.example.MyApp"}' -q -- npx xcodebuildmcp@latest - ``` -- **`start_sim_log_cap`**: Starts log capture for a simulator. - ```bash - npx reloaderoo@latest inspect call-tool start_sim_log_cap --params '{"simulatorUuid": "SIMULATOR_UUID", "bundleId": "com.example.MyApp"}' -q -- npx xcodebuildmcp@latest - ``` -- **`stop_device_log_cap`**: Stops log capture for a physical device. - ```bash - npx reloaderoo@latest inspect call-tool stop_device_log_cap --params '{"logSessionId": "SESSION_ID"}' -q -- npx xcodebuildmcp@latest - ``` -- **`stop_sim_log_cap`**: Stops log capture for a simulator. - ```bash - npx reloaderoo@latest inspect call-tool stop_sim_log_cap --params '{"logSessionId": "SESSION_ID"}' -q -- npx xcodebuildmcp@latest - ``` - -## macOS Development - -- **`build_macos`**: Builds a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool build_macos --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` -- **`build_run_macos`**: Builds and runs a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool build_run_macos --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` -- **`get_mac_app_path`**: Gets the `.app` bundle path for a macOS build. - ```bash - npx reloaderoo@latest inspect call-tool get_mac_app_path --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` -- **`launch_mac_app`**: Launches a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool launch_mac_app --params '{"appPath": "/Applications/Calculator.app"}' -q -- npx xcodebuildmcp@latest - ``` -- **`stop_mac_app`**: Stops a macOS app. - ```bash - npx reloaderoo@latest inspect call-tool stop_mac_app --params '{"appName": "Calculator"}' -q -- npx xcodebuildmcp@latest - ``` -- **`test_macos`**: Runs tests for a macOS project. - ```bash - npx reloaderoo@latest inspect call-tool test_macos --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` - -## Project Discovery - -- **`discover_projs`**: Discovers Xcode projects and workspaces. - ```bash - npx reloaderoo@latest inspect call-tool discover_projs --params '{"workspaceRoot": "/path/to/workspace"}' -q -- npx xcodebuildmcp@latest - ``` -- **`get_app_bundle_id`**: Gets an app's bundle identifier. - ```bash - npx reloaderoo@latest inspect call-tool get_app_bundle_id --params '{"appPath": "/path/to/MyApp.app"}' -q -- npx xcodebuildmcp@latest - ``` -- **`get_mac_bundle_id`**: Gets a macOS app's bundle identifier. - ```bash - npx reloaderoo@latest inspect call-tool get_mac_bundle_id --params '{"appPath": "/Applications/Calculator.app"}' -q -- npx xcodebuildmcp@latest - ``` -- **`list_schemes`**: Lists schemes in a project or workspace. - ```bash - npx reloaderoo@latest inspect call-tool list_schemes --params '{"projectPath": "/path/to/MyProject.xcodeproj"}' -q -- npx xcodebuildmcp@latest - ``` -- **`show_build_settings`**: Shows build settings for a scheme. - ```bash - npx reloaderoo@latest inspect call-tool show_build_settings --params '{"projectPath": "/path/to/MyProject.xcodeproj", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` - -## Project Scaffolding - -- **`scaffold_ios_project`**: Scaffolds a new iOS project. - ```bash - npx reloaderoo@latest inspect call-tool scaffold_ios_project --params '{"projectName": "MyNewApp", "outputPath": "/path/to/projects"}' -q -- npx xcodebuildmcp@latest - ``` -- **`scaffold_macos_project`**: Scaffolds a new macOS project. - ```bash - npx reloaderoo@latest inspect call-tool scaffold_macos_project --params '{"projectName": "MyNewMacApp", "outputPath": "/path/to/projects"}' -q -- npx xcodebuildmcp@latest - ``` - -## Project Utilities - -- **`clean`**: Cleans build artifacts. - ```bash - # For a project - npx reloaderoo@latest inspect call-tool clean --params '{"projectPath": "/path/to/MyProject.xcodeproj"}' -q -- npx xcodebuildmcp@latest - # For a workspace - npx reloaderoo@latest inspect call-tool clean --params '{"workspacePath": "/path/to/MyWorkspace.xcworkspace", "scheme": "MyScheme"}' -q -- npx xcodebuildmcp@latest - ``` - -## Simulator Management - -- **`reset_sim_location`**: Resets a simulator's location. - ```bash - npx reloaderoo@latest inspect call-tool reset_sim_location --params '{"simulatorUuid": "SIMULATOR_UUID"}' -q -- npx xcodebuildmcp@latest - ``` -- **`set_sim_appearance`**: Sets a simulator's appearance (dark/light mode). - ```bash - npx reloaderoo@latest inspect call-tool set_sim_appearance --params '{"simulatorUuid": "SIMULATOR_UUID", "mode": "dark"}' -q -- npx xcodebuildmcp@latest - ``` -- **`set_sim_location`**: Sets a simulator's GPS location. - ```bash - npx reloaderoo@latest inspect call-tool set_sim_location --params '{"simulatorUuid": "SIMULATOR_UUID", "latitude": 37.7749, "longitude": -122.4194}' -q -- npx xcodebuildmcp@latest - ``` -- **`sim_statusbar`**: Overrides a simulator's status bar. - ```bash - npx reloaderoo@latest inspect call-tool sim_statusbar --params '{"simulatorUuid": "SIMULATOR_UUID", "dataNetwork": "wifi"}' -q -- npx xcodebuildmcp@latest - ``` - -## Swift Package Manager - -- **`swift_package_build`**: Builds a Swift package. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_build --params '{"packagePath": "/path/to/package"}' -q -- npx xcodebuildmcp@latest - ``` -- **`swift_package_clean`**: Cleans a Swift package. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_clean --params '{"packagePath": "/path/to/package"}' -q -- npx xcodebuildmcp@latest - ``` -- **`swift_package_list`**: Lists running Swift package processes. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_list --params '{}' -q -- npx xcodebuildmcp@latest - ``` -- **`swift_package_run`**: Runs a Swift package executable. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_run --params '{"packagePath": "/path/to/package"}' -q -- npx xcodebuildmcp@latest - ``` -- **`swift_package_stop`**: Stops a running Swift package process. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_stop --params '{"pid": 12345}' -q -- npx xcodebuildmcp@latest - ``` -- **`swift_package_test`**: Tests a Swift package. - ```bash - npx reloaderoo@latest inspect call-tool swift_package_test --params '{"packagePath": "/path/to/package"}' -q -- npx xcodebuildmcp@latest - ``` - -## System Doctor - -- **`doctor`**: Runs system diagnostics. - ```bash - npx reloaderoo@latest inspect call-tool doctor --params '{}' -q -- npx xcodebuildmcp@latest - ``` - -## UI Testing & Automation - -- **`button`**: Simulates a hardware button press. - ```bash - npx reloaderoo@latest inspect call-tool button --params '{"simulatorUuid": "SIMULATOR_UUID", "buttonType": "home"}' -q -- npx xcodebuildmcp@latest - ``` -- **`describe_ui`**: Gets the UI hierarchy of the current screen. - ```bash - npx reloaderoo@latest inspect call-tool describe_ui --params '{"simulatorUuid": "SIMULATOR_UUID"}' -q -- npx xcodebuildmcp@latest - ``` -- **`gesture`**: Performs a pre-defined gesture. - ```bash - npx reloaderoo@latest inspect call-tool gesture --params '{"simulatorUuid": "SIMULATOR_UUID", "preset": "scroll-up"}' -q -- npx xcodebuildmcp@latest - ``` -- **`key_press`**: Simulates a key press. - ```bash - npx reloaderoo@latest inspect call-tool key_press --params '{"simulatorUuid": "SIMULATOR_UUID", "keyCode": 40}' -q -- npx xcodebuildmcp@latest - ``` -- **`key_sequence`**: Simulates a sequence of key presses. - ```bash - npx reloaderoo@latest inspect call-tool key_sequence --params '{"simulatorUuid": "SIMULATOR_UUID", "keyCodes": [40, 42, 44]}' -q -- npx xcodebuildmcp@latest - ``` -- **`long_press`**: Performs a long press at coordinates. - ```bash - npx reloaderoo@latest inspect call-tool long_press --params '{"simulatorUuid": "SIMULATOR_UUID", "x": 100, "y": 200, "duration": 1500}' -q -- npx xcodebuildmcp@latest - ``` -- **`screenshot`**: Takes a screenshot. - ```bash - npx reloaderoo@latest inspect call-tool screenshot --params '{"simulatorUuid": "SIMULATOR_UUID"}' -q -- npx xcodebuildmcp@latest - ``` -- **`swipe`**: Performs a swipe gesture. - ```bash - npx reloaderoo@latest inspect call-tool swipe --params '{"simulatorUuid": "SIMULATOR_UUID", "x1": 100, "y1": 200, "x2": 100, "y2": 400}' -q -- npx xcodebuildmcp@latest - ``` -- **`tap`**: Performs a tap at coordinates. - ```bash - npx reloaderoo@latest inspect call-tool tap --params '{"simulatorUuid": "SIMULATOR_UUID", "x": 100, "y": 200}' -q -- npx xcodebuildmcp@latest - ``` -- **`touch`**: Simulates a touch down or up event. - ```bash - npx reloaderoo@latest inspect call-tool touch --params '{"simulatorUuid": "SIMULATOR_UUID", "x": 100, "y": 200, "down": true}' -q -- npx xcodebuildmcp@latest - ``` -- **`type_text`**: Types text into the focused element. - ```bash - npx reloaderoo@latest inspect call-tool type_text --params '{"simulatorUuid": "SIMULATOR_UUID", "text": "Hello, World!"}' -q -- npx xcodebuildmcp@latest - ``` - -## Resources - -- **Read devices resource**: - ```bash - npx reloaderoo@latest inspect read-resource "xcodebuildmcp://devices" -q -- npx xcodebuildmcp@latest - ``` -- **Read simulators resource**: - ```bash - npx reloaderoo@latest inspect read-resource "xcodebuildmcp://simulators" -q -- npx xcodebuildmcp@latest - ``` -- **Read doctor resource**: - ```bash - npx reloaderoo@latest inspect read-resource "xcodebuildmcp://doctor" -q -- npx xcodebuildmcp@latest - ``` diff --git a/docs/SENTRY_AUDIT_REPORT_2026-02-11.md b/docs/SENTRY_AUDIT_REPORT_2026-02-11.md new file mode 100644 index 00000000..4af47a89 --- /dev/null +++ b/docs/SENTRY_AUDIT_REPORT_2026-02-11.md @@ -0,0 +1,184 @@ +# Investigation: Sentry Telemetry Scope and Privacy Audit (#204) + +## Summary +The current implementation captured too much data by default (including broad MCP instrumentation and PII-heavy tagging). The fix narrows telemetry to internal runtime failures only, keeps tracing enabled for observability, disables MCP input/output capture, and adds payload scrubbing/redaction. + +## Symptoms +- Sentry was initialized with `sendDefaultPii: true` and `tracesSampleRate: 1.0`. +- MCP server was wrapped with `wrapMcpServerWithSentry`, enabling broad per-call instrumentation. +- Logger default sent all `error` logs to Sentry, including user-domain failures. +- Sentry tags included sensitive environment/system values (HOME, USER, PATH, Xcode paths). +- Privacy docs understated actual collection scope. + +## Investigation Log + +### Phase 1 - Issue and baseline audit +**Hypothesis:** Telemetry defaults and wrapper behavior were broader than documented. +**Findings:** Issue #204 correctly identified mismatch between docs and implementation. +**Evidence:** +- GitHub issue: https://github.com/getsentry/XcodeBuildMCP/issues/204 +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/src/utils/sentry.ts` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/src/server/server.ts` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/src/utils/logger.ts` +**Conclusion:** Confirmed. + +### Phase 2 - Runtime path tracing +**Hypothesis:** User-domain tool failures were reaching Sentry through logger defaults. +**Findings:** `log('error', ...)` implicitly captured to Sentry unless overridden; many tool/runtime paths emit user-domain errors at error level. +**Evidence:** +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/src/utils/logger.ts` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/src/utils/build-utils.ts` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/src/runtime/tool-invoker.ts` +**Conclusion:** Confirmed. + +### Phase 3 - Version and docs alignment +**Hypothesis:** Sentry SDK was not at latest patch and docs were not aligned with behavior. +**Findings:** `@sentry/node` moved from `^10.37.0` to latest `10.38.0` and docs were updated to match internal-only policy. +**Evidence:** +- Command: `npm view @sentry/node version` returned `10.38.0` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/package.json` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/docs/PRIVACY.md` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/docs/CONFIGURATION.md` +- `/Users/cameroncooke/.codex/worktrees/a59a/XcodeBuildMCP/README.md` +**Conclusion:** Confirmed. + +## Root Cause +Three defaults combined to over-collect telemetry: +1. `sendDefaultPii: true` and tracing at 100% in Sentry init. +2. `wrapMcpServerWithSentry` around the MCP server. +3. Logger behavior that captured every `error` log to Sentry by default. + +This effectively blurred boundaries between internal platform failures and user-domain build/config/runtime failures, and increased risk of sensitive metadata leakage. + +## Changes Implemented +1. Sentry initialization hardened (`sendDefaultPii: false`, `tracesSampleRate: 1.0`, breadcrumbs disabled, `beforeSend` scrubbing/redaction). +2. Sensitive tags/context removed (no env dumps, no HOME/USER/PATH/Xcode path tagging). +3. Restored MCP wrapper with explicit safe options (`recordInputs: false`, `recordOutputs: false`) to keep tool-level observability without payload capture. +4. Logger changed to explicit opt-in capture only (`context.sentry === true`). +5. Internal boundary capture retained only where appropriate (startup/shutdown/fatal daemon internal errors). +6. Added tests for explicit capture policy and path redaction. +7. Updated privacy/config/README/architecture docs and changelog. + +## Eliminated Hypotheses +- "Only MCP-level faults are captured today": Eliminated (not true before this patch due to logger defaults and wrapper). +- "Docs accurately reflected telemetry scope": Eliminated. + +## Recommendations +1. Keep Sentry capture explicit and centralized to internal runtime boundaries. +2. Avoid adding environment or filesystem metadata to telemetry tags. +3. Preserve redaction tests to prevent regressions. +4. Continue documenting telemetry scope in user-facing docs whenever behavior changes. + +## Preventive Measures +- CI should keep redaction and logger policy tests running by default. +- Any future telemetry additions should require explicit privacy review with docs update in same PR. + +## Validation +All relevant quality checks were executed after changes: + +- `npm run format:check` ✅ +- `npm run lint` ✅ +- `npm run typecheck` ✅ +- `npm run build` ✅ +- `npm test` ✅ (117 test files passed; 1274 tests passed, 15 skipped) + +Notes: +- Test output includes expected stderr from negative-path parser tests in `src/utils/__tests__/nskeyedarchiver-parser.test.ts`; test run still passed. + +## Addendum: MCP Wrapper Capture Semantics (verified against SDK 10.38.0) +- `wrapMcpServerWithSentry` resolves options as: + - `recordInputs: options?.recordInputs ?? sendDefaultPii` + - `recordOutputs: options?.recordOutputs ?? sendDefaultPii` +- For MCP request spans, `tools/call` and `prompts/get` arguments are added as `mcp.request.argument.*` attributes when `recordInputs=true`. +- Tool/prompt results are added as `mcp.tool.result*` / `mcp.prompt.result*` attributes when `recordOutputs=true`. +- Built-in MCP PII filtering in the SDK only strips network-level fields (`client.address`, `client.port`, `mcp.resource.uri`) when `sendDefaultPii=false`; it does not by itself scrub arbitrary tool argument/output payloads. + +Evidence (source-inspected package build): +- `@sentry/core@10.38.0` `build/cjs/integrations/mcp-server/index.js` +- `@sentry/core@10.38.0` `build/cjs/integrations/mcp-server/methodConfig.js` +- `@sentry/core@10.38.0` `build/cjs/integrations/mcp-server/resultExtraction.js` +- `@sentry/core@10.38.0` `build/cjs/integrations/mcp-server/piiFiltering.js` + +## Addendum: Live Validation (2026-02-12) + +### Findings +- Runtime config/dependency tags are being attached to issues when events are captured after bootstrap context is set. +- Example: issue `XCODEBUILDMCP-6` includes `runtime.mode`, `xcode.version`, `xcode.xcodebuild_path`, `axe.source`, and config tags. +- Startup-phase config parse warnings can occur before full runtime context is attached, so those earlier events may not show the full tag set. +- MCP wrapper instrumentation is active in-process: + - Local debug output shows sampled MCP spans for `initialize`, `notifications/initialized`, and `tools/call session_show_defaults`. + - Local exporter reports spans exported. +- Despite local span export, Sentry project query for spans currently returns `count() = 0` in the tested time window. + +### Evidence +- Local MCP call validation: + - `session_show_defaults` invoked over stdio client; server started successfully. +- Local in-memory instrumentation validation: + - Debug logs show: + - `Starting sampled root span op: mcp.server` + - `Finishing ... tools/call session_show_defaults` + - `SpanExporter exported 3 spans` +- Sentry MCP queries: + - Spans in last hour: `count() = 0` + - Transactions in last hour: `count() = 0` + - Trace for issue `XCODEBUILDMCP-6`: `Total Spans: 0, Errors: 1` + +## Addendum: MCP View + Logs Explorer Deep Dive (2026-02-12) + +### Scope +Investigated two active symptoms: +- MCP tools are not visible in Sentry MCP view. +- Logs are not visible in Sentry Logs Explorer. + +### Key Findings +- Error events are ingesting correctly in the target project. + - Sentry query: errors in last 24h = `11`. +- Logs and spans datasets remain empty in the same project/time windows. + - Sentry query: logs in last 24h/7d = `0`. + - Sentry query: spans in last 24h/14d = `0`. + - Sentry query: transactions in last 14d/15m = `0`. +- SDK-side emission is working for both logs and transactions. + - Direct probe emitted: + - `Sentry.logger.info('envelope logger probe ...')` + - `Sentry.startSpan({ forceTransaction: true, ... })` + - Runtime instrumentation confirmed envelope item types sent: + - `ENVELOPE_ITEM_TYPES log` + - `ENVELOPE_ITEM_TYPES transaction` + - plus expected `event`/`session` items. +- Despite emitted envelopes, Sentry queries still return zero logs/spans/transactions. + - Strongly indicates an ingestion/storage/configuration issue outside current app code path. + +### Code-Path Validation +- MCP wrapper is enabled with safe options: + - `src/server/server.ts:72` uses `wrapMcpServerWithSentry(..., { recordInputs: false, recordOutputs: false })`. +- Sentry logs pipeline is enabled: + - `src/utils/sentry.ts:282` sets `enableLogs: true`. + - `src/utils/sentry.ts:286` sets `beforeSendLog: redactLog`. +- Logger forwards only explicit opt-in internal logs: + - `src/utils/logger.ts:56` (`context?.sentry === true`). + - `src/utils/logger.ts:236` fallback uses `captureMessage` only if logger method is unavailable. +- Runtime split is real: + - Daemon handles `tool.invoke` requests (`src/daemon/daemon-server.ts:117`), including `runtime: 'daemon'` (`src/daemon/daemon-server.ts:128`). + - CLI paths route many invocations through daemon (`src/runtime/tool-invoker.ts:192`). + - MCP wrapper only covers stdio MCP server runtime (`src/server/server.ts:72`). + +### Root Cause Assessment (Current Confidence) +- Most likely primary blocker: Sentry-side configuration/entitlement/pipeline for traces and logs in this project/org (not client emission). +- Secondary (not primary) code risk: + - `process.exit(...)` without explicit `Sentry.flush/close` in shutdown paths can still drop buffered telemetry in some paths: + - `src/server/start-mcp-server.ts:68` + - `src/server/start-mcp-server.ts:83` + - `src/daemon.ts:303` + - `src/daemon.ts:310` + - `src/daemon.ts:402` + +### Eliminated Hypotheses +- "MCP wrapper is removed or disabled." Eliminated. +- "Logs are not being captured by SDK at all." Eliminated (capture hooks + envelope inspection confirm capture/send). +- "Transactions are not being created by SDK at all." Eliminated (manual forced transaction emitted and sent). + +### Recommended Next Steps +1. Verify project-level traces/logs ingestion settings in Sentry (`sentry/xcodebuildmcp`) and any org-level sampling/filtering rules dropping transactions/logs. +2. Verify account/product entitlement for Logs and Performance on this project. +3. Add explicit shutdown drain in app code (`Sentry.flush`/`Sentry.close`) before `process.exit(...)` to reduce telemetry loss on fast shutdown. +4. Keep MCP-view expectation scoped to MCP stdio runtime; add daemon-specific spans if daemon tool-call observability is required in traces. diff --git a/docs/SESSION_DEFAULTS.md b/docs/SESSION_DEFAULTS.md new file mode 100644 index 00000000..afe318ef --- /dev/null +++ b/docs/SESSION_DEFAULTS.md @@ -0,0 +1,73 @@ +# Session Defaults + +By default, XcodeBuildMCP uses a session-aware mode. The client sets shared defaults once (simulator, device, project/workspace, scheme, etc.) and all tools reuse them. This reduces schema size and repeated payloads and ensures a more deterministic experience. + +## How it works +- Agent calls `session_set_defaults` once at the start of a workflow. +- Tools reuse those defaults automatically. +- Agent can call `session_show_defaults` to inspect current values. +- Agent can call `session_clear_defaults` to clear values when switching contexts. +- Defaults can be seeded from `.xcodebuildmcp/config.yaml` at server startup. + +See the session-management tools in [TOOLS.md](TOOLS.md). + +## Opting out +If you prefer explicit parameters on every tool call, set `disableSessionDefaults: true` in your `.xcodebuildmcp/config.yaml` file. + +This restores the legacy schemas with per-call parameters while still honoring any defaults you choose to set. + +See [CONFIGURATION.md](CONFIGURATION.md) for more information. + +## Persisting defaults +Session defaults can be persisted between sessions by setting the `persist` flag to `true` on `session_set_defaults`. This writes to `.xcodebuildmcp/config.yaml` at the root of your workspace. + +The persistence is patch-only: only keys provided in that call are written (plus any removals needed for mutual exclusivity). + +You can also manually create the config file to essentially seed the defaults at startup; see [CONFIGURATION.md](CONFIGURATION.md) for more information. + +## Namespaced profiles +Session defaults support named profiles so one workspace can keep separate defaults for iOS/watchOS/macOS (or any custom profile names). + +- Use `session_use_defaults_profile` to switch the active profile (existing profiles only). +- Existing tools (`session_set_defaults`, `session_show_defaults`, build/test tools) use the active profile automatically. +- `session_set_defaults` can also accept `profile` to switch and set in one call; use `createIfNotExists: true` to create a new profile intentionally. +- Profiles are strictly isolated: values are not inherited from global defaults or other profiles. +- The unnamed global defaults profile exists for backwards compatibility and is the default active profile when no named profile is selected. +- There is always an active profile context: either a named profile or `global`. +- Use `global: true` to switch back to the unnamed global profile. +- Set `persist: true` on `session_use_defaults_profile` to write `activeSessionDefaultsProfile` in `.xcodebuildmcp/config.yaml`. + +## Recommended startup flow (monorepo / multi-target) +Copy/paste this sequence when starting a new session: + +```json +{"name":"session_use_defaults_profile","arguments":{"profile":"ios","persist":true}} +{"name":"session_set_defaults","arguments":{ + "workspacePath":"/repo/MyApp.xcworkspace", + "scheme":"MyApp-iOS", + "simulatorName":"iPhone 16 Pro", + "persist":true +}} +{"name":"session_show_defaults","arguments":{}} +``` + +Switch targets later in the same session: + +```json +{"name":"session_use_defaults_profile","arguments":{"profile":"watch","persist":true}} +{"name":"session_set_defaults","arguments":{ + "workspacePath":"/repo/MyApp.xcworkspace", + "scheme":"MyApp-watchOS", + "simulatorName":"Apple Watch Series 10 (45mm)", + "persist":true +}} +``` + +Isolation example: +- Global profile has `workspacePath: /repo/MyApp.xcworkspace` +- Active profile is `watch` with only `scheme` set +- Result: `watch` does not see global `workspacePath` until you set it on `watch` or switch back to `global` + +## Related docs +- Configuration options: [CONFIGURATION.md](CONFIGURATION.md) +- Tools reference: [TOOLS.md](TOOLS.md) diff --git a/docs/SKILLS.md b/docs/SKILLS.md new file mode 100644 index 00000000..fbf59bb4 --- /dev/null +++ b/docs/SKILLS.md @@ -0,0 +1,57 @@ +# XcodeBuildMCP Skill + +XcodeBuildMCP now includes two optional agent skills: + +- **MCP Skill**: Primes the agent with instructions on how to use the MCP server's tools (optional when using the MCP server). + +- **CLI Skill**: Primes the agent with instructions on how to navigate the CLI (recommended when using the CLI). + +## Easiest way to install + +Install via the interactive installer and follow the on-screen instructions. + +```bash +curl -fsSL https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v2.0.7/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh +``` + +## Automated installation + +Useful for CI/CD pipelines or for agentic installation. `--skill` should be set to either `mcp` or `cli` to install the appropriate skill. + +### Install (Claude Code) + +```bash +curl -fsSL https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v2.0.7/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --claude --remove-conflict --skill +``` + +### Install (Cursor) + +```bash +curl -fsSL https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v2.0.7/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --cursor --remove-conflict --skill +``` + +### Install (Codex CLI) + +```bash +curl -fsSL https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v2.0.7/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --codex --remove-conflict --skill +``` + +### Install (Other Clients) + +For other clients if you know the path to the skills directory you can pass the `--dest` flag. + +```bash +curl -fsSL https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v2.0.7/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --dest /path/to/skills --remove-conflict --skill +``` + +## Unsupporting Clients + +Some MCP clients that do not yet support skills. Use the skill content as a concise, static instruction prompt: + +1. Open `skills/xcodebuildmcp[-cli]/SKILL.md`. +2. Copy the body (everything below the YAML frontmatter). +3. Paste it into the client’s global or project-level instructions/rules area. + +## Skills + +To learn more about skills see: [https://agentskills.io/home](https://agentskills.io/home). \ No newline at end of file diff --git a/docs/SMITHERY.md b/docs/SMITHERY.md deleted file mode 100644 index abb64578..00000000 --- a/docs/SMITHERY.md +++ /dev/null @@ -1,273 +0,0 @@ -# TypeScript Servers - -> Deploy and publish TypeScript MCP servers on Smithery using Smithery CLI - -## Overview - -Deploy TypeScript MCP servers using the official MCP SDK with two deployment options: - -* **Remote deployment**: Automatic containerization and infrastructure managed by Smithery -* **Local servers** (Beta): Distribute your server as [MCP bundle](https://github.com/anthropics/mcpb) allowing users to run it locally and one-click install it - -## Prerequisites - -* TypeScript MCP server using the official MCP SDK that exports the MCP server object at entry point -* Node.js 18+ and npm installed locally -* Smithery CLI installed as a dev dependency (`npm i -D @smithery/cli`) - - - **New to MCP servers?** See the [Getting Started guide](/getting_started) to learn how to build TypeScript MCP servers from scratch using the official SDK. - - -## Project Structure - -Your TypeScript project should look like this: - -``` -my-mcp-server/ - smithery.yaml # Smithery configuration - package.json # Node.js dependencies and scripts - tsconfig.json # TypeScript configuration - src/ - index.ts # Your MCP server code with exported createServer function -``` - -## Setup - -### 1. Configure smithery.yaml - -Create a `smithery.yaml` file in your repository root (usually where the `package.json` is): - -#### Remote Deployment (Default) - -```yaml theme={null} -runtime: "typescript" -``` - -#### Local Server (Beta) - -```yaml theme={null} -runtime: "typescript" -target: "local" -``` - - - **Local servers are in beta** - When you set `target: "local"`, your server runs locally on user's machine but is accessible through Smithery's registry for easy discovery and connection by MCP clients. - - -### 2. Configure package.json - -Your `package.json` must include the `module` field pointing to your server entry point: - -```json theme={null} -{ - "name": "my-mcp-server", - "version": "1.0.0", - "type": "module", - "module": "src/index.ts", // Points to your server entry point - "scripts": { - "build": "npx smithery build", - "dev": "npx smithery dev" - }, - "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.3", - "zod": "^3.25.46" - }, - "devDependencies": { - "@smithery/cli": "^1.4.6" - } -} -``` - - - Install the CLI locally with: - - ```bash theme={null} - npm i -D @smithery/cli - ``` - - The Smithery CLI externalizes your SDKs during bundling so your runtime uses the versions you install. If you see a warning about missing SDKs, add them to your dependencies (most servers need `@modelcontextprotocol/sdk` and `@smithery/sdk`). - - -### 3. Ensure Proper Server Structure - -Your TypeScript MCP server must export a default `createServer` function that returns the MCP server object. If you built your server following the [Getting Started guide](/getting_started), it should already have this structure. - -```typescript theme={null} -// src/index.ts -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; - -// Required: Export default createServer function -export default function createServer({ config }) { - // config contains user-provided settings (see configSchema below) - const server = new McpServer({ - name: "Your Server Name", - version: "1.0.0", - }); - - // Register your tools here... - - return server.server; // Must return the MCP server object -} -``` - -**Optional Configuration Schema**: If your server needs user configuration (API keys, settings, etc.), export a `configSchema`: - -```typescript theme={null} -// Optional: If your server doesn't need configuration, omit this -export const configSchema = z.object({ - apiKey: z.string().describe("Your API key"), - timeout: z.number().default(5000).describe("Request timeout in milliseconds"), -}); -``` - -**Where it goes**: Export `configSchema` from the same file as your `createServer` function (typically `src/index.ts`). - -**What it does**: Automatically generates [session configuration](/build/session-config) forms for users connecting to your server. - -## OAuth - - - OAuth is designed only for **remote servers**. OAuth is not available for local servers (`target: "local"`). - - -If your entry module exports `oauth`, Smithery CLI auto-mounts the required OAuth endpoints for you during remote deployment. - -### Export an OAuth provider - -```typescript theme={null} -// src/index.ts -import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js" -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" -import type { OAuthProvider } from "@smithery/sdk" -import { MyProvider } from "./provider.js" - -export default function createServer({ auth }: { auth: AuthInfo }) { - const server = new McpServer({ name: "My MCP", version: "1.0.0" }) - // register tools... - return server.server -} - -export const oauth: OAuthProvider = new MyProvider() // [!code highlight] -``` - -The CLI detects `oauth` and injects the auth routes automatically. For implementing `OAuthServerProvider`, see the [official MCP SDK authorization guide](https://modelcontextprotocol.io/docs/tutorials/security/authorization). - - - **You don't need to implement client registration.** Modern MCP clients use [Client ID Metadata Documents](https://modelcontextprotocol.io/specification/draft/basic/authorization#client-id-metadata-documents) (CIMD). Your server should advertise `client_id_metadata_document_supported: true` in its OAuth metadata — see the [spec requirements](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#implementation-requirements). - - -## Local Development - -Test your server locally using the Smithery CLI: - -```bash theme={null} -# Start development server with interactive playground -npm run dev -``` - -This opens the **Smithery interactive playground** where you can: - -* Test your MCP server tools in real-time -* See tool responses and debug issues -* Validate your configuration schema -* Experiment with different inputs - -## Advanced Build Configuration - -For advanced use cases, you can customize the build process using a `smithery.config.js` file. This is useful for: - -* Marking packages as external (to avoid bundling issues) -* Configuring minification, targets, and other build options -* Adding custom esbuild plugins - -### Configuration File - -Create `smithery.config.js` in your project root: - -```javascript theme={null} -export default { - esbuild: { - // Mark problematic packages as external - external: ["playwright-core", "puppeteer-core"], - - // Enable minification for production - minify: true, - - // Set Node.js target version - target: "node18", - }, -}; -``` - -### Common Use Cases - -**External Dependencies**: If you encounter bundling issues with packages like Playwright or native modules: - -```javascript theme={null} -export default { - esbuild: { - external: ["playwright-core", "sharp", "@grpc/grpc-js"], - }, -}; -``` - -Configuration applies to both `build` and `dev` commands. - -## Deploy - -1. Push your code (including `smithery.yaml`) to GitHub -2. [Connect your GitHub](https://smithery.ai/new) to Smithery (or claim your server if already listed) -3. Navigate to the Deployments tab on your server page -4. Click Deploy to build and host your server - -## Good to Know - - - **Remote Deployment**: When you deploy to Smithery's infrastructure: - - 1. Clone your repository - 2. Parse your `smithery.yaml` to detect TypeScript runtime - 3. Install dependencies with `npm ci` - 4. Build your TypeScript code using the `module` entry point from your `package.json` - 5. Package your server into a containerized HTTP service - 6. Deploy the container to our hosting infrastructure - 7. Send MCP `initialize` and `list_tools` messages with a dummy configuration to discover your server's capabilities - 8. Make it available at `https://server.smithery.ai/your-server` - 9. Handle load balancing, scaling, and monitoring - - **Local Server (Beta)**: When you use `target: "local"`: - - 1. Your server runs locally on user's machine using `npm run dev` - 2. Smithery registers your server in the registry for discovery - 3. MCP clients can find and connect to your local server through Smithery - 4. Your server remains under your control while being accessible to others - - -## Troubleshooting - - - Common issues and solutions: - - **Remote Deployment Issues**: - - * **Missing module field**: Ensure your `package.json` has the `module` field pointing to your entry point - * **Dependencies not found**: All dependencies must be listed in `dependencies` or `devDependencies` - * **Server doesn't build locally**: Before deploying, verify your server builds and runs locally: - ```bash theme={null} - npm install - npm run build - ``` - If this fails, fix any TypeScript compilation errors or missing dependencies first - - **Local Server Issues** (Beta): - - * **Server not discoverable**: Ensure you have `target: "local"` in your `smithery.yaml` - * **Local server won't start**: Verify your server runs with `npm run dev` before expecting registry integration - * **Connection issues**: Make sure your local development environment allows the necessary network connections - - - ---- - -> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://smithery.ai/docs/llms.txt \ No newline at end of file diff --git a/docs/TEST_RUNNER_ENV_IMPLEMENTATION_PLAN.md b/docs/TEST_RUNNER_ENV_IMPLEMENTATION_PLAN.md deleted file mode 100644 index e294f9e8..00000000 --- a/docs/TEST_RUNNER_ENV_IMPLEMENTATION_PLAN.md +++ /dev/null @@ -1,423 +0,0 @@ -# TEST_RUNNER_ Environment Variables Implementation Plan - -## Problem Statement - -**GitHub Issue**: [#101 - Support TEST_RUNNER_ prefixed env vars](https://github.com/cameroncooke/XcodeBuildMCP/issues/101) - -**Core Need**: Enable conditional test behavior by passing TEST_RUNNER_ prefixed environment variables from MCP client configurations to xcodebuild test processes. This addresses the specific use case of disabling `runsForEachTargetApplicationUIConfiguration` for faster development testing. - -## Background Context - -### xcodebuild Environment Variable Support - -From the xcodebuild man page: -``` -TEST_RUNNER_ Set an environment variable whose name is prefixed - with TEST_RUNNER_ to have that variable passed, with - its prefix stripped, to all test runner processes - launched during a test action. For example, - TEST_RUNNER_Foo=Bar xcodebuild test ... sets the - environment variable Foo=Bar in the test runner's - environment. -``` - -### User Requirements - -Users want to configure their MCP server with TEST_RUNNER_ prefixed environment variables: - -```json -{ - "mcpServers": { - "XcodeBuildMCP": { - "type": "stdio", - "command": "npx", - "args": ["-y", "xcodebuildmcp@latest"], - "env": { - "TEST_RUNNER_USE_DEV_MODE": "YES" - } - } - } -} -``` - -And have tests that can conditionally execute based on these variables: - -```swift -func testFoo() throws { - let useDevMode = ProcessInfo.processInfo.environment["USE_DEV_MODE"] == "YES" - guard useDevMode else { - XCTFail("Test requires USE_DEV_MODE to be true") - return - } - // Test logic here... -} -``` - -## Current Architecture Analysis - -### XcodeBuildMCP Execution Flow -1. All Xcode commands flow through `executeXcodeBuildCommand()` function -2. Generic `CommandExecutor` interface handles all command execution -3. Test tools exist for device/simulator/macOS platforms -4. Zod schemas provide parameter validation and type safety - -### Key Files in Current Architecture -- `src/utils/CommandExecutor.ts` - Command execution interface -- `src/utils/build-utils.ts` - Contains `executeXcodeBuildCommand` -- `src/mcp/tools/device/test_device.ts` - Device testing tool -- `src/mcp/tools/simulator/test_sim.ts` - Simulator testing tool -- `src/mcp/tools/macos/test_macos.ts` - macOS testing tool -- `src/utils/test/index.ts` - Shared test logic for simulator - -## Solution Analysis - -### Design Options Considered - -1. **Automatic Detection** (❌ Rejected) - - Scan `process.env` for TEST_RUNNER_ variables and always pass them - - **Issue**: Security risk of environment variable leakage - - **Issue**: Unpredictable behavior based on server environment - -2. **Explicit Parameter** (✅ Chosen) - - Add `testRunnerEnv` parameter to test tools - - Users explicitly specify which variables to pass - - **Benefits**: Secure, predictable, well-validated - -3. **Hybrid Approach** (🤔 Future Enhancement) - - Both automatic + explicit with explicit overriding - - **Issue**: Adds complexity, deferred for future consideration - -### Expert Analysis Summary - -**RepoPrompt Analysis**: Comprehensive architectural plan emphasizing security, type safety, and integration with existing patterns. - -**Gemini Analysis**: Confirmed explicit approach as optimal, highlighting: -- Security benefits of explicit allow-list approach -- Architectural soundness of extending CommandExecutor -- Recommendation for automatic prefix handling for better UX - -## Recommended Solution: Explicit Parameter with Automatic Prefix Handling - -### Key Design Decisions - -1. **Security-First**: Only explicitly provided variables are passed (no automatic process.env scanning) -2. **User Experience**: Automatic prefix handling - users provide unprefixed keys -3. **Architecture**: Extend execution layer generically for future extensibility -4. **Validation**: Zod schema enforcement with proper type safety - -### User Experience Design - -**Input** (what users specify): -```json -{ - "testRunnerEnv": { - "USE_DEV_MODE": "YES", - "runsForEachTargetApplicationUIConfiguration": "NO" - } -} -``` - -**Output** (what gets passed to xcodebuild): -```bash -TEST_RUNNER_USE_DEV_MODE=YES \ -TEST_RUNNER_runsForEachTargetApplicationUIConfiguration=NO \ -xcodebuild test ... -``` - -## Implementation Plan - -### Phase 0: Test-Driven Development Setup - -**Objective**: Create reproduction test to validate issue and later prove fix works - -#### Tasks: -- [ ] Create test in `example_projects/iOS/MCPTest` that checks for environment variable -- [ ] Run current test tools to demonstrate limitation (test should fail) -- [ ] Document baseline behavior - -**Test Code Example**: -```swift -func testEnvironmentVariablePassthrough() throws { - let useDevMode = ProcessInfo.processInfo.environment["USE_DEV_MODE"] == "YES" - guard useDevMode else { - XCTFail("Test requires USE_DEV_MODE=YES via TEST_RUNNER_USE_DEV_MODE") - return - } - XCTAssertTrue(true, "Environment variable successfully passed through") -} -``` - -### Phase 1: Core Infrastructure Updates - -**Objective**: Extend CommandExecutor and build utilities to support environment variables - -#### 1.1 Update CommandExecutor Interface - -**File**: `src/utils/CommandExecutor.ts` - -**Changes**: -- Add `CommandExecOptions` type for execution options -- Update `CommandExecutor` type signature to accept optional execution options - -```typescript -export type CommandExecOptions = { - cwd?: string; - env?: Record; -}; - -export type CommandExecutor = ( - args: string[], - description?: string, - quiet?: boolean, - opts?: CommandExecOptions -) => Promise; -``` - -#### 1.2 Update Execution Facade - -**File**: `src/utils/execution/index.ts` - -**Changes**: -- Re-export `CommandExecOptions` type - -```typescript -export type { CommandExecutor, CommandResponse, CommandExecOptions } from '../CommandExecutor.js'; -``` - -#### 1.3 Update Default Command Executor - -**File**: `src/utils/command.ts` - -**Changes**: -- Modify `getDefaultCommandExecutor` to merge `opts.env` with `process.env` when spawning - -```typescript -// In the returned function: -const env = { ...process.env, ...(opts?.env ?? {}) }; -// Pass env and opts?.cwd to spawn/exec call -``` - -#### 1.4 Create Environment Variable Utility - -**File**: `src/utils/environment.ts` - -**Changes**: -- Add `normalizeTestRunnerEnv` function - -```typescript -export function normalizeTestRunnerEnv( - userVars?: Record -): Record { - const result: Record = {}; - if (userVars) { - for (const [key, value] of Object.entries(userVars)) { - if (value !== undefined) { - result[`TEST_RUNNER_${key}`] = value; - } - } - } - return result; -} -``` - -#### 1.5 Update executeXcodeBuildCommand - -**File**: `src/utils/build-utils.ts` - -**Changes**: -- Add optional `execOpts?: CommandExecOptions` parameter (6th parameter) -- Pass execution options through to `CommandExecutor` calls - -```typescript -export async function executeXcodeBuildCommand( - build: { /* existing fields */ }, - runtime: { /* existing fields */ }, - preferXcodebuild = false, - action: 'build' | 'test' | 'archive' | 'analyze' | string, - executor: CommandExecutor = getDefaultCommandExecutor(), - execOpts?: CommandExecOptions, // NEW -): Promise -``` - -### Phase 2: Test Tool Integration - -**Objective**: Add `testRunnerEnv` parameter to all test tools and wire through execution - -#### 2.1 Update Device Test Tool - -**File**: `src/mcp/tools/device/test_device.ts` - -**Changes**: -- Add `testRunnerEnv` to Zod schema with validation -- Import and use `normalizeTestRunnerEnv` -- Pass execution options to `executeXcodeBuildCommand` - -**Schema Addition**: -```typescript -testRunnerEnv: z - .record(z.string(), z.string().optional()) - .optional() - .describe('Test runner environment variables (TEST_RUNNER_ prefix added automatically)') -``` - -**Usage**: -```typescript -const execEnv = normalizeTestRunnerEnv(params.testRunnerEnv); -const testResult = await executeXcodeBuildCommand( - { /* build params */ }, - { /* runtime params */ }, - params.preferXcodebuild ?? false, - 'test', - executor, - { env: execEnv } // NEW -); -``` - -#### 2.2 Update macOS Test Tool - -**File**: `src/mcp/tools/macos/test_macos.ts` - -**Changes**: Same pattern as device test tool -- Schema addition for `testRunnerEnv` -- Import `normalizeTestRunnerEnv` -- Pass execution options to `executeXcodeBuildCommand` - -#### 2.3 Update Simulator Test Tool and Logic - -**File**: `src/mcp/tools/simulator/test_sim.ts` - -**Changes**: -- Add `testRunnerEnv` to schema -- Pass through to `handleTestLogic` - -**File**: `src/utils/test/index.ts` - -**Changes**: -- Update `handleTestLogic` signature to accept `testRunnerEnv?: Record` -- Import and use `normalizeTestRunnerEnv` -- Pass execution options to `executeXcodeBuildCommand` - -### Phase 3: Testing and Validation - -**Objective**: Comprehensive testing coverage for new functionality - -#### 3.1 Unit Tests - -**File**: `src/utils/__tests__/environment.test.ts` - -**Tests**: -- Test `normalizeTestRunnerEnv` with various inputs -- Verify prefix addition -- Verify undefined filtering -- Verify empty input handling - -#### 3.2 Integration Tests - -**Files**: Update existing test files for test tools - -**Tests**: -- Verify `testRunnerEnv` parameter is properly validated -- Verify environment variables are passed through `CommandExecutor` -- Mock executor to verify correct env object construction - -#### 3.3 Tool Export Validation - -**Files**: Test files in each tool directory - -**Tests**: -- Verify schema exports include new `testRunnerEnv` field -- Verify parameter typing is correct - -### Phase 4: End-to-End Validation - -**Objective**: Prove the fix works with real xcodebuild scenarios - -#### 4.1 Reproduction Test Validation - -**Tasks**: -- Run reproduction test from Phase 0 with new `testRunnerEnv` parameter -- Verify test passes (proving env var was successfully passed) -- Document the before/after behavior - -#### 4.2 Real-World Scenario Testing - -**Tasks**: -- Test with actual iOS project using `runsForEachTargetApplicationUIConfiguration` -- Verify performance difference when variable is set -- Test with multiple environment variables -- Test edge cases (empty values, special characters) - -## Security Considerations - -### Security Benefits -- **No Environment Leakage**: Only explicit user-provided variables are passed -- **Command Injection Prevention**: Environment variables passed as separate object, not interpolated into command string -- **Input Validation**: Zod schemas prevent malformed inputs -- **Prefix Enforcement**: Only TEST_RUNNER_ prefixed variables can be set - -### Security Best Practices -- Never log environment variable values (keys only for debugging) -- Filter out undefined values to prevent accidental exposure -- Validate all user inputs through Zod schemas -- Document supported TEST_RUNNER_ variables from Apple's documentation - -## Architectural Benefits - -### Clean Integration -- Extends existing `CommandExecutor` pattern generically -- Maintains backward compatibility (all existing calls remain valid) -- Follows established Zod validation patterns -- Consistent API across all test tools - -### Future Extensibility -- `CommandExecOptions` can support additional execution options (timeout, cwd, etc.) -- Pattern can be extended to other tools that need environment variables -- Generic approach allows for non-TEST_RUNNER_ use cases in the future - -## File Modification Summary - -### New Files -- `src/utils/__tests__/environment.test.ts` - Unit tests for environment utilities - -### Modified Files -- `src/utils/CommandExecutor.ts` - Add execution options types -- `src/utils/execution/index.ts` - Re-export new types -- `src/utils/command.ts` - Update default executor to handle env -- `src/utils/environment.ts` - Add `normalizeTestRunnerEnv` utility -- `src/utils/build-utils.ts` - Update `executeXcodeBuildCommand` signature -- `src/mcp/tools/device/test_device.ts` - Add schema and integration -- `src/mcp/tools/macos/test_macos.ts` - Add schema and integration -- `src/mcp/tools/simulator/test_sim.ts` - Add schema and pass-through -- `src/utils/test/index.ts` - Update `handleTestLogic` for simulator path -- Test files for each modified tool - Add validation tests - -## Success Criteria - -1. **Functionality**: Users can pass `testRunnerEnv` parameter to test tools and have variables appear in test runner environment -2. **Security**: No unintended environment variable leakage from server process -3. **Usability**: Users specify unprefixed variable names for better UX -4. **Compatibility**: All existing test tool calls continue to work unchanged -5. **Validation**: Comprehensive test coverage proves the feature works end-to-end - -## Future Enhancements (Out of Scope) - -1. **Configuration Profiles**: Allow users to define common TEST_RUNNER_ variable sets in config files -2. **Variable Discovery**: Help users discover available TEST_RUNNER_ variables -3. **Build Tool Support**: Extend to build tools if Apple adds similar BUILD_RUNNER_ support -4. **Performance Monitoring**: Track impact of environment variable passing on build times - -## Implementation Timeline - -- **Phase 0**: 1-2 hours (reproduction test setup) -- **Phase 1**: 4-6 hours (infrastructure changes) -- **Phase 2**: 3-4 hours (tool integration) -- **Phase 3**: 4-5 hours (testing) -- **Phase 4**: 2-3 hours (validation) - -**Total Estimated Time**: 14-20 hours - -## Conclusion - -This implementation plan provides a secure, user-friendly, and architecturally sound solution for TEST_RUNNER_ environment variable support. The explicit parameter approach with automatic prefix handling balances security concerns with user experience, while the test-driven development approach ensures we can prove the solution works as intended. - -The plan leverages XcodeBuildMCP's existing patterns and provides a foundation for future environment variable needs across the tool ecosystem. \ No newline at end of file diff --git a/docs/TOOLS-CLI.md b/docs/TOOLS-CLI.md new file mode 100644 index 00000000..3322f503 --- /dev/null +++ b/docs/TOOLS-CLI.md @@ -0,0 +1,192 @@ +# XcodeBuildMCP CLI Tools Reference + +This document lists CLI tool names as exposed by `xcodebuildmcp `. + +XcodeBuildMCP provides 73 canonical tools organized into 13 workflow groups. + +## Workflow Groups + +### Build Utilities (`utilities`) +**Purpose**: Utility tools for cleaning build products and managing build artifacts. (1 tools) + +- `clean` - Defined in iOS Device Development workflow. + + + +### iOS Device Development (`device`) +**Purpose**: Complete iOS development workflow for physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). (14 tools) + +- `build` - Build for device. +- `clean` - Clean build products. +- `discover-projects` - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. +- `get-app-bundle-id` - Extract bundle id from .app. +- `get-app-path` - Get device built app path. +- `install` - Install app on device. +- `launch` - Launch app on device. +- `list` - List connected devices. +- `list-schemes` - List Xcode schemes. +- `show-build-settings` - Show build settings. +- `start-device-log-capture` - Start device log capture. +- `stop` - Stop device app. +- `stop-device-log-capture` - Stop device app and return logs. +- `test` - Test on device. + + + +### iOS Simulator Development (`simulator`) +**Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. (21 tools) + +- `boot` - Defined in Simulator Management workflow. +- `build` - Build for iOS sim (compile-only, no launch). +- `build-and-run` - Build and run iOS sim (preferred for run/launch intent). +- `clean` - Defined in iOS Device Development workflow. +- `discover-projects` - Defined in iOS Device Development workflow. +- `get-app-bundle-id` - Defined in iOS Device Development workflow. +- `get-app-path` - Get sim built app path. +- `install` - Install app on sim. +- `launch-app` - Launch app on simulator. +- `launch-app-with-logs` - Launch sim app with logs. +- `list` - Defined in Simulator Management workflow. +- `list-schemes` - Defined in iOS Device Development workflow. +- `open` - Defined in Simulator Management workflow. +- `record-video` - Record sim video. +- `screenshot` - Capture screenshot. +- `show-build-settings` - Defined in iOS Device Development workflow. +- `snapshot-ui` - Print view hierarchy with precise view coordinates (x, y, width, height) for visible elements. +- `start-simulator-log-capture` - Defined in Log Capture workflow. +- `stop` - Stop sim app. +- `stop-simulator-log-capture` - Defined in Log Capture workflow. +- `test` - Test on iOS sim. + + + +### LLDB Debugging (`debugging`) +**Purpose**: Attach LLDB debugger to simulator apps, set breakpoints, inspect variables and call stacks. (8 tools) + +- `add-breakpoint` - Add breakpoint. +- `attach` - Attach LLDB to sim app. +- `continue` - Continue debug session. +- `detach` - Detach debugger. +- `lldb-command` - Run LLDB command. +- `remove-breakpoint` - Remove breakpoint. +- `stack` - Get backtrace. +- `variables` - Get frame variables. + + + +### Log Capture (`logging`) +**Purpose**: Capture and retrieve logs from simulator and device apps. (4 tools) + +- `start-device-log-capture` - Defined in iOS Device Development workflow. +- `start-simulator-log-capture` - Start sim log capture. +- `stop-device-log-capture` - Defined in iOS Device Development workflow. +- `stop-simulator-log-capture` - Stop sim app and return logs. + + + +### macOS Development (`macos`) +**Purpose**: Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications. (11 tools) + +- `build` - Build macOS app. +- `build-and-run` - Build and run macOS app. +- `clean` - Defined in iOS Device Development workflow. +- `discover-projects` - Defined in iOS Device Development workflow. +- `get-app-path` - Get macOS built app path. +- `get-macos-bundle-id` - Extract bundle id from macOS .app. +- `launch` - Launch macOS app. +- `list-schemes` - Defined in iOS Device Development workflow. +- `show-build-settings` - Defined in iOS Device Development workflow. +- `stop` - Stop macOS app. +- `test` - Test macOS target. + + + +### MCP Doctor (`doctor`) +**Purpose**: Diagnostic tool providing comprehensive information about the MCP server environment, dependencies, and configuration. (1 tools) + +- `doctor` - MCP environment info. + + + +### Project Discovery (`project-discovery`) +**Purpose**: Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information. (5 tools) + +- `discover-projects` - Defined in iOS Device Development workflow. +- `get-app-bundle-id` - Defined in iOS Device Development workflow. +- `get-macos-bundle-id` - Defined in macOS Development workflow. +- `list-schemes` - Defined in iOS Device Development workflow. +- `show-build-settings` - Defined in iOS Device Development workflow. + + + +### Project Scaffolding (`project-scaffolding`) +**Purpose**: Scaffold new iOS and macOS projects from templates. (2 tools) + +- `scaffold-ios` - Scaffold iOS project. +- `scaffold-macos` - Scaffold macOS project. + + + +### Simulator Management (`simulator-management`) +**Purpose**: Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance. (8 tools) + +- `boot` - Boot iOS simulator. +- `erase` - Erase simulator. +- `list` - List iOS simulators. +- `open` - Open Simulator app. +- `reset-location` - Reset sim location. +- `set-appearance` - Set sim appearance. +- `set-location` - Set sim location. +- `statusbar` - Set sim status bar network. + + + +### Swift Package Development (`swift-package`) +**Purpose**: Build, test, run and manage Swift Package Manager projects. (6 tools) + +- `build` - swift package target build. +- `clean` - swift package clean. +- `list` - List SwiftPM processes. +- `run` - swift package target run. +- `stop` - Stop SwiftPM run. +- `test` - Run swift package target tests. + + + +### UI Automation (`ui-automation`) +**Purpose**: UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows. (11 tools) + +- `button` - Press simulator hardware button. +- `gesture` - Simulator gesture preset. +- `key-press` - Press key by keycode. +- `key-sequence` - Press a sequence of keys by their keycodes. +- `long-press` - Long press at coords. +- `screenshot` - Defined in iOS Simulator Development workflow. +- `snapshot-ui` - Defined in iOS Simulator Development workflow. +- `swipe` - Swipe between points. +- `tap` - Tap UI element by accessibility id/label (recommended) or coordinates as fallback. +- `touch` - Touch down/up at coords. +- `type-text` - Type text. + + + +### Xcode IDE Integration (`xcode-ide`) +**Purpose**: Bridge tools for connecting to Xcode's built-in MCP server (mcpbridge) to access IDE-specific functionality. (5 tools) + +- `bridge-disconnect` - Disconnect bridge and unregister proxied `xcode_tools_*` tools. +- `bridge-status` - Show xcrun mcpbridge availability and proxy tool sync status. +- `bridge-sync` - One-shot connect + tools/list sync (manual retry; avoids background prompt spam). +- `call-tool` - Call a remote Xcode IDE MCP tool. +- `list-tools` - Lists Xcode-IDE-only MCP capabilities (Use for: SwiftUI previews image capture, code snippet execution, issue Navigator/build logs, and window/tab context). + + + +## Summary Statistics + +- **Canonical Tools**: 73 +- **Total Tools**: 97 +- **Workflow Groups**: 13 + +--- + +*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-17T21:48:36.993Z UTC* diff --git a/docs/TOOLS.md b/docs/TOOLS.md index 962e3cff..77f93d82 100644 --- a/docs/TOOLS.md +++ b/docs/TOOLS.md @@ -1,114 +1,208 @@ -# XcodeBuildMCP Tools Reference +# XcodeBuildMCP MCP Tools Reference -XcodeBuildMCP provides 63 tools organized into 12 workflow groups for comprehensive Apple development workflows. +This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP provides 79 canonical tools organized into 15 workflow groups for comprehensive Apple development workflows. ## Workflow Groups +### Build Utilities (`utilities`) +**Purpose**: Utility tools for cleaning build products and managing build artifacts. (1 tools) + +- `clean` - Defined in iOS Device Development workflow. + + + ### iOS Device Development (`device`) -**Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Build, test, deploy, and debug apps on real hardware. (7 tools) - -- `build_device` - Builds an app for a connected device. -- `get_device_app_path` - Retrieves the built app path for a connected device. -- `install_app_device` - Installs an app on a connected device. -- `launch_app_device` - Launches an app on a connected device. -- `list_devices` - Lists connected physical Apple devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) with their UUIDs, names, and connection status. Use this to discover physical devices for testing. -- `stop_app_device` - Stops a running app on a connected device. -- `test_device` - Runs tests on a physical Apple device. +**Purpose**: Complete iOS development workflow for physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). (14 tools) + +- `build_device` - Build for device. +- `clean` - Clean build products. +- `discover_projs` - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. +- `get_app_bundle_id` - Extract bundle id from .app. +- `get_device_app_path` - Get device built app path. +- `install_app_device` - Install app on device. +- `launch_app_device` - Launch app on device. +- `list_devices` - List connected devices. +- `list_schemes` - List Xcode schemes. +- `show_build_settings` - Show build settings. +- `start_device_log_cap` - Start device log capture. +- `stop_app_device` - Stop device app. +- `stop_device_log_cap` - Stop device app and return logs. +- `test_device` - Test on device. + + + ### iOS Simulator Development (`simulator`) -**Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators. (12 tools) - -- `boot_sim` - Boots an iOS simulator. -- `build_run_sim` - Builds and runs an app on an iOS simulator. -- `build_sim` - Builds an app for an iOS simulator. -- `get_sim_app_path` - Retrieves the built app path for an iOS simulator. -- `install_app_sim` - Installs an app in an iOS simulator. -- `launch_app_logs_sim` - Launches an app in an iOS simulator and captures its logs. -- `launch_app_sim` - Launches an app in an iOS simulator. -- `list_sims` - Lists available iOS simulators with their UUIDs. -- `open_sim` - Opens the iOS Simulator app. -- `record_sim_video` - Starts or stops video capture for an iOS simulator. -- `stop_app_sim` - Stops an app running in an iOS simulator. -- `test_sim` - Runs tests on an iOS simulator. -### Log Capture & Management (`logging`) -**Purpose**: Log capture and management tools for iOS simulators and physical devices. Start, stop, and analyze application and system logs during development and testing. (4 tools) - -- `start_device_log_cap` - Starts log capture on a connected device. -- `start_sim_log_cap` - Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs. -- `stop_device_log_cap` - Stops an active Apple device log capture session and returns the captured logs. -- `stop_sim_log_cap` - Stops an active simulator log capture session and returns the captured logs. +**Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. (21 tools) + +- `boot_sim` - Defined in Simulator Management workflow. +- `build_run_sim` - Build and run iOS sim (preferred for run/launch intent). +- `build_sim` - Build for iOS sim (compile-only, no launch). +- `clean` - Defined in iOS Device Development workflow. +- `discover_projs` - Defined in iOS Device Development workflow. +- `get_app_bundle_id` - Defined in iOS Device Development workflow. +- `get_sim_app_path` - Get sim built app path. +- `install_app_sim` - Install app on sim. +- `launch_app_logs_sim` - Launch sim app with logs. +- `launch_app_sim` - Launch app on simulator. +- `list_schemes` - Defined in iOS Device Development workflow. +- `list_sims` - Defined in Simulator Management workflow. +- `open_sim` - Defined in Simulator Management workflow. +- `record_sim_video` - Record sim video. +- `screenshot` - Capture screenshot. +- `show_build_settings` - Defined in iOS Device Development workflow. +- `snapshot_ui` - Print view hierarchy with precise view coordinates (x, y, width, height) for visible elements. +- `start_sim_log_cap` - Defined in Log Capture workflow. +- `stop_app_sim` - Stop sim app. +- `stop_sim_log_cap` - Defined in Log Capture workflow. +- `test_sim` - Test on iOS sim. + + + +### LLDB Debugging (`debugging`) +**Purpose**: Attach LLDB debugger to simulator apps, set breakpoints, inspect variables and call stacks. (8 tools) + +- `debug_attach_sim` - Attach LLDB to sim app. +- `debug_breakpoint_add` - Add breakpoint. +- `debug_breakpoint_remove` - Remove breakpoint. +- `debug_continue` - Continue debug session. +- `debug_detach` - Detach debugger. +- `debug_lldb_command` - Run LLDB command. +- `debug_stack` - Get backtrace. +- `debug_variables` - Get frame variables. + + + +### Log Capture (`logging`) +**Purpose**: Capture and retrieve logs from simulator and device apps. (4 tools) + +- `start_device_log_cap` - Defined in iOS Device Development workflow. +- `start_sim_log_cap` - Start sim log capture. +- `stop_device_log_cap` - Defined in iOS Device Development workflow. +- `stop_sim_log_cap` - Stop sim app and return logs. + + + ### macOS Development (`macos`) -**Purpose**: Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications. (6 tools) - -- `build_macos` - Builds a macOS app. -- `build_run_macos` - Builds and runs a macOS app. -- `get_mac_app_path` - Retrieves the built macOS app bundle path. -- `launch_mac_app` - Launches a macOS application. Note: In some environments, this tool may be prefixed as mcp0_launch_macos_app. -- `stop_mac_app` - Stops a running macOS application. Can stop by app name or process ID. -- `test_macos` - Runs tests for a macOS target. +**Purpose**: Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications. (11 tools) + +- `build_macos` - Build macOS app. +- `build_run_macos` - Build and run macOS app. +- `clean` - Defined in iOS Device Development workflow. +- `discover_projs` - Defined in iOS Device Development workflow. +- `get_mac_app_path` - Get macOS built app path. +- `get_mac_bundle_id` - Extract bundle id from macOS .app. +- `launch_mac_app` - Launch macOS app. +- `list_schemes` - Defined in iOS Device Development workflow. +- `show_build_settings` - Defined in iOS Device Development workflow. +- `stop_mac_app` - Stop macOS app. +- `test_macos` - Test macOS target. + + + +### MCP Doctor (`doctor`) +**Purpose**: Diagnostic tool providing comprehensive information about the MCP server environment, dependencies, and configuration. (1 tools) + +- `doctor` - MCP environment info. + + + ### Project Discovery (`project-discovery`) **Purpose**: Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information. (5 tools) -- `discover_projs` - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. -- `get_app_bundle_id` - Extracts the bundle identifier from an app bundle (.app) for any Apple platform (iOS, iPadOS, watchOS, tvOS, visionOS). -- `get_mac_bundle_id` - Extracts the bundle identifier from a macOS app bundle (.app). Note: In some environments, this tool may be prefixed as mcp0_get_macos_bundle_id. -- `list_schemes` - Lists schemes for a project or workspace. -- `show_build_settings` - Shows xcodebuild build settings. +- `discover_projs` - Defined in iOS Device Development workflow. +- `get_app_bundle_id` - Defined in iOS Device Development workflow. +- `get_mac_bundle_id` - Defined in macOS Development workflow. +- `list_schemes` - Defined in iOS Device Development workflow. +- `show_build_settings` - Defined in iOS Device Development workflow. + + + ### Project Scaffolding (`project-scaffolding`) -**Purpose**: Tools for creating new iOS and macOS projects from templates. Bootstrap new applications with best practices, standard configurations, and modern project structures. (2 tools) +**Purpose**: Scaffold new iOS and macOS projects from templates. (2 tools) + +- `scaffold_ios_project` - Scaffold iOS project. +- `scaffold_macos_project` - Scaffold macOS project. + + + +### Session Management (`session-management`) +**Purpose**: Manage session defaults for project/workspace paths, scheme, configuration, simulator/device settings. (5 tools) + +- `session_clear_defaults` - Clear session defaults for the active profile or a specified profile. +- `session_set_defaults` - Set session defaults for the active profile, or for a specified profile and make it active. +- `session_show_defaults` - Show the current active defaults. +- `session_use_defaults_profile` - Switch the active session defaults profile. +- `sync_xcode_defaults` - Sync session defaults (scheme, simulator) from Xcode's current IDE selection. -- `scaffold_ios_project` - Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration. -- `scaffold_macos_project` - Scaffold a new macOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper macOS configuration. -### Project Utilities (`utilities`) -**Purpose**: Essential project maintenance utilities for cleaning and managing existing projects. Provides clean operations for both .xcodeproj and .xcworkspace files. (1 tools) -- `clean` - Cleans build products with xcodebuild. -### session-management (`session-management`) -**Purpose**: Manage session defaults for projectPath/workspacePath, scheme, configuration, simulatorName/simulatorId, deviceId, useLatestOS and arch. These defaults are required by many tools and must be set before attempting to call tools that would depend on these values. (3 tools) -- `session_clear_defaults` - Clear selected or all session defaults. -- `session_set_defaults` - Set the session defaults needed by many tools. Most tools require one or more session defaults to be set before they can be used. Agents should set all relevant defaults up front in a single call (e.g., project/workspace, scheme, simulator or device ID, useLatestOS) to avoid iterative prompts; only set the keys your workflow needs. -- `session_show_defaults` - Show current session defaults. ### Simulator Management (`simulator-management`) -**Purpose**: Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance. (5 tools) - -- `erase_sims` - Erases a simulator by UDID. -- `reset_sim_location` - Resets the simulator's location to default. -- `set_sim_appearance` - Sets the appearance mode (dark/light) of an iOS simulator. -- `set_sim_location` - Sets a custom GPS location for the simulator. -- `sim_statusbar` - Sets the data network indicator in the iOS simulator status bar. Use "clear" to reset all overrides, or specify a network type (hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc). -### Swift Package Manager (`swift-package`) -**Purpose**: Swift Package Manager operations for building, testing, running, and managing Swift packages and dependencies. Complete SPM workflow support. (6 tools) - -- `swift_package_build` - Builds a Swift Package with swift build -- `swift_package_clean` - Cleans Swift Package build artifacts and derived data -- `swift_package_list` - Lists currently running Swift Package processes -- `swift_package_run` - Runs an executable target from a Swift Package with swift run -- `swift_package_stop` - Stops a running Swift Package executable started with swift_package_run -- `swift_package_test` - Runs tests for a Swift Package with swift test -### System Doctor (`doctor`) -**Purpose**: Debug tools and system doctor for troubleshooting XcodeBuildMCP server, development environment, and tool availability. (1 tools) - -- `doctor` - Provides comprehensive information about the MCP server environment, available dependencies, and configuration status. -### UI Testing & Automation (`ui-testing`) +**Purpose**: Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance. (8 tools) + +- `boot_sim` - Boot iOS simulator. +- `erase_sims` - Erase simulator. +- `list_sims` - List iOS simulators. +- `open_sim` - Open Simulator app. +- `reset_sim_location` - Reset sim location. +- `set_sim_appearance` - Set sim appearance. +- `set_sim_location` - Set sim location. +- `sim_statusbar` - Set sim status bar network. + + + +### Swift Package Development (`swift-package`) +**Purpose**: Build, test, run and manage Swift Package Manager projects. (6 tools) + +- `swift_package_build` - swift package target build. +- `swift_package_clean` - swift package clean. +- `swift_package_list` - List SwiftPM processes. +- `swift_package_run` - swift package target run. +- `swift_package_stop` - Stop SwiftPM run. +- `swift_package_test` - Run swift package target tests. + + + +### UI Automation (`ui-automation`) **Purpose**: UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows. (11 tools) -- `button` - Press hardware button on iOS simulator. Supported buttons: apple-pay, home, lock, side-button, siri -- `describe_ui` - Gets entire view hierarchy with precise frame coordinates (x, y, width, height) for all visible elements. Use this before UI interactions or after layout changes - do NOT guess coordinates from screenshots. Returns JSON tree with frame data for accurate automation. -- `gesture` - Perform gesture on iOS simulator using preset gestures: scroll-up, scroll-down, scroll-left, scroll-right, swipe-from-left-edge, swipe-from-right-edge, swipe-from-top-edge, swipe-from-bottom-edge -- `key_press` - Press a single key by keycode on the simulator. Common keycodes: 40=Return, 42=Backspace, 43=Tab, 44=Space, 58-67=F1-F10. -- `key_sequence` - Press key sequence using HID keycodes on iOS simulator with configurable delay -- `long_press` - Long press at specific coordinates for given duration (ms). Use describe_ui for precise coordinates (don't guess from screenshots). -- `screenshot` - Captures screenshot for visual verification. For UI coordinates, use describe_ui instead (don't determine coordinates from screenshots). -- `swipe` - Swipe from one point to another. Use describe_ui for precise coordinates (don't guess from screenshots). Supports configurable timing. -- `tap` - Tap at specific coordinates or target elements by accessibility id or label. Use describe_ui to get precise element coordinates prior to using x/y parameters (don't guess from screenshots). Supports optional timing delays. -- `touch` - Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots). -- `type_text` - Type text (supports US keyboard characters). Use describe_ui to find text field, tap to focus, then type. +- `button` - Press simulator hardware button. +- `gesture` - Simulator gesture preset. +- `key_press` - Press key by keycode. +- `key_sequence` - Press a sequence of keys by their keycodes. +- `long_press` - Long press at coords. +- `screenshot` - Defined in iOS Simulator Development workflow. +- `snapshot_ui` - Defined in iOS Simulator Development workflow. +- `swipe` - Swipe between points. +- `tap` - Tap UI element by accessibility id/label (recommended) or coordinates as fallback. +- `touch` - Touch down/up at coords. +- `type_text` - Type text. + + + +### Workflow Discovery (`workflow-discovery`) +**Purpose**: Manage enabled workflows at runtime. (1 tools) + +- `manage-workflows` - Workflows are groups of tools exposed by XcodeBuildMCP. By default, not all workflows (and therefore tools) are enabled; only simulator tools are enabled by default. Some workflows are mandatory and can't be disabled. + + + +### Xcode IDE Integration (`xcode-ide`) +**Purpose**: Bridge tools for connecting to Xcode's built-in MCP server (mcpbridge) to access IDE-specific functionality. (5 tools) + +- `xcode_ide_call_tool` - Call a remote Xcode IDE MCP tool. +- `xcode_ide_list_tools` - Lists Xcode-IDE-only MCP capabilities (Use for: SwiftUI previews image capture, code snippet execution, issue Navigator/build logs, and window/tab context). +- `xcode_tools_bridge_disconnect` - Disconnect bridge and unregister proxied `xcode_tools_*` tools. +- `xcode_tools_bridge_status` - Show xcrun mcpbridge availability and proxy tool sync status. +- `xcode_tools_bridge_sync` - One-shot connect + tools/list sync (manual retry; avoids background prompt spam). + + ## Summary Statistics -- **Total Tools**: 63 canonical tools + 22 re-exports = 85 total -- **Workflow Groups**: 12 +- **Canonical Tools**: 79 +- **Total Tools**: 103 +- **Workflow Groups**: 15 --- -*This documentation is automatically generated by `scripts/update-tools-docs.ts` using static analysis. Last updated: 2025-12-30* +*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-17T21:48:36.993Z UTC* diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 00000000..f5d824e3 --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -0,0 +1,43 @@ +# Troubleshooting + +## Quick triage +- Run the doctor tool and include output when filing issues. +- Confirm Xcode and Command Line Tools are installed. +- Verify required workflows are enabled in configuration. +- Check simulator/device availability and permissions. + +## Doctor tool +The doctor tool checks system configuration and reports on all dependencies required by XcodeBuildMCP. + +```bash +npx --package xcodebuildmcp@latest xcodebuildmcp-doctor +``` + +It reports on: +- System and Node.js environment +- Xcode installation and configuration +- Required dependencies (xcodebuild, AXe, etc.) +- Environment variables affecting XcodeBuildMCP +- Feature availability status + +> [!NOTE] +> You can also ask you agent to run the doctor tool which will provide a more representative output. + +## Common issues + +### UI automation reports missing AXe +UI automation (describe/tap/swipe/type) and simulator video capture require the AXe binary. If you see a missing AXe error: +- Ensure `bundled/` artifacts exist in your installation. +- Or set `XCODEBUILDMCP_AXE_PATH` to a known AXe binary path (preferred), or `AXE_PATH`. +- Re-run the doctor tool to confirm AXe is detected. + +### Tool timeouts +Some clients have short tool timeouts. If you see timeouts, increase the client timeout (for example, `tool_timeout_sec = 600` in Codex). + +### Missing tools +If tools do not appear, verify `XCODEBUILDMCP_ENABLED_WORKFLOWS` includes the required workflow groups. + +## Related docs +- Configuration options: [CONFIGURATION.md](CONFIGURATION.md) +- Tools reference: [TOOLS.md](TOOLS.md) +- Privacy and telemetry: [PRIVACY.md](PRIVACY.md) diff --git a/docs/XCODE_IDE_MCPBRIDGE.md b/docs/XCODE_IDE_MCPBRIDGE.md new file mode 100644 index 00000000..880fdd27 --- /dev/null +++ b/docs/XCODE_IDE_MCPBRIDGE.md @@ -0,0 +1,60 @@ +# Xcode IDE MCP Bridge (`xcrun mcpbridge`) + +XcodeBuildMCP can optionally proxy Xcode’s built-in “Xcode Tools” MCP service via Xcode 26’s `xcrun mcpbridge`. + +This enables IDE-only tools (e.g. Preview rendering, Issue Navigator queries, documentation search, project navigator operations) that are not available via `xcodebuild`. + +## Enable + +Add `xcode-ide` to `enabledWorkflows` in `.xcodebuildmcp/config.yaml`: + +```yaml +schemaVersion: 1 +enabledWorkflows: ["simulator", "debugging", "xcode-ide"] +``` + +If the workflow is not enabled, XcodeBuildMCP does not start the bridge. + +## MCP tools in `xcode-ide` workflow + +When `xcode-ide` is enabled and `mcpbridge` is available, XcodeBuildMCP exposes two gateway tools: + +- `xcode_ide_list_tools`: Lists remote tools from Xcode's MCP server (name, description, schemas). +- `xcode_ide_call_tool`: Calls a remote Xcode tool by name with a JSON argument payload. + +These tools are stable and manifest-managed. They are shown only when `mcpbridge` is available. + +CLI behavior is unchanged and continues to use dynamic `xcode_tools_*` proxy naming. + +## Bridge debug tools + +These tools are stable and do not depend on Xcode’s tool catalog, but they are intentionally hidden unless you enable debugging (`debug: true`) because they are primarily for troubleshooting: + +- `xcode_tools_bridge_status`: Shows `mcpbridge` availability, connection state, last error, and proxied tool count. +- `xcode_tools_bridge_sync`: One-shot connect + re-sync (use this if Xcode prompts blocked startup sync). +- `xcode_tools_bridge_disconnect`: Disconnect and unregister proxied `xcode_tools_*` tools. + +## Trust prompts / troubleshooting + +Xcode may show trust/allow prompts when the bridge connects and/or when tools are invoked. + +Recommended flow: + +1. Launch Xcode. +2. Start XcodeBuildMCP with `xcode-ide` enabled. +3. If `xcode_ide_list_tools` fails, temporarily set `debug: true` and call `xcode_tools_bridge_status` to inspect bridge health, then retry after approving prompts. + +## Targeting a specific Xcode instance (optional) + +If you need to scope the bridge to a specific Xcode instance, XcodeBuildMCP forwards these environment variables to the bridge: + +- `XCODEBUILDMCP_XCODE_PID` → `MCP_XCODE_PID` +- `XCODEBUILDMCP_XCODE_SESSION_ID` → `MCP_XCODE_SESSION_ID` + +## Dev probe script + +For manual verification against a real Xcode install: + +```bash +npx tsx scripts/probe-xcode-mcpbridge.ts +``` diff --git a/docs/ZOD_MIGRATION_GUIDE.md b/docs/ZOD_MIGRATION_GUIDE.md deleted file mode 100644 index b3a638cc..00000000 --- a/docs/ZOD_MIGRATION_GUIDE.md +++ /dev/null @@ -1,879 +0,0 @@ -# Migration guide - -import { Callout } from "fumadocs-ui/components/callout"; -import { Tabs, Tab } from "fumadocs-ui/components/tabs"; - -This migration guide aims to list the breaking changes in Zod 4 in order of highest to lowest impact. To learn more about the performance enhancements and new features of Zod 4, read the [introductory post](/v4). - -{/* To give the ecosystem time to migrate, Zod 4 will initially be published alongside Zod v3.25. To use Zod 4, upgrade to `zod@3.25.0` or later: */} - -``` -npm install zod@^4.0.0 -``` - -{/* Zod 4 is available at the `"/v4"` subpath: - - ```ts - import * as z from "zod"; - ``` */} - -Many of Zod's behaviors and APIs have been made more intuitive and cohesive. The breaking changes described in this document often represent major quality-of-life improvements for Zod users. I strongly recommend reading this guide thoroughly. - - - **Note** — Zod 3 exported a number of undocumented quasi-internal utility types and functions that are not considered part of the public API. Changes to those are not documented here. - - - - **Unofficial codemod** — A community-maintained codemod [`zod-v3-to-v4`](https://github.com/nicoespeon/zod-v3-to-v4) is available. - - -## Error customization - -Zod 4 standardizes the APIs for error customization under a single, unified `error` param. Previously Zod's error customization APIs were fragmented and inconsistent. This is cleaned up in Zod 4. - -### deprecates `message` - -Replaces `message` with `error`. The `message` parameter is still supported but deprecated. - - - - ```ts - z.string().min(5, { error: "Too short." }); - ``` - - - - ```ts - z.string().min(5, { message: "Too short." }); - ``` - - - -### drops `invalid_type_error` and `required_error` - -The `invalid_type_error` / `required_error` params have been dropped. These were hastily added years ago as a way to customize errors that was less verbose than `errorMap`. They came with all sorts of footguns (they can't be used in conjunction with `errorMap`) and do not align with Zod's actual issue codes (there is no `required` issue code). - -These can now be cleanly represented with the new `error` parameter. - - - - ```ts - z.string({ - error: (issue) => issue.input === undefined - ? "This field is required" - : "Not a string" - }); - ``` - - - - ```ts - z.string({ - required_error: "This field is required", - invalid_type_error: "Not a string", - }); - ``` - - - -### drops `errorMap` - -This is renamed to `error`. - -Error maps can also now return a plain `string` (instead of `{message: string}`). They can also return `undefined`, which tells Zod to yield control to the next error map in the chain. - - - - ```ts - z.string().min(5, { - error: (issue) => { - if (issue.code === "too_small") { - return `Value must be >${issue.minimum}` - } - }, - }); - ``` - - - - ```ts - z.string({ - errorMap: (issue, ctx) => { - if (issue.code === "too_small") { - return { message: `Value must be >${issue.minimum}` }; - } - return { message: ctx.defaultError }; - }, - }); - ``` - - - -{/* ## `.safeParse()` - - For performance reasons, the errors returned by `.safeParse()` and `.safeParseAsync()` no longer extend `Error`. - - ```ts - const result = z.string().safeParse(12); - result.error! instanceof Error; // => false - ``` - - It is very slow to instantiate `Error` instances in JavaScript, as the initialization process snapshots the call stack. In the case of Zod's "safe" parse methods, it's expected that you will handle errors at the point of parsing, so instantiating a true `Error` object adds little value anyway. - - > Pro tip: prefer `.safeParse()` over `try/catch` in performance-sensitive code. - - By contrast the errors thrown by `.parse()` and `.parseAsync()` still extend `Error`. Aside from the prototype difference, the error classes are identical. - - ```ts - try { - z.string().parse(12); - } catch (err) { - console.log(err instanceof Error); // => true - } - ``` - */} - -## `ZodError` - -{/* - ### changes to `.message` - - Previously the `.message` property on `ZodError` was a JSON.stringified copy of the `.issues` array. This was redundant, confusing, and a bit of an abuse of the `.message` property. Also due to the [`Error` prototype changes](#safeparse) (and inconsistencies in how Node.js logs `Error` subclasses vs other objects) the logging of a multi-line `.message` property got a lot uglier: - - ```sh - $ tsx index.ts - ZodError { - message: '[\n' + - ' {\n' + - ' "expected": "string",\n' + - ' "code": "invalid_type",\n' + - ' "path": [],\n' + - ' "message": "Invalid input: expected string, received number"\n' + - ' }\n' + - ']' - } - ``` - - - For these reasons, the `.message` property is left empty and the `.issues` array is marked as enumerable. This keeps error logging consistent and pretty: - - ```sh - $ tsx index.ts - z.string().parse(234); - - ZodError { - issues: [ - { - expected: 'string', - code: 'invalid_type', - path: [], - message: 'Invalid input: expected string, received number' - } - ] - } - ``` - - Vitest uses special handling for `Error` subclasses that ignores enumerable properties. */} - -### updates issue formats - -The issue formats have been dramatically streamlined. - -```ts -import * as z from "zod"; // v4 - -type IssueFormats = - | z.core.$ZodIssueInvalidType - | z.core.$ZodIssueTooBig - | z.core.$ZodIssueTooSmall - | z.core.$ZodIssueInvalidStringFormat - | z.core.$ZodIssueNotMultipleOf - | z.core.$ZodIssueUnrecognizedKeys - | z.core.$ZodIssueInvalidValue - | z.core.$ZodIssueInvalidUnion - | z.core.$ZodIssueInvalidKey // new: used for z.record/z.map - | z.core.$ZodIssueInvalidElement // new: used for z.map/z.set - | z.core.$ZodIssueCustom; -``` - -Below is the list of Zod 3 issues types and their Zod 4 equivalent: - -```ts -import * as z from "zod"; // v3 - -export type IssueFormats = - | z.ZodInvalidTypeIssue // ♻️ renamed to z.core.$ZodIssueInvalidType - | z.ZodTooBigIssue // ♻️ renamed to z.core.$ZodIssueTooBig - | z.ZodTooSmallIssue // ♻️ renamed to z.core.$ZodIssueTooSmall - | z.ZodInvalidStringIssue // ♻️ z.core.$ZodIssueInvalidStringFormat - | z.ZodNotMultipleOfIssue // ♻️ renamed to z.core.$ZodIssueNotMultipleOf - | z.ZodUnrecognizedKeysIssue // ♻️ renamed to z.core.$ZodIssueUnrecognizedKeys - | z.ZodInvalidUnionIssue // ♻️ renamed to z.core.$ZodIssueInvalidUnion - | z.ZodCustomIssue // ♻️ renamed to z.core.$ZodIssueCustom - | z.ZodInvalidEnumValueIssue // ❌ merged in z.core.$ZodIssueInvalidValue - | z.ZodInvalidLiteralIssue // ❌ merged into z.core.$ZodIssueInvalidValue - | z.ZodInvalidUnionDiscriminatorIssue // ❌ throws an Error at schema creation time - | z.ZodInvalidArgumentsIssue // ❌ z.function throws ZodError directly - | z.ZodInvalidReturnTypeIssue // ❌ z.function throws ZodError directly - | z.ZodInvalidDateIssue // ❌ merged into invalid_type - | z.ZodInvalidIntersectionTypesIssue // ❌ removed (throws regular Error) - | z.ZodNotFiniteIssue // ❌ infinite values no longer accepted (invalid_type) -``` - -While certain Zod 4 issue types have been merged, dropped, and modified, each issue remains structurally similar to Zod 3 counterpart (identical, in most cases). All issues still conform to the same base interface as Zod 3, so most common error handling logic will work without modification. - -```ts -export interface $ZodIssueBase { - readonly code?: string; - readonly input?: unknown; - readonly path: PropertyKey[]; - readonly message: string; -} -``` - -### changes error map precedence - -The error map precedence has been changed to be more consistent. Specifically, an error map passed into `.parse()` *no longer* takes precedence over a schema-level error map. - -```ts -const mySchema = z.string({ error: () => "Schema-level error" }); - -// in Zod 3 -mySchema.parse(12, { error: () => "Contextual error" }); // => "Contextual error" - -// in Zod 4 -mySchema.parse(12, { error: () => "Contextual error" }); // => "Schema-level error" -``` - -### deprecates `.format()` - -The `.format()` method on `ZodError` has been deprecated. Instead use the top-level `z.treeifyError()` function. Read the [Formatting errors docs](/error-formatting) for more information. - -### deprecates `.flatten()` - -The `.flatten()` method on `ZodError` has also been deprecated. Instead use the top-level `z.treeifyError()` function. Read the [Formatting errors docs](/error-formatting) for more information. - -### drops `.formErrors` - -This API was identical to `.flatten()`. It exists for historical reasons and isn't documented. - -### deprecates `.addIssue()` and `.addIssues()` - -Directly push to `err.issues` array instead, if necessary. - -```ts -myError.issues.push({ - // new issue -}); -``` - -{/* ## `.and()` dropped - - The `.and()` method on `ZodType` has been dropped in favor of `z.intersection(A, B)`. Not only is this method rarely used, there are few good reasons to use intersections at all. The `.and()` API prevented bundlers from treeshaking `ZodIntersection`, a fairly large and complex class. - - ```ts - z.object({ a: z.string() }).and(z.object({ b: z.number() })); // ❌ - - // use z.intersection - z.intersection(z.object({ a: z.string() }), z.object({ b: z.number() })); // ✅ - // or .extend() when possible - z.object({ a: z.string() }).extend(z.object({ b: z.number() })); // ✅ - ``` */} - -## `z.number()` - -### no infinite values - -`POSITIVE_INFINITY` and `NEGATIVE_INFINITY` are no longer considered valid values for `z.number()`. - -### `.safe()` no longer accepts floats - -In Zod 3, `z.number().safe()` is deprecated. It now behaves identically to `.int()` (see below). Importantly, that means it no longer accepts floats. - -### `.int()` accepts safe integers only - -The `z.number().int()` API no longer accepts unsafe integers (outside the range of `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`). Using integers out of this range causes spontaneous rounding errors. (Also: You should switch to `z.int()`.) - -## `z.string()` updates - -### deprecates `.email()` etc - -String formats are now represented as *subclasses* of `ZodString`, instead of simple internal refinements. As such, these APIs have been moved to the top-level `z` namespace. Top-level APIs are also less verbose and more tree-shakable. - -```ts -z.email(); -z.uuid(); -z.url(); -z.emoji(); // validates a single emoji character -z.base64(); -z.base64url(); -z.nanoid(); -z.cuid(); -z.cuid2(); -z.ulid(); -z.ipv4(); -z.ipv6(); -z.cidrv4(); // ip range -z.cidrv6(); // ip range -z.iso.date(); -z.iso.time(); -z.iso.datetime(); -z.iso.duration(); -``` - -The method forms (`z.string().email()`) still exist and work as before, but are now deprecated. - -```ts -z.string().email(); // ❌ deprecated -z.email(); // ✅ -``` - -### stricter `.uuid()` - -The `z.uuid()` now validates UUIDs more strictly against the RFC 9562/4122 specification; specifically, the variant bits must be `10` per the spec. For a more permissive "UUID-like" validator, use `z.guid()`. - -```ts -z.uuid(); // RFC 9562/4122 compliant UUID -z.guid(); // any 8-4-4-4-12 hex pattern -``` - -### no padding in `.base64url()` - -Padding is no longer allowed in `z.base64url()` (formerly `z.string().base64url()`). Generally it's desirable for base64url strings to be unpadded and URL-safe. - -### drops `z.string().ip()` - -This has been replaced with separate `.ipv4()` and `.ipv6()` methods. Use `z.union()` to combine them if you need to accept both. - -```ts -z.string().ip() // ❌ -z.ipv4() // ✅ -z.ipv6() // ✅ -``` - -### updates `z.string().ipv6()` - -Validation now happens using the `new URL()` constructor, which is far more robust than the old regular expression approach. Some invalid values that passed validation previously may now fail. - -### drops `z.string().cidr()` - -Similarly, this has been replaced with separate `.cidrv4()` and `.cidrv6()` methods. Use `z.union()` to combine them if you need to accept both. - -```ts -z.string().cidr() // ❌ -z.cidrv4() // ✅ -z.cidrv6() // ✅ -``` - -## `z.coerce` updates - -The input type of all `z.coerce` schemas is now `unknown`. - -```ts -const schema = z.coerce.string(); -type schemaInput = z.input; - -// Zod 3: string; -// Zod 4: unknown; -``` - -## `.default()` updates - -The application of `.default()` has changed in a subtle way. If the input is `undefined`, `ZodDefault` short-circuits the parsing process and returns the default value. The default value must be assignable to the *output type*. - -```ts -const schema = z.string() - .transform(val => val.length) - .default(0); // should be a number -schema.parse(undefined); // => 0 -``` - -In Zod 3, `.default()` expected a value that matched the *input type*. `ZodDefault` would parse the default value, instead of short-circuiting. As such, the default value must be assignable to the *input type* of the schema. - -```ts -// Zod 3 -const schema = z.string() - .transform(val => val.length) - .default("tuna"); -schema.parse(undefined); // => 4 -``` - -To replicate the old behavior, Zod implements a new `.prefault()` API. This is short for "pre-parse default". - -```ts -// Zod 3 -const schema = z.string() - .transform(val => val.length) - .prefault("tuna"); -schema.parse(undefined); // => 4 -``` - -## `z.object()` - -### defaults applied within optional fields - -Defaults inside your properties are applied, even within optional fields. This aligns better with expectations and resolves a long-standing usability issue with Zod 3. This is a subtle change that may cause breakage in code paths that rely on key existence, etc. - -```ts -const schema = z.object({ - a: z.string().default("tuna").optional(), -}); - -schema.parse({}); -// Zod 4: { a: "tuna" } -// Zod 3: {} -``` - -### deprecates `.strict()` and `.passthrough()` - -These methods are generally no longer necessary. Instead use the top-level `z.strictObject()` and `z.looseObject()` functions. - -```ts -// Zod 3 -z.object({ name: z.string() }).strict(); -z.object({ name: z.string() }).passthrough(); - -// Zod 4 -z.strictObject({ name: z.string() }); -z.looseObject({ name: z.string() }); -``` - -> These methods are still available for backwards compatibility, and they will not be removed. They are considered legacy. - -### deprecates `.strip()` - -This was never particularly useful, as it was the default behavior of `z.object()`. To convert a strict object to a "regular" one, use `z.object(A.shape)`. - -### drops `.nonstrict()` - -This long-deprecated alias for `.strip()` has been removed. - -### drops `.deepPartial()` - -This has been long deprecated in Zod 3 and it now removed in Zod 4. There is no direct alternative to this API. There were lots of footguns in its implementation, and its use is generally an anti-pattern. - -### changes `z.unknown()` optionality - -The `z.unknown()` and `z.any()` types are no longer marked as "key optional" in the inferred types. - -```ts -const mySchema = z.object({ - a: z.any(), - b: z.unknown() -}); -// Zod 3: { a?: any; b?: unknown }; -// Zod 4: { a: any; b: unknown }; -``` - -### deprecates `.merge()` - -The `.merge()` method on `ZodObject` has been deprecated in favor of `.extend()`. The `.extend()` method provides the same functionality, avoids ambiguity around strictness inheritance, and has better TypeScript performance. - -```ts -// .merge (deprecated) -const ExtendedSchema = BaseSchema.merge(AdditionalSchema); - -// .extend (recommended) -const ExtendedSchema = BaseSchema.extend(AdditionalSchema.shape); - -// or use destructuring (best tsc performance) -const ExtendedSchema = z.object({ - ...BaseSchema.shape, - ...AdditionalSchema.shape, -}); -``` - -> **Note**: For even better TypeScript performance, consider using object destructuring instead of `.extend()`. See the [API documentation](/api?id=extend) for more details. - -## `z.nativeEnum()` deprecated - -The `z.nativeEnum()` function is now deprecated in favor of just `z.enum()`. The `z.enum()` API has been overloaded to support an enum-like input. - -```ts -enum Color { - Red = "red", - Green = "green", - Blue = "blue", -} - -const ColorSchema = z.enum(Color); // ✅ -``` - -As part of this refactor of `ZodEnum`, a number of long-deprecated and redundant features have been removed. These were all identical and only existed for historical reasons. - -```ts -ColorSchema.enum.Red; // ✅ => "Red" (canonical API) -ColorSchema.Enum.Red; // ❌ removed -ColorSchema.Values.Red; // ❌ removed -``` - -## `z.array()` - -### changes `.nonempty()` type - -This now behaves identically to `z.array().min(1)`. The inferred type does not change. - -```ts -const NonEmpty = z.array(z.string()).nonempty(); - -type NonEmpty = z.infer; -// Zod 3: [string, ...string[]] -// Zod 4: string[] -``` - -The old behavior is now better represented with `z.tuple()` and a "rest" argument. This aligns more closely to TypeScript's type system. - -```ts -z.tuple([z.string()], z.string()); -// => [string, ...string[]] -``` - -## `z.promise()` deprecated - -There's rarely a reason to use `z.promise()`. If you have an input that may be a `Promise`, just `await` it before parsing it with Zod. - -> If you are using `z.promise` to define an async function with `z.function()`, that's no longer necessary either; see the [`ZodFunction`](#function) section below. - -## `z.function()` - -The result of `z.function()` is no longer a Zod schema. Instead, it acts as a standalone "function factory" for defining Zod-validated functions. The API has also changed; you define an `input` and `output` schema upfront, instead of using `args()` and `.returns()` methods. - - - - ```ts - const myFunction = z.function({ - input: [z.object({ - name: z.string(), - age: z.number().int(), - })], - output: z.string(), - }); - - myFunction.implement((input) => { - return `Hello ${input.name}, you are ${input.age} years old.`; - }); - ``` - - - - ```ts - const myFunction = z.function() - .args(z.object({ - name: z.string(), - age: z.number().int(), - })) - .returns(z.string()); - - myFunction.implement((input) => { - return `Hello ${input.name}, you are ${input.age} years old.`; - }); - ``` - - - -If you have a desperate need for a Zod schema with a function type, consider [this workaround](https://github.com/colinhacks/zod/issues/4143#issuecomment-2845134912). - -### adds `.implementAsync()` - -To define an async function, use `implementAsync()` instead of `implement()`. - -```ts -myFunction.implementAsync(async (input) => { - return `Hello ${input.name}, you are ${input.age} years old.`; -}); -``` - -## `.refine()` - -### ignores type predicates - -In Zod 3, passing a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) as a refinement functions could still narrow the type of a schema. This wasn't documented but was discussed in some issues. This is no longer the case. - -```ts -const mySchema = z.unknown().refine((val): val is string => { - return typeof val === "string" -}); - -type MySchema = z.infer; -// Zod 3: `string` -// Zod 4: still `unknown` -``` - -### drops `ctx.path` - -Zod's new parsing architecture does not eagerly evaluate the `path` array. This was a necessary change that unlocks Zod 4's dramatic performance improvements. - -```ts -z.string().superRefine((val, ctx) => { - ctx.path; // ❌ no longer available -}); -``` - -### drops function as second argument - -The following horrifying overload has been removed. - -```ts -const longString = z.string().refine( - (val) => val.length > 10, - (val) => ({ message: `${val} is not more than 10 characters` }) -); -``` - -{/* ## `.superRefine()` deprecated - - The `.superRefine()` method has been deprecated in favor of `.check()`. The `.check()` method provides the same functionality with a cleaner API. The `.check()` method is also available on Zod and Zod Mini schemas. - - ```ts - const UniqueStringArray = z.array(z.string()).check((ctx) => { - if (ctx.value.length > 3) { - ctx.issues.push({ - code: "too_big", - maximum: 3, - origin: "array", - inclusive: true, - message: "Too many items 😡", - input: ctx.value - }); - } - - if (ctx.value.length !== new Set(ctx.value).size) { - ctx.issues.push({ - code: "custom", - message: `No duplicates allowed.`, - input: ctx.value - }); - } - }); - ``` */} - -## `z.ostring()`, etc dropped - -The undocumented convenience methods `z.ostring()`, `z.onumber()`, etc. have been removed. These were shorthand methods for defining optional string schemas. - -## `z.literal()` - -### drops `symbol` support - -Symbols aren't considered literal values, nor can they be simply compared with `===`. This was an oversight in Zod 3. - -## static `.create()` factories dropped - -Previously all Zod classes defined a static `.create()` method. These are now implemented as standalone factory functions. - -```ts -z.ZodString.create(); // ❌ -``` - -## `z.record()` - -### drops single argument usage - -Before, `z.record()` could be used with a single argument. This is no longer supported. - -```ts -// Zod 3 -z.record(z.string()); // ✅ - -// Zod 4 -z.record(z.string()); // ❌ -z.record(z.string(), z.string()); // ✅ -``` - -### improves enum support - -Records have gotten a lot smarter. In Zod 3, passing an enum into `z.record()` as a key schema would result in a partial type - -```ts -const myRecord = z.record(z.enum(["a", "b", "c"]), z.number()); -// { a?: number; b?: number; c?: number; } -``` - -In Zod 4, this is no longer the case. The inferred type is what you'd expect, and Zod ensures exhaustiveness; that is, it makes sure all enum keys exist in the input during parsing. - -```ts -const myRecord = z.record(z.enum(["a", "b", "c"]), z.number()); -// { a: number; b: number; c: number; } -``` - -To replicate the old behavior with optional keys, use `z.partialRecord()`: - -```ts -const myRecord = z.partialRecord(z.enum(["a", "b", "c"]), z.number()); -// { a?: number; b?: number; c?: number; } -``` - -## `z.intersection()` - -### throws `Error` on merge conflict - -Zod intersection parses the input against two schemas, then attempts to merge the results. In Zod 3, when the results were unmergable, Zod threw a `ZodError` with a special `"invalid_intersection_types"` issue. - -In Zod 4, this will throw a regular `Error` instead. The existence of unmergable results indicates a structural problem with the schema: an intersection of two incompatible types. Thus, a regular error is more appropriate than a validation error. - -## Internal changes - -> The typical user of Zod can likely ignore everything below this line. These changes do not impact the user-facing `z` APIs. - -There are too many internal changes to list here, but some may be relevant to regular users who are (intentionally or not) relying on certain implementation details. These changes will be of particular interest to library authors building tools on top of Zod. - -### updates generics - -The generic structure of several classes has changed. Perhaps most significant is the change to the `ZodType` base class: - -```ts -// Zod 3 -class ZodType { - // ... -} - -// Zod 4 -class ZodType { - // ... -} -``` - -The second generic `Def` has been entirely removed. Instead the base class now only tracks `Output` and `Input`. While previously the `Input` value defaulted to `Output`, it now defaults to `unknown`. This allows generic functions involving `z.ZodType` to behave more intuitively in many cases. - -```ts -function inferSchema(schema: T): T { - return schema; -}; - -inferSchema(z.string()); // z.ZodString -``` - -The need for `z.ZodTypeAny` has been eliminated; just use `z.ZodType` instead. - -### adds `z.core` - -Many utility functions and types have been moved to the new `zod/v4/core` sub-package, to facilitate code sharing between Zod and Zod Mini. - -```ts -import * as z from "zod/v4/core"; - -function handleError(iss: z.$ZodError) { - // do stuff -} -``` - -For convenience, the contents of `zod/v4/core` are also re-exported from `zod` and `zod/mini` under the `z.core` namespace. - -```ts -import * as z from "zod"; - -function handleError(iss: z.core.$ZodError) { - // do stuff -} -``` - -Refer to the [Zod Core](/packages/core) docs for more information on the contents of the core sub-library. - -### moves `._def` - -The `._def` property is now moved to `._zod.def`. The structure of all internal defs is subject to change; this is relevant to library authors but won't be comprehensively documented here. - -### drops `ZodEffects` - -This doesn't affect the user-facing APIs, but it's an internal change worth highlighting. It's part of a larger restructure of how Zod handles *refinements*. - -Previously both refinements and transformations lived inside a wrapper class called `ZodEffects`. That means adding either one to a schema would wrap the original schema in a `ZodEffects` instance. In Zod 4, refinements now live inside the schemas themselves. More accurately, each schema contains an array of "checks"; the concept of a "check" is new in Zod 4 and generalizes the concept of a refinement to include potentially side-effectful transforms like `z.toLowerCase()`. - -This is particularly apparent in the Zod Mini API, which heavily relies on the `.check()` method to compose various validations together. - -```ts -import * as z from "zod/mini"; - -z.string().check( - z.minLength(10), - z.maxLength(100), - z.toLowerCase(), - z.trim(), -); -``` - -### adds `ZodTransform` - -Meanwhile, transforms have been moved into a dedicated `ZodTransform` class. This schema class represents an input transform; in fact, you can actually define standalone transformations now: - -```ts -import * as z from "zod"; - -const schema = z.transform(input => String(input)); - -schema.parse(12); // => "12" -``` - -This is primarily used in conjunction with `ZodPipe`. The `.transform()` method now returns an instance of `ZodPipe`. - -```ts -z.string().transform(val => val); // ZodPipe -``` - -### drops `ZodPreprocess` - -As with `.transform()`, the `z.preprocess()` function now returns a `ZodPipe` instance instead of a dedicated `ZodPreprocess` instance. - -```ts -z.preprocess(val => val, z.string()); // ZodPipe -``` - -### drops `ZodBranded` - -Branding is now handled with a direct modification to the inferred type, instead of a dedicated `ZodBranded` class. The user-facing APIs remain the same. - -{/* - Dropping support for ES5 - - Zod relies on `Set` internally */} - -{/* - `z.keyof` now returns `ZodEnum` instead of `ZodLiteral` */} - -{/* ## Changed: `.refine()` - - The `.refine()` method used to accept a function as the second argument. - - ```ts - // no longer supported - const longString = z.string().refine( - (val) => val.length > 10, - (val) => ({ message: `${val} is not more than 10 characters` }) - ); - ``` - - This can be better represented with the new `error` parameter, so this overload has been removed. - - ```ts - const longString = z.string().refine((val) => val.length > 10, { - error: (issue) => `${issue.input} is not more than 10 characters`, - }); - `` - */} - -{/* - - No support for `null` or `undefined` in `z.literal` - - `z.literal(null)` - - `z.literal(undefined)` - - this was never documented */} - -{/* - Array min/max/length checks now run after parsing. This means they won't run if the parse has already aborted. */} - -{/* - Drops single-argument `z.record()` */} - -{/* - Smarter `z.record`: no longer Partial by default */} - -{/* - Intersection merge errors are now thrown as Error not ZodError - - These usually do not reflect a parse error but a structural problem with the schema */} - -{/* - Consolidates `unknownKeys` and `catchall` in ZodObject */} - -{/* - Dropping - - `ZodBranded`: purely a static-domain annotation - - `ZodFunction` */} - -{/* - The `description` is now stored in `z.defaultRegistry`, not the def - - No support for `description` in factory params - - Descriptions do not cascade in `.optional()`, etc */} - -{/* - Enums: - - ZodEnum and ZodNativeEnum are merged - - `.Values` and `.Enum` are removed. Use `.enum` instead. - - `.options` is removed */} diff --git a/docs/ARCHITECTURE.md b/docs/dev/ARCHITECTURE.md similarity index 91% rename from docs/ARCHITECTURE.md rename to docs/dev/ARCHITECTURE.md index 0655827c..7d72cb13 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/dev/ARCHITECTURE.md @@ -30,7 +30,7 @@ XcodeBuildMCP is a Model Context Protocol (MCP) server that exposes Xcode operat ### Runtime Flow 1. **Initialization** - - The `xcodebuildmcp` executable, as defined in `package.json`, points to the compiled `build/index.js` which executes the main logic from `src/index.ts`. + - The `xcodebuildmcp` executable, as defined in `package.json`, points to the compiled `build/cli.js` (CLI entrypoint from `src/cli.ts`); the MCP server starts via the `mcp` subcommand which invokes `src/index.ts`. - Sentry initialized for error tracking (optional) - Version information loaded from `package.json` @@ -47,7 +47,7 @@ XcodeBuildMCP is a Model Context Protocol (MCP) server that exposes Xcode operat 4. **Plugin & Resource Loading (Runtime)** - At runtime, `loadPlugins()` and `loadResources()` use the generated loaders from the previous step - All workflow loaders are executed at startup to register tools - - If `XCODEBUILDMCP_ENABLED_WORKFLOWS` is set, only those workflows (plus `session-management`) are registered +- If `XCODEBUILDMCP_ENABLED_WORKFLOWS` is set, only those workflows (plus `session-management`) are registered; `workflow-discovery` is only auto-included when `XCODEBUILDMCP_EXPERIMENTAL_WORKFLOW_DISCOVERY=true` 5. **Tool Registration** - Discovered tools automatically registered with server using pre-generated maps @@ -223,14 +223,14 @@ export async function someToolLogic( executor: CommandExecutor, ): Promise { log('info', `Executing some_tool with param: ${params.requiredParam}`); - + try { const result = await executor(['some', 'command'], 'Some Tool Operation'); - + if (!result.success) { return createErrorResponse('Operation failed', result.error); } - + return createTextResponse(`✅ Success: ${result.output}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -243,7 +243,7 @@ export default { name: 'some_tool', description: 'Tool description for AI agents. Example: some_tool({ requiredParam: "value" })', schema: someToolSchema.shape, // Expose shape for MCP SDK - + // 5. Create the handler using the type-safe factory handler: createTypedTool( someToolSchema, @@ -260,9 +260,18 @@ This pattern ensures that: - Import paths use focused facades for clear dependency management ``` +### Debugger Subsystem + +The debugging workflow relies on a long-lived, interactive LLDB subprocess. A `DebuggerManager` owns the session lifecycle and routes tool calls to a backend implementation. The default backend is the LLDB CLI (`xcrun lldb --no-lldbinit`) and configures a unique prompt sentinel to safely read command results. A stub DAP backend exists for future expansion. + +Key elements: +- **Interactive execution**: Uses a dedicated interactive spawner with `stdin: 'pipe'` so LLDB commands can be streamed across multiple tool calls. +- **Session manager**: Tracks debug session metadata (session id, simulator id, pid, timestamps) and maintains a “current” session. +- **Backend abstraction**: `DebuggerBackend` keeps the tool contract stable while allowing future DAP support. + ### MCP Resources System -XcodeBuildMCP provides dual interfaces: traditional MCP tools and efficient MCP resources for supported clients. Resources are located in `src/mcp/resources/` and are automatically discovered **at build time**. The build process generates `src/core/generated-resources.ts`, which contains dynamic loaders for each resource, improving startup performance. For more details on creating resources, see the [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md). +XcodeBuildMCP provides dual interfaces: traditional MCP tools and efficient MCP resources for supported clients. Resources are located in `src/mcp/resources/` and are automatically discovered **at build time**. The build process generates `src/core/generated-resources.ts`, which contains dynamic loaders for each resource, improving startup performance. For more details on creating resources, see the [Plugin Development Guide](PLUGIN_DEVELOPMENT.md). #### Resource Architecture @@ -409,9 +418,9 @@ Not all parts are required for every tool. For example, `swift_package_build` ha ### Testing Principles -XcodeBuildMCP uses a strict **Dependency Injection (DI)** pattern for testing, which completely bans the use of traditional mocking libraries like Vitest's `vi.mock` or `vi.fn`. This ensures that tests are robust, maintainable, and verify the actual integration between components. +XcodeBuildMCP uses a **Dependency Injection (DI)** pattern for testing external boundaries (command execution, filesystem, and other side effects). Vitest mocking libraries (`vi.mock`, `vi.fn`, etc.) are acceptable for internal collaborators when needed. This keeps tests robust while preserving deterministic behavior at external boundaries. -For detailed guidelines, see the [Testing Guide](docs/TESTING.md). +For detailed guidelines, see the [Testing Guide](TESTING.md). ### Test Structure Example @@ -432,7 +441,7 @@ describe('Tool Name', () => { // 2. Call the tool's logic function, injecting the mock executor const result = await someToolLogic({ requiredParam: 'value' }, mockExecutor); - + // 3. Assert the final result expect(result).toEqual({ content: [{ type: 'text', text: 'Expected output' }], @@ -499,7 +508,7 @@ bundled/ ## Extension Guidelines -This project is designed to be extensible. For comprehensive instructions on creating new tools, workflow groups, and resources, please refer to the dedicated [**Plugin Development Guide**](docs/PLUGIN_DEVELOPMENT.md). +This project is designed to be extensible. For comprehensive instructions on creating new tools, workflow groups, and resources, please refer to the dedicated [**Plugin Development Guide**](PLUGIN_DEVELOPMENT.md). The guide covers: - The auto-discovery system architecture. @@ -555,4 +564,6 @@ The guide covers: - Sensitive information scrubbed from errors - Stack traces limited to application code -- Sentry integration respects privacy settings +- Sentry capture is explicit (`{ sentry: true }`) and limited to internal runtime failures +- MCP wrapper auto-instrumentation is enabled for MCP observability, with tool input/output capture disabled (`recordInputs: false`, `recordOutputs: false`) +- Request/user context and user home paths are scrubbed before telemetry is sent diff --git a/docs/CODE_QUALITY.md b/docs/dev/CODE_QUALITY.md similarity index 92% rename from docs/CODE_QUALITY.md rename to docs/dev/CODE_QUALITY.md index 274f5ab1..a3aa7554 100644 --- a/docs/CODE_QUALITY.md +++ b/docs/dev/CODE_QUALITY.md @@ -35,7 +35,7 @@ The project uses a comprehensive ESLint setup that covers: ### ESLint Rules -For detailed ESLint rules and rationale, see [ESLINT_RULES.md](./ESLINT_RULES.md). +For detailed ESLint rules and rationale, see [ESLINT_TYPE_SAFETY.md](ESLINT_TYPE_SAFETY.md). ### Running ESLint @@ -61,9 +61,9 @@ XcodeBuildMCP enforces several architectural patterns that cannot be expressed t - Logic functions accepting `executor?: CommandExecutor` parameter ❌ **Forbidden**: -- Direct use of `vi.mock()`, `vi.fn()`, or any Vitest mocking - Direct calls to `execSync`, `spawn`, or `exec` in production code - Testing handler functions directly +- Real external side effects in unit tests (xcodebuild/xcrun/filesystem writes outside mocks) ### 2. Handler Signature Compliance @@ -145,7 +145,7 @@ The pattern checker enforces XcodeBuildMCP-specific architectural rules: node scripts/check-code-patterns.js # Check specific pattern type -node scripts/check-code-patterns.js --pattern=vitest +node scripts/check-code-patterns.js node scripts/check-code-patterns.js --pattern=execsync node scripts/check-code-patterns.js --pattern=handler node scripts/check-code-patterns.js --pattern=handler-testing @@ -172,12 +172,11 @@ npm run tools:all The pattern checker identifies the following violations: -### 1. Vitest Mocking Violations - -**What**: Any use of Vitest mocking functions -**Why**: Breaks dependency injection architecture -**Fix**: Use `createMockExecutor()` instead +### 1. External-Boundary Mocking Violations +**What**: Tests that mock external side effects without injected executors/filesystem dependencies +**Why**: Breaks deterministic external-boundary testing +**Fix**: Use `createMockExecutor()` / `createMockFileSystemExecutor()` for external dependencies ### 2. ExecSync Violations **What**: Direct use of Node.js child_process functions in production code @@ -251,7 +250,7 @@ node scripts/check-code-patterns.js # Check architectural compliance ### 3. Writing Tests 1. Import the logic function, not the default export -2. Use `createMockExecutor()` for mocking +2. Use `createMockExecutor()` / `createMockFileSystemExecutor()` for external side effects 3. Test three dimensions: validation, command generation, output processing 4. Never test handlers directly @@ -300,4 +299,4 @@ If migration tooling reports incorrect status: 2. **IDE Integration**: Create VS Code extension for real-time checking 3. **Performance Metrics**: Add build and test performance tracking 4. **Complexity Analysis**: Add code complexity metrics -5. **Documentation Linting**: Add documentation quality checks \ No newline at end of file +5. **Documentation Linting**: Add documentation quality checks diff --git a/docs/CONTRIBUTING.md b/docs/dev/CONTRIBUTING.md similarity index 68% rename from docs/CONTRIBUTING.md rename to docs/dev/CONTRIBUTING.md index 82aec783..5289af9b 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/dev/CONTRIBUTING.md @@ -2,6 +2,36 @@ Contributions are welcome! Here's how you can help improve XcodeBuildMCP. +- [Local development setup](#local-development-setup) + - [Prerequisites](#prerequisites) + - [Optional: Enabling UI Automation](#optional-enabling-ui-automation) + - [Installation](#installation) + - [Configure your MCP client](#configure-your-mcp-client) + - [Developing using VS Code](#developing-using-vs-code) + - [Debugging](#debugging) + - [MCP Inspector (Basic Debugging)](#mcp-inspector-basic-debugging) + - [Reloaderoo (Advanced Debugging) - **RECOMMENDED**](#reloaderoo-advanced-debugging---recommended) + - [1. Proxy Mode (Hot-Reloading)](#1-proxy-mode-hot-reloading) + - [2. Inspection Mode (Raw MCP Debugging)](#2-inspection-mode-raw-mcp-debugging) + - [Workflow Selection Testing](#workflow-selection-testing) + - [Using XcodeBuildMCP doctor tool](#using-xcodebuildmcp-doctor-tool) + - [Development Workflow with Reloaderoo](#development-workflow-with-reloaderoo) +- [Architecture and Code Standards](#architecture-and-code-standards) + - [Code Quality Requirements](#code-quality-requirements) + - [Testing Standards](#testing-standards) + - [Pre-Commit Checklist](#pre-commit-checklist) +- [Making changes](#making-changes) +- [Plugin Development](#plugin-development) + - [Quick Plugin Development Checklist](#quick-plugin-development-checklist) + - [Working with Project Templates](#working-with-project-templates) + - [Template Repositories](#template-repositories) + - [Local Template Development](#local-template-development) + - [Template Versioning](#template-versioning) + - [Testing Template Changes](#testing-template-changes) +- [Testing](#testing) +- [Submitting](#submitting) +- [Code of Conduct](#code-of-conduct) + ## Local development setup ### Prerequisites @@ -21,6 +51,22 @@ brew tap cameroncooke/axe brew install axe ``` +#### Optional: Using a Local AXe Checkout for Bundling + +`npm run bundle:axe` defaults to downloading pinned AXe release artifacts from GitHub. + +To bundle from a local AXe source checkout instead: + +```bash +AXE_USE_LOCAL=1 AXE_LOCAL_DIR=/absolute/path/to/AXe npm run bundle:axe +``` + +Rules: +- Local mode is enabled only when `AXE_USE_LOCAL=1`. +- `AXE_LOCAL_DIR` must point to a valid AXe repository (must contain `Package.swift`). +- If `AXE_USE_LOCAL=1` and `AXE_LOCAL_DIR` is missing/invalid, bundling fails fast. +- `AXE_FORCE_REMOTE=1` overrides local mode and forces remote artifact download. + ### Installation 1. Clone the repository @@ -28,13 +74,18 @@ brew install axe ``` npm install ``` -3. Build the project: +3. Install repository-managed git hooks: + ``` + npm run hooks:install + ``` + This configures `core.hooksPath` to `.githooks` so the shared pre-commit hook runs for this repository. +4. Build the project: ``` npm run build ``` -4. Start the server: +5. Start the server: ``` - node build/index.js + node build/cli.js mcp ``` ### Configure your MCP client @@ -47,7 +98,8 @@ Most MCP clients (Cursor, VS Code, Windsurf, Claude Desktop etc) have standardis "XcodeBuildMCP": { "command": "node", "args": [ - "/path_to/XcodeBuildMCP/build/index.js" + "/path_to/XcodeBuildMCP/build/cli.js", + "mcp" ] } } @@ -79,7 +131,7 @@ npm run inspect or if you prefer the explicit command: ```bash -npx @modelcontextprotocol/inspector node build/index.js +npx @modelcontextprotocol/inspector node build/cli.js mcp ``` #### Reloaderoo (Advanced Debugging) - **RECOMMENDED** @@ -96,7 +148,7 @@ Provides transparent hot-reloading without disconnecting your MCP client: npm install -g reloaderoo # Start XcodeBuildMCP through reloaderoo proxy -reloaderoo -- node build/index.js +reloaderoo -- node build/cli.js mcp ``` **Benefits**: @@ -109,7 +161,7 @@ reloaderoo -- node build/index.js ```json "XcodeBuildMCP": { "command": "reloaderoo", - "args": ["--", "node", "/path/to/XcodeBuildMCP/build/index.js"], + "args": ["--", "node", "/path/to/XcodeBuildMCP/build/cli.js", "mcp"], "env": { "XCODEBUILDMCP_DEBUG": "true" } @@ -121,7 +173,7 @@ Exposes debug tools for making raw MCP protocol calls and inspecting server resp ```bash # Start reloaderoo in inspection mode -reloaderoo inspect mcp -- node build/index.js +reloaderoo inspect mcp -- node build/cli.js mcp ``` **Available Debug Tools**: @@ -143,7 +195,7 @@ reloaderoo inspect mcp -- node build/index.js "inspect", "mcp", "--working-dir", "/path/to/XcodeBuildMCP", "--", - "node", "/path/to/XcodeBuildMCP/build/index.js" + "node", "/path/to/XcodeBuildMCP/build/cli.js", "mcp" ], "env": { "XCODEBUILDMCP_DEBUG": "true" @@ -157,10 +209,10 @@ Test full vs. selective workflow registration during development: ```bash # Test full tool registration (default) -reloaderoo inspect mcp -- node build/index.js +reloaderoo inspect mcp -- node build/cli.js mcp # Test selective workflow registration -XCODEBUILDMCP_ENABLED_WORKFLOWS=simulator,device reloaderoo inspect mcp -- node build/index.js +XCODEBUILDMCP_ENABLED_WORKFLOWS=simulator,device reloaderoo inspect mcp -- node build/cli.js mcp ``` **Key Differences to Test**: - **Full Registration**: All tools are available immediately via `list_tools` @@ -181,7 +233,7 @@ Running the XcodeBuildMCP server with the environmental variable `XCODEBUILDMCP_ 1. **Start Development Session**: ```bash # Terminal 1: Start in hot-reload mode - reloaderoo -- node build/index.js + reloaderoo -- node build/cli.js mcp # Terminal 2: Start build watcher npm run build:watch @@ -201,9 +253,9 @@ Running the XcodeBuildMCP server with the environmental variable `XCODEBUILDMCP_ Before making changes, please familiarize yourself with: - [ARCHITECTURE.md](ARCHITECTURE.md) - Comprehensive architectural overview -- [CLAUDE.md](CLAUDE.md) - AI assistant guidelines and testing principles -- [TOOLS.md](TOOLS.md) - Complete tool documentation -- [TOOL_OPTIONS.md](TOOL_OPTIONS.md) - Tool configuration options +- [CLAUDE.md](../../CLAUDE.md) - AI assistant guidelines and testing principles +- [TOOLS.md](../TOOLS.md) - Complete tool documentation +- [CONFIGURATION.md](../CONFIGURATION.md) - Tool configuration options ### Code Quality Requirements @@ -215,15 +267,15 @@ Before making changes, please familiarize yourself with: ### Testing Standards -All contributions must adhere to the testing standards outlined in the [**XcodeBuildMCP Plugin Testing Guidelines (docs/TESTING.md)**](docs/TESTING.md). This is the canonical source of truth for all testing practices. +All contributions must adhere to the testing standards outlined in the [**XcodeBuildMCP Plugin Testing Guidelines (TESTING.md)**](TESTING.md). This is the canonical source of truth for all testing practices. **Key Principles (Summary):** -- **No Vitest Mocking**: All forms of `vi.mock`, `vi.fn`, `vi.spyOn`, etc., are strictly forbidden. -- **Dependency Injection**: All external dependencies (command execution, file system access) must be injected into tool logic functions using the `CommandExecutor` and `FileSystemExecutor` patterns. +- **Dependency Injection for External Boundaries**: All external dependencies (command execution, file system access) must be injected into tool logic functions using the `CommandExecutor` and `FileSystemExecutor` patterns. +- **Internal Mocking Is Allowed**: Vitest mocking (`vi.mock`, `vi.fn`, `vi.spyOn`, etc.) is acceptable for internal modules/collaborators. - **Test Production Code**: Tests must import and execute the actual tool logic, not mock implementations. - **Comprehensive Coverage**: Tests must cover input validation, command generation, and output processing. -Please read [docs/TESTING.md](docs/TESTING.md) in its entirety before writing tests. +Please read [TESTING.md](TESTING.md) in its entirety before writing tests. ### Pre-Commit Checklist @@ -231,20 +283,32 @@ Please read [docs/TESTING.md](docs/TESTING.md) in its entirety before writing te ```bash # 1. Run linting (must pass with 0 errors) -npm run lint +npm run lint:fix -# 2. Run formatting (must format all files) +# 2. Run typechecker (must pass with 0 errors) +npm run typecheck + +# 3. Run formatting (must format all files) npm run format -# 3. Run build (must compile successfully) +# 4. Run build (must compile successfully) npm run build -# 4. Run tests (all tests must pass) +# 5. Validate docs CLI command references (requires built CLI artifact) +npm run docs:check + +# 6. Run tests (all tests must pass) npm test ``` **NO EXCEPTIONS**: Code that fails any of these commands cannot be committed. +The shared pre-commit hook installed via `npm run hooks:install` runs: +- `npm run format:check` +- `npm run lint` +- `npm run build` +- `npm run docs:check` + ## Making changes 1. Fork the repository and create a new branch @@ -253,7 +317,7 @@ npm test ## Plugin Development -For comprehensive instructions on creating new tools and workflow groups, see our dedicated [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md). +For comprehensive instructions on creating new tools and workflow groups, see our dedicated [Plugin Development Guide](PLUGIN_DEVELOPMENT.md). The plugin development guide covers: - Auto-discovery system architecture @@ -270,7 +334,7 @@ The plugin development guide covers: 4. Create comprehensive tests using `createMockExecutor()` 5. Add workflow metadata if creating new workflow group -See [PLUGIN_DEVELOPMENT.md](docs/PLUGIN_DEVELOPMENT.md) for complete details. +See [PLUGIN_DEVELOPMENT.md](PLUGIN_DEVELOPMENT.md) for complete details. ### Working with Project Templates @@ -278,8 +342,8 @@ XcodeBuildMCP uses external template repositories for the iOS and macOS project #### Template Repositories -- **iOS Template**: [XcodeBuildMCP-iOS-Template](https://github.com/cameroncooke/XcodeBuildMCP-iOS-Template) -- **macOS Template**: [XcodeBuildMCP-macOS-Template](https://github.com/cameroncooke/XcodeBuildMCP-macOS-Template) +- **iOS Template**: [XcodeBuildMCP-iOS-Template](https://github.com/getsentry/XcodeBuildMCP-iOS-Template) +- **macOS Template**: [XcodeBuildMCP-macOS-Template](https://github.com/getsentry/XcodeBuildMCP-macOS-Template) #### Local Template Development @@ -287,8 +351,8 @@ When developing or testing changes to the templates: 1. Clone the template repository you want to work on: ```bash - git clone https://github.com/cameroncooke/XcodeBuildMCP-iOS-Template.git - git clone https://github.com/cameroncooke/XcodeBuildMCP-macOS-Template.git + git clone https://github.com/getsentry/XcodeBuildMCP-iOS-Template.git + git clone https://github.com/getsentry/XcodeBuildMCP-macOS-Template.git ``` 2. Set the appropriate environment variable to use your local template: @@ -304,7 +368,7 @@ When developing or testing changes to the templates: ```json "XcodeBuildMCP": { "command": "node", - "args": ["/path_to/XcodeBuildMCP/build/index.js"], + "args": ["/path_to/XcodeBuildMCP/build/cli.js", "mcp"], "env": { "XCODEBUILDMCP_IOS_TEMPLATE_PATH": "/path/to/XcodeBuildMCP-iOS-Template", "XCODEBUILDMCP_MACOS_TEMPLATE_PATH": "/path/to/XcodeBuildMCP-macOS-Template" @@ -351,4 +415,4 @@ For major changes or new features, please open an issue first to discuss your pr ## Code of Conduct -Please follow our [Code of Conduct](CODE_OF_CONDUCT.md) and community guidelines. +Please follow our [Code of Conduct](../../CODE_OF_CONDUCT.md) and community guidelines. diff --git a/docs/ESLINT_TYPE_SAFETY.md b/docs/dev/ESLINT_TYPE_SAFETY.md similarity index 100% rename from docs/ESLINT_TYPE_SAFETY.md rename to docs/dev/ESLINT_TYPE_SAFETY.md diff --git a/docs/dev/MANIFEST_FORMAT.md b/docs/dev/MANIFEST_FORMAT.md new file mode 100644 index 00000000..578a6a80 --- /dev/null +++ b/docs/dev/MANIFEST_FORMAT.md @@ -0,0 +1,467 @@ +# Manifest Format Reference + +This document describes the YAML manifest format used to define tools and workflows in XcodeBuildMCP. Manifests are the single source of truth for tool/workflow metadata, visibility rules, and runtime behavior. + +## Overview + +Manifests are stored in the `manifests/` directory: + +``` +manifests/ +├── tools/ # Tool manifest files +│ ├── build_sim.yaml +│ ├── list_sims.yaml +│ └── ... +└── workflows/ # Workflow manifest files + ├── simulator.yaml + ├── device.yaml + └── ... +``` + +Each tool and workflow has its own YAML file. The manifest loader reads all files at startup and validates them against the schema. + +## Directory Structure + +Tool implementations live in `src/mcp/tools//`: + +``` +src/mcp/tools/ +├── simulator/ +│ ├── build_sim.ts # Tool implementation +│ ├── build_run_sim.ts +│ ├── list_sims.ts +│ └── ... +├── device/ +│ ├── build_device.ts +│ └── ... +└── ... +``` + +## Tool Manifest Format + +Tool manifests define individual tools and their metadata. + +### Schema + +```yaml +# Required fields +id: string # Unique tool identifier (must match filename without .yaml) +module: string # Module path (see Module Path section) +names: + mcp: string # MCP tool name (globally unique, used in MCP protocol) + cli: string # CLI command name (optional, derived from mcp if omitted) + +# Optional fields +description: string # Tool description (shown in tool listings) +availability: # Per-runtime availability flags + mcp: boolean # Available via MCP server (default: true) + cli: boolean # Available via CLI (default: true) +predicates: string[] # Predicate names for visibility filtering (default: []) +routing: # CLI daemon routing + stateful: boolean # Tool maintains state (default: false) +annotations: # MCP tool annotations (hints for clients) + title: string # Human-readable title (optional) + readOnlyHint: boolean # Tool only reads data (optional) + destructiveHint: boolean # Tool may modify/delete data (optional) + idempotentHint: boolean # Safe to retry (optional) + openWorldHint: boolean # May access external resources (optional) +``` + +### Example: Basic Tool + +```yaml +id: list_sims +module: mcp/tools/simulator/list_sims +names: + mcp: list_sims +description: "List available iOS simulators." +availability: + mcp: true + cli: true +predicates: [] +annotations: + title: "List Simulators" + readOnlyHint: true +``` + +### Example: Tool with Predicates + +```yaml +id: build_sim +module: mcp/tools/simulator/build_sim +names: + mcp: build_sim +description: "Build for iOS sim." +availability: + mcp: true + cli: true +predicates: + - hideWhenXcodeAgentMode # Hidden when Xcode provides equivalent tool +``` + +### Example: MCP-Only Tool + +```yaml +id: manage_workflows +module: mcp/tools/workflow-discovery/manage_workflows +names: + mcp: manage-workflows # Note: MCP name uses hyphens +description: "Manage enabled workflows at runtime." +availability: + mcp: true + cli: false # Not available in CLI +predicates: + - experimentalWorkflowDiscoveryEnabled +``` + +## Workflow Manifest Format + +Workflow manifests define groups of related tools. + +### Schema + +```yaml +# Required fields +id: string # Unique workflow identifier (must match filename without .yaml) +title: string # Display title +description: string # Workflow description +tools: string[] # Array of tool IDs belonging to this workflow + +# Optional fields +availability: # Per-runtime availability flags + mcp: boolean # Available via MCP server (default: true) + cli: boolean # Available via CLI (default: true) +selection: # MCP selection rules + mcp: + defaultEnabled: boolean # Enabled when config.enabledWorkflows is empty (default: false) + autoInclude: boolean # Include when predicates pass, even if not requested (default: false) +predicates: string[] # Predicate names for visibility filtering (default: []) +``` + +### Example: Default-Enabled Workflow + +```yaml +id: simulator +title: "iOS Simulator Development" +description: "Complete iOS development workflow for simulators." +availability: + mcp: true + cli: true +selection: + mcp: + defaultEnabled: true # Enabled by default + autoInclude: false +predicates: [] +tools: + - list_sims + - boot_sim + - build_sim + - build_run_sim + - test_sim + # ... more tools +``` + +### Example: Auto-Include Workflow + +```yaml +id: doctor +title: "MCP Doctor" +description: "Diagnostic tool for the MCP server environment." +availability: + mcp: true + cli: true +selection: + mcp: + defaultEnabled: false + autoInclude: true # Auto-included when predicates pass +predicates: + - debugEnabled # Only shown in debug mode +tools: + - doctor +``` + +### Example: Conditional Workflow + +```yaml +id: workflow-discovery +title: "Workflow Discovery" +description: "Manage enabled workflows at runtime." +availability: + mcp: true + cli: false +selection: + mcp: + defaultEnabled: false + autoInclude: true +predicates: + - experimentalWorkflowDiscoveryEnabled # Feature flag +tools: + - manage_workflows +``` + +## Field Reference + +### Tool Fields + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `id` | string | Yes | - | Unique identifier, must match filename | +| `module` | string | Yes | - | Module path relative to `src/` (extensionless) | +| `names.mcp` | string | Yes | - | MCP protocol tool name | +| `names.cli` | string | No | Derived from MCP name | CLI command name | +| `description` | string | No | - | Tool description | +| `availability.mcp` | boolean | No | `true` | Available via MCP | +| `availability.cli` | boolean | No | `true` | Available via CLI | +| `predicates` | string[] | No | `[]` | Visibility predicates (all must pass) | +| `routing.stateful` | boolean | No | `false` | Tool maintains state | +| `annotations.title` | string | No | - | Human-readable title | +| `annotations.readOnlyHint` | boolean | No | - | Tool only reads data | +| `annotations.destructiveHint` | boolean | No | - | Tool may modify/delete data | +| `annotations.idempotentHint` | boolean | No | - | Safe to retry | +| `annotations.openWorldHint` | boolean | No | - | May access external resources | + +### Workflow Fields + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `id` | string | Yes | - | Unique identifier, must match filename | +| `title` | string | Yes | - | Display title | +| `description` | string | Yes | - | Workflow description | +| `tools` | string[] | Yes | - | Tool IDs in this workflow | +| `availability.mcp` | boolean | No | `true` | Available via MCP | +| `availability.cli` | boolean | No | `true` | Available via CLI | +| `selection.mcp.defaultEnabled` | boolean | No | `false` | Enabled when no workflows configured | +| `selection.mcp.autoInclude` | boolean | No | `false` | Auto-include when predicates pass | +| `predicates` | string[] | No | `[]` | Visibility predicates (all must pass) | + +## Module Path + +The `module` field specifies where to find the tool implementation. It uses a package-relative path without file extension: + +``` +mcp/tools// +``` + +At runtime, this resolves to: +``` +build/mcp/tools//.js +``` + +The module must export either: +1. **Named exports** (preferred): `{ schema, handler }` +2. **Legacy default export**: `export default { schema, handler }` + +Note: `name`, `description`, and `annotations` are defined in the YAML manifest, not the module. + +Example module structure: +```typescript +// src/mcp/tools/simulator/build_sim.ts +import { z } from 'zod'; + +export const schema = z.object({ + projectPath: z.string().describe('Path to project'), + // ... +}); + +export async function handler(params: z.infer) { + // Implementation +} +``` + +## Naming Conventions + +### Tool ID +- Use `snake_case`: `build_sim`, `list_devices` +- Must match the YAML filename (without `.yaml`) +- Must be unique across all tools + +### MCP Name (`names.mcp`) +- Use `snake_case` or `kebab-case` consistently +- Must be globally unique across all tools +- This is what LLMs see and call + +### CLI Name (`names.cli`) +- Optional; if omitted, derived from MCP name +- Derivation: `snake_case` → `kebab-case` (`build_sim` → `build-sim`) +- Use `kebab-case` for explicit names + +### Workflow ID +- Use `kebab-case`: `simulator`, `swift-package`, `ui-automation` +- Must match the YAML filename (without `.yaml`) + +## Predicates + +Predicates control visibility based on runtime context. All predicates in the array must pass (AND logic) for the tool/workflow to be visible. + +### Available Predicates + +| Predicate | Description | +|-----------|-------------| +| `debugEnabled` | Show only when `config.debug` is `true` | +| `experimentalWorkflowDiscoveryEnabled` | Show only when experimental workflow discovery is enabled | +| `mcpRuntimeOnly` | Show only in MCP runtime (hide in CLI/daemon catalogs) | +| `runningUnderXcodeAgent` | Show only when running under Xcode's coding agent | +| `hideWhenXcodeAgentMode` | Hide when running inside Xcode's coding agent (tools conflict with Xcode's native equivalents) | +| `xcodeAutoSyncDisabled` | Show only when running under Xcode and `config.disableXcodeAutoSync` is `true` | +| `always` | Always visible (explicit documentation) | +| `never` | Never visible (temporarily disable) | + +Notes: +- Bridge availability/connection is handled at tool call time, not as a visibility predicate. +- Prefer runtime/config predicates for deterministic tool exposure. + +### Predicate Context + +Predicates receive a context object: + +```typescript +interface PredicateContext { + runtime: 'cli' | 'mcp' | 'daemon'; + config: ResolvedRuntimeConfig; + runningUnderXcode: boolean; +} +``` + +### Adding New Predicates + +To add a new predicate, edit `src/visibility/predicate-registry.ts`: + +```typescript +export const PREDICATES: Record = { + // Existing predicates... + + myNewPredicate: (ctx: PredicateContext): boolean => { + return ctx.config.someFlag === true; + }, +}; +``` + +## Workflow Selection Rules + +For MCP runtime, workflows are selected based on these rules (in order): + +1. **Auto-include workflows** (`autoInclude: true`) when their predicates pass +2. **Explicitly requested workflows** from `config.enabledWorkflows` +3. **Default workflows** (`defaultEnabled: true`) when `config.enabledWorkflows` is empty +4. All selected workflows are filtered by availability + predicates + +### Selection Examples + +```yaml +# Always included (autoInclude with no predicates = always passes) +selection: + mcp: + autoInclude: true + +# Enabled by default when no workflows configured +selection: + mcp: + defaultEnabled: true + +# MCP-only workflow/tool visibility +predicates: + - mcpRuntimeOnly + +# Auto-included only when predicates pass (e.g., debug mode) +selection: + mcp: + autoInclude: true +predicates: + - debugEnabled + +# Show only when manual Xcode sync is needed +predicates: + - xcodeAutoSyncDisabled +``` + +## Tool Re-export + +A single tool can belong to multiple workflows. This is useful for shared utilities: + +```yaml +# manifests/workflows/simulator.yaml +tools: + - clean # Shared tool + - discover_projs # Shared tool + - build_sim + +# manifests/workflows/device.yaml +tools: + - clean # Same tool, different workflow + - discover_projs # Same tool, different workflow + - build_device +``` + +The tool is defined once in `manifests/tools/clean.yaml` but referenced by both workflows. + +## Daemon Routing + +Daemon routing is intentionally simple: + +- **`routing.stateful: true`**: CLI routes this tool through the daemon. +- **`routing` omitted or `stateful: false`**: CLI runs the tool directly. +- **Special-case**: dynamic `xcode-ide` bridge tools use daemon-backed routing for bridge session persistence. + +## Validation + +Manifests are validated at load time against Zod schemas. Invalid manifests cause startup failures with descriptive error messages. + +The schema definitions are in `src/core/manifest/schema.ts`. + +## Runtime Tool Registration + +At startup, tools are registered dynamically from manifests: + +``` +1. loadManifest() + └── Reads all YAML files from manifests/tools/ and manifests/workflows/ + └── Validates against Zod schemas + └── Returns { tools: Map, workflows: Map } + +2. selectWorkflowsForMcp(workflows, requestedWorkflows, ctx) + └── Filters workflows by availability (mcp: true) + └── Applies selection rules (defaultEnabled, autoInclude) + └── Evaluates predicates against context + +3. For each selected workflow: + └── For each tool ID in workflow.tools: + └── Look up tool manifest by ID + └── Check tool availability and predicates + └── importToolModule(module) → { schema, handler, annotations } + └── server.registerTool(mcpName, schema, handler) +``` + +Key files: +- `src/core/manifest/load-manifest.ts` - Manifest loading and caching +- `src/core/manifest/import-tool-module.ts` - Dynamic module imports +- `src/utils/tool-registry.ts` - MCP server tool registration +- `src/runtime/tool-catalog.ts` - CLI/daemon tool catalog building +- `src/visibility/exposure.ts` - Workflow/tool visibility filtering + +## Creating a New Tool + +1. **Create the tool module** in `src/mcp/tools//.ts` +2. **Create the manifest** in `manifests/tools/.yaml` +3. **Add to workflow(s)** in `manifests/workflows/.yaml` +4. **Run tests** to validate + +Example checklist: +- [ ] Tool ID matches filename +- [ ] Module path is correct +- [ ] MCP name is unique +- [ ] Tool is added to at least one workflow +- [ ] Predicates reference valid predicate names +- [ ] Availability flags match intended runtimes + +## Creating a New Workflow + +1. **Create the manifest** in `manifests/workflows/.yaml` +2. **Add tool references** (tools must already exist) +3. **Configure selection rules** for MCP behavior +4. **Run tests** to validate + +Example checklist: +- [ ] Workflow ID matches filename +- [ ] All referenced tool IDs exist +- [ ] Selection rules are appropriate +- [ ] Predicates reference valid predicate names diff --git a/docs/MANUAL_TESTING.md b/docs/dev/MANUAL_TESTING.md similarity index 92% rename from docs/MANUAL_TESTING.md rename to docs/dev/MANUAL_TESTING.md index 7b1ff02c..b4049e56 100644 --- a/docs/MANUAL_TESTING.md +++ b/docs/dev/MANUAL_TESTING.md @@ -60,11 +60,11 @@ Black Box Testing means testing ONLY through external interfaces without any kno **ABSOLUTE TESTING RULES - NO EXCEPTIONS:** 1. **✅ ONLY ALLOWED: Reloaderoo Inspect Commands** - - `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/index.js` - - `npx reloaderoo@latest inspect list-tools -- node build/index.js` - - `npx reloaderoo@latest inspect read-resource "URI" -- node build/index.js` - - `npx reloaderoo@latest inspect server-info -- node build/index.js` - - `npx reloaderoo@latest inspect ping -- node build/index.js` + - `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect read-resource "URI" -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect server-info -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect ping -- node build/cli.js mcp` 2. **❌ COMPLETELY FORBIDDEN ACTIONS:** - **NEVER** call `mcp__XcodeBuildMCP__tool_name()` functions directly @@ -86,8 +86,8 @@ Black Box Testing means testing ONLY through external interfaces without any kno const result = await doctor(); // ✅ CORRECT - Only through Reloaderoo inspect - npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js - npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js + npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp + npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp ``` **WHY RELOADEROO INSPECT IS MANDATORY:** @@ -160,7 +160,7 @@ grep "^ • " /tmp/tools_detailed.txt | sed 's/^ • //' > /tmp/tool_names.t For EVERY tool in the list: ```bash # Test each tool individually - NO BATCHING -npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'APPROPRIATE_PARAMS' -- node build/index.js +npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'APPROPRIATE_PARAMS' -- node build/cli.js mcp # Mark tool as completed in TodoWrite IMMEDIATELY after testing # Record result (success/failure/blocked) for each tool @@ -260,7 +260,7 @@ fi - `test_*` tools - Run test suites 6. **UI Automation Tools** (depend on running apps): - - `describe_ui`, `screenshot`, `tap`, etc. + - `snapshot_ui`, `screenshot`, `tap`, etc. **MANDATORY: Record Key Outputs** @@ -277,6 +277,7 @@ Must capture and document these values for dependent tools: 1. **Build the server**: `npm run build` 2. **Install jq**: `brew install jq` (required for JSON parsing) 3. **System Requirements**: macOS with Xcode installed, connected devices/simulators optional +4. **AXe video capture (optional)**: run `npm run bundle:axe` before using `record_sim_video` in local tests (not required for unit tests) ## Step-by-Step Testing Process @@ -413,7 +414,7 @@ echo "Tool schema reference created at /tmp/tool_schemas.md" - Mark "completed" only after manual verification 2. **Test Each Tool Individually** - - Execute ONLY via `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/index.js` + - Execute ONLY via `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/cli.js mcp` - Wait for complete response before proceeding to next tool - Read and verify each tool's output manually - Record key outputs (UUIDs, paths, schemes) for dependent tools @@ -437,16 +438,16 @@ echo "Tool schema reference created at /tmp/tool_schemas.md" ```bash # Test server connectivity -npx reloaderoo@latest inspect ping -- node build/index.js +npx reloaderoo@latest inspect ping -- node build/cli.js mcp # Get server information -npx reloaderoo@latest inspect server-info -- node build/index.js +npx reloaderoo@latest inspect server-info -- node build/cli.js mcp # Verify tool count manually -npx reloaderoo@latest inspect list-tools -- node build/index.js 2>/dev/null | jq '.tools | length' +npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp 2>/dev/null | jq '.tools | length' # Verify resource count manually -npx reloaderoo@latest inspect list-resources -- node build/index.js 2>/dev/null | jq '.resources | length' +npx reloaderoo@latest inspect list-resources -- node build/cli.js mcp 2>/dev/null | jq '.resources | length' ``` #### Phase 2: Resource Testing @@ -455,7 +456,7 @@ npx reloaderoo@latest inspect list-resources -- node build/index.js 2>/dev/null # Test each resource systematically while IFS= read -r resource_uri; do echo "Testing resource: $resource_uri" - npx reloaderoo@latest inspect read-resource "$resource_uri" -- node build/index.js 2>/dev/null + npx reloaderoo@latest inspect read-resource "$resource_uri" -- node build/cli.js mcp 2>/dev/null echo "---" done < /tmp/resource_uris.txt ``` @@ -469,23 +470,23 @@ echo "=== FOUNDATION TOOL TESTING & DATA COLLECTION ===" # 1. Test doctor (no dependencies) echo "Testing doctor..." -npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp 2>/dev/null # 2. Collect device data echo "Collecting device UUIDs..." -npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js 2>/dev/null > /tmp/devices_output.json +npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp 2>/dev/null > /tmp/devices_output.json DEVICE_UUIDS=$(jq -r '.content[0].text' /tmp/devices_output.json | grep -E "UDID: [A-F0-9-]+" | sed 's/.*UDID: //' | head -2) echo "Device UUIDs captured: $DEVICE_UUIDS" # 3. Collect simulator data echo "Collecting simulator UUIDs..." -npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js 2>/dev/null > /tmp/sims_output.json +npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/cli.js mcp 2>/dev/null > /tmp/sims_output.json SIMULATOR_UUIDS=$(jq -r '.content[0].text' /tmp/sims_output.json | grep -E "\([A-F0-9-]+\)" | sed 's/.*(\([A-F0-9-]*\)).*/\1/' | head -3) echo "Simulator UUIDs captured: $SIMULATOR_UUIDS" # 4. Collect project data echo "Collecting project paths..." -npx reloaderoo@latest inspect call-tool "discover_projs" --params '{"workspaceRoot": "/Volumes/Developer/XcodeBuildMCP"}' -- node build/index.js 2>/dev/null > /tmp/projects_output.json +npx reloaderoo@latest inspect call-tool "discover_projs" --params '{"workspaceRoot": "/Volumes/Developer/XcodeBuildMCP"}' -- node build/cli.js mcp 2>/dev/null > /tmp/projects_output.json PROJECT_PATHS=$(jq -r '.content[1].text' /tmp/projects_output.json | grep -E "\.xcodeproj$" | sed 's/.*- //' | head -3) WORKSPACE_PATHS=$(jq -r '.content[2].text' /tmp/projects_output.json | grep -E "\.xcworkspace$" | sed 's/.*- //' | head -2) echo "Project paths captured: $PROJECT_PATHS" @@ -507,7 +508,7 @@ echo "=== DISCOVERY TOOL TESTING & METADATA COLLECTION ===" while IFS= read -r project_path; do if [ -n "$project_path" ]; then echo "Getting schemes for: $project_path" - npx reloaderoo@latest inspect call-tool "list_schems_proj" --params "{\"projectPath\": \"$project_path\"}" -- node build/index.js 2>/dev/null > /tmp/schemes_$$.json + npx reloaderoo@latest inspect call-tool "list_schems_proj" --params "{\"projectPath\": \"$project_path\"}" -- node build/cli.js mcp 2>/dev/null > /tmp/schemes_$$.json SCHEMES=$(jq -r '.content[1].text' /tmp/schemes_$$.json 2>/dev/null || echo "NoScheme") echo "$project_path|$SCHEMES" >> /tmp/project_schemes.txt echo "Schemes captured for $project_path: $SCHEMES" @@ -518,7 +519,7 @@ done < /tmp/project_paths.txt while IFS= read -r workspace_path; do if [ -n "$workspace_path" ]; then echo "Getting schemes for: $workspace_path" - npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/index.js 2>/dev/null > /tmp/ws_schemes_$$.json + npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/cli.js mcp 2>/dev/null > /tmp/ws_schemes_$$.json SCHEMES=$(jq -r '.content[1].text' /tmp/ws_schemes_$$.json 2>/dev/null || echo "NoScheme") echo "$workspace_path|$SCHEMES" >> /tmp/workspace_schemes.txt echo "Schemes captured for $workspace_path: $SCHEMES" @@ -543,29 +544,29 @@ done < /tmp/workspace_paths.txt ```bash # STEP 1: Test foundation tools (no parameters required) # Execute each command individually, wait for response, verify manually -npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp # [Wait for response, read output, mark tool complete in task list] -npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp # [Record device UUIDs from response for dependent tools] -npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/cli.js mcp # [Record simulator UUIDs from response for dependent tools] # STEP 2: Test project discovery (use discovered project paths) -npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPath": "/actual/path/from/discover_projs.xcodeproj"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPath": "/actual/path/from/discover_projs.xcodeproj"}' -- node build/cli.js mcp # [Record scheme names from response for build tools] # STEP 3: Test workspace tools (use discovered workspace paths) -npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/cli.js mcp # [Record scheme names from response for build tools] # STEP 4: Test simulator tools (use captured simulator UUIDs from step 1) -npx reloaderoo@latest inspect call-tool "boot_sim" --params '{"simulatorUuid": "ACTUAL_UUID_FROM_LIST_SIMS"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "boot_sim" --params '{"simulatorUuid": "ACTUAL_UUID_FROM_LIST_SIMS"}' -- node build/cli.js mcp # [Verify simulator boots successfully] # STEP 5: Test build tools (requires project + scheme + simulator from previous steps) -npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectPath": "/actual/project.xcodeproj", "scheme": "ActualSchemeName", "simulatorId": "ACTUAL_SIMULATOR_UUID"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectPath": "/actual/project.xcodeproj", "scheme": "ActualSchemeName", "simulatorId": "ACTUAL_SIMULATOR_UUID"}' -- node build/cli.js mcp # [Verify build succeeds and record app bundle path] ``` @@ -591,7 +592,7 @@ npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectP ```bash # ❌ IMMEDIATE TERMINATION - Using scripts to test tools for tool in $(cat tool_list.txt); do - npx reloaderoo inspect call-tool "$tool" --params '{}' -- node build/index.js + npx reloaderoo inspect call-tool "$tool" --params '{}' -- node build/cli.js mcp done ``` @@ -617,19 +618,19 @@ npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectP ```bash # ✅ CORRECT - Step-by-step manual execution via Reloaderoo # Tool 1: Test doctor -npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp # [Read response, verify, mark complete in TodoWrite] # Tool 2: Test list_devices -npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp # [Read response, capture UUIDs, mark complete in TodoWrite] # Tool 3: Test list_sims -npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/cli.js mcp # [Read response, capture UUIDs, mark complete in TodoWrite] # Tool X: Test stateful tool (expected to fail) -npx reloaderoo@latest inspect call-tool "swift_package_stop" --params '{"pid": 12345}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "swift_package_stop" --params '{"pid": 12345}' -- node build/cli.js mcp # [Tool fails as expected - no in-memory state available] # [Mark as "false negative - stateful tool limitation" in TodoWrite] # [Continue to next tool without investigation] @@ -654,15 +655,15 @@ echo "=== Error Testing ===" # Test with invalid JSON parameters echo "Testing invalid parameter types..." -npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": 123}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": 123}' -- node build/cli.js mcp 2>/dev/null # Test with non-existent paths echo "Testing non-existent paths..." -npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": "/nonexistent/path.xcodeproj"}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": "/nonexistent/path.xcodeproj"}' -- node build/cli.js mcp 2>/dev/null # Test with invalid UUIDs echo "Testing invalid UUIDs..." -npx reloaderoo@latest inspect call-tool boot_sim --params '{"simulatorUuid": "invalid-uuid"}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool boot_sim --params '{"simulatorUuid": "invalid-uuid"}' -- node build/cli.js mcp 2>/dev/null ``` ## Testing Report Generation @@ -698,12 +699,12 @@ echo "Testing session template created: TESTING_SESSION_$(date +%Y-%m-%d).md" ```bash # Essential testing commands -npx reloaderoo@latest inspect ping -- node build/index.js -npx reloaderoo@latest inspect server-info -- node build/index.js -npx reloaderoo@latest inspect list-tools -- node build/index.js | jq '.tools | length' -npx reloaderoo@latest inspect list-resources -- node build/index.js | jq '.resources | length' -npx reloaderoo@latest inspect call-tool TOOL_NAME --params '{}' -- node build/index.js -npx reloaderoo@latest inspect read-resource "xcodebuildmcp://RESOURCE" -- node build/index.js +npx reloaderoo@latest inspect ping -- node build/cli.js mcp +npx reloaderoo@latest inspect server-info -- node build/cli.js mcp +npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp | jq '.tools | length' +npx reloaderoo@latest inspect list-resources -- node build/cli.js mcp | jq '.resources | length' +npx reloaderoo@latest inspect call-tool TOOL_NAME --params '{}' -- node build/cli.js mcp +npx reloaderoo@latest inspect read-resource "xcodebuildmcp://RESOURCE" -- node build/cli.js mcp # Schema extraction jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .inputSchema' /tmp/tools.json @@ -719,7 +720,7 @@ jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .description' /tm **Cause**: Server startup issues or MCP protocol communication problems **Resolution**: - Verify server builds successfully: `npm run build` -- Test direct server startup: `node build/index.js` +- Test direct server startup: `node build/cli.js mcp` - Check for TypeScript compilation errors #### 2. Tool Parameter Validation Errors @@ -734,7 +735,7 @@ jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .description' /tm **Symptoms**: Reloaderoo reports tool not found **Cause**: Tool name mismatch or server registration issues **Resolution**: -- Verify tool exists in list: `npx reloaderoo@latest inspect list-tools -- node build/index.js | jq '.tools[].name'` +- Verify tool exists in list: `npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp | jq '.tools[].name'` - Check exact tool name spelling and case sensitivity - Ensure server built successfully @@ -746,4 +747,4 @@ jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .description' /tm - Mark tool as "failed - empty response" in task list - Continue with next tool in sequence -This systematic approach ensures comprehensive, accurate testing using programmatic discovery and validation of all XcodeBuildMCP functionality through the MCP interface exclusively. \ No newline at end of file +This systematic approach ensures comprehensive, accurate testing using programmatic discovery and validation of all XcodeBuildMCP functionality through the MCP interface exclusively. diff --git a/docs/NODEJS_2025.md b/docs/dev/NODEJS_2025.md similarity index 100% rename from docs/NODEJS_2025.md rename to docs/dev/NODEJS_2025.md diff --git a/docs/dev/README.md b/docs/dev/README.md new file mode 100644 index 00000000..c704c142 --- /dev/null +++ b/docs/dev/README.md @@ -0,0 +1,25 @@ +# Developer Documentation + +## Getting started for contributors +- [Contributing guide](CONTRIBUTING.md) +- [Code quality standards](CODE_QUALITY.md) +- [Testing guidelines](TESTING.md) +- [Architecture overview](ARCHITECTURE.md) + +## Build and tooling +- [ESLint and type safety](ESLINT_TYPE_SAFETY.md) +- [Node.js and runtime notes](NODEJS_2025.md) + +## Release and maintenance +- [Release process](RELEASE_PROCESS.md) +- [Manual testing](MANUAL_TESTING.md) + +## Deep dives and plans +- [Plugin development](PLUGIN_DEVELOPMENT.md) +- [Reloaderoo docs](RELOADEROO.md) +- [Reloaderoo primer](RELOADEROO_XCODEBUILDMCP_PRIMER.md) +- [Reloaderoo for XcodeBuildMCP](RELOADEROO_FOR_XCODEBUILDMCP.md) +- [Zod migration guide](ZOD_MIGRATION_GUIDE.md) +- [Test runner env plan](TEST_RUNNER_ENV_IMPLEMENTATION_PLAN.md) +- [Session management plan](session_management_plan.md) +- [Session-aware migration todo](session-aware-migration-todo.md) diff --git a/docs/RELEASE_PROCESS.md b/docs/dev/RELEASE_PROCESS.md similarity index 68% rename from docs/RELEASE_PROCESS.md rename to docs/dev/RELEASE_PROCESS.md index 9751ee83..b75e75ad 100644 --- a/docs/RELEASE_PROCESS.md +++ b/docs/dev/RELEASE_PROCESS.md @@ -1,5 +1,48 @@ # Release Process +## GitHub Release Notes Source of Truth + +GitHub release descriptions are generated from the matching version section in `CHANGELOG.md`. The release process now enforces this in both local and CI flows: + +- `scripts/release.sh` validates release notes generation before tagging and pushing +- `.github/workflows/release.yml` generates the final GitHub release body from `CHANGELOG.md` + +If the changelog section for the target version is missing or empty, release execution fails with a clear error. + +If the latest changelog section is `## [Unreleased]` and no matching version heading exists yet, `scripts/release.sh` automatically renames that heading to `## []` for the release. In `--dry-run`, this rename is performed only in a temporary file and does not modify `CHANGELOG.md`. + +Preview release notes locally: + +```bash +node scripts/generate-github-release-notes.mjs --version 2.0.0 +``` + +## Release Workflow Modes + +The release workflow (`.github/workflows/release.yml`) has two execution modes: + +### Tag push (`push` on `v*`) + +Production release behavior: +- Publishes package to npm. +- Creates GitHub release and uploads npm tarball. +- Builds and verifies portable macOS artifacts (`arm64`, `x64`, `universal`). +- Uploads portable artifacts to GitHub release assets. +- Updates the Homebrew tap repository (`getsentry/homebrew-xcodebuildmcp`) directly when `HOMEBREW_TAP_TOKEN` is configured. +- Attempts MCP Registry publish (best effort based on configured secrets). + +### Manual dispatch (`workflow_dispatch`) + +Validation behavior only (no production deployment): +- Runs formatting/build/tests and packaging checks. +- Runs npm publish in `--dry-run` mode. +- Builds and verifies portable artifacts for release-pipeline validation. +- Does **not** publish to npm. +- Does **not** create GitHub release. +- Does **not** upload portable assets to a release. +- Does **not** update Homebrew tap. +- Does **not** run MCP Registry publish job. + ## Step-by-Step Development Workflow ### 1. Starting New Work @@ -171,43 +214,25 @@ Every PR must include these sections in order: ### CI/CD Pipeline Our GitHub Actions CI pipeline automatically enforces these quality checks: 1. `npm run build` - Compilation check -2. `npm run lint` - ESLint validation -3. `npm run format:check` - Prettier formatting check -4. `npm run typecheck` - **TypeScript error validation** -5. `npm run test` - Test suite execution +2. `npm run docs:check` - Validate CLI command references in consumer docs +3. `npm run lint` - ESLint validation +4. `npm run format:check` - Prettier formatting check +5. `npm run typecheck` - **TypeScript error validation** +6. `npm run test` - Test suite execution **All checks must pass before PR merge is allowed.** ### Optional: Pre-commit Hook Setup -To catch TypeScript errors before committing locally: +To install the repository-managed pre-commit hook: ```bash -# Create pre-commit hook -cat > .git/hooks/pre-commit << 'EOF' -#!/bin/sh -echo "🔍 Running pre-commit checks..." - -# Run TypeScript type checking -echo "📝 Checking TypeScript..." -npm run typecheck -if [ $? -ne 0 ]; then - echo "❌ TypeScript errors found. Please fix before committing." - exit 1 -fi - -# Run linting -echo "🧹 Running linter..." -npm run lint -if [ $? -ne 0 ]; then - echo "❌ Linting errors found. Please fix before committing." - exit 1 -fi - -echo "✅ Pre-commit checks passed!" -EOF - -# Make it executable -chmod +x .git/hooks/pre-commit +npm run hooks:install ``` -This hook will automatically run `typecheck` and `lint` before every commit, preventing TypeScript errors from being committed. \ No newline at end of file +This installs `.githooks/pre-commit` and configures `core.hooksPath` for this repository. + +The shared pre-commit hook runs: +- `npm run format:check` +- `npm run lint` +- `npm run build` +- `npm run docs:check` diff --git a/docs/TESTING.md b/docs/dev/TESTING.md similarity index 90% rename from docs/TESTING.md rename to docs/dev/TESTING.md index ec8756be..c1e20e6e 100644 --- a/docs/TESTING.md +++ b/docs/dev/TESTING.md @@ -18,53 +18,40 @@ This document provides comprehensive testing guidelines for XcodeBuildMCP plugin ## Testing Philosophy -### 🚨 CRITICAL: No Vitest Mocking Allowed - -### ABSOLUTE RULE: ALL VITEST MOCKING IS COMPLETELY BANNED - -### FORBIDDEN PATTERNS (will cause immediate test failure): - -#### Vitest Mocking (COMPLETELY BANNED): -- `vi.mock()` - BANNED -- `vi.fn()` - BANNED -- `vi.mocked()` - BANNED -- `vi.spyOn()` - BANNED -- `.mockResolvedValue()` - BANNED -- `.mockRejectedValue()` - BANNED -- `.mockReturnValue()` - BANNED -- `.mockImplementation()` - BANNED -- `.toHaveBeenCalled()` - BANNED -- `.toHaveBeenCalledWith()` - BANNED -- `MockedFunction` type - BANNED - -#### Manual Mock Implementations (BANNED - use our utilities instead): -- `const mockExecutor = async (...) => { ... }` - Use `createMockExecutor()` instead -- `const mockFsDeps = { readFile: async () => ... }` - Use `createMockFileSystemExecutor()` instead -- `const mockServer = { ... }` - Refactor to use dependency injection pattern -- Any manual async function implementations for mocking behavior - -### ONLY ALLOWED MOCKING: -- `createMockExecutor({ success: true, output: 'result' })` - command execution -- `createMockFileSystemExecutor({ readFile: async () => 'content' })` - file system operations +### 🚨 CRITICAL: External Dependency Mocking Rules + +### ABSOLUTE RULE: External side effects must use dependency injection utilities + +### Use dependency-injection mocks for EXTERNAL dependencies: +- `createMockExecutor()` / `createNoopExecutor()` for command execution (`xcrun`, `xcodebuild`, AXe, etc.) +- `createMockFileSystemExecutor()` / `createNoopFileSystemExecutor()` for file system interactions + +### Internal mocking guidance: +- Vitest mocking (`vi.fn`, `vi.mock`, `vi.spyOn`, `.mockResolvedValue`, etc.) is allowed for internal modules and in-memory collaborators +- Prefer straightforward, readable test doubles over over-engineered mocks + +### Still forbidden: +- Hitting real external systems in unit tests (real `xcodebuild`, `xcrun`, AXe, filesystem writes/reads outside test harness) +- Bypassing dependency injection for external effects ### OUR CORE PRINCIPLE -**Simple Rule**: No mocking other than `createMockExecutor()` and `createMockFileSystemExecutor()` (and their noop variants). +**Simple Rule**: Use dependency-injection mock executors for external boundaries; use Vitest mocking only for internal behavior. **Why This Rule Exists**: -1. **Consistency**: All tests use the same mocking utilities, making them predictable and maintainable -2. **Reliability**: Our utilities are thoroughly tested and handle edge cases properly -3. **Architectural Enforcement**: Prevents bypassing our dependency injection patterns -4. **Simplicity**: One clear rule instead of complex guidelines about what mocking is acceptable +1. **Reliability**: External side effects stay deterministic and hermetic +2. **Clarity**: Internal collaboration assertions remain concise and readable +3. **Architectural Enforcement**: External boundaries are explicit in tool logic signatures +4. **Maintainability**: Tests fail for behavior regressions, not incidental environment differences ### Integration Testing with Dependency Injection -XcodeBuildMCP follows a **pure dependency injection** testing philosophy that eliminates vitest mocking: +XcodeBuildMCP follows a dependency-injection testing philosophy for external boundaries: - ✅ **Test plugin interfaces** (public API contracts) - ✅ **Test integration flows** (plugin → utilities → external tools) -- ✅ **Use dependency injection** with createMockExecutor() -- ❌ **Never mock vitest functions** (vi.mock, vi.fn, etc.) +- ✅ **Use dependency injection** with createMockExecutor()/createMockFileSystemExecutor() for external dependencies +- ✅ **Use Vitest mocking when needed** for internal modules and collaborators ### Benefits @@ -76,7 +63,7 @@ XcodeBuildMCP follows a **pure dependency injection** testing philosophy that el ### Automated Violation Checking -To enforce the no-mocking policy, the project includes a script that automatically checks for banned testing patterns. +To enforce external-boundary testing policy, the project includes a script that checks for architectural test-pattern violations. ```bash # Run the script to check for violations @@ -91,7 +78,7 @@ This script is part of the standard development workflow and should be run befor - Manual mock executors: `const mockExecutor = async (...) => { ... }` - Manual filesystem mocks: `const mockFsDeps = { readFile: async () => ... }` - Manual server mocks: `const mockServer = { ... }` -- Vitest mocking patterns: `vi.mock()`, `vi.fn()`, etc. +- External side-effect patterns that bypass injected executors/filesystem dependencies #### ❌ FALSE POSITIVES (should NOT be flagged): - Test data tracking: `commandCalls.push({ ... })` - This is just collecting test data, not mocking behavior @@ -118,7 +105,7 @@ Test → Plugin Handler → utilities → [DEPENDENCY INJECTION] createMockExecu ### What Gets Mocked - Command execution via `createMockExecutor()` - File system operations via `createMockFileSystemExecutor()` -- Nothing else - all vitest mocking is banned +- Internal modules can use Vitest mocks where appropriate ## Dependency Injection Strategy @@ -359,8 +346,8 @@ describe('simulator-project re-exports', () => { import { vi, describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -// CRITICAL: NO VITEST MOCKING ALLOWED -// Import ONLY what you need - no mock setup +// Use dependency-injection mocks for external boundaries. +// Vitest mocks are acceptable for internal collaborators when needed. import tool from '../tool_name.ts'; import { createMockExecutor } from '../../utils/command.js'; @@ -555,11 +542,11 @@ Black Box Testing means testing ONLY through external interfaces without any kno ### ABSOLUTE TESTING RULES - NO EXCEPTIONS 1. **✅ ONLY ALLOWED: Reloaderoo Inspect Commands** - - `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/index.js` - - `npx reloaderoo@latest inspect list-tools -- node build/index.js` - - `npx reloaderoo@latest inspect read-resource "URI" -- node build/index.js` - - `npx reloaderoo@latest inspect server-info -- node build/index.js` - - `npx reloaderoo@latest inspect ping -- node build/index.js` + - `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect read-resource "URI" -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect server-info -- node build/cli.js mcp` + - `npx reloaderoo@latest inspect ping -- node build/cli.js mcp` 2. **❌ COMPLETELY FORBIDDEN ACTIONS:** - **NEVER** call `mcp__XcodeBuildMCP__tool_name()` functions directly @@ -581,8 +568,8 @@ Black Box Testing means testing ONLY through external interfaces without any kno const result = await doctor(); // ✅ CORRECT - Only through Reloaderoo inspect - npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js - npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js + npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp + npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp ``` ### WHY RELOADEROO INSPECT IS MANDATORY @@ -631,7 +618,7 @@ Some tools rely on in-memory state within the MCP server and will fail when test #### Step 1: Create Complete Tool Inventory ```bash # Generate complete list of all tools -npx reloaderoo@latest inspect list-tools -- node build/index.js > /tmp/all_tools.json +npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp > /tmp/all_tools.json TOTAL_TOOLS=$(jq '.tools | length' /tmp/all_tools.json) echo "TOTAL TOOLS TO TEST: $TOTAL_TOOLS" @@ -653,7 +640,7 @@ jq -r '.tools[].name' /tmp/all_tools.json > /tmp/tool_names.txt For EVERY tool in the list: ```bash # Test each tool individually - NO BATCHING -npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'APPROPRIATE_PARAMS' -- node build/index.js +npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'APPROPRIATE_PARAMS' -- node build/cli.js mcp # Mark tool as completed in TodoWrite IMMEDIATELY after testing # Record result (success/failure/blocked) for each tool @@ -753,7 +740,7 @@ fi - `test_*` tools - Run test suites 6. **UI Automation Tools** (depend on running apps): - - `describe_ui`, `screenshot`, `tap`, etc. + - `snapshot_ui`, `screenshot`, `tap`, etc. ### MANDATORY: Record Key Outputs @@ -777,7 +764,7 @@ Must capture and document these values for dependent tools: ```bash # Generate complete tool list with accurate count -npx reloaderoo@latest inspect list-tools -- node build/index.js 2>/dev/null > /tmp/tools.json +npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp 2>/dev/null > /tmp/tools.json # Get accurate tool count TOOL_COUNT=$(jq '.tools | length' /tmp/tools.json) @@ -792,7 +779,7 @@ echo "Tool names saved to /tmp/tool_names.txt" ```bash # Generate complete resource list -npx reloaderoo@latest inspect list-resources -- node build/index.js 2>/dev/null > /tmp/resources.json +npx reloaderoo@latest inspect list-resources -- node build/cli.js mcp 2>/dev/null > /tmp/resources.json # Get accurate resource count RESOURCE_COUNT=$(jq '.resources | length' /tmp/resources.json) @@ -905,7 +892,7 @@ echo "Tool schema reference created at /tmp/tool_schemas.md" - Mark "completed" only after manual verification 2. **Test Each Tool Individually** - - Execute ONLY via `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/index.js` + - Execute ONLY via `npx reloaderoo@latest inspect call-tool "TOOL_NAME" --params 'JSON' -- node build/cli.js mcp` - Wait for complete response before proceeding to next tool - Read and verify each tool's output manually - Record key outputs (UUIDs, paths, schemes) for dependent tools @@ -929,16 +916,16 @@ echo "Tool schema reference created at /tmp/tool_schemas.md" ```bash # Test server connectivity -npx reloaderoo@latest inspect ping -- node build/index.js +npx reloaderoo@latest inspect ping -- node build/cli.js mcp # Get server information -npx reloaderoo@latest inspect server-info -- node build/index.js +npx reloaderoo@latest inspect server-info -- node build/cli.js mcp # Verify tool count manually -npx reloaderoo@latest inspect list-tools -- node build/index.js 2>/dev/null | jq '.tools | length' +npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp 2>/dev/null | jq '.tools | length' # Verify resource count manually -npx reloaderoo@latest inspect list-resources -- node build/index.js 2>/dev/null | jq '.resources | length' +npx reloaderoo@latest inspect list-resources -- node build/cli.js mcp 2>/dev/null | jq '.resources | length' ``` #### Phase 2: Resource Testing @@ -947,7 +934,7 @@ npx reloaderoo@latest inspect list-resources -- node build/index.js 2>/dev/null # Test each resource systematically while IFS= read -r resource_uri; do echo "Testing resource: $resource_uri" - npx reloaderoo@latest inspect read-resource "$resource_uri" -- node build/index.js 2>/dev/null + npx reloaderoo@latest inspect read-resource "$resource_uri" -- node build/cli.js mcp 2>/dev/null echo "---" done < /tmp/resource_uris.txt ``` @@ -961,23 +948,23 @@ echo "=== FOUNDATION TOOL TESTING & DATA COLLECTION ===" # 1. Test doctor (no dependencies) echo "Testing doctor..." -npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp 2>/dev/null # 2. Collect device data echo "Collecting device UUIDs..." -npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js 2>/dev/null > /tmp/devices_output.json +npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp 2>/dev/null > /tmp/devices_output.json DEVICE_UUIDS=$(jq -r '.content[0].text' /tmp/devices_output.json | grep -E "UDID: [A-F0-9-]+" | sed 's/.*UDID: //' | head -2) echo "Device UUIDs captured: $DEVICE_UUIDS" # 3. Collect simulator data echo "Collecting simulator UUIDs..." -npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js 2>/dev/null > /tmp/sims_output.json +npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/cli.js mcp 2>/dev/null > /tmp/sims_output.json SIMULATOR_UUIDS=$(jq -r '.content[0].text' /tmp/sims_output.json | grep -E "\([A-F0-9-]+\)" | sed 's/.*(\([A-F0-9-]*\)).*/\1/' | head -3) echo "Simulator UUIDs captured: $SIMULATOR_UUIDS" # 4. Collect project data echo "Collecting project paths..." -npx reloaderoo@latest inspect call-tool "discover_projs" --params '{"workspaceRoot": "/Volumes/Developer/XcodeBuildMCP"}' -- node build/index.js 2>/dev/null > /tmp/projects_output.json +npx reloaderoo@latest inspect call-tool "discover_projs" --params '{"workspaceRoot": "/Volumes/Developer/XcodeBuildMCP"}' -- node build/cli.js mcp 2>/dev/null > /tmp/projects_output.json PROJECT_PATHS=$(jq -r '.content[1].text' /tmp/projects_output.json | grep -E "\.xcodeproj$" | sed 's/.*- //' | head -3) WORKSPACE_PATHS=$(jq -r '.content[2].text' /tmp/projects_output.json | grep -E "\.xcworkspace$" | sed 's/.*- //' | head -2) echo "Project paths captured: $PROJECT_PATHS" @@ -999,7 +986,7 @@ echo "=== DISCOVERY TOOL TESTING & METADATA COLLECTION ===" while IFS= read -r project_path; do if [ -n "$project_path" ]; then echo "Getting schemes for: $project_path" - npx reloaderoo@latest inspect call-tool "list_schems_proj" --params "{\"projectPath\": \"$project_path\"}" -- node build/index.js 2>/dev/null > /tmp/schemes_$$.json + npx reloaderoo@latest inspect call-tool "list_schems_proj" --params "{\"projectPath\": \"$project_path\"}" -- node build/cli.js mcp 2>/dev/null > /tmp/schemes_$$.json SCHEMES=$(jq -r '.content[1].text' /tmp/schemes_$$.json 2>/dev/null || echo "NoScheme") echo "$project_path|$SCHEMES" >> /tmp/project_schemes.txt echo "Schemes captured for $project_path: $SCHEMES" @@ -1010,7 +997,7 @@ done < /tmp/project_paths.txt while IFS= read -r workspace_path; do if [ -n "$workspace_path" ]; then echo "Getting schemes for: $workspace_path" - npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/index.js 2>/dev/null > /tmp/ws_schemes_$$.json + npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/cli.js mcp 2>/dev/null > /tmp/ws_schemes_$$.json SCHEMES=$(jq -r '.content[1].text' /tmp/ws_schemes_$$.json 2>/dev/null || echo "NoScheme") echo "$workspace_path|$SCHEMES" >> /tmp/workspace_schemes.txt echo "Schemes captured for $workspace_path: $SCHEMES" @@ -1035,29 +1022,29 @@ done < /tmp/workspace_paths.txt ```bash # STEP 1: Test foundation tools (no parameters required) # Execute each command individually, wait for response, verify manually -npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp # [Wait for response, read output, mark tool complete in task list] -npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp # [Record device UUIDs from response for dependent tools] -npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/cli.js mcp # [Record simulator UUIDs from response for dependent tools] # STEP 2: Test project discovery (use discovered project paths) -npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPath": "/actual/path/from/discover_projs.xcodeproj"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPath": "/actual/path/from/discover_projs.xcodeproj"}' -- node build/cli.js mcp # [Record scheme names from response for build tools] # STEP 3: Test workspace tools (use discovered workspace paths) -npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/cli.js mcp # [Record scheme names from response for build tools] # STEP 4: Test simulator tools (use captured simulator UUIDs from step 1) -npx reloaderoo@latest inspect call-tool "boot_sim" --params '{"simulatorUuid": "ACTUAL_UUID_FROM_LIST_SIMS"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "boot_sim" --params '{"simulatorUuid": "ACTUAL_UUID_FROM_LIST_SIMS"}' -- node build/cli.js mcp # [Verify simulator boots successfully] # STEP 5: Test build tools (requires project + scheme + simulator from previous steps) -npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectPath": "/actual/project.xcodeproj", "scheme": "ActualSchemeName", "simulatorId": "ACTUAL_SIMULATOR_UUID"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectPath": "/actual/project.xcodeproj", "scheme": "ActualSchemeName", "simulatorId": "ACTUAL_SIMULATOR_UUID"}' -- node build/cli.js mcp # [Verify build succeeds and record app bundle path] ``` @@ -1083,7 +1070,7 @@ npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectP ```bash # ❌ IMMEDIATE TERMINATION - Using scripts to test tools for tool in $(cat tool_list.txt); do - npx reloaderoo inspect call-tool "$tool" --params '{}' -- node build/index.js + npx reloaderoo inspect call-tool "$tool" --params '{}' -- node build/cli.js mcp done ``` @@ -1109,19 +1096,19 @@ npx reloaderoo@latest inspect call-tool "build_sim_id_proj" --params '{"projectP ```bash # ✅ CORRECT - Step-by-step manual execution via Reloaderoo # Tool 1: Test doctor -npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "doctor" --params '{}' -- node build/cli.js mcp # [Read response, verify, mark complete in TodoWrite] # Tool 2: Test list_devices -npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_devices" --params '{}' -- node build/cli.js mcp # [Read response, capture UUIDs, mark complete in TodoWrite] # Tool 3: Test list_sims -npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_sims" --params '{}' -- node build/cli.js mcp # [Read response, capture UUIDs, mark complete in TodoWrite] # Tool X: Test stateful tool (expected to fail) -npx reloaderoo@latest inspect call-tool "swift_package_stop" --params '{"pid": 12345}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "swift_package_stop" --params '{"pid": 12345}' -- node build/cli.js mcp # [Tool fails as expected - no in-memory state available] # [Mark as "false negative - stateful tool limitation" in TodoWrite] # [Continue to next tool without investigation] @@ -1146,15 +1133,15 @@ echo "=== Error Testing ===" # Test with invalid JSON parameters echo "Testing invalid parameter types..." -npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": 123}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": 123}' -- node build/cli.js mcp 2>/dev/null # Test with non-existent paths echo "Testing non-existent paths..." -npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": "/nonexistent/path.xcodeproj"}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": "/nonexistent/path.xcodeproj"}' -- node build/cli.js mcp 2>/dev/null # Test with invalid UUIDs echo "Testing invalid UUIDs..." -npx reloaderoo@latest inspect call-tool boot_sim --params '{"simulatorUuid": "invalid-uuid"}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool boot_sim --params '{"simulatorUuid": "invalid-uuid"}' -- node build/cli.js mcp 2>/dev/null ``` ### Step 5: Generate Testing Report @@ -1190,12 +1177,12 @@ echo "Testing session template created: TESTING_SESSION_$(date +%Y-%m-%d).md" ```bash # Essential testing commands -npx reloaderoo@latest inspect ping -- node build/index.js -npx reloaderoo@latest inspect server-info -- node build/index.js -npx reloaderoo@latest inspect list-tools -- node build/index.js | jq '.tools | length' -npx reloaderoo@latest inspect list-resources -- node build/index.js | jq '.resources | length' -npx reloaderoo@latest inspect call-tool TOOL_NAME --params '{}' -- node build/index.js -npx reloaderoo@latest inspect read-resource "xcodebuildmcp://RESOURCE" -- node build/index.js +npx reloaderoo@latest inspect ping -- node build/cli.js mcp +npx reloaderoo@latest inspect server-info -- node build/cli.js mcp +npx reloaderoo@latest inspect list-tools -- node build/cli.js mcp | jq '.tools | length' +npx reloaderoo@latest inspect list-resources -- node build/cli.js mcp | jq '.resources | length' +npx reloaderoo@latest inspect call-tool TOOL_NAME --params '{}' -- node build/cli.js mcp +npx reloaderoo@latest inspect read-resource "xcodebuildmcp://RESOURCE" -- node build/cli.js mcp # Schema extraction jq --arg tool "TOOL_NAME" '.tools[] | select(.name == $tool) | .inputSchema' /tmp/tools.json @@ -1266,8 +1253,8 @@ npm run test:coverage -- src/plugins/simulator-workspace/ ### Validation Scripts ```bash -# Check for vitest mocking violations -node scripts/check-code-patterns.js --pattern=vitest +# Check for architectural pattern violations +node scripts/check-code-patterns.js # Check dependency injection compliance node scripts/audit-dependency-container.js @@ -1278,7 +1265,7 @@ node scripts/audit-dependency-container.js ## Best Practices Summary 1. **Dependency injection**: Always use createMockExecutor() and createMockFileSystemExecutor() -2. **No vitest mocking**: All vi.mock, vi.fn, etc. patterns are banned +2. **External boundaries via DI**: mock command execution/filesystem with injected executors 3. **Three dimensions**: Test input validation, command execution, and output processing 4. **Literal expectations**: Use exact strings in assertions to catch regressions 5. **Performance**: Ensure fast execution through proper mocking @@ -1286,4 +1273,4 @@ node scripts/audit-dependency-container.js 7. **Consistency**: Follow standard patterns across all plugin tests 8. **Test safety**: Default executors prevent accidental real system calls -This testing strategy ensures robust, maintainable tests that provide confidence in plugin functionality while remaining resilient to implementation changes and completely eliminating vitest mocking dependencies. +This testing strategy ensures robust, maintainable tests that provide confidence in plugin functionality while remaining resilient to implementation changes and keeping external boundaries deterministic. diff --git a/docs/dev/TOOL_DISCOVERY_LOGIC.md b/docs/dev/TOOL_DISCOVERY_LOGIC.md new file mode 100644 index 00000000..5008b8f8 --- /dev/null +++ b/docs/dev/TOOL_DISCOVERY_LOGIC.md @@ -0,0 +1,137 @@ +# Tool Discovery & Visibility Logic (MCP server + CLI) + +This document describes how XcodeBuildMCP discovers workflows/tools and decides which ones are visible in: + +- the **MCP server** (`xcodebuildmcp mcp`), and +- the **CLI** (`node build/cli.js` / `xcodebuildmcp ...`). + +It also documents the current and intended **visibility filtering** behavior (post workflow-selection filtering). + +## Terminology + +- **Workflow**: a directory under `src/mcp/tools//` containing an `index.ts` with workflow metadata and tool modules. +- **Tool**: a `PluginMeta` exported from a workflow module with `name`, `schema`, and `handler`. +- **Workflow selection**: picking which workflows are active (coarse-grained inclusion). +- **Visibility filtering**: hiding specific tools even if their workflow is enabled (fine-grained exclusion). +- **Dynamic tools**: tools registered at runtime that do not come from static workflows (e.g. proxied Xcode Tools). + +## Where workflows/tools come from (source of truth) + +Workflows are discovered via generated loaders in `src/core/generated-plugins.ts` (the `WORKFLOW_LOADERS` map). At runtime, `loadWorkflowGroups()` imports each workflow module via these loaders and collects tools from it (`src/core/plugin-registry.ts`). + +Key properties of this design: + +- Workflows are “discoverable” by enumerating `Object.keys(WORKFLOW_LOADERS)`. +- Tools within a workflow are whatever `index.ts` exports (excluding `workflow` itself). +- A single tool name can appear in multiple workflows (re-exports). This matters for workflow management and hiding. + +## MCP server: registration pipeline + +At MCP server startup: + +1) Runtime config is loaded (`bootstrapRuntime` → `initConfigStore`) from: + - config file (project config), + - env (`XCODEBUILDMCP_*`), + - explicit overrides. + +2) Enabled workflows are taken from config (`enabledWorkflows`). + +3) `registerWorkflows(enabledWorkflows)` runs, which calls `applyWorkflowSelection(...)` (`src/utils/tool-registry.ts`). + +4) Workflow selection uses `selectWorkflowsForMcp(...)` (`src/visibility/exposure.ts`) which: + - includes auto-include workflows whose predicates pass (e.g., `session-management` with no predicates is always included), + - includes explicitly requested workflows from config, + - defaults to `defaultEnabled: true` workflows (e.g., `simulator`) when `enabledWorkflows` is empty, + - filters all selected workflows by availability + predicates. + +5) For each selected workflow, each tool is considered for registration, then filtered by `shouldExposeTool(workflowName, toolName)` (`src/utils/tool-registry.ts`). + +6) If visible, the tool is registered via `server.registerTool(...)`. + +### Runtime workflow management (MCP) + +The `manage-workflows` tool updates the enabled workflow list at runtime and re-applies workflow selection (`src/mcp/tools/workflow-discovery/manage_workflows.ts` → `applyWorkflowSelection(...)`). + +Important nuance: + +- Because tools can be re-exported across workflows, disabling one workflow may not remove a tool if another enabled workflow still provides that same tool name. +- Visibility filtering operates on the tool name (and workflow name) at registration time; it is layered after workflow selection. + +## CLI: discovery and help output + +The CLI has two related but distinct concepts: + +1) **Command registration / `--help` tree** (yargs commands) +2) **Tool listing** (`xcodebuildmcp tools`) which is driven by a manifest + +### CLI command registration (yargs) + +CLI mode builds a tool catalog using `buildCliToolCatalog()` which enables **all workflows** returned by `listWorkflowDirectoryNames()` except `session-management` and `workflow-discovery` (`src/cli/cli-tool-catalog.ts`). + +The catalog is built via `buildToolCatalog(...)` (`src/runtime/tool-catalog.ts`), which: + +- loads workflow groups via `loadWorkflowGroups()`, +- resolves selected workflows (as above), +- applies `shouldExposeTool(...)` as a per-tool visibility filter, +- generates CLI names and disambiguates collisions. + +Yargs then registers workflow command groups and per-tool subcommands from the catalog (`src/cli/register-tool-commands.ts`). + +Additionally, the CLI registers workflow command groups from workflow metadata even if there are currently zero visible tools in that workflow (so the workflow still appears in `--help` output). + +### CLI `tools` command (manifest-driven) + +`xcodebuildmcp tools` reads `build/tools-manifest.json` (`src/cli/commands/tools.ts`) which is generated by `npm run generate:tools-manifest` (invoked by `npm run build:tsup`). + +Key implications: + +- The manifest is a **static analysis** snapshot (canonical source for docs/CLI listing). +- Dynamic runtime tools (see below) are **not** represented in the manifest. + +## Dynamic tools: Xcode Tools via `xcrun mcpbridge` + +When the `xcode-ide` workflow is enabled, XcodeBuildMCP can proxy Xcode’s IDE tool service through `xcrun mcpbridge` and register proxied tools at runtime. + +Properties: + +- Proxied tools are registered dynamically and prefixed as `xcode_tools_` (see `src/integrations/xcode-tools-bridge/registry.ts`). +- Proxied tools are not part of any static workflow and are not present in `build/tools-manifest.json`. +- Proxied tool registration can change over time if Xcode’s tool catalog changes (the bridge supports `tools/listChanged`). + +## Visibility filtering (post-selection hiding) + +### Current behavior (implemented) + +`shouldExposeTool(...)` is the single, shared visibility gate for: + +- MCP server tool registration (`src/utils/tool-registry.ts`) +- CLI catalog building (`src/runtime/tool-catalog.ts`) + +Currently it is used to hide Xcode IDE bridge **debug** tools unless debugging is enabled: + +- `xcode_tools_bridge_status` +- `xcode_tools_bridge_sync` +- `xcode_tools_bridge_disconnect` + +These tools are gated behind `debug: true` / `XCODEBUILDMCP_DEBUG=true` (`src/utils/tool-visibility.ts`). + +### Intended behavior (documented policy) + +We also want a broader “Xcode agent mode” visibility filter where specific XcodeBuildMCP tools are hidden when: + +- `runningUnderXcode === true`, AND +- Xcode Tools MCP is enabled/available + +This is documented in `docs/dev/XCODE_IDE_TOOL_CONFLICTS.md`. + +Design principle: + +- Workflow selection stays the **primary** “what areas are enabled” control. +- Visibility filtering is a **secondary** control to reduce confusion/duplication and align behavior with the environment (e.g. “inside Xcode, builds/tests should run inside Xcode”). + +## Layering summary (what wins) + +1) **Workflow selection** decides which workflows are in play. +2) **Visibility filtering** can hide individual tools even within enabled workflows. +3) **Dynamic tools** may appear even if there are no static tools in a workflow (e.g. proxied `xcode_tools_*`). + diff --git a/docs/session-aware-migration-todo.md b/docs/session-aware-migration-todo.md deleted file mode 100644 index 0aee3a22..00000000 --- a/docs/session-aware-migration-todo.md +++ /dev/null @@ -1,64 +0,0 @@ -# Session-Aware Migration TODO - -_Audit date: October 6, 2025_ - -Reference: `docs/session_management_plan.md` - -## Utilities -- [x] `src/mcp/tools/utilities/clean.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`. - -## Project Discovery -- [x] `src/mcp/tools/project-discovery/list_schemes.ts` — session defaults: `projectPath`, `workspacePath`. -- [x] `src/mcp/tools/project-discovery/show_build_settings.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`. - -## Device Workflows -- [x] `src/mcp/tools/device/build_device.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`. -- [x] `src/mcp/tools/device/test_device.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `deviceId`, `configuration`. -- [x] `src/mcp/tools/device/get_device_app_path.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`. -- [x] `src/mcp/tools/device/install_app_device.ts` — session defaults: `deviceId`. -- [x] `src/mcp/tools/device/launch_app_device.ts` — session defaults: `deviceId`. -- [x] `src/mcp/tools/device/stop_app_device.ts` — session defaults: `deviceId`. - -## Device Logging -- [x] `src/mcp/tools/logging/start_device_log_cap.ts` — session defaults: `deviceId`. - -## macOS Workflows -- [x] `src/mcp/tools/macos/build_macos.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`, `arch`. -- [x] `src/mcp/tools/macos/build_run_macos.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`, `arch`. -- [x] `src/mcp/tools/macos/test_macos.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`. -- [x] `src/mcp/tools/macos/get_mac_app_path.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `configuration`, `arch`. - -## Simulator Build/Test/Path -- [x] `src/mcp/tools/simulator/test_sim.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `simulatorId`, `simulatorName`, `configuration`, `useLatestOS`. -- [x] `src/mcp/tools/simulator/get_sim_app_path.ts` — session defaults: `projectPath`, `workspacePath`, `scheme`, `simulatorId`, `simulatorName`, `configuration`, `useLatestOS`, `arch`. - -## Simulator Runtime Actions -- [x] `src/mcp/tools/simulator/boot_sim.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator/install_app_sim.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator/launch_app_sim.ts` — session defaults: `simulatorId`, `simulatorName` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator/launch_app_logs_sim.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator/stop_app_sim.ts` — session defaults: `simulatorId`, `simulatorName` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator/record_sim_video.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). - -## Simulator Management -- [x] `src/mcp/tools/simulator-management/erase_sims.ts` — session defaults: `simulatorId` (covers `simulatorUdid`). -- [x] `src/mcp/tools/simulator-management/set_sim_location.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator-management/reset_sim_location.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator-management/set_sim_appearance.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/simulator-management/sim_statusbar.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). - -## Simulator Logging -- [x] `src/mcp/tools/logging/start_sim_log_cap.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). - -## AXe UI Testing Tools -- [x] `src/mcp/tools/ui-testing/button.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/describe_ui.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/gesture.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/key_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/key_sequence.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/long_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/screenshot.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/swipe.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/tap.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/touch.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). -- [x] `src/mcp/tools/ui-testing/type_text.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`). diff --git a/docs/session_management_plan.md b/docs/session_management_plan.md deleted file mode 100644 index ce67d58f..00000000 --- a/docs/session_management_plan.md +++ /dev/null @@ -1,482 +0,0 @@ -# Stateful Session Defaults for MCP Tools — Design, Middleware, and Plan - -Below is a concise architecture and implementation plan to introduce a session-aware defaults layer that removes repeated tool parameters from public schemas, while keeping all tool logic and tests unchanged. - -## Architecture Overview - -- **Core idea**: keep logic functions and tests untouched; move argument consolidation into a session-aware interop layer and expose minimal public schemas. -- **Data flow**: - - Client calls a tool with zero or few args → session middleware merges session defaults → validates with the internal schema → calls the existing logic function. -- **Components**: - - `SessionStore` (singleton, in-memory): set/get/clear/show defaults. - - Session-aware tool factory: merges defaults, performs preflight requirement checks (allOf/oneOf), then validates with the tool's internal zod schema. - - Public vs internal schema: plugins register a minimal "public" input schema; handlers validate with the unchanged "internal" schema. - -## Core Types - -```typescript -// src/utils/session-store.ts -export type SessionDefaults = { - projectPath?: string; - workspacePath?: string; - scheme?: string; - configuration?: string; - simulatorName?: string; - simulatorId?: string; - deviceId?: string; - useLatestOS?: boolean; - arch?: 'arm64' | 'x86_64'; -}; -``` - -## Session Store (singleton) - -```typescript -// src/utils/session-store.ts -import { log } from './logger.ts'; - -class SessionStore { - private defaults: SessionDefaults = {}; - - setDefaults(partial: Partial): void { - this.defaults = { ...this.defaults, ...partial }; - log('info', '[Session] Defaults set', { keys: Object.keys(partial) }); - } - - clear(keys?: (keyof SessionDefaults)[]): void { - if (!keys || keys.length === 0) { - this.defaults = {}; - log('info', '[Session] All defaults cleared'); - return; - } - for (const k of keys) delete this.defaults[k]; - log('info', '[Session] Defaults cleared', { keys }); - } - - get(key: K): SessionDefaults[K] { - return this.defaults[key]; - } - - getAll(): SessionDefaults { - return { ...this.defaults }; - } -} - -export const sessionStore = new SessionStore(); -``` - -## Session-Aware Tool Factory - -```typescript -// src/utils/typed-tool-factory.ts (add new helper, keep createTypedTool as-is) -import { z } from 'zod'; -import { sessionStore, type SessionDefaults } from './session-store.ts'; -import type { CommandExecutor } from './execution/index.ts'; -import { createErrorResponse } from './responses/index.ts'; -import type { ToolResponse } from '../types/common.ts'; - -export type SessionRequirement = - | { allOf: (keyof SessionDefaults)[]; message?: string } - | { oneOf: (keyof SessionDefaults)[]; message?: string }; - -function missingFromArgsAndSession( - keys: (keyof SessionDefaults)[], - args: Record, -): string[] { - return keys.filter((k) => args[k] == null && sessionStore.get(k) == null); -} - -export function createSessionAwareTool(opts: { - internalSchema: z.ZodType; - logicFunction: (params: TParams, executor: CommandExecutor) => Promise; - getExecutor: () => CommandExecutor; - requirements?: SessionRequirement[]; // preflight, friendlier than raw zod errors -}) { - const { internalSchema, logicFunction, getExecutor, requirements = [] } = opts; - - return async (rawArgs: Record): Promise => { - try { - // Merge: explicit args take precedence over session defaults - const merged: Record = { ...sessionStore.getAll(), ...rawArgs }; - - // Preflight requirement checks (clear message how to fix) - for (const req of requirements) { - if ('allOf' in req) { - const missing = missingFromArgsAndSession(req.allOf, rawArgs); - if (missing.length > 0) { - return createErrorResponse( - 'Missing required session defaults', - `${req.message ?? `Required: ${req.allOf.join(', ')}`}\n` + - `Set with: session-set-defaults { ${missing.map((k) => `"${k}": "..."`).join(', ')} }`, - ); - } - } else if ('oneOf' in req) { - const missing = missingFromArgsAndSession(req.oneOf, rawArgs); - // oneOf satisfied if at least one is present in merged - const satisfied = req.oneOf.some((k) => merged[k] != null); - if (!satisfied) { - return createErrorResponse( - 'Missing required session defaults', - `${req.message ?? `Provide one of: ${req.oneOf.join(', ')}`}\n` + - `Set with: session-set-defaults { "${req.oneOf[0]}": "..." }`, - ); - } - } - } - - // Validate against unchanged internal schema (logic/api untouched) - const validated = internalSchema.parse(merged); - return await logicFunction(validated, getExecutor()); - } catch (error) { - if (error instanceof z.ZodError) { - const msgs = error.errors.map((e) => `${e.path.join('.') || 'root'}: ${e.message}`); - return createErrorResponse( - 'Parameter validation failed', - `Invalid parameters:\n${msgs.join('\n')}\n` + - `Tip: set session defaults via session-set-defaults`, - ); - } - throw error; - } - }; -} -``` - -## Plugin Migration Pattern (Example: build_sim) - -Public schema hides session fields; handler uses session-aware factory with internal schema and requirements; logic function unchanged. - -```typescript -// src/mcp/tools/simulator/build_sim.ts (key parts only) -import { z } from 'zod'; -import { createSessionAwareTool } from '../../../utils/typed-tool-factory.ts'; -import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; - -// Existing internal schema (unchanged)… -const baseOptions = { /* as-is (scheme, simulatorId, simulatorName, configuration, …) */ }; -const baseSchemaObject = z.object({ - projectPath: z.string().optional(), - workspacePath: z.string().optional(), - ...baseOptions, -}); -const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); -const buildSimulatorSchema = baseSchema - .refine(/* as-is: projectPath XOR workspacePath */) - .refine(/* as-is: simulatorId XOR simulatorName */); - -export type BuildSimulatorParams = z.infer; - -// Public schema = internal minus session-managed fields -const sessionManaged = [ - 'projectPath', - 'workspacePath', - 'scheme', - 'configuration', - 'simulatorId', - 'simulatorName', - 'useLatestOS', -] as const; - -const publicSchemaObject = baseSchemaObject.omit( - Object.fromEntries(sessionManaged.map((k) => [k, true])) as Record, -); - -export default { - name: 'build_sim', - description: 'Builds an app for an iOS simulator.', - schema: publicSchemaObject.shape, // what the MCP client sees - handler: createSessionAwareTool({ - internalSchema: buildSimulatorSchema, - logicFunction: build_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, - ], - }), -}; -``` - -This same pattern applies to `build_run_sim`, `test_sim`, device/macos tools, etc. Public schemas become minimal, while internal schemas and logic remain unchanged. - -## New Tool Group: session-management - -### session_set_defaults.ts - -```typescript -// src/mcp/tools/session-management/session_set_defaults.ts -import { z } from 'zod'; -import { sessionStore, type SessionDefaults } from '../../../utils/session-store.ts'; -import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; -import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; - -const schemaObj = z.object({ - projectPath: z.string().optional(), - workspacePath: z.string().optional(), - scheme: z.string().optional(), - configuration: z.string().optional(), - simulatorName: z.string().optional(), - simulatorId: z.string().optional(), - deviceId: z.string().optional(), - useLatestOS: z.boolean().optional(), - arch: z.enum(['arm64', 'x86_64']).optional(), -}); -type Params = z.infer; - -async function logic(params: Params): Promise { - sessionStore.setDefaults(params as Partial); - const current = sessionStore.getAll(); - return { content: [{ type: 'text', text: `Defaults updated:\n${JSON.stringify(current, null, 2)}` }] }; -} - -export default { - name: 'session-set-defaults', - description: 'Set session defaults used by other tools.', - schema: schemaObj.shape, - handler: createTypedTool(schemaObj, logic, getDefaultCommandExecutor), -}; -``` - -### session_clear_defaults.ts - -```typescript -// src/mcp/tools/session-management/session_clear_defaults.ts -import { z } from 'zod'; -import { sessionStore } from '../../../utils/session-store.ts'; -import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; -import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; - -const keys = [ - 'projectPath','workspacePath','scheme','configuration', - 'simulatorName','simulatorId','deviceId','useLatestOS','arch', -] as const; -const schemaObj = z.object({ - keys: z.array(z.enum(keys)).optional(), - all: z.boolean().optional(), -}); - -async function logic(params: z.infer) { - if (params.all || !params.keys) sessionStore.clear(); - else sessionStore.clear(params.keys); - return { content: [{ type: 'text', text: 'Session defaults cleared' }] }; -} - -export default { - name: 'session-clear-defaults', - description: 'Clear selected or all session defaults.', - schema: schemaObj.shape, - handler: createTypedTool(schemaObj, logic, getDefaultCommandExecutor), -}; -``` - -### session_show_defaults.ts - -```typescript -// src/mcp/tools/session-management/session_show_defaults.ts -import { sessionStore } from '../../../utils/session-store.ts'; - -export default { - name: 'session-show-defaults', - description: 'Show current session defaults.', - schema: {}, // no args - handler: async () => { - const current = sessionStore.getAll(); - return { content: [{ type: 'text', text: JSON.stringify(current, null, 2) }] }; - }, -}; -``` - -## Step-by-Step Implementation Plan (Incremental, buildable at each step) - -1. **Add SessionStore** ✅ **DONE** - - New file: `src/utils/session-store.ts`. - - No existing code changes; run: `npm run build`, `lint`, `test`. - - Commit checkpoint (after review): see Commit & Review Protocol below. - -2. **Add session-management tools** ✅ **DONE** - - New folder: `src/mcp/tools/session-management` with the three tools above. - - Register via existing plugin discovery (same pattern as others). - - Build and test. - - Commit checkpoint (after review). - -3. **Add session-aware tool factory** ✅ **DONE** - - Add `createSessionAwareTool` to `src/utils/typed-tool-factory.ts` (keep `createTypedTool` intact). - - Unit tests for requirement preflight and merge precedence. - - Commit checkpoint (after review). - -4. **Migrate 2-3 representative tools** - - Example: `simulator/build_sim`, `macos/build_macos`, `device/build_device`. - - Create `publicSchemaObject` (omit session fields), switch handler to `createSessionAwareTool` with requirements. - - Keep internal schema and logic unchanged. Build and test. - - Commit checkpoint (after review). - -5. **Migrate remaining tools in small batches** - - Apply the same pattern across simulator/device/macos/test utilities. - - After each batch: `npm run typecheck`, `lint`, `test`. - - Commit checkpoint (after review). - -6. **Final polish** - - Add tests for session tools and session-aware preflight error messages. - - Ensure public schemas no longer expose session parameters globally. - - Commit checkpoint (after review). - -## Standard Testing & DI Checklist (Mandatory) - -- Handlers must use dependency injection; tests must never call real executors. -- For validation-only tests, calling the handler is acceptable because Zod validation occurs before executor acquisition. -- For logic tests that would otherwise trigger `getDefaultCommandExecutor`, export the logic function and test it directly (no executor needed if logic doesn’t use one): - -```ts -// Example: src/mcp/tools/session-management/session_clear_defaults.ts -export async function sessionClearDefaultsLogic(params: Params): Promise { /* ... */ } -export default { - name: 'session-clear-defaults', - handler: createTypedTool(schemaObj, sessionClearDefaultsLogic, getDefaultCommandExecutor), -}; - -// Test: import logic and call directly to avoid real executor -import plugin, { sessionClearDefaultsLogic } from '../session_clear_defaults.ts'; -``` - -- Add tests for the new group and tools: - - Group metadata test: `src/mcp/tools/session-management/__tests__/index.test.ts` - - Tool tests: `session_set_defaults.test.ts`, `session_clear_defaults.test.ts`, `session_show_defaults.test.ts` - - Utils tests: `src/utils/__tests__/session-store.test.ts` - - Factory tests: `src/utils/__tests__/session-aware-tool-factory.test.ts` covering: - - Preflight requirements (allOf/oneOf) - - Merge precedence (explicit args override session defaults) - - Zod error reporting with helpful tips - -- Always run locally before requesting review: - - `npm run typecheck` - - `npm run lint` - - `npm run format:check` - - `npm run build` - - `npm run test` - - Perform a quick manual CLI check (mcpli or reloaderoo) per the Manual Testing section - -### Minimal Changes Policy for Tests (Enforced) - -- Only make material, essential edits to tests required by the code change (e.g., new preflight error messages or added/removed fields). -- Do not change sample input values or defaults in tests (e.g., flipping a boolean like `preferXcodebuild`) unless strictly necessary to validate behavior. -- Preserve the original intent and coverage of logic-function tests; keep handler vs logic boundaries intact. -- When session-awareness is added, prefer setting/clearing session defaults around tests rather than altering existing assertions or sample inputs. - -### Tool Description Policy (Enforced) - -- Keep tool descriptions concise (maximum one short sentence). -- Do not mention session defaults, setup steps, examples, or parameter relationships in descriptions. -- Use clear, imperative phrasing (e.g., "Builds an app for an iOS simulator."). -- Apply consistently across all migrated tools; update any tests that assert `description` to match the concise string only. - -## Commit & Review Protocol (Enforced) - -At the end of each numbered step above: - -1. Ensure all checks pass: `typecheck`, `lint`, `format:check`, `build`, `test`; then perform a quick manual CLI test (mcpli or reloaderoo) per the Manual Testing section. - - Verify tool descriptions comply with the Tool Description Policy (concise, no session-defaults mention). -2. Stage only the files for that step. -3. Prepare a concise commit message focused on the “why”. -4. Request manual review and approval before committing. Do not push. - -Example messages per step: - -- Step 1 (SessionStore) - - `chore(utils): add in-memory SessionStore for session defaults` - - Body: “Introduces singleton SessionStore with set/get/clear/show for session defaults; no behavior changes.” - -- Step 2 (session-management tools) - - `feat(session-management): add set/clear/show session defaults tools and workflow metadata` - - Body: “Adds tools to manage session defaults and exposes workflow metadata; minimal schemas via typed factory.” - -- Step 3 (middleware) - - `feat(utils): add createSessionAwareTool with preflight requirements and args>session merge` - - Body: “Session-aware interop layer performing requirements checks and Zod validation against internal schema.” - -- Step 6 (tests/final polish) - - `test(session-management): add tool, store, and middleware tests; export logic for DI` - - Body: “Covers group metadata, tools, SessionStore, and factory (requirements/merge/errors). No production behavior changes.” - -Approval flow: -- After preparing messages and confirming checks, request maintainer approval. -- On approval: commit locally (no push). -- On rejection: revise and re-run checks. - -Note on commit hooks and selective commits: -- The pre-commit hook runs format/lint/build and can auto-add or modify files, causing additional files to be included in the commit. If you must commit a minimal subset, skip hooks with: `git commit --no-verify` (use sparingly and run `npm run typecheck && npm run lint && npm run test` manually first). - -## Safety, Buildability, Testability - -- Logic functions and their types remain unchanged; existing unit tests that import logic directly continue to pass. -- Public schemas shrink; MCP clients see smaller input schemas without session fields. -- Handlers validate with internal schemas after session-defaults merge, preserving runtime guarantees. -- Preflight requirement checks return clear guidance, e.g., "Provide one of: projectPath or workspacePath" + "Set with: session-set-defaults { "projectPath": "..." }". - -## Developer Usage - -- **Set defaults once**: - - `session-set-defaults { "workspacePath": "...", "scheme": "App", "simulatorName": "iPhone 16" }` -- **Run tools without args**: - - `build_sim {}` -- **Inspect/reset**: - - `session-show-defaults {}` - - `session-clear-defaults { "all": true }` - -## Manual Testing with mcpli (CLI) - -The following commands exercise the session workflow end‑to‑end using the built server. - -1) Build the server (required after code changes): - -```bash -npm run build -``` - -2) Discover a scheme (optional helper): - -```bash -mcpli --raw list-schemes --projectPath "/Volumes/Developer/XcodeBuildMCP/example_projects/iOS/MCPTest.xcodeproj" -- node build/index.js -``` - -3) Set the session defaults (project/workspace, scheme, and simulator): - -```bash -mcpli --raw session-set-defaults \ - --projectPath "/Volumes/Developer/XcodeBuildMCP/example_projects/iOS/MCPTest.xcodeproj" \ - --scheme MCPTest \ - --simulatorName "iPhone 16" \ - -- node build/index.js -``` - -4) Verify defaults are stored: - -```bash -mcpli --raw session-show-defaults -- node build/index.js -``` - -5) Run a session‑aware tool with zero or minimal args (defaults are merged automatically): - -```bash -# Optionally provide a scratch derived data path and a short timeout -mcpli --tool-timeout=60 --raw build-sim --derivedDataPath "/tmp/XBMCP_DD" -- node build/index.js -``` - -Troubleshooting: - -- If you see validation errors like “Missing required session defaults …”, (re)run step 3 with the missing keys. -- If you see connect ECONNREFUSED or the daemon appears flaky: - - Check logs: `mcpli daemon log --since=10m -- node build/index.js` - - Restart daemon: `mcpli daemon restart -- node build/index.js` - - Clean daemon state: `mcpli daemon clean -- node build/index.js` then `mcpli daemon start -- node build/index.js` - - After code changes, always: `npm run build` then `mcpli daemon restart -- node build/index.js` - -Notes: - -- Public schemas for session‑aware tools intentionally omit session fields (e.g., `scheme`, `projectPath`, `simulatorName`). Provide them once via `session-set-defaults` and then call the tool with zero/minimal flags. -- Use `--tool-timeout=` to cap long‑running builds during manual testing. -- mcpli CLI normalizes tool names: tools exported with underscores (e.g., `build_sim`) can be invoked with hyphens (e.g., `build-sim`). Copy/paste samples using hyphens are valid because mcpli converts underscores to dashes. - -## Next Steps - -Would you like me to proceed with Phase 1–3 implementation (store + session tools + middleware), then migrate a first tool (build_sim) and run the test suite? diff --git a/eslint.config.js b/eslint.config.js index 26677ec3..0785a57a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,7 +6,14 @@ export default [ eslint.configs.recommended, ...tseslint.configs.recommended, { - ignores: ['node_modules/**', 'build/**', 'dist/**', 'coverage/**', 'src/core/generated-plugins.ts', 'src/core/generated-resources.ts'], + ignores: [ + 'node_modules/**', + 'build/**', + 'dist/**', + 'coverage/**', + 'src/core/generated-plugins.ts', + 'src/core/generated-resources.ts', + ], }, { // TypeScript files in src/ directory (covered by tsconfig.json) @@ -21,55 +28,78 @@ export default [ }, plugins: { '@typescript-eslint': tseslint.plugin, - 'prettier': prettierPlugin, + prettier: prettierPlugin, }, rules: { 'prettier/prettier': 'error', '@typescript-eslint/explicit-function-return-type': 'warn', '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-unused-vars': ['error', { - argsIgnorePattern: 'never', - varsIgnorePattern: 'never' - }], + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: 'never', + varsIgnorePattern: 'never', + }, + ], 'no-console': ['warn', { allow: ['error'] }], - + // Prevent dangerous type casting anti-patterns (errors) - '@typescript-eslint/consistent-type-assertions': ['error', { - assertionStyle: 'as', - objectLiteralTypeAssertions: 'never' - }], + '@typescript-eslint/consistent-type-assertions': [ + 'error', + { + assertionStyle: 'as', + objectLiteralTypeAssertions: 'never', + }, + ], '@typescript-eslint/no-unsafe-argument': 'error', '@typescript-eslint/no-unsafe-assignment': 'error', '@typescript-eslint/no-unsafe-call': 'error', '@typescript-eslint/no-unsafe-member-access': 'error', '@typescript-eslint/no-unsafe-return': 'error', - + // Prevent specific anti-patterns we found - '@typescript-eslint/ban-ts-comment': ['error', { - 'ts-expect-error': 'allow-with-description', - 'ts-ignore': true, - 'ts-nocheck': true, - 'ts-check': false, - }], - + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-expect-error': 'allow-with-description', + 'ts-ignore': true, + 'ts-nocheck': true, + 'ts-check': false, + }, + ], + // Encourage best practices (warnings - can be gradually fixed) '@typescript-eslint/prefer-as-const': 'warn', '@typescript-eslint/prefer-nullish-coalescing': 'warn', '@typescript-eslint/prefer-optional-chain': 'warn', - + // Prevent barrel imports to maintain architectural improvements - 'no-restricted-imports': ['error', { - patterns: [ - { - group: ['**/utils/index.js', '../utils/index.js', '../../utils/index.js', '../../../utils/index.js', '**/utils/index.ts', '../utils/index.ts', '../../utils/index.ts', '../../../utils/index.ts'], - message: 'Barrel imports from utils/index are prohibited. Use focused facade imports instead (e.g., utils/logging/index.ts, utils/execution/index.ts).' - }, - { - group: ['./**/*.js', '../**/*.js'], - message: 'Import TypeScript files with .ts extension, not .js. This ensures compatibility with native TypeScript runtimes like Bun and Deno. Change .js to .ts in your import path.' - } - ] - }], + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: [ + '**/utils/index.js', + '../utils/index.js', + '../../utils/index.js', + '../../../utils/index.js', + '**/utils/index.ts', + '../utils/index.ts', + '../../utils/index.ts', + '../../../utils/index.ts', + ], + message: + 'Barrel imports from utils/index are prohibited. Use focused facade imports instead (e.g., utils/logging/index.ts, utils/execution/index.ts).', + }, + { + group: ['./**/*.js', '../**/*.js'], + message: + 'Import TypeScript files with .ts extension, not .js. This ensures compatibility with native TypeScript runtimes like Bun and Deno. Change .js to .ts in your import path.', + }, + ], + }, + ], }, }, { @@ -84,19 +114,22 @@ export default [ }, plugins: { '@typescript-eslint': tseslint.plugin, - 'prettier': prettierPlugin, + prettier: prettierPlugin, }, rules: { 'prettier/prettier': 'error', // Relaxed TypeScript rules for scripts since they're not in the main project '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/no-explicit-any': 'warn', - '@typescript-eslint/no-unused-vars': ['warn', { - argsIgnorePattern: 'never', - varsIgnorePattern: 'never' - }], + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: 'never', + varsIgnorePattern: 'never', + }, + ], 'no-console': 'off', // Scripts are allowed to use console - + // Disable project-dependent rules for scripts '@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', @@ -120,7 +153,7 @@ export default [ '@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/explicit-function-return-type': 'off', 'prefer-const': 'off', - + // Relax unsafe rules for tests - tests often need more flexibility '@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', diff --git a/example_projects/.xcodebuildmcp/config.yaml b/example_projects/.xcodebuildmcp/config.yaml new file mode 100644 index 00000000..57308faa --- /dev/null +++ b/example_projects/.xcodebuildmcp/config.yaml @@ -0,0 +1,21 @@ +schemaVersion: 1 +sessionDefaultsProfiles: + calculator: + workspacePath: ./iOS_Calculator/CalculatorApp.xcworkspace + scheme: CalculatorApp + simulatorName: iPhone 17 Pro + simulatorId: B38FE93D-578B-454B-BE9A-C6FA0CE5F096 + simulatorPlatform: iOS Simulator + ios-test: + projectPath: ./iOS/MCPTest.xcodeproj + scheme: MCPTest + simulatorName: iPhone 17 Pro + simulatorId: B38FE93D-578B-454B-BE9A-C6FA0CE5F096 + simulatorPlatform: iOS Simulator + macos-test: + projectPath: ./macOS/MCPTest.xcodeproj + scheme: MCPTest + spm: + projectPath: ./spm + scheme: spm +activeSessionDefaultsProfile: spm diff --git a/example_projects/iOS/.xcodebuildmcp/config.yaml b/example_projects/iOS/.xcodebuildmcp/config.yaml new file mode 100644 index 00000000..568d5e4d --- /dev/null +++ b/example_projects/iOS/.xcodebuildmcp/config.yaml @@ -0,0 +1,9 @@ +schemaVersion: 1 +enabledWorkflows: ['simulator', 'ui-automation', 'debugging', 'logging'] +sessionDefaults: + projectPath: ./MCPTest.xcodeproj + scheme: MCPTest + simulatorId: B38FE93D-578B-454B-BE9A-C6FA0CE5F096 + useLatestOS: true + platform: iOS Simulator + bundleId: io.sentry.MCPTest diff --git a/example_projects/iOS/MCPTest.xcodeproj/project.pbxproj b/example_projects/iOS/MCPTest.xcodeproj/project.pbxproj index a2827747..dde4604c 100644 --- a/example_projects/iOS/MCPTest.xcodeproj/project.pbxproj +++ b/example_projects/iOS/MCPTest.xcodeproj/project.pbxproj @@ -252,12 +252,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -309,11 +307,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; @@ -335,16 +331,29 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_WKApplication = YES; + "INFOPLIST_KEY_WKWatchOnly[sdk=watchos*]" = YES; + "INFOPLIST_KEY_WKWatchOnly[sdk=watchsimulator*]" = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", + "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.MCPTest; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MCPTest; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; + TVOS_DEPLOYMENT_TARGET = 18.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; + XROS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; @@ -364,16 +373,29 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_WKApplication = YES; + "INFOPLIST_KEY_WKWatchOnly[sdk=watchos*]" = YES; + "INFOPLIST_KEY_WKWatchOnly[sdk=watchsimulator*]" = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", + "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.MCPTest; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MCPTest; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; + TVOS_DEPLOYMENT_TARGET = 18.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; + XROS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; @@ -383,17 +405,25 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = BR6WD3M6ZD; + ENABLE_APP_SANDBOX = YES; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 26.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.test.MCPTestUITests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.test.MCPTestUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; STRING_CATALOG_GENERATE_SYMBOLS = NO; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; TEST_TARGET_NAME = MCPTest; }; name = Debug; @@ -407,14 +437,18 @@ GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 26.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.test.MCPTestUITests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.test.MCPTestUITests; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; TEST_TARGET_NAME = MCPTest; }; name = Release; diff --git a/example_projects/iOS/MCPTest/ContentView.swift b/example_projects/iOS/MCPTest/ContentView.swift index 44ae826d..b083d334 100644 --- a/example_projects/iOS/MCPTest/ContentView.swift +++ b/example_projects/iOS/MCPTest/ContentView.swift @@ -17,7 +17,9 @@ struct ContentView: View { .imageScale(.large) .foregroundStyle(.tint) TextField("Enter text", text: $text) + #if !os(watchOS) && !os(tvOS) .textFieldStyle(RoundedBorderTextFieldStyle()) + #endif .padding(.horizontal) Text(text) @@ -40,7 +42,7 @@ struct ContentView: View { // OS Log Extension extension Logger { static let myApp = Logger( - subsystem: "com.cameroncooke.MCPTest", + subsystem: "io.sentry.MCPTest", category: "default" ) } diff --git a/example_projects/iOS/MCPTestUITests/MCPTestUITests.swift b/example_projects/iOS/MCPTestUITests/MCPTestUITests.swift index b1c30c97..5461ac73 100644 --- a/example_projects/iOS/MCPTestUITests/MCPTestUITests.swift +++ b/example_projects/iOS/MCPTestUITests/MCPTestUITests.swift @@ -1,7 +1,7 @@ import XCTest /// Reproduction tests for TEST_RUNNER_ environment variable passthrough. -/// GitHub Issue: https://github.com/cameroncooke/XcodeBuildMCP/issues/101 +/// GitHub Issue: https://github.com/getsentry/XcodeBuildMCP/issues/101 /// /// Expected behavior: /// - When invoking xcodebuild test with TEST_RUNNER_USE_DEV_MODE=YES, diff --git a/example_projects/iOS_Calculator/.xcodebuildmcp/config.yaml b/example_projects/iOS_Calculator/.xcodebuildmcp/config.yaml new file mode 100644 index 00000000..237b38ba --- /dev/null +++ b/example_projects/iOS_Calculator/.xcodebuildmcp/config.yaml @@ -0,0 +1,19 @@ +schemaVersion: 1 +enabledWorkflows: + - simulator + - ui-automation + - debugging + - xcode-ide +sessionDefaults: + workspacePath: ./CalculatorApp.xcworkspace + scheme: CalculatorApp + configuration: Debug + simulatorName: iPhone 17 Pro + simulatorId: B38FE93D-578B-454B-BE9A-C6FA0CE5F096 + simulatorPlatform: iOS Simulator + useLatestOS: true + arch: arm64 + suppressWarnings: false + derivedDataPath: ./iOS_Calculator/.derivedData + preferXcodebuild: true + bundleId: io.sentry.calculatorapp diff --git a/example_projects/iOS_Calculator/CalculatorApp.xcodeproj/project.pbxproj b/example_projects/iOS_Calculator/CalculatorApp.xcodeproj/project.pbxproj index 029a697a..097479cb 100644 --- a/example_projects/iOS_Calculator/CalculatorApp.xcodeproj/project.pbxproj +++ b/example_projects/iOS_Calculator/CalculatorApp.xcodeproj/project.pbxproj @@ -414,7 +414,7 @@ DEVELOPMENT_TEAM = BR6WD3M6ZD; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mycompany.CalculatorAppTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.CalculatorAppTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = NO; @@ -434,7 +434,7 @@ DEVELOPMENT_TEAM = BR6WD3M6ZD; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mycompany.CalculatorAppTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.CalculatorAppTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = NO; diff --git a/example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift b/example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift index 38c4929d..91b505e1 100644 --- a/example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift +++ b/example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift @@ -94,6 +94,12 @@ public final class CalculatorService { guard let op = operation ?? lastOperation else { return } let operand = (operation != nil) ? currentNumber : lastOperand + #if DEBUG + if op == .add && previousNumber == 21 && operand == 21 { + fatalError("Intentional crash for debugger smoke test") + } + #endif + let result = op.calculate(previousNumber, operand) // Error handling diff --git a/example_projects/iOS_Calculator/Config/Shared.xcconfig b/example_projects/iOS_Calculator/Config/Shared.xcconfig index 2f68ec6a..f0435598 100644 --- a/example_projects/iOS_Calculator/Config/Shared.xcconfig +++ b/example_projects/iOS_Calculator/Config/Shared.xcconfig @@ -8,7 +8,7 @@ // ========================================== PRODUCT_NAME = CalculatorApp PRODUCT_DISPLAY_NAME = Calculator -PRODUCT_BUNDLE_IDENTIFIER = com.example.calculatorapp +PRODUCT_BUNDLE_IDENTIFIER = io.sentry.calculatorapp MARKETING_VERSION = 1.0 CURRENT_PROJECT_VERSION = 1 diff --git a/example_projects/iOS_Calculator/Config/Tests.xcconfig b/example_projects/iOS_Calculator/Config/Tests.xcconfig index 974d79ec..572bd47e 100644 --- a/example_projects/iOS_Calculator/Config/Tests.xcconfig +++ b/example_projects/iOS_Calculator/Config/Tests.xcconfig @@ -7,6 +7,6 @@ // ========================================== // Test Target Settings (Customizable by scaffold tool) // ========================================== -PRODUCT_BUNDLE_IDENTIFIER = com.example.calculatorapp +PRODUCT_BUNDLE_IDENTIFIER = io.sentry.calculatorapp TEST_TARGET_NAME = CalculatorApp DEFINES_MODULE = NO diff --git a/example_projects/macOS/MCPTest.xcodeproj/project.pbxproj b/example_projects/macOS/MCPTest.xcodeproj/project.pbxproj index 48863fab..2efd7d0b 100644 --- a/example_projects/macOS/MCPTest.xcodeproj/project.pbxproj +++ b/example_projects/macOS/MCPTest.xcodeproj/project.pbxproj @@ -338,7 +338,7 @@ "@executable_path/../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.MCPTest; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MCPTest; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -365,7 +365,7 @@ "@executable_path/../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.MCPTest; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MCPTest; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -382,7 +382,7 @@ GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 26.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.test.MCPTestTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.test.MCPTestTests; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -403,7 +403,7 @@ GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 26.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cameroncooke.test.MCPTestTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.test.MCPTestTests; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; diff --git a/manifests/tools/boot_sim.yaml b/manifests/tools/boot_sim.yaml new file mode 100644 index 00000000..22c2913f --- /dev/null +++ b/manifests/tools/boot_sim.yaml @@ -0,0 +1,25 @@ +id: boot_sim +module: mcp/tools/simulator/boot_sim +names: + mcp: boot_sim + cli: boot +description: Boot iOS simulator. +annotations: + title: Boot Simulator + destructiveHint: true +nextSteps: + - label: Open the Simulator app (makes it visible) + toolId: open_sim + priority: 1 + - label: Install an app + toolId: install_app_sim + params: + simulatorId: SIMULATOR_UUID + appPath: PATH_TO_YOUR_APP + priority: 2 + - label: Launch an app + toolId: launch_app_sim + params: + simulatorId: SIMULATOR_UUID + bundleId: YOUR_APP_BUNDLE_ID + priority: 3 diff --git a/manifests/tools/build_device.yaml b/manifests/tools/build_device.yaml new file mode 100644 index 00000000..4a472acf --- /dev/null +++ b/manifests/tools/build_device.yaml @@ -0,0 +1,11 @@ +id: build_device +module: mcp/tools/device/build_device +names: + mcp: build_device + cli: build +description: Build for device. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Build Device + destructiveHint: true diff --git a/manifests/tools/build_macos.yaml b/manifests/tools/build_macos.yaml new file mode 100644 index 00000000..b97f2aa2 --- /dev/null +++ b/manifests/tools/build_macos.yaml @@ -0,0 +1,11 @@ +id: build_macos +module: mcp/tools/macos/build_macos +names: + mcp: build_macos + cli: build +description: Build macOS app. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Build macOS + destructiveHint: true diff --git a/manifests/tools/build_run_macos.yaml b/manifests/tools/build_run_macos.yaml new file mode 100644 index 00000000..b39101ca --- /dev/null +++ b/manifests/tools/build_run_macos.yaml @@ -0,0 +1,11 @@ +id: build_run_macos +module: mcp/tools/macos/build_run_macos +names: + mcp: build_run_macos + cli: build-and-run +description: Build and run macOS app. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Build Run macOS + destructiveHint: true diff --git a/manifests/tools/build_run_sim.yaml b/manifests/tools/build_run_sim.yaml new file mode 100644 index 00000000..153b2059 --- /dev/null +++ b/manifests/tools/build_run_sim.yaml @@ -0,0 +1,21 @@ +id: build_run_sim +module: mcp/tools/simulator/build_run_sim +names: + mcp: build_run_sim + cli: build-and-run +description: Build and run iOS sim (preferred for run/launch intent). +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Build Run Simulator + destructiveHint: true +nextSteps: + - label: Capture structured logs (app continues running) + toolId: start_sim_log_cap + priority: 1 + - label: Capture console + structured logs (app restarts) + toolId: start_sim_log_cap + priority: 2 + - label: Launch app with logs in one step + toolId: launch_app_logs_sim + priority: 3 diff --git a/manifests/tools/build_sim.yaml b/manifests/tools/build_sim.yaml new file mode 100644 index 00000000..97f885d5 --- /dev/null +++ b/manifests/tools/build_sim.yaml @@ -0,0 +1,11 @@ +id: build_sim +module: mcp/tools/simulator/build_sim +names: + mcp: build_sim + cli: build +description: Build for iOS sim (compile-only, no launch). +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Build Simulator + destructiveHint: true diff --git a/manifests/tools/button.yaml b/manifests/tools/button.yaml new file mode 100644 index 00000000..e99ddb2e --- /dev/null +++ b/manifests/tools/button.yaml @@ -0,0 +1,9 @@ +id: button +module: mcp/tools/ui-automation/button +names: + mcp: button + cli: button +description: Press simulator hardware button. +annotations: + title: Hardware Button + destructiveHint: true diff --git a/manifests/tools/clean.yaml b/manifests/tools/clean.yaml new file mode 100644 index 00000000..010eeb9a --- /dev/null +++ b/manifests/tools/clean.yaml @@ -0,0 +1,11 @@ +id: clean +module: mcp/tools/utilities/clean +names: + mcp: clean + cli: clean +description: Clean build products. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Clean + destructiveHint: true diff --git a/manifests/tools/debug_attach_sim.yaml b/manifests/tools/debug_attach_sim.yaml new file mode 100644 index 00000000..bfb3bfe0 --- /dev/null +++ b/manifests/tools/debug_attach_sim.yaml @@ -0,0 +1,18 @@ +id: debug_attach_sim +module: mcp/tools/debugging/debug_attach_sim +names: + mcp: debug_attach_sim + cli: attach +description: Attach LLDB to sim app. +routing: + stateful: true +nextSteps: + - label: Add a breakpoint + toolId: debug_breakpoint_add + priority: 1 + - label: Continue execution + toolId: debug_continue + priority: 2 + - label: Show call stack + toolId: debug_stack + priority: 3 diff --git a/manifests/tools/debug_breakpoint_add.yaml b/manifests/tools/debug_breakpoint_add.yaml new file mode 100644 index 00000000..c0cf9597 --- /dev/null +++ b/manifests/tools/debug_breakpoint_add.yaml @@ -0,0 +1,8 @@ +id: debug_breakpoint_add +module: mcp/tools/debugging/debug_breakpoint_add +names: + mcp: debug_breakpoint_add + cli: add-breakpoint +description: Add breakpoint. +routing: + stateful: true diff --git a/manifests/tools/debug_breakpoint_remove.yaml b/manifests/tools/debug_breakpoint_remove.yaml new file mode 100644 index 00000000..d7a2775e --- /dev/null +++ b/manifests/tools/debug_breakpoint_remove.yaml @@ -0,0 +1,8 @@ +id: debug_breakpoint_remove +module: mcp/tools/debugging/debug_breakpoint_remove +names: + mcp: debug_breakpoint_remove + cli: remove-breakpoint +description: Remove breakpoint. +routing: + stateful: true diff --git a/manifests/tools/debug_continue.yaml b/manifests/tools/debug_continue.yaml new file mode 100644 index 00000000..6683cb7b --- /dev/null +++ b/manifests/tools/debug_continue.yaml @@ -0,0 +1,8 @@ +id: debug_continue +module: mcp/tools/debugging/debug_continue +names: + mcp: debug_continue + cli: continue +description: Continue debug session. +routing: + stateful: true diff --git a/manifests/tools/debug_detach.yaml b/manifests/tools/debug_detach.yaml new file mode 100644 index 00000000..44466a99 --- /dev/null +++ b/manifests/tools/debug_detach.yaml @@ -0,0 +1,8 @@ +id: debug_detach +module: mcp/tools/debugging/debug_detach +names: + mcp: debug_detach + cli: detach +description: Detach debugger. +routing: + stateful: true diff --git a/manifests/tools/debug_lldb_command.yaml b/manifests/tools/debug_lldb_command.yaml new file mode 100644 index 00000000..0642b0cb --- /dev/null +++ b/manifests/tools/debug_lldb_command.yaml @@ -0,0 +1,8 @@ +id: debug_lldb_command +module: mcp/tools/debugging/debug_lldb_command +names: + mcp: debug_lldb_command + cli: lldb-command +description: Run LLDB command. +routing: + stateful: true diff --git a/manifests/tools/debug_stack.yaml b/manifests/tools/debug_stack.yaml new file mode 100644 index 00000000..8b002069 --- /dev/null +++ b/manifests/tools/debug_stack.yaml @@ -0,0 +1,8 @@ +id: debug_stack +module: mcp/tools/debugging/debug_stack +names: + mcp: debug_stack + cli: stack +description: Get backtrace. +routing: + stateful: true diff --git a/manifests/tools/debug_variables.yaml b/manifests/tools/debug_variables.yaml new file mode 100644 index 00000000..287075f6 --- /dev/null +++ b/manifests/tools/debug_variables.yaml @@ -0,0 +1,8 @@ +id: debug_variables +module: mcp/tools/debugging/debug_variables +names: + mcp: debug_variables + cli: variables +description: Get frame variables. +routing: + stateful: true diff --git a/manifests/tools/discover_projs.yaml b/manifests/tools/discover_projs.yaml new file mode 100644 index 00000000..4c2cf4c1 --- /dev/null +++ b/manifests/tools/discover_projs.yaml @@ -0,0 +1,9 @@ +id: discover_projs +module: mcp/tools/project-discovery/discover_projs +names: + mcp: discover_projs + cli: discover-projects +description: Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. +annotations: + title: Discover Projects + readOnlyHint: true diff --git a/manifests/tools/doctor.yaml b/manifests/tools/doctor.yaml new file mode 100644 index 00000000..d7e08ed1 --- /dev/null +++ b/manifests/tools/doctor.yaml @@ -0,0 +1,9 @@ +id: doctor +module: mcp/tools/doctor/doctor +names: + mcp: doctor + cli: doctor +description: MCP environment info. +annotations: + title: Doctor + readOnlyHint: true diff --git a/manifests/tools/erase_sims.yaml b/manifests/tools/erase_sims.yaml new file mode 100644 index 00000000..4ecd376d --- /dev/null +++ b/manifests/tools/erase_sims.yaml @@ -0,0 +1,9 @@ +id: erase_sims +module: mcp/tools/simulator-management/erase_sims +names: + mcp: erase_sims + cli: erase +description: Erase simulator. +annotations: + title: Erase Simulators + destructiveHint: true diff --git a/manifests/tools/gesture.yaml b/manifests/tools/gesture.yaml new file mode 100644 index 00000000..abbab2c7 --- /dev/null +++ b/manifests/tools/gesture.yaml @@ -0,0 +1,9 @@ +id: gesture +module: mcp/tools/ui-automation/gesture +names: + mcp: gesture + cli: gesture +description: Simulator gesture preset. +annotations: + title: Gesture + destructiveHint: true diff --git a/manifests/tools/get_app_bundle_id.yaml b/manifests/tools/get_app_bundle_id.yaml new file mode 100644 index 00000000..0ffa51a9 --- /dev/null +++ b/manifests/tools/get_app_bundle_id.yaml @@ -0,0 +1,22 @@ +id: get_app_bundle_id +module: mcp/tools/project-discovery/get_app_bundle_id +names: + mcp: get_app_bundle_id + cli: get-app-bundle-id +description: Extract bundle id from .app. +annotations: + title: Get App Bundle ID + readOnlyHint: true +nextSteps: + - label: Install on simulator + toolId: install_app_sim + priority: 1 + - label: Launch on simulator + toolId: launch_app_sim + priority: 2 + - label: Install on device + toolId: install_app_device + priority: 3 + - label: Launch on device + toolId: launch_app_device + priority: 4 diff --git a/manifests/tools/get_device_app_path.yaml b/manifests/tools/get_device_app_path.yaml new file mode 100644 index 00000000..d60989fd --- /dev/null +++ b/manifests/tools/get_device_app_path.yaml @@ -0,0 +1,19 @@ +id: get_device_app_path +module: mcp/tools/device/get_device_app_path +names: + mcp: get_device_app_path + cli: get-app-path +description: Get device built app path. +annotations: + title: Get Device App Path + readOnlyHint: true +nextSteps: + - label: Get bundle ID + toolId: get_app_bundle_id + priority: 1 + - label: Install app on device + toolId: install_app_device + priority: 2 + - label: Launch app on device + toolId: launch_app_device + priority: 3 diff --git a/manifests/tools/get_mac_app_path.yaml b/manifests/tools/get_mac_app_path.yaml new file mode 100644 index 00000000..70d4f532 --- /dev/null +++ b/manifests/tools/get_mac_app_path.yaml @@ -0,0 +1,16 @@ +id: get_mac_app_path +module: mcp/tools/macos/get_mac_app_path +names: + mcp: get_mac_app_path + cli: get-app-path +description: Get macOS built app path. +annotations: + title: Get macOS App Path + readOnlyHint: true +nextSteps: + - label: Get bundle ID + toolId: get_mac_bundle_id + priority: 1 + - label: Launch app + toolId: launch_mac_app + priority: 2 diff --git a/manifests/tools/get_mac_bundle_id.yaml b/manifests/tools/get_mac_bundle_id.yaml new file mode 100644 index 00000000..476dd82e --- /dev/null +++ b/manifests/tools/get_mac_bundle_id.yaml @@ -0,0 +1,16 @@ +id: get_mac_bundle_id +module: mcp/tools/project-discovery/get_mac_bundle_id +names: + mcp: get_mac_bundle_id + cli: get-macos-bundle-id +description: Extract bundle id from macOS .app. +annotations: + title: Get Mac Bundle ID + readOnlyHint: true +nextSteps: + - label: Launch the app + toolId: launch_mac_app + priority: 1 + - label: Build again + toolId: build_macos + priority: 2 diff --git a/manifests/tools/get_sim_app_path.yaml b/manifests/tools/get_sim_app_path.yaml new file mode 100644 index 00000000..71970d4a --- /dev/null +++ b/manifests/tools/get_sim_app_path.yaml @@ -0,0 +1,22 @@ +id: get_sim_app_path +module: mcp/tools/simulator/get_sim_app_path +names: + mcp: get_sim_app_path + cli: get-app-path +description: Get sim built app path. +annotations: + title: Get Simulator App Path + readOnlyHint: true +nextSteps: + - label: Get bundle ID + toolId: get_app_bundle_id + priority: 1 + - label: Boot simulator + toolId: boot_sim + priority: 2 + - label: Install app + toolId: install_app_sim + priority: 3 + - label: Launch app + toolId: launch_app_sim + priority: 4 diff --git a/manifests/tools/install_app_device.yaml b/manifests/tools/install_app_device.yaml new file mode 100644 index 00000000..b6368485 --- /dev/null +++ b/manifests/tools/install_app_device.yaml @@ -0,0 +1,9 @@ +id: install_app_device +module: mcp/tools/device/install_app_device +names: + mcp: install_app_device + cli: install +description: Install app on device. +annotations: + title: Install App Device + destructiveHint: true diff --git a/manifests/tools/install_app_sim.yaml b/manifests/tools/install_app_sim.yaml new file mode 100644 index 00000000..ced0894f --- /dev/null +++ b/manifests/tools/install_app_sim.yaml @@ -0,0 +1,16 @@ +id: install_app_sim +module: mcp/tools/simulator/install_app_sim +names: + mcp: install_app_sim + cli: install +description: Install app on sim. +annotations: + title: Install App Simulator + destructiveHint: true +nextSteps: + - label: Open the Simulator app + toolId: open_sim + priority: 1 + - label: Launch the app + toolId: launch_app_sim + priority: 2 diff --git a/manifests/tools/key_press.yaml b/manifests/tools/key_press.yaml new file mode 100644 index 00000000..d5dd227a --- /dev/null +++ b/manifests/tools/key_press.yaml @@ -0,0 +1,9 @@ +id: key_press +module: mcp/tools/ui-automation/key_press +names: + mcp: key_press + cli: key-press +description: Press key by keycode. +annotations: + title: Key Press + destructiveHint: true diff --git a/manifests/tools/key_sequence.yaml b/manifests/tools/key_sequence.yaml new file mode 100644 index 00000000..4b0235de --- /dev/null +++ b/manifests/tools/key_sequence.yaml @@ -0,0 +1,9 @@ +id: key_sequence +module: mcp/tools/ui-automation/key_sequence +names: + mcp: key_sequence + cli: key-sequence +description: Press a sequence of keys by their keycodes. +annotations: + title: Key Sequence + destructiveHint: true diff --git a/manifests/tools/launch_app_device.yaml b/manifests/tools/launch_app_device.yaml new file mode 100644 index 00000000..4bed9250 --- /dev/null +++ b/manifests/tools/launch_app_device.yaml @@ -0,0 +1,13 @@ +id: launch_app_device +module: mcp/tools/device/launch_app_device +names: + mcp: launch_app_device + cli: launch +description: Launch app on device. +annotations: + title: Launch App Device + destructiveHint: true +nextSteps: + - label: Stop the app + toolId: stop_app_device + priority: 1 diff --git a/manifests/tools/launch_app_logs_sim.yaml b/manifests/tools/launch_app_logs_sim.yaml new file mode 100644 index 00000000..ee4b250a --- /dev/null +++ b/manifests/tools/launch_app_logs_sim.yaml @@ -0,0 +1,15 @@ +id: launch_app_logs_sim +module: mcp/tools/simulator/launch_app_logs_sim +names: + mcp: launch_app_logs_sim + cli: launch-app-with-logs +description: Launch sim app with logs. +routing: + stateful: true +annotations: + title: Launch App Logs Simulator + destructiveHint: true +nextSteps: + - label: Stop capture and retrieve logs + toolId: stop_sim_log_cap + priority: 1 diff --git a/manifests/tools/launch_app_sim.yaml b/manifests/tools/launch_app_sim.yaml new file mode 100644 index 00000000..f5a75989 --- /dev/null +++ b/manifests/tools/launch_app_sim.yaml @@ -0,0 +1,26 @@ +id: launch_app_sim +module: mcp/tools/simulator/launch_app_sim +names: + mcp: launch_app_sim + cli: launch-app +description: Launch app on simulator. +annotations: + title: Launch App Simulator + destructiveHint: true +nextSteps: + - label: Open Simulator app to see it + toolId: open_sim + priority: 1 + - label: Capture structured logs (app continues running) + toolId: start_sim_log_cap + params: + simulatorId: SIMULATOR_UUID + bundleId: BUNDLE_ID + priority: 2 + - label: Capture console + structured logs (app restarts) + toolId: start_sim_log_cap + params: + simulatorId: SIMULATOR_UUID + bundleId: BUNDLE_ID + captureConsole: true + priority: 3 diff --git a/manifests/tools/launch_mac_app.yaml b/manifests/tools/launch_mac_app.yaml new file mode 100644 index 00000000..f094a82e --- /dev/null +++ b/manifests/tools/launch_mac_app.yaml @@ -0,0 +1,9 @@ +id: launch_mac_app +module: mcp/tools/macos/launch_mac_app +names: + mcp: launch_mac_app + cli: launch +description: Launch macOS app. +annotations: + title: Launch macOS App + destructiveHint: true diff --git a/manifests/tools/list_devices.yaml b/manifests/tools/list_devices.yaml new file mode 100644 index 00000000..37a90974 --- /dev/null +++ b/manifests/tools/list_devices.yaml @@ -0,0 +1,19 @@ +id: list_devices +module: mcp/tools/device/list_devices +names: + mcp: list_devices + cli: list +description: List connected devices. +annotations: + title: List Devices + readOnlyHint: true +nextSteps: + - label: Build for device + toolId: build_device + priority: 1 + - label: Run tests on device + toolId: test_device + priority: 2 + - label: Get app path + toolId: get_device_app_path + priority: 3 diff --git a/manifests/tools/list_schemes.yaml b/manifests/tools/list_schemes.yaml new file mode 100644 index 00000000..3cc2f911 --- /dev/null +++ b/manifests/tools/list_schemes.yaml @@ -0,0 +1,22 @@ +id: list_schemes +module: mcp/tools/project-discovery/list_schemes +names: + mcp: list_schemes + cli: list-schemes +description: List Xcode schemes. +annotations: + title: List Schemes + readOnlyHint: true +nextSteps: + - label: Build for macOS + toolId: build_macos + priority: 1 + - label: Build and run on iOS Simulator (default for run intent) + toolId: build_run_sim + priority: 2 + - label: Build for iOS Simulator (compile-only) + toolId: build_sim + priority: 3 + - label: Show build settings + toolId: show_build_settings + priority: 4 diff --git a/manifests/tools/list_sims.yaml b/manifests/tools/list_sims.yaml new file mode 100644 index 00000000..32d171c8 --- /dev/null +++ b/manifests/tools/list_sims.yaml @@ -0,0 +1,31 @@ +id: list_sims +module: mcp/tools/simulator/list_sims +names: + mcp: list_sims + cli: list +description: List iOS simulators. +annotations: + title: List Simulators + readOnlyHint: true +nextSteps: + - label: Boot a simulator + toolId: boot_sim + params: + simulatorId: UUID_FROM_ABOVE + priority: 1 + - label: Open the simulator UI + toolId: open_sim + priority: 2 + - label: Build for simulator + toolId: build_sim + params: + scheme: YOUR_SCHEME + simulatorId: UUID_FROM_ABOVE + priority: 3 + - label: Get app path + toolId: get_sim_app_path + params: + scheme: YOUR_SCHEME + platform: iOS Simulator + simulatorId: UUID_FROM_ABOVE + priority: 4 diff --git a/manifests/tools/long_press.yaml b/manifests/tools/long_press.yaml new file mode 100644 index 00000000..2dbf4089 --- /dev/null +++ b/manifests/tools/long_press.yaml @@ -0,0 +1,9 @@ +id: long_press +module: mcp/tools/ui-automation/long_press +names: + mcp: long_press + cli: long-press +description: Long press at coords. +annotations: + title: Long Press + destructiveHint: true diff --git a/manifests/tools/manage_workflows.yaml b/manifests/tools/manage_workflows.yaml new file mode 100644 index 00000000..4f83dce4 --- /dev/null +++ b/manifests/tools/manage_workflows.yaml @@ -0,0 +1,8 @@ +id: manage_workflows +module: mcp/tools/workflow-discovery/manage_workflows +names: + mcp: manage-workflows + cli: manage-workflows +description: Workflows are groups of tools exposed by XcodeBuildMCP. By default, not all workflows (and therefore tools) are enabled; only simulator tools are enabled by default. Some workflows are mandatory and can't be disabled. +availability: + cli: true diff --git a/manifests/tools/open_sim.yaml b/manifests/tools/open_sim.yaml new file mode 100644 index 00000000..ab388239 --- /dev/null +++ b/manifests/tools/open_sim.yaml @@ -0,0 +1,34 @@ +id: open_sim +module: mcp/tools/simulator/open_sim +names: + mcp: open_sim + cli: open +description: Open Simulator app. +annotations: + title: Open Simulator + destructiveHint: true +nextSteps: + - label: Boot a simulator if needed + toolId: boot_sim + params: + simulatorId: UUID_FROM_LIST_SIMS + priority: 1 + - label: Capture structured logs (app continues running) + toolId: start_sim_log_cap + params: + simulatorId: UUID + bundleId: YOUR_APP_BUNDLE_ID + priority: 2 + - label: Capture console + structured logs (app restarts) + toolId: start_sim_log_cap + params: + simulatorId: UUID + bundleId: YOUR_APP_BUNDLE_ID + captureConsole: true + priority: 3 + - label: Launch app with logs in one step + toolId: launch_app_logs_sim + params: + simulatorId: UUID + bundleId: YOUR_APP_BUNDLE_ID + priority: 4 diff --git a/manifests/tools/record_sim_video.yaml b/manifests/tools/record_sim_video.yaml new file mode 100644 index 00000000..2a2536eb --- /dev/null +++ b/manifests/tools/record_sim_video.yaml @@ -0,0 +1,15 @@ +id: record_sim_video +module: mcp/tools/simulator/record_sim_video +names: + mcp: record_sim_video + cli: record-video +description: Record sim video. +routing: + stateful: true +annotations: + title: Record Simulator Video + destructiveHint: true +nextSteps: + - label: Stop and save the recording + toolId: record_sim_video + priority: 1 diff --git a/manifests/tools/reset_sim_location.yaml b/manifests/tools/reset_sim_location.yaml new file mode 100644 index 00000000..1ce2fb35 --- /dev/null +++ b/manifests/tools/reset_sim_location.yaml @@ -0,0 +1,9 @@ +id: reset_sim_location +module: mcp/tools/simulator-management/reset_sim_location +names: + mcp: reset_sim_location + cli: reset-location +description: Reset sim location. +annotations: + title: Reset Simulator Location + destructiveHint: true diff --git a/manifests/tools/scaffold_ios_project.yaml b/manifests/tools/scaffold_ios_project.yaml new file mode 100644 index 00000000..4aaa2ccc --- /dev/null +++ b/manifests/tools/scaffold_ios_project.yaml @@ -0,0 +1,19 @@ +id: scaffold_ios_project +module: mcp/tools/project-scaffolding/scaffold_ios_project +names: + mcp: scaffold_ios_project + cli: scaffold-ios +description: Scaffold iOS project. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Scaffold iOS Project + destructiveHint: true +nextSteps: + - label: "Important: Before working on the project make sure to read the README.md file in the workspace root directory." + - label: Build for simulator + toolId: build_sim + priority: 1 + - label: Build and run on simulator + toolId: build_run_sim + priority: 2 diff --git a/manifests/tools/scaffold_macos_project.yaml b/manifests/tools/scaffold_macos_project.yaml new file mode 100644 index 00000000..0348e524 --- /dev/null +++ b/manifests/tools/scaffold_macos_project.yaml @@ -0,0 +1,19 @@ +id: scaffold_macos_project +module: mcp/tools/project-scaffolding/scaffold_macos_project +names: + mcp: scaffold_macos_project + cli: scaffold-macos +description: Scaffold macOS project. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Scaffold macOS Project + destructiveHint: true +nextSteps: + - label: "Important: Before working on the project make sure to read the README.md file in the workspace root directory." + - label: Build for macOS + toolId: build_macos + priority: 1 + - label: Build & Run on macOS + toolId: build_run_macos + priority: 2 diff --git a/manifests/tools/screenshot.yaml b/manifests/tools/screenshot.yaml new file mode 100644 index 00000000..75dc871a --- /dev/null +++ b/manifests/tools/screenshot.yaml @@ -0,0 +1,9 @@ +id: screenshot +module: mcp/tools/ui-automation/screenshot +names: + mcp: screenshot + cli: screenshot +description: Capture screenshot. +annotations: + title: Screenshot + readOnlyHint: true diff --git a/manifests/tools/session_clear_defaults.yaml b/manifests/tools/session_clear_defaults.yaml new file mode 100644 index 00000000..d7682aa5 --- /dev/null +++ b/manifests/tools/session_clear_defaults.yaml @@ -0,0 +1,9 @@ +id: session_clear_defaults +module: mcp/tools/session-management/session_clear_defaults +names: + mcp: session_clear_defaults + cli: clear-defaults +description: Clear session defaults for the active profile or a specified profile. +annotations: + title: Clear Session Defaults + destructiveHint: true diff --git a/manifests/tools/session_set_defaults.yaml b/manifests/tools/session_set_defaults.yaml new file mode 100644 index 00000000..abaa1e17 --- /dev/null +++ b/manifests/tools/session_set_defaults.yaml @@ -0,0 +1,9 @@ +id: session_set_defaults +module: mcp/tools/session-management/session_set_defaults +names: + mcp: session_set_defaults + cli: set-defaults +description: Set session defaults for the active profile, or for a specified profile and make it active. +annotations: + title: Set Session Defaults + destructiveHint: true diff --git a/manifests/tools/session_show_defaults.yaml b/manifests/tools/session_show_defaults.yaml new file mode 100644 index 00000000..60ab807d --- /dev/null +++ b/manifests/tools/session_show_defaults.yaml @@ -0,0 +1,9 @@ +id: session_show_defaults +module: mcp/tools/session-management/session_show_defaults +names: + mcp: session_show_defaults + cli: show-defaults +description: Show the current active defaults. +annotations: + title: Show Session Defaults + readOnlyHint: true diff --git a/manifests/tools/session_use_defaults_profile.yaml b/manifests/tools/session_use_defaults_profile.yaml new file mode 100644 index 00000000..74f9fd93 --- /dev/null +++ b/manifests/tools/session_use_defaults_profile.yaml @@ -0,0 +1,9 @@ +id: session_use_defaults_profile +module: mcp/tools/session-management/session_use_defaults_profile +names: + mcp: session_use_defaults_profile + cli: use-defaults-profile +description: Switch the active session defaults profile. +annotations: + title: Use Session Defaults Profile + readOnlyHint: false diff --git a/manifests/tools/set_sim_appearance.yaml b/manifests/tools/set_sim_appearance.yaml new file mode 100644 index 00000000..d55a21f0 --- /dev/null +++ b/manifests/tools/set_sim_appearance.yaml @@ -0,0 +1,9 @@ +id: set_sim_appearance +module: mcp/tools/simulator-management/set_sim_appearance +names: + mcp: set_sim_appearance + cli: set-appearance +description: Set sim appearance. +annotations: + title: Set Simulator Appearance + destructiveHint: true diff --git a/manifests/tools/set_sim_location.yaml b/manifests/tools/set_sim_location.yaml new file mode 100644 index 00000000..b89db124 --- /dev/null +++ b/manifests/tools/set_sim_location.yaml @@ -0,0 +1,9 @@ +id: set_sim_location +module: mcp/tools/simulator-management/set_sim_location +names: + mcp: set_sim_location + cli: set-location +description: Set sim location. +annotations: + title: Set Simulator Location + destructiveHint: true diff --git a/manifests/tools/show_build_settings.yaml b/manifests/tools/show_build_settings.yaml new file mode 100644 index 00000000..f439c579 --- /dev/null +++ b/manifests/tools/show_build_settings.yaml @@ -0,0 +1,21 @@ +id: show_build_settings +module: mcp/tools/project-discovery/show_build_settings +names: + mcp: show_build_settings + cli: show-build-settings +description: Show build settings. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Show Build Settings + readOnlyHint: true +nextSteps: + - label: Build for macOS + toolId: build_macos + priority: 1 + - label: Build for iOS Simulator + toolId: build_sim + priority: 2 + - label: List schemes + toolId: list_schemes + priority: 3 diff --git a/manifests/tools/sim_statusbar.yaml b/manifests/tools/sim_statusbar.yaml new file mode 100644 index 00000000..690c5a3d --- /dev/null +++ b/manifests/tools/sim_statusbar.yaml @@ -0,0 +1,9 @@ +id: sim_statusbar +module: mcp/tools/simulator-management/sim_statusbar +names: + mcp: sim_statusbar + cli: statusbar +description: Set sim status bar network. +annotations: + title: Simulator Statusbar + destructiveHint: true diff --git a/manifests/tools/snapshot_ui.yaml b/manifests/tools/snapshot_ui.yaml new file mode 100644 index 00000000..c4db396d --- /dev/null +++ b/manifests/tools/snapshot_ui.yaml @@ -0,0 +1,24 @@ +id: snapshot_ui +module: mcp/tools/ui-automation/snapshot_ui +names: + mcp: snapshot_ui + cli: snapshot-ui +description: Print view hierarchy with precise view coordinates (x, y, width, height) for visible elements. +nextSteps: + - label: Refresh after layout changes + toolId: snapshot_ui + params: + simulatorId: SIMULATOR_UUID + - label: Tap on element + toolId: tap + params: + simulatorId: SIMULATOR_UUID + x: 0 + y: 0 + - label: Take screenshot for verification + toolId: screenshot + params: + simulatorId: SIMULATOR_UUID +annotations: + title: Snapshot UI + readOnlyHint: true diff --git a/manifests/tools/start_device_log_cap.yaml b/manifests/tools/start_device_log_cap.yaml new file mode 100644 index 00000000..9880a4a0 --- /dev/null +++ b/manifests/tools/start_device_log_cap.yaml @@ -0,0 +1,15 @@ +id: start_device_log_cap +module: mcp/tools/logging/start_device_log_cap +names: + mcp: start_device_log_cap + cli: start-device-log-capture +description: Start device log capture. +routing: + stateful: true +annotations: + title: Start Device Log Capture + destructiveHint: true +nextSteps: + - label: Stop capture and retrieve logs + toolId: stop_device_log_cap + priority: 1 diff --git a/manifests/tools/start_sim_log_cap.yaml b/manifests/tools/start_sim_log_cap.yaml new file mode 100644 index 00000000..cdfe5b03 --- /dev/null +++ b/manifests/tools/start_sim_log_cap.yaml @@ -0,0 +1,15 @@ +id: start_sim_log_cap +module: mcp/tools/logging/start_sim_log_cap +names: + mcp: start_sim_log_cap + cli: start-simulator-log-capture +description: Start sim log capture. +routing: + stateful: true +annotations: + title: Start Simulator Log Capture + destructiveHint: true +nextSteps: + - label: Stop capture and retrieve logs + toolId: stop_sim_log_cap + priority: 1 diff --git a/manifests/tools/stop_app_device.yaml b/manifests/tools/stop_app_device.yaml new file mode 100644 index 00000000..df943e69 --- /dev/null +++ b/manifests/tools/stop_app_device.yaml @@ -0,0 +1,9 @@ +id: stop_app_device +module: mcp/tools/device/stop_app_device +names: + mcp: stop_app_device + cli: stop +description: Stop device app. +annotations: + title: Stop App Device + destructiveHint: true diff --git a/manifests/tools/stop_app_sim.yaml b/manifests/tools/stop_app_sim.yaml new file mode 100644 index 00000000..5f44eb1d --- /dev/null +++ b/manifests/tools/stop_app_sim.yaml @@ -0,0 +1,9 @@ +id: stop_app_sim +module: mcp/tools/simulator/stop_app_sim +names: + mcp: stop_app_sim + cli: stop +description: Stop sim app. +annotations: + title: Stop App Simulator + destructiveHint: true diff --git a/manifests/tools/stop_device_log_cap.yaml b/manifests/tools/stop_device_log_cap.yaml new file mode 100644 index 00000000..a3149a6e --- /dev/null +++ b/manifests/tools/stop_device_log_cap.yaml @@ -0,0 +1,11 @@ +id: stop_device_log_cap +module: mcp/tools/logging/stop_device_log_cap +names: + mcp: stop_device_log_cap + cli: stop-device-log-capture +description: Stop device app and return logs. +routing: + stateful: true +annotations: + title: Stop Device and Return Logs + destructiveHint: true diff --git a/manifests/tools/stop_mac_app.yaml b/manifests/tools/stop_mac_app.yaml new file mode 100644 index 00000000..b2641369 --- /dev/null +++ b/manifests/tools/stop_mac_app.yaml @@ -0,0 +1,9 @@ +id: stop_mac_app +module: mcp/tools/macos/stop_mac_app +names: + mcp: stop_mac_app + cli: stop +description: Stop macOS app. +annotations: + title: Stop macOS App + destructiveHint: true diff --git a/manifests/tools/stop_sim_log_cap.yaml b/manifests/tools/stop_sim_log_cap.yaml new file mode 100644 index 00000000..52fc2232 --- /dev/null +++ b/manifests/tools/stop_sim_log_cap.yaml @@ -0,0 +1,11 @@ +id: stop_sim_log_cap +module: mcp/tools/logging/stop_sim_log_cap +names: + mcp: stop_sim_log_cap + cli: stop-simulator-log-capture +description: Stop sim app and return logs. +routing: + stateful: true +annotations: + title: Stop Simulator and Return Logs + destructiveHint: true diff --git a/manifests/tools/swift_package_build.yaml b/manifests/tools/swift_package_build.yaml new file mode 100644 index 00000000..14d328f8 --- /dev/null +++ b/manifests/tools/swift_package_build.yaml @@ -0,0 +1,11 @@ +id: swift_package_build +module: mcp/tools/swift-package/swift_package_build +names: + mcp: swift_package_build + cli: build +description: swift package target build. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Swift Package Build + destructiveHint: true diff --git a/manifests/tools/swift_package_clean.yaml b/manifests/tools/swift_package_clean.yaml new file mode 100644 index 00000000..4d291324 --- /dev/null +++ b/manifests/tools/swift_package_clean.yaml @@ -0,0 +1,9 @@ +id: swift_package_clean +module: mcp/tools/swift-package/swift_package_clean +names: + mcp: swift_package_clean + cli: clean +description: swift package clean. +annotations: + title: Swift Package Clean + destructiveHint: true diff --git a/manifests/tools/swift_package_list.yaml b/manifests/tools/swift_package_list.yaml new file mode 100644 index 00000000..974a3855 --- /dev/null +++ b/manifests/tools/swift_package_list.yaml @@ -0,0 +1,11 @@ +id: swift_package_list +module: mcp/tools/swift-package/swift_package_list +names: + mcp: swift_package_list + cli: list +description: List SwiftPM processes. +routing: + stateful: true +annotations: + title: Swift Package List + readOnlyHint: true diff --git a/manifests/tools/swift_package_run.yaml b/manifests/tools/swift_package_run.yaml new file mode 100644 index 00000000..627b90de --- /dev/null +++ b/manifests/tools/swift_package_run.yaml @@ -0,0 +1,11 @@ +id: swift_package_run +module: mcp/tools/swift-package/swift_package_run +names: + mcp: swift_package_run + cli: run +description: swift package target run. +routing: + stateful: true +annotations: + title: Swift Package Run + destructiveHint: true diff --git a/manifests/tools/swift_package_stop.yaml b/manifests/tools/swift_package_stop.yaml new file mode 100644 index 00000000..dd55bd1f --- /dev/null +++ b/manifests/tools/swift_package_stop.yaml @@ -0,0 +1,11 @@ +id: swift_package_stop +module: mcp/tools/swift-package/swift_package_stop +names: + mcp: swift_package_stop + cli: stop +description: Stop SwiftPM run. +routing: + stateful: true +annotations: + title: Swift Package Stop + destructiveHint: true diff --git a/manifests/tools/swift_package_test.yaml b/manifests/tools/swift_package_test.yaml new file mode 100644 index 00000000..a4165287 --- /dev/null +++ b/manifests/tools/swift_package_test.yaml @@ -0,0 +1,11 @@ +id: swift_package_test +module: mcp/tools/swift-package/swift_package_test +names: + mcp: swift_package_test + cli: test +description: Run swift package target tests. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Swift Package Test + destructiveHint: true diff --git a/manifests/tools/swipe.yaml b/manifests/tools/swipe.yaml new file mode 100644 index 00000000..b9d17e71 --- /dev/null +++ b/manifests/tools/swipe.yaml @@ -0,0 +1,9 @@ +id: swipe +module: mcp/tools/ui-automation/swipe +names: + mcp: swipe + cli: swipe +description: Swipe between points. +annotations: + title: Swipe + destructiveHint: true diff --git a/manifests/tools/sync_xcode_defaults.yaml b/manifests/tools/sync_xcode_defaults.yaml new file mode 100644 index 00000000..06c4d318 --- /dev/null +++ b/manifests/tools/sync_xcode_defaults.yaml @@ -0,0 +1,12 @@ +id: sync_xcode_defaults +module: mcp/tools/xcode-ide/sync_xcode_defaults +names: + mcp: sync_xcode_defaults + cli: sync-xcode-defaults +description: Sync session defaults (scheme, simulator) from Xcode's current IDE selection. +predicates: + - xcodeAutoSyncDisabled +annotations: + title: Sync Xcode Defaults + readOnlyHint: false + destructiveHint: false diff --git a/manifests/tools/tap.yaml b/manifests/tools/tap.yaml new file mode 100644 index 00000000..eda27341 --- /dev/null +++ b/manifests/tools/tap.yaml @@ -0,0 +1,9 @@ +id: tap +module: mcp/tools/ui-automation/tap +names: + mcp: tap + cli: tap +description: Tap UI element by accessibility id/label (recommended) or coordinates as fallback. +annotations: + title: Tap + destructiveHint: true diff --git a/manifests/tools/test_device.yaml b/manifests/tools/test_device.yaml new file mode 100644 index 00000000..1564fe55 --- /dev/null +++ b/manifests/tools/test_device.yaml @@ -0,0 +1,11 @@ +id: test_device +module: mcp/tools/device/test_device +names: + mcp: test_device + cli: test +description: Test on device. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Test Device + destructiveHint: true diff --git a/manifests/tools/test_macos.yaml b/manifests/tools/test_macos.yaml new file mode 100644 index 00000000..c28f7772 --- /dev/null +++ b/manifests/tools/test_macos.yaml @@ -0,0 +1,11 @@ +id: test_macos +module: mcp/tools/macos/test_macos +names: + mcp: test_macos + cli: test +description: Test macOS target. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Test macOS + destructiveHint: true diff --git a/manifests/tools/test_sim.yaml b/manifests/tools/test_sim.yaml new file mode 100644 index 00000000..d4b10d0c --- /dev/null +++ b/manifests/tools/test_sim.yaml @@ -0,0 +1,11 @@ +id: test_sim +module: mcp/tools/simulator/test_sim +names: + mcp: test_sim + cli: test +description: Test on iOS sim. +predicates: + - hideWhenXcodeAgentMode +annotations: + title: Test Simulator + destructiveHint: true diff --git a/manifests/tools/touch.yaml b/manifests/tools/touch.yaml new file mode 100644 index 00000000..88d480c6 --- /dev/null +++ b/manifests/tools/touch.yaml @@ -0,0 +1,9 @@ +id: touch +module: mcp/tools/ui-automation/touch +names: + mcp: touch + cli: touch +description: Touch down/up at coords. +annotations: + title: Touch + destructiveHint: true diff --git a/manifests/tools/type_text.yaml b/manifests/tools/type_text.yaml new file mode 100644 index 00000000..54058179 --- /dev/null +++ b/manifests/tools/type_text.yaml @@ -0,0 +1,9 @@ +id: type_text +module: mcp/tools/ui-automation/type_text +names: + mcp: type_text + cli: type-text +description: Type text. +annotations: + title: Type Text + destructiveHint: true diff --git a/manifests/tools/xcode_ide_call_tool.yaml b/manifests/tools/xcode_ide_call_tool.yaml new file mode 100644 index 00000000..7ecfb158 --- /dev/null +++ b/manifests/tools/xcode_ide_call_tool.yaml @@ -0,0 +1,11 @@ +id: xcode_ide_call_tool +module: mcp/tools/xcode-ide/xcode_ide_call_tool +names: + mcp: xcode_ide_call_tool + cli: call-tool +description: Call a remote Xcode IDE MCP tool. +predicates: + - mcpRuntimeOnly +annotations: + title: Call Xcode IDE Tool + readOnlyHint: false diff --git a/manifests/tools/xcode_ide_list_tools.yaml b/manifests/tools/xcode_ide_list_tools.yaml new file mode 100644 index 00000000..40ffca81 --- /dev/null +++ b/manifests/tools/xcode_ide_list_tools.yaml @@ -0,0 +1,11 @@ +id: xcode_ide_list_tools +module: mcp/tools/xcode-ide/xcode_ide_list_tools +names: + mcp: xcode_ide_list_tools + cli: list-tools +description: "Lists Xcode-IDE-only MCP capabilities (Use for: SwiftUI previews image capture, code snippet execution, issue Navigator/build logs, and window/tab context)." +predicates: + - mcpRuntimeOnly +annotations: + title: List Xcode IDE Tools + readOnlyHint: true diff --git a/manifests/tools/xcode_tools_bridge_disconnect.yaml b/manifests/tools/xcode_tools_bridge_disconnect.yaml new file mode 100644 index 00000000..30c2d151 --- /dev/null +++ b/manifests/tools/xcode_tools_bridge_disconnect.yaml @@ -0,0 +1,11 @@ +id: xcode_tools_bridge_disconnect +module: mcp/tools/xcode-ide/xcode_tools_bridge_disconnect +names: + mcp: xcode_tools_bridge_disconnect + cli: bridge-disconnect +description: Disconnect bridge and unregister proxied `xcode_tools_*` tools. +predicates: + - debugEnabled +annotations: + title: Disconnect Xcode Tools Bridge + readOnlyHint: false diff --git a/manifests/tools/xcode_tools_bridge_status.yaml b/manifests/tools/xcode_tools_bridge_status.yaml new file mode 100644 index 00000000..7166f140 --- /dev/null +++ b/manifests/tools/xcode_tools_bridge_status.yaml @@ -0,0 +1,11 @@ +id: xcode_tools_bridge_status +module: mcp/tools/xcode-ide/xcode_tools_bridge_status +names: + mcp: xcode_tools_bridge_status + cli: bridge-status +description: Show xcrun mcpbridge availability and proxy tool sync status. +predicates: + - debugEnabled +annotations: + title: Xcode Tools Bridge Status + readOnlyHint: true diff --git a/manifests/tools/xcode_tools_bridge_sync.yaml b/manifests/tools/xcode_tools_bridge_sync.yaml new file mode 100644 index 00000000..a8595996 --- /dev/null +++ b/manifests/tools/xcode_tools_bridge_sync.yaml @@ -0,0 +1,11 @@ +id: xcode_tools_bridge_sync +module: mcp/tools/xcode-ide/xcode_tools_bridge_sync +names: + mcp: xcode_tools_bridge_sync + cli: bridge-sync +description: One-shot connect + tools/list sync (manual retry; avoids background prompt spam). +predicates: + - debugEnabled +annotations: + title: Sync Xcode Tools Bridge + readOnlyHint: false diff --git a/manifests/workflows/debugging.yaml b/manifests/workflows/debugging.yaml new file mode 100644 index 00000000..d3a92819 --- /dev/null +++ b/manifests/workflows/debugging.yaml @@ -0,0 +1,12 @@ +id: debugging +title: LLDB Debugging +description: Attach LLDB debugger to simulator apps, set breakpoints, inspect variables and call stacks. +tools: + - debug_attach_sim + - debug_breakpoint_add + - debug_breakpoint_remove + - debug_continue + - debug_detach + - debug_lldb_command + - debug_stack + - debug_variables diff --git a/manifests/workflows/device.yaml b/manifests/workflows/device.yaml new file mode 100644 index 00000000..7a5aaa86 --- /dev/null +++ b/manifests/workflows/device.yaml @@ -0,0 +1,18 @@ +id: device +title: iOS Device Development +description: Complete iOS development workflow for physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). +tools: + - build_device + - test_device + - list_devices + - install_app_device + - launch_app_device + - stop_app_device + - get_device_app_path + - clean + - discover_projs + - list_schemes + - show_build_settings + - get_app_bundle_id + - start_device_log_cap + - stop_device_log_cap diff --git a/manifests/workflows/doctor.yaml b/manifests/workflows/doctor.yaml new file mode 100644 index 00000000..cc2b1879 --- /dev/null +++ b/manifests/workflows/doctor.yaml @@ -0,0 +1,10 @@ +id: doctor +title: MCP Doctor +description: Diagnostic tool providing comprehensive information about the MCP server environment, dependencies, and configuration. +selection: + mcp: + autoInclude: true +predicates: + - debugEnabled +tools: + - doctor diff --git a/manifests/workflows/logging.yaml b/manifests/workflows/logging.yaml new file mode 100644 index 00000000..4d2ad6d3 --- /dev/null +++ b/manifests/workflows/logging.yaml @@ -0,0 +1,8 @@ +id: logging +title: Log Capture +description: Capture and retrieve logs from simulator and device apps. +tools: + - start_sim_log_cap + - stop_sim_log_cap + - start_device_log_cap + - stop_device_log_cap diff --git a/manifests/workflows/macos.yaml b/manifests/workflows/macos.yaml new file mode 100644 index 00000000..c6cec69a --- /dev/null +++ b/manifests/workflows/macos.yaml @@ -0,0 +1,15 @@ +id: macos +title: macOS Development +description: Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications. +tools: + - build_macos + - build_run_macos + - test_macos + - launch_mac_app + - stop_mac_app + - get_mac_app_path + - get_mac_bundle_id + - clean + - discover_projs + - list_schemes + - show_build_settings diff --git a/manifests/workflows/project-discovery.yaml b/manifests/workflows/project-discovery.yaml new file mode 100644 index 00000000..279a74ba --- /dev/null +++ b/manifests/workflows/project-discovery.yaml @@ -0,0 +1,9 @@ +id: project-discovery +title: Project Discovery +description: Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information. +tools: + - discover_projs + - list_schemes + - show_build_settings + - get_app_bundle_id + - get_mac_bundle_id diff --git a/manifests/workflows/project-scaffolding.yaml b/manifests/workflows/project-scaffolding.yaml new file mode 100644 index 00000000..2ffdf8cb --- /dev/null +++ b/manifests/workflows/project-scaffolding.yaml @@ -0,0 +1,6 @@ +id: project-scaffolding +title: Project Scaffolding +description: Scaffold new iOS and macOS projects from templates. +tools: + - scaffold_ios_project + - scaffold_macos_project diff --git a/manifests/workflows/session-management.yaml b/manifests/workflows/session-management.yaml new file mode 100644 index 00000000..0c246917 --- /dev/null +++ b/manifests/workflows/session-management.yaml @@ -0,0 +1,15 @@ +id: session-management +title: Session Management +description: Manage session defaults for project/workspace paths, scheme, configuration, simulator/device settings. +availability: + cli: false +selection: + mcp: + defaultEnabled: true + autoInclude: true +tools: + - session_show_defaults + - session_use_defaults_profile + - session_set_defaults + - session_clear_defaults + - sync_xcode_defaults diff --git a/manifests/workflows/simulator-management.yaml b/manifests/workflows/simulator-management.yaml new file mode 100644 index 00000000..ca55174e --- /dev/null +++ b/manifests/workflows/simulator-management.yaml @@ -0,0 +1,12 @@ +id: simulator-management +title: Simulator Management +description: Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance. +tools: + - boot_sim + - list_sims + - open_sim + - erase_sims + - set_sim_location + - reset_sim_location + - set_sim_appearance + - sim_statusbar diff --git a/manifests/workflows/simulator.yaml b/manifests/workflows/simulator.yaml new file mode 100644 index 00000000..bd44ded9 --- /dev/null +++ b/manifests/workflows/simulator.yaml @@ -0,0 +1,28 @@ +id: simulator +title: iOS Simulator Development +description: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. +selection: + mcp: + defaultEnabled: true +tools: + - list_sims + - boot_sim + - open_sim + - build_sim + - build_run_sim + - test_sim + - get_sim_app_path + - install_app_sim + - launch_app_sim + - launch_app_logs_sim + - stop_app_sim + - record_sim_video + - clean + - discover_projs + - list_schemes + - show_build_settings + - get_app_bundle_id + - screenshot + - snapshot_ui + - stop_sim_log_cap + - start_sim_log_cap diff --git a/manifests/workflows/swift-package.yaml b/manifests/workflows/swift-package.yaml new file mode 100644 index 00000000..173ae76e --- /dev/null +++ b/manifests/workflows/swift-package.yaml @@ -0,0 +1,10 @@ +id: swift-package +title: Swift Package Development +description: Build, test, run and manage Swift Package Manager projects. +tools: + - swift_package_build + - swift_package_test + - swift_package_clean + - swift_package_run + - swift_package_stop + - swift_package_list diff --git a/manifests/workflows/ui-automation.yaml b/manifests/workflows/ui-automation.yaml new file mode 100644 index 00000000..9f471b3e --- /dev/null +++ b/manifests/workflows/ui-automation.yaml @@ -0,0 +1,15 @@ +id: ui-automation +title: UI Automation +description: UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows. +tools: + - tap + - touch + - long_press + - swipe + - gesture + - button + - key_press + - key_sequence + - type_text + - screenshot + - snapshot_ui diff --git a/manifests/workflows/utilities.yaml b/manifests/workflows/utilities.yaml new file mode 100644 index 00000000..dfcc802c --- /dev/null +++ b/manifests/workflows/utilities.yaml @@ -0,0 +1,5 @@ +id: utilities +title: Build Utilities +description: Utility tools for cleaning build products and managing build artifacts. +tools: + - clean diff --git a/manifests/workflows/workflow-discovery.yaml b/manifests/workflows/workflow-discovery.yaml new file mode 100644 index 00000000..099034f6 --- /dev/null +++ b/manifests/workflows/workflow-discovery.yaml @@ -0,0 +1,12 @@ +id: workflow-discovery +title: Workflow Discovery +description: Manage enabled workflows at runtime. +availability: + cli: false +selection: + mcp: + autoInclude: true +predicates: + - experimentalWorkflowDiscoveryEnabled +tools: + - manage_workflows diff --git a/manifests/workflows/xcode-ide.yaml b/manifests/workflows/xcode-ide.yaml new file mode 100644 index 00000000..4d9b8b58 --- /dev/null +++ b/manifests/workflows/xcode-ide.yaml @@ -0,0 +1,13 @@ +id: xcode-ide +title: Xcode IDE Integration +description: Bridge tools for connecting to Xcode's built-in MCP server (mcpbridge) to access IDE-specific functionality. +availability: + cli: false +predicates: + - hideWhenXcodeAgentMode +tools: + - xcode_ide_list_tools + - xcode_ide_call_tool + - xcode_tools_bridge_status + - xcode_tools_bridge_sync + - xcode_tools_bridge_disconnect diff --git a/package-lock.json b/package-lock.json index 2e7867b1..90005960 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,30 +1,35 @@ { "name": "xcodebuildmcp", - "version": "1.15.1", + "version": "2.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "xcodebuildmcp", - "version": "1.15.1", + "version": "2.0.7", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.25.1", - "@sentry/cli": "^2.43.1", - "@sentry/node": "^10.5.0", + "@sentry/cli": "^3.1.0", + "@sentry/node": "^10.38.0", + "bplist-parser": "^0.3.2", + "chokidar": "^5.0.0", "uuid": "^11.1.0", + "yaml": "^2.4.5", + "yargs": "^17.7.2", "zod": "^4.0.0" }, "bin": { - "xcodebuildmcp": "build/index.js", + "xcodebuildmcp": "build/cli.js", "xcodebuildmcp-doctor": "build/doctor-cli.js" }, "devDependencies": { "@bacons/xcode": "^1.0.0-alpha.24", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.23.0", - "@smithery/cli": "^1.4.6", + "@types/chokidar": "^1.7.5", "@types/node": "^22.13.6", + "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", "@vitest/coverage-v8": "^3.2.4", @@ -57,55 +62,21 @@ "node": ">=6.0.0" } }, - "node_modules/@anthropic-ai/mcpb": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/mcpb/-/mcpb-1.2.0.tgz", - "integrity": "sha512-XYVCxQJsr4D4ZecEXVe9PvBpKj2T31KgEiT8K4thoi7krPFIQjXj4yOolAXP8hGSJHrW1Nf4odZES1kjLNDVkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/prompts": "^6.0.1", - "commander": "^13.1.0", - "fflate": "^0.8.2", - "galactus": "^1.0.0", - "ignore": "^7.0.5", - "node-forge": "^1.3.1", - "pretty-bytes": "^5.6.0", - "zod": "^3.25.67", - "zod-to-json-schema": "^3.24.6" - }, - "bin": { - "mcpb": "dist/cli/cli.js" - } - }, - "node_modules/@anthropic-ai/mcpb/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@anthropic-ai/mcpb/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } + "node_modules/@apm-js-collab/code-transformer": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz", + "integrity": "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==", + "license": "Apache-2.0" }, - "node_modules/@anthropic-ai/mcpb/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "node_modules/@apm-js-collab/tracing-hooks": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.3.1.tgz", + "integrity": "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==", + "license": "Apache-2.0", + "dependencies": { + "@apm-js-collab/code-transformer": "^0.8.0", + "debug": "^4.4.1", + "module-details-from-path": "^1.0.4" } }, "node_modules/@babel/helper-string-parser": { @@ -797,9 +768,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.7", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.7.tgz", - "integrity": "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==", + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -874,316 +845,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@inquirer/checkbox": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", - "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/confirm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", - "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", - "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.5.5", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/editor": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", - "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "external-editor": "^3.1.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/expand": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", - "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/external-editor/node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/input": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", - "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/number": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", - "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/password": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", - "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "ansi-escapes": "^4.3.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/prompts": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", - "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^3.0.1", - "@inquirer/confirm": "^4.0.1", - "@inquirer/editor": "^3.0.1", - "@inquirer/expand": "^3.0.1", - "@inquirer/input": "^3.0.1", - "@inquirer/number": "^2.0.1", - "@inquirer/password": "^3.0.1", - "@inquirer/rawlist": "^3.0.1", - "@inquirer/search": "^2.0.1", - "@inquirer/select": "^3.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/rawlist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", - "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/search": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", - "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/select": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", - "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", - "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", - "dev": true, - "license": "MIT", - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1252,12 +913,12 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.1.tgz", - "integrity": "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.7", + "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", @@ -1265,14 +926,15 @@ "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "jose": "^6.1.1", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.0" + "zod-to-json-schema": "^3.25.1" }, "engines": { "node": ">=18" @@ -1312,261 +974,18 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/@ngrok/ngrok": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok/-/ngrok-1.7.0.tgz", - "integrity": "sha512-P06o9TpxrJbiRbHQkiwy/rUrlXRupc+Z8KT4MiJfmcdWxvIdzjCaJOdnNkcOTs6DMyzIOefG5tvk/HLdtjqr0g==", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@ngrok/ngrok-android-arm64": "1.7.0", - "@ngrok/ngrok-darwin-arm64": "1.7.0", - "@ngrok/ngrok-darwin-universal": "1.7.0", - "@ngrok/ngrok-darwin-x64": "1.7.0", - "@ngrok/ngrok-freebsd-x64": "1.7.0", - "@ngrok/ngrok-linux-arm-gnueabihf": "1.7.0", - "@ngrok/ngrok-linux-arm64-gnu": "1.7.0", - "@ngrok/ngrok-linux-arm64-musl": "1.7.0", - "@ngrok/ngrok-linux-x64-gnu": "1.7.0", - "@ngrok/ngrok-linux-x64-musl": "1.7.0", - "@ngrok/ngrok-win32-arm64-msvc": "1.7.0", - "@ngrok/ngrok-win32-ia32-msvc": "1.7.0", - "@ngrok/ngrok-win32-x64-msvc": "1.7.0" - } - }, - "node_modules/@ngrok/ngrok-android-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-android-arm64/-/ngrok-android-arm64-1.7.0.tgz", - "integrity": "sha512-8tco3ID6noSaNy+CMS7ewqPoIkIM6XO5COCzsUp3Wv3XEbMSyn65RN6cflX2JdqLfUCHcMyD0ahr9IEiHwqmbQ==", - "cpu": [ - "arm64" - ], + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-arm64/-/ngrok-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-+dmJSOzSO+MNDVrPOca2yYDP1W3KfP4qOlAkarIeFRIfqonQwq3QCBmcR7HAlZocLsSqEwyG6KP4RRvAuT0WGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-darwin-universal": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-universal/-/ngrok-darwin-universal-1.7.0.tgz", - "integrity": "sha512-fDEfewyE2pWGFBhOSwQZObeHUkc65U1l+3HIgSOe094TMHsqmyJD0KTCgW9KSn0VP4OvDZbAISi1T3nvqgZYhQ==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-x64/-/ngrok-darwin-x64-1.7.0.tgz", - "integrity": "sha512-+fwMi5uHd9G8BS42MMa9ye6exI5lwTcjUO6Ut497Vu0qgLONdVRenRqnEePV+Q3KtQR7NjqkMnomVfkr9MBjtw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-freebsd-x64/-/ngrok-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-2OGgbrjy3yLRrqAz5N6hlUKIWIXSpR5RjQa2chtZMsSbszQ6c9dI+uVQfOKAeo05tHMUgrYAZ7FocC+ig0dzdQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-arm-gnueabihf/-/ngrok-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-SN9YIfEQiR9xN90QVNvdgvAemqMLoFVSeTWZs779145hQMhvF9Qd9rnWi6J+2uNNK10OczdV1oc/nq1es7u/3g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-arm64-gnu/-/ngrok-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-KDMgzPKFU2kbpVSaA2RZBBia5IPdJEe063YlyVFnSMJmPYWCUnMwdybBsucXfV9u1Lw/ZjKTKotIlbTWGn3HGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-arm64-musl/-/ngrok-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-e66vUdVrBlQ0lT9ZdamB4U604zt5Gualt8/WVcUGzbu8s5LajWd6g/mzZCUjK4UepjvMpfgmCp1/+rX7Rk8d5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-x64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-x64-gnu/-/ngrok-linux-x64-gnu-1.7.0.tgz", - "integrity": "sha512-M6gF0DyOEFqXLfWxObfL3bxYZ4+PnKBHuyLVaqNfFN9Y5utY2mdPOn5422Ppbk4XoIK5/YkuhRqPJl/9FivKEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-x64-musl/-/ngrok-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-4Ijm0dKeoyzZTMaYxR2EiNjtlK81ebflg/WYIO1XtleFrVy4UJEGnxtxEidYoT4BfCqi4uvXiK2Mx216xXKvog==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-win32-arm64-msvc/-/ngrok-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-u7qyWIJI2/YG1HTBnHwUR1+Z2tyGfAsUAItJK/+N1G0FeWJhIWQvSIFJHlaPy4oW1Dc8mSDBX9qvVsiQgLaRFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-win32-ia32-msvc/-/ngrok-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-/UdYUsLNv/Q8j9YJsyIfq/jLCoD8WP+NidouucTUzSoDtmOsXBBT3itLrmPiZTEdEgKiFYLuC1Zon8XQQvbVLA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-win32-x64-msvc/-/ngrok-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-UFJg/duEWzZlLkEs61Gz6/5nYhGaKI62I8dvUGdBR3NCtIMagehnFaFxmnXZldyHmCM8U0aCIFNpWRaKcrQkoA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" + "node": ">= 8" } }, "node_modules/@nodelib/fs.stat": { @@ -1603,9 +1022,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", + "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -1615,9 +1034,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.1.tgz", - "integrity": "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.5.0.tgz", + "integrity": "sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1627,9 +1046,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", - "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", + "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -1642,14 +1061,14 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", + "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" + "@opentelemetry/api-logs": "0.211.0", + "import-in-the-middle": "^2.0.0", + "require-in-the-middle": "^8.0.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1659,14 +1078,14 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.50.0.tgz", - "integrity": "sha512-kwNs/itehHG/qaQBcVrLNcvXVPW0I4FCOVtw3LHMLdYIqD7GJ6Yv2nX+a4YHjzbzIeRYj8iyMp0Bl7tlkidq5w==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.58.0.tgz", + "integrity": "sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1676,13 +1095,13 @@ } }, "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.47.0.tgz", - "integrity": "sha512-pjenvjR6+PMRb6/4X85L4OtkQCootgb/Jzh/l/Utu3SJHBid1F+gk9sTGU2FWuhhEfV6P7MZ7BmCdHXQjgJ42g==", + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.54.0.tgz", + "integrity": "sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/instrumentation": "^0.211.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.38" }, @@ -1694,12 +1113,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.21.0.tgz", - "integrity": "sha512-Xu4CZ1bfhdkV3G6iVHFgKTgHx8GbKSqrTU01kcIJRGHpowVnyOPEv1CW5ow+9GU2X4Eki8zoNuVUenFc3RluxQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.28.0.tgz", + "integrity": "sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" + "@opentelemetry/instrumentation": "^0.211.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1709,13 +1128,13 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.52.0.tgz", - "integrity": "sha512-W7pizN0Wh1/cbNhhTf7C62NpyYw7VfCFTYg0DYieSTrtPBT1vmoSZei19wfKLnrMsz3sHayCg0HxCVL2c+cz5w==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.59.0.tgz", + "integrity": "sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/instrumentation": "^0.211.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -1726,13 +1145,13 @@ } }, "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.23.0.tgz", - "integrity": "sha512-Puan+QopWHA/KNYvDfOZN6M/JtF6buXEyD934vrb8WhsX1/FuM7OtoMlQyIqAadnE8FqqDL4KDPiEfCQH6pQcQ==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.30.0.tgz", + "integrity": "sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0" + "@opentelemetry/instrumentation": "^0.211.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1742,12 +1161,12 @@ } }, "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.47.0.tgz", - "integrity": "sha512-UfHqf3zYK+CwDwEtTjaD12uUqGGTswZ7ofLBEdQ4sEJp9GHSSJMQ2hT3pgBxyKADzUdoxQAv/7NqvL42ZI+Qbw==", + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.54.0.tgz", + "integrity": "sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" + "@opentelemetry/instrumentation": "^0.211.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1757,12 +1176,12 @@ } }, "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.51.0.tgz", - "integrity": "sha512-LchkOu9X5DrXAnPI1+Z06h/EH/zC7D6sA86hhPrk3evLlsJTz0grPrkL/yUJM9Ty0CL/y2HSvmWQCjbJEz/ADg==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.58.0.tgz", + "integrity": "sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" + "@opentelemetry/instrumentation": "^0.211.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1772,13 +1191,13 @@ } }, "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.50.0.tgz", - "integrity": "sha512-5xGusXOFQXKacrZmDbpHQzqYD1gIkrMWuwvlrEPkYOsjUqGUjl1HbxCsn5Y9bUXOCgP1Lj6A4PcKt1UiJ2MujA==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.57.0.tgz", + "integrity": "sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/instrumentation": "^0.211.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -1789,13 +1208,13 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.203.0.tgz", - "integrity": "sha512-y3uQAcCOAwnO6vEuNVocmpVzG3PER6/YZqbPbbffDdJ9te5NkHEkfSMNzlC3+v7KlE+WinPGc3N7MR30G1HY2g==", + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.211.0.tgz", + "integrity": "sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/core": "2.5.0", + "@opentelemetry/instrumentation": "0.211.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, @@ -1807,14 +1226,14 @@ } }, "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.51.0.tgz", - "integrity": "sha512-9IUws0XWCb80NovS+17eONXsw1ZJbHwYYMXiwsfR9TSurkLV5UNbRSKb9URHO+K+pIJILy9wCxvyiOneMr91Ig==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.59.0.tgz", + "integrity": "sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/redis-common": "^0.38.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/redis-common": "^0.38.2", + "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1824,12 +1243,12 @@ } }, "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.12.0.tgz", - "integrity": "sha512-bIe4aSAAxytp88nzBstgr6M7ZiEpW6/D1/SuKXdxxuprf18taVvFL2H5BDNGZ7A14K27haHqzYqtCTqFXHZOYg==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.20.0.tgz", + "integrity": "sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/instrumentation": "^0.211.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -1840,12 +1259,12 @@ } }, "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.48.0.tgz", - "integrity": "sha512-V5wuaBPv/lwGxuHjC6Na2JFRjtPgstw19jTFl1B1b6zvaX8zVDYUDaR5hL7glnQtUSCMktPttQsgK4dhXpddcA==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.55.0.tgz", + "integrity": "sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/instrumentation": "^0.211.0", "@opentelemetry/semantic-conventions": "^1.33.1" }, "engines": { @@ -1856,29 +1275,29 @@ } }, "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.51.0.tgz", - "integrity": "sha512-XNLWeMTMG1/EkQBbgPYzCeBD0cwOrfnn8ao4hWgLv0fNCFQu1kCsJYygz2cvKuCs340RlnG4i321hX7R8gj3Rg==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.59.0.tgz", + "integrity": "sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.36.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": "^1.9.0" } }, "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.48.0.tgz", - "integrity": "sha512-KUW29wfMlTPX1wFz+NNrmE7IzN7NWZDrmFWHM/VJcmFEuQGnnBuTIdsP55CnBDxKgQ/qqYFp4udQFNtjeFosPw==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.55.0.tgz", + "integrity": "sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" + "@opentelemetry/instrumentation": "^0.211.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1888,13 +1307,13 @@ } }, "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.56.0.tgz", - "integrity": "sha512-YG5IXUUmxX3Md2buVMvxm9NWlKADrnavI36hbJsihqqvBGsWnIfguf0rUP5Srr0pfPqhQjUP+agLMsvu0GmUpA==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.64.0.tgz", + "integrity": "sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1904,14 +1323,14 @@ } }, "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.50.0.tgz", - "integrity": "sha512-Am8pk1Ct951r4qCiqkBcGmPIgGhoDiFcRtqPSLbJrUZqEPUsigjtMjoWDRLG1Ki1NHgOF7D0H7d+suWz1AAizw==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.57.0.tgz", + "integrity": "sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1921,13 +1340,13 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.49.0.tgz", - "integrity": "sha512-QU9IUNqNsrlfE3dJkZnFHqLjlndiU39ll/YAAEvWE40sGOCi9AtOF6rmEGzJ1IswoZ3oyePV7q2MP8SrhJfVAA==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.57.0.tgz", + "integrity": "sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0", "@types/mysql": "2.15.27" }, "engines": { @@ -1938,14 +1357,14 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.49.0.tgz", - "integrity": "sha512-dCub9wc02mkJWNyHdVEZ7dvRzy295SmNJa+LrAJY2a/+tIiVBQqEAajFzKwp9zegVVnel9L+WORu34rGLQDzxA==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.57.0.tgz", + "integrity": "sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@opentelemetry/sql-common": "^0.41.0" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@opentelemetry/sql-common": "^0.41.2" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1955,17 +1374,17 @@ } }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.55.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.55.0.tgz", - "integrity": "sha512-yfJ5bYE7CnkW/uNsnrwouG/FR7nmg09zdk2MSs7k0ZOMkDDAE3WBGpVFFApGgNu2U+gtzLgEzOQG4I/X+60hXw==", + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.63.0.tgz", + "integrity": "sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@opentelemetry/sql-common": "^0.41.0", - "@types/pg": "8.15.4", - "@types/pg-pool": "2.0.6" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.41.2", + "@types/pg": "8.15.6", + "@types/pg-pool": "2.0.7" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1975,13 +1394,13 @@ } }, "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.51.0.tgz", - "integrity": "sha512-uL/GtBA0u72YPPehwOvthAe+Wf8k3T+XQPBssJmTYl6fzuZjNq8zTfxVFhl9nRFjFVEe+CtiYNT0Q3AyqW1Z0A==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.59.0.tgz", + "integrity": "sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/redis-common": "^0.38.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/redis-common": "^0.38.2", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -1992,13 +1411,13 @@ } }, "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.22.0.tgz", - "integrity": "sha512-XrrNSUCyEjH1ax9t+Uo6lv0S2FCCykcF7hSxBMxKf7Xn0bPRxD3KyFUZy25aQXzbbbUHhtdxj3r2h88SfEM3aA==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.30.0.tgz", + "integrity": "sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0", "@types/tedious": "^4.0.14" }, "engines": { @@ -2009,13 +1428,14 @@ } }, "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.14.0.tgz", - "integrity": "sha512-2HN+7ztxAReXuxzrtA3WboAKlfP5OsPA57KQn2AdYZbJ3zeRPcLXyW4uO/jpLE6PLm0QRtmeGCmfYpqRlwgSwg==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.21.0.tgz", + "integrity": "sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.24.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -2025,21 +1445,21 @@ } }, "node_modules/@opentelemetry/redis-common": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.0.tgz", - "integrity": "sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==", + "version": "0.38.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz", + "integrity": "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" } }, "node_modules/@opentelemetry/resources": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", - "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz", + "integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.1", + "@opentelemetry/core": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -2050,13 +1470,13 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", - "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz", + "integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/resources": "2.0.1", + "@opentelemetry/core": "2.5.0", + "@opentelemetry/resources": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -2067,18 +1487,18 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz", - "integrity": "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", + "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", "license": "Apache-2.0", "engines": { "node": ">=14" } }, "node_modules/@opentelemetry/sql-common": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.0.tgz", - "integrity": "sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA==", + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", + "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0" @@ -2122,44 +1542,41 @@ "license": "MIT" }, "node_modules/@prisma/instrumentation": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.13.0.tgz", - "integrity": "sha512-b97b0sBycGh89RQcqobSgjGl3jwPaC5cQIOFod6EX1v0zIxlXPmL3ckSXxoHpy+Js0QV/tgCzFvqicMJCtezBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.2.0.tgz", + "integrity": "sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" + "@opentelemetry/instrumentation": "^0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.8" } }, "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/api-logs": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", - "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", + "version": "0.207.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz", + "integrity": "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" }, "engines": { - "node": ">=14" + "node": ">=8.0.0" } }, "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/instrumentation": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", - "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", + "version": "0.207.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.207.0.tgz", + "integrity": "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.57.2", - "@types/shimmer": "^1.2.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" + "@opentelemetry/api-logs": "0.207.0", + "import-in-the-middle": "^2.0.0", + "require-in-the-middle": "^8.0.0" }, "engines": { - "node": ">=14" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" @@ -2446,56 +1863,55 @@ ] }, "node_modules/@sentry/cli": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.51.1.tgz", - "integrity": "sha512-FU+54kNcKJABU0+ekvtnoXHM9zVrDe1zXVFbQT7mS0On0m1P0zFRGdzbnWe2XzpzuEAJXtK6aog/W+esRU9AIA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-3.1.0.tgz", + "integrity": "sha512-ngnx6E8XjXpg1uzma45INfKCS8yurb/fl3cZdXTCa2wmek8b4N6WIlmOlTKFTBrV54OauF6mloJxAlpuzoQR6g==", "hasInstallScript": true, - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "dependencies": { - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.7", "progress": "^2.0.3", "proxy-from-env": "^1.1.0", + "undici": "^6.22.0", "which": "^2.0.2" }, "bin": { "sentry-cli": "bin/sentry-cli" }, "engines": { - "node": ">= 10" + "node": ">= 18" }, "optionalDependencies": { - "@sentry/cli-darwin": "2.51.1", - "@sentry/cli-linux-arm": "2.51.1", - "@sentry/cli-linux-arm64": "2.51.1", - "@sentry/cli-linux-i686": "2.51.1", - "@sentry/cli-linux-x64": "2.51.1", - "@sentry/cli-win32-arm64": "2.51.1", - "@sentry/cli-win32-i686": "2.51.1", - "@sentry/cli-win32-x64": "2.51.1" + "@sentry/cli-darwin": "3.1.0", + "@sentry/cli-linux-arm": "3.1.0", + "@sentry/cli-linux-arm64": "3.1.0", + "@sentry/cli-linux-i686": "3.1.0", + "@sentry/cli-linux-x64": "3.1.0", + "@sentry/cli-win32-arm64": "3.1.0", + "@sentry/cli-win32-i686": "3.1.0", + "@sentry/cli-win32-x64": "3.1.0" } }, "node_modules/@sentry/cli-darwin": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.51.1.tgz", - "integrity": "sha512-R1u8IQdn/7Rr8sf6bVVr0vJT4OqwCFdYsS44Y3OoWGVJW2aAQTWRJOTlV4ueclVLAyUQzmgBjfR8AtiUhd/M5w==", - "license": "BSD-3-Clause", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-3.1.0.tgz", + "integrity": "sha512-xT1WlCHenGGO29Lq/wKaIthdqZzNzZhlPs7dXrzlBx9DyA2Jnl0g7WEau0oWi8GyJGVRXCJMiCydR//Tb5qVwA==", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/cli-linux-arm": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.51.1.tgz", - "integrity": "sha512-Klro17OmSSKOOSaxVKBBNPXet2+HrIDZUTSp8NRl4LQsIubdc1S/aQ79cH/g52Muwzpl3aFwPxyXw+46isfEgA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-3.1.0.tgz", + "integrity": "sha512-kbP3/8/Ct/Jbm569KDXbFIyMyPypIegObvIT7LdSsfdYSZdBd396GV7vUpSGKiLUVVN0xjn8OqQ48AVGfjmuMg==", "cpu": [ "arm" ], - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "linux", @@ -2503,17 +1919,17 @@ "android" ], "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/cli-linux-arm64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.51.1.tgz", - "integrity": "sha512-nvA/hdhsw4bKLhslgbBqqvETjXwN1FVmwHLOrRvRcejDO6zeIKUElDiL5UOjGG0NC+62AxyNw5ri8Wzp/7rg9Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-3.1.0.tgz", + "integrity": "sha512-Jm/iHLKiHxrZYlAq2tT07amiegEVCOAQT9Unilr6djjcZzS2tcI9ThSRQvjP9tFpFRKop+NyNGE3XHXf69r00g==", "cpu": [ "arm64" ], - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "linux", @@ -2521,18 +1937,18 @@ "android" ], "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/cli-linux-i686": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.51.1.tgz", - "integrity": "sha512-jp4TmR8VXBdT9dLo6mHniQHN0xKnmJoPGVz9h9VDvO2Vp/8o96rBc555D4Am5wJOXmfuPlyjGcmwHlB3+kQRWw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-3.1.0.tgz", + "integrity": "sha512-f/PK/EGK5vFOy7LC4Riwb+BEE20Nk7RbEFEMjvRq26DpETCrZYUGlbpIKvJFKOaUmr79aAkFCA/EjJiYfcQP2Q==", "cpu": [ "x86", "ia32" ], - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "linux", @@ -2540,17 +1956,17 @@ "android" ], "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/cli-linux-x64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.51.1.tgz", - "integrity": "sha512-JuLt0MXM2KHNFmjqXjv23sly56mJmUQzGBWktkpY3r+jE08f5NLKPd5wQ6W/SoLXGIOKnwLz0WoUg7aBVyQdeQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-3.1.0.tgz", + "integrity": "sha512-T+v8x1ujhixZrOrH0sVhsW6uLwK4n0WS+B+5xV46WqUKe32cbYotursp2y53ROjgat8SQDGeP/VnC0Qa3Y2fEA==", "cpu": [ "x64" ], - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "linux", @@ -2558,107 +1974,116 @@ "android" ], "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/cli-win32-arm64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.51.1.tgz", - "integrity": "sha512-PiwjTdIFDazTQCTyDCutiSkt4omggYSKnO3HE1+LDjElsFrWY9pJs4fU3D40WAyE2oKu0MarjNH/WxYGdqEAlg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-3.1.0.tgz", + "integrity": "sha512-2DIPq6aW2DC34EDC9J0xwD+9BpFnKdFGdIcQUZMS+5pXlU6V7o8wpZxZAM8TdYNmsPkkQGKp7Dhl/arWpvNgrw==", "cpu": [ "arm64" ], - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/cli-win32-i686": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.51.1.tgz", - "integrity": "sha512-TMvZZpeiI2HmrDFNVQ0uOiTuYKvjEGOZdmUxe3WlhZW82A/2Oka7sQ24ljcOovbmBOj5+fjCHRUMYvLMCWiysA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-3.1.0.tgz", + "integrity": "sha512-2NuywEiiZn6xJ1yAV2xjv/nuHiy6kZU5XR3RSAIrPdEZD1nBoMsH/gB2FufQw58Ziz/7otFcX+vtGpJjbIT5mQ==", "cpu": [ "x86", "ia32" ], - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/cli-win32-x64": { - "version": "2.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.51.1.tgz", - "integrity": "sha512-v2hreYUPPTNK1/N7+DeX7XBN/zb7p539k+2Osf0HFyVBaoUC3Y3+KBwSf4ASsnmgTAK7HCGR+X0NH1vP+icw4w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-3.1.0.tgz", + "integrity": "sha512-Ip405Yqdrr+l9TImsZOJz6c9Nb4zvXcmtOIBKLHc9cowpfXfmlqsHbDp7Xh4+k4L0uLr9i+8ilgQ6ypcuF4UCg==", "cpu": [ "x64" ], - "license": "BSD-3-Clause", + "license": "FSL-1.1-MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=10" + "node": ">=18" + } + }, + "node_modules/@sentry/cli/node_modules/undici": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "license": "MIT", + "engines": { + "node": ">=18.17" } }, "node_modules/@sentry/core": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.5.0.tgz", - "integrity": "sha512-jTJ8NhZSKB2yj3QTVRXfCCngQzAOLThQUxCl9A7Mv+XF10tP7xbH/88MVQ5WiOr2IzcmrB9r2nmUe36BnMlLjA==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.38.0.tgz", + "integrity": "sha512-1pubWDZE5y5HZEPMAZERP4fVl2NH3Ihp1A+vMoVkb3Qc66Diqj1WierAnStlZP7tCx0TBa0dK85GTW/ZFYyB9g==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@sentry/node": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.5.0.tgz", - "integrity": "sha512-GqTkOc7tkWqRTKNjipysElh/bzIkhfLsvNGwH6+zel5kU15IdOCFtAqIri85ZLo9vbaIVtjQELXOzfo/5MMAFQ==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.38.0.tgz", + "integrity": "sha512-wriyDtWDAoatn8EhOj0U4PJR1WufiijTsCGALqakOHbFiadtBJANLe6aSkXoXT4tegw59cz1wY4NlzHjYksaPw==", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-amqplib": "0.50.0", - "@opentelemetry/instrumentation-connect": "0.47.0", - "@opentelemetry/instrumentation-dataloader": "0.21.0", - "@opentelemetry/instrumentation-express": "0.52.0", - "@opentelemetry/instrumentation-fs": "0.23.0", - "@opentelemetry/instrumentation-generic-pool": "0.47.0", - "@opentelemetry/instrumentation-graphql": "0.51.0", - "@opentelemetry/instrumentation-hapi": "0.50.0", - "@opentelemetry/instrumentation-http": "0.203.0", - "@opentelemetry/instrumentation-ioredis": "0.51.0", - "@opentelemetry/instrumentation-kafkajs": "0.12.0", - "@opentelemetry/instrumentation-knex": "0.48.0", - "@opentelemetry/instrumentation-koa": "0.51.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.48.0", - "@opentelemetry/instrumentation-mongodb": "0.56.0", - "@opentelemetry/instrumentation-mongoose": "0.50.0", - "@opentelemetry/instrumentation-mysql": "0.49.0", - "@opentelemetry/instrumentation-mysql2": "0.49.0", - "@opentelemetry/instrumentation-pg": "0.55.0", - "@opentelemetry/instrumentation-redis": "0.51.0", - "@opentelemetry/instrumentation-tedious": "0.22.0", - "@opentelemetry/instrumentation-undici": "0.14.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-trace-base": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0", - "@prisma/instrumentation": "6.13.0", - "@sentry/core": "10.5.0", - "@sentry/node-core": "10.5.0", - "@sentry/opentelemetry": "10.5.0", - "import-in-the-middle": "^1.14.2", + "@opentelemetry/context-async-hooks": "^2.5.0", + "@opentelemetry/core": "^2.5.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation-amqplib": "0.58.0", + "@opentelemetry/instrumentation-connect": "0.54.0", + "@opentelemetry/instrumentation-dataloader": "0.28.0", + "@opentelemetry/instrumentation-express": "0.59.0", + "@opentelemetry/instrumentation-fs": "0.30.0", + "@opentelemetry/instrumentation-generic-pool": "0.54.0", + "@opentelemetry/instrumentation-graphql": "0.58.0", + "@opentelemetry/instrumentation-hapi": "0.57.0", + "@opentelemetry/instrumentation-http": "0.211.0", + "@opentelemetry/instrumentation-ioredis": "0.59.0", + "@opentelemetry/instrumentation-kafkajs": "0.20.0", + "@opentelemetry/instrumentation-knex": "0.55.0", + "@opentelemetry/instrumentation-koa": "0.59.0", + "@opentelemetry/instrumentation-lru-memoizer": "0.55.0", + "@opentelemetry/instrumentation-mongodb": "0.64.0", + "@opentelemetry/instrumentation-mongoose": "0.57.0", + "@opentelemetry/instrumentation-mysql": "0.57.0", + "@opentelemetry/instrumentation-mysql2": "0.57.0", + "@opentelemetry/instrumentation-pg": "0.63.0", + "@opentelemetry/instrumentation-redis": "0.59.0", + "@opentelemetry/instrumentation-tedious": "0.30.0", + "@opentelemetry/instrumentation-undici": "0.21.0", + "@opentelemetry/resources": "^2.5.0", + "@opentelemetry/sdk-trace-base": "^2.5.0", + "@opentelemetry/semantic-conventions": "^1.39.0", + "@prisma/instrumentation": "7.2.0", + "@sentry/core": "10.38.0", + "@sentry/node-core": "10.38.0", + "@sentry/opentelemetry": "10.38.0", + "import-in-the-middle": "^2.0.6", "minimatch": "^9.0.0" }, "engines": { @@ -2666,26 +2091,27 @@ } }, "node_modules/@sentry/node-core": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.5.0.tgz", - "integrity": "sha512-VC4FCKMvvbUT32apTE0exfI/WigqKskzQA+VdFz61Y+T7mTCADngNrOjG3ilVYPBU7R9KEEziEd/oKgencqkmQ==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.38.0.tgz", + "integrity": "sha512-ErXtpedrY1HghgwM6AliilZPcUCoNNP1NThdO4YpeMq04wMX9/GMmFCu46TnCcg6b7IFIOSr2S4yD086PxLlHQ==", "license": "MIT", "dependencies": { - "@sentry/core": "10.5.0", - "@sentry/opentelemetry": "10.5.0", - "import-in-the-middle": "^1.14.2" + "@apm-js-collab/tracing-hooks": "^0.3.1", + "@sentry/core": "10.38.0", + "@sentry/opentelemetry": "10.38.0", + "import-in-the-middle": "^2.0.6" }, "engines": { "node": ">=18" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", - "@opentelemetry/core": "^1.30.1 || ^2.0.0", + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/resources": "^1.30.1 || ^2.0.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0" + "@opentelemetry/resources": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", + "@opentelemetry/semantic-conventions": "^1.39.0" } }, "node_modules/@sentry/node/node_modules/brace-expansion": { @@ -2713,135 +2139,35 @@ } }, "node_modules/@sentry/opentelemetry": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.5.0.tgz", - "integrity": "sha512-/Qva5vngtuh79YUUBA8kbbrD6w/A+u1vy1jnLoPMKDxWTfNPqT4tCiOOmWYotnITaE3QO0UtXK/j7LMX8FhtUA==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.38.0.tgz", + "integrity": "sha512-YPVhWfYmC7nD3EJqEHGtjp4fp5LwtAbE5rt9egQ4hqJlYFvr8YEz9sdoqSZxO0cZzgs2v97HFl/nmWAXe52G2Q==", "license": "MIT", "dependencies": { - "@sentry/core": "10.5.0" + "@sentry/core": "10.38.0" }, "engines": { "node": ">=18" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", - "@opentelemetry/core": "^1.30.1 || ^2.0.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", - "@opentelemetry/semantic-conventions": "^1.34.0" + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", + "@opentelemetry/semantic-conventions": "^1.39.0" } }, - "node_modules/@smithery/cli": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@smithery/cli/-/cli-1.6.7.tgz", - "integrity": "sha512-fkqK2HVerOr3+wHDXfrnC57qljBdQdokpcEbnwtsH+wqxMjNGoF75zRXgR2Sa//QcWsaezTOA7CX7R/NW1UXZg==", + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true, - "dependencies": { - "@anthropic-ai/mcpb": "^1.1.1", - "@modelcontextprotocol/sdk": "^1.25.1", - "@ngrok/ngrok": "^1.5.1", - "@smithery/registry": "^0.4.2", - "boxen": "^8.0.1", - "chalk": "^4.1.2", - "commander": "^14.0.0", - "cors": "^2.8.5", - "cross-fetch": "^4.1.0", - "esbuild": "^0.25.10", - "express": "^5.1.0", - "inquirer": "^8.2.4", - "inquirer-autocomplete-prompt": "^2.0.0", - "lodash": "^4.17.21", - "ora": "^8.2.0", - "shx": "^0.4.0", - "smol-toml": "^1.4.2", - "uuid": "^11.1.0", - "uuidv7": "^1.0.2", - "yaml": "^2.3.4" - }, - "bin": { - "smithery": "dist/index.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@smithery/sdk": "^3.0.1", - "zod": "^4" - } + "license": "MIT" }, - "node_modules/@smithery/cli/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/@smithery/registry": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@smithery/registry/-/registry-0.4.4.tgz", - "integrity": "sha512-oDS8eIa86cU2MZQBDGsCo9kZT9mgjTn0t5UnI2+tAUOyIhSwcDVFgCNBTI657jgzUPqRfALamM6IGekMZ9/wGA==", - "dev": true, - "dependencies": { - "zod": "^3.20.0" - } - }, - "node_modules/@smithery/registry/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/@smithery/sdk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@smithery/sdk/-/sdk-3.0.1.tgz", - "integrity": "sha512-ngwD/Tb4sYNxadsYEHe57KtDfJ26av9no9O0KF0lKfJLwiaxcj9XkNWWefXHiJX/48zs1zPW6iO+/ruIJvB7qg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@modelcontextprotocol/sdk": "^1.25.1", - "chalk": "^5.6.2", - "express": "^5.1.0", - "jose": "^6.1.0", - "lodash": "^4.17.21", - "okay-error": "^1.0.3" - }, - "peerDependencies": { - "zod": "^4" - } - }, - "node_modules/@smithery/sdk/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, "license": "MIT" }, @@ -2869,6 +2195,17 @@ "@types/deep-eql": "*" } }, + "node_modules/@types/chokidar": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@types/chokidar/-/chokidar-1.7.5.tgz", + "integrity": "sha512-PDkSRY7KltW3M60hSBlerxI8SFPXsO3AL/aRVsO4Kh9IHRW74Ih75gUuTd/aE4LSSFqypb10UIX3QzOJwBQMGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/events": "*", + "@types/node": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -2892,6 +2229,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2899,16 +2243,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mute-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", - "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mysql": { "version": "2.15.27", "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", @@ -2928,9 +2262,9 @@ } }, "node_modules/@types/pg": { - "version": "8.15.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.4.tgz", - "integrity": "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==", + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -2939,20 +2273,14 @@ } }, "node_modules/@types/pg-pool": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", - "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", + "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", "license": "MIT", "dependencies": { "@types/pg": "*" } }, - "node_modules/@types/shimmer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", - "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", - "license": "MIT" - }, "node_modules/@types/tedious": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", @@ -2962,10 +2290,20 @@ "@types/node": "*" } }, - "node_modules/@types/wrap-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", - "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true, "license": "MIT" }, @@ -3479,18 +2817,6 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3547,77 +2873,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -3635,7 +2890,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3721,24 +2975,11 @@ "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, "license": "Unlicense", "engines": { "node": ">=0.6" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/body-parser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", @@ -3763,111 +3004,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/boxen/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/boxen/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/boxen/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/bplist-creator": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", @@ -3879,10 +3015,9 @@ } }, "node_modules/bplist-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", - "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", - "dev": true, + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", "license": "MIT", "dependencies": { "big-integer": "1.6.x" @@ -3915,31 +3050,6 @@ "node": ">=8" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -4014,19 +3124,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/chai": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", @@ -4061,13 +3158,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" - }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -4079,91 +3169,102 @@ } }, "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "readdirp": "^5.0.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "license": "MIT" }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, "engines": { "node": ">=8" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">= 12" + "node": ">=8" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -4176,7 +3277,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/commander": { @@ -4272,16 +3372,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cross-fetch": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", - "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4330,19 +3420,6 @@ "dev": true, "license": "MIT" }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4353,9 +3430,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -4405,16 +3482,6 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -4494,6 +3561,15 @@ "@esbuild/win32-x64": "0.25.12" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -4781,105 +3857,6 @@ "node": ">=20.0.0" } }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/execa/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/execa/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/execa/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/execa/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -4934,10 +3911,13 @@ } }, "node_modules/express-rate-limit": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", - "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, "engines": { "node": ">= 16" }, @@ -4948,34 +3928,6 @@ "express": ">= 4.11" } }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/external-editor/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5066,32 +4018,6 @@ "dev": true, "license": "MIT" }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5185,20 +4111,6 @@ "dev": true, "license": "ISC" }, - "node_modules/flora-colossus": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", - "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -5240,21 +4152,6 @@ "node": ">= 0.8" } }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -5279,32 +4176,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/galactus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", - "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "flora-colossus": "^2.0.0", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", - "dev": true, - "license": "MIT", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { @@ -5344,19 +4222,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/get-tsconfig": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", @@ -5455,13 +4320,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -5504,11 +4362,10 @@ } }, "node_modules/hono": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.3.tgz", - "integrity": "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==", + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -5540,19 +4397,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/iconv-lite": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", @@ -5569,27 +4413,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5618,15 +4441,15 @@ } }, "node_modules/import-in-the-middle": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.2.tgz", - "integrity": "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", + "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", "license": "Apache-2.0", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" } }, "node_modules/imurmurhash": { @@ -5645,202 +4468,13 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, - "node_modules/inquirer": { - "version": "8.2.7", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", - "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/external-editor": "^1.0.0", - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer-autocomplete-prompt": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-2.0.1.tgz", - "integrity": "sha512-jUHrH0btO7j5r8DTQgANf2CBkTZChoVySD8zF/wp5fZCOLIuUbleXhf4ZY5jNBOc1owA3gdfWtfZuppfYBhcUg==", - "dev": true, - "license": "ISC", - "dependencies": { - "ansi-escapes": "^4.3.2", - "figures": "^3.2.0", - "picocolors": "^1.0.0", - "run-async": "^2.4.1", - "rxjs": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "inquirer": "^8.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "node_modules/inquirer/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true, - "license": "ISC" - }, - "node_modules/inquirer/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" + "node": ">= 12" } }, "node_modules/ipaddr.js": { @@ -5852,21 +4486,6 @@ "node": ">= 0.10" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -5881,7 +4500,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5900,19 +4518,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -5929,29 +4534,6 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -6094,19 +4676,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6177,13 +4746,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6194,52 +4756,9 @@ "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, "node_modules/loupe": { "version": "3.2.0", @@ -6375,29 +4894,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6411,16 +4907,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -6466,16 +4952,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -6523,66 +4999,6 @@ "node": ">= 0.6" } }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", - "dev": true, - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6604,14 +5020,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/okay-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/okay-error/-/okay-error-1.0.3.tgz", - "integrity": "sha512-1GZkj84Uw2STYhwcGhEkgvNXkremOEmTwSgufKm9CcprjwKFuF6md5f1CIvWJgtYlyfR6BbZYnjr6HCfhUuCpQ==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -6633,22 +5041,6 @@ "wrappy": "1" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -6667,137 +5059,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/ora/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6878,12 +5139,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -6937,9 +5192,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", + "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==", "license": "MIT" }, "node_modules/pg-types": { @@ -7158,9 +5413,9 @@ } }, "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7226,19 +5481,6 @@ "node": ">=6.0.0" } }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -7267,17 +5509,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7289,9 +5520,9 @@ } }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -7348,45 +5579,26 @@ "node": ">= 0.10" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", "license": "MIT", "engines": { - "node": ">= 14.18.0" + "node": ">= 20.19.0" }, "funding": { "type": "individual", "url": "https://paulmillr.com/funding/" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, "node_modules/require-from-string": { @@ -7399,37 +5611,16 @@ } }, "node_modules/require-in-the-middle": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", - "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", "license": "MIT", "dependencies": { "debug": "^4.3.5", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "module-details-from-path": "^1.0.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=9.3.0 || >=8.10.0 <9.0.0" } }, "node_modules/resolve-from": { @@ -7452,27 +5643,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -7537,17 +5707,7 @@ "path-to-regexp": "^8.0.0" }, "engines": { - "node": ">= 18" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" + "node": ">= 18" } }, "node_modules/run-parallel": { @@ -7574,16 +5734,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7611,9 +5761,10 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7686,48 +5837,6 @@ "node": ">=8" } }, - "node_modules/shelljs": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.9.2.tgz", - "integrity": "sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "execa": "^1.0.0", - "fast-glob": "^3.3.2", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", - "license": "BSD-2-Clause" - }, - "node_modules/shx": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.4.0.tgz", - "integrity": "sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.8", - "shelljs": "^0.9.2" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -7832,6 +5941,19 @@ "plist": "^3.0.5" } }, + "node_modules/simple-plist/node_modules/bplist-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", + "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/sirv": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", @@ -7847,19 +5969,6 @@ "node": ">=18" } }, - "node_modules/smol-toml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", - "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, "node_modules/source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", @@ -7936,19 +6045,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stream-buffers": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", @@ -7959,16 +6055,6 @@ "node": ">= 0.10.0" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -8073,16 +6159,6 @@ "node": ">=8" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -8145,18 +6221,6 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/synckit": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", @@ -8237,13 +6301,6 @@ "node": ">=0.8" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -8336,19 +6393,6 @@ "node": ">=14.0.0" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8381,12 +6425,6 @@ "node": ">=6" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -8461,13 +6499,6 @@ } } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/tsup": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", @@ -8521,6 +6552,36 @@ } } }, + "node_modules/tsup/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/tsup/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/tsup/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -8579,19 +6640,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -8657,16 +6705,6 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -8686,13 +6724,6 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -8706,16 +6737,6 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/uuidv7": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/uuidv7/-/uuidv7-1.1.0.tgz", - "integrity": "sha512-2VNnOC0+XQlwogChUDzy6pe8GQEys9QFZBGOh54l6qVfwoCUwwRvk7rDTgaIsRgsF5GFa5oiNg8LqXE3jofBBg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "uuidv7": "cli.js" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -8962,32 +6983,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9020,47 +7015,6 @@ "node": ">=8" } }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/widest-line/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -9215,11 +7169,19 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yaml": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -9231,6 +7193,74 @@ "url": "https://github.com/sponsors/eemeli" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -9254,19 +7284,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.2.tgz", diff --git a/package.json b/package.json index 50a8f914..dc22c2d3 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,44 @@ { "name": "xcodebuildmcp", - "version": "1.15.1", + "version": "2.0.7", "mcpName": "com.xcodebuildmcp/XcodeBuildMCP", "iOSTemplateVersion": "v1.0.8", "macOSTemplateVersion": "v1.0.5", - "main": "build/index.js", "type": "module", - "module": "src/smithery.ts", + "exports": { + ".": "./build/cli.js", + "./package.json": "./package.json" + }, "bin": { - "xcodebuildmcp": "build/index.js", + "xcodebuildmcp": "build/cli.js", "xcodebuildmcp-doctor": "build/doctor-cli.js" }, "scripts": { - "build": "npm run generate:version && npm run generate:loaders && npx smithery build", - "dev": "npm run generate:version && npm run generate:loaders && npx smithery dev", - "build:tsup": "npm run generate:version && npm run generate:loaders && tsup", + "build": "npm run build:tsup", + "dev": "npm run build:tsup && node build/cli.js mcp", + "build:tsup": "npm run generate:version && tsup", "dev:tsup": "npm run build:tsup && tsup --watch", + "prepare": "node scripts/install-git-hooks.js", + "hooks:install": "node scripts/install-git-hooks.js", "generate:version": "npx tsx scripts/generate-version.ts", - "generate:loaders": "npx tsx scripts/generate-loaders.ts", "bundle:axe": "scripts/bundle-axe.sh", + "package:macos": "scripts/package-macos-portable.sh", + "package:macos:universal": "scripts/package-macos-portable.sh --universal", + "verify:portable": "scripts/verify-portable-install.sh", + "homebrew:formula": "scripts/create-homebrew-formula.sh", "lint": "eslint 'src/**/*.{js,ts}'", "lint:fix": "eslint 'src/**/*.{js,ts}' --fix", "format": "prettier --write 'src/**/*.{js,ts}'", "format:check": "prettier --check 'src/**/*.{js,ts}'", - "typecheck": "npx tsc --noEmit", - "inspect": "npx @modelcontextprotocol/inspector node build/index.js", + "typecheck": "npx tsc --noEmit && npx tsc -p tsconfig.test.json", + "typecheck:tests": "npx tsc -p tsconfig.test.json", + "inspect": "npx @modelcontextprotocol/inspector node build/cli.js mcp", "doctor": "node build/doctor-cli.js", - "tools": "npx tsx scripts/tools-cli.ts", - "tools:list": "npx tsx scripts/tools-cli.ts list", - "tools:static": "npx tsx scripts/tools-cli.ts static", - "tools:count": "npx tsx scripts/tools-cli.ts count --static", - "tools:analysis": "npx tsx scripts/analysis/tools-analysis.ts", "docs:update": "npx tsx scripts/update-tools-docs.ts", "docs:update:dry-run": "npx tsx scripts/update-tools-docs.ts --dry-run --verbose", + "docs:check": "node scripts/check-docs-cli-commands.js", "test": "vitest run", + "test:smoke": "npm run build && vitest run --config vitest.smoke.config.ts", "test:watch": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest run --coverage" @@ -41,7 +46,8 @@ "files": [ "build", "bundled", - "plugins" + "plugins", + "manifests" ], "keywords": [ "xcodebuild", @@ -52,30 +58,34 @@ "macos", "simulator" ], - "author": "Cameron Cooke", "license": "MIT", - "description": "XcodeBuildMCP is a ModelContextProtocol server that provides tools for Xcode project management, simulator management, and app utilities.", + "description": "XcodeBuildMCP is a Model Context Protocol server that provides tools for Xcode project management, simulator management, and app utilities.", "repository": { "type": "git", - "url": "git+https://github.com/cameroncooke/XcodeBuildMCP.git" + "url": "git+https://github.com/getsentry/XcodeBuildMCP.git" }, - "homepage": "https://www.async-let.com/blog/xcodebuild-mcp/", + "homepage": "https://www.xcodebuildmcp.com", "bugs": { - "url": "https://github.com/cameroncooke/XcodeBuildMCP/issues" + "url": "https://github.com/getsentry/XcodeBuildMCP/issues" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.25.1", - "@sentry/cli": "^2.43.1", - "@sentry/node": "^10.5.0", + "@sentry/cli": "^3.1.0", + "@sentry/node": "^10.38.0", + "bplist-parser": "^0.3.2", + "chokidar": "^5.0.0", "uuid": "^11.1.0", + "yaml": "^2.4.5", + "yargs": "^17.7.2", "zod": "^4.0.0" }, "devDependencies": { "@bacons/xcode": "^1.0.0-alpha.24", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.23.0", - "@smithery/cli": "^1.4.6", + "@types/chokidar": "^1.7.5", "@types/node": "^22.13.6", + "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", "@vitest/coverage-v8": "^3.2.4", diff --git a/scripts/analysis/tools-analysis.ts b/scripts/analysis/tools-analysis.ts deleted file mode 100644 index 7e6d5988..00000000 --- a/scripts/analysis/tools-analysis.ts +++ /dev/null @@ -1,457 +0,0 @@ -#!/usr/bin/env node - -/** - * XcodeBuildMCP Tools Analysis - * - * Core TypeScript module for analyzing XcodeBuildMCP tools using AST parsing. - * Provides reliable extraction of tool information without fallback strategies. - */ - -import { - createSourceFile, - forEachChild, - isExportAssignment, - isIdentifier, - isNoSubstitutionTemplateLiteral, - isObjectLiteralExpression, - isPropertyAssignment, - isStringLiteral, - isTemplateExpression, - isVariableDeclaration, - isVariableStatement, - type Node, - type ObjectLiteralExpression, - ScriptTarget, - type SourceFile, - SyntaxKind, -} from 'typescript'; -import * as fs from 'fs'; -import * as path from 'path'; -import { glob } from 'glob'; -import { fileURLToPath } from 'url'; - -// Get project root -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const projectRoot = path.resolve(__dirname, '..', '..'); -const toolsDir = path.join(projectRoot, 'src', 'mcp', 'tools'); - -export interface ToolInfo { - name: string; - workflow: string; - path: string; - relativePath: string; - description: string; - isCanonical: boolean; -} - -export interface WorkflowInfo { - name: string; - displayName: string; - description: string; - tools: ToolInfo[]; - toolCount: number; - canonicalCount: number; - reExportCount: number; -} - -export interface AnalysisStats { - totalTools: number; - canonicalTools: number; - reExportTools: number; - workflowCount: number; -} - -export interface StaticAnalysisResult { - workflows: WorkflowInfo[]; - tools: ToolInfo[]; - stats: AnalysisStats; -} - -/** - * Extract the description from a tool's default export using TypeScript AST - */ -function extractToolDescription(sourceFile: SourceFile): string { - let description: string | null = null; - - function visit(node: Node): void { - let objectExpression: ObjectLiteralExpression | null = null; - - // Look for export default { ... } - the standard TypeScript pattern - // isExportEquals is undefined for `export default` and true for `export = ` - if (isExportAssignment(node) && !node.isExportEquals) { - if (isObjectLiteralExpression(node.expression)) { - objectExpression = node.expression; - } - } - - if (objectExpression) { - // Found export default { ... }, now look for description property - for (const property of objectExpression.properties) { - if ( - isPropertyAssignment(property) && - isIdentifier(property.name) && - property.name.text === 'description' - ) { - // Extract the description value - if (isStringLiteral(property.initializer)) { - // This is the most common case - simple string literal - description = property.initializer.text; - } else if ( - isTemplateExpression(property.initializer) || - isNoSubstitutionTemplateLiteral(property.initializer) - ) { - // Handle template literals - get the raw text and clean it - description = property.initializer.getFullText(sourceFile).trim(); - // Remove surrounding backticks - if (description.startsWith('`') && description.endsWith('`')) { - description = description.slice(1, -1); - } - } else { - // Handle any other expression (multiline strings, computed values) - const fullText = property.initializer.getFullText(sourceFile).trim(); - // This covers cases where the description spans multiple lines - // Remove surrounding quotes and normalize whitespace - let cleaned = fullText; - if ( - (cleaned.startsWith('"') && cleaned.endsWith('"')) || - (cleaned.startsWith("'") && cleaned.endsWith("'")) - ) { - cleaned = cleaned.slice(1, -1); - } - // Collapse multiple whitespaces and newlines into single spaces - description = cleaned.replace(/\s+/g, ' ').trim(); - } - return; // Found description, stop looking - } - } - } - - forEachChild(node, visit); - } - - visit(sourceFile); - - if (description === null) { - throw new Error('Could not extract description from tool export default object'); - } - - return description; -} - -/** - * Check if a file is a re-export by examining its content - */ -function isReExportFile(filePath: string): boolean { - const content = fs.readFileSync(filePath, 'utf-8'); - - // Remove comments and empty lines, then check for re-export pattern - // First remove multi-line comments - const contentWithoutBlockComments = content.replace(/\/\*[\s\S]*?\*\//g, ''); - - const cleanedLines = contentWithoutBlockComments - .split('\n') - .map((line) => { - // Remove inline comments but preserve the code before them - const codeBeforeComment = line.split('//')[0].trim(); - return codeBeforeComment; - }) - .filter((line) => line.length > 0); - - // Should have exactly one line: export { default } from '...'; - if (cleanedLines.length !== 1) { - return false; - } - - const exportLine = cleanedLines[0]; - return /^export\s*{\s*default\s*}\s*from\s*['"][^'"]+['"];?\s*$/.test(exportLine); -} - -/** - * Get workflow metadata from index.ts file if it exists - */ -async function getWorkflowMetadata( - workflowDir: string, -): Promise<{ displayName: string; description: string } | null> { - const indexPath = path.join(toolsDir, workflowDir, 'index.ts'); - - if (!fs.existsSync(indexPath)) { - return null; - } - - try { - const content = fs.readFileSync(indexPath, 'utf-8'); - const sourceFile = createSourceFile(indexPath, content, ScriptTarget.Latest, true); - - const workflowExport: { name?: string; description?: string } = {}; - - function visit(node: Node): void { - // Look for: export const workflow = { ... } - if ( - isVariableStatement(node) && - node.modifiers?.some((mod) => mod.kind === SyntaxKind.ExportKeyword) - ) { - for (const declaration of node.declarationList.declarations) { - if ( - isVariableDeclaration(declaration) && - isIdentifier(declaration.name) && - declaration.name.text === 'workflow' && - declaration.initializer && - isObjectLiteralExpression(declaration.initializer) - ) { - // Extract name and description properties - for (const property of declaration.initializer.properties) { - if (isPropertyAssignment(property) && isIdentifier(property.name)) { - const propertyName = property.name.text; - - if (propertyName === 'name' && isStringLiteral(property.initializer)) { - workflowExport.name = property.initializer.text; - } else if ( - propertyName === 'description' && - isStringLiteral(property.initializer) - ) { - workflowExport.description = property.initializer.text; - } - } - } - } - } - } - - forEachChild(node, visit); - } - - visit(sourceFile); - - if (workflowExport.name && workflowExport.description) { - return { - displayName: workflowExport.name, - description: workflowExport.description, - }; - } - } catch (error) { - console.error(`Warning: Could not parse workflow metadata from ${indexPath}: ${error}`); - } - - return null; -} - -/** - * Get a human-readable workflow name from directory name - */ -function getWorkflowDisplayName(workflowDir: string): string { - const displayNames: Record = { - device: 'iOS Device Development', - doctor: 'System Doctor', - logging: 'Logging & Monitoring', - macos: 'macOS Development', - 'project-discovery': 'Project Discovery', - 'project-scaffolding': 'Project Scaffolding', - simulator: 'iOS Simulator Development', - 'simulator-management': 'Simulator Management', - 'swift-package': 'Swift Package Manager', - 'ui-testing': 'UI Testing & Automation', - utilities: 'Utilities', - }; - - return displayNames[workflowDir] || workflowDir; -} - -/** - * Get workflow description - */ -function getWorkflowDescription(workflowDir: string): string { - const descriptions: Record = { - device: 'Physical device development, testing, and deployment', - doctor: 'System health checks and environment validation', - logging: 'Log capture and monitoring across platforms', - macos: 'Native macOS application development and testing', - 'project-discovery': 'Project analysis and information gathering', - 'project-scaffolding': 'Create new projects from templates', - simulator: 'Simulator-based development, testing, and deployment', - 'simulator-management': 'Simulator environment and configuration management', - 'swift-package': 'Swift Package development and testing', - 'ui-testing': 'Automated UI interaction and testing', - utilities: 'General utility operations', - }; - - return descriptions[workflowDir] || `${workflowDir} related tools`; -} - -/** - * Perform static analysis of all tools in the project - */ -export async function getStaticToolAnalysis(): Promise { - // Find all workflow directories - const workflowDirs = fs - .readdirSync(toolsDir, { withFileTypes: true }) - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => dirent.name) - .sort(); - - // Find all tool files - const files = await glob('**/*.ts', { - cwd: toolsDir, - ignore: [ - '**/__tests__/**', - '**/index.ts', - '**/*.test.ts', - '**/lib/**', - '**/*-processes.ts', // Process management utilities - '**/*.deps.ts', // Dependency files - '**/*-utils.ts', // Utility files - '**/*-common.ts', // Common/shared code - '**/*-types.ts', // Type definition files - ], - absolute: true, - }); - - const allTools: ToolInfo[] = []; - const workflowMap = new Map(); - - let canonicalCount = 0; - let reExportCount = 0; - - // Initialize workflow map - for (const workflowDir of workflowDirs) { - workflowMap.set(workflowDir, []); - } - - // Process each tool file - for (const filePath of files) { - const toolName = path.basename(filePath, '.ts'); - const workflowDir = path.basename(path.dirname(filePath)); - const relativePath = path.relative(projectRoot, filePath); - - const isReExport = isReExportFile(filePath); - - let description = ''; - - if (!isReExport) { - // Extract description from canonical tool using AST - try { - const content = fs.readFileSync(filePath, 'utf-8'); - const sourceFile = createSourceFile(filePath, content, ScriptTarget.Latest, true); - - description = extractToolDescription(sourceFile); - canonicalCount++; - } catch (error) { - throw new Error(`Failed to extract description from ${relativePath}: ${error}`); - } - } else { - description = '(Re-exported from shared workflow)'; - reExportCount++; - } - - const toolInfo: ToolInfo = { - name: toolName, - workflow: workflowDir, - path: filePath, - relativePath, - description, - isCanonical: !isReExport, - }; - - allTools.push(toolInfo); - - const workflowTools = workflowMap.get(workflowDir); - if (workflowTools) { - workflowTools.push(toolInfo); - } - } - - // Build workflow information - const workflows: WorkflowInfo[] = []; - - for (const workflowDir of workflowDirs) { - const workflowTools = workflowMap.get(workflowDir) ?? []; - const canonicalTools = workflowTools.filter((t) => t.isCanonical); - const reExportTools = workflowTools.filter((t) => !t.isCanonical); - - // Try to get metadata from index.ts, fall back to hardcoded names/descriptions - const metadata = await getWorkflowMetadata(workflowDir); - - const workflowInfo: WorkflowInfo = { - name: workflowDir, - displayName: metadata?.displayName ?? getWorkflowDisplayName(workflowDir), - description: metadata?.description ?? getWorkflowDescription(workflowDir), - tools: workflowTools.sort((a, b) => a.name.localeCompare(b.name)), - toolCount: workflowTools.length, - canonicalCount: canonicalTools.length, - reExportCount: reExportTools.length, - }; - - workflows.push(workflowInfo); - } - - const stats: AnalysisStats = { - totalTools: allTools.length, - canonicalTools: canonicalCount, - reExportTools: reExportCount, - workflowCount: workflows.length, - }; - - return { - workflows: workflows.sort((a, b) => a.displayName.localeCompare(b.displayName)), - tools: allTools.sort((a, b) => a.name.localeCompare(b.name)), - stats, - }; -} - -/** - * Get only canonical tools (excluding re-exports) for documentation generation - */ -export async function getCanonicalTools(): Promise { - const analysis = await getStaticToolAnalysis(); - return analysis.tools.filter((tool) => tool.isCanonical); -} - -/** - * Get tools grouped by workflow for documentation generation - */ -export async function getToolsByWorkflow(): Promise> { - const analysis = await getStaticToolAnalysis(); - const workflowMap = new Map(); - - for (const workflow of analysis.workflows) { - // Only include canonical tools for documentation - const canonicalTools = workflow.tools.filter((tool) => tool.isCanonical); - if (canonicalTools.length > 0) { - workflowMap.set(workflow.name, canonicalTools); - } - } - - return workflowMap; -} - -// CLI support - if run directly, perform analysis and output results -if (import.meta.url === `file://${process.argv[1]}`) { - async function main(): Promise { - try { - console.log('🔍 Performing static analysis...'); - const analysis = await getStaticToolAnalysis(); - - console.log('\n📊 Analysis Results:'); - console.log(` Workflows: ${analysis.stats.workflowCount}`); - console.log(` Total tools: ${analysis.stats.totalTools}`); - console.log(` Canonical tools: ${analysis.stats.canonicalTools}`); - console.log(` Re-export tools: ${analysis.stats.reExportTools}`); - - if (process.argv.includes('--json')) { - console.log('\n' + JSON.stringify(analysis, null, 2)); - } else { - console.log('\n📂 Workflows:'); - for (const workflow of analysis.workflows) { - console.log( - ` • ${workflow.displayName} (${workflow.canonicalCount} canonical, ${workflow.reExportCount} re-exports)`, - ); - } - } - } catch (error) { - console.error('❌ Analysis failed:', error); - process.exit(1); - } - } - - main(); -} diff --git a/scripts/bundle-axe.sh b/scripts/bundle-axe.sh index 82e739d8..946fda52 100755 --- a/scripts/bundle-axe.sh +++ b/scripts/bundle-axe.sh @@ -8,7 +8,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" BUNDLED_DIR="$PROJECT_ROOT/bundled" -AXE_LOCAL_DIR="/Volumes/Developer/AXe" +AXE_LOCAL_DIR="${AXE_LOCAL_DIR:-}" AXE_TEMP_DIR="/tmp/axe-download-$$" echo "🔨 Preparing AXe artifacts for bundling..." @@ -31,14 +31,20 @@ echo "📌 Using AXe version: $PINNED_AXE_VERSION" # Clean up any existing bundled directory if [ -d "$BUNDLED_DIR" ]; then echo "🧹 Cleaning existing bundled directory..." - rm -rf "$BUNDLED_DIR" + rm -r "$BUNDLED_DIR" fi # Create bundled directory mkdir -p "$BUNDLED_DIR" -# Use local AXe build if available (unless AXE_FORCE_REMOTE=1), otherwise download from GitHub releases -if [ -z "${AXE_FORCE_REMOTE}" ] && [ -d "$AXE_LOCAL_DIR" ] && [ -f "$AXE_LOCAL_DIR/Package.swift" ]; then +USE_LOCAL_AXE=false +AXE_ARCHIVE_FLAVOR="local-signed" +if [ -z "${AXE_FORCE_REMOTE}" ] && [ "${AXE_USE_LOCAL:-0}" = "1" ]; then + USE_LOCAL_AXE=true +fi + +# Use local AXe build only when explicitly requested, otherwise download from GitHub releases. +if [ "$USE_LOCAL_AXE" = true ] && [ -d "$AXE_LOCAL_DIR" ] && [ -f "$AXE_LOCAL_DIR/Package.swift" ]; then echo "🏠 Using local AXe source at $AXE_LOCAL_DIR" cd "$AXE_LOCAL_DIR" @@ -80,24 +86,40 @@ if [ -z "${AXE_FORCE_REMOTE}" ] && [ -d "$AXE_LOCAL_DIR" ] && [ -f "$AXE_LOCAL_D fi done else + if [ "$USE_LOCAL_AXE" = true ]; then + echo "❌ AXE_USE_LOCAL=1 requires AXE_LOCAL_DIR to point to a valid AXe checkout" + echo " Received AXE_LOCAL_DIR: ${AXE_LOCAL_DIR:-}" + exit 1 + fi + echo "📥 Downloading latest AXe release from GitHub..." - # Construct release download URL from pinned version - AXE_RELEASE_URL="https://github.com/cameroncooke/AXe/releases/download/v${PINNED_AXE_VERSION}/AXe-macOS-v${PINNED_AXE_VERSION}.tar.gz" + # Prefer Homebrew-specific archive (unsigned for relocation compatibility), + # and fall back to legacy signed archive for older AXe releases. + AXE_RELEASE_BASE_URL="https://github.com/cameroncooke/AXe/releases/download/v${PINNED_AXE_VERSION}" + AXE_HOMEBREW_URL="${AXE_RELEASE_BASE_URL}/AXe-macOS-homebrew-v${PINNED_AXE_VERSION}.tar.gz" + AXE_LEGACY_URL="${AXE_RELEASE_BASE_URL}/AXe-macOS-v${PINNED_AXE_VERSION}.tar.gz" # Create temp directory mkdir -p "$AXE_TEMP_DIR" cd "$AXE_TEMP_DIR" # Download and extract the release - echo "📥 Downloading AXe release archive ($AXE_RELEASE_URL)..." - curl -L -o "axe-release.tar.gz" "$AXE_RELEASE_URL" + echo "📥 Downloading AXe Homebrew archive ($AXE_HOMEBREW_URL)..." + if curl -fL -o "axe-release.tar.gz" "$AXE_HOMEBREW_URL"; then + AXE_ARCHIVE_FLAVOR="homebrew-unsigned" + echo "✅ Downloaded AXe Homebrew archive" + else + echo "⚠️ AXe Homebrew archive unavailable, falling back to legacy archive" + curl -fL -o "axe-release.tar.gz" "$AXE_LEGACY_URL" + AXE_ARCHIVE_FLAVOR="legacy-signed" + fi echo "📦 Extracting AXe release archive..." tar -xzf "axe-release.tar.gz" # Find the extracted directory (might be named differently) - EXTRACTED_DIR=$(find . -type d -name "*AXe*" -o -name "*axe*" | head -1) + EXTRACTED_DIR=$(find . -type d \( -name "*AXe*" -o -name "*axe*" \) | head -1) if [ -z "$EXTRACTED_DIR" ]; then # If no AXe directory found, assume files are in current directory EXTRACTED_DIR="." @@ -144,23 +166,96 @@ echo "📦 Copied $FRAMEWORK_COUNT frameworks" echo "🔍 Bundled frameworks:" ls -la "$BUNDLED_DIR/Frameworks/" -# Verify binary can run with bundled frameworks -echo "🧪 Testing bundled AXe binary..." -if DYLD_FRAMEWORK_PATH="$BUNDLED_DIR/Frameworks" "$BUNDLED_DIR/axe" --version > /dev/null 2>&1; then - echo "✅ Bundled AXe binary test passed" +ad_hoc_sign_bundled_axe_assets() { + echo "🔏 Applying ad-hoc signatures to bundled AXe assets..." + + while IFS= read -r framework_path; do + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [ ! -f "$framework_binary" ]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + if [ ! -f "$framework_binary" ]; then + echo "❌ Framework binary not found: $framework_binary" + exit 1 + fi + codesign --force --deep --sign - "$framework_binary" + done < <(find "$BUNDLED_DIR/Frameworks" -name "*.framework" -type d) + + codesign --force --deep --sign - "$BUNDLED_DIR/axe" +} + +# Verify binary can run with bundled frameworks (macOS only) +OS_NAME="$(uname -s)" +if [ "$OS_NAME" = "Darwin" ]; then + if ! codesign -dv "$BUNDLED_DIR/axe" >/dev/null 2>&1; then + ad_hoc_sign_bundled_axe_assets + fi + + if [ "$AXE_ARCHIVE_FLAVOR" = "homebrew-unsigned" ]; then + echo "ℹ️ Homebrew AXe archive detected; using ad-hoc signatures for local runtime compatibility" + else + echo "🔏 Verifying AXe signatures..." + if ! codesign --verify --deep --strict "$BUNDLED_DIR/axe"; then + echo "❌ Signature verification failed for bundled AXe binary" + exit 1 + fi + + while IFS= read -r framework_path; do + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [ ! -f "$framework_binary" ]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + if [ ! -f "$framework_binary" ]; then + echo "❌ Framework binary not found: $framework_binary" + exit 1 + fi + if ! codesign --verify --deep --strict "$framework_binary"; then + echo "❌ Signature verification failed for framework binary: $framework_binary" + exit 1 + fi + done < <(find "$BUNDLED_DIR/Frameworks" -name "*.framework" -type d) + fi + + if [ "$AXE_ARCHIVE_FLAVOR" = "homebrew-unsigned" ]; then + echo "ℹ️ Skipping Gatekeeper assessment for unsigned AXe Homebrew archive" + else + echo "🛡️ Assessing AXe with Gatekeeper..." + SPCTL_LOG="$(mktemp)" + if ! spctl --assess --type execute "$BUNDLED_DIR/axe" 2>"$SPCTL_LOG"; then + if grep -q "does not seem to be an app" "$SPCTL_LOG"; then + echo "⚠️ Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" + else + cat "$SPCTL_LOG" + echo "❌ Gatekeeper assessment failed for bundled AXe binary" + rm "$SPCTL_LOG" + exit 1 + fi + fi + rm "$SPCTL_LOG" + fi + + echo "🧪 Testing bundled AXe binary..." + if DYLD_FRAMEWORK_PATH="$BUNDLED_DIR/Frameworks" "$BUNDLED_DIR/axe" --version > /dev/null 2>&1; then + echo "✅ Bundled AXe binary test passed" + else + echo "❌ Bundled AXe binary test failed" + exit 1 + fi + + # Get AXe version for logging + AXE_VERSION=$(DYLD_FRAMEWORK_PATH="$BUNDLED_DIR/Frameworks" "$BUNDLED_DIR/axe" --version 2>/dev/null || echo "unknown") else - echo "❌ Bundled AXe binary test failed" - exit 1 + echo "⚠️ Skipping AXe binary verification on non-macOS (detected $OS_NAME)" + AXE_VERSION="unknown (verification skipped)" fi - -# Get AXe version for logging -AXE_VERSION=$(DYLD_FRAMEWORK_PATH="$BUNDLED_DIR/Frameworks" "$BUNDLED_DIR/axe" --version 2>/dev/null || echo "unknown") echo "📋 AXe version: $AXE_VERSION" # Clean up temp directory if it was used if [ -d "$AXE_TEMP_DIR" ]; then echo "🧹 Cleaning up temporary files..." - rm -rf "$AXE_TEMP_DIR" + rm -r "$AXE_TEMP_DIR" fi # Show final bundle size diff --git a/scripts/check-code-patterns.js b/scripts/check-code-patterns.js index a1aa1dd3..67f2d6fa 100755 --- a/scripts/check-code-patterns.js +++ b/scripts/check-code-patterns.js @@ -2,23 +2,22 @@ /** * XcodeBuildMCP Code Pattern Violations Checker - * + * * Validates that all code files follow XcodeBuildMCP-specific architectural patterns. * This script focuses on domain-specific rules that ESLint cannot express. - * + * * USAGE: * node scripts/check-code-patterns.js [--pattern=vitest|execsync|handler|handler-testing|all] * node scripts/check-code-patterns.js --help - * + * * ARCHITECTURAL RULES ENFORCED: - * 1. NO vitest mocking patterns (vi.mock, vi.fn, .mockResolvedValue, etc.) + * 1. External boundaries in tests should use dependency-injection utilities * 2. NO execSync usage in production code (use CommandExecutor instead) - * 3. ONLY dependency injection with createMockExecutor() and createMockFileSystemExecutor() - * 4. NO handler signature violations (handlers must have exact MCP SDK signatures) - * 5. NO handler testing violations (test logic functions, not handlers directly) - * - * For comprehensive code quality documentation, see docs/CODE_QUALITY.md - * + * 3. NO handler signature violations (handlers must have exact MCP SDK signatures) + * 4. NO handler testing violations (test logic functions, not handlers directly) + * + * For comprehensive code quality documentation, see docs/dev/CODE_QUALITY.md + * * Note: General code quality rules (TypeScript, ESLint) are handled by other tools. * This script only enforces XcodeBuildMCP-specific architectural patterns. */ @@ -34,7 +33,7 @@ const projectRoot = join(__dirname, '..'); // Parse command line arguments const args = process.argv.slice(2); -const patternFilter = args.find(arg => arg.startsWith('--pattern='))?.split('=')[1] || 'all'; +const patternFilter = args.find((arg) => arg.startsWith('--pattern='))?.split('=')[1] || 'all'; const showHelp = args.includes('--help') || args.includes('-h'); if (showHelp) { @@ -49,7 +48,7 @@ OPTIONS: --help, -h Show this help message PATTERN TYPES: - vitest Check only vitest mocking violations (vi.mock, vi.fn, etc.) + vitest Check only custom vitest policy violations (currently none) execsync Check only execSync usage in production code handler Check only handler signature violations handler-testing Check only handler testing violations (testing handlers instead of logic functions) @@ -71,34 +70,15 @@ EXAMPLES: // Patterns for execSync usage in production code (FORBIDDEN) // Note: execSync is allowed in test files for mocking, but not in production code const EXECSYNC_PATTERNS = [ - /\bexecSync\s*\(/, // Direct execSync usage - /\bexecSyncFn\s*[=:]/, // execSyncFn parameter or assignment - /^import\s+(?!type\s)[^}]*from\s+['"]child_process['"]/m, // Importing from child_process (except type-only imports) + /\bexecSync\s*\(/, // Direct execSync usage + /\bexecSyncFn\s*[=:]/, // execSyncFn parameter or assignment + /^import\s+(?!type\s)[^}]*from\s+['"]child_process['"]/m, // Importing from child_process (except type-only imports) /^import\s+{[^}]*(?:exec|spawn|execSync)[^}]*}\s+from\s+['"](?:node:)?child_process['"]/m, // Named imports of functions ]; -// CRITICAL: ALL VITEST MOCKING PATTERNS ARE COMPLETELY FORBIDDEN -// ONLY dependency injection with approved mock utilities is allowed -const VITEST_GENERIC_PATTERNS = [ - /vi\.mock\s*\(/, // vi.mock() - BANNED - /vi\.fn\s*\(/, // vi.fn() - BANNED - /vi\.mocked\s*\(/, // vi.mocked() - BANNED - /vi\.spyOn\s*\(/, // vi.spyOn() - BANNED - /vi\.clearAllMocks\s*\(/, // vi.clearAllMocks() - BANNED - /\.mockResolvedValue/, // .mockResolvedValue - BANNED - /\.mockRejectedValue/, // .mockRejectedValue - BANNED - /\.mockReturnValue/, // .mockReturnValue - BANNED - /\.mockImplementation/, // .mockImplementation - BANNED - /\.mockClear/, // .mockClear - BANNED - /\.mockReset/, // .mockReset - BANNED - /\.mockRestore/, // .mockRestore - BANNED - /\.toHaveBeenCalled/, // .toHaveBeenCalled - BANNED - /\.toHaveBeenCalledWith/, // .toHaveBeenCalledWith - BANNED - /MockedFunction/, // MockedFunction type - BANNED - /as MockedFunction/, // Type casting to MockedFunction - BANNED - /\bexecSync\b/, // execSync usage - BANNED (use executeCommand instead) - /\bexecSyncFn\b/, // execSyncFn usage - BANNED (use executeCommand instead) -]; +// Vitest mocking is allowed for internal collaborators. +// Keep this list empty unless a specific project policy requires certain vitest patterns to be blocked. +const VITEST_GENERIC_PATTERNS = []; // APPROVED mock utilities - ONLY these are allowed const APPROVED_MOCK_PATTERNS = [ @@ -110,25 +90,19 @@ const APPROVED_MOCK_PATTERNS = [ /\bcreateMockEnvironmentDetector\b/, ]; -// REFINED PATTERNS - Only flag ACTUAL vitest violations, not approved dependency injection patterns -// Manual executors and mock objects are APPROVED when used for dependency injection -const UNAPPROVED_MOCK_PATTERNS = [ - // ONLY ACTUAL VITEST PATTERNS (vi.* usage) - Everything else is approved - /\bmock[A-Z][a-zA-Z0-9]*\s*=\s*vi\./, // mockSomething = vi.fn() - vitest assignments only - - // No other patterns - manual executors and mock objects are approved for dependency injection -]; +// Custom vitest restrictions can be added here if needed. +const UNAPPROVED_MOCK_PATTERNS = []; // Function to check if a line contains unapproved mock patterns function hasUnapprovedMockPattern(line) { // Skip lines that contain approved patterns - const hasApprovedPattern = APPROVED_MOCK_PATTERNS.some(pattern => pattern.test(line)); + const hasApprovedPattern = APPROVED_MOCK_PATTERNS.some((pattern) => pattern.test(line)); if (hasApprovedPattern) { return false; } // Check for unapproved patterns - return UNAPPROVED_MOCK_PATTERNS.some(pattern => pattern.test(line)); + return UNAPPROVED_MOCK_PATTERNS.some((pattern) => pattern.test(line)); } // Combined pattern checker for backward compatibility @@ -136,47 +110,47 @@ const VITEST_MOCKING_PATTERNS = VITEST_GENERIC_PATTERNS; // CRITICAL: ARCHITECTURAL VIOLATIONS - Utilities bypassing CommandExecutor (BANNED) const UTILITY_BYPASS_PATTERNS = [ - /spawn\s*\(/, // Direct Node.js spawn usage in utilities - BANNED - /exec\s*\(/, // Direct Node.js exec usage in utilities - BANNED - /execSync\s*\(/, // Direct Node.js execSync usage in utilities - BANNED - /child_process\./, // Direct child_process module usage in utilities - BANNED + /spawn\s*\(/, // Direct Node.js spawn usage in utilities - BANNED + /exec\s*\(/, // Direct Node.js exec usage in utilities - BANNED + /execSync\s*\(/, // Direct Node.js execSync usage in utilities - BANNED + /child_process\./, // Direct child_process module usage in utilities - BANNED ]; // TypeScript patterns are now handled by ESLint - removed from domain-specific checks // ESLint has comprehensive TypeScript rules with proper test file exceptions // CRITICAL: HANDLER SIGNATURE VIOLATIONS ARE FORBIDDEN -// MCP SDK requires handlers to have exact signatures: +// MCP SDK requires handlers to have exact signatures: // Tools: (args: Record) => Promise // Resources: (uri: URL) => Promise<{ contents: Array<{ text: string }> }> const HANDLER_SIGNATURE_VIOLATIONS = [ - /async\s+handler\s*\([^)]*:\s*[^,)]+,\s*[^)]+\s*:/ms, // Handler with multiple parameters separated by comma - BANNED - /async\s+handler\s*\(\s*args\?\s*:/ms, // Handler with optional args parameter - BANNED (should be required) - /async\s+handler\s*\([^)]*,\s*[^)]*CommandExecutor/ms, // Handler with CommandExecutor parameter - BANNED + /async\s+handler\s*\([^)]*:\s*[^,)]+,\s*[^)]+\s*:/ms, // Handler with multiple parameters separated by comma - BANNED + /async\s+handler\s*\(\s*args\?\s*:/ms, // Handler with optional args parameter - BANNED (should be required) + /async\s+handler\s*\([^)]*,\s*[^)]*CommandExecutor/ms, // Handler with CommandExecutor parameter - BANNED /async\s+handler\s*\([^)]*,\s*[^)]*FileSystemExecutor/ms, // Handler with FileSystemExecutor parameter - BANNED - /async\s+handler\s*\([^)]*,\s*[^)]*Dependencies/ms, // Handler with Dependencies parameter - BANNED - /async\s+handler\s*\([^)]*,\s*[^)]*executor\s*:/ms, // Handler with executor parameter - BANNED - /async\s+handler\s*\([^)]*,\s*[^)]*dependencies\s*:/ms, // Handler with dependencies parameter - BANNED + /async\s+handler\s*\([^)]*,\s*[^)]*Dependencies/ms, // Handler with Dependencies parameter - BANNED + /async\s+handler\s*\([^)]*,\s*[^)]*executor\s*:/ms, // Handler with executor parameter - BANNED + /async\s+handler\s*\([^)]*,\s*[^)]*dependencies\s*:/ms, // Handler with dependencies parameter - BANNED ]; // CRITICAL: HANDLER TESTING IN TESTS IS FORBIDDEN // Tests must ONLY call logic functions with dependency injection, NEVER handlers directly // Handlers are thin wrappers for MCP SDK - testing them violates dependency injection architecture const HANDLER_TESTING_VIOLATIONS = [ - /\.handler\s*\(/, // Direct handler calls in tests - BANNED - /await\s+\w+\.handler\s*\(/, // Awaited handler calls - BANNED + /\.handler\s*\(/, // Direct handler calls in tests - BANNED + /await\s+\w+\.handler\s*\(/, // Awaited handler calls - BANNED /const\s+result\s*=\s*await\s+\w+\.handler/, // Handler result assignment - BANNED /expect\s*\(\s*await\s+\w+\.handler/, // Handler expectation calls - BANNED ]; -// CRITICAL: IMPROPER SERVER TYPING PATTERNS ARE FORBIDDEN +// CRITICAL: IMPROPER SERVER TYPING PATTERNS ARE FORBIDDEN // Server instances must use proper MCP SDK types, not generic Record casts const IMPROPER_SERVER_TYPING_VIOLATIONS = [ - /as Record.*server/, // Casting server to Record - BANNED - /server.*as Record/, // Casting server to Record - BANNED - /mcpServer\?\s*:\s*Record/, // Typing server as Record - BANNED - /server\.server\?\?\s*server.*as Record/, // Complex server casting - BANNED - /interface\s+MCPServerInterface\s*{/, // Custom MCP interfaces when SDK types exist - BANNED + /as Record.*server/, // Casting server to Record - BANNED + /server.*as Record/, // Casting server to Record - BANNED + /mcpServer\?\s*:\s*Record/, // Typing server as Record - BANNED + /server\.server\?\?\s*server.*as Record/, // Complex server casting - BANNED + /interface\s+MCPServerInterface\s*{/, // Custom MCP interfaces when SDK types exist - BANNED ]; // ALLOWED PATTERNS for cleanup (not mocking) @@ -186,8 +160,8 @@ const ALLOWED_CLEANUP_PATTERNS = [ // Patterns that indicate TRUE dependency injection approach const DEPENDENCY_INJECTION_PATTERNS = [ - /createMockExecutor/, // createMockExecutor usage - /createMockFileSystemExecutor/, // createMockFileSystemExecutor usage + /createMockExecutor/, // createMockExecutor usage + /createMockFileSystemExecutor/, // createMockFileSystemExecutor usage /executor\?\s*:\s*CommandExecutor/, // executor?: CommandExecutor parameter ]; @@ -203,7 +177,12 @@ function findTestFiles(dir) { if (stat.isDirectory()) { // Skip node_modules and other non-relevant directories - if (!item.startsWith('.') && item !== 'node_modules' && item !== 'dist' && item !== 'build') { + if ( + !item.startsWith('.') && + item !== 'node_modules' && + item !== 'dist' && + item !== 'build' + ) { traverse(fullPath); } } else if (item.endsWith('.test.ts') || item.endsWith('.test.js')) { @@ -228,10 +207,21 @@ function findToolAndResourceFiles(dir) { if (stat.isDirectory()) { // Skip test directories and other non-relevant directories - if (!item.startsWith('.') && item !== '__tests__' && item !== 'node_modules' && item !== 'dist' && item !== 'build') { + if ( + !item.startsWith('.') && + item !== '__tests__' && + item !== 'node_modules' && + item !== 'dist' && + item !== 'build' + ) { traverse(fullPath); } - } else if ((item.endsWith('.ts') || item.endsWith('.js')) && !item.includes('.test.') && item !== 'index.ts' && item !== 'index.js') { + } else if ( + (item.endsWith('.ts') || item.endsWith('.js')) && + !item.includes('.test.') && + item !== 'index.ts' && + item !== 'index.js' + ) { toolFiles.push(fullPath); } } @@ -253,17 +243,30 @@ function findUtilityFiles(dir) { if (stat.isDirectory()) { // Skip test directories and other non-relevant directories - if (!item.startsWith('.') && item !== '__tests__' && item !== 'node_modules' && item !== 'dist' && item !== 'build') { + if ( + !item.startsWith('.') && + item !== '__tests__' && + item !== 'node_modules' && + item !== 'dist' && + item !== 'build' + ) { traverse(fullPath); } - } else if ((item.endsWith('.ts') || item.endsWith('.js')) && !item.includes('.test.') && item !== 'index.ts' && item !== 'index.js') { + } else if ( + (item.endsWith('.ts') || item.endsWith('.js')) && + !item.includes('.test.') && + item !== 'index.ts' && + item !== 'index.js' + ) { // Only include key utility files that should use CommandExecutor // Exclude command.ts itself as it's the core implementation that is allowed to use spawn() - if (fullPath.includes('/utils/') && ( - fullPath.includes('log_capture.ts') || - fullPath.includes('build.ts') || - fullPath.includes('simctl.ts') - ) && !fullPath.includes('command.ts')) { + if ( + fullPath.includes('/utils/') && + (fullPath.includes('log_capture.ts') || + fullPath.includes('build.ts') || + fullPath.includes('simctl.ts')) && + !fullPath.includes('command.ts') + ) { utilityFiles.push(fullPath); } } @@ -276,7 +279,9 @@ function findUtilityFiles(dir) { // Helper function to determine if a file is a test file function isTestFile(filePath) { - return filePath.includes('__tests__') || filePath.endsWith('.test.ts') || filePath.endsWith('.test.js'); + return ( + filePath.includes('__tests__') || filePath.endsWith('.test.ts') || filePath.endsWith('.test.js') + ); } // Helper function to determine if a file is a production file @@ -287,8 +292,10 @@ function isProductionFile(filePath) { // Helper function to determine if a file is allowed to use child_process function isAllowedChildProcessFile(filePath) { // These files need direct child_process access for their core functionality - return filePath.includes('command.ts') || // Core CommandExecutor implementation - filePath.includes('swift_package_run.ts'); // Needs spawn for background process management + return ( + filePath.includes('command.ts') || // Core CommandExecutor implementation + filePath.includes('swift_package_run.ts') + ); // Needs spawn for background process management } function analyzeTestFile(filePath) { @@ -302,13 +309,13 @@ function analyzeTestFile(filePath) { // 1. Check generic vi.* patterns (always violations) lines.forEach((line, index) => { - VITEST_GENERIC_PATTERNS.forEach(pattern => { + VITEST_GENERIC_PATTERNS.forEach((pattern) => { if (pattern.test(line)) { vitestMockingDetails.push({ line: index + 1, content: line.trim(), pattern: pattern.source, - type: 'vitest-generic' + type: 'vitest-generic', }); } }); @@ -316,12 +323,12 @@ function analyzeTestFile(filePath) { // 2. Check for unapproved mock patterns if (hasUnapprovedMockPattern(line)) { // Find which specific pattern matched for better reporting - const matchedPattern = UNAPPROVED_MOCK_PATTERNS.find(pattern => pattern.test(line)); + const matchedPattern = UNAPPROVED_MOCK_PATTERNS.find((pattern) => pattern.test(line)); vitestMockingDetails.push({ line: index + 1, content: line.trim(), pattern: matchedPattern ? matchedPattern.source : 'unapproved mock pattern', - type: 'unapproved-mock' + type: 'unapproved-mock', }); } }); @@ -332,13 +339,17 @@ function analyzeTestFile(filePath) { const hasTypescriptAntipatterns = false; // Check for handler testing violations (FORBIDDEN - ARCHITECTURAL VIOLATION) - const hasHandlerTestingViolations = HANDLER_TESTING_VIOLATIONS.some(pattern => pattern.test(content)); + const hasHandlerTestingViolations = HANDLER_TESTING_VIOLATIONS.some((pattern) => + pattern.test(content), + ); // Check for improper server typing violations (FORBIDDEN - ARCHITECTURAL VIOLATION) - const hasImproperServerTypingViolations = IMPROPER_SERVER_TYPING_VIOLATIONS.some(pattern => pattern.test(content)); + const hasImproperServerTypingViolations = IMPROPER_SERVER_TYPING_VIOLATIONS.some((pattern) => + pattern.test(content), + ); // Check for dependency injection patterns (TRUE DI) - const hasDIPatterns = DEPENDENCY_INJECTION_PATTERNS.some(pattern => pattern.test(content)); + const hasDIPatterns = DEPENDENCY_INJECTION_PATTERNS.some((pattern) => pattern.test(content)); // Extract specific pattern occurrences for details const execSyncDetails = []; // Not applicable to test files @@ -347,25 +358,24 @@ function analyzeTestFile(filePath) { const improperServerTypingDetails = []; lines.forEach((line, index) => { - // TypeScript anti-patterns now handled by ESLint - removed from domain checks - HANDLER_TESTING_VIOLATIONS.forEach(pattern => { + HANDLER_TESTING_VIOLATIONS.forEach((pattern) => { if (pattern.test(line)) { handlerTestingDetails.push({ line: index + 1, content: line.trim(), - pattern: pattern.source + pattern: pattern.source, }); } }); - IMPROPER_SERVER_TYPING_VIOLATIONS.forEach(pattern => { + IMPROPER_SERVER_TYPING_VIOLATIONS.forEach((pattern) => { if (pattern.test(line)) { improperServerTypingDetails.push({ line: index + 1, content: line.trim(), - pattern: pattern.source + pattern: pattern.source, }); } }); @@ -384,9 +394,20 @@ function analyzeTestFile(filePath) { typescriptAntipatternDetails, handlerTestingDetails, improperServerTypingDetails, - needsConversion: hasVitestMockingPatterns || hasHandlerTestingViolations || hasImproperServerTypingViolations, - isConverted: hasDIPatterns && !hasVitestMockingPatterns && !hasHandlerTestingViolations && !hasImproperServerTypingViolations, - isMixed: (hasVitestMockingPatterns || hasHandlerTestingViolations || hasImproperServerTypingViolations) && hasDIPatterns + needsConversion: + hasVitestMockingPatterns || + hasHandlerTestingViolations || + hasImproperServerTypingViolations, + isConverted: + hasDIPatterns && + !hasVitestMockingPatterns && + !hasHandlerTestingViolations && + !hasImproperServerTypingViolations, + isMixed: + (hasVitestMockingPatterns || + hasHandlerTestingViolations || + hasImproperServerTypingViolations) && + hasDIPatterns, }; } catch (error) { console.error(`Error reading file ${filePath}: ${error.message}`); @@ -400,9 +421,10 @@ function analyzeToolOrResourceFile(filePath) { const relativePath = relative(projectRoot, filePath); // Check for execSync patterns in production code (excluding allowed files) - const hasExecSyncPatterns = isProductionFile(filePath) && + const hasExecSyncPatterns = + isProductionFile(filePath) && !isAllowedChildProcessFile(filePath) && - EXECSYNC_PATTERNS.some(pattern => pattern.test(content)); + EXECSYNC_PATTERNS.some((pattern) => pattern.test(content)); // Check for vitest mocking patterns using new robust approach const vitestMockingDetails = []; @@ -410,13 +432,13 @@ function analyzeToolOrResourceFile(filePath) { // 1. Check generic vi.* patterns (always violations) lines.forEach((line, index) => { - VITEST_GENERIC_PATTERNS.forEach(pattern => { + VITEST_GENERIC_PATTERNS.forEach((pattern) => { if (pattern.test(line)) { vitestMockingDetails.push({ line: index + 1, content: line.trim(), pattern: pattern.source, - type: 'vitest-generic' + type: 'vitest-generic', }); } }); @@ -424,12 +446,12 @@ function analyzeToolOrResourceFile(filePath) { // 2. Check for unapproved mock patterns if (hasUnapprovedMockPattern(line)) { // Find which specific pattern matched for better reporting - const matchedPattern = UNAPPROVED_MOCK_PATTERNS.find(pattern => pattern.test(line)); + const matchedPattern = UNAPPROVED_MOCK_PATTERNS.find((pattern) => pattern.test(line)); vitestMockingDetails.push({ line: index + 1, content: line.trim(), pattern: matchedPattern ? matchedPattern.source : 'unapproved mock pattern', - type: 'unapproved-mock' + type: 'unapproved-mock', }); } }); @@ -440,16 +462,22 @@ function analyzeToolOrResourceFile(filePath) { const hasTypescriptAntipatterns = false; // Check for dependency injection patterns (TRUE DI) - const hasDIPatterns = DEPENDENCY_INJECTION_PATTERNS.some(pattern => pattern.test(content)); + const hasDIPatterns = DEPENDENCY_INJECTION_PATTERNS.some((pattern) => pattern.test(content)); // Check for handler signature violations (FORBIDDEN) - const hasHandlerSignatureViolations = HANDLER_SIGNATURE_VIOLATIONS.some(pattern => pattern.test(content)); + const hasHandlerSignatureViolations = HANDLER_SIGNATURE_VIOLATIONS.some((pattern) => + pattern.test(content), + ); // Check for improper server typing violations (FORBIDDEN - ARCHITECTURAL VIOLATION) - const hasImproperServerTypingViolations = IMPROPER_SERVER_TYPING_VIOLATIONS.some(pattern => pattern.test(content)); + const hasImproperServerTypingViolations = IMPROPER_SERVER_TYPING_VIOLATIONS.some((pattern) => + pattern.test(content), + ); // Check for utility bypass patterns (ARCHITECTURAL VIOLATION) - const hasUtilityBypassPatterns = UTILITY_BYPASS_PATTERNS.some(pattern => pattern.test(content)); + const hasUtilityBypassPatterns = UTILITY_BYPASS_PATTERNS.some((pattern) => + pattern.test(content), + ); // Extract specific pattern occurrences for details const execSyncDetails = []; @@ -460,12 +488,12 @@ function analyzeToolOrResourceFile(filePath) { lines.forEach((line, index) => { if (isProductionFile(filePath) && !isAllowedChildProcessFile(filePath)) { - EXECSYNC_PATTERNS.forEach(pattern => { + EXECSYNC_PATTERNS.forEach((pattern) => { if (pattern.test(line)) { execSyncDetails.push({ line: index + 1, content: line.trim(), - pattern: pattern.source + pattern: pattern.source, }); } }); @@ -473,22 +501,22 @@ function analyzeToolOrResourceFile(filePath) { // TypeScript anti-patterns now handled by ESLint - removed from domain checks - IMPROPER_SERVER_TYPING_VIOLATIONS.forEach(pattern => { + IMPROPER_SERVER_TYPING_VIOLATIONS.forEach((pattern) => { if (pattern.test(line)) { improperServerTypingDetails.push({ line: index + 1, content: line.trim(), - pattern: pattern.source + pattern: pattern.source, }); } }); - UTILITY_BYPASS_PATTERNS.forEach(pattern => { + UTILITY_BYPASS_PATTERNS.forEach((pattern) => { if (pattern.test(line)) { utilityBypassDetails.push({ line: index + 1, content: line.trim(), - pattern: pattern.source + pattern: pattern.source, }); } }); @@ -498,7 +526,7 @@ function analyzeToolOrResourceFile(filePath) { const lines = content.split('\n'); const fullContent = content; - HANDLER_SIGNATURE_VIOLATIONS.forEach(pattern => { + HANDLER_SIGNATURE_VIOLATIONS.forEach((pattern) => { let match; const globalPattern = new RegExp(pattern.source, pattern.flags + 'g'); while ((match = globalPattern.exec(fullContent)) !== null) { @@ -509,7 +537,7 @@ function analyzeToolOrResourceFile(filePath) { handlerSignatureDetails.push({ line: lineNumber, content: match[0].replace(/\s+/g, ' ').trim(), - pattern: pattern.source + pattern: pattern.source, }); } }); @@ -530,9 +558,26 @@ function analyzeToolOrResourceFile(filePath) { handlerSignatureDetails, improperServerTypingDetails, utilityBypassDetails, - needsConversion: hasExecSyncPatterns || hasVitestMockingPatterns || hasHandlerSignatureViolations || hasImproperServerTypingViolations || hasUtilityBypassPatterns, - isConverted: hasDIPatterns && !hasExecSyncPatterns && !hasVitestMockingPatterns && !hasHandlerSignatureViolations && !hasImproperServerTypingViolations && !hasUtilityBypassPatterns, - isMixed: (hasExecSyncPatterns || hasVitestMockingPatterns || hasHandlerSignatureViolations || hasImproperServerTypingViolations || hasUtilityBypassPatterns) && hasDIPatterns + needsConversion: + hasExecSyncPatterns || + hasVitestMockingPatterns || + hasHandlerSignatureViolations || + hasImproperServerTypingViolations || + hasUtilityBypassPatterns, + isConverted: + hasDIPatterns && + !hasExecSyncPatterns && + !hasVitestMockingPatterns && + !hasHandlerSignatureViolations && + !hasImproperServerTypingViolations && + !hasUtilityBypassPatterns, + isMixed: + (hasExecSyncPatterns || + hasVitestMockingPatterns || + hasHandlerSignatureViolations || + hasImproperServerTypingViolations || + hasUtilityBypassPatterns) && + hasDIPatterns, }; } catch (error) { console.error(`Error reading file ${filePath}: ${error.message}`); @@ -544,13 +589,19 @@ function main() { console.log('🔍 XcodeBuildMCP Code Pattern Violations Checker\n'); console.log(`🎯 Checking pattern type: ${patternFilter.toUpperCase()}\n`); console.log('CODE GUIDELINES ENFORCED:'); - console.log('✅ ONLY ALLOWED: createMockExecutor() and createMockFileSystemExecutor()'); - console.log('❌ BANNED: vitest mocking patterns (vi.mock, vi.fn, .mockResolvedValue, etc.)'); + console.log('✅ External boundaries in tests should use createMockExecutor()/createMockFileSystemExecutor()'); + console.log('✅ Vitest mocking is allowed for internal collaborators'); console.log('❌ BANNED: execSync usage in production code (use CommandExecutor instead)'); console.log('ℹ️ TypeScript patterns: Handled by ESLint with proper test exceptions'); - console.log('❌ BANNED: handler signature violations (handlers must have exact MCP SDK signatures)'); - console.log('❌ BANNED: handler testing violations (test logic functions, not handlers directly)'); - console.log('❌ BANNED: improper server typing (use McpServer type, not Record)\n'); + console.log( + '❌ BANNED: handler signature violations (handlers must have exact MCP SDK signatures)', + ); + console.log( + '❌ BANNED: handler testing violations (test logic functions, not handlers directly)', + ); + console.log( + '❌ BANNED: improper server typing (use McpServer type, not Record)\n', + ); const testFiles = findTestFiles(join(projectRoot, 'src')); const testResults = testFiles.map(analyzeTestFile).filter(Boolean); @@ -568,7 +619,7 @@ function main() { // Combine test, tool, and utility file results for analysis const results = [...testResults, ...toolResults, ...utilityResults]; const handlerResults = toolResults; - const utilityBypassResults = utilityResults.filter(r => r.hasUtilityBypassPatterns); + const utilityBypassResults = utilityResults.filter((r) => r.hasUtilityBypassPatterns); // Filter results based on pattern type let filteredResults; @@ -576,44 +627,101 @@ function main() { switch (patternFilter) { case 'vitest': - filteredResults = results.filter(r => r.hasVitestMockingPatterns); - console.log(`Filtering to show only vitest mocking violations (${filteredResults.length} files)`); + filteredResults = results.filter((r) => r.hasVitestMockingPatterns); + console.log( + `Filtering to show only vitest mocking violations (${filteredResults.length} files)`, + ); break; case 'execsync': - filteredResults = results.filter(r => r.hasExecSyncPatterns); + filteredResults = results.filter((r) => r.hasExecSyncPatterns); console.log(`Filtering to show only execSync violations (${filteredResults.length} files)`); break; // TypeScript case removed - now handled by ESLint case 'handler': filteredResults = []; - filteredHandlerResults = handlerResults.filter(r => r.hasHandlerSignatureViolations); - console.log(`Filtering to show only handler signature violations (${filteredHandlerResults.length} files)`); + filteredHandlerResults = handlerResults.filter((r) => r.hasHandlerSignatureViolations); + console.log( + `Filtering to show only handler signature violations (${filteredHandlerResults.length} files)`, + ); break; case 'handler-testing': - filteredResults = results.filter(r => r.hasHandlerTestingViolations); - console.log(`Filtering to show only handler testing violations (${filteredResults.length} files)`); + filteredResults = results.filter((r) => r.hasHandlerTestingViolations); + console.log( + `Filtering to show only handler testing violations (${filteredResults.length} files)`, + ); break; case 'server-typing': - filteredResults = results.filter(r => r.hasImproperServerTypingViolations); - console.log(`Filtering to show only improper server typing violations (${filteredResults.length} files)`); + filteredResults = results.filter((r) => r.hasImproperServerTypingViolations); + console.log( + `Filtering to show only improper server typing violations (${filteredResults.length} files)`, + ); break; case 'all': default: - filteredResults = results.filter(r => r.needsConversion); - filteredHandlerResults = handlerResults.filter(r => r.hasHandlerSignatureViolations); - console.log(`Showing all pattern violations (${filteredResults.length} test files + ${filteredHandlerResults.length} handler files)`); + filteredResults = results.filter((r) => r.needsConversion); + filteredHandlerResults = handlerResults.filter((r) => r.hasHandlerSignatureViolations); + console.log( + `Showing all pattern violations (${filteredResults.length} test files + ${filteredHandlerResults.length} handler files)`, + ); break; } const needsConversion = filteredResults; - const converted = results.filter(r => r.isConverted); - const mixed = results.filter(r => r.isMixed); - const execSyncOnly = results.filter(r => r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && true && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns); - const vitestMockingOnly = results.filter(r => r.hasVitestMockingPatterns && !r.hasExecSyncPatterns && true && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns); - const typescriptOnly = results.filter(r => r.false && !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns); - const handlerTestingOnly = results.filter(r => r.hasHandlerTestingViolations && !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && true && !r.hasImproperServerTypingViolations && !r.hasDIPatterns); - const improperServerTypingOnly = results.filter(r => r.hasImproperServerTypingViolations && !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && !r.hasHandlerTestingViolations && !r.hasDIPatterns); - const noPatterns = results.filter(r => !r.hasExecSyncPatterns && !r.hasVitestMockingPatterns && true && !r.hasHandlerTestingViolations && !r.hasImproperServerTypingViolations && !r.hasDIPatterns); + const converted = results.filter((r) => r.isConverted); + const mixed = results.filter((r) => r.isMixed); + const execSyncOnly = results.filter( + (r) => + r.hasExecSyncPatterns && + !r.hasVitestMockingPatterns && + true && + !r.hasHandlerTestingViolations && + !r.hasImproperServerTypingViolations && + !r.hasDIPatterns, + ); + const vitestMockingOnly = results.filter( + (r) => + r.hasVitestMockingPatterns && + !r.hasExecSyncPatterns && + true && + !r.hasHandlerTestingViolations && + !r.hasImproperServerTypingViolations && + !r.hasDIPatterns, + ); + const typescriptOnly = results.filter( + (r) => + r.false && + !r.hasExecSyncPatterns && + !r.hasVitestMockingPatterns && + !r.hasHandlerTestingViolations && + !r.hasImproperServerTypingViolations && + !r.hasDIPatterns, + ); + const handlerTestingOnly = results.filter( + (r) => + r.hasHandlerTestingViolations && + !r.hasExecSyncPatterns && + !r.hasVitestMockingPatterns && + true && + !r.hasImproperServerTypingViolations && + !r.hasDIPatterns, + ); + const improperServerTypingOnly = results.filter( + (r) => + r.hasImproperServerTypingViolations && + !r.hasExecSyncPatterns && + !r.hasVitestMockingPatterns && + !r.hasHandlerTestingViolations && + !r.hasDIPatterns, + ); + const noPatterns = results.filter( + (r) => + !r.hasExecSyncPatterns && + !r.hasVitestMockingPatterns && + true && + !r.hasHandlerTestingViolations && + !r.hasImproperServerTypingViolations && + !r.hasDIPatterns, + ); console.log(`📊 CODE PATTERN VIOLATION ANALYSIS`); console.log(`=================================`); @@ -638,7 +746,7 @@ function main() { if (result.execSyncDetails && result.execSyncDetails.length > 0) { console.log(` 🚨 EXECSYNC PATTERNS (${result.execSyncDetails.length}):`); - result.execSyncDetails.slice(0, 2).forEach(detail => { + result.execSyncDetails.slice(0, 2).forEach((detail) => { console.log(` Line ${detail.line}: ${detail.content}`); }); if (result.execSyncDetails.length > 2) { @@ -649,7 +757,7 @@ function main() { if (result.vitestMockingDetails.length > 0) { console.log(` 🧪 VITEST MOCKING PATTERNS (${result.vitestMockingDetails.length}):`); - result.vitestMockingDetails.slice(0, 2).forEach(detail => { + result.vitestMockingDetails.slice(0, 2).forEach((detail) => { console.log(` Line ${detail.line}: ${detail.content}`); }); if (result.vitestMockingDetails.length > 2) { @@ -661,31 +769,41 @@ function main() { if (result.handlerTestingDetails && result.handlerTestingDetails.length > 0) { console.log(` 🚨 HANDLER TESTING VIOLATIONS (${result.handlerTestingDetails.length}):`); - result.handlerTestingDetails.slice(0, 2).forEach(detail => { + result.handlerTestingDetails.slice(0, 2).forEach((detail) => { console.log(` Line ${detail.line}: ${detail.content}`); }); if (result.handlerTestingDetails.length > 2) { - console.log(` ... and ${result.handlerTestingDetails.length - 2} more handler testing violations`); + console.log( + ` ... and ${result.handlerTestingDetails.length - 2} more handler testing violations`, + ); } - console.log(` 🔧 FIX: Replace handler calls with logic function calls using dependency injection`); + console.log( + ` 🔧 FIX: Replace handler calls with logic function calls using dependency injection`, + ); } if (result.improperServerTypingDetails && result.improperServerTypingDetails.length > 0) { - console.log(` 🔧 IMPROPER SERVER TYPING VIOLATIONS (${result.improperServerTypingDetails.length}):`); - result.improperServerTypingDetails.slice(0, 2).forEach(detail => { + console.log( + ` 🔧 IMPROPER SERVER TYPING VIOLATIONS (${result.improperServerTypingDetails.length}):`, + ); + result.improperServerTypingDetails.slice(0, 2).forEach((detail) => { console.log(` Line ${detail.line}: ${detail.content}`); }); if (result.improperServerTypingDetails.length > 2) { - console.log(` ... and ${result.improperServerTypingDetails.length - 2} more server typing violations`); + console.log( + ` ... and ${result.improperServerTypingDetails.length - 2} more server typing violations`, + ); } - console.log(` 🔧 FIX: Import McpServer from SDK and use proper typing instead of Record`); + console.log( + ` 🔧 FIX: Import McpServer from SDK and use proper typing instead of Record`, + ); } console.log(''); }); } - // Utility bypass violations reporting + // Utility bypass violations reporting if (utilityBypassResults.length > 0) { console.log(`🚨 CRITICAL: UTILITY ARCHITECTURAL VIOLATIONS (${utilityBypassResults.length}):`); console.log(`=======================================================`); @@ -696,12 +814,14 @@ function main() { if (result.utilityBypassDetails.length > 0) { console.log(` 🚨 BYPASS PATTERNS (${result.utilityBypassDetails.length}):`); - result.utilityBypassDetails.forEach(detail => { + result.utilityBypassDetails.forEach((detail) => { console.log(` Line ${detail.line}: ${detail.content}`); }); } - console.log(' 🔧 FIX: Refactor to accept CommandExecutor and use it instead of direct spawn/exec calls'); + console.log( + ' 🔧 FIX: Refactor to accept CommandExecutor and use it instead of direct spawn/exec calls', + ); console.log(''); }); } @@ -715,7 +835,7 @@ function main() { if (result.handlerSignatureDetails.length > 0) { console.log(` 🛠️ HANDLER VIOLATIONS (${result.handlerSignatureDetails.length}):`); - result.handlerSignatureDetails.forEach(detail => { + result.handlerSignatureDetails.forEach((detail) => { console.log(` Line ${detail.line}: ${detail.content}`); }); } @@ -744,15 +864,19 @@ function main() { } // Summary for next steps - const hasViolations = needsConversion.length > 0 || filteredHandlerResults.length > 0 || utilityBypassResults.length > 0; + const hasViolations = + needsConversion.length > 0 || + filteredHandlerResults.length > 0 || + utilityBypassResults.length > 0; if (needsConversion.length > 0) { console.log(`🚨 CRITICAL ACTION REQUIRED (TEST FILES):`); console.log(`=======================================`); - console.log(`1. IMMEDIATELY remove ALL vitest mocking from ${needsConversion.length} files`); - console.log(`2. BANNED: vi.mock(), vi.fn(), .mockResolvedValue(), .toHaveBeenCalled(), etc.`); - console.log(`3. BANNED: Testing handlers directly (.handler()) - test logic functions with dependency injection`); - console.log(`4. ONLY ALLOWED: createMockExecutor() and createMockFileSystemExecutor()`); + console.log(`1. Fix architectural violations in ${needsConversion.length} files`); + console.log( + `2. BANNED: Testing handlers directly (.handler()) - test logic functions with dependency injection`, + ); + console.log(`3. Use injected executors/filesystem mocks for external side effects`); console.log(`4. Update plugin implementations to accept executor?: CommandExecutor parameter`); console.log(`5. Run this script again after each fix to track progress`); console.log(''); @@ -760,16 +884,30 @@ function main() { // Show top files by total violation count const sortedByPatterns = needsConversion .sort((a, b) => { - const totalA = (a.execSyncDetails?.length || 0) + a.vitestMockingDetails.length + (a.handlerTestingDetails?.length || 0) + (a.improperServerTypingDetails?.length || 0); - const totalB = (b.execSyncDetails?.length || 0) + b.vitestMockingDetails.length + (b.handlerTestingDetails?.length || 0) + (b.improperServerTypingDetails?.length || 0); + const totalA = + (a.execSyncDetails?.length || 0) + + a.vitestMockingDetails.length + + (a.handlerTestingDetails?.length || 0) + + (a.improperServerTypingDetails?.length || 0); + const totalB = + (b.execSyncDetails?.length || 0) + + b.vitestMockingDetails.length + + (b.handlerTestingDetails?.length || 0) + + (b.improperServerTypingDetails?.length || 0); return totalB - totalA; }) .slice(0, 5); console.log(`🚨 TOP 5 FILES WITH MOST VIOLATIONS:`); sortedByPatterns.forEach((result, index) => { - const totalPatterns = (result.execSyncDetails?.length || 0) + result.vitestMockingDetails.length + (result.handlerTestingDetails?.length || 0) + (result.improperServerTypingDetails?.length || 0); - console.log(`${index + 1}. ${result.filePath} (${totalPatterns} violations: ${result.execSyncDetails?.length || 0} execSync + ${result.vitestMockingDetails.length} vitest + ${result.handlerTestingDetails?.length || 0} handler + ${result.improperServerTypingDetails?.length || 0} server)`); + const totalPatterns = + (result.execSyncDetails?.length || 0) + + result.vitestMockingDetails.length + + (result.handlerTestingDetails?.length || 0) + + (result.improperServerTypingDetails?.length || 0); + console.log( + `${index + 1}. ${result.filePath} (${totalPatterns} violations: ${result.execSyncDetails?.length || 0} execSync + ${result.vitestMockingDetails.length} vitest + ${result.handlerTestingDetails?.length || 0} handler + ${result.improperServerTypingDetails?.length || 0} server)`, + ); }); console.log(''); } @@ -777,7 +915,9 @@ function main() { if (utilityBypassResults.length > 0) { console.log(`🚨 CRITICAL ACTION REQUIRED (UTILITY FILES):`); console.log(`==========================================`); - console.log(`1. IMMEDIATELY fix ALL architectural violations in ${utilityBypassResults.length} files`); + console.log( + `1. IMMEDIATELY fix ALL architectural violations in ${utilityBypassResults.length} files`, + ); console.log(`2. Refactor utilities to accept CommandExecutor parameter`); console.log(`3. Replace direct spawn/exec calls with executor calls`); console.log(`4. These violations break our entire testing strategy`); @@ -788,10 +928,18 @@ function main() { if (filteredHandlerResults.length > 0) { console.log(`🚨 CRITICAL ACTION REQUIRED (HANDLER FILES):`); console.log(`==========================================`); - console.log(`1. IMMEDIATELY fix ALL handler signature violations in ${filteredHandlerResults.length} files`); - console.log(`2. Tools: Handler must be: async handler(args: Record): Promise`); - console.log(`3. Resources: Handler must be: async handler(uri: URL): Promise<{ contents: Array<{ text: string }> }>`); - console.log(`4. Inject dependencies INSIDE handler body: const executor = getDefaultCommandExecutor()`); + console.log( + `1. IMMEDIATELY fix ALL handler signature violations in ${filteredHandlerResults.length} files`, + ); + console.log( + `2. Tools: Handler must be: async handler(args: Record): Promise`, + ); + console.log( + `3. Resources: Handler must be: async handler(uri: URL): Promise<{ contents: Array<{ text: string }> }>`, + ); + console.log( + `4. Inject dependencies INSIDE handler body: const executor = getDefaultCommandExecutor()`, + ); console.log(`5. Run this script again after each fix to track progress`); console.log(''); } @@ -799,7 +947,7 @@ function main() { if (!hasViolations && mixed.length === 0) { console.log(`🎉 ALL FILES COMPLY WITH PROJECT STANDARDS!`); console.log(`==========================================`); - console.log(`✅ All files use ONLY createMockExecutor() and createMockFileSystemExecutor()`); + console.log(`✅ External boundary tests use injected executors/filesystem dependencies`); console.log(`✅ All files follow TypeScript best practices (no unsafe casts)`); console.log(`✅ All handler signatures comply with MCP SDK requirements`); console.log(`✅ All utilities properly use CommandExecutor dependency injection`); @@ -810,4 +958,4 @@ function main() { process.exit(hasViolations || mixed.length > 0 ? 1 : 0); } -main(); \ No newline at end of file +main(); diff --git a/scripts/check-docs-cli-commands.js b/scripts/check-docs-cli-commands.js new file mode 100755 index 00000000..3498c831 --- /dev/null +++ b/scripts/check-docs-cli-commands.js @@ -0,0 +1,188 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process'; +import { existsSync, readdirSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(scriptDir, '..'); +const cliPath = path.join(repoRoot, 'build', 'cli.js'); + +function fail(message, detail) { + console.error(`\n❌ ${message}`); + if (detail) { + console.error(detail); + } + process.exit(1); +} + +function loadToolCatalog() { + if (!existsSync(cliPath)) { + fail( + 'Missing build artifact: build/cli.js', + 'Run `npm run build:tsup` before `npm run docs:check`.', + ); + } + + const result = spawnSync(process.execPath, [cliPath, 'tools', '--json'], { + cwd: repoRoot, + encoding: 'utf8', + }); + + if (result.status !== 0) { + fail('Failed to load CLI tool catalog from build artifact.', result.stderr || result.stdout); + } + + try { + return JSON.parse(result.stdout); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown JSON parse error'; + fail('Could not parse JSON from `node build/cli.js tools --json`.', message); + } +} + +function getConsumerDocs() { + const docsDir = path.join(repoRoot, 'docs'); + const docsFiles = readdirSync(docsDir, { withFileTypes: true }) + .filter((entry) => entry.isFile() && entry.name.endsWith('.md')) + .map((entry) => path.join(docsDir, entry.name)) + .sort(); + + return [path.join(repoRoot, 'README.md'), path.join(repoRoot, 'CHANGELOG.md'), ...docsFiles]; +} + +function buildValidationSets(catalog) { + const validPairs = new Set(); + const validWorkflows = new Set(); + + if (!Array.isArray(catalog.workflows)) { + fail('Tool catalog does not contain a workflows array.'); + } + + for (const workflow of catalog.workflows) { + if (!workflow || typeof workflow.workflow !== 'string') { + continue; + } + + validWorkflows.add(workflow.workflow); + + if (!Array.isArray(workflow.tools)) { + continue; + } + + for (const tool of workflow.tools) { + if (tool && typeof tool.name === 'string') { + validPairs.add(`${workflow.workflow} ${tool.name}`); + } + } + } + + return { validPairs, validWorkflows }; +} + +function extractCommandCandidates(content) { + const lines = content.split(/\r?\n/u); + const candidates = []; + const inlineCodeRegex = /`([^`\n]+)`/g; + const fenceHeaderRegex = /^\s*(?:```|~~~)([a-z0-9_-]*)\s*$/iu; + const codeFenceLanguages = new Set(['', 'bash', 'sh', 'zsh', 'shell', 'console']); + + let inFence = false; + let shouldScanFence = false; + + for (let lineNumber = 1; lineNumber <= lines.length; lineNumber += 1) { + const line = lines[lineNumber - 1]; + const fenceMatch = line.match(fenceHeaderRegex); + + if (fenceMatch) { + if (!inFence) { + inFence = true; + shouldScanFence = codeFenceLanguages.has(fenceMatch[1].toLowerCase()); + } else { + inFence = false; + shouldScanFence = false; + } + continue; + } + + if (inFence) { + if (shouldScanFence) { + candidates.push({ lineNumber, text: line }); + } + continue; + } + + inlineCodeRegex.lastIndex = 0; + let inlineMatch = inlineCodeRegex.exec(line); + while (inlineMatch) { + candidates.push({ lineNumber, text: inlineMatch[1] }); + inlineMatch = inlineCodeRegex.exec(line); + } + } + + return candidates; +} + +function findInvalidCommands(files, validPairs, validWorkflows) { + const validTopLevel = new Set(['mcp', 'tools', 'daemon']); + const validDaemonActions = new Set(['status', 'start', 'stop', 'restart', 'list']); + const findings = []; + + const commandRegex = + /(?:^|[^a-z0-9-])xcodebuildmcp(?!-)\s+([a-z][a-z0-9-]*)(?:\s+([a-z][a-z0-9-]*))?/g; + + for (const absoluteFilePath of files) { + const relativePath = path.relative(repoRoot, absoluteFilePath) || absoluteFilePath; + const content = readFileSync(absoluteFilePath, 'utf8'); + const candidates = extractCommandCandidates(content); + + for (const candidate of candidates) { + const { lineNumber, text } = candidate; + commandRegex.lastIndex = 0; + let match = commandRegex.exec(text); + + while (match) { + const first = match[1]; + const second = match[2]; + const command = second ? `${first} ${second}` : first; + + let valid = false; + + if (!second) { + valid = validTopLevel.has(first) || validWorkflows.has(first); + } else if (first === 'daemon') { + valid = validDaemonActions.has(second); + } else { + valid = validPairs.has(command); + } + + if (!valid) { + findings.push(`${relativePath}:${lineNumber}: ${command}`); + } + + match = commandRegex.exec(text); + } + } + } + + return findings; +} + +function main() { + const catalog = loadToolCatalog(); + const files = getConsumerDocs(); + const { validPairs, validWorkflows } = buildValidationSets(catalog); + const findings = findInvalidCommands(files, validPairs, validWorkflows); + + if (findings.length > 0) { + fail( + 'Found invalid CLI command references in consumer docs.', + `${findings.join('\n')}\n\nRun \`node build/cli.js tools\` to inspect valid commands.`, + ); + } + + console.log('✅ Docs CLI command check passed (README.md + CHANGELOG.md + docs/*.md).'); +} + +main(); diff --git a/scripts/copy-build-assets.js b/scripts/copy-build-assets.js new file mode 100644 index 00000000..b1c14038 --- /dev/null +++ b/scripts/copy-build-assets.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node +/** + * Post-build script to copy assets and set permissions. + * Called after tsc compilation to prepare the build directory. + */ + +import { chmodSync, existsSync, copyFileSync, mkdirSync } from 'fs'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const projectRoot = join(__dirname, '..'); + +// Set executable permissions for entry points +const executables = ['build/cli.js', 'build/doctor-cli.js', 'build/daemon.js']; + +for (const file of executables) { + const fullPath = join(projectRoot, file); + if (existsSync(fullPath)) { + chmodSync(fullPath, '755'); + console.log(` Set executable: ${file}`); + } +} + +// Copy tools-manifest.json to build directory (for backward compatibility) +// This can be removed once Phase 7 is complete +const toolsManifestSrc = join(projectRoot, 'build', 'tools-manifest.json'); +if (existsSync(toolsManifestSrc)) { + console.log(' tools-manifest.json already in build/'); +} + +console.log('✅ Build assets copied successfully'); diff --git a/scripts/create-homebrew-formula.sh b/scripts/create-homebrew-formula.sh new file mode 100755 index 00000000..1b4a6706 --- /dev/null +++ b/scripts/create-homebrew-formula.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +set -euo pipefail + +VERSION="" +ARM64_SHA="" +X64_SHA="" +OUT_PATH="" +BASE_URL="" + +usage() { + cat <<'EOF' +Usage: + scripts/create-homebrew-formula.sh --version --arm64-sha --x64-sha [--base-url ] [--out ] +EOF +} + +require_arg_value() { + local flag_name="$1" + local value="${2:-}" + if [[ -z "$value" || "$value" == -* ]]; then + echo "Error: $flag_name requires a value" + usage + exit 1 + fi +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --version) + require_arg_value "--version" "${2:-}" + VERSION="$2" + shift 2 + ;; + --arm64-sha) + require_arg_value "--arm64-sha" "${2:-}" + ARM64_SHA="$2" + shift 2 + ;; + --x64-sha) + require_arg_value "--x64-sha" "${2:-}" + X64_SHA="$2" + shift 2 + ;; + --base-url) + require_arg_value "--base-url" "${2:-}" + BASE_URL="$2" + shift 2 + ;; + --out) + require_arg_value "--out" "${2:-}" + OUT_PATH="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +if [[ -z "$VERSION" || -z "$ARM64_SHA" || -z "$X64_SHA" ]]; then + usage + exit 1 +fi + +if [[ -z "$BASE_URL" ]]; then + BASE_URL="https://github.com/getsentry/XcodeBuildMCP/releases/download/v$VERSION" +fi + +FORMULA_CONTENT="$(cat < "$OUT_PATH" +else + printf "%s\n" "$FORMULA_CONTENT" +fi diff --git a/scripts/generate-github-release-notes.mjs b/scripts/generate-github-release-notes.mjs new file mode 100644 index 00000000..88d8e6e8 --- /dev/null +++ b/scripts/generate-github-release-notes.mjs @@ -0,0 +1,199 @@ +#!/usr/bin/env node + +import { readFile, writeFile } from 'node:fs/promises'; +import process from 'node:process'; + +const VERSION_HEADING_REGEX = /^##\s+\[([^\]]+)\](?:\s+-\s+.*)?\s*$/; + +function normalizeVersion(value) { + return value.trim().replace(/^v/, ''); +} + +function parseArgs(argv) { + const args = { + changelog: 'CHANGELOG.md', + out: '', + packageName: 'xcodebuildmcp', + version: '', + }; + + for (let i = 0; i < argv.length; i += 1) { + const arg = argv[i]; + const next = argv[i + 1]; + + if (arg === '--version') { + if (!next) { + throw new Error('Missing value for --version'); + } + args.version = next; + i += 1; + continue; + } + + if (arg === '--changelog') { + if (!next) { + throw new Error('Missing value for --changelog'); + } + args.changelog = next; + i += 1; + continue; + } + + if (arg === '--out') { + if (!next) { + throw new Error('Missing value for --out'); + } + args.out = next; + i += 1; + continue; + } + + if (arg === '--package') { + if (!next) { + throw new Error('Missing value for --package'); + } + args.packageName = next; + i += 1; + continue; + } + + if (arg === '--help' || arg === '-h') { + printHelp(); + process.exit(0); + } + + throw new Error(`Unknown argument: ${arg}`); + } + + if (!args.version) { + throw new Error('Missing required argument: --version'); + } + + return args; +} + +function printHelp() { + console.log(`Generate GitHub release notes from CHANGELOG.md. + +Usage: + node scripts/generate-github-release-notes.mjs --version [options] + +Options: + --version Required release version (e.g. 2.0.0 or 2.0.0-beta.1) + --changelog Changelog path (default: CHANGELOG.md) + --out Output file path (default: stdout) + --package Package name for install snippets (default: xcodebuildmcp) + -h, --help Show this help +`); +} + +function extractChangelogSection(changelog, version) { + const normalizedTarget = normalizeVersion(version); + const lines = changelog.split(/\r?\n/); + let sectionStartLine = -1; + + for (let index = 0; index < lines.length; index += 1) { + const match = lines[index].match(VERSION_HEADING_REGEX); + if (!match) { + continue; + } + + if (normalizeVersion(match[1]) === normalizedTarget) { + sectionStartLine = index + 1; + break; + } + } + + if (sectionStartLine === -1) { + throw new Error( + `Missing CHANGELOG section for version: ${normalizedTarget}\n` + + `Add a heading like: ## [${normalizedTarget}] (or ## [v${normalizedTarget}] - YYYY-MM-DD)`, + ); + } + + let sectionEndLine = lines.length; + for (let index = sectionStartLine; index < lines.length; index += 1) { + if (VERSION_HEADING_REGEX.test(lines[index])) { + sectionEndLine = index; + break; + } + } + + const section = lines.slice(sectionStartLine, sectionEndLine).join('\n').trim(); + if (!section) { + throw new Error(`CHANGELOG section for version ${normalizedTarget} is empty`); + } + + return section; +} + +function buildInstallAndSetupSection(version, packageName) { + const normalizedVersion = normalizeVersion(version); + return [ + '### Option A — Homebrew (no Node.js required)', + '', + 'Install:', + '```bash', + `brew tap getsentry/${packageName}`, + `brew install ${packageName}`, + '```', + '', + 'MCP config:', + '```json', + '"XcodeBuildMCP": {', + ` "command": "${packageName}",`, + ' "args": ["mcp"]', + '}', + '```', + '', + '### Option B — npm / npx (Node.js 18+)', + '', + 'Install:', + '```bash', + `npm install -g ${packageName}@latest`, + '```', + '', + 'MCP config:', + '```json', + '"XcodeBuildMCP": {', + ' "command": "npx",', + ` "args": ["-y", "${packageName}@latest", "mcp"]`, + '}', + '```', + '', + `📦 **NPM Package**: https://www.npmjs.com/package/${packageName}/v/${normalizedVersion}`, + ].join('\n'); +} + +function buildReleaseBody(version, changelogSection, packageName) { + const normalizedVersion = normalizeVersion(version); + const installAndSetup = buildInstallAndSetupSection(normalizedVersion, packageName); + return [`## Release v${normalizedVersion}`, '', changelogSection, '', installAndSetup, ''].join( + '\n', + ); +} + +async function main() { + try { + const { changelog, out, packageName, version } = parseArgs(process.argv.slice(2)); + const changelogContent = await readFile(changelog, 'utf8').catch(() => { + throw new Error(`Could not read CHANGELOG.md at ${changelog}`); + }); + + const section = extractChangelogSection(changelogContent, version); + const body = buildReleaseBody(version, section, packageName); + + if (out) { + await writeFile(out, body, 'utf8'); + return; + } + + process.stdout.write(body); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + process.stderr.write(`❌ ${message}\n`); + process.exit(1); + } +} + +await main(); diff --git a/scripts/generate-loaders.ts b/scripts/generate-loaders.ts deleted file mode 100644 index 9f36dc1c..00000000 --- a/scripts/generate-loaders.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { generateResourceLoaders, generateWorkflowLoaders } from '../build-plugins/plugin-discovery.ts'; - -async function main(): Promise { - await generateWorkflowLoaders(); - await generateResourceLoaders(); -} - -main().catch((error) => { - console.error('Failed to generate plugin/resource loaders:', error); - process.exit(1); -}); diff --git a/scripts/install-git-hooks.js b/scripts/install-git-hooks.js new file mode 100755 index 00000000..670bd477 --- /dev/null +++ b/scripts/install-git-hooks.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(scriptDir, '..'); + +function runGit(args) { + return spawnSync('git', args, { + cwd: repoRoot, + encoding: 'utf8', + }); +} + +const insideWorkTree = runGit(['rev-parse', '--is-inside-work-tree']); +if (insideWorkTree.status !== 0 || insideWorkTree.stdout.trim() !== 'true') { + console.log('[hooks] Skipping git hook install (not inside a git worktree).'); + process.exit(0); +} + +const setHookPath = runGit(['config', 'core.hooksPath', '.githooks']); +if (setHookPath.status !== 0) { + const output = (setHookPath.stderr || setHookPath.stdout || '').trim(); + console.error('[hooks] Failed to set core.hooksPath to .githooks'); + if (output) { + console.error(output); + } + process.exit(1); +} + +console.log('[hooks] Installed git hooks path: .githooks'); diff --git a/scripts/install-skill.sh b/scripts/install-skill.sh new file mode 100755 index 00000000..1d9e1c18 --- /dev/null +++ b/scripts/install-skill.sh @@ -0,0 +1,310 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Colors and formatting +if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]]; then + BOLD='\033[1m' + DIM='\033[2m' + RESET='\033[0m' + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[0;33m' + BLUE='\033[0;34m' + CYAN='\033[0;36m' +else + BOLD='' + DIM='' + RESET='' + RED='' + GREEN='' + YELLOW='' + BLUE='' + CYAN='' +fi + +# Symbols +CHECK="${GREEN}✓${RESET}" +CROSS="${RED}✗${RESET}" +ARROW="${CYAN}→${RESET}" +WARN="${YELLOW}!${RESET}" + +print_header() { + printf "\n" + printf "${BOLD}${BLUE}╭──────────────────────────────────╮${RESET}\n" + printf "${BOLD}${BLUE}│${RESET} ${BOLD}XcodeBuildMCP Skill Installer${RESET} ${BOLD}${BLUE}│${RESET}\n" + printf "${BOLD}${BLUE}╰──────────────────────────────────╯${RESET}\n" +} + +print_success() { + printf " ${CHECK} ${GREEN}%s${RESET}\n" "$1" +} + +print_error() { + printf " ${CROSS} ${RED}%s${RESET}\n" "$1" >&2 +} + +print_warning() { + printf " ${WARN} ${YELLOW}%s${RESET}\n" "$1" +} + +print_info() { + printf " ${ARROW} %s\n" "$1" +} + +print_step() { + printf "\n${BOLD}%s${RESET}\n" "$1" +} + +usage() { + cat < [options] + +${BOLD}Options:${RESET} + --codex Install to Codex skills directory + --claude Install to Claude skills directory + --cursor Install to Cursor skills directory + --dest Install to custom directory + --skill Skill to install (prompted if omitted) + --ref Git ref to download from (default: main) + --remove-conflict Auto-remove conflicting skill + -h, --help Show this help message + +${BOLD}Description:${RESET} + Installs the XcodeBuildMCP skill for your AI coding assistant. + If run from a local checkout, installs the local skill file. + Otherwise downloads from GitHub. +EOF +} + +destination="" +skill_choice="" +skill_ref_override="" +remove_conflict="false" +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "${script_dir}/.." && pwd)" + +while [[ $# -gt 0 ]]; do + case "$1" in + --codex) + destination="${HOME}/.codex/skills/public" + shift + ;; + --claude) + destination="${HOME}/.claude/skills" + shift + ;; + --cursor) + destination="${HOME}/.cursor/skills" + shift + ;; + --dest) + if [[ $# -lt 2 ]]; then + echo "Missing value for --dest" >&2 + usage + exit 1 + fi + destination="$2" + shift 2 + ;; + --skill) + if [[ $# -lt 2 ]]; then + echo "Missing value for --skill" >&2 + usage + exit 1 + fi + skill_choice="$2" + shift 2 + ;; + --ref) + if [[ $# -lt 2 ]]; then + echo "Missing value for --ref" >&2 + usage + exit 1 + fi + skill_ref_override="$2" + shift 2 + ;; + --remove-conflict) + remove_conflict="true" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + esac +done + +prompt_for_destination() { + print_step "Select Target Client" + while true; do + printf "\n" + printf " ${CYAN}[1]${RESET} Codex\n" + printf " ${CYAN}[2]${RESET} Claude\n" + printf " ${CYAN}[3]${RESET} Cursor\n" + printf "\n" + printf " ${DIM}Enter your choice${RESET} ${BOLD}[1-3]:${RESET} " + read -r selection + case "${selection}" in + 1) + destination="${HOME}/.codex/skills/public" + print_success "Selected Codex" + return 0 + ;; + 2) + destination="${HOME}/.claude/skills" + print_success "Selected Claude" + return 0 + ;; + 3) + destination="${HOME}/.cursor/skills" + print_success "Selected Cursor" + return 0 + ;; + *) + print_error "Invalid selection. Please enter 1, 2, or 3." + ;; + esac + done +} + +prompt_for_skill() { + print_step "Select Skill Type" + while true; do + printf "\n" + printf " ${CYAN}[1]${RESET} XcodeBuildMCP ${DIM}(MCP server)${RESET}\n" + printf " ${DIM}Full MCP integration with all tools${RESET}\n" + printf "\n" + printf " ${CYAN}[2]${RESET} XcodeBuildMCP CLI\n" + printf " ${DIM}Lightweight CLI-based commands${RESET}\n" + printf "\n" + printf " ${DIM}Enter your choice${RESET} ${BOLD}[1-2]:${RESET} " + read -r selection + case "${selection}" in + 1) + skill_choice="mcp" + print_success "Selected MCP server skill" + return 0 + ;; + 2) + skill_choice="cli" + print_success "Selected CLI skill" + return 0 + ;; + *) + print_error "Invalid selection. Please enter 1 or 2." + ;; + esac + done +} + +print_header + +if [[ -z "${destination}" ]]; then + prompt_for_destination +fi + +if [[ -z "${skill_choice}" ]]; then + prompt_for_skill +fi + +case "${skill_choice}" in + mcp|server|xcodebuildmcp) + skill_dir_name="xcodebuildmcp" + skill_label="XcodeBuildMCP (MCP server)" + alt_dir_name="xcodebuildmcp-cli" + alt_label="XcodeBuildMCP CLI" + ;; + cli|xcodebuildmcp-cli) + skill_dir_name="xcodebuildmcp-cli" + skill_label="XcodeBuildMCP CLI" + alt_dir_name="xcodebuildmcp" + alt_label="XcodeBuildMCP (MCP server)" + ;; + *) + echo "Unknown skill: ${skill_choice}" >&2 + usage + exit 1 + ;; +esac + +skill_dir="${destination}/${skill_dir_name}" +alt_dir="${destination}/${alt_dir_name}" +skill_path="skills/${skill_dir_name}/SKILL.md" +skill_base_url="https://raw.githubusercontent.com/getsentry/XcodeBuildMCP" +skill_ref="main" + +if [[ -n "${skill_ref_override}" ]]; then + skill_ref="${skill_ref_override}" +fi + +print_step "Installing" + +if [[ -e "${alt_dir}" ]]; then + if [[ "${remove_conflict}" == "true" ]]; then + rm -r "${alt_dir}" + print_info "Removed conflicting skill: ${alt_label}" + else + printf "\n" + print_warning "Conflict detected!" + printf " ${DIM}Only one skill can be installed at a time.${RESET}\n" + printf " ${DIM}Found:${RESET} ${alt_label}\n" + printf " ${DIM}Path:${RESET} ${alt_dir}\n" + printf "\n" + printf " ${BOLD}Remove existing skill to continue?${RESET} ${DIM}[y/N]:${RESET} " + read -r confirm + case "${confirm}" in + y|Y|yes|YES) + rm -r "${alt_dir}" + print_success "Removed ${alt_label}" + ;; + *) + print_error "Installation cancelled" + exit 1 + ;; + esac + fi +fi + +if [[ -e "${skill_dir}" ]]; then + rm -r "${skill_dir}" + print_info "Replacing existing installation" +fi +mkdir -p "${skill_dir}" + +primary_url="${skill_base_url}/${skill_ref}/${skill_path}" +fallback_url="${skill_base_url}/main/${skill_path}" +local_skill_path="${repo_root}/${skill_path}" + +if [[ -f "${local_skill_path}" ]]; then + cp "${local_skill_path}" "${skill_dir}/SKILL.md" + print_info "Installed from local checkout" +else + print_info "Downloading from GitHub..." + if ! curl -fsSL "${primary_url}" -o "${skill_dir}/SKILL.md" 2>/dev/null; then + if [[ "${skill_ref}" != "main" ]]; then + print_warning "Tag ${skill_ref} not found, falling back to main" + if ! curl -fsSL "${fallback_url}" -o "${skill_dir}/SKILL.md" 2>/dev/null; then + print_error "Failed to download skill" + exit 1 + fi + else + print_error "Failed to download skill" + exit 1 + fi + fi +fi + +printf "\n" +printf "${BOLD}${GREEN}╭─────────────────────────────────────╮${RESET}\n" +printf "${BOLD}${GREEN}│${RESET} ${CHECK} ${BOLD}Installation Complete${RESET} ${BOLD}${GREEN}│${RESET}\n" +printf "${BOLD}${GREEN}╰─────────────────────────────────────╯${RESET}\n" +printf "\n" +printf " ${BOLD}Skill:${RESET} %s\n" "${skill_label}" +printf " ${BOLD}Location:${RESET} %s\n" "${skill_dir}" +printf "\n" diff --git a/scripts/package-macos-portable.sh b/scripts/package-macos-portable.sh new file mode 100755 index 00000000..6d3161ed --- /dev/null +++ b/scripts/package-macos-portable.sh @@ -0,0 +1,372 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +DIST_DIR_DEFAULT="$PROJECT_ROOT/dist/portable" + +ARCH="" +UNIVERSAL=false +ARM64_ROOT="" +X64_ROOT="" +DIST_DIR="$DIST_DIR_DEFAULT" +VERSION="" + +usage() { + cat <<'EOF' +Usage: + scripts/package-macos-portable.sh [--arch arm64|x64] [--dist-dir ] [--version ] + scripts/package-macos-portable.sh --universal --arm64-root --x64-root [--dist-dir ] [--version ] + +Notes: + - Arch mode packages a bundled Node runtime plus compiled JS entrypoint. + - Universal mode expects prebuilt arm64/x64 roots and combines Node runtimes with lipo. +EOF +} + +require_arg_value() { + local flag_name="$1" + local value="${2:-}" + if [[ -z "$value" || "$value" == -* ]]; then + echo "Missing value for $flag_name" + usage + exit 1 + fi +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --arch) + require_arg_value "--arch" "${2:-}" + ARCH="$2" + shift 2 + ;; + --universal) + UNIVERSAL=true + shift + ;; + --arm64-root) + require_arg_value "--arm64-root" "${2:-}" + ARM64_ROOT="$2" + shift 2 + ;; + --x64-root) + require_arg_value "--x64-root" "${2:-}" + X64_ROOT="$2" + shift 2 + ;; + --dist-dir) + require_arg_value "--dist-dir" "${2:-}" + DIST_DIR="$2" + shift 2 + ;; + --version) + require_arg_value "--version" "${2:-}" + VERSION="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +if [[ -z "$VERSION" ]]; then + VERSION="$(node -p "require('$PROJECT_ROOT/package.json').version")" +fi + +mkdir -p "$DIST_DIR" + +verify_axe_assets() { + local bundled_dir="$PROJECT_ROOT/bundled" + local axe_bin="$bundled_dir/axe" + local frameworks_dir="$bundled_dir/Frameworks" + + if [[ ! -x "$axe_bin" ]]; then + echo "Missing executable AXe binary at $axe_bin" + exit 1 + fi + if [[ ! -d "$frameworks_dir" ]]; then + echo "Missing AXe frameworks at $frameworks_dir" + exit 1 + fi + if [[ "$(find "$frameworks_dir" -name "*.framework" -type d | wc -l | tr -d ' ')" -eq 0 ]]; then + echo "No frameworks found under $frameworks_dir" + exit 1 + fi + + if [[ "$(uname -s)" == "Darwin" ]]; then + while IFS= read -r framework_path; do + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [[ ! -f "$framework_binary" ]]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + if [[ ! -f "$framework_binary" ]]; then + echo "Missing framework binary at $framework_binary" + exit 1 + fi + done < <(find "$frameworks_dir" -name "*.framework" -type d) + + if codesign -dv "$axe_bin" >/dev/null 2>&1; then + local codesign_details + codesign_details="$(codesign -dv "$axe_bin" 2>&1 || true)" + local is_ad_hoc_signature=false + if grep -qi "Signature=adhoc" <<<"$codesign_details"; then + is_ad_hoc_signature=true + fi + + codesign --verify --deep --strict "$axe_bin" + while IFS= read -r framework_path; do + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [[ ! -f "$framework_binary" ]]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + codesign --verify --deep --strict "$framework_binary" + done < <(find "$frameworks_dir" -name "*.framework" -type d) + + if [[ "$is_ad_hoc_signature" == "true" ]]; then + echo "AXe binary uses ad-hoc signing; skipping Gatekeeper assessment" + else + spctl_log="$(mktemp)" + if ! spctl --assess --type execute "$axe_bin" 2>"$spctl_log"; then + if grep -q "does not seem to be an app" "$spctl_log"; then + echo "Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" + else + cat "$spctl_log" + rm "$spctl_log" + exit 1 + fi + fi + rm "$spctl_log" + fi + else + echo "AXe binary is unsigned; skipping codesign and Gatekeeper verification" + fi + + if ! DYLD_FRAMEWORK_PATH="$frameworks_dir" "$axe_bin" --version >/dev/null 2>&1; then + echo "Bundled AXe runtime execution check failed" + exit 1 + fi + fi +} + +install_node_runtime_for_arch() { + local target_arch="$1" + local output_path="$2" + local node_version="${NODE_RUNTIME_VERSION:-$(node -p "process.versions.node")}" + local node_arch="" + + case "$target_arch" in + arm64) + node_arch="arm64" + ;; + x64) + node_arch="x64" + ;; + *) + echo "Unsupported target arch for Node runtime: $target_arch" + exit 1 + ;; + esac + + local archive_name="node-v${node_version}-darwin-${node_arch}.tar.gz" + local checksums_name="SHASUMS256.txt" + local download_url="https://nodejs.org/dist/v${node_version}/${archive_name}" + local checksums_url="https://nodejs.org/dist/v${node_version}/${checksums_name}" + local temp_dir + temp_dir="$(mktemp -d)" + cleanup_temp_dir() { + if [[ -d "$temp_dir" ]]; then + rm -r "$temp_dir" + fi + } + trap cleanup_temp_dir RETURN + + curl -fLsS "$download_url" -o "$temp_dir/$archive_name" + curl -fLsS "$checksums_url" -o "$temp_dir/$checksums_name" + + local expected_sha + expected_sha="$(awk -v target="$archive_name" '$2 == target {print $1}' "$temp_dir/$checksums_name")" + if [[ -z "$expected_sha" ]]; then + echo "Unable to find checksum for $archive_name in ${checksums_name}" + exit 1 + fi + + local actual_sha + actual_sha="$(shasum -a 256 "$temp_dir/$archive_name" | awk '{print $1}')" + if [[ "$actual_sha" != "$expected_sha" ]]; then + echo "Node runtime checksum mismatch for $archive_name" + echo "Expected: $expected_sha" + echo "Actual: $actual_sha" + exit 1 + fi + + tar -xzf "$temp_dir/$archive_name" -C "$temp_dir" + + local extracted_node="$temp_dir/node-v${node_version}-darwin-${node_arch}/bin/node" + if [[ ! -x "$extracted_node" ]]; then + echo "Failed to locate extracted Node runtime at $extracted_node" + exit 1 + fi + + cp "$extracted_node" "$output_path" + chmod +x "$output_path" +} + +write_wrapper_scripts() { + local root="$1" + local bin_dir="$root/bin" + local libexec_dir="$root/libexec" + + cat > "$libexec_dir/_resolve-resource-root.sh" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +SOURCE="${BASH_SOURCE[0]}" +while [[ -L "$SOURCE" ]]; do + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" +RESOURCE_ROOT="$(cd "$SCRIPT_DIR" && pwd)" +export XCODEBUILDMCP_RESOURCE_ROOT="$RESOURCE_ROOT" +export DYLD_FRAMEWORK_PATH="$RESOURCE_ROOT/bundled/Frameworks${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH}" +EOF + + cat > "$libexec_dir/xcodebuildmcp" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$ROOT/node-runtime" "$ROOT/build/cli.js" "$@" +EOF + + cat > "$bin_dir/xcodebuildmcp" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +SOURCE="${BASH_SOURCE[0]}" +while [[ -L "$SOURCE" ]]; do + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" +source "$SCRIPT_DIR/../libexec/_resolve-resource-root.sh" +exec "$RESOURCE_ROOT/xcodebuildmcp" "$@" +EOF + + cat > "$bin_dir/xcodebuildmcp-doctor" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +SOURCE="${BASH_SOURCE[0]}" +while [[ -L "$SOURCE" ]]; do + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" +source "$SCRIPT_DIR/../libexec/_resolve-resource-root.sh" +exec "$RESOURCE_ROOT/xcodebuildmcp" doctor "$@" +EOF + + chmod +x \ + "$libexec_dir/_resolve-resource-root.sh" \ + "$libexec_dir/xcodebuildmcp" \ + "$bin_dir/xcodebuildmcp" \ + "$bin_dir/xcodebuildmcp-doctor" +} + +create_tarball_and_checksum() { + local portable_root="$1" + local artifact_name="$2" + local tarball_path="$DIST_DIR/$artifact_name.tar.gz" + local checksum_path="$tarball_path.sha256" + + ( + cd "$(dirname "$portable_root")" + tar -czf "$tarball_path" "$(basename "$portable_root")" + ) + shasum -a 256 "$tarball_path" | awk '{print $1}' > "$checksum_path" + echo "Created artifact: $tarball_path" + echo "Created checksum: $checksum_path" +} + +if [[ "$UNIVERSAL" == "true" ]]; then + if [[ -z "$ARM64_ROOT" || -z "$X64_ROOT" ]]; then + echo "--universal requires --arm64-root and --x64-root" + exit 1 + fi + if [[ ! -x "$ARM64_ROOT/libexec/node-runtime" || ! -x "$X64_ROOT/libexec/node-runtime" ]]; then + echo "Missing per-arch node runtimes under provided roots" + exit 1 + fi + + UNIVERSAL_ROOT="$DIST_DIR/xcodebuildmcp-$VERSION-darwin-universal" + if [[ -d "$UNIVERSAL_ROOT" ]]; then + rm -r "$UNIVERSAL_ROOT" + fi + mkdir -p "$UNIVERSAL_ROOT/bin" "$UNIVERSAL_ROOT/libexec" + cp -R "$ARM64_ROOT/libexec/build" "$UNIVERSAL_ROOT/libexec/" + cp -R "$ARM64_ROOT/libexec/manifests" "$UNIVERSAL_ROOT/libexec/" + cp -R "$ARM64_ROOT/libexec/bundled" "$UNIVERSAL_ROOT/libexec/" + cp -R "$ARM64_ROOT/libexec/node_modules" "$UNIVERSAL_ROOT/libexec/" + cp "$ARM64_ROOT/libexec/package.json" "$UNIVERSAL_ROOT/libexec/package.json" + + lipo -create \ + "$ARM64_ROOT/libexec/node-runtime" \ + "$X64_ROOT/libexec/node-runtime" \ + -output "$UNIVERSAL_ROOT/libexec/node-runtime" + chmod +x "$UNIVERSAL_ROOT/libexec/node-runtime" + + write_wrapper_scripts "$UNIVERSAL_ROOT" + create_tarball_and_checksum "$UNIVERSAL_ROOT" "xcodebuildmcp-$VERSION-darwin-universal" + exit 0 +fi + +if [[ -z "$ARCH" ]]; then + machine_arch="$(uname -m)" + if [[ "$machine_arch" == "arm64" ]]; then + ARCH="arm64" + elif [[ "$machine_arch" == "x86_64" ]]; then + ARCH="x64" + else + echo "Unsupported machine architecture: $machine_arch" + exit 1 + fi +fi + +if [[ "$ARCH" != "arm64" && "$ARCH" != "x64" ]]; then + echo "Unsupported arch: $ARCH (expected arm64 or x64)" + exit 1 +fi + +cd "$PROJECT_ROOT" +npm run build:tsup +AXE_FORCE_REMOTE=1 npm run bundle:axe +verify_axe_assets + +PORTABLE_ROOT="$DIST_DIR/xcodebuildmcp-$VERSION-darwin-$ARCH" +if [[ -d "$PORTABLE_ROOT" ]]; then + rm -r "$PORTABLE_ROOT" +fi +mkdir -p "$PORTABLE_ROOT/bin" "$PORTABLE_ROOT/libexec" + +install_node_runtime_for_arch "$ARCH" "$PORTABLE_ROOT/libexec/node-runtime" + +cp -R "$PROJECT_ROOT/build" "$PORTABLE_ROOT/libexec/" +cp -R "$PROJECT_ROOT/manifests" "$PORTABLE_ROOT/libexec/" +cp -R "$PROJECT_ROOT/bundled" "$PORTABLE_ROOT/libexec/" +cp "$PROJECT_ROOT/package.json" "$PORTABLE_ROOT/libexec/package.json" +cp "$PROJECT_ROOT/package-lock.json" "$PORTABLE_ROOT/libexec/package-lock.json" +npm ci --omit=dev --ignore-scripts --prefix "$PORTABLE_ROOT/libexec" + +write_wrapper_scripts "$PORTABLE_ROOT" +create_tarball_and_checksum "$PORTABLE_ROOT" "xcodebuildmcp-$VERSION-darwin-$ARCH" diff --git a/scripts/probe-xcode-mcpbridge.ts b/scripts/probe-xcode-mcpbridge.ts new file mode 100644 index 00000000..0d5714c5 --- /dev/null +++ b/scripts/probe-xcode-mcpbridge.ts @@ -0,0 +1,87 @@ +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; +import { CompatibilityCallToolResultSchema } from '@modelcontextprotocol/sdk/types.js'; +import process from 'node:process'; + +function parseArgs(argv: string[]): { limit: number; callWindows: boolean } { + let limit = 20; + let callWindows = true; + for (let i = 2; i < argv.length; i += 1) { + const arg = argv[i]; + if (arg === '--limit' && argv[i + 1]) { + limit = Number(argv[i + 1]); + i += 1; + continue; + } + if (arg === '--no-windows') { + callWindows = false; + continue; + } + } + return { limit: Number.isFinite(limit) ? limit : 20, callWindows }; +} + +function mapXcodeEnvForMcpBridge(env: NodeJS.ProcessEnv): Record { + const mapped: Record = {}; + + for (const [key, value] of Object.entries(env)) { + if (typeof value === 'string') { + mapped[key] = value; + } + } + + if (typeof env.XCODEBUILDMCP_XCODE_PID === 'string' && mapped.MCP_XCODE_PID === undefined) { + mapped.MCP_XCODE_PID = env.XCODEBUILDMCP_XCODE_PID; + } + if ( + typeof env.XCODEBUILDMCP_XCODE_SESSION_ID === 'string' && + mapped.MCP_XCODE_SESSION_ID === undefined + ) { + mapped.MCP_XCODE_SESSION_ID = env.XCODEBUILDMCP_XCODE_SESSION_ID; + } + + return mapped; +} + +async function main(): Promise { + const { limit, callWindows } = parseArgs(process.argv); + + const transport = new StdioClientTransport({ + command: 'xcrun', + args: ['mcpbridge'], + stderr: 'inherit', + env: mapXcodeEnvForMcpBridge(process.env), + }); + + const client = new Client({ name: 'xcodebuildmcp-probe', version: '0.0.0' }); + await client.connect(transport, { timeout: 15_000 }); + + const serverInfo = client.getServerVersion(); + const capabilities = client.getServerCapabilities(); + + console.log('serverInfo:', serverInfo); + console.log('capabilities.tools.listChanged:', capabilities?.tools?.listChanged ?? false); + + const toolsResult = await client.listTools(undefined, { timeout: 15_000 }); + console.log(`tools: ${toolsResult.tools.length}`); + console.log( + 'first tools:', + toolsResult.tools.slice(0, limit).map((t) => t.name), + ); + + if (callWindows) { + const windows = await client.request( + { method: 'tools/call', params: { name: 'XcodeListWindows', arguments: {} } }, + CompatibilityCallToolResultSchema, + { timeout: 15_000 }, + ); + console.log('XcodeListWindows:', windows); + } + + await client.close(); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/scripts/release.sh b/scripts/release.sh index f19bcf9d..f14a8e4b 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -270,22 +270,134 @@ sed_inplace() { fi } +prepare_changelog_for_release_notes() { + local source_path="$1" + local destination_path="$2" + local target_version="$3" + + node - "$source_path" "$destination_path" "$target_version" <<'NODE' +const fs = require('fs'); + +const [sourcePath, destinationPath, targetVersion] = process.argv.slice(2); +const versionHeadingRegex = /^##\s+\[([^\]]+)\](?:\s+-\s+.*)?\s*$/; +const normalizeVersion = (value) => value.trim().replace(/^v/, ''); + +try { + const changelog = fs.readFileSync(sourcePath, 'utf8'); + const lines = changelog.split(/\r?\n/); + const normalizedTargetVersion = normalizeVersion(targetVersion); + let firstHeadingIndex = -1; + let firstHeadingLabel = ''; + + for (let index = 0; index < lines.length; index += 1) { + const match = lines[index].match(versionHeadingRegex); + if (!match) { + continue; + } + + const label = match[1].trim(); + if (normalizeVersion(label) === normalizedTargetVersion) { + process.exit(3); + } + + if (firstHeadingIndex === -1) { + firstHeadingIndex = index; + firstHeadingLabel = label; + } + } + + if (firstHeadingIndex === -1 || firstHeadingLabel !== 'Unreleased') { + process.exit(3); + } + + lines[firstHeadingIndex] = lines[firstHeadingIndex].replace('[Unreleased]', `[${targetVersion}]`); + fs.writeFileSync(destinationPath, `${lines.join('\n')}\n`, 'utf8'); + process.exit(0); +} catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(`❌ Failed to prepare changelog for release notes: ${message}`); + process.exit(1); +} +NODE +} + # Ensure we're in the project root (parent of scripts directory) cd "$(dirname "$0")/.." -# Check if working directory is clean (only enforced for real runs) +# Files this script modifies and commits as part of the release +RELEASE_MANAGED_FILES=( + "CHANGELOG.md" + "package.json" + "package-lock.json" + "README.md" + "docs/SKILLS.md" + "docs/GETTING_STARTED.md" + "server.json" +) + +has_unmanaged_changes() { + local exclude_args=() + for f in "${RELEASE_MANAGED_FILES[@]}"; do + exclude_args+=(":(exclude)$f") + done + ! git diff-index --quiet HEAD -- . "${exclude_args[@]}" +} + +# Check if working directory is clean outside release-managed files if ! $DRY_RUN; then - if ! git diff-index --quiet HEAD --; then - echo "❌ Error: Working directory is not clean." - echo "Please commit or stash your changes before creating a release." + if has_unmanaged_changes; then + echo "❌ Error: Working directory has uncommitted changes outside release-managed files." + echo "Please commit or stash those changes before creating a release." exit 1 fi else - if ! git diff-index --quiet HEAD --; then - echo "⚠️ Dry-run: working directory is not clean (continuing)." + if has_unmanaged_changes; then + echo "⚠️ Dry-run: working directory has unmanaged changes (continuing)." + fi +fi + +CHANGELOG_PATH="CHANGELOG.md" +CHANGELOG_FOR_VALIDATION="$CHANGELOG_PATH" +CHANGELOG_VALIDATION_TEMP="" +CHANGELOG_RENAMED_ON_DISK=false + +if $DRY_RUN; then + CHANGELOG_VALIDATION_TEMP=$(mktemp "${TMPDIR:-/tmp}/xcodebuildmcp-changelog-validation.XXXXXX") + if prepare_changelog_for_release_notes "$CHANGELOG_PATH" "$CHANGELOG_VALIDATION_TEMP" "$VERSION"; then + CHANGELOG_FOR_VALIDATION="$CHANGELOG_VALIDATION_TEMP" + echo "ℹ️ Dry-run: prepared release changelog from [Unreleased] in a temp file." + else + PREPARE_STATUS=$? + if [[ $PREPARE_STATUS -eq 3 ]]; then + rm "$CHANGELOG_VALIDATION_TEMP" + CHANGELOG_VALIDATION_TEMP="" + else + rm "$CHANGELOG_VALIDATION_TEMP" + exit $PREPARE_STATUS + fi + fi +else + if prepare_changelog_for_release_notes "$CHANGELOG_PATH" "$CHANGELOG_PATH" "$VERSION"; then + CHANGELOG_RENAMED_ON_DISK=true + echo "📝 Renamed CHANGELOG heading [Unreleased] -> [$VERSION]" + else + PREPARE_STATUS=$? + if [[ $PREPARE_STATUS -ne 3 ]]; then + exit $PREPARE_STATUS + fi fi fi +echo "" +echo "🧾 Validating CHANGELOG release notes for v$VERSION..." +RELEASE_NOTES_TMP=$(mktemp "${TMPDIR:-/tmp}/xcodebuildmcp-release-notes.XXXXXX") +node scripts/generate-github-release-notes.mjs --version "$VERSION" --changelog "$CHANGELOG_FOR_VALIDATION" --out "$RELEASE_NOTES_TMP" +rm "$RELEASE_NOTES_TMP" +if [[ -n "$CHANGELOG_VALIDATION_TEMP" ]]; then + rm "$CHANGELOG_VALIDATION_TEMP" +fi +echo "✅ CHANGELOG entry found and release notes generated." + # Check if package.json already has this version (from previous attempt) CURRENT_PACKAGE_VERSION=$(node -p "require('./package.json').version") if [[ "$CURRENT_PACKAGE_VERSION" == "$VERSION" ]]; then @@ -296,6 +408,17 @@ else fi if [[ "$SKIP_VERSION_UPDATE" == "false" ]]; then + NPM_TAG="latest" + if [[ "$VERSION" == *"-"* ]]; then + PRERELEASE_TAG="${VERSION#*-}" + PRERELEASE_LABEL="${PRERELEASE_TAG%%.*}" + if [[ "$PRERELEASE_LABEL" == "alpha" ]]; then + NPM_TAG="alpha" + elif [[ "$PRERELEASE_LABEL" == "beta" ]]; then + NPM_TAG="beta" + fi + fi + # Version update echo "" echo "🔧 Setting version to $VERSION..." @@ -303,15 +426,29 @@ if [[ "$SKIP_VERSION_UPDATE" == "false" ]]; then # README update echo "" - echo "📝 Updating version in README.md..." - # Update version references in code examples using extended regex for precise semver matching - README_AT_SEMVER_REGEX='@[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?(-[a-zA-Z0-9]+\.[0-9]+)*(-[a-zA-Z0-9]+)?' - run sed_inplace "s/${README_AT_SEMVER_REGEX}/@${VERSION}/g" README.md - - # Update URL-encoded version references in shield links - echo "📝 Updating version in README.md shield links..." - README_URLENCODED_NPM_AT_SEMVER_REGEX='npm%3Axcodebuildmcp%40[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?(-[a-zA-Z0-9]+\.[0-9]+)*(-[a-zA-Z0-9]+)?' - run sed_inplace "s/${README_URLENCODED_NPM_AT_SEMVER_REGEX}/npm%3Axcodebuildmcp%40${VERSION}/g" README.md + echo "📝 Updating install tags in README.md and docs/GETTING_STARTED.md..." + README_AT_TAG_REGEX='xcodebuildmcp@([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?|latest|beta|alpha)' + README_URLENCODED_AT_TAG_REGEX='xcodebuildmcp%40([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?|latest|beta|alpha)' + run sed_inplace "s/${README_AT_TAG_REGEX}/xcodebuildmcp@${NPM_TAG}/g" README.md + run sed_inplace "s/${README_AT_TAG_REGEX}/xcodebuildmcp@${NPM_TAG}/g" docs/GETTING_STARTED.md + run sed_inplace "s/${README_URLENCODED_AT_TAG_REGEX}/xcodebuildmcp%40${NPM_TAG}/g" README.md + + echo "📝 Updating Cursor install link config in README.md..." + CURSOR_INSTALL_CONFIG=$(node -e "const tag='${NPM_TAG}';const config=JSON.stringify({command:\`npx -y xcodebuildmcp@\${tag} mcp\`});console.log(encodeURIComponent(Buffer.from(config).toString('base64')));") + run node -e "const fs=require('fs');const path='README.md';const next='config=${CURSOR_INSTALL_CONFIG}';const contents=fs.readFileSync(path,'utf8');const updated=contents.replace(/config=[^)\\s]+/g,next);fs.writeFileSync(path,updated);" + + # Update skill installer URL and versioned ref in README.md + echo "📝 Updating skill installer URL in README.md..." + README_SKILL_INSTALL_URL_REGEX='https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/[^/]+/scripts/install-skill.sh' + run sed_inplace "s#${README_SKILL_INSTALL_URL_REGEX}#https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v${VERSION}/scripts/install-skill.sh#g" README.md + + # Update skill installer URL in docs/SKILLS.md + if [[ -f docs/SKILLS.md ]]; then + echo "📝 Updating skill installer URL in docs/SKILLS.md..." + run sed_inplace "s#${README_SKILL_INSTALL_URL_REGEX}#https://raw.githubusercontent.com/getsentry/XcodeBuildMCP/v${VERSION}/scripts/install-skill.sh#g" docs/SKILLS.md + else + echo "⚠️ docs/SKILLS.md not found; skipping update" + fi # server.json update echo "" @@ -326,9 +463,9 @@ if [[ "$SKIP_VERSION_UPDATE" == "false" ]]; then echo "" echo "📦 Committing version changes..." if [[ -f server.json ]]; then - run git add package.json package-lock.json README.md server.json + run git add package.json package-lock.json README.md docs/SKILLS.md docs/GETTING_STARTED.md CHANGELOG.md server.json else - run git add package.json package-lock.json README.md + run git add package.json package-lock.json README.md docs/SKILLS.md docs/GETTING_STARTED.md CHANGELOG.md fi run git commit -m "Release v$VERSION" else @@ -343,6 +480,12 @@ else run git commit -m "Align server.json for v$VERSION" fi fi + + if $CHANGELOG_RENAMED_ON_DISK; then + echo "📝 Committing changelog release heading update..." + run git add CHANGELOG.md + run git commit -m "Finalize changelog for v$VERSION" + fi fi # Create or recreate tag at current HEAD @@ -365,11 +508,16 @@ echo "" echo "⏳ Monitoring GitHub Actions workflow..." echo "This may take a few minutes..." -# Wait for workflow to start -sleep 5 - -# Get the workflow run ID for this tag -RUN_ID=$(gh run list --workflow=release.yml --limit=1 --json databaseId --jq '.[0].databaseId') +# Poll for the workflow run triggered by this tag (may take a few seconds to appear) +RUN_ID="" +for i in $(seq 1 12); do + RUN_ID=$(gh run list --workflow=release.yml --branch="v$VERSION" --limit=1 --json databaseId --jq '.[0].databaseId') + if [[ -n "$RUN_ID" ]]; then + break + fi + echo " Waiting for workflow to appear... (attempt $i/12)" + sleep 5 +done if [[ -n "$RUN_ID" ]]; then echo "📊 Workflow run ID: $RUN_ID" @@ -382,12 +530,14 @@ if [[ -n "$RUN_ID" ]]; then echo "" echo "✅ Release v$VERSION completed successfully!" echo "📦 View on NPM: https://www.npmjs.com/package/xcodebuildmcp/v/$VERSION" - echo "🎉 View release: https://github.com/cameroncooke/XcodeBuildMCP/releases/tag/v$VERSION" + echo "🎉 View release: https://github.com/getsentry/XcodeBuildMCP/releases/tag/v$VERSION" # MCP Registry verification link echo "🔎 Verify MCP Registry: https://registry.modelcontextprotocol.io/v0/servers?search=com.xcodebuildmcp/XcodeBuildMCP&version=latest" else echo "" - echo "❌ CI workflow failed!" + echo "❌ CI workflow monitoring failed!" + echo "ℹ️ This may be a transient API error. The workflow may still be running." + echo " Check manually: gh run view $RUN_ID" echo "" # Prefer job state: if the primary 'release' job succeeded, treat as success. RELEASE_JOB_CONCLUSION=$(gh run view "$RUN_ID" --json jobs --jq '.jobs[] | select(.name=="release") | .conclusion') @@ -421,5 +571,5 @@ if [[ -n "$RUN_ID" ]]; then fi else echo "⚠️ Could not find workflow run. Please check manually:" - echo "https://github.com/cameroncooke/XcodeBuildMCP/actions" + echo "https://github.com/getsentry/XcodeBuildMCP/actions" fi diff --git a/scripts/tools-cli.ts b/scripts/tools-cli.ts deleted file mode 100644 index a8169964..00000000 --- a/scripts/tools-cli.ts +++ /dev/null @@ -1,686 +0,0 @@ -#!/usr/bin/env node - -/** - * XcodeBuildMCP Tools CLI - * - * A unified command-line tool that provides comprehensive information about - * XcodeBuildMCP tools and resources. Supports both runtime inspection - * (actual server state) and static analysis (source file analysis). - * - * Usage: - * npm run tools [command] [options] - * npx tsx src/cli/tools-cli.ts [command] [options] - * - * Commands: - * count, c Show tool and workflow counts - * list, l List all tools and resources - * static, s Show static source file analysis - * help, h Show this help message - * - * Options: - * --runtime, -r Use runtime inspection (respects env config) - * --static, -s Use static file analysis (development mode) - * --tools, -t Include tools in output - * --resources Include resources in output - * --workflows, -w Include workflow information - * --verbose, -v Show detailed information - * --json Output JSON format - * --help Show help for specific command - * - * Examples: - * npm run tools # Runtime summary with workflows - * npm run tools:count # Runtime tool count - * npm run tools:static # Static file analysis - * npm run tools:list # List runtime tools - * npx tsx src/cli/tools-cli.ts --json # JSON output - */ - -import { spawn } from 'child_process'; -import * as path from 'path'; -import { fileURLToPath } from 'url'; -import * as fs from 'fs'; -import { getStaticToolAnalysis, type StaticAnalysisResult } from './analysis/tools-analysis.js'; - -// Get project paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -// ANSI color codes -const colors = { - reset: '\x1b[0m', - bright: '\x1b[1m', - red: '\x1b[31m', - green: '\x1b[32m', - yellow: '\x1b[33m', - blue: '\x1b[34m', - cyan: '\x1b[36m', - magenta: '\x1b[35m', -} as const; - -// Types -interface CLIOptions { - runtime: boolean; - static: boolean; - tools: boolean; - resources: boolean; - workflows: boolean; - verbose: boolean; - json: boolean; - help: boolean; -} - -interface RuntimeTool { - name: string; - description: string; -} - -interface RuntimeResource { - uri: string; - name: string; - description: string; -} - -interface RuntimeData { - tools: RuntimeTool[]; - resources: RuntimeResource[]; - toolCount: number; - resourceCount: number; - mode: 'runtime'; -} - -// CLI argument parsing -const args = process.argv.slice(2); - -// Find the command (first non-flag argument) -let command = 'count'; // default -for (const arg of args) { - if (!arg.startsWith('-')) { - command = arg; - break; - } -} - -const options: CLIOptions = { - runtime: args.includes('--runtime') || args.includes('-r'), - static: args.includes('--static') || args.includes('-s'), - tools: args.includes('--tools') || args.includes('-t'), - resources: args.includes('--resources'), - workflows: args.includes('--workflows') || args.includes('-w'), - verbose: args.includes('--verbose') || args.includes('-v'), - json: args.includes('--json'), - help: args.includes('--help') || args.includes('-h'), -}; - -// Set sensible defaults for each command -if (!options.runtime && !options.static) { - if (command === 'static' || command === 's') { - options.static = true; - } else { - // Default to static analysis for development-friendly usage - options.static = true; - } -} - -// Set sensible content defaults -if (command === 'list' || command === 'l') { - if (!options.tools && !options.resources && !options.workflows) { - options.tools = true; // Default to showing tools for list command - } -} else if (!command || command === 'count' || command === 'c') { - // For no command or count, show comprehensive summary - if (!options.tools && !options.resources && !options.workflows) { - options.workflows = true; // Show workflows by default for summary - } -} - -// Help text -const helpText = { - main: ` -${colors.bright}${colors.blue}XcodeBuildMCP Tools CLI${colors.reset} - -A unified command-line tool for XcodeBuildMCP tool and resource information. - -${colors.bright}COMMANDS:${colors.reset} - count, c Show tool and workflow counts - list, l List all tools and resources - static, s Show static source file analysis - help, h Show this help message - -${colors.bright}OPTIONS:${colors.reset} - --runtime, -r Use runtime inspection (respects env config) - --static, -s Use static file analysis (default, development mode) - --tools, -t Include tools in output - --resources Include resources in output - --workflows, -w Include workflow information - --verbose, -v Show detailed information - --json Output JSON format - -${colors.bright}EXAMPLES:${colors.reset} - ${colors.cyan}npm run tools${colors.reset} # Static summary with workflows (default) - ${colors.cyan}npm run tools list${colors.reset} # List tools - ${colors.cyan}npm run tools --runtime${colors.reset} # Runtime analysis (requires build) - ${colors.cyan}npm run tools static${colors.reset} # Static analysis summary - ${colors.cyan}npm run tools count --json${colors.reset} # JSON output - -${colors.bright}ANALYSIS MODES:${colors.reset} - ${colors.green}Runtime${colors.reset} Uses actual server inspection via Reloaderoo - - Respects XCODEBUILDMCP_ENABLED_WORKFLOWS environment variable - - Shows tools actually enabled at runtime - - Requires built server (npm run build) - - ${colors.yellow}Static${colors.reset} Scans source files directly using AST parsing - - Shows all tools in codebase regardless of config - - Development-time analysis with reliable description extraction - - No server build required -`, - - count: ` -${colors.bright}COUNT COMMAND${colors.reset} - -Shows tool and workflow counts using runtime or static analysis. - -${colors.bright}Usage:${colors.reset} npx tsx scripts/tools-cli.ts count [options] - -${colors.bright}Options:${colors.reset} - --runtime, -r Count tools from running server - --static, -s Count tools from source files - --workflows, -w Include workflow directory counts - --json Output JSON format - -${colors.bright}Examples:${colors.reset} - ${colors.cyan}npx tsx scripts/tools-cli.ts count${colors.reset} # Runtime count - ${colors.cyan}npx tsx scripts/tools-cli.ts count --static${colors.reset} # Static count - ${colors.cyan}npx tsx scripts/tools-cli.ts count --workflows${colors.reset} # Include workflows -`, - - list: ` -${colors.bright}LIST COMMAND${colors.reset} - -Lists tools and resources with optional details. - -${colors.bright}Usage:${colors.reset} npx tsx scripts/tools-cli.ts list [options] - -${colors.bright}Options:${colors.reset} - --runtime, -r List from running server - --static, -s List from source files - --tools, -t Show tool names - --resources Show resource URIs - --verbose, -v Show detailed information - --json Output JSON format - -${colors.bright}Examples:${colors.reset} - ${colors.cyan}npx tsx scripts/tools-cli.ts list --tools${colors.reset} # Runtime tool list - ${colors.cyan}npx tsx scripts/tools-cli.ts list --resources${colors.reset} # Runtime resource list - ${colors.cyan}npx tsx scripts/tools-cli.ts list --static --verbose${colors.reset} # Static detailed list -`, - - static: ` -${colors.bright}STATIC COMMAND${colors.reset} - -Performs detailed static analysis of source files using AST parsing. - -${colors.bright}Usage:${colors.reset} npx tsx scripts/tools-cli.ts static [options] - -${colors.bright}Options:${colors.reset} - --tools, -t Show canonical tool details - --workflows, -w Show workflow directory analysis - --verbose, -v Show detailed file information - --json Output JSON format - -${colors.bright}Examples:${colors.reset} - ${colors.cyan}npx tsx scripts/tools-cli.ts static${colors.reset} # Basic static analysis - ${colors.cyan}npx tsx scripts/tools-cli.ts static --verbose${colors.reset} # Detailed analysis - ${colors.cyan}npx tsx scripts/tools-cli.ts static --workflows${colors.reset} # Include workflow info -`, -}; - -if (options.help) { - console.log(helpText[command as keyof typeof helpText] || helpText.main); - process.exit(0); -} - -if (command === 'help' || command === 'h') { - const helpCommand = args[1]; - console.log(helpText[helpCommand as keyof typeof helpText] || helpText.main); - process.exit(0); -} - -/** - * Execute reloaderoo command and parse JSON response - */ -async function executeReloaderoo(reloaderooArgs: string[]): Promise { - const buildPath = path.resolve(__dirname, '..', 'build', 'index.js'); - - if (!fs.existsSync(buildPath)) { - throw new Error('Build not found. Please run "npm run build" first.'); - } - - const tempFile = `/tmp/reloaderoo-output-${Date.now()}.json`; - const command = `npx -y reloaderoo@latest inspect ${reloaderooArgs.join(' ')} -- node "${buildPath}"`; - - return new Promise((resolve, reject) => { - const child = spawn('bash', ['-c', `${command} > "${tempFile}"`], { - stdio: 'inherit', - }); - - child.on('close', (code) => { - try { - if (code !== 0) { - reject(new Error(`Command failed with code ${code}`)); - return; - } - - const content = fs.readFileSync(tempFile, 'utf8'); - - // Remove stderr log lines and find JSON - const lines = content.split('\n'); - const cleanLines: string[] = []; - - for (const line of lines) { - if ( - line.match(/^\[\d{4}-\d{2}-\d{2}T/) || - line.includes('[INFO]') || - line.includes('[DEBUG]') || - line.includes('[ERROR]') - ) { - continue; - } - - const trimmed = line.trim(); - if (trimmed) { - cleanLines.push(line); - } - } - - // Find JSON start - let jsonStartIndex = -1; - for (let i = 0; i < cleanLines.length; i++) { - if (cleanLines[i].trim().startsWith('{')) { - jsonStartIndex = i; - break; - } - } - - if (jsonStartIndex === -1) { - reject( - new Error(`No JSON response found in output.\nOutput: ${content.substring(0, 500)}...`), - ); - return; - } - - const jsonText = cleanLines.slice(jsonStartIndex).join('\n'); - const response = JSON.parse(jsonText); - resolve(response); - } catch (error) { - reject(new Error(`Failed to parse JSON response: ${(error as Error).message}`)); - } finally { - try { - fs.unlinkSync(tempFile); - } catch { - // Ignore cleanup errors - } - } - }); - - child.on('error', (error) => { - reject(new Error(`Failed to spawn process: ${error.message}`)); - }); - }); -} - -/** - * Get runtime server information - */ -async function getRuntimeInfo(): Promise { - try { - const toolsResponse = (await executeReloaderoo(['list-tools'])) as { - tools?: { name: string; description: string }[]; - }; - const resourcesResponse = (await executeReloaderoo(['list-resources'])) as { - resources?: { uri: string; name: string; description?: string; title?: string }[]; - }; - - let tools: RuntimeTool[] = []; - let toolCount = 0; - - if (toolsResponse.tools && Array.isArray(toolsResponse.tools)) { - toolCount = toolsResponse.tools.length; - tools = toolsResponse.tools.map((tool) => ({ - name: tool.name, - description: tool.description, - })); - } - - let resources: RuntimeResource[] = []; - let resourceCount = 0; - - if (resourcesResponse.resources && Array.isArray(resourcesResponse.resources)) { - resourceCount = resourcesResponse.resources.length; - resources = resourcesResponse.resources.map((resource) => ({ - uri: resource.uri, - name: resource.name, - description: resource.title ?? resource.description ?? 'No description available', - })); - } - - return { - tools, - resources, - toolCount, - resourceCount, - mode: 'runtime', - }; - } catch (error) { - throw new Error(`Runtime analysis failed: ${(error as Error).message}`); - } -} - -/** - * Display summary information - */ -function displaySummary( - runtimeData: RuntimeData | null, - staticData: StaticAnalysisResult | null, -): void { - if (options.json) { - return; // JSON output handled separately - } - - console.log(`${colors.bright}${colors.blue}📊 XcodeBuildMCP Tools Summary${colors.reset}`); - console.log('═'.repeat(60)); - - if (runtimeData) { - console.log(`${colors.green}🚀 Runtime Analysis:${colors.reset}`); - console.log(` Tools: ${runtimeData.toolCount}`); - console.log(` Resources: ${runtimeData.resourceCount}`); - console.log(` Total: ${runtimeData.toolCount + runtimeData.resourceCount}`); - console.log(); - } - - if (staticData) { - console.log(`${colors.cyan}📁 Static Analysis:${colors.reset}`); - console.log(` Workflow directories: ${staticData.stats.workflowCount}`); - console.log(` Canonical tools: ${staticData.stats.canonicalTools}`); - console.log(` Re-export files: ${staticData.stats.reExportTools}`); - console.log(` Total tool files: ${staticData.stats.totalTools}`); - console.log(); - } -} - -/** - * Display workflow information - */ -function displayWorkflows(staticData: StaticAnalysisResult | null): void { - if (!options.workflows || !staticData || options.json) return; - - console.log(`${colors.bright}📂 Workflow Directories:${colors.reset}`); - console.log('─'.repeat(40)); - - for (const workflow of staticData.workflows) { - const totalTools = workflow.toolCount; - console.log(`${colors.green}• ${workflow.displayName}${colors.reset} (${totalTools} tools)`); - - if (options.verbose) { - const canonicalTools = workflow.tools.filter((t) => t.isCanonical).map((t) => t.name); - const reExportTools = workflow.tools.filter((t) => !t.isCanonical).map((t) => t.name); - - if (canonicalTools.length > 0) { - console.log(` ${colors.cyan}Canonical:${colors.reset} ${canonicalTools.join(', ')}`); - } - if (reExportTools.length > 0) { - console.log(` ${colors.yellow}Re-exports:${colors.reset} ${reExportTools.join(', ')}`); - } - } - } - console.log(); -} - -/** - * Display tool lists - */ -function displayTools( - runtimeData: RuntimeData | null, - staticData: StaticAnalysisResult | null, -): void { - if (!options.tools || options.json) return; - - if (runtimeData) { - console.log(`${colors.bright}🛠️ Runtime Tools (${runtimeData.toolCount}):${colors.reset}`); - console.log('─'.repeat(40)); - - if (runtimeData.tools.length === 0) { - console.log(' No tools available'); - } else { - runtimeData.tools.forEach((tool) => { - if (options.verbose && tool.description) { - console.log( - ` ${colors.green}•${colors.reset} ${colors.bright}${tool.name}${colors.reset}`, - ); - console.log(` ${tool.description}`); - } else { - console.log(` ${colors.green}•${colors.reset} ${tool.name}`); - } - }); - } - console.log(); - } - - if (staticData && options.static) { - const canonicalTools = staticData.tools.filter((tool) => tool.isCanonical); - console.log(`${colors.bright}📁 Static Tools (${canonicalTools.length}):${colors.reset}`); - console.log('─'.repeat(40)); - - if (canonicalTools.length === 0) { - console.log(' No tools found'); - } else { - canonicalTools - .sort((a, b) => a.name.localeCompare(b.name)) - .forEach((tool) => { - if (options.verbose) { - console.log( - ` ${colors.green}•${colors.reset} ${colors.bright}${tool.name}${colors.reset} (${tool.workflow})`, - ); - console.log(` ${tool.description}`); - console.log(` ${colors.cyan}${tool.relativePath}${colors.reset}`); - } else { - console.log(` ${colors.green}•${colors.reset} ${tool.name}`); - } - }); - } - console.log(); - } -} - -/** - * Display resource lists - */ -function displayResources(runtimeData: RuntimeData | null): void { - if (!options.resources || !runtimeData || options.json) return; - - console.log(`${colors.bright}📚 Resources (${runtimeData.resourceCount}):${colors.reset}`); - console.log('─'.repeat(40)); - - if (runtimeData.resources.length === 0) { - console.log(' No resources available'); - } else { - runtimeData.resources.forEach((resource) => { - if (options.verbose) { - console.log( - ` ${colors.magenta}•${colors.reset} ${colors.bright}${resource.uri}${colors.reset}`, - ); - console.log(` ${resource.description}`); - } else { - console.log(` ${colors.magenta}•${colors.reset} ${resource.uri}`); - } - }); - } - console.log(); -} - -/** - * Output JSON format - matches the structure of human-readable output - */ -function outputJSON( - runtimeData: RuntimeData | null, - staticData: StaticAnalysisResult | null, -): void { - const output: Record = {}; - - // Add summary stats (equivalent to the summary table) - if (runtimeData) { - output.runtime = { - toolCount: runtimeData.toolCount, - resourceCount: runtimeData.resourceCount, - totalCount: runtimeData.toolCount + runtimeData.resourceCount, - }; - } - - if (staticData) { - output.static = { - workflowCount: staticData.stats.workflowCount, - canonicalTools: staticData.stats.canonicalTools, - reExportTools: staticData.stats.reExportTools, - totalTools: staticData.stats.totalTools, - }; - } - - // Add detailed data only if requested - if (options.workflows && staticData) { - output.workflows = staticData.workflows.map((w) => ({ - name: w.displayName, - toolCount: w.toolCount, - canonicalCount: w.canonicalCount, - reExportCount: w.reExportCount, - })); - } - - if (options.tools) { - if (runtimeData) { - output.runtimeTools = runtimeData.tools.map((t) => t.name); - } - if (staticData) { - output.staticTools = staticData.tools - .filter((t) => t.isCanonical) - .map((t) => t.name) - .sort(); - } - } - - if (options.resources && runtimeData) { - output.resources = runtimeData.resources.map((r) => r.uri); - } - - console.log(JSON.stringify(output, null, 2)); -} - -/** - * Main execution function - */ -async function main(): Promise { - try { - let runtimeData: RuntimeData | null = null; - let staticData: StaticAnalysisResult | null = null; - - // Gather data based on options - if (options.runtime) { - if (!options.json) { - console.log(`${colors.cyan}🔍 Gathering runtime information...${colors.reset}`); - } - runtimeData = await getRuntimeInfo(); - } - - if (options.static) { - if (!options.json) { - console.log(`${colors.cyan}📁 Performing static analysis...${colors.reset}`); - } - staticData = await getStaticToolAnalysis(); - } - - // For default command or workflows option, always gather static data for workflow info - if (options.workflows && !staticData) { - if (!options.json) { - console.log(`${colors.cyan}📁 Gathering workflow information...${colors.reset}`); - } - staticData = await getStaticToolAnalysis(); - } - - if (!options.json) { - console.log(); // Blank line after gathering - } - - // Handle JSON output - if (options.json) { - outputJSON(runtimeData, staticData); - return; - } - - // Display based on command - switch (command) { - case 'count': - case 'c': - displaySummary(runtimeData, staticData); - displayWorkflows(staticData); - break; - - case 'list': - case 'l': - displaySummary(runtimeData, staticData); - displayTools(runtimeData, staticData); - displayResources(runtimeData); - break; - - case 'static': - case 's': - if (!staticData) { - console.log(`${colors.cyan}📁 Performing static analysis...${colors.reset}\n`); - staticData = await getStaticToolAnalysis(); - } - displaySummary(null, staticData); - displayWorkflows(staticData); - - if (options.verbose) { - displayTools(null, staticData); - const reExportTools = staticData.tools.filter((t) => !t.isCanonical); - console.log( - `${colors.bright}🔄 Re-export Files (${reExportTools.length}):${colors.reset}`, - ); - console.log('─'.repeat(40)); - reExportTools.forEach((file) => { - console.log(` ${colors.yellow}•${colors.reset} ${file.name} (${file.workflow})`); - console.log(` ${file.relativePath}`); - }); - } - break; - - default: - // Default case (no command) - show runtime summary with workflows - displaySummary(runtimeData, staticData); - displayWorkflows(staticData); - break; - } - - if (!options.json) { - console.log(`${colors.green}✅ Analysis complete!${colors.reset}`); - } - } catch (error) { - if (options.json) { - console.error( - JSON.stringify( - { - success: false, - error: (error as Error).message, - timestamp: new Date().toISOString(), - }, - null, - 2, - ), - ); - } else { - console.error(`${colors.red}❌ Error: ${(error as Error).message}${colors.reset}`); - } - process.exit(1); - } -} - -// Run the CLI -main(); diff --git a/scripts/update-tools-docs.ts b/scripts/update-tools-docs.ts index 91938196..a570e341 100644 --- a/scripts/update-tools-docs.ts +++ b/scripts/update-tools-docs.ts @@ -3,8 +3,8 @@ /** * XcodeBuildMCP Tools Documentation Updater * - * Automatically updates docs/TOOLS.md with current tool and workflow information - * using static AST analysis. Ensures documentation always reflects the actual codebase. + * Automatically updates docs/TOOLS.md and docs/TOOLS-CLI.md with current tool and workflow information + * using the build tools manifest. * * Usage: * npx tsx scripts/update-tools-docs.ts [--dry-run] [--verbose] @@ -18,17 +18,28 @@ import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; -import { - getStaticToolAnalysis, - type StaticAnalysisResult, - type WorkflowInfo, -} from './analysis/tools-analysis.js'; +import { loadManifest as loadYamlManifest } from '../src/core/manifest/load-manifest.ts'; +import { getEffectiveCliName } from '../src/core/manifest/schema.ts'; // Get project paths const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const projectRoot = path.resolve(__dirname, '..'); const docsPath = path.join(projectRoot, 'docs', 'TOOLS.md'); +const docsCliPath = path.join(projectRoot, 'docs', 'TOOLS-CLI.md'); +const cliExcludedWorkflows = new Set(['session-management', 'workflow-discovery']); + +type ToolsManifest = { + generatedAt: string; + stats: { + totalTools: number; + canonicalTools: number; + reExportTools: number; + workflowCount: number; + }; + workflows: DocumentationWorkflow[]; + tools: DocumentationTool[]; +}; // CLI options const args = process.argv.slice(2); @@ -53,7 +64,7 @@ if (options.help) { console.log(` ${colors.bright}${colors.blue}XcodeBuildMCP Tools Documentation Updater${colors.reset} -Automatically updates docs/TOOLS.md with current tool and workflow information. +Automatically updates docs/TOOLS.md and docs/TOOLS-CLI.md with current tool and workflow information. ${colors.bright}Usage:${colors.reset} npx tsx scripts/update-tools-docs.ts [options] @@ -64,7 +75,7 @@ ${colors.bright}Options:${colors.reset} --help, -h Show this help message ${colors.bright}Examples:${colors.reset} - ${colors.cyan}npx tsx scripts/update-tools-docs.ts${colors.reset} # Update docs/TOOLS.md + ${colors.cyan}npx tsx scripts/update-tools-docs.ts${colors.reset} # Update docs/TOOLS.md + docs/TOOLS-CLI.md ${colors.cyan}npx tsx scripts/update-tools-docs.ts --dry-run${colors.reset} # Preview changes ${colors.cyan}npx tsx scripts/update-tools-docs.ts --verbose${colors.reset} # Show detailed progress `); @@ -74,56 +85,274 @@ ${colors.bright}Examples:${colors.reset} /** * Generate the workflow section content */ -function generateWorkflowSection(workflow: WorkflowInfo): string { - const canonicalTools = workflow.tools.filter((tool) => tool.isCanonical); - const toolCount = canonicalTools.length; +function cleanToolDescription(description: string | undefined): string { + if (!description) { + return 'No description available'; + } + + return description + .replace(/IMPORTANT:.*?Example:.*?\)/g, '') // Remove IMPORTANT sections + .replace(/\s+/g, ' ') // Normalize whitespace + .trim(); +} + +type DocumentationTool = { + name: string; + description?: string; + isCanonical?: boolean; + originWorkflowDisplayName?: string; + workflow?: string; + cliName?: string; + originWorkflow?: string; +}; + +type DocumentationWorkflow = { + name: string; + displayName: string; + description: string; +}; + +function generateWorkflowSection( + workflow: DocumentationWorkflow, + tools: DocumentationTool[], +): string { + const toolCount = tools.length; let content = `### ${workflow.displayName} (\`${workflow.name}\`)\n`; content += `**Purpose**: ${workflow.description} (${toolCount} tools)\n\n`; // List each tool with its description - for (const tool of canonicalTools.sort((a, b) => a.name.localeCompare(b.name))) { - // Clean up the description for documentation - const cleanDescription = tool.description - .replace(/IMPORTANT:.*?Example:.*?\)/g, '') // Remove IMPORTANT sections - .replace(/\s+/g, ' ') // Normalize whitespace - .trim(); + const sortedTools = [...tools].sort((a, b) => a.name.localeCompare(b.name)); + for (const tool of sortedTools) { + let description = tool.description; + if (tool.isCanonical === false) { + if (tool.originWorkflowDisplayName) { + description = `Defined in ${tool.originWorkflowDisplayName} workflow.`; + } else { + description = 'Defined in another workflow.'; + } + } + const cleanDescription = cleanToolDescription(description); content += `- \`${tool.name}\` - ${cleanDescription}\n`; } + content += '\n\n'; + return content; } +function loadManifest(): ToolsManifest { + const manifest = loadYamlManifest(); + const workflowList = Array.from(manifest.workflows.values()); + const firstWorkflowByToolId = new Map(); + const docsTools: DocumentationTool[] = []; + + for (const workflow of workflowList) { + for (const toolId of workflow.tools) { + if (!firstWorkflowByToolId.has(toolId)) { + firstWorkflowByToolId.set(toolId, workflow.id); + } + + const tool = manifest.tools.get(toolId); + if (!tool) { + continue; + } + + const originWorkflow = firstWorkflowByToolId.get(toolId); + docsTools.push({ + name: tool.names.mcp, + description: tool.description, + isCanonical: originWorkflow === workflow.id, + originWorkflow, + workflow: workflow.id, + cliName: getEffectiveCliName(tool), + }); + } + } + + return { + generatedAt: new Date().toISOString(), + stats: { + totalTools: docsTools.length, + canonicalTools: manifest.tools.size, + reExportTools: docsTools.length - manifest.tools.size, + workflowCount: workflowList.length, + }, + workflows: workflowList.map((workflow) => ({ + name: workflow.id, + displayName: workflow.title, + description: workflow.description, + })), + tools: docsTools, + }; +} + /** * Generate the complete TOOLS.md content */ -function generateToolsDocumentation(analysis: StaticAnalysisResult): string { - const { workflows, stats } = analysis; +function generateToolsDocumentation(manifest: ToolsManifest): string { + const { workflows, stats, tools } = manifest; // Sort workflows by display name for consistent ordering - const sortedWorkflows = workflows.sort((a, b) => a.displayName.localeCompare(b.displayName)); + const sortedWorkflows = [...workflows].sort((a, b) => a.displayName.localeCompare(b.displayName)); + const workflowMeta = new Map(workflows.map((workflow) => [workflow.name, workflow])); + const toolsByWorkflow = new Map(); + for (const tool of tools) { + const workflowKey = tool.workflow ?? ''; + const workflowTools = toolsByWorkflow.get(workflowKey) ?? []; + workflowTools.push(tool); + toolsByWorkflow.set(workflowKey, workflowTools); + } + const workflowSections = sortedWorkflows + .map((workflow) => { + const workflowTools = toolsByWorkflow.get(workflow.name) ?? []; + const docTools = workflowTools.map((tool) => { + const originWorkflow = tool.originWorkflow + ? (workflowMeta.get(tool.originWorkflow)?.displayName ?? tool.originWorkflow) + : undefined; + + return { + name: tool.name, + description: tool.description, + isCanonical: tool.isCanonical, + originWorkflowDisplayName: originWorkflow, + }; + }); + + return generateWorkflowSection( + { + name: workflow.name, + displayName: workflow.displayName, + description: workflow.description, + }, + docTools, + ); + }) + .join('\n'); + + const lastUpdated = `${new Date(manifest.generatedAt).toISOString()} UTC`; - const content = `# XcodeBuildMCP Tools Reference + const content = `# XcodeBuildMCP MCP Tools Reference -XcodeBuildMCP provides ${stats.canonicalTools} tools organized into ${stats.workflowCount} workflow groups for comprehensive Apple development workflows. +This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP provides ${stats.canonicalTools} canonical tools organized into ${stats.workflowCount} workflow groups for comprehensive Apple development workflows. ## Workflow Groups -${sortedWorkflows.map((workflow) => generateWorkflowSection(workflow)).join('')} +${workflowSections} ## Summary Statistics -- **Total Tools**: ${stats.canonicalTools} canonical tools + ${stats.reExportTools} re-exports = ${stats.totalTools} total +- **Canonical Tools**: ${stats.canonicalTools} +- **Total Tools**: ${stats.totalTools} - **Workflow Groups**: ${stats.workflowCount} --- -*This documentation is automatically generated by \`scripts/update-tools-docs.ts\` using static analysis. Last updated: ${new Date().toISOString().split('T')[0]}* +*This documentation is automatically generated by \`scripts/update-tools-docs.ts\` from the tools manifest. Last updated: ${lastUpdated}* `; return content; } +/** + * Generate CLI tools documentation content + */ +type CliDocumentationStats = { + toolCount: number; + canonicalToolCount: number; + workflowCount: number; +}; + +type CliDocumentationResult = { + content: string; + stats: CliDocumentationStats; +}; + +function generateCliToolsDocumentation(manifest: ToolsManifest): CliDocumentationResult { + const workflowMeta = new Map(manifest.workflows.map((workflow) => [workflow.name, workflow])); + const toolsByWorkflow = new Map(); + let canonicalToolCount = 0; + for (const tool of manifest.tools) { + if (cliExcludedWorkflows.has(tool.workflow)) { + continue; + } + + if (tool.isCanonical) { + canonicalToolCount++; + } + + const tools = toolsByWorkflow.get(tool.workflow) ?? []; + const originWorkflow = tool.originWorkflow + ? (workflowMeta.get(tool.originWorkflow)?.displayName ?? tool.originWorkflow) + : undefined; + + tools.push({ + name: tool.cliName ?? tool.name, + description: tool.description, + isCanonical: tool.isCanonical, + originWorkflowDisplayName: originWorkflow, + }); + toolsByWorkflow.set(tool.workflow, tools); + } + + const sortedWorkflows = [...manifest.workflows] + .filter((workflow) => toolsByWorkflow.has(workflow.name)) + .filter((workflow) => !cliExcludedWorkflows.has(workflow.name)) + .sort((a, b) => a.displayName.localeCompare(b.displayName)); + + const workflowSections = sortedWorkflows + .map((workflow) => { + const tools = toolsByWorkflow.get(workflow.name) ?? []; + const meta = workflowMeta.get(workflow.name); + return generateWorkflowSection( + { + name: workflow.name, + displayName: meta?.displayName ?? workflow.name, + description: meta?.description ?? `${workflow.name} related tools`, + }, + tools, + ); + }) + .join('\n'); + + const workflowCount = sortedWorkflows.length; + const totalTools = Array.from(toolsByWorkflow.values()).reduce( + (sum, tools) => sum + tools.length, + 0, + ); + + const lastUpdated = `${new Date(manifest.generatedAt).toISOString()} UTC`; + + const content = `# XcodeBuildMCP CLI Tools Reference + +This document lists CLI tool names as exposed by \`xcodebuildmcp \`. + +XcodeBuildMCP provides ${canonicalToolCount} canonical tools organized into ${workflowCount} workflow groups. + +## Workflow Groups + +${workflowSections} +## Summary Statistics + +- **Canonical Tools**: ${canonicalToolCount} +- **Total Tools**: ${totalTools} +- **Workflow Groups**: ${workflowCount} + +--- + +*This documentation is automatically generated by \`scripts/update-tools-docs.ts\` from the tools manifest. Last updated: ${lastUpdated}* +`; + + return { + content, + stats: { + toolCount: totalTools, + canonicalToolCount, + workflowCount, + }, + }; +} + /** * Compare old and new content to show what changed */ @@ -177,46 +406,66 @@ async function main(): Promise { console.log(`${colors.cyan}📊 Analyzing tools...${colors.reset}`); - // Get current tool analysis - const analysis = await getStaticToolAnalysis(); + const manifest = loadManifest(); if (options.verbose) { console.log( - `${colors.green}✓ Found ${analysis.stats.canonicalTools} canonical tools in ${analysis.stats.workflowCount} workflows${colors.reset}`, - ); - console.log( - `${colors.green}✓ Found ${analysis.stats.reExportTools} re-export files${colors.reset}`, + `${colors.green}✓ Found ${manifest.stats.canonicalTools} canonical tools in ${manifest.stats.workflowCount} workflows${colors.reset}`, ); } // Generate new documentation content console.log(`${colors.cyan}📝 Generating documentation...${colors.reset}`); - const newContent = generateToolsDocumentation(analysis); + const mcpContent = generateToolsDocumentation(manifest); + const cliDocumentation = generateCliToolsDocumentation(manifest); + const cliContent = cliDocumentation.content; + const cliStats = cliDocumentation.stats; - // Read current content for comparison - let oldContent = ''; - if (fs.existsSync(docsPath)) { - oldContent = fs.readFileSync(docsPath, 'utf-8'); - } + const targets = [ + { label: 'MCP tools', path: docsPath, content: mcpContent }, + { label: 'CLI tools', path: docsCliPath, content: cliContent }, + ]; + + const changes = targets.map((target) => { + const existing = fs.existsSync(target.path) ? fs.readFileSync(target.path, 'utf-8') : ''; + + const changed = existing !== target.content; + return { ...target, existing, changed }; + }); + + const changedTargets = changes.filter((target) => target.changed); // Check if content has changed - if (oldContent === newContent) { + if (changedTargets.length === 0) { console.log(`${colors.green}✅ Documentation is already up to date!${colors.reset}`); return; } // Show differences if verbose - if (oldContent && options.verbose) { - showDiff(oldContent, newContent); + if (options.verbose) { + for (const target of changedTargets) { + if (target.existing) { + console.log( + `${colors.bright}${colors.magenta}📄 ${target.label} content comparison:${colors.reset}`, + ); + showDiff(target.existing, target.content); + } + } } if (options.dryRun) { console.log( `${colors.yellow}📋 Dry run completed. Documentation would be updated with:${colors.reset}`, ); - console.log(` - ${analysis.stats.canonicalTools} canonical tools`); - console.log(` - ${analysis.stats.workflowCount} workflow groups`); - console.log(` - ${newContent.split('\n').length} lines total`); + for (const target of changedTargets) { + console.log(` - ${path.relative(projectRoot, target.path)} (${target.label})`); + } + console.log(` - MCP tools: ${manifest.stats.canonicalTools} canonical tools`); + console.log( + ` - CLI tools: ${cliStats.toolCount} tools across ${cliStats.workflowCount} workflows`, + ); + console.log(` - MCP lines: ${mcpContent.split('\n').length}`); + console.log(` - CLI lines: ${cliContent.split('\n').length}`); if (!options.verbose) { console.log(`\n${colors.cyan}💡 Use --verbose to see detailed changes${colors.reset}`); @@ -227,20 +476,24 @@ async function main(): Promise { // Write new content console.log(`${colors.cyan}✏️ Writing updated documentation...${colors.reset}`); - fs.writeFileSync(docsPath, newContent, 'utf-8'); - - console.log( - `${colors.green}✅ Successfully updated ${path.relative(projectRoot, docsPath)}!${colors.reset}`, - ); + for (const target of changedTargets) { + fs.writeFileSync(target.path, target.content, 'utf-8'); + console.log( + `${colors.green}✅ Successfully updated ${path.relative(projectRoot, target.path)}!${colors.reset}`, + ); + } if (options.verbose) { console.log(`\n${colors.bright}📈 Update Summary:${colors.reset}`); console.log( - ` Tools: ${analysis.stats.canonicalTools} canonical + ${analysis.stats.reExportTools} re-exports = ${analysis.stats.totalTools} total`, + ` MCP tools: ${manifest.stats.canonicalTools} canonical (${manifest.stats.totalTools} total)`, ); - console.log(` Workflows: ${analysis.stats.workflowCount}`); - console.log(` File size: ${(newContent.length / 1024).toFixed(1)}KB`); - console.log(` Lines: ${newContent.split('\n').length}`); + console.log(` MCP workflows: ${manifest.stats.workflowCount}`); + console.log(` CLI tools: ${cliStats.toolCount} across ${cliStats.workflowCount} workflows`); + console.log(` MCP file size: ${(mcpContent.length / 1024).toFixed(1)}KB`); + console.log(` CLI file size: ${(cliContent.length / 1024).toFixed(1)}KB`); + console.log(` MCP lines: ${mcpContent.split('\n').length}`); + console.log(` CLI lines: ${cliContent.split('\n').length}`); } } catch (error) { console.error(`${colors.red}❌ Error: ${(error as Error).message}${colors.reset}`); diff --git a/scripts/verify-portable-install.sh b/scripts/verify-portable-install.sh new file mode 100755 index 00000000..3bb97c78 --- /dev/null +++ b/scripts/verify-portable-install.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ARCHIVE_PATH="" +PORTABLE_ROOT="" +TEMP_DIR="" + +usage() { + cat <<'EOF' +Usage: + scripts/verify-portable-install.sh --archive + scripts/verify-portable-install.sh --root +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --archive) + ARCHIVE_PATH="${2:-}" + shift 2 + ;; + --root) + PORTABLE_ROOT="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +cleanup() { + if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then + rm -r "$TEMP_DIR" + fi +} + +if [[ -z "$ARCHIVE_PATH" && -z "$PORTABLE_ROOT" ]]; then + usage + exit 1 +fi + +if [[ -n "$ARCHIVE_PATH" ]]; then + TEMP_DIR="$(mktemp -d)" + trap cleanup EXIT + tar -xzf "$ARCHIVE_PATH" -C "$TEMP_DIR" + extracted_count="$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" + if [[ "$extracted_count" -ne 1 ]]; then + echo "Expected archive to contain exactly one top-level directory" + exit 1 + fi + PORTABLE_ROOT="$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d | head -1)" +fi + +if [[ ! -d "$PORTABLE_ROOT/bin" || ! -d "$PORTABLE_ROOT/libexec" ]]; then + echo "Portable layout missing bin/ or libexec/: $PORTABLE_ROOT" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/bin/xcodebuildmcp" ]]; then + echo "Missing executable wrapper: $PORTABLE_ROOT/bin/xcodebuildmcp" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/bin/xcodebuildmcp-doctor" ]]; then + echo "Missing executable wrapper: $PORTABLE_ROOT/bin/xcodebuildmcp-doctor" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/libexec/xcodebuildmcp" ]]; then + echo "Missing executable binary: $PORTABLE_ROOT/libexec/xcodebuildmcp" + exit 1 +fi +if [[ ! -d "$PORTABLE_ROOT/libexec/manifests" ]]; then + echo "Missing manifests directory under libexec" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/libexec/bundled/axe" ]]; then + echo "Missing bundled axe binary under libexec" + exit 1 +fi +if [[ ! -d "$PORTABLE_ROOT/libexec/bundled/Frameworks" ]]; then + echo "Missing bundled Frameworks under libexec" + exit 1 +fi + +HOST_ARCH="$(uname -m)" +NODE_RUNTIME="$PORTABLE_ROOT/libexec/node-runtime" +if [[ ! -x "$NODE_RUNTIME" ]]; then + echo "Missing executable Node runtime under libexec" + exit 1 +fi + +RUNTIME_ARCHS="$(lipo -archs "$NODE_RUNTIME" 2>/dev/null || true)" +if [[ -z "$RUNTIME_ARCHS" ]]; then + if file "$NODE_RUNTIME" | grep -q "x86_64"; then + RUNTIME_ARCHS="x86_64" + elif file "$NODE_RUNTIME" | grep -q "arm64"; then + RUNTIME_ARCHS="arm64" + fi +fi + +NORMALIZED_HOST_ARCH="$HOST_ARCH" +if [[ "$HOST_ARCH" == "aarch64" ]]; then + NORMALIZED_HOST_ARCH="arm64" +fi + +CAN_EXECUTE="false" +for runtime_arch in $RUNTIME_ARCHS; do + if [[ "$runtime_arch" == "$NORMALIZED_HOST_ARCH" ]]; then + CAN_EXECUTE="true" + break + fi +done + +if [[ "$CAN_EXECUTE" == "true" ]]; then + "$PORTABLE_ROOT/bin/xcodebuildmcp" --help >/dev/null + "$PORTABLE_ROOT/bin/xcodebuildmcp-doctor" --help >/dev/null +else + echo "Skipping binary execution checks: host arch ($HOST_ARCH) not in runtime archs ($RUNTIME_ARCHS)" +fi + +echo "Portable install verification passed for: $PORTABLE_ROOT" diff --git a/server.json b/server.json index 17cdf8b3..2384e75c 100644 --- a/server.json +++ b/server.json @@ -1,20 +1,19 @@ { - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "com.xcodebuildmcp/XcodeBuildMCP", "description": "XcodeBuildMCP provides tools for Xcode project management, simulator management, and app utilities.", - "status": "active", "repository": { - "url": "https://github.com/cameroncooke/XcodeBuildMCP", + "url": "https://github.com/getsentry/XcodeBuildMCP", "source": "github", "id": "945551361" }, - "version": "1.15.1", + "version": "2.0.7", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "xcodebuildmcp", - "version": "1.15.1", + "version": "2.0.7", "transport": { "type": "stdio" }, diff --git a/skills/xcodebuildmcp-cli/SKILL.md b/skills/xcodebuildmcp-cli/SKILL.md new file mode 100644 index 00000000..e76fb452 --- /dev/null +++ b/skills/xcodebuildmcp-cli/SKILL.md @@ -0,0 +1,197 @@ +--- +name: xcodebuildmcp-cli +description: Official skill for the XcodeBuildMCP CLI. Use when doing iOS/macOS/watchOS/tvOS/visionOS work (build, test, run, debug, log, UI automation). +--- + +# XcodeBuildMCP CLI + +This skill is for AI agents. It positions the XcodeBuildMCP CLI as a low‑overhead alternative to MCP tool calls: agents can already run shell commands, and the CLI exposes the same tool surface without the schema‑exchange cost. Prefer the CLI over raw `xcodebuild`, `xcrun`, or `simctl`. + +## When To Use This CLI (Capabilities And Workflows) + +- When you need build/test/run/debugging/logging/UI automation capabilities. +- When you want simulator/device management capabilities. +- When you want AI optimized tools and tool responses. +- When you need project discovery capabilities (schemes, bundle IDs, app paths). + +## Command Discovery + +Use `--help` to discover workflows, tools, and arguments. + +```bash +xcodebuildmcp --help +xcodebuildmcp tools --help +xcodebuildmcp tools --json +xcodebuildmcp --help +xcodebuildmcp --help +``` + +Notes: +- Use `--json '{...}'` for complex arguments and `--output json` if you need machine-readable results (not recommended). + +## Common Workflows + +### Build And Run On Simulator + +If your intent is to run the app in Simulator, use `build-and-run` directly. It already performs the build step. +Do not run `build` first unless the user explicitly requests both commands. + +1. List simulators and pick a device name or UDID. +2. Build and run. + +If app and project details are not known: +```bash +xcodebuildmcp simulator discover-projects --workspace-root . +xcodebuildmcp simulator list-schemes --project-path ./MyApp.xcodeproj +xcodebuildmcp simulator list +``` + +To build, install and launch the app in one command: +```bash +xcodebuildmcp simulator build-and-run --scheme MyApp --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17 Pro" +``` + +### Build only + +Use this only when you want compile feedback and do not want to launch the app. +For run/launch intent, use `build-and-run` instead of chaining `build` and `build-and-run`. + +```bash +xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17 Pro" +``` + +### Run Tests + +When you need to run tests, you can do so with the `test` tool. + +```bash +xcodebuildmcp simulator test --scheme MyAppTests --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17 Pro" +``` + +### Install And Launch On Physical Device + +```bash +xcodebuildmcp device list +xcodebuildmcp device build --scheme MyApp --project-path ./MyApp.xcodeproj +xcodebuildmcp device get-app-path --scheme MyApp --project-path ./MyApp.xcodeproj +xcodebuildmcp device get-app-bundle-id --app-path /path/to/MyApp.app +xcodebuildmcp device install --device-id DEVICE_UDID --app-path /path/to/MyApp.app +xcodebuildmcp device launch --device-id DEVICE_UDID --bundle-id io.sentry.MyApp +``` + +### Capture Logs On Simulator + +```bash +xcodebuildmcp logging start-simulator-log-capture --simulator-id SIMULATOR_UDID --bundle-id io.sentry.MyApp +xcodebuildmcp logging stop-simulator-log-capture --log-session-id LOG_SESSION_ID +``` + +### Debug A Running App (Simulator) + +1. Launch the app. +2. Attach the debugger after the app is fully launched. + +Launch if not already running: +```bash +xcodebuildmcp simulator launch-app --bundle-id io.sentry.MyApp --simulator-id SIMULATOR_UDID +``` + +Attach the debugger: + +It's generally a good idea to wait for 1-2s for the app to fully launch before attaching the debugger. + +```bash +xcodebuildmcp debugging attach --bundle-id io.sentry.MyApp --simulator-id SIMULATOR_UDID +``` + +To add/remove breakpoints, inspect stack/variables, and issue arbitrary LLDB commands, view debugging help: +```bash +xcodebuildmcp debugging --help +``` + + +### Inspect UI And Automate Input + +Snapshot UI accessibility tree, tap/swipe/type, and capture screenshots: + +```bash +xcodebuildmcp ui-automation snapshot-ui --simulator-id SIMULATOR_UDID +xcodebuildmcp ui-automation tap --simulator-id SIMULATOR_UDID --label "Submit" +xcodebuildmcp ui-automation tap --simulator-id SIMULATOR_UDID --id "SubmitButton" +# Coordinate fallback when label/id is unavailable +xcodebuildmcp ui-automation tap --simulator-id SIMULATOR_UDID --x 200 --y 400 +xcodebuildmcp ui-automation type-text --simulator-id SIMULATOR_UDID --text "hello" +xcodebuildmcp ui-automation screenshot --simulator-id SIMULATOR_UDID --return-format path +``` + +To see all UI automation tools, view UI automation help: +```bash +xcodebuildmcp ui-automation --help +``` + +### macOS App Build/Run + +```bash +xcodebuildmcp macos build --scheme MyMacApp --project-path ./MyMacApp.xcodeproj +xcodebuildmcp macos build-and-run --scheme MyMacApp --project-path ./MyMacApp.xcodeproj +``` + +To see all macOS tools, view macOS help: +```bash +xcodebuildmcp macos --help +``` + +### SwiftPM Package Workflows + +```bash +xcodebuildmcp swift-package list +xcodebuildmcp swift-package build --package-path ./MyPackage +xcodebuildmcp swift-package test --package-path ./MyPackage +``` + +To see all SwiftPM tools, view SwiftPM help: +```bash +xcodebuildmcp swift-package --help +``` + +### Project Discovery + +```bash +xcodebuildmcp project-discovery discover-projects --workspace-root . +xcodebuildmcp project-discovery list-schemes --project-path ./MyApp.xcodeproj +xcodebuildmcp project-discovery get-app-bundle-id --app-path ./Build/MyApp.app +``` + +To see all project discovery tools, view project discovery help: +```bash +xcodebuildmcp project-discovery --help +``` + +### Scaffolding new projects + +It's worth viewing the --help for the scaffolding tools to see the available options. +Here are some minimal examples: + +```bash +xcodebuildmcp project-scaffolding scaffold-ios --project-name MyApp --output-path ./Projects +xcodebuildmcp project-scaffolding scaffold-macos --project-name MyMacApp --output-path ./Projects +``` + +To see all project scaffolding tools, view project scaffolding help: +```bash +xcodebuildmcp project-scaffolding --help +``` + +## Daemon Notes (Stateful Tools) + +Stateful tools (logs, debug, video recording, background run) go through a per-workspace daemon that auto-starts, if you find you are getting errors with the stateful tools, you can manage the daemon process manually. + +```bash +xcodebuildmcp daemon status +xcodebuildmcp daemon restart +``` + +To see all daemon commands, view daemon help: +```bash +xcodebuildmcp daemon --help +``` diff --git a/skills/xcodebuildmcp/SKILL.md b/skills/xcodebuildmcp/SKILL.md new file mode 100644 index 00000000..f5ff3f1b --- /dev/null +++ b/skills/xcodebuildmcp/SKILL.md @@ -0,0 +1,203 @@ +--- +name: xcodebuildmcp +description: Official skill for XcodeBuildMCP. Use when doing iOS/macOS/watchOS/tvOS/visionOS work (build, test, run, debug, log, UI automation). +--- + +# XcodeBuildMCP + +Prefer XcodeBuildMCP over raw `xcodebuild`, `xcrun`, or `simctl`. + +If a capability is missing, assume your tool list may be hiding tools (search/progressive disclosure) or not loading tool schemas yet. Use your tool-search or “load tools” mechanism. If you still can’t find the tools, ask the user to enable them in the MCP client's configuration. + +## Default Tool Choice (Simulator) + +- If intent includes run/launch/open in Simulator, use `build_run_sim` as the default. +- If intent is compile-only feedback (no launch), use `build_sim`. +- Do not call `build_sim` and then `build_run_sim` in sequence unless the user explicitly asks for both. +- If the app is already built and you need launch only without rebuilding, use `install_app_sim` + `launch_app_sim` (or `launch_app_logs_sim`). + +## Tools (exact names + official descriptions) + +### Session defaults + +Before you call any other tools, you **must** call `session_show_defaults` to show the current defaults, then fill in any missing defaults. You may need discovery/list tools first to obtain valid values. + +- `session_show_defaults` + - Show the current active defaults (including the active profile name). +- `session_set_defaults` + - Set defaults for the current active profile, or set defaults for a specific profile via `profile`. +- `session_use_defaults_profile` + - Switch the active defaults profile. +- `session_clear_defaults` + - Clear defaults (current active profile by default, or a specific profile when provided). + +### Project discovery + +- `discover_projs` + - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. +- `list_schemes` + - List Xcode schemes. +- `show_build_settings` + - Show build settings. +- `get_app_bundle_id` + - Extract bundle id from .app. +- `get_mac_bundle_id` + - Extract bundle id from macOS .app. + +### Simulator + +- `boot_sim` + - Boot iOS simulator. +- `list_sims` + - List iOS simulators. +- `open_sim` + - Open Simulator app. +- `build_sim` + - Build for iOS sim. +- `build_run_sim` + - Build and run iOS sim. +- `test_sim` + - Test on iOS sim. +- `get_sim_app_path` + - Get sim built app path. +- `install_app_sim` + - Install app on sim. +- `launch_app_sim` + - Launch app on simulator. +- `launch_app_logs_sim` + - Launch sim app with logs. +- `stop_app_sim` + - Stop sim app. +- `record_sim_video` + - Record sim video. + +### Simulator management + +- `erase_sims` + - Erase simulator. +- `set_sim_location` + - Set sim location. +- `reset_sim_location` + - Reset sim location. +- `set_sim_appearance` + - Set sim appearance. +- `sim_statusbar` + - Set sim status bar network. + +### Device + +- `list_devices` + - List connected devices. +- `build_device` + - Build for device. +- `test_device` + - Test on device. +- `get_device_app_path` + - Get device built app path. +- `install_app_device` + - Install app on device. +- `launch_app_device` + - Launch app on device. +- `stop_app_device` + - Stop device app. + +### macOS + +- `build_macos` + - Build macOS app. +- `build_run_macos` + - Build and run macOS app. +- `test_macos` + - Test macOS target. +- `get_mac_app_path` + - Get macOS built app path. +- `launch_mac_app` + - Launch macOS app. +- `stop_mac_app` + - Stop macOS app. + +### Logging + +- `start_device_log_cap` + - Start device log capture. +- `start_sim_log_cap` + - Start sim log capture. +- `stop_device_log_cap` + - Stop device log capture. +- `stop_sim_log_cap` + - Stop sim log capture. + +### Debugging + +- `debug_attach_sim` + - Attach LLDB to sim app. +- `debug_breakpoint_add` + - Add breakpoint. +- `debug_breakpoint_remove` + - Remove breakpoint. +- `debug_continue` + - Continue debug session. +- `debug_detach` + - Detach debugger. +- `debug_lldb_command` + - Run LLDB command. +- `debug_stack` + - Get backtrace. +- `debug_variables` + - Get frame variables. + +### UI automation + +- `button` + - Press simulator hardware button. +- `gesture` + - Simulator gesture preset. +- `key_press` + - Press key by keycode. +- `key_sequence` + - Press a sequence of keys by their keycodes. +- `long_press` + - Long press at coords. +- `screenshot` + - Capture screenshot. +- `snapshot_ui` + - Print view hierarchy with element ids/labels and precise coordinates (x, y, width, height) for visible elements. +- `swipe` + - Swipe between points. +- `tap` + - Tap UI element by accessibility id/label (recommended) or coordinates as fallback. +- `touch` + - Touch down/up at coords. +- `type_text` + - Type text. + +### SwiftPM + +- `swift_package_build` + - swift package target build. +- `swift_package_clean` + - swift package clean. +- `swift_package_list` + - List SwiftPM processes. +- `swift_package_run` + - swift package target run. +- `swift_package_stop` + - Stop SwiftPM run. +- `swift_package_test` + - Run swift package target tests. + +### Scaffolding / utilities + +- `scaffold_ios_project` + - Scaffold iOS project. +- `scaffold_macos_project` + - Scaffold macOS project. +- `clean` + - Clean build products. + +### Diagnostics + +- `doctor` + - MCP environment info. +- `manage_workflows` + - Workflows are groups of tools exposed by XcodeBuildMCP. By default, not all workflows (and therefore tools) are enabled; only simulator tools are enabled by default. Some workflows are mandatory and can't be disabled. diff --git a/smithery.config.js b/smithery.config.js deleted file mode 100644 index 409d69dc..00000000 --- a/smithery.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - esbuild: { - format: 'cjs', - target: 'node18', - }, -}; diff --git a/smithery.yaml b/smithery.yaml deleted file mode 100644 index 450a78dd..00000000 --- a/smithery.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Smithery configuration file: https://smithery.ai/docs/build/project-config -runtime: "typescript" -target: "local" diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 00000000..6e9f0e35 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,102 @@ +#!/usr/bin/env node +import { bootstrapRuntime } from './runtime/bootstrap-runtime.ts'; +import { buildCliToolCatalog } from './cli/cli-tool-catalog.ts'; +import { buildYargsApp } from './cli/yargs-app.ts'; +import { getSocketPath, getWorkspaceKey, resolveWorkspaceRoot } from './daemon/socket-path.ts'; +import { startMcpServer } from './server/start-mcp-server.ts'; +import { listCliWorkflowIdsFromManifest } from './runtime/tool-catalog.ts'; +import { flushAndCloseSentry, initSentry, recordBootstrapDurationMetric } from './utils/sentry.ts'; + +function findTopLevelCommand(argv: string[]): string | undefined { + const flagsWithValue = new Set(['--socket', '--log-level', '--style']); + let skipNext = false; + + for (const token of argv) { + if (skipNext) { + skipNext = false; + continue; + } + + if (token.startsWith('-')) { + if (flagsWithValue.has(token)) { + skipNext = true; + } + continue; + } + + return token; + } + + return undefined; +} + +async function main(): Promise { + const cliBootstrapStartedAt = Date.now(); + if (process.argv.includes('mcp')) { + await startMcpServer(); + return; + } + initSentry({ mode: 'cli' }); + + // CLI mode uses disableSessionDefaults to show all tool parameters as flags + const result = await bootstrapRuntime({ + runtime: 'cli', + configOverrides: { + disableSessionDefaults: true, + }, + }); + + // Compute workspace context for daemon routing + const workspaceRoot = resolveWorkspaceRoot({ + cwd: result.runtime.cwd, + projectConfigPath: result.configPath, + }); + + const defaultSocketPath = getSocketPath({ + cwd: result.runtime.cwd, + projectConfigPath: result.configPath, + }); + + const workspaceKey = getWorkspaceKey({ + cwd: result.runtime.cwd, + projectConfigPath: result.configPath, + }); + + const cliExposedWorkflowIds = await listCliWorkflowIdsFromManifest({ + excludeWorkflows: ['session-management', 'workflow-discovery'], + }); + const topLevelCommand = findTopLevelCommand(process.argv.slice(2)); + const discoveryMode = topLevelCommand === 'xcode-ide' ? 'quick' : 'none'; + + // CLI uses a manifest-resolved catalog plus daemon-backed xcode-ide dynamic tools. + const catalog = await buildCliToolCatalog({ + socketPath: defaultSocketPath, + workspaceRoot, + cliExposedWorkflowIds, + logLevel: result.runtime.config.debug ? 'info' : undefined, + discoveryMode, + }); + + const yargsApp = buildYargsApp({ + catalog, + runtimeConfig: result.runtime.config, + defaultSocketPath, + workspaceRoot, + workspaceKey, + workflowNames: cliExposedWorkflowIds, + cliExposedWorkflowIds, + }); + + recordBootstrapDurationMetric('cli', Date.now() - cliBootstrapStartedAt); + await yargsApp.parseAsync(); +} + +main() + .then(async () => { + await flushAndCloseSentry(2000); + }) + .catch(async (err) => { + console.error(err instanceof Error ? err.message : String(err)); + await flushAndCloseSentry(2000); + process.exit(1); + }); diff --git a/src/cli/cli-tool-catalog.ts b/src/cli/cli-tool-catalog.ts new file mode 100644 index 00000000..235584a7 --- /dev/null +++ b/src/cli/cli-tool-catalog.ts @@ -0,0 +1,183 @@ +import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; +import type { ToolSchemaShape } from '../core/plugin-types.ts'; +import { startDaemonBackground } from './daemon-control.ts'; +import { DaemonClient } from './daemon-client.ts'; +import { buildCliToolCatalogFromManifest, createToolCatalog } from '../runtime/tool-catalog.ts'; +import type { ToolCatalog, ToolDefinition } from '../runtime/types.ts'; +import { toKebabCase } from '../runtime/naming.ts'; +import type { ToolResponse } from '../types/common.ts'; +import { jsonSchemaToZod } from '../integrations/xcode-tools-bridge/jsonschema-to-zod.ts'; +import { XcodeIdeToolService } from '../integrations/xcode-tools-bridge/tool-service.ts'; +import { toLocalToolName } from '../integrations/xcode-tools-bridge/registry.ts'; +import { log } from '../utils/logging/index.ts'; + +interface BuildCliToolCatalogOptions { + socketPath: string; + workspaceRoot: string; + cliExposedWorkflowIds: string[]; + logLevel?: string; + discoveryMode?: 'none' | 'quick'; +} + +type JsonSchemaObject = { + properties?: Record; + required?: unknown[]; +}; + +function jsonSchemaToToolSchemaShape(inputSchema: unknown): ToolSchemaShape { + if (!inputSchema || typeof inputSchema !== 'object') { + return {}; + } + + const schema = inputSchema as JsonSchemaObject; + const properties = schema.properties; + if (!properties || typeof properties !== 'object' || Array.isArray(properties)) { + return {}; + } + + const requiredFields = new Set( + Array.isArray(schema.required) + ? schema.required.filter((name): name is string => typeof name === 'string') + : [], + ); + + const shape: ToolSchemaShape = {}; + for (const [name, propertySchema] of Object.entries(properties)) { + const zodSchema = jsonSchemaToZod(propertySchema); + shape[name] = requiredFields.has(name) ? zodSchema : zodSchema.optional(); + } + + return shape; +} + +function buildDaemonEnvOverrides(opts: BuildCliToolCatalogOptions): Record { + const env: Record = {}; + + if (opts.logLevel) { + env.XCODEBUILDMCP_DAEMON_LOG_LEVEL = opts.logLevel; + } + + return env; +} + +async function invokeRemoteToolOneShot( + remoteToolName: string, + args: Record, +): Promise { + const service = new XcodeIdeToolService(); + service.setWorkflowEnabled(true); + try { + const response = await service.invokeTool(remoteToolName, args); + return response as unknown as ToolResponse; + } finally { + await service.disconnect(); + } +} + +type DynamicBridgeTool = { + name: string; + description?: string; + inputSchema?: unknown; + annotations?: ToolAnnotations; +}; + +function createCliXcodeProxyTool(remoteTool: DynamicBridgeTool): ToolDefinition { + const cliSchema = jsonSchemaToToolSchemaShape(remoteTool.inputSchema); + + return { + cliName: toKebabCase(remoteTool.name), + mcpName: toLocalToolName(remoteTool.name), + workflow: 'xcode-ide', + description: remoteTool.description ?? '', + annotations: remoteTool.annotations, + mcpSchema: cliSchema, + cliSchema, + stateful: false, + xcodeIdeRemoteToolName: remoteTool.name, + handler: async (params): Promise => { + return invokeRemoteToolOneShot(remoteTool.name, params); + }, + }; +} + +async function loadDaemonBackedXcodeProxyTools( + opts: BuildCliToolCatalogOptions, +): Promise { + const discoveryMode = opts.discoveryMode ?? 'none'; + const quickMode = discoveryMode === 'quick'; + const daemonClient = new DaemonClient({ + socketPath: opts.socketPath, + timeout: quickMode ? 400 : 250, + }); + + try { + const isRunning = await daemonClient.isRunning(); + if (!isRunning) { + if (!quickMode) { + return []; + } + + // Fast path for CLI help/discovery: fire-and-forget daemon startup to avoid + // blocking command rendering while still warming a long-lived bridge session. + try { + startDaemonBackground({ + socketPath: opts.socketPath, + workspaceRoot: opts.workspaceRoot, + env: buildDaemonEnvOverrides(opts), + }); + } catch (startError) { + const message = startError instanceof Error ? startError.message : String(startError); + log('warning', `[xcode-ide] Failed to start daemon in background: ${message}`); + } + return []; + } + + const tools = await daemonClient.listXcodeIdeTools({ + refresh: false, + prefetch: quickMode, + }); + + return tools.map( + (tool): ToolDefinition => + createCliXcodeProxyTool({ + name: tool.remoteName, + description: tool.description, + inputSchema: tool.inputSchema, + annotations: tool.annotations, + }), + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (quickMode) { + log('warning', `[xcode-ide] CLI daemon-backed bridge discovery failed: ${message}`); + } else { + log('debug', `[xcode-ide] CLI cached bridge discovery skipped: ${message}`); + } + return []; + } +} + +/** + * Build a tool catalog for CLI usage using the manifest system. + * CLI visibility is determined by manifest availability and predicates. + */ +export async function buildCliToolCatalog(opts: BuildCliToolCatalogOptions): Promise { + const manifestCatalog = await buildCliToolCatalogFromManifest(); + + if (!opts.cliExposedWorkflowIds.includes('xcode-ide')) { + return manifestCatalog; + } + + const dynamicTools = await loadDaemonBackedXcodeProxyTools(opts); + if (dynamicTools.length === 0) { + return manifestCatalog; + } + + const existingCliNames = new Set(manifestCatalog.tools.map((tool) => tool.cliName)); + const mergedTools = [ + ...manifestCatalog.tools, + ...dynamicTools.filter((tool) => !existingCliNames.has(tool.cliName)), + ]; + + return createToolCatalog(mergedTools); +} diff --git a/src/cli/commands/daemon.ts b/src/cli/commands/daemon.ts new file mode 100644 index 00000000..8acc3079 --- /dev/null +++ b/src/cli/commands/daemon.ts @@ -0,0 +1,344 @@ +import type { Argv } from 'yargs'; +import { readFileSync } from 'node:fs'; +import { DaemonClient } from '../daemon-client.ts'; +import { + ensureDaemonRunning, + startDaemonForeground, + DEFAULT_DAEMON_STARTUP_TIMEOUT_MS, +} from '../daemon-control.ts'; +import { + listDaemonRegistryEntries, + readDaemonRegistryEntry, +} from '../../daemon/daemon-registry.ts'; + +export interface DaemonCommandsOptions { + defaultSocketPath: string; + workspaceRoot: string; + workspaceKey: string; +} + +function writeLine(text: string): void { + process.stdout.write(`${text}\n`); +} + +/** + * Register daemon management commands. + */ +export function registerDaemonCommands(app: Argv, opts: DaemonCommandsOptions): void { + app.command( + 'daemon ', + 'Manage the xcodebuildmcp daemon', + (yargs) => { + return yargs + .positional('action', { + describe: 'Daemon action', + choices: ['start', 'stop', 'status', 'restart', 'list', 'logs'] as const, + demandOption: true, + }) + .option('daemon-log-path', { + type: 'string', + describe: 'Override daemon log file path (start/restart only)', + }) + .option('daemon-log-level', { + type: 'string', + describe: 'Set daemon log level (start/restart only)', + choices: [ + 'none', + 'emergency', + 'alert', + 'critical', + 'error', + 'warning', + 'notice', + 'info', + 'debug', + ] as const, + }) + .option('tail', { + type: 'number', + default: 200, + describe: 'Number of log lines to show (logs action)', + }) + .option('foreground', { + alias: 'f', + type: 'boolean', + default: false, + describe: 'Run daemon in foreground (for debugging)', + }) + .option('json', { + type: 'boolean', + default: false, + describe: 'Output in JSON format (for list command)', + }) + .option('all', { + type: 'boolean', + default: true, + describe: 'Include stale daemons in list', + }); + }, + async (argv) => { + const action = argv.action as string; + // Socket path comes from global --socket which defaults to workspace socket + const socketPath = argv.socket as string; + const client = new DaemonClient({ socketPath }); + + const logPath = argv['daemon-log-path'] as string | undefined; + const logLevel = argv['daemon-log-level'] as string | undefined; + const tail = argv.tail as number | undefined; + + switch (action) { + case 'status': + await handleStatus(client, opts.workspaceRoot, opts.workspaceKey); + break; + case 'stop': + await handleStop(client); + break; + case 'start': + await handleStart(socketPath, opts.workspaceRoot, argv.foreground as boolean, { + logPath, + logLevel, + }); + break; + case 'restart': + await handleRestart(client, socketPath, opts.workspaceRoot, argv.foreground as boolean, { + logPath, + logLevel, + }); + break; + case 'list': + await handleList(argv.json as boolean, argv.all as boolean); + break; + case 'logs': + await handleLogs(opts.workspaceKey, tail ?? 200); + break; + } + }, + ); +} + +async function handleStatus( + client: DaemonClient, + workspaceRoot: string, + workspaceKey: string, +): Promise { + try { + const status = await client.status(); + writeLine('Daemon Status: Running'); + writeLine(` PID: ${status.pid}`); + writeLine(` Workspace: ${status.workspaceRoot ?? workspaceRoot}`); + writeLine(` Socket: ${status.socketPath}`); + if (status.logPath) { + writeLine(` Logs: ${status.logPath}`); + } + writeLine(` Started: ${status.startedAt}`); + writeLine(` Tools: ${status.toolCount}`); + writeLine(` Workflows: ${status.enabledWorkflows.join(', ') || '(default)'}`); + } catch (err) { + if (err instanceof Error && err.message.includes('not running')) { + writeLine('Daemon Status: Not running'); + writeLine(` Workspace: ${workspaceRoot}`); + const entry = readDaemonRegistryEntry(workspaceKey); + if (entry?.logPath) { + writeLine(` Logs: ${entry.logPath}`); + } + } else { + console.error('Error:', err instanceof Error ? err.message : String(err)); + process.exitCode = 1; + } + } +} + +async function handleStop(client: DaemonClient): Promise { + try { + await client.stop(); + writeLine('Daemon stopped'); + } catch (err) { + if (err instanceof Error && err.message.includes('not running')) { + writeLine('Daemon is not running'); + } else { + console.error('Error:', err instanceof Error ? err.message : String(err)); + process.exitCode = 1; + } + } +} + +async function handleStart( + socketPath: string, + workspaceRoot: string, + foreground: boolean, + logOpts: { logPath?: string; logLevel?: string }, +): Promise { + const client = new DaemonClient({ socketPath }); + + // Check if already running + const isRunning = await client.isRunning(); + if (isRunning) { + writeLine('Daemon is already running'); + return; + } + + const envOverrides: Record = {}; + if (logOpts.logPath) { + envOverrides.XCODEBUILDMCP_DAEMON_LOG_PATH = logOpts.logPath; + } + if (logOpts.logLevel) { + envOverrides.XCODEBUILDMCP_DAEMON_LOG_LEVEL = logOpts.logLevel; + } + + if (foreground) { + // Run in foreground (useful for debugging) + writeLine('Starting daemon in foreground...'); + writeLine(`Workspace: ${workspaceRoot}`); + writeLine(`Socket: ${socketPath}`); + writeLine('Press Ctrl+C to stop\n'); + + const exitCode = await startDaemonForeground({ + socketPath, + workspaceRoot, + env: Object.keys(envOverrides).length > 0 ? envOverrides : undefined, + }); + process.exit(exitCode); + } else { + // Run in background with auto-start helper + try { + await ensureDaemonRunning({ + socketPath, + workspaceRoot, + startupTimeoutMs: DEFAULT_DAEMON_STARTUP_TIMEOUT_MS, + env: Object.keys(envOverrides).length > 0 ? envOverrides : undefined, + }); + writeLine('Daemon started'); + writeLine(`Workspace: ${workspaceRoot}`); + writeLine(`Socket: ${socketPath}`); + } catch (err) { + console.error('Failed to start daemon:', err instanceof Error ? err.message : String(err)); + process.exitCode = 1; + } + } +} + +async function handleRestart( + client: DaemonClient, + socketPath: string, + workspaceRoot: string, + foreground: boolean, + logOpts: { logPath?: string; logLevel?: string }, +): Promise { + // Try to stop existing daemon + try { + const isRunning = await client.isRunning(); + if (isRunning) { + writeLine('Stopping existing daemon...'); + await client.stop(); + // Wait for it to fully stop + await new Promise((resolve) => setTimeout(resolve, 500)); + } + } catch { + // Ignore errors during stop + } + + // Start new daemon + await handleStart(socketPath, workspaceRoot, foreground, logOpts); +} + +interface DaemonListEntry { + workspaceKey: string; + workspaceRoot: string; + socketPath: string; + pid: number; + startedAt: string; + version: string; + status: 'running' | 'stale'; +} + +async function handleLogs(workspaceKey: string, tail: number): Promise { + const entry = readDaemonRegistryEntry(workspaceKey); + const logPath = entry?.logPath; + + if (!logPath) { + writeLine('No daemon log path available for this workspace.'); + return; + } + + let content = ''; + try { + content = readFileSync(logPath, 'utf8'); + } catch (err) { + console.error('Error:', err instanceof Error ? err.message : String(err)); + process.exitCode = 1; + return; + } + + const lines = content.split(/\r?\n/); + const limited = lines.slice(Math.max(0, lines.length - Math.max(1, tail))); + writeLine(limited.join('\n')); +} + +async function handleList(jsonOutput: boolean, includeStale: boolean): Promise { + const registryEntries = listDaemonRegistryEntries(); + + if (registryEntries.length === 0) { + if (jsonOutput) { + writeLine(JSON.stringify([])); + } else { + writeLine('No daemons found'); + } + return; + } + + // Check each daemon's status + const entries: DaemonListEntry[] = []; + + for (const entry of registryEntries) { + const client = new DaemonClient({ + socketPath: entry.socketPath, + timeout: 1000, // Short timeout for status check + }); + + let status: 'running' | 'stale' = 'stale'; + try { + await client.status(); + status = 'running'; + } catch { + status = 'stale'; + } + + if (status === 'stale' && !includeStale) { + continue; + } + + entries.push({ + workspaceKey: entry.workspaceKey, + workspaceRoot: entry.workspaceRoot, + socketPath: entry.socketPath, + pid: entry.pid, + startedAt: entry.startedAt, + version: entry.version, + status, + }); + } + + if (jsonOutput) { + writeLine(JSON.stringify(entries, null, 2)); + } else { + if (entries.length === 0) { + writeLine('No daemons found'); + return; + } + + writeLine('Daemons:\n'); + for (const entry of entries) { + const statusLabel = entry.status === 'running' ? '[running]' : '[stale]'; + writeLine(` ${statusLabel} ${entry.workspaceKey}`); + writeLine(` Workspace: ${entry.workspaceRoot}`); + writeLine(` PID: ${entry.pid}`); + writeLine(` Started: ${entry.startedAt}`); + writeLine(` Version: ${entry.version}`); + writeLine(''); + } + + const runningCount = entries.filter((e) => e.status === 'running').length; + const staleCount = entries.filter((e) => e.status === 'stale').length; + writeLine(`Total: ${entries.length} (${runningCount} running, ${staleCount} stale)`); + } +} diff --git a/src/cli/commands/mcp.ts b/src/cli/commands/mcp.ts new file mode 100644 index 00000000..8fb48cf3 --- /dev/null +++ b/src/cli/commands/mcp.ts @@ -0,0 +1,11 @@ +import type { Argv } from 'yargs'; +import { startMcpServer } from '../../server/start-mcp-server.ts'; + +/** + * Register the `mcp` command to start the MCP server. + */ +export function registerMcpCommand(app: Argv): void { + app.command('mcp', 'Start the MCP server (for use with MCP clients)', {}, async () => { + await startMcpServer(); + }); +} diff --git a/src/cli/commands/tools.ts b/src/cli/commands/tools.ts new file mode 100644 index 00000000..c18a2f36 --- /dev/null +++ b/src/cli/commands/tools.ts @@ -0,0 +1,271 @@ +import type { Argv } from 'yargs'; +import { formatToolList } from '../output.ts'; +import { + loadManifest, + type ResolvedManifest, + type ToolManifestEntry, +} from '../../core/manifest/load-manifest.ts'; +import { getEffectiveCliName } from '../../core/manifest/schema.ts'; +import { isWorkflowEnabledForRuntime, isToolExposedForRuntime } from '../../visibility/exposure.ts'; +import type { PredicateContext } from '../../visibility/predicate-types.ts'; +import { getConfig } from '../../utils/config-store.ts'; + +const CLI_EXCLUDED_WORKFLOWS = new Set(['session-management', 'workflow-discovery']); + +function writeLine(text: string): void { + process.stdout.write(`${text}\n`); +} + +type ToolListItem = { + cliName: string; + command: string; + workflow: string; + description: string; + stateful: boolean; + isCanonical: boolean; + originWorkflow?: string; +}; + +type JsonToolBase = { + name: string; + command: string; + description: string; + stateful: boolean; +}; + +type JsonTool = JsonToolBase & { + canonicalWorkflow?: string; +}; + +type JsonToolWithWorkflow = JsonTool & { + workflow: string; +}; + +function toJsonToolBase(tool: ToolListItem): JsonToolBase { + return { + name: tool.cliName, + command: tool.command, + description: tool.description, + stateful: tool.stateful, + }; +} + +function withCanonicalWorkflow( + tool: ToolListItem, + base: T, +): T & { + canonicalWorkflow?: string; +} { + if (!tool.isCanonical && tool.originWorkflow) { + return { ...base, canonicalWorkflow: tool.originWorkflow }; + } + + return base; +} + +function toFlatJsonTool(tool: ToolListItem): JsonToolWithWorkflow { + const base = { + workflow: tool.workflow, + ...toJsonToolBase(tool), + }; + + return withCanonicalWorkflow(tool, base); +} + +function toGroupedJsonTool(tool: ToolListItem): JsonTool { + return withCanonicalWorkflow(tool, toJsonToolBase(tool)); +} + +/** + * Build CLI predicate context. + * CLI is never running under Xcode and never has Xcode tools active. + */ +async function buildCliPredicateContext(): Promise { + return { + runtime: 'cli', + config: getConfig(), + runningUnderXcode: false, + }; +} + +/** + * Build tool list from YAML manifest with predicate filtering. + */ +async function buildToolList(manifest: ResolvedManifest): Promise { + const tools: ToolListItem[] = []; + const seenToolIds = new Set(); + const ctx = await buildCliPredicateContext(); + + // Get all CLI-available workflows that pass predicate checks + const cliWorkflows = Array.from(manifest.workflows.values()).filter( + (wf) => !CLI_EXCLUDED_WORKFLOWS.has(wf.id) && isWorkflowEnabledForRuntime(wf, ctx), + ); + + for (const workflow of cliWorkflows) { + for (const toolId of workflow.tools) { + const tool = manifest.tools.get(toolId); + if (!tool) continue; + + // Check tool availability and predicates for CLI + if (!isToolExposedForRuntime(tool, ctx)) continue; + + const cliName = getEffectiveCliName(tool); + + // Determine if this is a canonical tool or re-export + const isCanonical = isToolCanonicalInWorkflow(tool, workflow.id); + const originWorkflow = isCanonical ? undefined : getCanonicalWorkflow(tool); + + // Track seen tools to avoid duplicates + const toolKey = `${workflow.id}:${toolId}`; + if (seenToolIds.has(toolKey)) continue; + seenToolIds.add(toolKey); + + tools.push({ + cliName, + command: `${workflow.id} ${cliName}`, + workflow: workflow.id, + description: tool.description ?? '', + stateful: tool.routing?.stateful ?? false, + isCanonical, + originWorkflow, + }); + } + } + + return tools; +} + +/** + * Determine if a tool is canonical in a given workflow. + * A tool is canonical if its module path matches the workflow ID. + */ +function isToolCanonicalInWorkflow(tool: ToolManifestEntry, workflowId: string): boolean { + // Check if the module path contains the workflow ID + // e.g., "mcp/tools/simulator/build_sim" is canonical for "simulator" + const moduleParts = tool.module.split('/'); + const workflowPart = moduleParts[2]; // mcp/tools// + return workflowPart === workflowId; +} + +/** + * Get the canonical workflow for a tool based on its module path. + */ +function getCanonicalWorkflow(tool: ToolManifestEntry): string | undefined { + const moduleParts = tool.module.split('/'); + if (moduleParts.length >= 3) { + return moduleParts[2]; // mcp/tools// + } + return undefined; +} + +/** + * Register the 'tools' command for listing available tools. + */ +export function registerToolsCommand(app: Argv): void { + app.command( + 'tools', + 'List available tools', + (yargs) => { + return yargs + .option('flat', { + alias: 'f', + type: 'boolean', + default: false, + describe: 'Show flat list instead of grouped by workflow', + }) + .option('verbose', { + alias: 'v', + type: 'boolean', + default: false, + describe: 'Show full descriptions', + }) + .option('json', { + type: 'boolean', + default: false, + describe: 'Output as JSON', + }) + .option('workflow', { + alias: 'w', + type: 'string', + describe: 'Filter by workflow name', + }); + }, + async (argv) => { + const manifest = loadManifest(); + let tools = await buildToolList(manifest); + + // Filter by workflow if specified + if (argv.workflow) { + const workflowFilter = (argv.workflow as string).toLowerCase(); + tools = tools.filter((t) => t.workflow.toLowerCase() === workflowFilter); + } + + if (argv.json) { + if (argv.flat) { + const flatTools = [...tools] + .sort((a, b) => { + const aKey = `${a.workflow} ${a.cliName}`; + const bKey = `${b.workflow} ${b.cliName}`; + return aKey.localeCompare(bKey); + }) + .map((tool) => toFlatJsonTool(tool)); + + const canonicalCount = flatTools.filter((t) => !t.canonicalWorkflow).length; + writeLine( + JSON.stringify( + { + canonicalToolCount: canonicalCount, + toolCount: flatTools.length, + tools: flatTools, + }, + null, + 2, + ), + ); + return; + } + + const workflows = new Map(); + for (const tool of tools) { + const workflowTools = workflows.get(tool.workflow) ?? []; + workflowTools.push(tool); + workflows.set(tool.workflow, workflowTools); + } + + const grouped = Array.from(workflows.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([workflow, workflowTools]) => ({ + workflow, + tools: [...workflowTools] + .sort((a, b) => a.cliName.localeCompare(b.cliName)) + .map((tool) => toGroupedJsonTool(tool)), + })); + + const canonicalCount = tools.filter((t) => t.isCanonical).length; + writeLine( + JSON.stringify( + { + workflowCount: grouped.length, + canonicalToolCount: canonicalCount, + toolCount: tools.length, + workflows: grouped, + }, + null, + 2, + ), + ); + } else { + const totalCount = tools.length; + const canonicalCount = tools.filter((t) => t.isCanonical).length; + writeLine(`Available tools (${canonicalCount} canonical, ${totalCount} total):\n`); + // Default to grouped view (use --flat for flat list) + writeLine( + formatToolList(tools, { + grouped: !argv.flat, + verbose: argv.verbose as boolean, + }), + ); + } + }, + ); +} diff --git a/src/cli/daemon-client.ts b/src/cli/daemon-client.ts new file mode 100644 index 00000000..6c25a1f7 --- /dev/null +++ b/src/cli/daemon-client.ts @@ -0,0 +1,191 @@ +import net from 'node:net'; +import { randomUUID } from 'node:crypto'; +import { writeFrame, createFrameReader } from '../daemon/framing.ts'; +import { + DAEMON_PROTOCOL_VERSION, + type DaemonRequest, + type DaemonResponse, + type DaemonMethod, + type ToolInvokeParams, + type ToolInvokeResult, + type DaemonStatusResult, + type ToolListItem, + type XcodeIdeListParams, + type XcodeIdeListResult, + type XcodeIdeToolListItem, + type XcodeIdeInvokeParams, + type XcodeIdeInvokeResult, +} from '../daemon/protocol.ts'; +import type { ToolResponse } from '../types/common.ts'; +import { getSocketPath } from '../daemon/socket-path.ts'; + +export interface DaemonClientOptions { + socketPath?: string; + timeout?: number; +} + +export class DaemonClient { + private socketPath: string; + private timeout: number; + + constructor(opts: DaemonClientOptions = {}) { + this.socketPath = opts.socketPath ?? getSocketPath(); + this.timeout = opts.timeout ?? 30000; + } + + /** + * Send a request to the daemon and wait for a response. + */ + async request(method: DaemonMethod, params?: unknown): Promise { + const id = randomUUID(); + const req: DaemonRequest = { + v: DAEMON_PROTOCOL_VERSION, + id, + method, + params, + }; + + return new Promise((resolve, reject) => { + const socket = net.createConnection(this.socketPath); + let resolved = false; + + const cleanup = (): void => { + if (!resolved) { + resolved = true; + socket.destroy(); + } + }; + + const timeoutId = setTimeout(() => { + cleanup(); + reject(new Error(`Daemon request timed out after ${this.timeout}ms`)); + }, this.timeout); + + socket.on('error', (err) => { + clearTimeout(timeoutId); + cleanup(); + if (err.message.includes('ECONNREFUSED') || err.message.includes('ENOENT')) { + reject(new Error('Daemon is not running. Start it with: xcodebuildmcp daemon start')); + } else { + reject(err); + } + }); + + const onData = createFrameReader( + (msg) => { + const res = msg as DaemonResponse; + if (res.id !== id) return; + + clearTimeout(timeoutId); + resolved = true; + socket.end(); + + if (res.error) { + reject(new Error(`${res.error.code}: ${res.error.message}`)); + } else { + resolve(res.result as TResult); + } + }, + (err) => { + clearTimeout(timeoutId); + cleanup(); + reject(err); + }, + ); + + socket.on('data', onData); + socket.on('connect', () => { + writeFrame(socket, req); + }); + }); + } + + /** + * Get daemon status. + */ + async status(): Promise { + return this.request('daemon.status'); + } + + /** + * Stop the daemon. + */ + async stop(): Promise { + await this.request<{ ok: boolean }>('daemon.stop'); + } + + /** + * List available tools. + */ + async listTools(): Promise { + return this.request('tool.list'); + } + + /** + * Invoke a tool. + */ + async invokeTool(tool: string, args: Record): Promise { + const result = await this.request('tool.invoke', { + tool, + args, + } satisfies ToolInvokeParams); + return result.response; + } + + /** + * List dynamic xcode-ide bridge tools from the daemon-managed bridge session. + */ + async listXcodeIdeTools(params?: XcodeIdeListParams): Promise { + const result = await this.request('xcode-ide.list', params); + return result.tools; + } + + /** + * Invoke a dynamic xcode-ide bridge tool through the daemon-managed bridge session. + */ + async invokeXcodeIdeTool( + remoteTool: string, + args: Record, + ): Promise { + const result = await this.request('xcode-ide.invoke', { + remoteTool, + args, + } satisfies XcodeIdeInvokeParams); + return result.response as ToolResponse; + } + + /** + * Check if daemon is running by attempting to connect. + */ + async isRunning(): Promise { + return new Promise((resolve) => { + const socket = net.createConnection(this.socketPath); + let settled = false; + + const finish = (value: boolean): void => { + if (settled) return; + settled = true; + try { + socket.destroy(); + } catch { + // ignore + } + resolve(value); + }; + + const timeoutId = setTimeout(() => { + finish(false); + }, this.timeout); + + socket.on('connect', () => { + clearTimeout(timeoutId); + finish(true); + }); + + socket.on('error', () => { + clearTimeout(timeoutId); + finish(false); + }); + }); + } +} diff --git a/src/cli/daemon-control.ts b/src/cli/daemon-control.ts new file mode 100644 index 00000000..eb5ad843 --- /dev/null +++ b/src/cli/daemon-control.ts @@ -0,0 +1,168 @@ +import { spawn } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; +import { existsSync } from 'node:fs'; +import { DaemonClient } from './daemon-client.ts'; + +/** + * Default timeout for daemon startup in milliseconds. + */ +export const DEFAULT_DAEMON_STARTUP_TIMEOUT_MS = 5000; + +/** + * Default polling interval when waiting for daemon to be ready. + */ +export const DEFAULT_POLL_INTERVAL_MS = 100; + +/** + * Get the path to the daemon executable. + */ +export function getDaemonExecutablePath(): string { + // In the built output, this file is build/cli/daemon-control.js and daemon is build/daemon.js. + const currentFile = fileURLToPath(import.meta.url); + const buildDir = dirname(currentFile); + const candidateJs = resolve(buildDir, '..', 'daemon.js'); + if (existsSync(candidateJs)) { + return candidateJs; + } + + // Fallback for source/dev layouts. + return resolve(buildDir, '..', 'daemon.ts'); +} + +export interface StartDaemonBackgroundOptions { + socketPath: string; + workspaceRoot?: string; + env?: Record; +} + +/** + * Start the daemon in the background (detached mode). + * Does not wait for the daemon to be ready. + */ +export function startDaemonBackground(opts: StartDaemonBackgroundOptions): void { + const daemonPath = getDaemonExecutablePath(); + + const child = spawn(process.execPath, [daemonPath], { + detached: true, + stdio: 'ignore', + cwd: opts.workspaceRoot, + env: { + ...process.env, + ...opts.env, + XCODEBUILDMCP_SOCKET: opts.socketPath, + XCODEBUILDCLI_SOCKET: opts.socketPath, + }, + }); + + child.unref(); +} + +export interface WaitForDaemonReadyOptions { + socketPath: string; + timeoutMs: number; + pollIntervalMs?: number; +} + +/** + * Wait for the daemon to be ready by polling status. + * Throws if the daemon doesn't respond within the timeout. + */ +export async function waitForDaemonReady(opts: WaitForDaemonReadyOptions): Promise { + const client = new DaemonClient({ + socketPath: opts.socketPath, + timeout: Math.min(opts.timeoutMs, 2000), // Short timeout for each status check + }); + + const pollInterval = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS; + const startTime = Date.now(); + + while (Date.now() - startTime < opts.timeoutMs) { + try { + // Use status() to confirm protocol handler is ready (not just connect) + await client.status(); + return; // Success + } catch { + // Not ready yet, wait and retry + await new Promise((resolve) => setTimeout(resolve, pollInterval)); + } + } + + throw new Error( + `Daemon failed to start within ${opts.timeoutMs}ms. ` + + `Check if another daemon is running or if there are permission issues.`, + ); +} + +export interface EnsureDaemonRunningOptions { + socketPath: string; + workspaceRoot?: string; + startupTimeoutMs?: number; + env?: Record; +} + +/** + * Ensure the daemon is running, starting it if necessary. + * Returns when the daemon is ready to accept requests. + * + * This is the main entry point for auto-start behavior. + */ +export async function ensureDaemonRunning(opts: EnsureDaemonRunningOptions): Promise { + const client = new DaemonClient({ socketPath: opts.socketPath }); + const timeoutMs = opts.startupTimeoutMs ?? DEFAULT_DAEMON_STARTUP_TIMEOUT_MS; + + // Check if already running + const isRunning = await client.isRunning(); + if (isRunning) { + return; + } + + // Start daemon in background + const startOptions: StartDaemonBackgroundOptions = { + socketPath: opts.socketPath, + workspaceRoot: opts.workspaceRoot, + }; + + if (opts.env) { + startOptions.env = { ...opts.env }; + } + + startDaemonBackground(startOptions); + + // Wait for it to be ready + await waitForDaemonReady({ + socketPath: opts.socketPath, + timeoutMs, + }); +} + +export interface StartDaemonForegroundOptions { + socketPath: string; + workspaceRoot?: string; + env?: Record; +} + +/** + * Start the daemon in the foreground (blocking). + * Used for debugging. The function returns when the daemon exits. + */ +export function startDaemonForeground(opts: StartDaemonForegroundOptions): Promise { + const daemonPath = getDaemonExecutablePath(); + + return new Promise((resolve) => { + const child = spawn(process.execPath, [daemonPath], { + stdio: 'inherit', + cwd: opts.workspaceRoot, + env: { + ...process.env, + ...opts.env, + XCODEBUILDMCP_SOCKET: opts.socketPath, + XCODEBUILDCLI_SOCKET: opts.socketPath, + }, + }); + + child.on('exit', (code) => { + resolve(code ?? 0); + }); + }); +} diff --git a/src/cli/output.ts b/src/cli/output.ts new file mode 100644 index 00000000..dca2ddd7 --- /dev/null +++ b/src/cli/output.ts @@ -0,0 +1,114 @@ +import type { ToolResponse, OutputStyle } from '../types/common.ts'; +import { processToolResponse } from '../utils/responses/index.ts'; + +export type OutputFormat = 'text' | 'json'; + +export interface PrintToolResponseOptions { + format?: OutputFormat; + style?: OutputStyle; +} + +function writeLine(text: string): void { + process.stdout.write(`${text}\n`); +} + +/** + * Print a tool response to the terminal. + * Applies runtime-aware rendering of next steps for CLI output. + */ +export function printToolResponse( + response: ToolResponse, + options: PrintToolResponseOptions = {}, +): void { + const { format = 'text', style = 'normal' } = options; + + // Apply next steps rendering for CLI runtime + const processed = processToolResponse(response, 'cli', style); + + if (format === 'json') { + writeLine(JSON.stringify(processed, null, 2)); + } else { + printToolResponseText(processed); + } + + if (response.isError) { + process.exitCode = 1; + } +} + +/** + * Print tool response content as text. + */ +function printToolResponseText(response: ToolResponse): void { + for (const item of response.content ?? []) { + if (item.type === 'text') { + writeLine(item.text); + } else if (item.type === 'image') { + // For images, show a placeholder with metadata + const sizeKb = Math.round((item.data.length * 3) / 4 / 1024); + writeLine(`[Image: ${item.mimeType}, ~${sizeKb}KB base64]`); + writeLine(' Use --output json to get the full image data'); + } + } +} + +/** + * Format a tool list for display. + */ +export function formatToolList( + tools: Array<{ cliName: string; workflow: string; description?: string; stateful: boolean }>, + options: { grouped?: boolean; verbose?: boolean } = {}, +): string { + const lines: string[] = []; + + if (options.grouped) { + const byWorkflow = new Map(); + for (const tool of tools) { + const existing = byWorkflow.get(tool.workflow) ?? []; + byWorkflow.set(tool.workflow, [...existing, tool]); + } + + const sortedWorkflows = [...byWorkflow.keys()].sort(); + for (const workflow of sortedWorkflows) { + lines.push(`\n${workflow}:`); + const workflowTools = byWorkflow.get(workflow) ?? []; + const sortedTools = workflowTools.sort((a, b) => a.cliName.localeCompare(b.cliName)); + + for (const tool of sortedTools) { + const statefulMarker = tool.stateful ? ' [stateful]' : ''; + if (options.verbose && tool.description) { + lines.push(` ${tool.cliName}${statefulMarker}`); + lines.push(` ${tool.description}`); + } else { + const desc = tool.description ? ` - ${truncate(tool.description, 60)}` : ''; + lines.push(` ${tool.cliName}${statefulMarker}${desc}`); + } + } + } + } else { + const sortedTools = [...tools].sort((a, b) => { + const aFull = `${a.workflow} ${a.cliName}`; + const bFull = `${b.workflow} ${b.cliName}`; + return aFull.localeCompare(bFull); + }); + + for (const tool of sortedTools) { + const fullCommand = `${tool.workflow} ${tool.cliName}`; + const statefulMarker = tool.stateful ? ' [stateful]' : ''; + if (options.verbose && tool.description) { + lines.push(`${fullCommand}${statefulMarker}`); + lines.push(` ${tool.description}`); + } else { + const desc = tool.description ? ` - ${truncate(tool.description, 60)}` : ''; + lines.push(`${fullCommand}${statefulMarker}${desc}`); + } + } + } + + return lines.join('\n'); +} + +function truncate(str: string, maxLength: number): string { + if (str.length <= maxLength) return str; + return str.slice(0, maxLength - 3) + '...'; +} diff --git a/src/cli/register-tool-commands.ts b/src/cli/register-tool-commands.ts new file mode 100644 index 00000000..8f020506 --- /dev/null +++ b/src/cli/register-tool-commands.ts @@ -0,0 +1,189 @@ +import type { Argv } from 'yargs'; +import type { ToolCatalog, ToolDefinition } from '../runtime/types.ts'; +import type { OutputStyle } from '../types/common.ts'; +import { DefaultToolInvoker } from '../runtime/tool-invoker.ts'; +import { schemaToYargsOptions, getUnsupportedSchemaKeys } from './schema-to-yargs.ts'; +import { convertArgvToToolParams } from '../runtime/naming.ts'; +import { printToolResponse, type OutputFormat } from './output.ts'; +import { groupToolsByWorkflow } from '../runtime/tool-catalog.ts'; +import { getWorkflowMetadataFromManifest } from '../core/manifest/load-manifest.ts'; + +export interface RegisterToolCommandsOptions { + workspaceRoot: string; + cliExposedWorkflowIds?: string[]; + /** Workflows to register as command groups (even if currently empty) */ + workflowNames?: string[]; +} + +function buildXcodeIdeNoCommandsMessage(workflowName: string): string { + return ( + `No CLI commands are currently exposed for '${workflowName}'.\n\n` + + `If you're expecting Xcode IDE tools here:\n` + + `1. Make sure Xcode MCP Tools is enabled in:\n` + + ` Settings > Intelligence > Xcode Tools\n\n` + + `If Xcode showed an authorization prompt, make sure you clicked Allow.\n\n` + + `Then run this command again.` + ); +} + +/** + * Register all tool commands from the catalog with yargs, grouped by workflow. + */ +export function registerToolCommands( + app: Argv, + catalog: ToolCatalog, + opts: RegisterToolCommandsOptions, +): void { + const invoker = new DefaultToolInvoker(catalog); + const toolsByWorkflow = groupToolsByWorkflow(catalog); + const cliExposedWorkflowIds = opts.cliExposedWorkflowIds ?? [...toolsByWorkflow.keys()]; + const workflowNames = opts.workflowNames ?? [...toolsByWorkflow.keys()]; + const workflowMetadata = getWorkflowMetadataFromManifest(); + + for (const workflowName of workflowNames) { + const tools = toolsByWorkflow.get(workflowName) ?? []; + const workflowMeta = workflowMetadata[workflowName]; + const workflowDescription = workflowMeta?.name ?? workflowName; + + app.command( + workflowName, + workflowDescription, + (yargs) => { + // Hide root-level options from workflow help + yargs.option('log-level', { hidden: true }).option('style', { hidden: true }); + + // Register each tool as a subcommand under this workflow + for (const tool of tools) { + registerToolSubcommand(yargs, tool, invoker, opts, cliExposedWorkflowIds); + } + + if (tools.length === 0) { + const hint = + workflowName === 'xcode-ide' + ? buildXcodeIdeNoCommandsMessage(workflowName) + : `No CLI commands are currently exposed for '${workflowName}'.`; + + yargs.epilogue(hint); + return yargs.help(); + } + + return yargs.demandCommand(1, '').help(); + }, + () => { + if (tools.length === 0) { + console.error( + workflowName === 'xcode-ide' + ? buildXcodeIdeNoCommandsMessage(workflowName) + : `No CLI commands are currently exposed for '${workflowName}'.`, + ); + } + }, + ); + } +} + +/** + * Register a single tool as a subcommand. + */ +function registerToolSubcommand( + yargs: Argv, + tool: ToolDefinition, + invoker: DefaultToolInvoker, + opts: RegisterToolCommandsOptions, + cliExposedWorkflowIds: string[], +): void { + const yargsOptions = schemaToYargsOptions(tool.cliSchema); + const unsupportedKeys = getUnsupportedSchemaKeys(tool.cliSchema); + + const commandName = tool.cliName; + + yargs.command( + commandName, + tool.description ?? `Run the ${tool.mcpName} tool`, + (subYargs) => { + // Hide root-level options from tool help + subYargs.option('log-level', { hidden: true }).option('style', { hidden: true }); + + // Register schema-derived options (tool arguments) + const toolArgNames: string[] = []; + for (const [flagName, config] of yargsOptions) { + subYargs.option(flagName, config); + toolArgNames.push(flagName); + } + + // Add --json option for complex args or full override + subYargs.option('json', { + type: 'string', + describe: 'JSON object of tool args (merged with flags)', + }); + + // Add --output option for format control + subYargs.option('output', { + type: 'string', + choices: ['text', 'json'] as const, + default: 'text', + describe: 'Output format', + }); + + // Group options for cleaner help display + if (toolArgNames.length > 0) { + subYargs.group(toolArgNames, 'Tool Arguments:'); + } + subYargs.group(['json', 'output'], 'Output Options:'); + + // Add note about unsupported keys if any + if (unsupportedKeys.length > 0) { + subYargs.epilogue( + `Note: Complex parameters (${unsupportedKeys.join(', ')}) must be passed via --json`, + ); + } + + return subYargs; + }, + async (argv) => { + // Extract our options + const jsonArg = argv.json as string | undefined; + const outputFormat = (argv.output as OutputFormat) ?? 'text'; + const outputStyle = (argv.style as OutputStyle) ?? 'normal'; + const socketPath = argv.socket as string; + const logLevel = argv['log-level'] as string | undefined; + + // Parse JSON args if provided + let jsonArgs: Record = {}; + if (jsonArg) { + try { + jsonArgs = JSON.parse(jsonArg) as Record; + } catch { + console.error(`Error: Invalid JSON in --json argument`); + process.exitCode = 1; + return; + } + } + + // Convert CLI argv to tool params (kebab-case -> camelCase) + // Filter out internal CLI options before converting + const internalKeys = new Set(['json', 'output', 'style', 'socket', '_', '$0']); + const flagArgs: Record = {}; + for (const [key, value] of Object.entries(argv as Record)) { + if (!internalKeys.has(key)) { + flagArgs[key] = value; + } + } + const toolParams = convertArgvToToolParams(flagArgs); + + // Merge: flag args first, then JSON overrides + const args = { ...toolParams, ...jsonArgs }; + + // Invoke the tool + const response = await invoker.invokeDirect(tool, args, { + runtime: 'cli', + cliExposedWorkflowIds, + socketPath, + workspaceRoot: opts.workspaceRoot, + logLevel, + }); + + printToolResponse(response, { format: outputFormat, style: outputStyle }); + }, + ); +} diff --git a/src/cli/schema-to-yargs.ts b/src/cli/schema-to-yargs.ts new file mode 100644 index 00000000..252fa966 --- /dev/null +++ b/src/cli/schema-to-yargs.ts @@ -0,0 +1,245 @@ +import * as z from 'zod'; +import type { Options } from 'yargs'; +import { toKebabCase } from '../runtime/naming.ts'; +import type { ToolSchemaShape } from '../core/plugin-types.ts'; + +export interface YargsOptionConfig extends Options { + type: 'string' | 'number' | 'boolean' | 'array'; +} + +/** + * Check the Zod type kind using the internal _zod property. + * This is more reliable than instanceof checks which can fail + * across module boundaries or with different Zod versions. + */ +function getZodTypeName(t: z.ZodType): string | undefined { + // Zod 4 uses _zod.def.type + const zod4Def = (t as { _zod?: { def?: { type?: string } } })._zod?.def; + if (zod4Def?.type) return zod4Def.type; + + // Zod 3 fallback uses _def.typeName + const zod3Def = (t as { _def?: { typeName?: string } })._def; + return zod3Def?.typeName; +} + +/** + * Get the inner type from wrapper types (optional, nullable, default, transform, pipe). + */ +function getInnerType(t: z.ZodType): z.ZodType | undefined { + // Use unknown as intermediate to avoid type conflicts + const tAny = t as unknown as Record; + const zod4Def = (tAny._zod as Record | undefined)?.def as + | Record + | undefined; + + // ZodOptional, ZodNullable, ZodDefault use innerType + if (zod4Def?.innerType) return zod4Def.innerType as z.ZodType; + // ZodPipe uses 'in' + if (zod4Def?.in) return zod4Def.in as z.ZodType; + // ZodTransform uses 'type' as inner type (when it's an object/ZodType) + if (zod4Def?.type && typeof zod4Def.type === 'object') return zod4Def.type as z.ZodType; + + // Zod 3 fallback + const zod3Def = tAny._def as Record | undefined; + return zod3Def?.innerType as z.ZodType | undefined; +} + +/** + * Unwrap Zod wrapper types to get the underlying type. + */ +function unwrap(t: z.ZodType): z.ZodType { + const typeName = getZodTypeName(t); + + // Wrapper types that should be unwrapped + const wrapperTypes = [ + 'optional', + 'nullable', + 'default', + 'transform', + 'pipe', + 'prefault', + 'catch', + 'readonly', + ]; + + if (typeName && wrapperTypes.includes(typeName)) { + const inner = getInnerType(t); + if (inner) return unwrap(inner); + } + + return t; +} + +/** + * Check if a Zod type is optional/nullable/has default. + */ +function isOptional(t: z.ZodType): boolean { + const typeName = getZodTypeName(t); + + if ( + typeName === 'optional' || + typeName === 'nullable' || + typeName === 'default' || + typeName === 'prefault' + ) { + return true; + } + + // Check wrapper types recursively + const inner = getInnerType(t); + if (inner) return isOptional(inner); + + return false; +} + +/** + * Get description from a Zod type if available. + */ +function getDescription(t: z.ZodType): string | undefined { + // Zod 4 uses _zod.def.description + const def = (t as { _zod?: { def?: { description?: string } } })._zod?.def; + if (def?.description) return def.description; + + // Zod 3 fallback + const legacyDef = (t as { _def?: { description?: string } })._def; + return legacyDef?.description; +} + +/** + * Get enum values from a Zod enum type. + */ +function getEnumValues(t: z.ZodType): string[] | undefined { + const def = (t as { _zod?: { def?: { entries?: Record; values?: string[] } } }) + ._zod?.def; + if (def?.entries) return Object.values(def.entries); + if (def?.values) return def.values; + + // Zod 3 fallback + const legacyDef = (t as { _def?: { values?: string[] } })._def; + return legacyDef?.values; +} + +/** + * Get the element type from an array type. + */ +function getArrayElement(t: z.ZodType): z.ZodType | undefined { + const tAny = t as unknown as Record; + const zod4Def = (tAny._zod as Record | undefined)?.def as + | Record + | undefined; + if (zod4Def?.element) return zod4Def.element as z.ZodType; + + // Zod 3 fallback + const zod3Def = tAny._def as Record | undefined; + return zod3Def?.type as z.ZodType | undefined; +} + +/** + * Get the literal value from a literal type. + */ +function getLiteralValue(t: z.ZodType): unknown { + const def = (t as { _zod?: { def?: { value?: unknown } } })._zod?.def; + if (def?.value !== undefined) return def.value; + + // Zod 3 fallback + const legacyDef = (t as { _def?: { value?: unknown } })._def; + return legacyDef?.value; +} + +/** + * Convert a Zod type to yargs option configuration. + * Returns null for types that can't be represented as CLI flags. + */ +export function zodToYargsOption(t: z.ZodType): YargsOptionConfig | null { + const unwrapped = unwrap(t); + const description = getDescription(t); + const demandOption = !isOptional(t); + const typeName = getZodTypeName(unwrapped); + + if (typeName === 'string') { + return { type: 'string', describe: description, demandOption }; + } + + if (typeName === 'number' || typeName === 'int' || typeName === 'bigint') { + return { type: 'number', describe: description, demandOption }; + } + + if (typeName === 'boolean') { + return { type: 'boolean', describe: description, demandOption: false }; + } + + if (typeName === 'enum' || typeName === 'nativeEnum') { + const values = getEnumValues(unwrapped); + if (values) { + return { + type: 'string', + choices: values, + describe: description, + demandOption, + }; + } + } + + if (typeName === 'array') { + const element = getArrayElement(unwrapped); + if (element) { + const elemTypeName = getZodTypeName(unwrap(element)); + if (elemTypeName === 'string' || elemTypeName === 'number') { + return { type: 'array', describe: description, demandOption: false }; + } + } + // Complex array types - use --json fallback + return null; + } + + if (typeName === 'literal') { + const value = getLiteralValue(unwrapped); + if (typeof value === 'string') { + return { type: 'string', default: value, describe: description, demandOption: false }; + } + if (typeof value === 'number') { + return { type: 'number', default: value, describe: description, demandOption: false }; + } + if (typeof value === 'boolean') { + return { type: 'boolean', default: value, describe: description, demandOption: false }; + } + } + + // Complex types (objects, unions, etc.) - use --json fallback + return null; +} + +/** + * Convert a tool schema shape to yargs options. + * Returns a map of flag names (kebab-case) to yargs options. + */ +export function schemaToYargsOptions(schema: ToolSchemaShape): Map { + const options = new Map(); + + for (const [key, zodType] of Object.entries(schema)) { + const opt = zodToYargsOption(zodType); + if (opt) { + const flagName = toKebabCase(key); + options.set(flagName, opt); + } + } + + return options; +} + +/** + * Get list of schema keys that couldn't be converted to CLI flags. + * These need to be passed via --json. + */ +export function getUnsupportedSchemaKeys(schema: ToolSchemaShape): string[] { + const unsupported: string[] = []; + + for (const [key, zodType] of Object.entries(schema)) { + const opt = zodToYargsOption(zodType); + if (!opt) { + unsupported.push(key); + } + } + + return unsupported; +} diff --git a/src/cli/yargs-app.ts b/src/cli/yargs-app.ts new file mode 100644 index 00000000..47d60561 --- /dev/null +++ b/src/cli/yargs-app.ts @@ -0,0 +1,87 @@ +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import type { ToolCatalog } from '../runtime/types.ts'; +import type { ResolvedRuntimeConfig } from '../utils/config-store.ts'; +import { registerDaemonCommands } from './commands/daemon.ts'; +import { registerMcpCommand } from './commands/mcp.ts'; +import { registerToolsCommand } from './commands/tools.ts'; +import { registerToolCommands } from './register-tool-commands.ts'; +import { version } from '../version.ts'; +import { setLogLevel, type LogLevel } from '../utils/logger.ts'; + +export interface YargsAppOptions { + catalog: ToolCatalog; + runtimeConfig: ResolvedRuntimeConfig; + defaultSocketPath: string; + workspaceRoot: string; + workspaceKey: string; + workflowNames: string[]; + cliExposedWorkflowIds: string[]; +} + +/** + * Build the main yargs application with all commands registered. + */ +export function buildYargsApp(opts: YargsAppOptions): ReturnType { + const app = yargs(hideBin(process.argv)) + .scriptName('') + .usage('Usage: xcodebuildmcp [options]') + .strict() + .recommendCommands() + .wrap(Math.min(120, yargs().terminalWidth())) + .parserConfiguration({ + // Accept --derived-data-path -> derivedDataPath + 'camel-case-expansion': true, + }) + .option('socket', { + type: 'string', + describe: 'Override daemon unix socket path', + default: opts.defaultSocketPath, + hidden: true, + }) + .option('log-level', { + type: 'string', + describe: 'Set log verbosity level', + choices: ['none', 'error', 'warning', 'info', 'debug'] as const, + default: 'none', + }) + .option('style', { + type: 'string', + describe: 'Output verbosity (minimal hides next steps)', + choices: ['normal', 'minimal'] as const, + default: 'normal', + }) + .middleware((argv) => { + const level = argv['log-level'] as LogLevel | undefined; + if (level) { + setLogLevel(level); + } + }) + .version(String(version)) + .help() + .alias('h', 'help') + .alias('v', 'version') + .demandCommand(1, '') + .epilogue( + `Run 'xcodebuildmcp mcp' to start the MCP server.\n` + + `Run 'xcodebuildmcp tools' to see all available tools.\n` + + `Run 'xcodebuildmcp --help' for tool-specific help.`, + ); + + // Register command groups with workspace context + registerMcpCommand(app); + registerToolsCommand(app); + registerToolCommands(app, opts.catalog, { + workspaceRoot: opts.workspaceRoot, + cliExposedWorkflowIds: opts.cliExposedWorkflowIds, + workflowNames: opts.workflowNames, + }); + // Daemon management is an advanced debugging tool - register last + registerDaemonCommands(app, { + defaultSocketPath: opts.defaultSocketPath, + workspaceRoot: opts.workspaceRoot, + workspaceKey: opts.workspaceKey, + }); + + return app; +} diff --git a/src/core/__tests__/resource-root.test.ts b/src/core/__tests__/resource-root.test.ts new file mode 100644 index 00000000..167143d9 --- /dev/null +++ b/src/core/__tests__/resource-root.test.ts @@ -0,0 +1,54 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { mkdtempSync, mkdirSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { + getBundledAxePath, + getBundledFrameworksDir, + getManifestsDir, + getResourceRoot, + resetResourceRootCacheForTests, +} from '../resource-root.ts'; + +describe('resource-root', () => { + let originalExecPath: string; + let originalResourceRoot: string | undefined; + let tempDir: string; + + beforeEach(() => { + originalExecPath = process.execPath; + originalResourceRoot = process.env.XCODEBUILDMCP_RESOURCE_ROOT; + tempDir = mkdtempSync(join(tmpdir(), 'xbmcp-resource-root-')); + resetResourceRootCacheForTests(); + }); + + afterEach(() => { + process.execPath = originalExecPath; + if (originalResourceRoot === undefined) { + delete process.env.XCODEBUILDMCP_RESOURCE_ROOT; + } else { + process.env.XCODEBUILDMCP_RESOURCE_ROOT = originalResourceRoot; + } + rmSync(tempDir, { recursive: true, force: true }); + resetResourceRootCacheForTests(); + }); + + it('uses XCODEBUILDMCP_RESOURCE_ROOT when set', () => { + const explicitRoot = join(tempDir, 'explicit-root'); + process.env.XCODEBUILDMCP_RESOURCE_ROOT = explicitRoot; + + expect(getResourceRoot()).toBe(resolve(explicitRoot)); + expect(getManifestsDir()).toBe(join(resolve(explicitRoot), 'manifests')); + expect(getBundledAxePath()).toBe(join(resolve(explicitRoot), 'bundled', 'axe')); + }); + + it('falls back to executable-relative root when resources exist next to executable', () => { + delete process.env.XCODEBUILDMCP_RESOURCE_ROOT; + const executableRoot = join(tempDir, 'portable-install', 'libexec'); + mkdirSync(join(executableRoot, 'manifests', 'tools'), { recursive: true }); + process.execPath = join(executableRoot, 'xcodebuildmcp'); + + expect(getResourceRoot()).toBe(executableRoot); + expect(getBundledFrameworksDir()).toBe(join(executableRoot, 'bundled', 'Frameworks')); + }); +}); diff --git a/src/core/generated-plugins.ts b/src/core/generated-plugins.ts deleted file mode 100644 index 0ca52998..00000000 --- a/src/core/generated-plugins.ts +++ /dev/null @@ -1,396 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// This file is generated by the plugin discovery esbuild plugin - -// Generated based on filesystem scan -export const WORKFLOW_LOADERS = { - device: async () => { - const { workflow } = await import('../mcp/tools/device/index.js'); - const tool_0 = await import('../mcp/tools/device/build_device.js').then((m) => m.default); - const tool_1 = await import('../mcp/tools/device/clean.js').then((m) => m.default); - const tool_2 = await import('../mcp/tools/device/discover_projs.js').then((m) => m.default); - const tool_3 = await import('../mcp/tools/device/get_app_bundle_id.js').then((m) => m.default); - const tool_4 = await import('../mcp/tools/device/get_device_app_path.js').then( - (m) => m.default, - ); - const tool_5 = await import('../mcp/tools/device/install_app_device.js').then((m) => m.default); - const tool_6 = await import('../mcp/tools/device/launch_app_device.js').then((m) => m.default); - const tool_7 = await import('../mcp/tools/device/list_devices.js').then((m) => m.default); - const tool_8 = await import('../mcp/tools/device/list_schemes.js').then((m) => m.default); - const tool_9 = await import('../mcp/tools/device/show_build_settings.js').then( - (m) => m.default, - ); - const tool_10 = await import('../mcp/tools/device/start_device_log_cap.js').then( - (m) => m.default, - ); - const tool_11 = await import('../mcp/tools/device/stop_app_device.js').then((m) => m.default); - const tool_12 = await import('../mcp/tools/device/stop_device_log_cap.js').then( - (m) => m.default, - ); - const tool_13 = await import('../mcp/tools/device/test_device.js').then((m) => m.default); - - return { - workflow, - build_device: tool_0, - clean: tool_1, - discover_projs: tool_2, - get_app_bundle_id: tool_3, - get_device_app_path: tool_4, - install_app_device: tool_5, - launch_app_device: tool_6, - list_devices: tool_7, - list_schemes: tool_8, - show_build_settings: tool_9, - start_device_log_cap: tool_10, - stop_app_device: tool_11, - stop_device_log_cap: tool_12, - test_device: tool_13, - }; - }, - doctor: async () => { - const { workflow } = await import('../mcp/tools/doctor/index.js'); - const tool_0 = await import('../mcp/tools/doctor/doctor.js').then((m) => m.default); - - return { - workflow, - doctor: tool_0, - }; - }, - logging: async () => { - const { workflow } = await import('../mcp/tools/logging/index.js'); - const tool_0 = await import('../mcp/tools/logging/start_device_log_cap.js').then( - (m) => m.default, - ); - const tool_1 = await import('../mcp/tools/logging/start_sim_log_cap.js').then((m) => m.default); - const tool_2 = await import('../mcp/tools/logging/stop_device_log_cap.js').then( - (m) => m.default, - ); - const tool_3 = await import('../mcp/tools/logging/stop_sim_log_cap.js').then((m) => m.default); - - return { - workflow, - start_device_log_cap: tool_0, - start_sim_log_cap: tool_1, - stop_device_log_cap: tool_2, - stop_sim_log_cap: tool_3, - }; - }, - macos: async () => { - const { workflow } = await import('../mcp/tools/macos/index.js'); - const tool_0 = await import('../mcp/tools/macos/build_macos.js').then((m) => m.default); - const tool_1 = await import('../mcp/tools/macos/build_run_macos.js').then((m) => m.default); - const tool_2 = await import('../mcp/tools/macos/clean.js').then((m) => m.default); - const tool_3 = await import('../mcp/tools/macos/discover_projs.js').then((m) => m.default); - const tool_4 = await import('../mcp/tools/macos/get_mac_app_path.js').then((m) => m.default); - const tool_5 = await import('../mcp/tools/macos/get_mac_bundle_id.js').then((m) => m.default); - const tool_6 = await import('../mcp/tools/macos/launch_mac_app.js').then((m) => m.default); - const tool_7 = await import('../mcp/tools/macos/list_schemes.js').then((m) => m.default); - const tool_8 = await import('../mcp/tools/macos/show_build_settings.js').then((m) => m.default); - const tool_9 = await import('../mcp/tools/macos/stop_mac_app.js').then((m) => m.default); - const tool_10 = await import('../mcp/tools/macos/test_macos.js').then((m) => m.default); - - return { - workflow, - build_macos: tool_0, - build_run_macos: tool_1, - clean: tool_2, - discover_projs: tool_3, - get_mac_app_path: tool_4, - get_mac_bundle_id: tool_5, - launch_mac_app: tool_6, - list_schemes: tool_7, - show_build_settings: tool_8, - stop_mac_app: tool_9, - test_macos: tool_10, - }; - }, - 'project-discovery': async () => { - const { workflow } = await import('../mcp/tools/project-discovery/index.js'); - const tool_0 = await import('../mcp/tools/project-discovery/discover_projs.js').then( - (m) => m.default, - ); - const tool_1 = await import('../mcp/tools/project-discovery/get_app_bundle_id.js').then( - (m) => m.default, - ); - const tool_2 = await import('../mcp/tools/project-discovery/get_mac_bundle_id.js').then( - (m) => m.default, - ); - const tool_3 = await import('../mcp/tools/project-discovery/list_schemes.js').then( - (m) => m.default, - ); - const tool_4 = await import('../mcp/tools/project-discovery/show_build_settings.js').then( - (m) => m.default, - ); - - return { - workflow, - discover_projs: tool_0, - get_app_bundle_id: tool_1, - get_mac_bundle_id: tool_2, - list_schemes: tool_3, - show_build_settings: tool_4, - }; - }, - 'project-scaffolding': async () => { - const { workflow } = await import('../mcp/tools/project-scaffolding/index.js'); - const tool_0 = await import('../mcp/tools/project-scaffolding/scaffold_ios_project.js').then( - (m) => m.default, - ); - const tool_1 = await import('../mcp/tools/project-scaffolding/scaffold_macos_project.js').then( - (m) => m.default, - ); - - return { - workflow, - scaffold_ios_project: tool_0, - scaffold_macos_project: tool_1, - }; - }, - 'session-management': async () => { - const { workflow } = await import('../mcp/tools/session-management/index.js'); - const tool_0 = await import('../mcp/tools/session-management/session_clear_defaults.js').then( - (m) => m.default, - ); - const tool_1 = await import('../mcp/tools/session-management/session_set_defaults.js').then( - (m) => m.default, - ); - const tool_2 = await import('../mcp/tools/session-management/session_show_defaults.js').then( - (m) => m.default, - ); - - return { - workflow, - session_clear_defaults: tool_0, - session_set_defaults: tool_1, - session_show_defaults: tool_2, - }; - }, - simulator: async () => { - const { workflow } = await import('../mcp/tools/simulator/index.js'); - const tool_0 = await import('../mcp/tools/simulator/boot_sim.js').then((m) => m.default); - const tool_1 = await import('../mcp/tools/simulator/build_run_sim.js').then((m) => m.default); - const tool_2 = await import('../mcp/tools/simulator/build_sim.js').then((m) => m.default); - const tool_3 = await import('../mcp/tools/simulator/clean.js').then((m) => m.default); - const tool_4 = await import('../mcp/tools/simulator/describe_ui.js').then((m) => m.default); - const tool_5 = await import('../mcp/tools/simulator/discover_projs.js').then((m) => m.default); - const tool_6 = await import('../mcp/tools/simulator/get_app_bundle_id.js').then( - (m) => m.default, - ); - const tool_7 = await import('../mcp/tools/simulator/get_sim_app_path.js').then( - (m) => m.default, - ); - const tool_8 = await import('../mcp/tools/simulator/install_app_sim.js').then((m) => m.default); - const tool_9 = await import('../mcp/tools/simulator/launch_app_logs_sim.js').then( - (m) => m.default, - ); - const tool_10 = await import('../mcp/tools/simulator/launch_app_sim.js').then((m) => m.default); - const tool_11 = await import('../mcp/tools/simulator/list_schemes.js').then((m) => m.default); - const tool_12 = await import('../mcp/tools/simulator/list_sims.js').then((m) => m.default); - const tool_13 = await import('../mcp/tools/simulator/open_sim.js').then((m) => m.default); - const tool_14 = await import('../mcp/tools/simulator/record_sim_video.js').then( - (m) => m.default, - ); - const tool_15 = await import('../mcp/tools/simulator/screenshot.js').then((m) => m.default); - const tool_16 = await import('../mcp/tools/simulator/show_build_settings.js').then( - (m) => m.default, - ); - const tool_17 = await import('../mcp/tools/simulator/stop_app_sim.js').then((m) => m.default); - const tool_18 = await import('../mcp/tools/simulator/test_sim.js').then((m) => m.default); - - return { - workflow, - boot_sim: tool_0, - build_run_sim: tool_1, - build_sim: tool_2, - clean: tool_3, - describe_ui: tool_4, - discover_projs: tool_5, - get_app_bundle_id: tool_6, - get_sim_app_path: tool_7, - install_app_sim: tool_8, - launch_app_logs_sim: tool_9, - launch_app_sim: tool_10, - list_schemes: tool_11, - list_sims: tool_12, - open_sim: tool_13, - record_sim_video: tool_14, - screenshot: tool_15, - show_build_settings: tool_16, - stop_app_sim: tool_17, - test_sim: tool_18, - }; - }, - 'simulator-management': async () => { - const { workflow } = await import('../mcp/tools/simulator-management/index.js'); - const tool_0 = await import('../mcp/tools/simulator-management/boot_sim.js').then( - (m) => m.default, - ); - const tool_1 = await import('../mcp/tools/simulator-management/erase_sims.js').then( - (m) => m.default, - ); - const tool_2 = await import('../mcp/tools/simulator-management/list_sims.js').then( - (m) => m.default, - ); - const tool_3 = await import('../mcp/tools/simulator-management/open_sim.js').then( - (m) => m.default, - ); - const tool_4 = await import('../mcp/tools/simulator-management/reset_sim_location.js').then( - (m) => m.default, - ); - const tool_5 = await import('../mcp/tools/simulator-management/set_sim_appearance.js').then( - (m) => m.default, - ); - const tool_6 = await import('../mcp/tools/simulator-management/set_sim_location.js').then( - (m) => m.default, - ); - const tool_7 = await import('../mcp/tools/simulator-management/sim_statusbar.js').then( - (m) => m.default, - ); - - return { - workflow, - boot_sim: tool_0, - erase_sims: tool_1, - list_sims: tool_2, - open_sim: tool_3, - reset_sim_location: tool_4, - set_sim_appearance: tool_5, - set_sim_location: tool_6, - sim_statusbar: tool_7, - }; - }, - 'swift-package': async () => { - const { workflow } = await import('../mcp/tools/swift-package/index.js'); - const tool_0 = await import('../mcp/tools/swift-package/swift_package_build.js').then( - (m) => m.default, - ); - const tool_1 = await import('../mcp/tools/swift-package/swift_package_clean.js').then( - (m) => m.default, - ); - const tool_2 = await import('../mcp/tools/swift-package/swift_package_list.js').then( - (m) => m.default, - ); - const tool_3 = await import('../mcp/tools/swift-package/swift_package_run.js').then( - (m) => m.default, - ); - const tool_4 = await import('../mcp/tools/swift-package/swift_package_stop.js').then( - (m) => m.default, - ); - const tool_5 = await import('../mcp/tools/swift-package/swift_package_test.js').then( - (m) => m.default, - ); - - return { - workflow, - swift_package_build: tool_0, - swift_package_clean: tool_1, - swift_package_list: tool_2, - swift_package_run: tool_3, - swift_package_stop: tool_4, - swift_package_test: tool_5, - }; - }, - 'ui-testing': async () => { - const { workflow } = await import('../mcp/tools/ui-testing/index.js'); - const tool_0 = await import('../mcp/tools/ui-testing/button.js').then((m) => m.default); - const tool_1 = await import('../mcp/tools/ui-testing/describe_ui.js').then((m) => m.default); - const tool_2 = await import('../mcp/tools/ui-testing/gesture.js').then((m) => m.default); - const tool_3 = await import('../mcp/tools/ui-testing/key_press.js').then((m) => m.default); - const tool_4 = await import('../mcp/tools/ui-testing/key_sequence.js').then((m) => m.default); - const tool_5 = await import('../mcp/tools/ui-testing/long_press.js').then((m) => m.default); - const tool_6 = await import('../mcp/tools/ui-testing/screenshot.js').then((m) => m.default); - const tool_7 = await import('../mcp/tools/ui-testing/swipe.js').then((m) => m.default); - const tool_8 = await import('../mcp/tools/ui-testing/tap.js').then((m) => m.default); - const tool_9 = await import('../mcp/tools/ui-testing/touch.js').then((m) => m.default); - const tool_10 = await import('../mcp/tools/ui-testing/type_text.js').then((m) => m.default); - - return { - workflow, - button: tool_0, - describe_ui: tool_1, - gesture: tool_2, - key_press: tool_3, - key_sequence: tool_4, - long_press: tool_5, - screenshot: tool_6, - swipe: tool_7, - tap: tool_8, - touch: tool_9, - type_text: tool_10, - }; - }, - utilities: async () => { - const { workflow } = await import('../mcp/tools/utilities/index.js'); - const tool_0 = await import('../mcp/tools/utilities/clean.js').then((m) => m.default); - - return { - workflow, - clean: tool_0, - }; - }, -}; - -export type WorkflowName = keyof typeof WORKFLOW_LOADERS; - -// Optional: Export workflow metadata for quick access -export const WORKFLOW_METADATA = { - device: { - name: 'iOS Device Development', - description: - 'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Build, test, deploy, and debug apps on real hardware.', - }, - doctor: { - name: 'System Doctor', - description: - 'Debug tools and system doctor for troubleshooting XcodeBuildMCP server, development environment, and tool availability.', - }, - logging: { - name: 'Log Capture & Management', - description: - 'Log capture and management tools for iOS simulators and physical devices. Start, stop, and analyze application and system logs during development and testing.', - }, - macos: { - name: 'macOS Development', - description: - 'Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications.', - }, - 'project-discovery': { - name: 'Project Discovery', - description: - 'Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information.', - }, - 'project-scaffolding': { - name: 'Project Scaffolding', - description: - 'Tools for creating new iOS and macOS projects from templates. Bootstrap new applications with best practices, standard configurations, and modern project structures.', - }, - 'session-management': { - name: 'session-management', - description: - 'Manage session defaults for projectPath/workspacePath, scheme, configuration, simulatorName/simulatorId, deviceId, useLatestOS and arch. These defaults are required by many tools and must be set before attempting to call tools that would depend on these values.', - }, - simulator: { - name: 'iOS Simulator Development', - description: - 'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators.', - }, - 'simulator-management': { - name: 'Simulator Management', - description: - 'Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance.', - }, - 'swift-package': { - name: 'Swift Package Manager', - description: - 'Swift Package Manager operations for building, testing, running, and managing Swift packages and dependencies. Complete SPM workflow support.', - }, - 'ui-testing': { - name: 'UI Testing & Automation', - description: - 'UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows.', - }, - utilities: { - name: 'Project Utilities', - description: - 'Essential project maintenance utilities for cleaning and managing existing projects. Provides clean operations for both .xcodeproj and .xcworkspace files.', - }, -}; diff --git a/src/core/generated-resources.ts b/src/core/generated-resources.ts deleted file mode 100644 index 8a1f914c..00000000 --- a/src/core/generated-resources.ts +++ /dev/null @@ -1,19 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// This file is generated by the plugin discovery esbuild plugin - -export const RESOURCE_LOADERS = { - devices: async () => { - const module = await import('../mcp/resources/devices.js'); - return module.default; - }, - doctor: async () => { - const module = await import('../mcp/resources/doctor.js'); - return module.default; - }, - simulators: async () => { - const module = await import('../mcp/resources/simulators.js'); - return module.default; - }, -}; - -export type ResourceName = keyof typeof RESOURCE_LOADERS; diff --git a/src/core/manifest/__tests__/load-manifest.test.ts b/src/core/manifest/__tests__/load-manifest.test.ts new file mode 100644 index 00000000..7035c96a --- /dev/null +++ b/src/core/manifest/__tests__/load-manifest.test.ts @@ -0,0 +1,163 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { + loadManifest, + getWorkflowTools, + getToolsForWorkflows, + ManifestValidationError, +} from '../load-manifest.ts'; + +describe('load-manifest', () => { + describe('loadManifest (integration with real manifests)', () => { + it('should load all manifests from the manifests directory', () => { + const manifest = loadManifest(); + + // Check that we have tools and workflows + expect(manifest.tools.size).toBeGreaterThan(0); + expect(manifest.workflows.size).toBeGreaterThan(0); + }); + + it('should have required workflows', () => { + const manifest = loadManifest(); + + expect(manifest.workflows.has('simulator')).toBe(true); + expect(manifest.workflows.has('device')).toBe(true); + expect(manifest.workflows.has('session-management')).toBe(true); + }); + + it('should have required tools', () => { + const manifest = loadManifest(); + + expect(manifest.tools.has('build_sim')).toBe(true); + expect(manifest.tools.has('discover_projs')).toBe(true); + expect(manifest.tools.has('session_show_defaults')).toBe(true); + expect(manifest.tools.has('session_use_defaults_profile')).toBe(true); + }); + + it('should validate tool references in workflows', () => { + const manifest = loadManifest(); + + // Every tool referenced in a workflow should exist + for (const [workflowId, workflow] of manifest.workflows) { + for (const toolId of workflow.tools) { + expect( + manifest.tools.has(toolId), + `Workflow '${workflowId}' references unknown tool '${toolId}'`, + ).toBe(true); + } + } + }); + + it('should have unique MCP names across all tools', () => { + const manifest = loadManifest(); + const mcpNames = new Set(); + + for (const [, tool] of manifest.tools) { + expect(mcpNames.has(tool.names.mcp), `Duplicate MCP name '${tool.names.mcp}'`).toBe(false); + mcpNames.add(tool.names.mcp); + } + }); + + it('should have session-management as auto-include workflow', () => { + const manifest = loadManifest(); + const sessionMgmt = manifest.workflows.get('session-management'); + + expect(sessionMgmt).toBeDefined(); + expect(sessionMgmt?.selection?.mcp?.autoInclude).toBe(true); + }); + + it('should have simulator as default-enabled workflow', () => { + const manifest = loadManifest(); + const simulator = manifest.workflows.get('simulator'); + + expect(simulator).toBeDefined(); + expect(simulator?.selection?.mcp?.defaultEnabled).toBe(true); + }); + + it('should have doctor workflow with debugEnabled predicate', () => { + const manifest = loadManifest(); + const doctor = manifest.workflows.get('doctor'); + + expect(doctor).toBeDefined(); + expect(doctor?.predicates).toContain('debugEnabled'); + expect(doctor?.selection?.mcp?.autoInclude).toBe(true); + }); + + it('should have xcode-ide workflow hidden in Xcode agent mode only', () => { + const manifest = loadManifest(); + const xcodeIde = manifest.workflows.get('xcode-ide'); + + expect(xcodeIde).toBeDefined(); + expect(xcodeIde?.predicates).toContain('hideWhenXcodeAgentMode'); + expect(xcodeIde?.predicates).not.toContain('debugEnabled'); + }); + + it('should keep xcode bridge debug tools gated by debugEnabled', () => { + const manifest = loadManifest(); + + expect(manifest.tools.get('xcode_ide_list_tools')?.predicates).toContain('mcpRuntimeOnly'); + expect(manifest.tools.get('xcode_ide_call_tool')?.predicates).toContain('mcpRuntimeOnly'); + expect(manifest.tools.get('xcode_tools_bridge_status')?.predicates).toContain('debugEnabled'); + expect(manifest.tools.get('xcode_tools_bridge_sync')?.predicates).toContain('debugEnabled'); + expect(manifest.tools.get('xcode_tools_bridge_disconnect')?.predicates).toContain( + 'debugEnabled', + ); + }); + }); + + describe('getWorkflowTools', () => { + it('should return tools for a workflow', () => { + const manifest = loadManifest(); + const tools = getWorkflowTools(manifest, 'simulator'); + + expect(tools.length).toBeGreaterThan(0); + expect(tools.some((t) => t.id === 'build_sim')).toBe(true); + }); + + it('should return empty array for unknown workflow', () => { + const manifest = loadManifest(); + const tools = getWorkflowTools(manifest, 'nonexistent-workflow'); + + expect(tools).toEqual([]); + }); + }); + + describe('getToolsForWorkflows', () => { + it('should return unique tools across multiple workflows', () => { + const manifest = loadManifest(); + const tools = getToolsForWorkflows(manifest, ['simulator', 'device']); + + // Should have tools from both workflows + expect(tools.some((t) => t.id === 'build_sim')).toBe(true); + expect(tools.some((t) => t.id === 'build_device')).toBe(true); + + // Tools should be unique (discover_projs is in both) + const toolIds = tools.map((t) => t.id); + const uniqueIds = new Set(toolIds); + expect(toolIds.length).toBe(uniqueIds.size); + }); + + it('should return empty array for empty workflow list', () => { + const manifest = loadManifest(); + const tools = getToolsForWorkflows(manifest, []); + + expect(tools).toEqual([]); + }); + }); +}); + +describe('ManifestValidationError', () => { + it('should include source file in message', () => { + const error = new ManifestValidationError('Test error', 'test.yaml'); + expect(error.message).toBe('Test error (in test.yaml)'); + expect(error.sourceFile).toBe('test.yaml'); + }); + + it('should work without source file', () => { + const error = new ManifestValidationError('Test error'); + expect(error.message).toBe('Test error'); + expect(error.sourceFile).toBeUndefined(); + }); +}); diff --git a/src/core/manifest/__tests__/schema.test.ts b/src/core/manifest/__tests__/schema.test.ts new file mode 100644 index 00000000..a50b9255 --- /dev/null +++ b/src/core/manifest/__tests__/schema.test.ts @@ -0,0 +1,224 @@ +import { describe, it, expect } from 'vitest'; +import { + toolManifestEntrySchema, + workflowManifestEntrySchema, + deriveCliName, + getEffectiveCliName, + type ToolManifestEntry, +} from '../schema.ts'; + +describe('schema', () => { + describe('toolManifestEntrySchema', () => { + it('should parse valid tool manifest', () => { + const input = { + id: 'build_sim', + module: 'mcp/tools/simulator/build_sim', + names: { mcp: 'build_sim' }, + description: 'Build iOS app for simulator', + availability: { mcp: true, cli: true }, + predicates: [], + routing: { stateful: false }, + }; + + const result = toolManifestEntrySchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.id).toBe('build_sim'); + expect(result.data.names.mcp).toBe('build_sim'); + } + }); + + it('should apply default availability', () => { + const input = { + id: 'test_tool', + module: 'mcp/tools/test/test_tool', + names: { mcp: 'test_tool' }, + }; + + const result = toolManifestEntrySchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.availability).toEqual({ mcp: true, cli: true }); + expect(result.data.predicates).toEqual([]); + expect(result.data.nextSteps).toEqual([]); + } + }); + + it('should reject missing required fields', () => { + const input = { + id: 'test_tool', + // missing module and names + }; + + const result = toolManifestEntrySchema.safeParse(input); + expect(result.success).toBe(false); + }); + + it('should accept optional CLI name', () => { + const input = { + id: 'build_sim', + module: 'mcp/tools/simulator/build_sim', + names: { mcp: 'build_sim', cli: 'build-simulator' }, + }; + + const result = toolManifestEntrySchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.names.cli).toBe('build-simulator'); + } + }); + + it('should reject availability.daemon', () => { + const input = { + id: 'tool1', + module: 'mcp/tools/test/tool1', + names: { mcp: 'tool1' }, + availability: { mcp: true, cli: true, daemon: true }, + }; + + expect(toolManifestEntrySchema.safeParse(input).success).toBe(false); + }); + + it('should reject routing.daemonAffinity', () => { + const input = { + id: 'tool2', + module: 'mcp/tools/test/tool2', + names: { mcp: 'tool2' }, + routing: { stateful: true, daemonAffinity: 'required' }, + }; + + expect(toolManifestEntrySchema.safeParse(input).success).toBe(false); + }); + }); + + describe('workflowManifestEntrySchema', () => { + it('should parse valid workflow manifest', () => { + const input = { + id: 'simulator', + title: 'iOS Simulator Development', + description: 'Build and test iOS apps on simulators', + availability: { mcp: true, cli: true }, + selection: { + mcp: { + defaultEnabled: true, + autoInclude: false, + }, + }, + predicates: [], + tools: ['build_sim', 'test_sim', 'boot_sim'], + }; + + const result = workflowManifestEntrySchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.id).toBe('simulator'); + expect(result.data.tools).toHaveLength(3); + expect(result.data.selection?.mcp?.defaultEnabled).toBe(true); + } + }); + + it('should apply default values', () => { + const input = { + id: 'test-workflow', + title: 'Test Workflow', + description: 'A test workflow', + tools: ['tool1'], + }; + + const result = workflowManifestEntrySchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.availability).toEqual({ mcp: true, cli: true }); + expect(result.data.predicates).toEqual([]); + } + }); + + it('should reject empty tools array', () => { + const input = { + id: 'empty-workflow', + title: 'Empty Workflow', + description: 'A workflow with no tools', + tools: [], + }; + + // Empty tools array is technically valid per schema + const result = workflowManifestEntrySchema.safeParse(input); + expect(result.success).toBe(true); + }); + + it('should parse autoInclude workflow', () => { + const input = { + id: 'session-management', + title: 'Session Management', + description: 'Manage session defaults', + availability: { mcp: true, cli: false }, + selection: { + mcp: { + defaultEnabled: true, + autoInclude: true, + }, + }, + tools: ['session_show_defaults'], + }; + + const result = workflowManifestEntrySchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.selection?.mcp?.autoInclude).toBe(true); + expect(result.data.availability.cli).toBe(false); + } + }); + }); + + describe('deriveCliName', () => { + it('should convert underscores to hyphens', () => { + expect(deriveCliName('build_sim')).toBe('build-sim'); + expect(deriveCliName('get_app_bundle_id')).toBe('get-app-bundle-id'); + }); + + it('should convert camelCase to kebab-case', () => { + expect(deriveCliName('buildSim')).toBe('build-sim'); + expect(deriveCliName('getAppBundleId')).toBe('get-app-bundle-id'); + }); + + it('should handle mixed underscores and camelCase', () => { + expect(deriveCliName('build_simApp')).toBe('build-sim-app'); + }); + + it('should handle already kebab-case', () => { + expect(deriveCliName('build-sim')).toBe('build-sim'); + }); + + it('should lowercase the result', () => { + expect(deriveCliName('BUILD_SIM')).toBe('build-sim'); + }); + }); + + describe('getEffectiveCliName', () => { + it('should use explicit CLI name when provided', () => { + const tool: ToolManifestEntry = { + id: 'build_sim', + module: 'mcp/tools/simulator/build_sim', + names: { mcp: 'build_sim', cli: 'build-simulator' }, + availability: { mcp: true, cli: true }, + predicates: [], + nextSteps: [], + }; + + expect(getEffectiveCliName(tool)).toBe('build-simulator'); + }); + + it('should derive CLI name when not provided', () => { + const tool: ToolManifestEntry = { + id: 'build_sim', + module: 'mcp/tools/simulator/build_sim', + names: { mcp: 'build_sim' }, + availability: { mcp: true, cli: true }, + predicates: [], + nextSteps: [], + }; + + expect(getEffectiveCliName(tool)).toBe('build-sim'); + }); + }); +}); diff --git a/src/core/manifest/import-tool-module.ts b/src/core/manifest/import-tool-module.ts new file mode 100644 index 00000000..a2371e90 --- /dev/null +++ b/src/core/manifest/import-tool-module.ts @@ -0,0 +1,106 @@ +/** + * Tool module importer with backward-compatible adapter. + * Dynamically imports tool modules and adapts both old (PluginMeta default export) + * and new (named exports) formats. + */ + +import * as path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import type { ToolSchemaShape } from '../plugin-types.ts'; +import { getPackageRoot } from './load-manifest.ts'; + +/** + * Imported tool module interface. + * This is what we extract from each tool module for runtime use. + */ +export interface ImportedToolModule { + schema: ToolSchemaShape; + handler: (params: Record) => Promise; +} + +/** + * Cache for imported modules. + */ +const moduleCache = new Map(); + +/** + * Import a tool module by its manifest module path. + * + * Supports two module formats: + * 1. Legacy: `export default { name, schema, handler, ... }` + * 2. New: Named exports `{ schema, handler }` + * + * @param moduleId - Extensionless module path (e.g., 'mcp/tools/simulator/build_sim') + * @returns Imported tool module with schema and handler + */ +export async function importToolModule(moduleId: string): Promise { + // Check cache first + const cached = moduleCache.get(moduleId); + if (cached) { + return cached; + } + + const packageRoot = getPackageRoot(); + const modulePath = path.join(packageRoot, 'build', `${moduleId}.js`); + const moduleUrl = pathToFileURL(modulePath).href; + + let mod: Record; + try { + mod = (await import(moduleUrl)) as Record; + } catch (err) { + throw new Error(`Failed to import tool module '${moduleId}': ${err}`); + } + + const result = extractToolExports(mod, moduleId); + + // Cache the result + moduleCache.set(moduleId, result); + + return result; +} + +/** + * Extract tool exports from a module, supporting both legacy and new formats. + */ +function extractToolExports(mod: Record, moduleId: string): ImportedToolModule { + // Try legacy format first: default export with PluginMeta shape + if (mod.default && typeof mod.default === 'object') { + const defaultExport = mod.default as Record; + + // Check if it looks like a PluginMeta (has schema and handler) + if (defaultExport.schema && typeof defaultExport.handler === 'function') { + return { + schema: defaultExport.schema as ToolSchemaShape, + handler: defaultExport.handler as (params: Record) => Promise, + }; + } + } + + // Try new format: named exports + if (mod.schema && typeof mod.handler === 'function') { + return { + schema: mod.schema as ToolSchemaShape, + handler: mod.handler as (params: Record) => Promise, + }; + } + + throw new Error( + `Tool module '${moduleId}' does not export the required shape. ` + + `Expected either a default export with { schema, handler } or named exports { schema, handler }.`, + ); +} + +/** + * Clear the module cache. + * Useful for testing or hot-reloading scenarios. + */ +export function clearModuleCache(): void { + moduleCache.clear(); +} + +/** + * Preload multiple tool modules in parallel. + */ +export async function preloadToolModules(moduleIds: string[]): Promise { + await Promise.all(moduleIds.map((id) => importToolModule(id))); +} diff --git a/src/core/manifest/index.ts b/src/core/manifest/index.ts new file mode 100644 index 00000000..002f0437 --- /dev/null +++ b/src/core/manifest/index.ts @@ -0,0 +1,7 @@ +/** + * Manifest system exports. + */ + +export * from './schema.ts'; +export * from './load-manifest.ts'; +export * from './import-tool-module.ts'; diff --git a/src/core/manifest/load-manifest.ts b/src/core/manifest/load-manifest.ts new file mode 100644 index 00000000..f9b2f57e --- /dev/null +++ b/src/core/manifest/load-manifest.ts @@ -0,0 +1,257 @@ +/** + * Manifest loader for YAML-based tool and workflow definitions. + * Loads and merges multiple YAML files into a resolved manifest. + */ + +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { parse as parseYaml } from 'yaml'; +import { + toolManifestEntrySchema, + workflowManifestEntrySchema, + type ToolManifestEntry, + type WorkflowManifestEntry, + type ResolvedManifest, +} from './schema.ts'; +import { getManifestsDir, getPackageRoot } from '../resource-root.ts'; + +// Re-export types for consumers +export type { ResolvedManifest, ToolManifestEntry, WorkflowManifestEntry }; +import { isValidPredicate } from '../../visibility/predicate-registry.ts'; +export { getManifestsDir, getPackageRoot } from '../resource-root.ts'; + +/** + * Load all YAML files from a directory. + */ +function loadYamlFiles(dir: string): unknown[] { + if (!fs.existsSync(dir)) { + return []; + } + + const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml') || f.endsWith('.yml')); + const results: unknown[] = []; + + for (const file of files) { + const filePath = path.join(dir, file); + const content = fs.readFileSync(filePath, 'utf-8'); + try { + const parsed = parseYaml(content) as Record | null; + if (parsed) { + results.push({ ...parsed, _sourceFile: file }); + } + } catch (err) { + throw new Error(`Failed to parse YAML file ${filePath}: ${err}`); + } + } + + return results; +} + +/** + * Validation error for manifest loading. + */ +export class ManifestValidationError extends Error { + constructor( + message: string, + public readonly sourceFile?: string, + ) { + super(sourceFile ? `${message} (in ${sourceFile})` : message); + this.name = 'ManifestValidationError'; + } +} + +/** + * Load and validate the complete manifest registry. + * Merges all YAML files from manifests/tools/ and manifests/workflows/. + */ +export function loadManifest(): ResolvedManifest { + const manifestsDir = getManifestsDir(); + const toolsDir = path.join(manifestsDir, 'tools'); + const workflowsDir = path.join(manifestsDir, 'workflows'); + + const tools = new Map(); + const workflows = new Map(); + + // Load tools + const toolFiles = loadYamlFiles(toolsDir); + for (const raw of toolFiles) { + const sourceFile = (raw as { _sourceFile?: string })._sourceFile; + const result = toolManifestEntrySchema.safeParse(raw); + if (!result.success) { + throw new ManifestValidationError( + `Invalid tool manifest: ${result.error.message}`, + sourceFile, + ); + } + + const tool = result.data; + + // Check for duplicate ID + if (tools.has(tool.id)) { + throw new ManifestValidationError(`Duplicate tool ID '${tool.id}'`, sourceFile); + } + + // Validate predicates + for (const pred of tool.predicates) { + if (!isValidPredicate(pred)) { + throw new ManifestValidationError( + `Unknown predicate '${pred}' in tool '${tool.id}'`, + sourceFile, + ); + } + } + + tools.set(tool.id, tool); + } + + // Load workflows + const workflowFiles = loadYamlFiles(workflowsDir); + for (const raw of workflowFiles) { + const sourceFile = (raw as { _sourceFile?: string })._sourceFile; + const result = workflowManifestEntrySchema.safeParse(raw); + if (!result.success) { + throw new ManifestValidationError( + `Invalid workflow manifest: ${result.error.message}`, + sourceFile, + ); + } + + const workflow = result.data; + + // Check for duplicate ID + if (workflows.has(workflow.id)) { + throw new ManifestValidationError(`Duplicate workflow ID '${workflow.id}'`, sourceFile); + } + + // Validate predicates + for (const pred of workflow.predicates) { + if (!isValidPredicate(pred)) { + throw new ManifestValidationError( + `Unknown predicate '${pred}' in workflow '${workflow.id}'`, + sourceFile, + ); + } + } + + // Validate tool references + for (const toolId of workflow.tools) { + if (!tools.has(toolId)) { + throw new ManifestValidationError( + `Workflow '${workflow.id}' references unknown tool '${toolId}'`, + sourceFile, + ); + } + } + + workflows.set(workflow.id, workflow); + } + + // Validate MCP name uniqueness + const mcpNames = new Map(); // mcpName -> toolId + for (const [toolId, tool] of tools) { + const existing = mcpNames.get(tool.names.mcp); + if (existing) { + throw new ManifestValidationError( + `Duplicate MCP name '${tool.names.mcp}' used by tools '${existing}' and '${toolId}'`, + ); + } + mcpNames.set(tool.names.mcp, toolId); + } + + // Validate next step template references + for (const [toolId, tool] of tools.entries()) { + const sourceFile = toolFiles.find((raw) => { + const candidate = raw as { id?: string; _sourceFile?: string }; + return candidate.id === toolId; + }) as { _sourceFile?: string } | undefined; + + for (const nextStep of tool.nextSteps) { + if (nextStep.toolId && !tools.has(nextStep.toolId)) { + throw new ManifestValidationError( + `Tool '${toolId}' next step references unknown tool '${nextStep.toolId}'`, + sourceFile?._sourceFile, + ); + } + } + } + + return { tools, workflows }; +} + +/** + * Validate that all tool modules exist on disk. + * Call this at startup to fail fast on missing modules. + */ +export function validateToolModules(manifest: ResolvedManifest): void { + const packageRoot = getPackageRoot(); + + for (const [toolId, tool] of manifest.tools) { + const modulePath = path.join(packageRoot, 'build', `${tool.module}.js`); + if (!fs.existsSync(modulePath)) { + throw new ManifestValidationError( + `Tool '${toolId}' references missing module: ${modulePath}`, + ); + } + } +} + +/** + * Get tools for a specific workflow. + */ +export function getWorkflowTools( + manifest: ResolvedManifest, + workflowId: string, +): ToolManifestEntry[] { + const workflow = manifest.workflows.get(workflowId); + if (!workflow) { + return []; + } + + return workflow.tools + .map((toolId) => manifest.tools.get(toolId)) + .filter((t): t is ToolManifestEntry => t !== undefined); +} + +/** + * Get all unique tools across selected workflows. + */ +export function getToolsForWorkflows( + manifest: ResolvedManifest, + workflowIds: string[], +): ToolManifestEntry[] { + const seenToolIds = new Set(); + const tools: ToolManifestEntry[] = []; + + for (const workflowId of workflowIds) { + const workflowTools = getWorkflowTools(manifest, workflowId); + for (const tool of workflowTools) { + if (!seenToolIds.has(tool.id)) { + seenToolIds.add(tool.id); + tools.push(tool); + } + } + } + + return tools; +} + +/** + * Get workflow metadata from the manifest. + * Returns a record mapping workflow IDs to their title/description. + */ +export function getWorkflowMetadataFromManifest(): Record< + string, + { name: string; description: string } +> { + const manifest = loadManifest(); + const metadata: Record = {}; + + for (const [id, workflow] of manifest.workflows.entries()) { + metadata[id] = { + name: workflow.title, + description: workflow.description, + }; + } + + return metadata; +} diff --git a/src/core/manifest/schema.ts b/src/core/manifest/schema.ts new file mode 100644 index 00000000..11e4e7e0 --- /dev/null +++ b/src/core/manifest/schema.ts @@ -0,0 +1,184 @@ +/** + * Zod schemas for manifest YAML validation. + * These schemas define the canonical data model for tools and workflows. + */ + +import { z } from 'zod'; + +/** + * Availability flags for different runtimes. + */ +export const availabilitySchema = z + .object({ + mcp: z.boolean().default(true), + cli: z.boolean().default(true), + }) + .strict(); + +export type Availability = z.infer; + +/** + * Routing hints for daemon-backed CLI execution. + */ +export const routingSchema = z + .object({ + stateful: z.boolean().default(false), + }) + .strict(); + +export type Routing = z.infer; + +/** + * MCP tool annotations (hints for clients). + * All properties are optional hints, not guarantees. + */ +export const annotationsSchema = z.object({ + title: z.string().optional(), + readOnlyHint: z.boolean().optional(), + destructiveHint: z.boolean().optional(), + idempotentHint: z.boolean().optional(), + openWorldHint: z.boolean().optional(), +}); + +export type Annotations = z.infer; + +/** + * Tool names for MCP and CLI. + */ +export const toolNamesSchema = z.object({ + /** MCP name is required and must be globally unique */ + mcp: z.string(), + /** CLI name is optional; if omitted, derived from MCP name */ + cli: z.string().optional(), +}); + +export type ToolNames = z.infer; + +/** + * Static next-step template declared on a tool manifest. + */ +export const manifestNextStepTemplateSchema = z + .object({ + label: z.string(), + toolId: z.string().optional(), + params: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).default({}), + priority: z.number().optional(), + }) + .strict(); + +export type ManifestNextStepTemplate = z.infer; + +/** + * Tool manifest entry schema. + * Describes a single tool's metadata and configuration. + */ +export const toolManifestEntrySchema = z.object({ + /** Unique tool identifier */ + id: z.string(), + + /** + * Module path (extensionless, package-relative). + * Resolved to build/.js at runtime. + */ + module: z.string(), + + /** Tool names for MCP and CLI */ + names: toolNamesSchema, + + /** Tool description */ + description: z.string().optional(), + + /** Per-runtime availability flags */ + availability: availabilitySchema.default({ mcp: true, cli: true }), + + /** Predicate names for visibility filtering (all must pass) */ + predicates: z.array(z.string()).default([]), + + /** Routing hints for daemon */ + routing: routingSchema.optional(), + + /** MCP annotations (hints for clients) */ + annotations: annotationsSchema.optional(), + + /** Static next-step templates for this tool */ + nextSteps: z.array(manifestNextStepTemplateSchema).default([]), +}); + +export type ToolManifestEntry = z.infer; + +/** + * MCP-specific workflow selection rules. + */ +export const workflowSelectionMcpSchema = z.object({ + /** Used when config.enabledWorkflows is empty */ + defaultEnabled: z.boolean().default(false), + /** Include when predicates pass, regardless of user selection */ + autoInclude: z.boolean().default(false), +}); + +export type WorkflowSelectionMcp = z.infer; + +/** + * Workflow selection rules. + */ +export const workflowSelectionSchema = z.object({ + mcp: workflowSelectionMcpSchema.optional(), +}); + +export type WorkflowSelection = z.infer; + +/** + * Workflow manifest entry schema. + * Describes a workflow's metadata and tool composition. + */ +export const workflowManifestEntrySchema = z.object({ + /** Unique workflow identifier (matches directory name) */ + id: z.string(), + + /** Display title for the workflow */ + title: z.string(), + + /** Workflow description */ + description: z.string(), + + /** Per-runtime availability flags */ + availability: availabilitySchema.default({ mcp: true, cli: true }), + + /** MCP selection rules */ + selection: workflowSelectionSchema.optional(), + + /** Predicate names for visibility filtering (all must pass) */ + predicates: z.array(z.string()).default([]), + + /** Tool IDs belonging to this workflow */ + tools: z.array(z.string()), +}); + +export type WorkflowManifestEntry = z.infer; + +/** + * Resolved manifest containing all tools and workflows. + */ +export interface ResolvedManifest { + tools: Map; + workflows: Map; +} + +/** + * Derive CLI name from MCP name using kebab-case conversion. + * - Underscores become hyphens + * - camelCase becomes kebab-case + */ +export function deriveCliName(mcpName: string): string { + return mcpName + .replace(/_/g, '-') + .replace(/([a-z])([A-Z])/g, '$1-$2') + .toLowerCase(); +} + +/** + * Get the effective CLI name for a tool. + */ +export function getEffectiveCliName(tool: ToolManifestEntry): string { + return tool.names.cli ?? deriveCliName(tool.names.mcp); +} diff --git a/src/core/plugin-registry.ts b/src/core/plugin-registry.ts deleted file mode 100644 index cf4b2aaa..00000000 --- a/src/core/plugin-registry.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { PluginMeta, WorkflowGroup, WorkflowMeta } from './plugin-types.ts'; -import { WORKFLOW_LOADERS, WorkflowName, WORKFLOW_METADATA } from './generated-plugins.ts'; - -export async function loadPlugins(): Promise> { - const plugins = new Map(); - - // Load all workflows and collect all their tools - const workflowGroups = await loadWorkflowGroups(); - - for (const [, workflow] of workflowGroups.entries()) { - for (const tool of workflow.tools) { - if (tool?.name && typeof tool.handler === 'function') { - plugins.set(tool.name, tool); - } - } - } - - return plugins; -} - -/** - * Load workflow groups with metadata validation using generated loaders - */ -export async function loadWorkflowGroups(): Promise> { - const workflows = new Map(); - - for (const [workflowName, loader] of Object.entries(WORKFLOW_LOADERS)) { - try { - // Dynamic import with code-splitting - const workflowModule = (await loader()) as { - workflow?: WorkflowMeta; - [key: string]: unknown; - }; - - if (!workflowModule.workflow) { - throw new Error(`Workflow metadata missing in ${workflowName}/index.js`); - } - - // Validate required fields - const workflowMeta = workflowModule.workflow as WorkflowMeta; - if (!workflowMeta.name || typeof workflowMeta.name !== 'string') { - throw new Error( - `Invalid workflow.name in ${workflowName}/index.js: must be a non-empty string`, - ); - } - - if (!workflowMeta.description || typeof workflowMeta.description !== 'string') { - throw new Error( - `Invalid workflow.description in ${workflowName}/index.js: must be a non-empty string`, - ); - } - - workflows.set(workflowName, { - workflow: workflowMeta, - tools: await loadWorkflowTools(workflowModule), - directoryName: workflowName, - }); - } catch (error) { - throw new Error( - `Failed to load workflow '${workflowName}': ${error instanceof Error ? error.message : 'Unknown error'}`, - ); - } - } - - return workflows; -} - -/** - * Load workflow tools from the workflow module - */ -async function loadWorkflowTools(workflowModule: Record): Promise { - const tools: PluginMeta[] = []; - - // Load individual tool files from the workflow module - for (const [key, value] of Object.entries(workflowModule)) { - if (key !== 'workflow' && value && typeof value === 'object') { - const tool = value as PluginMeta; - if (tool.name && typeof tool.handler === 'function') { - tools.push(tool); - } - } - } - - return tools; -} - -/** - * Get workflow metadata by directory name using generated loaders - */ -export async function getWorkflowMetadata(directoryName: string): Promise { - try { - // First try to get from generated metadata (fast path) - const metadata = WORKFLOW_METADATA[directoryName as WorkflowName]; - if (metadata) { - return metadata; - } - - // Fall back to loading the actual module - const loader = WORKFLOW_LOADERS[directoryName as WorkflowName]; - if (loader) { - const workflowModule = (await loader()) as { workflow?: WorkflowMeta }; - return workflowModule.workflow ?? null; - } - - return null; - } catch { - return null; - } -} diff --git a/src/core/plugin-types.ts b/src/core/plugin-types.ts index 4077682a..ad617ac5 100644 --- a/src/core/plugin-types.ts +++ b/src/core/plugin-types.ts @@ -1,14 +1,24 @@ import * as z from 'zod'; -import { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; -import { ToolResponse } from '../types/common.ts'; +import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; +import type { ToolResponse } from '../types/common.ts'; export type ToolSchemaShape = Record; +export interface PluginCliMeta { + /** Optional override of derived CLI name */ + readonly name?: string; + /** Full schema shape for CLI flag generation (legacy, includes session-managed fields) */ + readonly schema?: ToolSchemaShape; + /** Mark tool as requiring daemon routing */ + readonly stateful?: boolean; +} + export interface PluginMeta { readonly name: string; // Verb used by MCP readonly schema: ToolSchemaShape; // Zod validation schema (object schema) readonly description?: string; // One-liner shown in help readonly annotations?: ToolAnnotations; // MCP tool annotations for LLM behavior hints + readonly cli?: PluginCliMeta; // CLI-specific metadata (optional) handler(params: Record): Promise; } diff --git a/src/core/resource-root.ts b/src/core/resource-root.ts new file mode 100644 index 00000000..7005a1ec --- /dev/null +++ b/src/core/resource-root.ts @@ -0,0 +1,99 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const RESOURCE_ROOT_ENV_VAR = 'XCODEBUILDMCP_RESOURCE_ROOT'; +let cachedPackageRoot: string | null = null; +let cachedResourceRoot: string | null = null; + +export function resetResourceRootCacheForTests(): void { + cachedPackageRoot = null; + cachedResourceRoot = null; +} + +function hasResourceLayout(root: string): boolean { + return fs.existsSync(path.join(root, 'manifests')) || fs.existsSync(path.join(root, 'bundled')); +} + +function findPackageRootFrom(startDir: string): string | null { + let dir = startDir; + while (dir !== path.dirname(dir)) { + if (fs.existsSync(path.join(dir, 'package.json'))) { + return dir; + } + dir = path.dirname(dir); + } + return null; +} + +export function getPackageRoot(): string { + if (cachedPackageRoot) { + return cachedPackageRoot; + } + + const candidates: string[] = []; + const importMetaUrl = typeof import.meta.url === 'string' ? import.meta.url : null; + if (importMetaUrl) { + candidates.push(path.dirname(fileURLToPath(importMetaUrl))); + } + candidates.push(process.cwd()); + const entry = process.argv[1]; + if (entry) { + candidates.push(path.dirname(entry)); + } + + for (const candidate of candidates) { + const found = findPackageRootFrom(candidate); + if (found) { + cachedPackageRoot = found; + return found; + } + } + + throw new Error('Could not find package root (no package.json found in parent directories)'); +} + +function getExecutableResourceRoot(): string | null { + const execPath = process.execPath; + const candidateDirs = [path.dirname(execPath), path.dirname(path.dirname(execPath))]; + for (const candidate of candidateDirs) { + if (hasResourceLayout(candidate)) { + return candidate; + } + } + + return null; +} + +export function getResourceRoot(): string { + if (cachedResourceRoot) { + return cachedResourceRoot; + } + + const explicitRoot = process.env[RESOURCE_ROOT_ENV_VAR]; + if (explicitRoot) { + cachedResourceRoot = path.resolve(explicitRoot); + return cachedResourceRoot; + } + + const executableRoot = getExecutableResourceRoot(); + if (executableRoot) { + cachedResourceRoot = executableRoot; + return cachedResourceRoot; + } + + cachedResourceRoot = getPackageRoot(); + return cachedResourceRoot; +} + +export function getManifestsDir(): string { + return path.join(getResourceRoot(), 'manifests'); +} + +export function getBundledAxePath(): string { + return path.join(getResourceRoot(), 'bundled', 'axe'); +} + +export function getBundledFrameworksDir(): string { + return path.join(getResourceRoot(), 'bundled', 'Frameworks'); +} diff --git a/src/core/resources.ts b/src/core/resources.ts index 478f7a8d..c62722e8 100644 --- a/src/core/resources.ts +++ b/src/core/resources.ts @@ -2,20 +2,20 @@ * Resource Management - MCP Resource handlers and URI management * * This module manages MCP resources, providing a unified interface for exposing - * data through the Model Context Protocol resource system. Resources allow clients - * to access data via URI references without requiring tool calls. - * - * Responsibilities: - * - Loading resources from the plugin-based resource system - * - Managing resource registration with the MCP server - * - Providing fallback compatibility for clients without resource support + * data through the Model Context Protocol resource system. */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; +import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; import { log } from '../utils/logging/index.ts'; import type { CommandExecutor } from '../utils/execution/index.ts'; -import { RESOURCE_LOADERS } from './generated-resources.ts'; + +// Direct imports - no codegen needed +import devicesResource from '../mcp/resources/devices.ts'; +import doctorResource from '../mcp/resources/doctor.ts'; +import sessionStatusResource from '../mcp/resources/session-status.ts'; +import simulatorsResource from '../mcp/resources/simulators.ts'; +import xcodeIdeStateResource from '../mcp/resources/xcode-ide-state.ts'; /** * Resource metadata interface @@ -34,46 +34,51 @@ export interface ResourceMeta { } /** - * Load all resources using generated loaders + * All available resources + */ +const RESOURCES: ResourceMeta[] = [ + devicesResource, + doctorResource, + sessionStatusResource, + simulatorsResource, + xcodeIdeStateResource, +]; + +/** + * Load all resources * @returns Map of resource URI to resource metadata */ export async function loadResources(): Promise> { const resources = new Map(); - for (const [resourceName, loader] of Object.entries(RESOURCE_LOADERS)) { - try { - const resource = (await loader()) as ResourceMeta; - - if (!resource.uri || !resource.handler || typeof resource.handler !== 'function') { - throw new Error(`Invalid resource structure for ${resourceName}`); - } - - resources.set(resource.uri, resource); - log('info', `Loaded resource: ${resourceName} (${resource.uri})`); - } catch (error) { + for (const resource of RESOURCES) { + if (!resource.uri || !resource.handler || typeof resource.handler !== 'function') { log( 'error', - `Failed to load resource ${resourceName}: ${error instanceof Error ? error.message : String(error)}`, + `[infra/resources] invalid resource structure for ${resource.name ?? 'unknown'}`, + { sentry: true }, ); + continue; } + + resources.set(resource.uri, resource); + log('info', `Loaded resource: ${resource.name} (${resource.uri})`); } return resources; } /** - * Register all resources with the MCP server if client supports resources + * Register all resources with the MCP server * @param server The MCP server instance - * @returns true if resources were registered, false if skipped due to client limitations + * @returns true if resources were registered */ export async function registerResources(server: McpServer): Promise { const resources = await loadResources(); - for (const [uri, resource] of Array.from(resources)) { - // Create a handler wrapper that matches ReadResourceCallback signature + for (const [uri, resource] of resources) { const readCallback = async (resourceUri: URL): Promise => { const result = await resource.handler(resourceUri); - // Transform the content to match MCP SDK expectations return { contents: result.contents.map((content) => ({ uri: resourceUri.toString(), diff --git a/src/daemon.ts b/src/daemon.ts new file mode 100644 index 00000000..4c691559 --- /dev/null +++ b/src/daemon.ts @@ -0,0 +1,425 @@ +#!/usr/bin/env node +import net from 'node:net'; +import { dirname } from 'node:path'; +import { existsSync, mkdirSync, renameSync, statSync } from 'node:fs'; +import { bootstrapRuntime } from './runtime/bootstrap-runtime.ts'; +import { buildDaemonToolCatalogFromManifest } from './runtime/tool-catalog.ts'; +import { loadManifest } from './core/manifest/load-manifest.ts'; +import { + ensureSocketDir, + removeStaleSocket, + getSocketPath, + getWorkspaceKey, + resolveWorkspaceRoot, + logPathForWorkspaceKey, +} from './daemon/socket-path.ts'; +import { startDaemonServer } from './daemon/daemon-server.ts'; +import { + writeDaemonRegistryEntry, + removeDaemonRegistryEntry, + cleanupWorkspaceDaemonFiles, +} from './daemon/daemon-registry.ts'; +import { log, setLogFile, setLogLevel, type LogLevel } from './utils/logger.ts'; +import { version } from './version.ts'; +import { + DAEMON_IDLE_TIMEOUT_ENV_KEY, + DEFAULT_DAEMON_IDLE_CHECK_INTERVAL_MS, + resolveDaemonIdleTimeoutMs, + getDaemonRuntimeActivitySnapshot, + hasActiveRuntimeSessions, +} from './daemon/idle-shutdown.ts'; +import { getDefaultCommandExecutor } from './utils/command.ts'; +import { resolveAxeBinary } from './utils/axe/index.ts'; +import { + flushAndCloseSentry, + getAxeVersionMetadata, + getXcodeVersionMetadata, + initSentry, + recordBootstrapDurationMetric, + recordDaemonGaugeMetric, + recordDaemonLifecycleMetric, + setSentryRuntimeContext, +} from './utils/sentry.ts'; +import { isXcodemakeBinaryAvailable, isXcodemakeEnabled } from './utils/xcodemake/index.ts'; + +async function checkExistingDaemon(socketPath: string): Promise { + return new Promise((resolve) => { + const socket = net.createConnection(socketPath); + + socket.on('connect', () => { + socket.end(); + resolve(true); + }); + + socket.on('error', () => { + resolve(false); + }); + }); +} + +function writeLine(text: string): void { + process.stdout.write(`${text}\n`); +} + +const MAX_LOG_BYTES = 10 * 1024 * 1024; +const MAX_LOG_ROTATIONS = 3; + +function rotateLogIfNeeded(logPath: string): void { + if (!existsSync(logPath)) { + return; + } + + const size = statSync(logPath).size; + if (size < MAX_LOG_BYTES) { + return; + } + + for (let index = MAX_LOG_ROTATIONS - 1; index >= 1; index -= 1) { + const from = `${logPath}.${index}`; + const to = `${logPath}.${index + 1}`; + if (existsSync(from)) { + renameSync(from, to); + } + } + + renameSync(logPath, `${logPath}.1`); +} + +function resolveDaemonLogPath(workspaceKey: string): string | null { + const override = process.env.XCODEBUILDMCP_DAEMON_LOG_PATH?.trim(); + if (override) { + return override; + } + + return logPathForWorkspaceKey(workspaceKey); +} + +function ensureLogDir(logPath: string): void { + const dir = dirname(logPath); + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true, mode: 0o700 }); + } +} + +function resolveLogLevel(): LogLevel | null { + const raw = process.env.XCODEBUILDMCP_DAEMON_LOG_LEVEL?.trim().toLowerCase(); + if (!raw) { + return null; + } + + const knownLevels: LogLevel[] = [ + 'none', + 'emergency', + 'alert', + 'critical', + 'error', + 'warning', + 'notice', + 'info', + 'debug', + ]; + + if (knownLevels.includes(raw as LogLevel)) { + return raw as LogLevel; + } + + return null; +} + +async function main(): Promise { + const daemonBootstrapStart = Date.now(); + const result = await bootstrapRuntime({ + runtime: 'daemon', + configOverrides: { + disableSessionDefaults: true, + }, + }); + + const workspaceRoot = resolveWorkspaceRoot({ + cwd: result.runtime.cwd, + projectConfigPath: result.configPath, + }); + + const workspaceKey = getWorkspaceKey({ + cwd: result.runtime.cwd, + projectConfigPath: result.configPath, + }); + + const logPath = resolveDaemonLogPath(workspaceKey); + if (logPath) { + ensureLogDir(logPath); + rotateLogIfNeeded(logPath); + setLogFile(logPath); + + setLogLevel(resolveLogLevel() ?? 'info'); + } + + initSentry({ mode: 'cli-daemon' }); + recordDaemonLifecycleMetric('start'); + + log('info', `[Daemon] xcodebuildmcp daemon ${version} starting...`); + + const socketPath = getSocketPath({ + cwd: result.runtime.cwd, + projectConfigPath: result.configPath, + }); + + log('info', `[Daemon] Workspace: ${workspaceRoot}`); + log('info', `[Daemon] Socket: ${socketPath}`); + if (logPath) { + log('info', `[Daemon] Logs: ${logPath}`); + } + + ensureSocketDir(socketPath); + + const isRunning = await checkExistingDaemon(socketPath); + if (isRunning) { + log('error', '[Daemon] Another daemon is already running for this workspace'); + console.error('Error: Daemon is already running for this workspace'); + await flushAndCloseSentry(1000); + process.exit(1); + } + + removeStaleSocket(socketPath); + + const excludedWorkflows = ['session-management', 'workflow-discovery']; + + // Daemon runtime serves CLI routing and should not be filtered by enabledWorkflows. + // CLI exposure is controlled at CLI catalog/command registration time. + // Get all workflows from manifest (for reporting purposes and filtering). + const manifest = loadManifest(); + const allWorkflowIds = Array.from(manifest.workflows.keys()); + const daemonWorkflows = allWorkflowIds.filter( + (workflowId) => !excludedWorkflows.includes(workflowId), + ); + const xcodeIdeWorkflowEnabled = daemonWorkflows.includes('xcode-ide'); + const axeBinary = resolveAxeBinary(); + const axeAvailable = axeBinary !== null; + const axeSource: 'env' | 'bundled' | 'path' | 'unavailable' = axeBinary?.source ?? 'unavailable'; + const xcodemakeAvailable = isXcodemakeBinaryAvailable(); + const xcodemakeEnabled = isXcodemakeEnabled(); + const baseSentryRuntimeContext = { + mode: 'cli-daemon' as const, + enabledWorkflows: daemonWorkflows, + disableSessionDefaults: result.runtime.config.disableSessionDefaults, + disableXcodeAutoSync: result.runtime.config.disableXcodeAutoSync, + incrementalBuildsEnabled: result.runtime.config.incrementalBuildsEnabled, + debugEnabled: result.runtime.config.debug, + uiDebuggerGuardMode: result.runtime.config.uiDebuggerGuardMode, + xcodeIdeWorkflowEnabled, + axeAvailable, + axeSource, + xcodemakeAvailable, + xcodemakeEnabled, + }; + setSentryRuntimeContext(baseSentryRuntimeContext); + + const enrichSentryMetadata = async (): Promise => { + const commandExecutor = getDefaultCommandExecutor(); + const xcodeVersion = await getXcodeVersionMetadata(async (command) => { + const result = await commandExecutor(command, 'Get Xcode Version'); + return { success: result.success, output: result.output }; + }); + const xcodeAvailable = Boolean( + xcodeVersion.version || + xcodeVersion.buildVersion || + xcodeVersion.developerDir || + xcodeVersion.xcodebuildPath, + ); + const axeVersion = await getAxeVersionMetadata(async (command) => { + const result = await commandExecutor(command, 'Get AXe Version'); + return { success: result.success, output: result.output }; + }, axeBinary?.path); + + setSentryRuntimeContext({ + ...baseSentryRuntimeContext, + xcodeAvailable, + axeVersion, + xcodeDeveloperDir: xcodeVersion.developerDir, + xcodebuildPath: xcodeVersion.xcodebuildPath, + xcodeVersion: xcodeVersion.version, + xcodeBuildVersion: xcodeVersion.buildVersion, + }); + }; + + const catalog = await buildDaemonToolCatalogFromManifest({ + excludeWorkflows: excludedWorkflows, + }); + + log('info', `[Daemon] Loaded ${catalog.tools.length} tools`); + + const startedAt = new Date().toISOString(); + const idleTimeoutMs = resolveDaemonIdleTimeoutMs(); + const configuredIdleTimeout = process.env[DAEMON_IDLE_TIMEOUT_ENV_KEY]?.trim(); + if (configuredIdleTimeout) { + const parsedIdleTimeout = Number(configuredIdleTimeout); + if (!Number.isFinite(parsedIdleTimeout) || parsedIdleTimeout < 0) { + log( + 'warn', + `[Daemon] Invalid ${DAEMON_IDLE_TIMEOUT_ENV_KEY}=${configuredIdleTimeout}; using default ${idleTimeoutMs}ms`, + ); + } + } + + if (idleTimeoutMs === 0) { + log('info', '[Daemon] Idle shutdown disabled'); + } else { + log( + 'info', + `[Daemon] Idle shutdown enabled: timeout=${idleTimeoutMs}ms interval=${DEFAULT_DAEMON_IDLE_CHECK_INTERVAL_MS}ms`, + ); + } + recordDaemonGaugeMetric('idle_timeout_ms', idleTimeoutMs); + + let isShuttingDown = false; + let inFlightRequests = 0; + let lastActivityAt = Date.now(); + let idleCheckTimer: NodeJS.Timeout | null = null; + + const markActivity = (): void => { + lastActivityAt = Date.now(); + }; + + // Unified shutdown handler + const shutdown = (): void => { + if (isShuttingDown) { + return; + } + isShuttingDown = true; + + if (idleCheckTimer) { + clearInterval(idleCheckTimer); + idleCheckTimer = null; + } + + recordDaemonLifecycleMetric('shutdown'); + log('info', '[Daemon] Shutting down...'); + + // Close the server + server.close(() => { + log('info', '[Daemon] Server closed'); + + // Remove registry entry and socket + removeDaemonRegistryEntry(workspaceKey); + removeStaleSocket(socketPath); + + log('info', '[Daemon] Cleanup complete'); + void flushAndCloseSentry(2000).finally(() => { + process.exit(0); + }); + }); + + // Force exit if server doesn't close in time + setTimeout(() => { + log('warning', '[Daemon] Forced shutdown after timeout'); + cleanupWorkspaceDaemonFiles(workspaceKey); + void flushAndCloseSentry(1000).finally(() => { + process.exit(1); + }); + }, 5000); + }; + + const emitRequestGauges = (): void => { + recordDaemonGaugeMetric('inflight_requests', inFlightRequests); + recordDaemonGaugeMetric( + 'active_sessions', + getDaemonRuntimeActivitySnapshot().activeOperationCount, + ); + }; + + const server = startDaemonServer({ + socketPath, + logPath: logPath ?? undefined, + startedAt, + enabledWorkflows: daemonWorkflows, + catalog, + workspaceRoot, + workspaceKey, + xcodeIdeWorkflowEnabled, + requestShutdown: shutdown, + onRequestStarted: () => { + inFlightRequests += 1; + markActivity(); + emitRequestGauges(); + }, + onRequestFinished: () => { + inFlightRequests = Math.max(0, inFlightRequests - 1); + markActivity(); + emitRequestGauges(); + }, + }); + emitRequestGauges(); + + if (idleTimeoutMs > 0) { + idleCheckTimer = setInterval(() => { + if (isShuttingDown) { + return; + } + + emitRequestGauges(); + + const idleForMs = Date.now() - lastActivityAt; + if (idleForMs < idleTimeoutMs) { + return; + } + + if (inFlightRequests > 0) { + return; + } + + if (hasActiveRuntimeSessions(getDaemonRuntimeActivitySnapshot())) { + return; + } + + log( + 'info', + `[Daemon] Idle timeout reached (${idleForMs}ms >= ${idleTimeoutMs}ms); shutting down`, + ); + shutdown(); + }, DEFAULT_DAEMON_IDLE_CHECK_INTERVAL_MS); + idleCheckTimer.unref?.(); + } + + server.listen(socketPath, () => { + log('info', `[Daemon] Listening on ${socketPath}`); + + // Write registry entry after successful listen + writeDaemonRegistryEntry({ + workspaceKey, + workspaceRoot, + socketPath, + logPath: logPath ?? undefined, + pid: process.pid, + startedAt, + enabledWorkflows: daemonWorkflows, + version: String(version), + }); + + writeLine(`Daemon started (PID: ${process.pid})`); + writeLine(`Workspace: ${workspaceRoot}`); + writeLine(`Socket: ${socketPath}`); + writeLine(`Tools: ${catalog.tools.length}`); + recordBootstrapDurationMetric('cli-daemon', Date.now() - daemonBootstrapStart); + + setImmediate(() => { + void enrichSentryMetadata().catch((error) => { + const message = error instanceof Error ? error.message : String(error); + log('warning', `[Daemon] Failed to enrich Sentry metadata: ${message}`); + }); + }); + }); + + process.on('SIGTERM', shutdown); + process.on('SIGINT', shutdown); +} + +main().catch(async (err) => { + recordDaemonLifecycleMetric('crash'); + const message = + err == null ? 'Unknown daemon error' : err instanceof Error ? err.message : String(err); + + log('error', `Daemon error: ${message}`, { sentry: true }); + console.error('Daemon error:', message); + await flushAndCloseSentry(2000); + process.exit(1); +}); diff --git a/src/daemon/__tests__/activity-registry.test.ts b/src/daemon/__tests__/activity-registry.test.ts new file mode 100644 index 00000000..b747e9ab --- /dev/null +++ b/src/daemon/__tests__/activity-registry.test.ts @@ -0,0 +1,57 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { + acquireDaemonActivity, + clearDaemonActivityRegistry, + getDaemonActivitySnapshot, +} from '../activity-registry.ts'; + +describe('daemon activity registry', () => { + beforeEach(() => { + clearDaemonActivityRegistry(); + }); + + afterEach(() => { + clearDaemonActivityRegistry(); + }); + + it('tracks acquired activity by category', () => { + const releaseFirst = acquireDaemonActivity('logging.simulator'); + const releaseSecond = acquireDaemonActivity('logging.simulator'); + + expect(getDaemonActivitySnapshot()).toEqual({ + activeOperationCount: 2, + byCategory: { + 'logging.simulator': 2, + }, + }); + + releaseFirst(); + expect(getDaemonActivitySnapshot()).toEqual({ + activeOperationCount: 1, + byCategory: { + 'logging.simulator': 1, + }, + }); + + releaseSecond(); + expect(getDaemonActivitySnapshot()).toEqual({ + activeOperationCount: 0, + byCategory: {}, + }); + }); + + it('treats release as idempotent', () => { + const release = acquireDaemonActivity('video.capture'); + release(); + release(); + + expect(getDaemonActivitySnapshot()).toEqual({ + activeOperationCount: 0, + byCategory: {}, + }); + }); + + it('rejects empty activity keys', () => { + expect(() => acquireDaemonActivity(' ')).toThrow('activityKey must be a non-empty string'); + }); +}); diff --git a/src/daemon/__tests__/idle-shutdown.test.ts b/src/daemon/__tests__/idle-shutdown.test.ts new file mode 100644 index 00000000..7d72e31b --- /dev/null +++ b/src/daemon/__tests__/idle-shutdown.test.ts @@ -0,0 +1,66 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { + DAEMON_IDLE_TIMEOUT_ENV_KEY, + DEFAULT_DAEMON_IDLE_TIMEOUT_MS, + getDaemonRuntimeActivitySnapshot, + hasActiveRuntimeSessions, + resolveDaemonIdleTimeoutMs, +} from '../idle-shutdown.ts'; +import { acquireDaemonActivity, clearDaemonActivityRegistry } from '../activity-registry.ts'; + +describe('daemon idle shutdown', () => { + beforeEach(() => { + clearDaemonActivityRegistry(); + }); + + afterEach(() => { + clearDaemonActivityRegistry(); + }); + + describe('resolveDaemonIdleTimeoutMs', () => { + it('uses default timeout when env is not set', () => { + expect(resolveDaemonIdleTimeoutMs({})).toBe(DEFAULT_DAEMON_IDLE_TIMEOUT_MS); + }); + + it('uses configured timeout when env has a valid value', () => { + expect(resolveDaemonIdleTimeoutMs({ [DAEMON_IDLE_TIMEOUT_ENV_KEY]: '15000' })).toBe(15000); + }); + + it('falls back to default timeout when env has an invalid value', () => { + expect(resolveDaemonIdleTimeoutMs({ [DAEMON_IDLE_TIMEOUT_ENV_KEY]: '-1' })).toBe( + DEFAULT_DAEMON_IDLE_TIMEOUT_MS, + ); + expect(resolveDaemonIdleTimeoutMs({ [DAEMON_IDLE_TIMEOUT_ENV_KEY]: 'NaN' })).toBe( + DEFAULT_DAEMON_IDLE_TIMEOUT_MS, + ); + }); + }); + + describe('hasActiveRuntimeSessions', () => { + it('returns false when active operation count is zero', () => { + expect(hasActiveRuntimeSessions({ activeOperationCount: 0, byCategory: {} })).toBe(false); + }); + + it('returns true when active operation count is positive', () => { + expect( + hasActiveRuntimeSessions({ + activeOperationCount: 1, + byCategory: { 'video.capture': 1 }, + }), + ).toBe(true); + }); + }); + + describe('getDaemonRuntimeActivitySnapshot', () => { + it('reports category counters for active daemon activity', () => { + const release = acquireDaemonActivity('swift-package.background-process'); + + const snapshot = getDaemonRuntimeActivitySnapshot(); + expect(snapshot.activeOperationCount).toBe(1); + expect(snapshot.byCategory).toEqual({ + 'swift-package.background-process': 1, + }); + release(); + }); + }); +}); diff --git a/src/daemon/activity-registry.ts b/src/daemon/activity-registry.ts new file mode 100644 index 00000000..17185856 --- /dev/null +++ b/src/daemon/activity-registry.ts @@ -0,0 +1,67 @@ +const activityCounts = new Map(); + +function normalizeActivityKey(activityKey: string): string { + return activityKey.trim(); +} + +function incrementActivity(activityKey: string): void { + const current = activityCounts.get(activityKey) ?? 0; + activityCounts.set(activityKey, current + 1); +} + +function decrementActivity(activityKey: string): void { + const current = activityCounts.get(activityKey) ?? 0; + if (current <= 1) { + activityCounts.delete(activityKey); + return; + } + activityCounts.set(activityKey, current - 1); +} + +/** + * Acquire a long-running daemon activity lease. + * Call the returned release function once the activity has finished. + */ +export function acquireDaemonActivity(activityKey: string): () => void { + const normalizedKey = normalizeActivityKey(activityKey); + if (!normalizedKey) { + throw new Error('activityKey must be a non-empty string'); + } + + incrementActivity(normalizedKey); + + let released = false; + return (): void => { + if (released) { + return; + } + released = true; + decrementActivity(normalizedKey); + }; +} + +export interface DaemonActivitySnapshot { + activeOperationCount: number; + byCategory: Record; +} + +export function getDaemonActivitySnapshot(): DaemonActivitySnapshot { + const byCategory = Object.fromEntries( + Array.from(activityCounts.entries()).sort(([left], [right]) => left.localeCompare(right)), + ); + const activeOperationCount = Array.from(activityCounts.values()).reduce( + (accumulator, count) => accumulator + count, + 0, + ); + return { + activeOperationCount, + byCategory, + }; +} + +/** + * Test helper to reset shared process-local activity state. + */ +export function clearDaemonActivityRegistry(): void { + activityCounts.clear(); +} diff --git a/src/daemon/daemon-registry.ts b/src/daemon/daemon-registry.ts new file mode 100644 index 00000000..5a18d141 --- /dev/null +++ b/src/daemon/daemon-registry.ts @@ -0,0 +1,137 @@ +import { + existsSync, + mkdirSync, + readdirSync, + readFileSync, + unlinkSync, + writeFileSync, +} from 'node:fs'; +import { join, dirname } from 'node:path'; +import { + daemonsDir, + daemonDirForWorkspaceKey, + registryPathForWorkspaceKey, +} from './socket-path.ts'; + +/** + * Metadata stored for each running daemon. + */ +export interface DaemonRegistryEntry { + workspaceKey: string; + workspaceRoot: string; + socketPath: string; + logPath?: string; + pid: number; + startedAt: string; + enabledWorkflows: string[]; + version: string; +} + +/** + * Write a daemon registry entry. + * Creates the daemon directory if it doesn't exist. + */ +export function writeDaemonRegistryEntry(entry: DaemonRegistryEntry): void { + const registryPath = registryPathForWorkspaceKey(entry.workspaceKey); + const dir = dirname(registryPath); + + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true, mode: 0o700 }); + } + + writeFileSync(registryPath, JSON.stringify(entry, null, 2), { + mode: 0o600, + }); +} + +/** + * Remove a daemon registry entry. + */ +export function removeDaemonRegistryEntry(workspaceKey: string): void { + const registryPath = registryPathForWorkspaceKey(workspaceKey); + + if (existsSync(registryPath)) { + unlinkSync(registryPath); + } +} + +/** + * Read a daemon registry entry by workspace key. + * Returns null if the entry doesn't exist. + */ +export function readDaemonRegistryEntry(workspaceKey: string): DaemonRegistryEntry | null { + const registryPath = registryPathForWorkspaceKey(workspaceKey); + + if (!existsSync(registryPath)) { + return null; + } + + try { + const content = readFileSync(registryPath, 'utf8'); + return JSON.parse(content) as DaemonRegistryEntry; + } catch { + return null; + } +} + +/** + * List all daemon registry entries. + * Enumerates the daemons directory and reads each daemon.json file. + */ +export function listDaemonRegistryEntries(): DaemonRegistryEntry[] { + const dir = daemonsDir(); + + if (!existsSync(dir)) { + return []; + } + + const entries: DaemonRegistryEntry[] = []; + + try { + const subdirs = readdirSync(dir, { withFileTypes: true }); + + for (const subdir of subdirs) { + if (!subdir.isDirectory()) continue; + + const workspaceKey = subdir.name; + const registryPath = join(daemonDirForWorkspaceKey(workspaceKey), 'daemon.json'); + + if (!existsSync(registryPath)) continue; + + try { + const content = readFileSync(registryPath, 'utf8'); + const entry = JSON.parse(content) as DaemonRegistryEntry; + entries.push(entry); + } catch { + // Skip malformed entries + } + } + } catch { + // Directory read error, return empty + } + + return entries; +} + +/** + * Remove all registry files for a workspace key (socket + registry). + */ +export function cleanupWorkspaceDaemonFiles(workspaceKey: string): void { + const daemonDir = daemonDirForWorkspaceKey(workspaceKey); + + if (!existsSync(daemonDir)) { + return; + } + + // Remove daemon.json + const registryPath = join(daemonDir, 'daemon.json'); + if (existsSync(registryPath)) { + unlinkSync(registryPath); + } + + // Remove daemon.sock + const socketPath = join(daemonDir, 'daemon.sock'); + if (existsSync(socketPath)) { + unlinkSync(socketPath); + } +} diff --git a/src/daemon/daemon-server.ts b/src/daemon/daemon-server.ts new file mode 100644 index 00000000..07f0dab9 --- /dev/null +++ b/src/daemon/daemon-server.ts @@ -0,0 +1,236 @@ +import net from 'node:net'; +import { writeFrame, createFrameReader } from './framing.ts'; +import type { ToolCatalog } from '../runtime/types.ts'; +import type { + DaemonRequest, + DaemonResponse, + ToolInvokeParams, + DaemonStatusResult, + ToolListItem, + XcodeIdeListParams, + XcodeIdeListResult, + XcodeIdeInvokeParams, + XcodeIdeInvokeResult, +} from './protocol.ts'; +import { DAEMON_PROTOCOL_VERSION } from './protocol.ts'; +import { DefaultToolInvoker } from '../runtime/tool-invoker.ts'; +import { log } from '../utils/logger.ts'; +import { XcodeIdeToolService } from '../integrations/xcode-tools-bridge/tool-service.ts'; +import { toLocalToolName } from '../integrations/xcode-tools-bridge/registry.ts'; + +export interface DaemonServerContext { + socketPath: string; + logPath?: string; + startedAt: string; + enabledWorkflows: string[]; + catalog: ToolCatalog; + workspaceRoot: string; + workspaceKey: string; + xcodeIdeWorkflowEnabled: boolean; + /** Callback to request graceful shutdown (used instead of direct process.exit) */ + requestShutdown: () => void; + /** Callback invoked whenever a daemon request starts processing. */ + onRequestStarted?: () => void; + /** Callback invoked after a daemon request has finished processing. */ + onRequestFinished?: () => void; +} + +/** + * Start the daemon server listening on a Unix domain socket. + */ +export function startDaemonServer(ctx: DaemonServerContext): net.Server { + const invoker = new DefaultToolInvoker(ctx.catalog); + const xcodeIdeService = new XcodeIdeToolService(); + xcodeIdeService.setWorkflowEnabled(ctx.xcodeIdeWorkflowEnabled); + + const server = net.createServer((socket) => { + log('info', '[Daemon] Client connected'); + + const onData = createFrameReader( + async (msg) => { + const req = msg as DaemonRequest; + const base: Pick = { + v: DAEMON_PROTOCOL_VERSION, + id: req?.id ?? 'unknown', + }; + + ctx.onRequestStarted?.(); + try { + if (!req || typeof req !== 'object') { + return writeFrame(socket, { + ...base, + error: { code: 'BAD_REQUEST', message: 'Invalid request format' }, + }); + } + + if (req.v !== DAEMON_PROTOCOL_VERSION) { + return writeFrame(socket, { + ...base, + error: { + code: 'BAD_REQUEST', + message: `Unsupported protocol version: ${req.v}`, + }, + }); + } + + switch (req.method) { + case 'daemon.status': { + const result: DaemonStatusResult = { + pid: process.pid, + socketPath: ctx.socketPath, + logPath: ctx.logPath, + startedAt: ctx.startedAt, + enabledWorkflows: ctx.enabledWorkflows, + toolCount: ctx.catalog.tools.length, + workspaceRoot: ctx.workspaceRoot, + workspaceKey: ctx.workspaceKey, + }; + return writeFrame(socket, { ...base, result }); + } + + case 'daemon.stop': { + log('info', '[Daemon] Stop requested'); + // Send response before initiating shutdown + writeFrame(socket, { ...base, result: { ok: true } }); + // Request shutdown through callback (allows proper cleanup) + setTimeout(() => ctx.requestShutdown(), 100); + return; + } + + case 'tool.list': { + const result: ToolListItem[] = ctx.catalog.tools.map((t) => ({ + name: t.cliName, + workflow: t.workflow, + description: t.description ?? '', + stateful: t.stateful, + })); + return writeFrame(socket, { ...base, result }); + } + + case 'tool.invoke': { + const params = req.params as ToolInvokeParams; + if (!params?.tool) { + return writeFrame(socket, { + ...base, + error: { code: 'BAD_REQUEST', message: 'Missing tool parameter' }, + }); + } + + log('info', `[Daemon] Invoking tool: ${params.tool}`); + const response = await invoker.invoke(params.tool, params.args ?? {}, { + runtime: 'daemon', + enabledWorkflows: ctx.enabledWorkflows, + }); + + return writeFrame(socket, { ...base, result: { response } }); + } + + case 'xcode-ide.list': { + if (!ctx.xcodeIdeWorkflowEnabled) { + return writeFrame(socket, { + ...base, + error: { + code: 'NOT_FOUND', + message: + 'xcode-ide workflow is not enabled for this daemon session (set XCODEBUILDMCP_ENABLED_WORKFLOWS to include xcode-ide)', + }, + }); + } + + const params = (req.params ?? {}) as XcodeIdeListParams; + const refresh = params.refresh === true; + if (params.prefetch === true && !refresh) { + void xcodeIdeService.listTools({ refresh: true }).catch((error) => { + const message = error instanceof Error ? error.message : String(error); + log('debug', `[Daemon] xcode-ide prefetch failed: ${message}`); + }); + } + const tools = await xcodeIdeService.listTools({ + refresh, + }); + const result: XcodeIdeListResult = { + tools: tools.map((tool) => ({ + remoteName: tool.name, + localName: toLocalToolName(tool.name), + description: tool.description ?? '', + inputSchema: tool.inputSchema, + annotations: tool.annotations, + })), + }; + return writeFrame(socket, { ...base, result }); + } + + case 'xcode-ide.invoke': { + if (!ctx.xcodeIdeWorkflowEnabled) { + return writeFrame(socket, { + ...base, + error: { + code: 'NOT_FOUND', + message: + 'xcode-ide workflow is not enabled for this daemon session (set XCODEBUILDMCP_ENABLED_WORKFLOWS to include xcode-ide)', + }, + }); + } + + const params = req.params as XcodeIdeInvokeParams; + if (!params?.remoteTool) { + return writeFrame(socket, { + ...base, + error: { + code: 'BAD_REQUEST', + message: 'Missing remoteTool parameter', + }, + }); + } + + const response = await xcodeIdeService.invokeTool( + params.remoteTool, + params.args ?? {}, + ); + const result: XcodeIdeInvokeResult = { response }; + return writeFrame(socket, { ...base, result }); + } + + default: + return writeFrame(socket, { + ...base, + error: { code: 'BAD_REQUEST', message: `Unknown method: ${req.method}` }, + }); + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + log('error', `[Daemon] Internal error handling request: ${message}`, { sentry: true }); + return writeFrame(socket, { + ...base, + error: { + code: 'INTERNAL', + message, + }, + }); + } finally { + ctx.onRequestFinished?.(); + } + }, + (err) => { + log('warning', `[Daemon] Frame parse error: ${err.message}`); + }, + ); + + socket.on('data', onData); + socket.on('close', () => { + log('info', '[Daemon] Client disconnected'); + }); + socket.on('error', (err) => { + log('warning', `[Daemon] Socket error: ${err.message}`); + }); + }); + + server.on('error', (err) => { + log('warning', `[Daemon] Server error: ${err.message}`); + }); + server.on('close', () => { + void xcodeIdeService.disconnect(); + }); + + return server; +} diff --git a/src/daemon/framing.ts b/src/daemon/framing.ts new file mode 100644 index 00000000..ded554fa --- /dev/null +++ b/src/daemon/framing.ts @@ -0,0 +1,58 @@ +import type net from 'node:net'; + +/** + * Write a length-prefixed JSON frame to a socket. + * Format: 4-byte big-endian length + JSON payload + */ +export function writeFrame(socket: net.Socket, obj: unknown): void { + const json = Buffer.from(JSON.stringify(obj), 'utf8'); + const header = Buffer.alloc(4); + header.writeUInt32BE(json.length, 0); + socket.write(Buffer.concat([header, json])); +} + +/** + * Create a frame reader that buffers incoming data and emits complete messages. + * Returns a function to be used as the 'data' event handler. + */ +export function createFrameReader( + onMessage: (msg: unknown) => void, + onError?: (err: Error) => void, +): (chunk: Buffer) => void { + let buffer = Buffer.alloc(0); + + return (chunk: Buffer) => { + buffer = Buffer.concat([buffer, chunk]); + + while (buffer.length >= 4) { + const len = buffer.readUInt32BE(0); + + // Sanity check: reject messages larger than 100MB + if (len > 100 * 1024 * 1024) { + const err = new Error(`Message too large: ${len} bytes`); + if (onError) { + onError(err); + } + buffer = Buffer.alloc(0); + return; + } + + if (buffer.length < 4 + len) { + // Not enough data yet, wait for more + return; + } + + const payload = buffer.subarray(4, 4 + len); + buffer = buffer.subarray(4 + len); + + try { + const msg = JSON.parse(payload.toString('utf8')) as unknown; + onMessage(msg); + } catch (err) { + if (onError) { + onError(err instanceof Error ? err : new Error(String(err))); + } + } + } + }; +} diff --git a/src/daemon/idle-shutdown.ts b/src/daemon/idle-shutdown.ts new file mode 100644 index 00000000..fee5cc05 --- /dev/null +++ b/src/daemon/idle-shutdown.ts @@ -0,0 +1,32 @@ +import { getDaemonActivitySnapshot, type DaemonActivitySnapshot } from './activity-registry.ts'; + +export const DAEMON_IDLE_TIMEOUT_ENV_KEY = 'XCODEBUILDMCP_DAEMON_IDLE_TIMEOUT_MS'; +export const DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 10 * 60 * 1000; +export const DEFAULT_DAEMON_IDLE_CHECK_INTERVAL_MS = 30 * 1000; + +export type DaemonRuntimeActivitySnapshot = DaemonActivitySnapshot; + +export function resolveDaemonIdleTimeoutMs( + env: NodeJS.ProcessEnv = process.env, + fallbackMs: number = DEFAULT_DAEMON_IDLE_TIMEOUT_MS, +): number { + const raw = env[DAEMON_IDLE_TIMEOUT_ENV_KEY]?.trim(); + if (!raw) { + return fallbackMs; + } + + const parsed = Number(raw); + if (!Number.isFinite(parsed) || parsed < 0) { + return fallbackMs; + } + + return Math.floor(parsed); +} + +export function getDaemonRuntimeActivitySnapshot(): DaemonRuntimeActivitySnapshot { + return getDaemonActivitySnapshot(); +} + +export function hasActiveRuntimeSessions(snapshot: DaemonRuntimeActivitySnapshot): boolean { + return snapshot.activeOperationCount > 0; +} diff --git a/src/daemon/protocol.ts b/src/daemon/protocol.ts new file mode 100644 index 00000000..61fd9802 --- /dev/null +++ b/src/daemon/protocol.ts @@ -0,0 +1,95 @@ +import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; +import type { ToolResponse } from '../types/common.ts'; + +export const DAEMON_PROTOCOL_VERSION = 1 as const; + +export type DaemonMethod = + | 'daemon.status' + | 'daemon.stop' + | 'tool.list' + | 'tool.invoke' + | 'xcode-ide.list' + | 'xcode-ide.invoke'; + +export interface DaemonRequest { + v: typeof DAEMON_PROTOCOL_VERSION; + id: string; + method: DaemonMethod; + params?: TParams; +} + +export type DaemonErrorCode = + | 'BAD_REQUEST' + | 'NOT_FOUND' + | 'AMBIGUOUS_TOOL' + | 'TOOL_FAILED' + | 'INTERNAL'; + +export interface DaemonError { + code: DaemonErrorCode; + message: string; + data?: unknown; +} + +export interface DaemonResponse { + v: typeof DAEMON_PROTOCOL_VERSION; + id: string; + result?: TResult; + error?: DaemonError; +} + +export interface ToolInvokeParams { + tool: string; + args: Record; +} + +export interface ToolInvokeResult { + response: ToolResponse; +} + +export interface DaemonStatusResult { + pid: number; + socketPath: string; + logPath?: string; + startedAt: string; + enabledWorkflows: string[]; + toolCount: number; + /** Workspace root this daemon is serving */ + workspaceRoot: string; + /** Short hash key identifying this workspace */ + workspaceKey: string; +} + +export interface ToolListItem { + name: string; + workflow: string; + description: string; + stateful: boolean; +} + +export interface XcodeIdeListParams { + refresh?: boolean; + /** Trigger a background refresh while still returning cached tools immediately. */ + prefetch?: boolean; +} + +export interface XcodeIdeToolListItem { + remoteName: string; + localName: string; + description: string; + inputSchema?: unknown; + annotations?: ToolAnnotations; +} + +export interface XcodeIdeListResult { + tools: XcodeIdeToolListItem[]; +} + +export interface XcodeIdeInvokeParams { + remoteTool: string; + args: Record; +} + +export interface XcodeIdeInvokeResult { + response: unknown; +} diff --git a/src/daemon/socket-path.ts b/src/daemon/socket-path.ts new file mode 100644 index 00000000..fd4fce23 --- /dev/null +++ b/src/daemon/socket-path.ts @@ -0,0 +1,147 @@ +import { createHash } from 'node:crypto'; +import { mkdirSync, existsSync, unlinkSync, realpathSync } from 'node:fs'; +import { homedir } from 'node:os'; +import { join, dirname } from 'node:path'; + +/** + * Base directory for all daemon-related files. + */ +export function daemonBaseDir(): string { + return join(homedir(), '.xcodebuildmcp'); +} + +/** + * Directory containing all workspace daemons. + */ +export function daemonsDir(): string { + return join(daemonBaseDir(), 'daemons'); +} + +/** + * Resolve the workspace root from the given context. + * + * If a project config was found (path to .xcodebuildmcp/config.yaml), use its parent directory. + * Otherwise, use realpath(cwd). + */ +export function resolveWorkspaceRoot(opts: { cwd: string; projectConfigPath?: string }): string { + if (opts.projectConfigPath) { + // Config is at .xcodebuildmcp/config.yaml, so parent of parent is workspace root + const configDir = dirname(opts.projectConfigPath); + return dirname(configDir); + } + try { + return realpathSync(opts.cwd); + } catch { + return opts.cwd; + } +} + +/** + * Generate a short, stable key from a workspace root path. + * Uses first 12 characters of SHA-256 hash. + */ +export function workspaceKeyForRoot(workspaceRoot: string): string { + const hash = createHash('sha256').update(workspaceRoot).digest('hex'); + return hash.slice(0, 12); +} + +/** + * Get the daemon directory for a specific workspace key. + */ +export function daemonDirForWorkspaceKey(key: string): string { + return join(daemonsDir(), key); +} + +/** + * Get the socket path for a specific workspace root. + */ +export function socketPathForWorkspaceRoot(workspaceRoot: string): string { + const key = workspaceKeyForRoot(workspaceRoot); + return join(daemonDirForWorkspaceKey(key), 'daemon.sock'); +} + +/** + * Get the registry file path for a specific workspace key. + */ +export function registryPathForWorkspaceKey(key: string): string { + return join(daemonDirForWorkspaceKey(key), 'daemon.json'); +} + +/** + * Get the log file path for a specific workspace key. + */ +export function logPathForWorkspaceKey(key: string): string { + return join(daemonDirForWorkspaceKey(key), 'daemon.log'); +} + +export interface GetSocketPathOptions { + cwd?: string; + projectConfigPath?: string; + env?: NodeJS.ProcessEnv; +} + +/** + * Get the socket path from environment or compute per-workspace. + * + * Resolution order: + * 1. If env.XCODEBUILDMCP_SOCKET is set, use it (explicit override) + * 2. If cwd is provided, compute workspace root and return per-workspace socket + * 3. Fall back to process.cwd() and compute workspace socket from that + */ +export function getSocketPath(opts?: GetSocketPathOptions): string { + const env = opts?.env ?? process.env; + + // Explicit override takes precedence + if (env.XCODEBUILDMCP_SOCKET) { + return env.XCODEBUILDMCP_SOCKET; + } + + // Compute workspace-derived socket path + const cwd = opts?.cwd ?? process.cwd(); + const workspaceRoot = resolveWorkspaceRoot({ + cwd, + projectConfigPath: opts?.projectConfigPath, + }); + + return socketPathForWorkspaceRoot(workspaceRoot); +} + +/** + * Get the workspace key for the current context. + */ +export function getWorkspaceKey(opts?: GetSocketPathOptions): string { + const cwd = opts?.cwd ?? process.cwd(); + const workspaceRoot = resolveWorkspaceRoot({ + cwd, + projectConfigPath: opts?.projectConfigPath, + }); + return workspaceKeyForRoot(workspaceRoot); +} + +/** + * Ensure the directory for the socket exists with proper permissions. + */ +export function ensureSocketDir(socketPath: string): void { + const dir = dirname(socketPath); + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true, mode: 0o700 }); + } +} + +/** + * Remove a stale socket file if it exists. + * Should only be called after confirming no daemon is running. + */ +export function removeStaleSocket(socketPath: string): void { + if (existsSync(socketPath)) { + unlinkSync(socketPath); + } +} + +/** + * Legacy: Get the default socket path for the daemon. + * @deprecated Use getSocketPath() with workspace context instead. + */ +export function defaultSocketPath(): string { + return getSocketPath(); +} diff --git a/src/doctor-cli.ts b/src/doctor-cli.ts index e3adafc3..73fc8e3c 100644 --- a/src/doctor-cli.ts +++ b/src/doctor-cli.ts @@ -24,7 +24,7 @@ async function runDoctor(): Promise { // Output the doctor information if (result.content && result.content.length > 0) { const textContent = result.content.find((item) => item.type === 'text'); - if (textContent && textContent.type === 'text') { + if (textContent?.type === 'text') { // eslint-disable-next-line no-console console.log(textContent.text); } else { diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 4ce746e8..00000000 --- a/src/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env node - -/** - * XcodeBuildMCP - Main entry point - * - * This file serves as the entry point for the XcodeBuildMCP server, importing and registering - * all tool modules with the MCP server. It follows the platform-specific approach for Xcode tools. - * - * Responsibilities: - * - Creating and starting the MCP server - * - Registering all platform-specific tool modules - * - Configuring server options and logging - * - Handling server lifecycle events - */ - -// Import server components -import { createServer, startServer } from './server/server.ts'; - -// Import utilities -import { log } from './utils/logger.ts'; -import { initSentry } from './utils/sentry.ts'; - -// Import version -import { version } from './version.ts'; - -// Import xcodemake utilities -import { isXcodemakeEnabled, isXcodemakeAvailable } from './utils/xcodemake.ts'; - -// Import process for stdout configuration -import process from 'node:process'; - -import { bootstrapServer } from './server/bootstrap.ts'; - -/** - * Main function to start the server - */ -async function main(): Promise { - try { - initSentry(); - - // Check if xcodemake is enabled and available - if (isXcodemakeEnabled()) { - log('info', 'xcodemake is enabled, checking if available...'); - const available = await isXcodemakeAvailable(); - if (available) { - log('info', 'xcodemake is available and will be used for builds'); - } else { - log( - 'warn', - 'xcodemake is enabled but could not be made available, falling back to xcodebuild', - ); - } - } else { - log('debug', 'xcodemake is disabled, using standard xcodebuild'); - } - - // Create the server - const server = createServer(); - - await bootstrapServer(server); - - // Start the server - await startServer(server); - - // Clean up on exit - process.on('SIGTERM', async () => { - await server.close(); - process.exit(0); - }); - - process.on('SIGINT', async () => { - await server.close(); - process.exit(0); - }); - - // Log successful startup - log('info', `XcodeBuildMCP server (version ${version}) started successfully`); - } catch (error) { - console.error('Fatal error in main():', error); - process.exit(1); - } -} - -// Start the server -main().catch((error) => { - console.error('Unhandled exception:', error); - // Give Sentry a moment to send the error before exiting - setTimeout(() => process.exit(1), 1000); -}); diff --git a/src/integrations/xcode-tools-bridge/__tests__/fixtures/fake-xcode-tools-server.mjs b/src/integrations/xcode-tools-bridge/__tests__/fixtures/fake-xcode-tools-server.mjs new file mode 100644 index 00000000..6718f409 --- /dev/null +++ b/src/integrations/xcode-tools-bridge/__tests__/fixtures/fake-xcode-tools-server.mjs @@ -0,0 +1,105 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import * as z from 'zod'; + +const server = new McpServer( + { name: 'fake-xcode-tools', version: '0.0.0' }, + { + capabilities: { + tools: { listChanged: true }, + }, + }, +); + +let alphaRegistered; +let betaRegistered; +let gammaRegistered; + +function registerInitialTools() { + alphaRegistered = server.registerTool( + 'Alpha', + { + description: 'Alpha tool', + inputSchema: z + .object({ + value: z.string(), + }) + .passthrough(), + annotations: { readOnlyHint: true, title: 'Alpha' }, + }, + async (args) => ({ + content: [{ type: 'text', text: `Alpha:${args.value}` }], + isError: false, + }), + ); + + betaRegistered = server.registerTool( + 'Beta', + { + description: 'Beta tool', + inputSchema: z + .object({ + n: z.number().int(), + }) + .passthrough(), + }, + async (args) => ({ + content: [{ type: 'text', text: `Beta:${args.n}` }], + isError: false, + }), + ); + + server.registerTool( + 'TriggerChange', + { + description: 'Mutate tool catalog and emit list_changed', + inputSchema: z.object({}).passthrough(), + }, + async () => { + applyCatalogChange(); + return { content: [{ type: 'text', text: 'changed' }], isError: false }; + }, + ); +} + +function applyCatalogChange() { + betaRegistered?.remove(); + betaRegistered = undefined; + + alphaRegistered?.remove(); + alphaRegistered = server.registerTool( + 'Alpha', + { + description: 'Alpha tool (changed schema)', + inputSchema: z + .object({ + value: z.string(), + extra: z.string().optional(), + }) + .passthrough(), + }, + async (args) => ({ + content: [{ type: 'text', text: `Alpha2:${args.value}:${args.extra ?? ''}` }], + isError: false, + }), + ); + + gammaRegistered?.remove(); + gammaRegistered = server.registerTool( + 'Gamma', + { + description: 'Gamma tool', + inputSchema: z.object({ ok: z.boolean() }).passthrough(), + }, + async (args) => ({ + content: [{ type: 'text', text: `Gamma:${args.ok}` }], + isError: false, + }), + ); + + server.sendToolListChanged(); +} + +registerInitialTools(); + +await server.connect(new StdioServerTransport()); diff --git a/src/integrations/xcode-tools-bridge/__tests__/jsonschema-to-zod.test.ts b/src/integrations/xcode-tools-bridge/__tests__/jsonschema-to-zod.test.ts new file mode 100644 index 00000000..5e1c5cf9 --- /dev/null +++ b/src/integrations/xcode-tools-bridge/__tests__/jsonschema-to-zod.test.ts @@ -0,0 +1,72 @@ +import { describe, it, expect } from 'vitest'; +import { jsonSchemaToZod } from '../jsonschema-to-zod.ts'; + +describe('jsonSchemaToZod', () => { + it('converts object properties + required correctly', () => { + const schema = { + type: 'object', + properties: { + a: { type: 'string' }, + b: { type: 'integer' }, + }, + required: ['a'], + }; + + const zod = jsonSchemaToZod(schema); + + expect(zod.safeParse({ a: 'x' }).success).toBe(true); + expect(zod.safeParse({}).success).toBe(false); + expect(zod.safeParse({ a: 'x', b: 1 }).success).toBe(true); + expect(zod.safeParse({ a: 'x', b: 1.5 }).success).toBe(false); + }); + + it('supports enums (mixed types)', () => { + const schema = { + enum: ['a', 1, true], + description: 'mixed enum', + }; + + const zod = jsonSchemaToZod(schema); + + expect(zod.safeParse('a').success).toBe(true); + expect(zod.safeParse(1).success).toBe(true); + expect(zod.safeParse(true).success).toBe(true); + expect(zod.safeParse('b').success).toBe(false); + }); + + it('supports arrays with items', () => { + const schema = { type: 'array', items: { type: 'number' } }; + const zod = jsonSchemaToZod(schema); + + expect(zod.safeParse([1, 2, 3]).success).toBe(true); + expect(zod.safeParse([1, 'x']).success).toBe(false); + }); + + it('is permissive for unknown constructs', () => { + const schema: unknown = { + type: 'object', + properties: { + x: { oneOf: [{ type: 'string' }, { type: 'number' }] }, + }, + required: ['x'], + }; + + const zod = jsonSchemaToZod(schema); + expect(zod.safeParse({ x: 'hello' }).success).toBe(true); + expect(zod.safeParse({ x: 123 }).success).toBe(true); + }); + + it('does not reject unknown fields on objects (passthrough)', () => { + const schema = { + type: 'object', + properties: { + a: { type: 'string' }, + }, + required: ['a'], + }; + + const zod = jsonSchemaToZod(schema); + const parsed = zod.parse({ a: 'x', extra: 1 }) as Record; + expect(parsed.extra).toBe(1); + }); +}); diff --git a/src/integrations/xcode-tools-bridge/__tests__/registry.integration.test.ts b/src/integrations/xcode-tools-bridge/__tests__/registry.integration.test.ts new file mode 100644 index 00000000..27d9c1cb --- /dev/null +++ b/src/integrations/xcode-tools-bridge/__tests__/registry.integration.test.ts @@ -0,0 +1,117 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { XcodeToolsBridgeClient } from '../client.ts'; +import { XcodeToolsProxyRegistry } from '../registry.ts'; + +function fixturePath(rel: string): string { + const here = path.dirname(fileURLToPath(import.meta.url)); + return path.join(here, 'fixtures', rel); +} + +async function waitFor(fn: () => boolean, timeoutMs = 2000): Promise { + const start = Date.now(); + while (true) { + if (fn()) return; + if (Date.now() - start > timeoutMs) { + throw new Error('Timed out waiting for condition'); + } + await new Promise((r) => setTimeout(r, 25)); + } +} + +describe('XcodeToolsProxyRegistry (stdio integration)', () => { + let localServer: McpServer; + let localClient: Client; + let bridgeClient: XcodeToolsBridgeClient; + let registry: XcodeToolsProxyRegistry; + + const doSync = async (): Promise => { + const tools = await bridgeClient.listTools(); + registry.sync(tools, async (remoteName, args) => bridgeClient.callTool(remoteName, args)); + if (localServer.isConnected()) { + localServer.sendToolListChanged(); + } + }; + + beforeAll(async () => { + localServer = new McpServer( + { name: 'local-test-server', version: '0.0.0' }, + { capabilities: { tools: { listChanged: true } } }, + ); + + registry = new XcodeToolsProxyRegistry(localServer); + + const fakeServerScript = fixturePath('fake-xcode-tools-server.mjs'); + const env: Record = {}; + for (const [key, value] of Object.entries(process.env)) { + if (typeof value === 'string') { + env[key] = value; + } + } + bridgeClient = new XcodeToolsBridgeClient({ + serverParams: { + command: process.execPath, + args: [fakeServerScript], + stderr: 'pipe', + env, + }, + onToolsListChanged: () => { + void doSync(); + }, + }); + + await bridgeClient.connectOnce(); + await doSync(); + + // Connect after initial tool registration so MCP server can register capabilities before connect. + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + await localServer.connect(serverTransport); + + localClient = new Client({ name: 'local-test-client', version: '0.0.0' }); + await localClient.connect(clientTransport); + }); + + afterAll(async () => { + await bridgeClient.disconnect(); + await localClient.close(); + await localServer.close(); + }); + + it('registers proxied tools and forwards calls', async () => { + const tools = await localClient.listTools(); + const names = tools.tools.map((t) => t.name); + expect(names).toContain('xcode_tools_Alpha'); + expect(names).toContain('xcode_tools_Beta'); + expect(names).toContain('xcode_tools_TriggerChange'); + + const res = (await localClient.callTool({ + name: 'xcode_tools_Alpha', + arguments: { value: 'hi' }, + })) as CallToolResult; + expect(res.isError).not.toBe(true); + expect(res.content[0]).toMatchObject({ type: 'text', text: 'Alpha:hi' }); + }); + + it('updates registered tools on remote list change', async () => { + await localClient.callTool({ name: 'xcode_tools_TriggerChange', arguments: {} }); + + await waitFor(() => registry.getRegisteredToolNames().includes('xcode_tools_Gamma')); + + const tools = await localClient.listTools(); + const names = tools.tools.map((t) => t.name); + expect(names).toContain('xcode_tools_Alpha'); + expect(names).not.toContain('xcode_tools_Beta'); + expect(names).toContain('xcode_tools_Gamma'); + + const res = (await localClient.callTool({ + name: 'xcode_tools_Alpha', + arguments: { value: 'hi', extra: 'e' }, + })) as CallToolResult; + expect(res.content[0]).toMatchObject({ type: 'text', text: 'Alpha2:hi:e' }); + }); +}); diff --git a/src/integrations/xcode-tools-bridge/client.ts b/src/integrations/xcode-tools-bridge/client.ts new file mode 100644 index 00000000..5ee60165 --- /dev/null +++ b/src/integrations/xcode-tools-bridge/client.ts @@ -0,0 +1,206 @@ +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { + StdioClientTransport, + type StdioServerParameters, +} from '@modelcontextprotocol/sdk/client/stdio.js'; +import { CompatibilityCallToolResultSchema } from '@modelcontextprotocol/sdk/types.js'; +import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js'; +import process from 'node:process'; + +export interface XcodeToolsBridgeClientStatus { + connected: boolean; + bridgePid: number | null; + lastError: string | null; +} + +export interface XcodeToolsBridgeClientOptions { + serverParams?: StdioServerParameters; + connectTimeoutMs?: number; + listToolsTimeoutMs?: number; + callToolTimeoutMs?: number; + onToolsListChanged?: () => void; + onBridgeClosed?: () => void; +} + +export class XcodeToolsBridgeClient { + private readonly options: Required< + Pick< + XcodeToolsBridgeClientOptions, + 'connectTimeoutMs' | 'listToolsTimeoutMs' | 'callToolTimeoutMs' + > + > & + Omit< + XcodeToolsBridgeClientOptions, + 'connectTimeoutMs' | 'listToolsTimeoutMs' | 'callToolTimeoutMs' + >; + + private transport: StdioClientTransport | null = null; + private client: Client | null = null; + private connectPromise: Promise | null = null; + private lastError: string | null = null; + + constructor(options: XcodeToolsBridgeClientOptions = {}) { + this.options = { + connectTimeoutMs: options.connectTimeoutMs ?? 15_000, + listToolsTimeoutMs: options.listToolsTimeoutMs ?? 15_000, + callToolTimeoutMs: options.callToolTimeoutMs ?? 60_000, + ...options, + }; + } + + getStatus(): XcodeToolsBridgeClientStatus { + return { + connected: this.client !== null, + bridgePid: this.transport?.pid ?? null, + lastError: this.lastError, + }; + } + + async connectOnce(): Promise { + if (this.client) return; + if (this.connectPromise) return this.connectPromise; + + this.connectPromise = (async (): Promise => { + try { + const serverParams = + this.options.serverParams ?? + ({ + command: 'xcrun', + args: ['mcpbridge'], + stderr: 'pipe', + env: mapXcodeEnvForMcpBridge(process.env), + } satisfies StdioServerParameters); + + const transport = new StdioClientTransport(serverParams); + transport.onclose = (): void => { + this.client = null; + this.transport = null; + this.connectPromise = null; + this.options.onBridgeClosed?.(); + }; + + const client = new Client( + { name: 'xcodebuildmcp-xcode-tools-bridge', version: '0.0.0' }, + { + listChanged: { + tools: { + autoRefresh: false, + debounceMs: 250, + onChanged: (): void => { + this.options.onToolsListChanged?.(); + }, + }, + }, + }, + ); + + await client.connect(transport, { timeout: this.options.connectTimeoutMs }); + + this.transport = transport; + this.client = client; + this.lastError = null; + } catch (error) { + this.lastError = error instanceof Error ? error.message : String(error); + await this.disconnect(); + throw error; + } finally { + this.connectPromise = null; + } + })(); + + return this.connectPromise; + } + + async disconnect(): Promise { + const client = this.client; + const transport = this.transport; + this.client = null; + this.transport = null; + this.connectPromise = null; + + try { + await client?.close(); + } finally { + try { + await transport?.close?.(); + } catch { + // ignore + } + } + } + + async listTools(): Promise { + if (!this.client) { + throw new Error('Bridge client is not connected'); + } + const result = await this.client.listTools(undefined, { + timeout: this.options.listToolsTimeoutMs, + }); + return result.tools; + } + + async callTool( + name: string, + args: Record, + opts: { timeoutMs?: number } = {}, + ): Promise { + if (!this.client) { + throw new Error('Bridge client is not connected'); + } + const result: unknown = await this.client.request( + { method: 'tools/call', params: { name, arguments: args } }, + CompatibilityCallToolResultSchema, + { + timeout: opts.timeoutMs ?? this.options.callToolTimeoutMs, + resetTimeoutOnProgress: true, + }, + ); + + if (isCallToolResult(result)) { + return result; + } + if (result && typeof result === 'object' && 'toolResult' in result) { + const toolResult = (result as { toolResult: unknown }).toolResult; + if (isCallToolResult(toolResult)) { + return toolResult; + } + } + + // If this is a task result, we don't support it today. + if (result && typeof result === 'object' && 'task' in result) { + throw new Error( + `Tool "${name}" returned a task result; task-based tools are not supported by the bridge proxy`, + ); + } + + throw new Error(`Tool "${name}" returned an unexpected result shape`); + } +} + +function isCallToolResult(result: unknown): result is CallToolResult { + if (!result || typeof result !== 'object') return false; + const record = result as Record; + return Array.isArray(record.content); +} + +function mapXcodeEnvForMcpBridge(env: NodeJS.ProcessEnv): Record { + const mapped: Record = {}; + + for (const [key, value] of Object.entries(env)) { + if (typeof value === 'string') { + mapped[key] = value; + } + } + + if (typeof env.XCODEBUILDMCP_XCODE_PID === 'string' && mapped.MCP_XCODE_PID === undefined) { + mapped.MCP_XCODE_PID = env.XCODEBUILDMCP_XCODE_PID; + } + if ( + typeof env.XCODEBUILDMCP_XCODE_SESSION_ID === 'string' && + mapped.MCP_XCODE_SESSION_ID === undefined + ) { + mapped.MCP_XCODE_SESSION_ID = env.XCODEBUILDMCP_XCODE_SESSION_ID; + } + + return mapped; +} diff --git a/src/integrations/xcode-tools-bridge/core.ts b/src/integrations/xcode-tools-bridge/core.ts new file mode 100644 index 00000000..bce54ac0 --- /dev/null +++ b/src/integrations/xcode-tools-bridge/core.ts @@ -0,0 +1,114 @@ +import { execFile } from 'node:child_process'; +import process from 'node:process'; +import { promisify } from 'node:util'; +import type { Tool } from '@modelcontextprotocol/sdk/types.js'; +import type { XcodeToolsBridgeClientStatus } from './client.ts'; + +const execFileAsync = promisify(execFile); + +export type XcodeToolsBridgeStatus = { + workflowEnabled: boolean; + bridgeAvailable: boolean; + bridgePath: string | null; + xcodeRunning: boolean | null; + connected: boolean; + bridgePid: number | null; + proxiedToolCount: number; + lastError: string | null; + xcodePid: string | null; + xcodeSessionId: string | null; +}; + +export function serializeBridgeTool(tool: Tool): Record { + return { + name: tool.name, + title: tool.title, + description: tool.description, + inputSchema: tool.inputSchema, + outputSchema: tool.outputSchema, + annotations: tool.annotations, + }; +} + +export interface BuildXcodeToolsBridgeStatusArgs { + workflowEnabled: boolean; + proxiedToolCount: number; + lastError: string | null; + clientStatus: XcodeToolsBridgeClientStatus; +} + +export async function buildXcodeToolsBridgeStatus( + args: BuildXcodeToolsBridgeStatusArgs, +): Promise { + const bridge = await getMcpBridgeAvailability(); + const xcodeRunning = await isXcodeRunning(); + + return { + workflowEnabled: args.workflowEnabled, + bridgeAvailable: bridge.available, + bridgePath: bridge.path, + xcodeRunning, + connected: args.clientStatus.connected, + bridgePid: args.clientStatus.bridgePid, + proxiedToolCount: args.proxiedToolCount, + lastError: args.lastError ?? args.clientStatus.lastError, + xcodePid: process.env.XCODEBUILDMCP_XCODE_PID ?? process.env.MCP_XCODE_PID ?? null, + xcodeSessionId: + process.env.XCODEBUILDMCP_XCODE_SESSION_ID ?? process.env.MCP_XCODE_SESSION_ID ?? null, + }; +} + +export async function getMcpBridgeAvailability(): Promise<{ + available: boolean; + path: string | null; +}> { + try { + const res = await execFileAsync('xcrun', ['--find', 'mcpbridge'], { timeout: 2000 }); + const out = (res.stdout ?? '').toString().trim(); + return out ? { available: true, path: out } : { available: false, path: null }; + } catch { + return { available: false, path: null }; + } +} + +export async function isXcodeRunning(): Promise { + try { + const res = await execFileAsync('pgrep', ['-x', 'Xcode'], { timeout: 1000 }); + const out = (res.stdout ?? '').toString().trim(); + return out.length > 0; + } catch { + return null; + } +} + +export function classifyBridgeError( + error: unknown, + operation: 'list' | 'call', + opts?: { connected?: boolean }, +): string { + const message = (error instanceof Error ? error.message : String(error)).toLowerCase(); + + if (message.includes('mcpbridge not available')) { + return 'MCPBRIDGE_NOT_FOUND'; + } + if (message.includes('workflow is not enabled')) { + return 'XCODE_MCP_UNAVAILABLE'; + } + if (message.includes('timed out') || message.includes('timeout')) { + if (opts?.connected === false) { + return 'BRIDGE_CONNECT_TIMEOUT'; + } + return operation === 'list' ? 'BRIDGE_LIST_TIMEOUT' : 'BRIDGE_CALL_TIMEOUT'; + } + if (message.includes('permission') || message.includes('not allowed')) { + return 'XCODE_APPROVAL_REQUIRED'; + } + if ( + message.includes('connection closed') || + message.includes('closed') || + message.includes('disconnected') + ) { + return 'XCODE_SESSION_NOT_READY'; + } + return 'XCODE_MCP_UNAVAILABLE'; +} diff --git a/src/integrations/xcode-tools-bridge/index.ts b/src/integrations/xcode-tools-bridge/index.ts new file mode 100644 index 00000000..a0fe12a4 --- /dev/null +++ b/src/integrations/xcode-tools-bridge/index.ts @@ -0,0 +1,47 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { ToolResponse } from '../../types/common.ts'; +import { XcodeToolsBridgeManager } from './manager.ts'; +import { StandaloneXcodeToolsBridge } from './standalone.ts'; + +let manager: XcodeToolsBridgeManager | null = null; +let standalone: StandaloneXcodeToolsBridge | null = null; + +export interface XcodeToolsBridgeToolHandler { + statusTool(): Promise; + syncTool(): Promise; + disconnectTool(): Promise; + listToolsTool(params: { refresh?: boolean }): Promise; + callToolTool(params: { + remoteTool: string; + arguments: Record; + timeoutMs?: number; + }): Promise; +} + +export function getXcodeToolsBridgeManager(server?: McpServer): XcodeToolsBridgeManager | null { + if (manager) return manager; + if (!server) return null; + manager = new XcodeToolsBridgeManager(server); + return manager; +} + +export function peekXcodeToolsBridgeManager(): XcodeToolsBridgeManager | null { + return manager; +} + +export function getXcodeToolsBridgeToolHandler( + server?: McpServer, +): XcodeToolsBridgeToolHandler | null { + if (server) { + return getXcodeToolsBridgeManager(server); + } + standalone ??= new StandaloneXcodeToolsBridge(); + return standalone; +} + +export async function shutdownXcodeToolsBridge(): Promise { + await manager?.shutdown(); + await standalone?.shutdown(); + manager = null; + standalone = null; +} diff --git a/src/integrations/xcode-tools-bridge/jsonschema-to-zod.ts b/src/integrations/xcode-tools-bridge/jsonschema-to-zod.ts new file mode 100644 index 00000000..b1bb9744 --- /dev/null +++ b/src/integrations/xcode-tools-bridge/jsonschema-to-zod.ts @@ -0,0 +1,104 @@ +import * as z from 'zod'; + +type JsonSchemaEnumValue = string | number | boolean | null; + +type JsonSchema = { + type?: string | string[]; + description?: string; + enum?: unknown[]; + items?: JsonSchema; + properties?: Record; + required?: string[]; +}; + +function applyDescription(schema: T, description?: string): T { + if (!description) return schema; + return schema.describe(description) as T; +} + +function isObjectSchema(schema: JsonSchema): boolean { + const types = + schema.type === undefined ? [] : Array.isArray(schema.type) ? schema.type : [schema.type]; + return types.includes('object') || schema.properties !== undefined; +} + +function isEnumValue(value: unknown): value is JsonSchemaEnumValue { + return ( + value === null || + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ); +} + +export function jsonSchemaToZod(schema: JsonSchema | unknown): z.ZodTypeAny { + if (!schema || typeof schema !== 'object') { + return z.any(); + } + + const s = schema as JsonSchema; + + if (Array.isArray(s.enum)) { + const enumValues = s.enum.filter(isEnumValue); + if (enumValues.length === 0) { + return applyDescription(z.any(), s.description); + } + const allStrings = enumValues.every((v) => typeof v === 'string'); + if (allStrings) { + const stringValues = enumValues as string[]; + if (stringValues.length === 1) { + return applyDescription(z.literal(stringValues[0]), s.description); + } + return applyDescription(z.enum(stringValues as [string, ...string[]]), s.description); + } + + // z.enum only supports string unions; use z.literal union for mixed enums. + const literals = enumValues.map((v) => z.literal(v)) as z.ZodLiteral[]; + if (literals.length === 1) { + return applyDescription(literals[0], s.description); + } + return applyDescription( + z.union( + literals as [ + z.ZodLiteral, + z.ZodLiteral, + ...z.ZodLiteral[], + ], + ), + s.description, + ); + } + + const types = s.type === undefined ? [] : Array.isArray(s.type) ? s.type : [s.type]; + const primaryType = types[0]; + + switch (primaryType) { + case 'string': + return applyDescription(z.string(), s.description); + case 'integer': + return applyDescription(z.number().int(), s.description); + case 'number': + return applyDescription(z.number(), s.description); + case 'boolean': + return applyDescription(z.boolean(), s.description); + case 'array': { + const itemSchema = jsonSchemaToZod(s.items ?? {}); + return applyDescription(z.array(itemSchema), s.description); + } + case 'object': + default: { + if (!isObjectSchema(s)) { + return applyDescription(z.any(), s.description); + } + const required = new Set(s.required ?? []); + const props = s.properties ?? {}; + const shape: Record = {}; + for (const [key, value] of Object.entries(props)) { + const propSchema = jsonSchemaToZod(value); + shape[key] = required.has(key) ? propSchema : propSchema.optional(); + } + // Use passthrough to avoid breaking when Apple adds new fields. + return applyDescription(z.object(shape).passthrough(), s.description); + } + } +} diff --git a/src/integrations/xcode-tools-bridge/manager.ts b/src/integrations/xcode-tools-bridge/manager.ts new file mode 100644 index 00000000..9df3a74f --- /dev/null +++ b/src/integrations/xcode-tools-bridge/manager.ts @@ -0,0 +1,207 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { log } from '../../utils/logger.ts'; +import { + createErrorResponse, + createTextResponse, + type ToolResponse, +} from '../../utils/responses/index.ts'; +import { XcodeToolsProxyRegistry, type ProxySyncResult } from './registry.ts'; +import { + buildXcodeToolsBridgeStatus, + classifyBridgeError, + getMcpBridgeAvailability, + serializeBridgeTool, + type XcodeToolsBridgeStatus, +} from './core.ts'; +import { XcodeIdeToolService } from './tool-service.ts'; + +export class XcodeToolsBridgeManager { + private readonly server: McpServer; + private readonly registry: XcodeToolsProxyRegistry; + private readonly service: XcodeIdeToolService; + + private workflowEnabled = false; + private lastError: string | null = null; + private syncInFlight: Promise | null = null; + + constructor(server: McpServer) { + this.server = server; + this.registry = new XcodeToolsProxyRegistry(server); + this.service = new XcodeIdeToolService({ + onToolCatalogInvalidated: (): void => { + void this.syncTools({ reason: 'listChanged' }); + }, + }); + } + + setWorkflowEnabled(enabled: boolean): void { + this.workflowEnabled = enabled; + this.service.setWorkflowEnabled(enabled); + } + + async shutdown(): Promise { + this.registry.clear(); + await this.service.disconnect(); + } + + async getStatus(): Promise { + return buildXcodeToolsBridgeStatus({ + workflowEnabled: this.workflowEnabled, + proxiedToolCount: this.registry.getRegisteredCount(), + lastError: this.lastError ?? this.service.getLastError(), + clientStatus: this.service.getClientStatus(), + }); + } + + async syncTools(opts: { + reason: 'startup' | 'manual' | 'listChanged'; + }): Promise { + if (!this.workflowEnabled) { + throw new Error('xcode-ide workflow is not enabled'); + } + + if (this.syncInFlight) return this.syncInFlight; + + this.syncInFlight = (async (): Promise => { + const bridge = await getMcpBridgeAvailability(); + if (!bridge.available) { + this.lastError = 'mcpbridge not available (xcrun --find mcpbridge failed)'; + const existingCount = this.registry.getRegisteredCount(); + this.registry.clear(); + this.server.sendToolListChanged(); + return { added: 0, updated: 0, removed: existingCount, total: 0 }; + } + + try { + const remoteTools = await this.service.listTools({ refresh: true }); + + const sync = this.registry.sync(remoteTools, async (remoteName, args) => { + return this.service.invokeTool(remoteName, args); + }); + + if (opts.reason !== 'listChanged') { + log( + 'info', + `[xcode-ide] Synced proxied tools (added=${sync.added}, updated=${sync.updated}, removed=${sync.removed}, total=${sync.total})`, + ); + } + + this.lastError = null; + // Notify clients that our own tool list changed. + this.server.sendToolListChanged(); + + return sync; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + this.lastError = message; + log('warn', `[xcode-ide] Tool sync failed: ${message}`); + this.registry.clear(); + this.server.sendToolListChanged(); + return { added: 0, updated: 0, removed: 0, total: 0 }; + } finally { + this.syncInFlight = null; + } + })(); + + return this.syncInFlight; + } + + async disconnect(): Promise { + this.registry.clear(); + this.server.sendToolListChanged(); + await this.service.disconnect(); + } + + async statusTool(): Promise { + const status = await this.getStatus(); + return createTextResponse(JSON.stringify(status, null, 2)); + } + + async syncTool(): Promise { + try { + const sync = await this.syncTools({ reason: 'manual' }); + const status = await this.getStatus(); + return createTextResponse( + JSON.stringify( + { + sync, + status, + }, + null, + 2, + ), + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Bridge sync failed', message); + } + } + + async disconnectTool(): Promise { + try { + await this.disconnect(); + const status = await this.getStatus(); + return createTextResponse(JSON.stringify(status, null, 2)); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Bridge disconnect failed', message); + } + } + + async listToolsTool(params: { refresh?: boolean }): Promise { + if (!this.workflowEnabled) { + return this.createBridgeFailureResponse( + 'XCODE_MCP_UNAVAILABLE', + 'xcode-ide workflow is not enabled', + ); + } + + try { + const tools = await this.service.listTools({ refresh: params.refresh !== false }); + const payload = { + toolCount: tools.length, + tools: tools.map(serializeBridgeTool), + }; + return createTextResponse(JSON.stringify(payload, null, 2)); + } catch (error) { + return this.createBridgeFailureResponse( + classifyBridgeError(error, 'list', { + connected: this.service.getClientStatus().connected, + }), + error, + ); + } + } + + async callToolTool(params: { + remoteTool: string; + arguments: Record; + timeoutMs?: number; + }): Promise { + if (!this.workflowEnabled) { + return this.createBridgeFailureResponse( + 'XCODE_MCP_UNAVAILABLE', + 'xcode-ide workflow is not enabled', + ); + } + + try { + const response = await this.service.invokeTool(params.remoteTool, params.arguments, { + timeoutMs: params.timeoutMs, + }); + return response as ToolResponse; + } catch (error) { + return this.createBridgeFailureResponse( + classifyBridgeError(error, 'call', { + connected: this.service.getClientStatus().connected, + }), + error, + ); + } + } + + private createBridgeFailureResponse(code: string, error: unknown): ToolResponse { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(code, message); + } +} diff --git a/src/integrations/xcode-tools-bridge/registry.ts b/src/integrations/xcode-tools-bridge/registry.ts new file mode 100644 index 00000000..40bbce1b --- /dev/null +++ b/src/integrations/xcode-tools-bridge/registry.ts @@ -0,0 +1,177 @@ +import type { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { CallToolResult, Tool, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; +import * as z from 'zod'; +import { jsonSchemaToZod } from './jsonschema-to-zod.ts'; + +export type CallRemoteTool = ( + remoteToolName: string, + args: Record, +) => Promise; + +type Entry = { + remoteName: string; + localName: string; + fingerprint: string; + registered: RegisteredTool; +}; + +export type ProxySyncResult = { + added: number; + updated: number; + removed: number; + total: number; +}; + +export class XcodeToolsProxyRegistry { + private readonly server: McpServer; + private readonly tools: Map = new Map(); + + constructor(server: McpServer) { + this.server = server; + } + + getRegisteredToolNames(): string[] { + return [...this.tools.values()].map((t) => t.localName).sort(); + } + + getRegisteredCount(): number { + return this.tools.size; + } + + clear(): void { + for (const entry of this.tools.values()) { + entry.registered.remove(); + } + this.tools.clear(); + } + + sync(remoteTools: Tool[], callRemoteTool: CallRemoteTool): ProxySyncResult { + const desiredRemoteNames = new Set(remoteTools.map((t) => t.name)); + let added = 0; + let updated = 0; + let removed = 0; + + for (const remoteTool of remoteTools) { + const remoteName = remoteTool.name; + const localName = toLocalToolName(remoteName); + const fingerprint = stableFingerprint(remoteTool); + const existing = this.tools.get(remoteName); + + if (!existing) { + this.tools.set(remoteName, { + remoteName, + localName, + fingerprint, + registered: this.registerProxyTool(remoteTool, localName, callRemoteTool), + }); + added += 1; + continue; + } + + if (existing.fingerprint !== fingerprint) { + existing.registered.remove(); + this.tools.set(remoteName, { + remoteName, + localName, + fingerprint, + registered: this.registerProxyTool(remoteTool, localName, callRemoteTool), + }); + updated += 1; + } + } + + for (const [remoteName, entry] of this.tools.entries()) { + if (!desiredRemoteNames.has(remoteName)) { + entry.registered.remove(); + this.tools.delete(remoteName); + removed += 1; + } + } + + return { added, updated, removed, total: this.tools.size }; + } + + private registerProxyTool( + tool: Tool, + localName: string, + callRemoteTool: CallRemoteTool, + ): RegisteredTool { + const inputSchema = buildBestEffortInputSchema(tool); + const annotations = buildBestEffortAnnotations(tool, localName); + + return this.server.registerTool( + localName, + { + description: tool.description ?? '', + inputSchema, + annotations, + _meta: { + xcodeToolsBridge: { + remoteTool: tool.name, + source: 'xcrun mcpbridge', + }, + }, + }, + async (args: unknown) => { + const params = (args ?? {}) as Record; + return callRemoteTool(tool.name, params); + }, + ); + } +} + +export function toLocalToolName(remoteToolName: string): string { + return `xcode_tools_${remoteToolName}`; +} + +function stableFingerprint(tool: Tool): string { + return JSON.stringify({ + name: tool.name, + description: tool.description ?? null, + inputSchema: tool.inputSchema ?? null, + outputSchema: tool.outputSchema ?? null, + annotations: tool.annotations ?? null, + execution: tool.execution ?? null, + }); +} + +function buildBestEffortInputSchema(tool: Tool): z.ZodTypeAny { + if (!tool.inputSchema) { + return z.object({}).passthrough(); + } + const zod = jsonSchemaToZod(tool.inputSchema); + return zod; +} + +function buildBestEffortAnnotations(tool: Tool, localName: string): ToolAnnotations { + const existing = (tool.annotations ?? {}) as ToolAnnotations; + + if (existing.readOnlyHint !== undefined) { + return existing; + } + + return { + ...existing, + readOnlyHint: inferReadOnlyHint(localName), + }; +} + +function inferReadOnlyHint(localToolName: string): boolean { + // Default to conservative: most IDE tools can mutate project state. + const name = localToolName.toLowerCase(); + + const definitelyReadOnlyPrefixes = [ + 'xcode_tools_xcodelist', + 'xcode_tools_xcodeglob', + 'xcode_tools_xcodegrep', + 'xcode_tools_xcoderead', + 'xcode_tools_xcoderefreshcodeissuesinfile', + 'xcode_tools_documentationsearch', + 'xcode_tools_getbuildlog', + 'xcode_tools_gettestlist', + ]; + + if (definitelyReadOnlyPrefixes.some((p) => name.startsWith(p))) return true; + + return false; +} diff --git a/src/integrations/xcode-tools-bridge/standalone.ts b/src/integrations/xcode-tools-bridge/standalone.ts new file mode 100644 index 00000000..040f5976 --- /dev/null +++ b/src/integrations/xcode-tools-bridge/standalone.ts @@ -0,0 +1,105 @@ +import { + createErrorResponse, + createTextResponse, + type ToolResponse, +} from '../../utils/responses/index.ts'; +import { + buildXcodeToolsBridgeStatus, + classifyBridgeError, + serializeBridgeTool, + type XcodeToolsBridgeStatus, +} from './core.ts'; +import { XcodeIdeToolService } from './tool-service.ts'; + +export class StandaloneXcodeToolsBridge { + private readonly service: XcodeIdeToolService; + + constructor() { + this.service = new XcodeIdeToolService(); + this.service.setWorkflowEnabled(true); + } + + async shutdown(): Promise { + await this.service.disconnect(); + } + + async getStatus(): Promise { + return buildXcodeToolsBridgeStatus({ + workflowEnabled: false, + proxiedToolCount: 0, + lastError: this.service.getLastError(), + clientStatus: this.service.getClientStatus(), + }); + } + + async statusTool(): Promise { + const status = await this.getStatus(); + return createTextResponse(JSON.stringify(status, null, 2)); + } + + async syncTool(): Promise { + try { + const remoteTools = await this.service.listTools({ refresh: true }); + + const sync = { + added: remoteTools.length, + updated: 0, + removed: 0, + total: remoteTools.length, + }; + const status = await this.getStatus(); + return createTextResponse(JSON.stringify({ sync, status }, null, 2)); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Bridge sync failed', message); + } finally { + await this.service.disconnect(); + } + } + + async disconnectTool(): Promise { + try { + await this.service.disconnect(); + const status = await this.getStatus(); + return createTextResponse(JSON.stringify(status, null, 2)); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Bridge disconnect failed', message); + } + } + + async listToolsTool(params: { refresh?: boolean }): Promise { + try { + const tools = await this.service.listTools({ refresh: params.refresh !== false }); + return createTextResponse( + JSON.stringify( + { + toolCount: tools.length, + tools: tools.map(serializeBridgeTool), + }, + null, + 2, + ), + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(classifyBridgeError(error, 'list'), message); + } + } + + async callToolTool(params: { + remoteTool: string; + arguments: Record; + timeoutMs?: number; + }): Promise { + try { + const response = await this.service.invokeTool(params.remoteTool, params.arguments, { + timeoutMs: params.timeoutMs, + }); + return response as ToolResponse; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(classifyBridgeError(error, 'call'), message); + } + } +} diff --git a/src/integrations/xcode-tools-bridge/tool-service.ts b/src/integrations/xcode-tools-bridge/tool-service.ts new file mode 100644 index 00000000..9656a44d --- /dev/null +++ b/src/integrations/xcode-tools-bridge/tool-service.ts @@ -0,0 +1,158 @@ +import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js'; +import { + XcodeToolsBridgeClient, + type XcodeToolsBridgeClientOptions, + type XcodeToolsBridgeClientStatus, +} from './client.ts'; +import { getMcpBridgeAvailability } from './core.ts'; + +export interface BridgeCapabilities { + available: boolean; + path: string | null; + connected: boolean; + bridgePid: number | null; + lastError: string | null; + toolCount: number; +} + +export interface XcodeIdeToolServiceOptions { + onToolCatalogInvalidated?: () => void; + clientOptions?: XcodeToolsBridgeClientOptions; +} + +export interface ListBridgeToolsOptions { + refresh?: boolean; +} + +export class XcodeIdeToolService { + private readonly client: XcodeToolsBridgeClient; + private readonly options: XcodeIdeToolServiceOptions; + + private workflowEnabled = false; + private toolCatalog = new Map(); + private lastError: string | null = null; + private listInFlight: Promise | null = null; + + constructor(options: XcodeIdeToolServiceOptions = {}) { + this.options = options; + this.client = new XcodeToolsBridgeClient({ + ...this.options.clientOptions, + onToolsListChanged: (): void => { + this.toolCatalog.clear(); + this.options.onToolCatalogInvalidated?.(); + }, + onBridgeClosed: (): void => { + this.toolCatalog.clear(); + this.lastError = this.client.getStatus().lastError ?? this.lastError; + this.options.onToolCatalogInvalidated?.(); + }, + }); + } + + setWorkflowEnabled(enabled: boolean): void { + this.workflowEnabled = enabled; + } + + isWorkflowEnabled(): boolean { + return this.workflowEnabled; + } + + getClientStatus(): XcodeToolsBridgeClientStatus { + return this.client.getStatus(); + } + + getLastError(): string | null { + return this.lastError ?? this.client.getStatus().lastError; + } + + getCachedTools(): Tool[] { + return [...this.toolCatalog.values()]; + } + + async getCapabilities(): Promise { + const bridge = await getMcpBridgeAvailability(); + const clientStatus = this.client.getStatus(); + return { + available: bridge.available, + path: bridge.path, + connected: clientStatus.connected, + bridgePid: clientStatus.bridgePid, + lastError: this.getLastError(), + toolCount: this.toolCatalog.size, + }; + } + + async listTools(opts: ListBridgeToolsOptions = {}): Promise { + if (opts.refresh === false) { + return this.getCachedTools(); + } + return this.refreshTools(); + } + + async invokeTool( + name: string, + args: Record, + opts: { timeoutMs?: number } = {}, + ): Promise { + await this.ensureConnected(); + try { + const response = await this.client.callTool(name, args, opts); + this.lastError = null; + return response; + } catch (error) { + this.lastError = toErrorMessage(error); + throw error; + } + } + + async disconnect(): Promise { + this.toolCatalog.clear(); + this.listInFlight = null; + await this.client.disconnect(); + } + + private async refreshTools(): Promise { + if (this.listInFlight) { + return this.listInFlight; + } + + this.listInFlight = (async (): Promise => { + await this.ensureConnected(); + const tools = await this.client.listTools(); + this.toolCatalog = new Map(tools.map((tool) => [tool.name, tool])); + this.lastError = null; + return tools; + })(); + + try { + return await this.listInFlight; + } catch (error) { + this.toolCatalog.clear(); + this.lastError = toErrorMessage(error); + throw error; + } finally { + this.listInFlight = null; + } + } + + private async ensureConnected(): Promise { + if (!this.workflowEnabled) { + const message = 'xcode-ide workflow is not enabled'; + this.lastError = message; + throw new Error(message); + } + + const bridge = await getMcpBridgeAvailability(); + if (!bridge.available) { + const message = 'mcpbridge not available (xcrun --find mcpbridge failed)'; + this.lastError = message; + throw new Error(message); + } + + await this.client.connectOnce(); + } +} + +function toErrorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/src/mcp/resources/__tests__/session-status.test.ts b/src/mcp/resources/__tests__/session-status.test.ts new file mode 100644 index 00000000..433305df --- /dev/null +++ b/src/mcp/resources/__tests__/session-status.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import { activeLogSessions } from '../../../utils/log_capture.ts'; +import { activeDeviceLogSessions } from '../../../utils/log-capture/device-log-sessions.ts'; +import sessionStatusResource, { sessionStatusResourceLogic } from '../session-status.ts'; + +describe('session-status resource', () => { + beforeEach(async () => { + activeLogSessions.clear(); + activeDeviceLogSessions.clear(); + await getDefaultDebuggerManager().disposeAll(); + }); + + afterEach(async () => { + activeLogSessions.clear(); + activeDeviceLogSessions.clear(); + await getDefaultDebuggerManager().disposeAll(); + }); + + describe('Export Field Validation', () => { + it('should export correct uri', () => { + expect(sessionStatusResource.uri).toBe('xcodebuildmcp://session-status'); + }); + + it('should export correct description', () => { + expect(sessionStatusResource.description).toBe( + 'Runtime session state for log capture and debugging', + ); + }); + + it('should export correct mimeType', () => { + expect(sessionStatusResource.mimeType).toBe('application/json'); + }); + + it('should export handler function', () => { + expect(typeof sessionStatusResource.handler).toBe('function'); + }); + }); + + describe('Handler Functionality', () => { + it('should return empty status when no sessions exist', async () => { + const result = await sessionStatusResourceLogic(); + + expect(result.contents).toHaveLength(1); + const parsed = JSON.parse(result.contents[0].text); + + expect(parsed.logging.simulator.activeSessionIds).toEqual([]); + expect(parsed.logging.device.activeSessionIds).toEqual([]); + expect(parsed.debug.currentSessionId).toBe(null); + expect(parsed.debug.sessionIds).toEqual([]); + }); + }); +}); diff --git a/src/mcp/resources/__tests__/simulators.test.ts b/src/mcp/resources/__tests__/simulators.test.ts index 9cc5707b..eadc7a99 100644 --- a/src/mcp/resources/__tests__/simulators.test.ts +++ b/src/mcp/resources/__tests__/simulators.test.ts @@ -2,7 +2,10 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import simulatorsResource, { simulatorsResourceLogic } from '../simulators.ts'; -import { createMockExecutor } from '../../../test-utils/mock-executors.ts'; +import { + createMockCommandResponse, + createMockExecutor, +} from '../../../test-utils/mock-executors.ts'; describe('simulators resource', () => { describe('Export Field Validation', () => { @@ -73,21 +76,19 @@ describe('simulators resource', () => { const mockExecutor = async (command: string[]) => { // JSON command returns invalid JSON if (command.includes('--json')) { - return { + return createMockCommandResponse({ success: true, output: 'invalid json', error: undefined, - process: { pid: 12345 }, - }; + }); } // Text command returns valid text output - return { + return createMockCommandResponse({ success: true, output: mockTextOutput, error: undefined, - process: { pid: 12345 }, - }; + }); }; const result = await simulatorsResourceLogic(mockExecutor); @@ -170,7 +171,7 @@ describe('simulators resource', () => { expect(result.contents[0].text).not.toContain('iPhone 14'); }); - it('should include next steps guidance', async () => { + it('should include hint about setting defaults', async () => { const mockExecutor = createMockExecutor({ success: true, output: JSON.stringify({ @@ -189,11 +190,10 @@ describe('simulators resource', () => { const result = await simulatorsResourceLogic(mockExecutor); - expect(result.contents[0].text).toContain('Next Steps:'); - expect(result.contents[0].text).toContain('boot_sim'); - expect(result.contents[0].text).toContain('open_sim'); - expect(result.contents[0].text).toContain('build_sim'); - expect(result.contents[0].text).toContain('get_sim_app_path'); + // The resource returns text content with simulator list and hint + expect(result.contents[0].text).toContain('iPhone 15 Pro'); + expect(result.contents[0].text).toContain('ABC123-DEF456-GHI789'); + expect(result.contents[0].text).toContain('session-set-defaults'); }); }); }); diff --git a/src/mcp/resources/__tests__/xcode-ide-state.test.ts b/src/mcp/resources/__tests__/xcode-ide-state.test.ts new file mode 100644 index 00000000..b7713a75 --- /dev/null +++ b/src/mcp/resources/__tests__/xcode-ide-state.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect } from 'vitest'; +import xcodeIdeStateResource, { xcodeIdeStateResourceLogic } from '../xcode-ide-state.ts'; + +describe('xcode-ide-state resource', () => { + describe('Export Field Validation', () => { + it('should export correct uri', () => { + expect(xcodeIdeStateResource.uri).toBe('xcodebuildmcp://xcode-ide-state'); + }); + + it('should export correct name', () => { + expect(xcodeIdeStateResource.name).toBe('xcode-ide-state'); + }); + + it('should export correct description', () => { + expect(xcodeIdeStateResource.description).toBe( + "Current Xcode IDE selection (scheme and simulator) from Xcode's UI state", + ); + }); + + it('should export correct mimeType', () => { + expect(xcodeIdeStateResource.mimeType).toBe('application/json'); + }); + + it('should export handler function', () => { + expect(typeof xcodeIdeStateResource.handler).toBe('function'); + }); + }); + + describe('Handler Functionality', () => { + it('should return JSON response with expected structure', async () => { + const result = await xcodeIdeStateResourceLogic(); + + expect(result.contents).toHaveLength(1); + const parsed = JSON.parse(result.contents[0].text); + + // Response should have the expected structure + expect(typeof parsed.detected).toBe('boolean'); + + // Optional fields may or may not be present + if (parsed.scheme !== undefined) { + expect(typeof parsed.scheme).toBe('string'); + } + if (parsed.simulatorId !== undefined) { + expect(typeof parsed.simulatorId).toBe('string'); + } + if (parsed.simulatorName !== undefined) { + expect(typeof parsed.simulatorName).toBe('string'); + } + if (parsed.error !== undefined) { + expect(typeof parsed.error).toBe('string'); + } + }); + + it('should indicate detected=false when no Xcode project found', async () => { + // Running from the XcodeBuildMCP repo root (not an iOS project) + // should return detected=false with an error + const result = await xcodeIdeStateResourceLogic(); + const parsed = JSON.parse(result.contents[0].text); + + // In our test environment without a proper iOS project, + // we expect either an error or detected=false + expect(parsed.detected === false || parsed.error !== undefined).toBe(true); + }); + }); +}); diff --git a/src/mcp/resources/doctor.ts b/src/mcp/resources/doctor.ts index 851b9a1d..ee07509c 100644 --- a/src/mcp/resources/doctor.ts +++ b/src/mcp/resources/doctor.ts @@ -6,7 +6,8 @@ */ import { log } from '../../utils/logging/index.ts'; -import { getDefaultCommandExecutor, CommandExecutor } from '../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../utils/execution/index.ts'; import { doctorLogic } from '../tools/doctor/doctor.ts'; // Testable resource logic separated from MCP handler diff --git a/src/mcp/resources/session-status.ts b/src/mcp/resources/session-status.ts new file mode 100644 index 00000000..dbe46c78 --- /dev/null +++ b/src/mcp/resources/session-status.ts @@ -0,0 +1,44 @@ +/** + * Session Status Resource Plugin + * + * Provides read-only runtime session state for log capture and debugging. + */ + +import { log } from '../../utils/logging/index.ts'; +import { getSessionRuntimeStatusSnapshot } from '../../utils/session-status.ts'; + +export async function sessionStatusResourceLogic(): Promise<{ contents: Array<{ text: string }> }> { + try { + log('info', 'Processing session status resource request'); + const status = getSessionRuntimeStatusSnapshot(); + + return { + contents: [ + { + text: JSON.stringify(status, null, 2), + }, + ], + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + log('error', `Error in session status resource handler: ${errorMessage}`); + + return { + contents: [ + { + text: `Error retrieving session status: ${errorMessage}`, + }, + ], + }; + } +} + +export default { + uri: 'xcodebuildmcp://session-status', + name: 'session-status', + description: 'Runtime session state for log capture and debugging', + mimeType: 'application/json', + async handler(): Promise<{ contents: Array<{ text: string }> }> { + return sessionStatusResourceLogic(); + }, +}; diff --git a/src/mcp/resources/xcode-ide-state.ts b/src/mcp/resources/xcode-ide-state.ts new file mode 100644 index 00000000..950969e7 --- /dev/null +++ b/src/mcp/resources/xcode-ide-state.ts @@ -0,0 +1,75 @@ +/** + * Xcode IDE State Resource + * + * Provides read-only access to Xcode's current IDE selection (scheme and simulator). + * Reads from UserInterfaceState.xcuserstate without modifying session defaults. + * + * Only available when running under Xcode's coding agent. + */ + +import { log } from '../../utils/logging/index.ts'; +import { getDefaultCommandExecutor } from '../../utils/execution/index.ts'; +import { readXcodeIdeState } from '../../utils/xcode-state-reader.ts'; + +export interface XcodeIdeStateResponse { + detected: boolean; + scheme?: string; + simulatorId?: string; + simulatorName?: string; + error?: string; +} + +export async function xcodeIdeStateResourceLogic(): Promise<{ + contents: Array<{ text: string }>; +}> { + try { + log('info', 'Processing Xcode IDE state resource request'); + + const executor = getDefaultCommandExecutor(); + const cwd = process.cwd(); + + const state = await readXcodeIdeState({ executor, cwd }); + + const response: XcodeIdeStateResponse = { + detected: !state.error && (!!state.scheme || !!state.simulatorId), + scheme: state.scheme, + simulatorId: state.simulatorId, + simulatorName: state.simulatorName, + error: state.error, + }; + + return { + contents: [ + { + text: JSON.stringify(response, null, 2), + }, + ], + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + log('error', `Error in Xcode IDE state resource handler: ${errorMessage}`); + + const response: XcodeIdeStateResponse = { + detected: false, + error: errorMessage, + }; + + return { + contents: [ + { + text: JSON.stringify(response, null, 2), + }, + ], + }; + } +} + +export default { + uri: 'xcodebuildmcp://xcode-ide-state', + name: 'xcode-ide-state', + description: "Current Xcode IDE selection (scheme and simulator) from Xcode's UI state", + mimeType: 'application/json', + async handler(): Promise<{ contents: Array<{ text: string }> }> { + return xcodeIdeStateResourceLogic(); + }, +}; diff --git a/src/mcp/tools/debugging/__tests__/debugging-tools.test.ts b/src/mcp/tools/debugging/__tests__/debugging-tools.test.ts new file mode 100644 index 00000000..4559e787 --- /dev/null +++ b/src/mcp/tools/debugging/__tests__/debugging-tools.test.ts @@ -0,0 +1,899 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { sessionStore } from '../../../../utils/session-store.ts'; +import { DebuggerManager } from '../../../../utils/debugger/index.ts'; +import type { DebuggerToolContext } from '../../../../utils/debugger/index.ts'; +import type { DebuggerBackend } from '../../../../utils/debugger/backends/DebuggerBackend.ts'; +import type { BreakpointSpec, DebugSessionInfo } from '../../../../utils/debugger/types.ts'; + +import { + schema as attachSchema, + handler as attachHandler, + debug_attach_simLogic, +} from '../debug_attach_sim.ts'; +import { + schema as bpAddSchema, + handler as bpAddHandler, + debug_breakpoint_addLogic, +} from '../debug_breakpoint_add.ts'; +import { + schema as bpRemoveSchema, + handler as bpRemoveHandler, + debug_breakpoint_removeLogic, +} from '../debug_breakpoint_remove.ts'; +import { + schema as continueSchema, + handler as continueHandler, + debug_continueLogic, +} from '../debug_continue.ts'; +import { + schema as detachSchema, + handler as detachHandler, + debug_detachLogic, +} from '../debug_detach.ts'; +import { + schema as lldbSchema, + handler as lldbHandler, + debug_lldb_commandLogic, +} from '../debug_lldb_command.ts'; +import { + schema as stackSchema, + handler as stackHandler, + debug_stackLogic, +} from '../debug_stack.ts'; +import { + schema as variablesSchema, + handler as variablesHandler, + debug_variablesLogic, +} from '../debug_variables.ts'; + +function createMockBackend(overrides: Partial = {}): DebuggerBackend { + return { + kind: 'dap', + attach: async () => {}, + detach: async () => {}, + runCommand: async () => 'mock output', + resume: async () => {}, + addBreakpoint: async (spec: BreakpointSpec) => ({ + id: 1, + spec, + rawOutput: 'Breakpoint 1: mock', + }), + removeBreakpoint: async () => 'removed', + getStack: async () => 'frame #0: mock stack', + getVariables: async () => 'x = 42', + getExecutionState: async () => ({ status: 'stopped' as const }), + dispose: async () => {}, + ...overrides, + }; +} + +function createTestDebuggerManager( + backendOverrides: Partial = {}, +): DebuggerManager { + const backend = createMockBackend(backendOverrides); + return new DebuggerManager({ + backendFactory: async () => backend, + }); +} + +function createTestContext(backendOverrides: Partial = {}): DebuggerToolContext { + return { + executor: createMockExecutor({ success: true, output: '' }), + debugger: createTestDebuggerManager(backendOverrides), + }; +} + +async function createSessionAndContext( + backendOverrides: Partial = {}, +): Promise<{ ctx: DebuggerToolContext; session: DebugSessionInfo }> { + const ctx = createTestContext(backendOverrides); + const session = await ctx.debugger.createSession({ + simulatorId: 'test-sim-uuid', + pid: 1234, + }); + ctx.debugger.setCurrentSession(session.id); + return { ctx, session }; +} + +// --------------------------------------------------------------------------- +// debug_attach_sim +// --------------------------------------------------------------------------- +describe('debug_attach_sim', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof attachHandler).toBe('function'); + }); + + it('should expose schema with expected shape', () => { + expect(attachSchema).toBeDefined(); + }); + }); + + describe('Handler Requirements', () => { + it('should return error when no session defaults for simulator', async () => { + const result = await attachHandler({ + bundleId: 'com.test.app', + }); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('simulatorId'); + }); + }); + + describe('Logic Behavior', () => { + it('should attach successfully with pid', async () => { + const ctx = createTestContext(); + + const result = await debug_attach_simLogic( + { + simulatorId: 'test-sim-uuid', + pid: 1234, + continueOnAttach: true, + makeCurrent: true, + }, + ctx, + ); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Attached'); + expect(text).toContain('1234'); + expect(text).toContain('test-sim-uuid'); + expect(text).toContain('Debug session ID:'); + }); + + it('should attach without continuing when continueOnAttach is false', async () => { + const ctx = createTestContext(); + + const result = await debug_attach_simLogic( + { + simulatorId: 'test-sim-uuid', + pid: 1234, + continueOnAttach: false, + makeCurrent: true, + }, + ctx, + ); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Execution is paused'); + }); + + it('should return error when createSession throws', async () => { + const ctx = createTestContext({ + attach: async () => { + throw new Error('LLDB attach failed'); + }, + }); + + const result = await debug_attach_simLogic( + { + simulatorId: 'test-sim-uuid', + pid: 1234, + continueOnAttach: true, + makeCurrent: true, + }, + ctx, + ); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to attach debugger'); + expect(text).toContain('LLDB attach failed'); + }); + + it('should return error when resume throws after attach', async () => { + const ctx = createTestContext({ + resume: async () => { + throw new Error('Resume failed'); + }, + }); + + const result = await debug_attach_simLogic( + { + simulatorId: 'test-sim-uuid', + pid: 1234, + continueOnAttach: true, + makeCurrent: true, + }, + ctx, + ); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to resume debugger after attach'); + }); + + it('should return error when simulator resolution fails', async () => { + const ctx: DebuggerToolContext = { + executor: createMockExecutor({ + success: false, + error: 'No simulators found', + }), + debugger: createTestDebuggerManager(), + }; + + const result = await debug_attach_simLogic( + { + simulatorName: 'NonExistent Simulator', + bundleId: 'com.test.app', + continueOnAttach: true, + makeCurrent: true, + }, + ctx, + ); + + expect(result.isError).toBe(true); + }); + + it('should return error when pid resolution fails for bundleId', async () => { + const ctx: DebuggerToolContext = { + executor: createMockExecutor({ + success: false, + error: 'launchctl failed', + }), + debugger: createTestDebuggerManager(), + }; + + const result = await debug_attach_simLogic( + { + simulatorId: 'test-sim-uuid', + bundleId: 'com.test.app', + continueOnAttach: true, + makeCurrent: true, + }, + ctx, + ); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to resolve simulator PID'); + }); + + it('should include nextStepParams on success', async () => { + const ctx = createTestContext(); + + const result = await debug_attach_simLogic( + { + simulatorId: 'test-sim-uuid', + pid: 1234, + continueOnAttach: true, + makeCurrent: true, + }, + ctx, + ); + + expect(result.nextStepParams).toBeDefined(); + const breakpointStep = result.nextStepParams?.debug_breakpoint_add; + const continueStep = result.nextStepParams?.debug_continue; + const stackStep = result.nextStepParams?.debug_stack; + + expect(Array.isArray(breakpointStep)).toBe(false); + expect(Array.isArray(continueStep)).toBe(false); + expect(Array.isArray(stackStep)).toBe(false); + + const breakpointParams = Array.isArray(breakpointStep) ? undefined : breakpointStep; + const continueParams = Array.isArray(continueStep) ? undefined : continueStep; + const stackParams = Array.isArray(stackStep) ? undefined : stackStep; + + const debugSessionId = breakpointParams?.debugSessionId; + expect(typeof debugSessionId).toBe('string'); + expect(breakpointParams).toMatchObject({ file: '...', line: 123 }); + expect(continueParams?.debugSessionId).toBe(debugSessionId); + expect(stackParams?.debugSessionId).toBe(debugSessionId); + }); + }); +}); + +// --------------------------------------------------------------------------- +// debug_breakpoint_add +// --------------------------------------------------------------------------- +describe('debug_breakpoint_add', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof bpAddHandler).toBe('function'); + }); + + it('should expose schema with expected keys', () => { + expect(bpAddSchema).toBeDefined(); + expect('debugSessionId' in bpAddSchema).toBe(true); + expect('file' in bpAddSchema).toBe(true); + expect('line' in bpAddSchema).toBe(true); + expect('function' in bpAddSchema).toBe(true); + expect('condition' in bpAddSchema).toBe(true); + }); + }); + + describe('Handler Requirements', () => { + it('should handle missing debug session gracefully', async () => { + const ctx = createTestContext(); + + const result = await debug_breakpoint_addLogic({ file: 'main.swift', line: 10 }, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('No active debug session'); + }); + }); + + describe('Logic Behavior', () => { + it('should add file-line breakpoint successfully', async () => { + const { ctx, session } = await createSessionAndContext(); + + const result = await debug_breakpoint_addLogic( + { debugSessionId: session.id, file: 'main.swift', line: 42 }, + ctx, + ); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Breakpoint'); + expect(text).toContain('set'); + }); + + it('should add function breakpoint successfully', async () => { + const { ctx, session } = await createSessionAndContext(); + + const result = await debug_breakpoint_addLogic( + { debugSessionId: session.id, function: 'viewDidLoad' }, + ctx, + ); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Breakpoint'); + }); + + it('should add breakpoint with condition', async () => { + const { ctx, session } = await createSessionAndContext(); + + const result = await debug_breakpoint_addLogic( + { + debugSessionId: session.id, + file: 'main.swift', + line: 10, + condition: 'x > 5', + }, + ctx, + ); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Breakpoint'); + }); + + it('should return error when addBreakpoint throws', async () => { + const { ctx, session } = await createSessionAndContext({ + addBreakpoint: async () => { + throw new Error('Invalid file path'); + }, + }); + + const result = await debug_breakpoint_addLogic( + { debugSessionId: session.id, file: 'missing.swift', line: 1 }, + ctx, + ); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to add breakpoint'); + expect(text).toContain('Invalid file path'); + }); + + it('should use current session when debugSessionId is omitted', async () => { + const { ctx } = await createSessionAndContext(); + + const result = await debug_breakpoint_addLogic({ file: 'main.swift', line: 10 }, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Breakpoint'); + }); + }); +}); + +// --------------------------------------------------------------------------- +// debug_breakpoint_remove +// --------------------------------------------------------------------------- +describe('debug_breakpoint_remove', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof bpRemoveHandler).toBe('function'); + }); + + it('should expose schema with expected keys', () => { + expect(bpRemoveSchema).toBeDefined(); + expect('debugSessionId' in bpRemoveSchema).toBe(true); + expect('breakpointId' in bpRemoveSchema).toBe(true); + }); + }); + + describe('Handler Requirements', () => { + it('should handle missing debug session gracefully', async () => { + const ctx = createTestContext(); + + const result = await debug_breakpoint_removeLogic({ breakpointId: 1 }, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('No active debug session'); + }); + }); + + describe('Logic Behavior', () => { + it('should remove breakpoint successfully', async () => { + const { ctx, session } = await createSessionAndContext(); + + const result = await debug_breakpoint_removeLogic( + { debugSessionId: session.id, breakpointId: 1 }, + ctx, + ); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Breakpoint 1 removed'); + }); + + it('should return error when removeBreakpoint throws', async () => { + const { ctx, session } = await createSessionAndContext({ + removeBreakpoint: async () => { + throw new Error('Breakpoint not found'); + }, + }); + + const result = await debug_breakpoint_removeLogic( + { debugSessionId: session.id, breakpointId: 999 }, + ctx, + ); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to remove breakpoint'); + expect(text).toContain('Breakpoint not found'); + }); + + it('should use current session when debugSessionId is omitted', async () => { + const { ctx } = await createSessionAndContext(); + + const result = await debug_breakpoint_removeLogic({ breakpointId: 1 }, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Breakpoint 1 removed'); + }); + }); +}); + +// --------------------------------------------------------------------------- +// debug_continue +// --------------------------------------------------------------------------- +describe('debug_continue', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof continueHandler).toBe('function'); + }); + + it('should expose schema with expected keys', () => { + expect(continueSchema).toBeDefined(); + expect('debugSessionId' in continueSchema).toBe(true); + }); + }); + + describe('Handler Requirements', () => { + it('should handle missing debug session gracefully', async () => { + const ctx = createTestContext(); + + const result = await debug_continueLogic({}, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('No active debug session'); + }); + }); + + describe('Logic Behavior', () => { + it('should resume session successfully with explicit id', async () => { + const { ctx, session } = await createSessionAndContext(); + + const result = await debug_continueLogic({ debugSessionId: session.id }, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Resumed debugger session'); + expect(text).toContain(session.id); + }); + + it('should resume current session when debugSessionId is omitted', async () => { + const { ctx } = await createSessionAndContext(); + + const result = await debug_continueLogic({}, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Resumed debugger session'); + }); + + it('should return error when resume throws', async () => { + const { ctx, session } = await createSessionAndContext({ + resume: async () => { + throw new Error('Process terminated'); + }, + }); + + const result = await debug_continueLogic({ debugSessionId: session.id }, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to resume debugger'); + expect(text).toContain('Process terminated'); + }); + }); +}); + +// --------------------------------------------------------------------------- +// debug_detach +// --------------------------------------------------------------------------- +describe('debug_detach', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof detachHandler).toBe('function'); + }); + + it('should expose schema with expected keys', () => { + expect(detachSchema).toBeDefined(); + expect('debugSessionId' in detachSchema).toBe(true); + }); + }); + + describe('Handler Requirements', () => { + it('should handle missing debug session gracefully', async () => { + const ctx = createTestContext(); + + const result = await debug_detachLogic({}, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('No active debug session'); + }); + }); + + describe('Logic Behavior', () => { + it('should detach session successfully with explicit id', async () => { + const { ctx, session } = await createSessionAndContext(); + + const result = await debug_detachLogic({ debugSessionId: session.id }, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Detached debugger session'); + expect(text).toContain(session.id); + }); + + it('should detach current session when debugSessionId is omitted', async () => { + const { ctx } = await createSessionAndContext(); + + const result = await debug_detachLogic({}, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toContain('Detached debugger session'); + }); + + it('should return error when detach throws', async () => { + const { ctx, session } = await createSessionAndContext({ + detach: async () => { + throw new Error('Connection lost'); + }, + }); + + const result = await debug_detachLogic({ debugSessionId: session.id }, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to detach debugger'); + expect(text).toContain('Connection lost'); + }); + }); +}); + +// --------------------------------------------------------------------------- +// debug_lldb_command +// --------------------------------------------------------------------------- +describe('debug_lldb_command', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof lldbHandler).toBe('function'); + }); + + it('should expose schema with expected keys', () => { + expect(lldbSchema).toBeDefined(); + expect('debugSessionId' in lldbSchema).toBe(true); + expect('command' in lldbSchema).toBe(true); + expect('timeoutMs' in lldbSchema).toBe(true); + }); + }); + + describe('Handler Requirements', () => { + it('should handle missing debug session gracefully', async () => { + const ctx = createTestContext(); + + const result = await debug_lldb_commandLogic({ command: 'bt' }, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('No active debug session'); + }); + }); + + describe('Logic Behavior', () => { + it('should run command successfully', async () => { + const { ctx, session } = await createSessionAndContext({ + runCommand: async () => ' frame #0: main\n', + }); + + const result = await debug_lldb_commandLogic( + { debugSessionId: session.id, command: 'bt' }, + ctx, + ); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toBe('frame #0: main'); + }); + + it('should pass timeoutMs through to runCommand', async () => { + let receivedOpts: { timeoutMs?: number } | undefined; + const { ctx, session } = await createSessionAndContext({ + runCommand: async (_cmd: string, opts?: { timeoutMs?: number }) => { + receivedOpts = opts; + return 'ok'; + }, + }); + + await debug_lldb_commandLogic( + { debugSessionId: session.id, command: 'expr x', timeoutMs: 5000 }, + ctx, + ); + + expect(receivedOpts?.timeoutMs).toBe(5000); + }); + + it('should return error when runCommand throws', async () => { + const { ctx, session } = await createSessionAndContext({ + runCommand: async () => { + throw new Error('Command timed out'); + }, + }); + + const result = await debug_lldb_commandLogic( + { debugSessionId: session.id, command: 'expr longRunning()' }, + ctx, + ); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to run LLDB command'); + expect(text).toContain('Command timed out'); + }); + + it('should use current session when debugSessionId is omitted', async () => { + const { ctx } = await createSessionAndContext({ + runCommand: async () => 'result', + }); + + const result = await debug_lldb_commandLogic({ command: 'po self' }, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toBe('result'); + }); + }); +}); + +// --------------------------------------------------------------------------- +// debug_stack +// --------------------------------------------------------------------------- +describe('debug_stack', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof stackHandler).toBe('function'); + }); + + it('should expose schema with expected keys', () => { + expect(stackSchema).toBeDefined(); + expect('debugSessionId' in stackSchema).toBe(true); + expect('threadIndex' in stackSchema).toBe(true); + expect('maxFrames' in stackSchema).toBe(true); + }); + }); + + describe('Handler Requirements', () => { + it('should handle missing debug session gracefully', async () => { + const ctx = createTestContext(); + + const result = await debug_stackLogic({}, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('No active debug session'); + }); + }); + + describe('Logic Behavior', () => { + it('should return stack output successfully', async () => { + const stackOutput = ' frame #0: 0x0000 main at main.swift:10\n frame #1: 0x0001 start\n'; + const { ctx, session } = await createSessionAndContext({ + getStack: async () => stackOutput, + }); + + const result = await debug_stackLogic({ debugSessionId: session.id }, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toBe(stackOutput.trim()); + }); + + it('should pass threadIndex and maxFrames through', async () => { + let receivedOpts: { threadIndex?: number; maxFrames?: number } | undefined; + const { ctx, session } = await createSessionAndContext({ + getStack: async (opts?: { threadIndex?: number; maxFrames?: number }) => { + receivedOpts = opts; + return 'frame #0'; + }, + }); + + await debug_stackLogic({ debugSessionId: session.id, threadIndex: 2, maxFrames: 5 }, ctx); + + expect(receivedOpts?.threadIndex).toBe(2); + expect(receivedOpts?.maxFrames).toBe(5); + }); + + it('should return error when getStack throws', async () => { + const { ctx, session } = await createSessionAndContext({ + getStack: async () => { + throw new Error('Process not stopped'); + }, + }); + + const result = await debug_stackLogic({ debugSessionId: session.id }, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to get stack'); + expect(text).toContain('Process not stopped'); + }); + + it('should use current session when debugSessionId is omitted', async () => { + const { ctx } = await createSessionAndContext({ + getStack: async () => 'frame #0: main', + }); + + const result = await debug_stackLogic({}, ctx); + + expect(result.isError).toBe(false); + expect(result.content[0].text).toBe('frame #0: main'); + }); + }); +}); + +// --------------------------------------------------------------------------- +// debug_variables +// --------------------------------------------------------------------------- +describe('debug_variables', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have handler function', () => { + expect(typeof variablesHandler).toBe('function'); + }); + + it('should expose schema with expected keys', () => { + expect(variablesSchema).toBeDefined(); + expect('debugSessionId' in variablesSchema).toBe(true); + expect('frameIndex' in variablesSchema).toBe(true); + }); + }); + + describe('Handler Requirements', () => { + it('should handle missing debug session gracefully', async () => { + const ctx = createTestContext(); + + const result = await debug_variablesLogic({}, ctx); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('No active debug session'); + }); + }); + + describe('Logic Behavior', () => { + it('should return variables output successfully', async () => { + const variablesOutput = ' (Int) x = 42\n (String) name = "hello"\n'; + const { ctx, session } = await createSessionAndContext({ + getVariables: async () => variablesOutput, + }); + + const result = await debug_variablesLogic({ debugSessionId: session.id }, ctx); + + expect(result.isError).toBe(false); + const text = result.content[0].text; + expect(text).toBe(variablesOutput.trim()); + }); + + it('should pass frameIndex through', async () => { + let receivedOpts: { frameIndex?: number } | undefined; + const { ctx, session } = await createSessionAndContext({ + getVariables: async (opts?: { frameIndex?: number }) => { + receivedOpts = opts; + return 'x = 1'; + }, + }); + + await debug_variablesLogic({ debugSessionId: session.id, frameIndex: 3 }, ctx); + + expect(receivedOpts?.frameIndex).toBe(3); + }); + + it('should return error when getVariables throws', async () => { + const { ctx, session } = await createSessionAndContext({ + getVariables: async () => { + throw new Error('Frame index out of range'); + }, + }); + + const result = await debug_variablesLogic( + { debugSessionId: session.id, frameIndex: 999 }, + ctx, + ); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Failed to get variables'); + expect(text).toContain('Frame index out of range'); + }); + + it('should use current session when debugSessionId is omitted', async () => { + const { ctx } = await createSessionAndContext({ + getVariables: async () => 'y = 99', + }); + + const result = await debug_variablesLogic({}, ctx); + + expect(result.isError).toBe(false); + expect(result.content[0].text).toBe('y = 99'); + }); + }); +}); diff --git a/src/mcp/tools/debugging/debug_attach_sim.ts b/src/mcp/tools/debugging/debug_attach_sim.ts new file mode 100644 index 00000000..b0adeb16 --- /dev/null +++ b/src/mcp/tools/debugging/debug_attach_sim.ts @@ -0,0 +1,184 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { determineSimulatorUuid } from '../../../utils/simulator-utils.ts'; +import { + createSessionAwareToolWithContext, + getSessionAwareToolSchemaShape, +} from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + resolveSimulatorAppPid, + type DebuggerToolContext, +} from '../../../utils/debugger/index.ts'; + +const baseSchemaObject = z.object({ + simulatorId: z + .string() + .optional() + .describe( + 'UUID of the simulator to use (obtained from list_sims). Provide EITHER this OR simulatorName, not both', + ), + simulatorName: z + .string() + .optional() + .describe( + "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + ), + bundleId: z.string().optional(), + pid: z.number().int().positive().optional(), + waitFor: z.boolean().optional().describe('Wait for the process to appear when attaching'), + continueOnAttach: z.boolean().optional().default(true).describe('default: true'), + makeCurrent: z + .boolean() + .optional() + .default(true) + .describe('Set debug session as current (default: true)'), +}); + +const debugAttachSchema = z.preprocess( + nullifyEmptyStrings, + baseSchemaObject + .refine((val) => val.simulatorId !== undefined || val.simulatorName !== undefined, { + message: 'Either simulatorId or simulatorName is required.', + }) + .refine((val) => !(val.simulatorId && val.simulatorName), { + message: 'simulatorId and simulatorName are mutually exclusive. Provide only one.', + }) + .refine((val) => val.bundleId !== undefined || val.pid !== undefined, { + message: 'Provide either bundleId or pid to attach.', + }) + .refine((val) => !(val.bundleId && val.pid), { + message: 'bundleId and pid are mutually exclusive. Provide only one.', + }), +); + +export type DebugAttachSimParams = z.infer; + +export async function debug_attach_simLogic( + params: DebugAttachSimParams, + ctx: DebuggerToolContext, +): Promise { + const { executor, debugger: debuggerManager } = ctx; + + const simResult = await determineSimulatorUuid( + { simulatorId: params.simulatorId, simulatorName: params.simulatorName }, + executor, + ); + + if (simResult.error) { + return simResult.error; + } + + const simulatorId = simResult.uuid; + if (!simulatorId) { + return createErrorResponse('Simulator resolution failed', 'Unable to determine simulator UUID'); + } + + let pid = params.pid; + if (!pid && params.bundleId) { + try { + pid = await resolveSimulatorAppPid({ + executor, + simulatorId, + bundleId: params.bundleId, + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to resolve simulator PID', message); + } + } + + if (!pid) { + return createErrorResponse('Missing PID', 'Unable to resolve process ID to attach'); + } + + try { + const session = await debuggerManager.createSession({ + simulatorId, + pid, + waitFor: params.waitFor, + }); + + const isCurrent = params.makeCurrent ?? true; + if (isCurrent) { + debuggerManager.setCurrentSession(session.id); + } + + const shouldContinue = params.continueOnAttach ?? true; + if (shouldContinue) { + try { + await debuggerManager.resumeSession(session.id); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + try { + await debuggerManager.detachSession(session.id); + } catch (detachError) { + const detachMessage = + detachError instanceof Error ? detachError.message : String(detachError); + log('warn', `Failed to detach debugger session after resume failure: ${detachMessage}`); + } + return createErrorResponse('Failed to resume debugger after attach', message); + } + } + + const warningText = simResult.warning ? `⚠️ ${simResult.warning}\n\n` : ''; + const currentText = isCurrent + ? 'This session is now the current debug session.' + : 'This session is not set as the current session.'; + const resumeText = shouldContinue + ? 'Execution resumed after attach.' + : 'Execution is paused. Use debug_continue to resume before UI automation.'; + + const backendLabel = session.backend === 'dap' ? 'DAP debugger' : 'LLDB'; + + return { + content: [ + { + type: 'text', + text: + `${warningText}✅ Attached ${backendLabel} to simulator process ${pid} (${simulatorId}).\n\n` + + `Debug session ID: ${session.id}\n` + + `${currentText}\n` + + `${resumeText}`, + }, + ], + nextStepParams: { + debug_breakpoint_add: { debugSessionId: session.id, file: '...', line: 123 }, + debug_continue: { debugSessionId: session.id }, + debug_stack: { debugSessionId: session.id }, + }, + isError: false, + }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + log('error', `Failed to attach LLDB: ${message}`); + return createErrorResponse('Failed to attach debugger', message); + } +} + +const publicSchemaObject = z.strictObject( + baseSchemaObject.omit({ + simulatorId: true, + simulatorName: true, + }).shape, +); + +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareToolWithContext( + { + internalSchema: debugAttachSchema as unknown as z.ZodType, + logicFunction: debug_attach_simLogic, + getContext: getDefaultDebuggerToolContext, + requirements: [ + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + ], + exclusivePairs: [['simulatorId', 'simulatorName']], + }, +); diff --git a/src/mcp/tools/debugging/debug_breakpoint_add.ts b/src/mcp/tools/debugging/debug_breakpoint_add.ts new file mode 100644 index 00000000..fe6d17c5 --- /dev/null +++ b/src/mcp/tools/debugging/debug_breakpoint_add.ts @@ -0,0 +1,62 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse, createTextResponse } from '../../../utils/responses/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + type DebuggerToolContext, + type BreakpointSpec, +} from '../../../utils/debugger/index.ts'; + +const baseSchemaObject = z.object({ + debugSessionId: z.string().optional().describe('default: current session'), + file: z.string().optional(), + line: z.number().int().positive().optional(), + function: z.string().optional(), + condition: z.string().optional().describe('Expression for breakpoint condition'), +}); + +const debugBreakpointAddSchema = z.preprocess( + nullifyEmptyStrings, + baseSchemaObject + .refine((val) => !(val.file && val.function), { + message: 'Provide either file/line or function, not both.', + }) + .refine((val) => Boolean(val.function ?? (val.file && val.line !== undefined)), { + message: 'Provide file + line or function.', + }) + .refine((val) => !(val.line && !val.file), { + message: 'file is required when line is provided.', + }), +); + +export type DebugBreakpointAddParams = z.infer; + +export async function debug_breakpoint_addLogic( + params: DebugBreakpointAddParams, + ctx: DebuggerToolContext, +): Promise { + try { + const spec: BreakpointSpec = params.function + ? { kind: 'function', name: params.function } + : { kind: 'file-line', file: params.file!, line: params.line! }; + + const result = await ctx.debugger.addBreakpoint(params.debugSessionId, spec, { + condition: params.condition, + }); + + return createTextResponse(`✅ Breakpoint ${result.id} set.\n\n${result.rawOutput.trim()}`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to add breakpoint', message); + } +} + +export const schema = baseSchemaObject.shape; + +export const handler = createTypedToolWithContext( + debugBreakpointAddSchema as unknown as z.ZodType, + debug_breakpoint_addLogic, + getDefaultDebuggerToolContext, +); diff --git a/src/mcp/tools/debugging/debug_breakpoint_remove.ts b/src/mcp/tools/debugging/debug_breakpoint_remove.ts new file mode 100644 index 00000000..53e7b95d --- /dev/null +++ b/src/mcp/tools/debugging/debug_breakpoint_remove.ts @@ -0,0 +1,36 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse, createTextResponse } from '../../../utils/responses/index.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + type DebuggerToolContext, +} from '../../../utils/debugger/index.ts'; + +const debugBreakpointRemoveSchema = z.object({ + debugSessionId: z.string().optional().describe('default: current session'), + breakpointId: z.number().int().positive(), +}); + +export type DebugBreakpointRemoveParams = z.infer; + +export async function debug_breakpoint_removeLogic( + params: DebugBreakpointRemoveParams, + ctx: DebuggerToolContext, +): Promise { + try { + const output = await ctx.debugger.removeBreakpoint(params.debugSessionId, params.breakpointId); + return createTextResponse(`✅ Breakpoint ${params.breakpointId} removed.\n\n${output.trim()}`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to remove breakpoint', message); + } +} + +export const schema = debugBreakpointRemoveSchema.shape; + +export const handler = createTypedToolWithContext( + debugBreakpointRemoveSchema, + debug_breakpoint_removeLogic, + getDefaultDebuggerToolContext, +); diff --git a/src/mcp/tools/debugging/debug_continue.ts b/src/mcp/tools/debugging/debug_continue.ts new file mode 100644 index 00000000..4da697cd --- /dev/null +++ b/src/mcp/tools/debugging/debug_continue.ts @@ -0,0 +1,37 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse, createTextResponse } from '../../../utils/responses/index.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + type DebuggerToolContext, +} from '../../../utils/debugger/index.ts'; + +const debugContinueSchema = z.object({ + debugSessionId: z.string().optional().describe('default: current session'), +}); + +export type DebugContinueParams = z.infer; + +export async function debug_continueLogic( + params: DebugContinueParams, + ctx: DebuggerToolContext, +): Promise { + try { + const targetId = params.debugSessionId ?? ctx.debugger.getCurrentSessionId(); + await ctx.debugger.resumeSession(targetId ?? undefined); + + return createTextResponse(`✅ Resumed debugger session${targetId ? ` ${targetId}` : ''}.`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to resume debugger', message); + } +} + +export const schema = debugContinueSchema.shape; + +export const handler = createTypedToolWithContext( + debugContinueSchema, + debug_continueLogic, + getDefaultDebuggerToolContext, +); diff --git a/src/mcp/tools/debugging/debug_detach.ts b/src/mcp/tools/debugging/debug_detach.ts new file mode 100644 index 00000000..a1bb25ec --- /dev/null +++ b/src/mcp/tools/debugging/debug_detach.ts @@ -0,0 +1,37 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse, createTextResponse } from '../../../utils/responses/index.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + type DebuggerToolContext, +} from '../../../utils/debugger/index.ts'; + +const debugDetachSchema = z.object({ + debugSessionId: z.string().optional().describe('default: current session'), +}); + +export type DebugDetachParams = z.infer; + +export async function debug_detachLogic( + params: DebugDetachParams, + ctx: DebuggerToolContext, +): Promise { + try { + const targetId = params.debugSessionId ?? ctx.debugger.getCurrentSessionId(); + await ctx.debugger.detachSession(targetId ?? undefined); + + return createTextResponse(`✅ Detached debugger session${targetId ? ` ${targetId}` : ''}.`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to detach debugger', message); + } +} + +export const schema = debugDetachSchema.shape; + +export const handler = createTypedToolWithContext( + debugDetachSchema, + debug_detachLogic, + getDefaultDebuggerToolContext, +); diff --git a/src/mcp/tools/debugging/debug_lldb_command.ts b/src/mcp/tools/debugging/debug_lldb_command.ts new file mode 100644 index 00000000..7e34475e --- /dev/null +++ b/src/mcp/tools/debugging/debug_lldb_command.ts @@ -0,0 +1,42 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse, createTextResponse } from '../../../utils/responses/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + type DebuggerToolContext, +} from '../../../utils/debugger/index.ts'; + +const baseSchemaObject = z.object({ + debugSessionId: z.string().optional().describe('default: current session'), + command: z.string(), + timeoutMs: z.number().int().positive().optional(), +}); + +const debugLldbCommandSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); + +export type DebugLldbCommandParams = z.infer; + +export async function debug_lldb_commandLogic( + params: DebugLldbCommandParams, + ctx: DebuggerToolContext, +): Promise { + try { + const output = await ctx.debugger.runCommand(params.debugSessionId, params.command, { + timeoutMs: params.timeoutMs, + }); + return createTextResponse(output.trim()); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to run LLDB command', message); + } +} + +export const schema = baseSchemaObject.shape; + +export const handler = createTypedToolWithContext( + debugLldbCommandSchema as unknown as z.ZodType, + debug_lldb_commandLogic, + getDefaultDebuggerToolContext, +); diff --git a/src/mcp/tools/debugging/debug_stack.ts b/src/mcp/tools/debugging/debug_stack.ts new file mode 100644 index 00000000..46f149c6 --- /dev/null +++ b/src/mcp/tools/debugging/debug_stack.ts @@ -0,0 +1,40 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse, createTextResponse } from '../../../utils/responses/index.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + type DebuggerToolContext, +} from '../../../utils/debugger/index.ts'; + +const debugStackSchema = z.object({ + debugSessionId: z.string().optional().describe('default: current session'), + threadIndex: z.number().int().nonnegative().optional(), + maxFrames: z.number().int().positive().optional(), +}); + +export type DebugStackParams = z.infer; + +export async function debug_stackLogic( + params: DebugStackParams, + ctx: DebuggerToolContext, +): Promise { + try { + const output = await ctx.debugger.getStack(params.debugSessionId, { + threadIndex: params.threadIndex, + maxFrames: params.maxFrames, + }); + return createTextResponse(output.trim()); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to get stack', message); + } +} + +export const schema = debugStackSchema.shape; + +export const handler = createTypedToolWithContext( + debugStackSchema, + debug_stackLogic, + getDefaultDebuggerToolContext, +); diff --git a/src/mcp/tools/debugging/debug_variables.ts b/src/mcp/tools/debugging/debug_variables.ts new file mode 100644 index 00000000..7946b011 --- /dev/null +++ b/src/mcp/tools/debugging/debug_variables.ts @@ -0,0 +1,38 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse, createTextResponse } from '../../../utils/responses/index.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { + getDefaultDebuggerToolContext, + type DebuggerToolContext, +} from '../../../utils/debugger/index.ts'; + +const debugVariablesSchema = z.object({ + debugSessionId: z.string().optional().describe('default: current session'), + frameIndex: z.number().int().nonnegative().optional(), +}); + +export type DebugVariablesParams = z.infer; + +export async function debug_variablesLogic( + params: DebugVariablesParams, + ctx: DebuggerToolContext, +): Promise { + try { + const output = await ctx.debugger.getVariables(params.debugSessionId, { + frameIndex: params.frameIndex, + }); + return createTextResponse(output.trim()); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Failed to get variables', message); + } +} + +export const schema = debugVariablesSchema.shape; + +export const handler = createTypedToolWithContext( + debugVariablesSchema, + debug_variablesLogic, + getDefaultDebuggerToolContext, +); diff --git a/src/mcp/tools/device/__tests__/build_device.test.ts b/src/mcp/tools/device/__tests__/build_device.test.ts index b7205691..aa2153d9 100644 --- a/src/mcp/tools/device/__tests__/build_device.test.ts +++ b/src/mcp/tools/device/__tests__/build_device.test.ts @@ -6,8 +6,12 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; -import buildDevice, { buildDeviceLogic } from '../build_device.ts'; +import { + createMockCommandResponse, + createMockExecutor, + createNoopExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, buildDeviceLogic } from '../build_device.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('build_device plugin', () => { @@ -16,34 +20,28 @@ describe('build_device plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(buildDevice.name).toBe('build_device'); - }); - - it('should have correct description', () => { - expect(buildDevice.description).toBe('Builds an app for a connected device.'); - }); - it('should have handler function', () => { - expect(typeof buildDevice.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose only optional build-tuning fields in public schema', () => { - const schema = z.strictObject(buildDevice.schema); - expect(schema.safeParse({}).success).toBe(true); - expect( - schema.safeParse({ derivedDataPath: '/path/to/derived-data', extraArgs: [] }).success, - ).toBe(true); - expect(schema.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe(false); - - const schemaKeys = Object.keys(buildDevice.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild']); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ extraArgs: [] }).success).toBe(true); + expect(schemaObj.safeParse({ derivedDataPath: '/path/to/derived-data' }).success).toBe(false); + expect(schemaObj.safeParse({ preferXcodebuild: true }).success).toBe(false); + expect(schemaObj.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe( + false, + ); + + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs']); }); }); describe('XOR Validation', () => { it('should error when neither projectPath nor workspacePath provided', async () => { - const result = await buildDevice.handler({ + const result = await handler({ scheme: 'MyScheme', }); @@ -53,7 +51,7 @@ describe('build_device plugin', () => { }); it('should error when both projectPath and workspacePath provided', async () => { - const result = await buildDevice.handler({ + const result = await handler({ projectPath: '/path/to/MyProject.xcodeproj', workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', @@ -67,7 +65,7 @@ describe('build_device plugin', () => { describe('Parameter Validation (via Handler)', () => { it('should return Zod validation error for missing scheme', async () => { - const result = await buildDevice.handler({ + const result = await handler({ projectPath: '/path/to/MyProject.xcodeproj', }); @@ -77,7 +75,7 @@ describe('build_device plugin', () => { }); it('should return Zod validation error for invalid parameter types', async () => { - const result = await buildDevice.handler({ + const result = await handler({ projectPath: 123, // Should be string scheme: 'MyScheme', }); @@ -130,24 +128,24 @@ describe('build_device plugin', () => { it('should verify workspace command generation with mock executor', async () => { const commandCalls: Array<{ args: string[]; - logPrefix: string; - silent: boolean; - timeout: number | undefined; + logPrefix?: string; + silent?: boolean; + opts: { cwd?: string } | undefined; }> = []; const stubExecutor = async ( args: string[], - logPrefix: string, - silent: boolean, - timeout?: number, + logPrefix?: string, + silent?: boolean, + opts?: { cwd?: string }, + _detached?: boolean, ) => { - commandCalls.push({ args, logPrefix, silent, timeout }); - return { + commandCalls.push({ args, logPrefix, silent, opts }); + return createMockCommandResponse({ success: true, output: 'Build succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; await buildDeviceLogic( @@ -174,32 +172,32 @@ describe('build_device plugin', () => { 'build', ], logPrefix: 'iOS Device Build', - silent: true, - timeout: undefined, + silent: false, + opts: { cwd: '/path/to' }, }); }); it('should verify command generation with mock executor', async () => { const commandCalls: Array<{ args: string[]; - logPrefix: string; - silent: boolean; - timeout: number | undefined; + logPrefix?: string; + silent?: boolean; + opts: { cwd?: string } | undefined; }> = []; const stubExecutor = async ( args: string[], - logPrefix: string, - silent: boolean, - timeout?: number, + logPrefix?: string, + silent?: boolean, + opts?: { cwd?: string }, + _detached?: boolean, ) => { - commandCalls.push({ args, logPrefix, silent, timeout }); - return { + commandCalls.push({ args, logPrefix, silent, opts }); + return createMockCommandResponse({ success: true, output: 'Build succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; await buildDeviceLogic( @@ -226,8 +224,8 @@ describe('build_device plugin', () => { 'build', ], logPrefix: 'iOS Device Build', - silent: true, - timeout: undefined, + silent: false, + opts: { cwd: '/path/to' }, }); }); @@ -291,24 +289,24 @@ describe('build_device plugin', () => { it('should include optional parameters in command', async () => { const commandCalls: Array<{ args: string[]; - logPrefix: string; - silent: boolean; - timeout: number | undefined; + logPrefix?: string; + silent?: boolean; + opts: { cwd?: string } | undefined; }> = []; const stubExecutor = async ( args: string[], - logPrefix: string, - silent: boolean, - timeout?: number, + logPrefix?: string, + silent?: boolean, + opts?: { cwd?: string }, + _detached?: boolean, ) => { - commandCalls.push({ args, logPrefix, silent, timeout }); - return { + commandCalls.push({ args, logPrefix, silent, opts }); + return createMockCommandResponse({ success: true, output: 'Build succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; await buildDeviceLogic( @@ -341,8 +339,8 @@ describe('build_device plugin', () => { 'build', ], logPrefix: 'iOS Device Build', - silent: true, - timeout: undefined, + silent: false, + opts: { cwd: '/path/to' }, }); }); }); diff --git a/src/mcp/tools/device/__tests__/get_device_app_path.test.ts b/src/mcp/tools/device/__tests__/get_device_app_path.test.ts index 4557850f..231f07c7 100644 --- a/src/mcp/tools/device/__tests__/get_device_app_path.test.ts +++ b/src/mcp/tools/device/__tests__/get_device_app_path.test.ts @@ -6,8 +6,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; -import getDeviceAppPath, { get_device_app_pathLogic } from '../get_device_app_path.ts'; +import { + createMockCommandResponse, + createMockExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, get_device_app_pathLogic } from '../get_device_app_path.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('get_device_app_path plugin', () => { @@ -16,34 +19,26 @@ describe('get_device_app_path plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(getDeviceAppPath.name).toBe('get_device_app_path'); - }); - - it('should have correct description', () => { - expect(getDeviceAppPath.description).toBe( - 'Retrieves the built app path for a connected device.', - ); - }); - it('should have handler function', () => { - expect(typeof getDeviceAppPath.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); - it('should expose only platform in public schema', () => { - const schema = z.strictObject(getDeviceAppPath.schema); - expect(schema.safeParse({}).success).toBe(true); - expect(schema.safeParse({ platform: 'iOS' }).success).toBe(true); - expect(schema.safeParse({ projectPath: '/path/to/project.xcodeproj' }).success).toBe(false); + it('should expose empty public schema', () => { + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ platform: 'iOS' }).success).toBe(false); + expect(schemaObj.safeParse({ projectPath: '/path/to/project.xcodeproj' }).success).toBe( + false, + ); - const schemaKeys = Object.keys(getDeviceAppPath.schema).sort(); - expect(schemaKeys).toEqual(['platform']); + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual([]); }); }); describe('XOR Validation', () => { it('should error when neither projectPath nor workspacePath provided', async () => { - const result = await getDeviceAppPath.handler({ + const result = await handler({ scheme: 'MyScheme', }); expect(result.isError).toBe(true); @@ -52,7 +47,7 @@ describe('get_device_app_path plugin', () => { }); it('should error when both projectPath and workspacePath provided', async () => { - const result = await getDeviceAppPath.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', scheme: 'MyScheme', @@ -65,7 +60,7 @@ describe('get_device_app_path plugin', () => { describe('Handler Requirements', () => { it('should require scheme when missing', async () => { - const result = await getDeviceAppPath.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', }); expect(result.isError).toBe(true); @@ -76,7 +71,7 @@ describe('get_device_app_path plugin', () => { it('should require project or workspace when scheme default exists', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await getDeviceAppPath.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); }); @@ -88,26 +83,28 @@ describe('get_device_app_path plugin', () => { it('should generate correct xcodebuild command for iOS', async () => { const calls: Array<{ - args: any[]; - description: string; - suppressErrors: boolean; - workingDirectory: string | undefined; + args: string[]; + logPrefix?: string; + useShell?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor = ( - args: any[], - description: string, - suppressErrors: boolean, - workingDirectory: string | undefined, + args: string[], + logPrefix?: string, + useShell?: boolean, + opts?: { cwd?: string }, + _detached?: boolean, ) => { - calls.push({ args, description, suppressErrors, workingDirectory }); - return Promise.resolve({ - success: true, - output: - 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Debug-iphoneos\nFULL_PRODUCT_NAME = MyApp.app\n', - error: undefined, - process: { pid: 12345 }, - }); + calls.push({ args, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: + 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Debug-iphoneos\nFULL_PRODUCT_NAME = MyApp.app\n', + error: undefined, + }), + ); }; await get_device_app_pathLogic( @@ -132,34 +129,36 @@ describe('get_device_app_path plugin', () => { '-destination', 'generic/platform=iOS', ], - description: 'Get App Path', - suppressErrors: true, - workingDirectory: undefined, + logPrefix: 'Get App Path', + useShell: false, + opts: undefined, }); }); it('should generate correct xcodebuild command for watchOS', async () => { const calls: Array<{ - args: any[]; - description: string; - suppressErrors: boolean; - workingDirectory: string | undefined; + args: string[]; + logPrefix?: string; + useShell?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor = ( - args: any[], - description: string, - suppressErrors: boolean, - workingDirectory: string | undefined, + args: string[], + logPrefix?: string, + useShell?: boolean, + opts?: { cwd?: string }, + _detached?: boolean, ) => { - calls.push({ args, description, suppressErrors, workingDirectory }); - return Promise.resolve({ - success: true, - output: - 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Debug-watchos\nFULL_PRODUCT_NAME = MyApp.app\n', - error: undefined, - process: { pid: 12345 }, - }); + calls.push({ args, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: + 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Debug-watchos\nFULL_PRODUCT_NAME = MyApp.app\n', + error: undefined, + }), + ); }; await get_device_app_pathLogic( @@ -185,34 +184,36 @@ describe('get_device_app_path plugin', () => { '-destination', 'generic/platform=watchOS', ], - description: 'Get App Path', - suppressErrors: true, - workingDirectory: undefined, + logPrefix: 'Get App Path', + useShell: false, + opts: undefined, }); }); it('should generate correct xcodebuild command for workspace with iOS', async () => { const calls: Array<{ - args: any[]; - description: string; - suppressErrors: boolean; - workingDirectory: string | undefined; + args: string[]; + logPrefix?: string; + useShell?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor = ( - args: any[], - description: string, - suppressErrors: boolean, - workingDirectory: string | undefined, + args: string[], + logPrefix?: string, + useShell?: boolean, + opts?: { cwd?: string }, + _detached?: boolean, ) => { - calls.push({ args, description, suppressErrors, workingDirectory }); - return Promise.resolve({ - success: true, - output: - 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Debug-iphoneos\nFULL_PRODUCT_NAME = MyApp.app\n', - error: undefined, - process: { pid: 12345 }, - }); + calls.push({ args, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: + 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Debug-iphoneos\nFULL_PRODUCT_NAME = MyApp.app\n', + error: undefined, + }), + ); }; await get_device_app_pathLogic( @@ -237,9 +238,9 @@ describe('get_device_app_path plugin', () => { '-destination', 'generic/platform=iOS', ], - description: 'Get App Path', - suppressErrors: true, - workingDirectory: undefined, + logPrefix: 'Get App Path', + useShell: false, + opts: undefined, }); }); @@ -264,11 +265,15 @@ describe('get_device_app_path plugin', () => { type: 'text', text: '✅ App path retrieved successfully: /path/to/build/Debug-iphoneos/MyApp.app', }, - { - type: 'text', - text: 'Next Steps:\n1. Get bundle ID: get_app_bundle_id({ appPath: "/path/to/build/Debug-iphoneos/MyApp.app" })\n2. Install app on device: install_app_device({ deviceId: "DEVICE_UDID", appPath: "/path/to/build/Debug-iphoneos/MyApp.app" })\n3. Launch app on device: launch_app_device({ deviceId: "DEVICE_UDID", bundleId: "BUNDLE_ID" })', - }, ], + nextStepParams: { + get_app_bundle_id: { appPath: '/path/to/build/Debug-iphoneos/MyApp.app' }, + install_app_device: { + deviceId: 'DEVICE_UDID', + appPath: '/path/to/build/Debug-iphoneos/MyApp.app', + }, + launch_app_device: { deviceId: 'DEVICE_UDID', bundleId: 'BUNDLE_ID' }, + }, }); }); @@ -324,26 +329,28 @@ describe('get_device_app_path plugin', () => { it('should include optional configuration parameter in command', async () => { const calls: Array<{ - args: any[]; - description: string; - suppressErrors: boolean; - workingDirectory: string | undefined; + args: string[]; + logPrefix?: string; + useShell?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor = ( - args: any[], - description: string, - suppressErrors: boolean, - workingDirectory: string | undefined, + args: string[], + logPrefix?: string, + useShell?: boolean, + opts?: { cwd?: string }, + _detached?: boolean, ) => { - calls.push({ args, description, suppressErrors, workingDirectory }); - return Promise.resolve({ - success: true, - output: - 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Release-iphoneos\nFULL_PRODUCT_NAME = MyApp.app\n', - error: undefined, - process: { pid: 12345 }, - }); + calls.push({ args, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: + 'Build settings for scheme "MyScheme"\n\nBUILT_PRODUCTS_DIR = /path/to/build/Release-iphoneos\nFULL_PRODUCT_NAME = MyApp.app\n', + error: undefined, + }), + ); }; await get_device_app_pathLogic( @@ -369,14 +376,20 @@ describe('get_device_app_path plugin', () => { '-destination', 'generic/platform=iOS', ], - description: 'Get App Path', - suppressErrors: true, - workingDirectory: undefined, + logPrefix: 'Get App Path', + useShell: false, + opts: undefined, }); }); it('should return exact exception handling response', async () => { - const mockExecutor = () => { + const mockExecutor = ( + _args: string[], + _logPrefix?: string, + _useShell?: boolean, + _opts?: { cwd?: string }, + _detached?: boolean, + ) => { return Promise.reject(new Error('Network error')); }; diff --git a/src/mcp/tools/device/__tests__/index.test.ts b/src/mcp/tools/device/__tests__/index.test.ts deleted file mode 100644 index 0ca73e76..00000000 --- a/src/mcp/tools/device/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for device-project workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('device-project workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('iOS Device Development'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Build, test, deploy, and debug apps on real hardware.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/device/__tests__/install_app_device.test.ts b/src/mcp/tools/device/__tests__/install_app_device.test.ts index 224ecd0d..0806bb2e 100644 --- a/src/mcp/tools/device/__tests__/install_app_device.test.ts +++ b/src/mcp/tools/device/__tests__/install_app_device.test.ts @@ -7,7 +7,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; -import installAppDevice, { install_app_deviceLogic } from '../install_app_device.ts'; +import { schema, handler, install_app_deviceLogic } from '../install_app_device.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('install_app_device plugin', () => { @@ -17,7 +17,7 @@ describe('install_app_device plugin', () => { describe('Handler Requirements', () => { it('should require deviceId when session defaults are missing', async () => { - const result = await installAppDevice.handler({ + const result = await handler({ appPath: '/path/to/test.app', }); @@ -27,34 +27,26 @@ describe('install_app_device plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(installAppDevice.name).toBe('install_app_device'); - }); - - it('should have correct description', () => { - expect(installAppDevice.description).toBe('Installs an app on a connected device.'); - }); - it('should have handler function', () => { - expect(typeof installAppDevice.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should require appPath in public schema', () => { - const schema = z.strictObject(installAppDevice.schema); - expect(schema.safeParse({ appPath: '/path/to/test.app' }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ deviceId: 'test-device-123' }).success).toBe(false); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({ appPath: '/path/to/test.app' }).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({ deviceId: 'test-device-123' }).success).toBe(false); - expect(Object.keys(installAppDevice.schema)).toEqual(['appPath']); + expect(Object.keys(schema)).toEqual(['appPath']); }); }); describe('Command Generation', () => { it('should generate correct devicectl command with basic parameters', async () => { - let capturedCommand: unknown[] = []; + let capturedCommand: string[] = []; let capturedDescription: string = ''; let capturedUseShell: boolean = false; - let capturedEnv: unknown = undefined; + let capturedEnv: Record | undefined = undefined; const mockExecutor = createMockExecutor({ success: true, @@ -63,16 +55,17 @@ describe('install_app_device plugin', () => { }); const trackingExecutor = async ( - command: unknown[], - description: string, - useShell: boolean, - env: unknown, + command: string[], + description?: string, + useShell?: boolean, + opts?: { env?: Record }, + _detached?: boolean, ) => { capturedCommand = command; - capturedDescription = description; - capturedUseShell = useShell; - capturedEnv = env; - return mockExecutor(command, description, useShell, env); + capturedDescription = description ?? ''; + capturedUseShell = !!useShell; + capturedEnv = opts?.env; + return mockExecutor(command, description, useShell, opts, _detached); }; await install_app_deviceLogic( @@ -94,12 +87,12 @@ describe('install_app_device plugin', () => { '/path/to/test.app', ]); expect(capturedDescription).toBe('Install app on device'); - expect(capturedUseShell).toBe(true); + expect(capturedUseShell).toBe(false); expect(capturedEnv).toBe(undefined); }); it('should generate correct command with different device ID', async () => { - let capturedCommand: unknown[] = []; + let capturedCommand: string[] = []; const mockExecutor = createMockExecutor({ success: true, @@ -107,7 +100,7 @@ describe('install_app_device plugin', () => { process: { pid: 12345 }, }); - const trackingExecutor = async (command: unknown[]) => { + const trackingExecutor = async (command: string[]) => { capturedCommand = command; return mockExecutor(command); }; @@ -133,7 +126,7 @@ describe('install_app_device plugin', () => { }); it('should generate correct command with paths containing spaces', async () => { - let capturedCommand: unknown[] = []; + let capturedCommand: string[] = []; const mockExecutor = createMockExecutor({ success: true, @@ -141,7 +134,7 @@ describe('install_app_device plugin', () => { process: { pid: 12345 }, }); - const trackingExecutor = async (command: unknown[]) => { + const trackingExecutor = async (command: string[]) => { capturedCommand = command; return mockExecutor(command); }; diff --git a/src/mcp/tools/device/__tests__/launch_app_device.test.ts b/src/mcp/tools/device/__tests__/launch_app_device.test.ts index b0008996..5798fe76 100644 --- a/src/mcp/tools/device/__tests__/launch_app_device.test.ts +++ b/src/mcp/tools/device/__tests__/launch_app_device.test.ts @@ -9,8 +9,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; -import launchAppDevice, { launch_app_deviceLogic } from '../launch_app_device.ts'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, launch_app_deviceLogic } from '../launch_app_device.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('launch_app_device plugin (device-shared)', () => { @@ -19,38 +22,31 @@ describe('launch_app_device plugin (device-shared)', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(launchAppDevice.name).toBe('launch_app_device'); - }); - - it('should have correct description', () => { - expect(launchAppDevice.description).toBe('Launches an app on a connected device.'); - }); - it('should have handler function', () => { - expect(typeof launchAppDevice.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema with valid inputs', () => { - const schema = z.strictObject(launchAppDevice.schema); - expect(schema.safeParse({ bundleId: 'com.example.app' }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(false); - expect(Object.keys(launchAppDevice.schema)).toEqual(['bundleId']); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ bundleId: 'io.sentry.app' }).success).toBe(false); + expect(Object.keys(schema).sort()).toEqual(['env']); }); it('should validate schema with invalid inputs', () => { - const schema = z.strictObject(launchAppDevice.schema); - expect(schema.safeParse({ bundleId: null }).success).toBe(false); - expect(schema.safeParse({ bundleId: 123 }).success).toBe(false); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({ bundleId: null }).success).toBe(false); + expect(schemaObj.safeParse({ bundleId: 123 }).success).toBe(false); }); }); describe('Handler Requirements', () => { - it('should require deviceId when not provided', async () => { - const result = await launchAppDevice.handler({ bundleId: 'com.example.app' }); + it('should require deviceId and bundleId when not provided', async () => { + const result = await handler({}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('deviceId is required'); + expect(result.content[0].text).toContain('Missing required session defaults'); + expect(result.content[0].text).toContain('Provide deviceId and bundleId'); }); }); @@ -67,18 +63,20 @@ describe('launch_app_device plugin (device-shared)', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record }, + _detached?: boolean, ) => { - calls.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + calls.push({ command, logPrefix, useShell, env: opts?.env }); + return mockExecutor(command, logPrefix, useShell, opts, _detached); }; await launch_app_deviceLogic( { deviceId: 'test-device-123', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, trackingExecutor, + createMockFileSystemExecutor(), ); expect(calls).toHaveLength(1); @@ -93,10 +91,10 @@ describe('launch_app_device plugin (device-shared)', () => { '--json-output', expect.stringMatching(/^\/.*\/launch-\d+\.json$/), '--terminate-existing', - 'com.example.app', + 'io.sentry.app', ]); expect(calls[0].logPrefix).toBe('Launch app on device'); - expect(calls[0].useShell).toBe(true); + expect(calls[0].useShell).toBe(false); expect(calls[0].env).toBeUndefined(); }); @@ -119,6 +117,7 @@ describe('launch_app_device plugin (device-shared)', () => { bundleId: 'com.apple.mobilesafari', }, trackingExecutor, + createMockFileSystemExecutor(), ); expect(calls[0].command).toEqual([ @@ -135,6 +134,67 @@ describe('launch_app_device plugin (device-shared)', () => { 'com.apple.mobilesafari', ]); }); + + it('should append a JSON --environment-variables payload before bundleId when env is provided', async () => { + const calls: any[] = []; + const mockExecutor = createMockExecutor({ + success: true, + output: 'App launched successfully', + process: { pid: 12345 }, + }); + + const trackingExecutor = async (command: string[]) => { + calls.push({ command }); + return mockExecutor(command); + }; + + await launch_app_deviceLogic( + { + deviceId: 'test-device-123', + bundleId: 'io.sentry.app', + env: { STAGING_ENABLED: '1', DEBUG: 'true' }, + }, + trackingExecutor, + createMockFileSystemExecutor(), + ); + + expect(calls).toHaveLength(1); + const cmd = calls[0].command; + // bundleId should be the last element + expect(cmd[cmd.length - 1]).toBe('io.sentry.app'); + // --environment-variables should be provided exactly once as JSON + const envFlagIndices = cmd + .map((part: string, index: number) => (part === '--environment-variables' ? index : -1)) + .filter((index: number) => index >= 0); + expect(envFlagIndices).toHaveLength(1); + const envIdx = envFlagIndices[0]; + expect(JSON.parse(cmd[envIdx + 1])).toEqual({ STAGING_ENABLED: '1', DEBUG: 'true' }); + }); + + it('should not include --environment-variables when env is not provided', async () => { + const calls: any[] = []; + const mockExecutor = createMockExecutor({ + success: true, + output: 'App launched successfully', + process: { pid: 12345 }, + }); + + const trackingExecutor = async (command: string[]) => { + calls.push({ command }); + return mockExecutor(command); + }; + + await launch_app_deviceLogic( + { + deviceId: 'test-device-123', + bundleId: 'io.sentry.app', + }, + trackingExecutor, + createMockFileSystemExecutor(), + ); + + expect(calls[0].command).not.toContain('--environment-variables'); + }); }); describe('Success Path Tests', () => { @@ -147,9 +207,10 @@ describe('launch_app_device plugin (device-shared)', () => { const result = await launch_app_deviceLogic( { deviceId: 'test-device-123', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, mockExecutor, + createMockFileSystemExecutor(), ); expect(result).toEqual({ @@ -171,9 +232,10 @@ describe('launch_app_device plugin (device-shared)', () => { const result = await launch_app_deviceLogic( { deviceId: 'test-device-123', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, mockExecutor, + createMockFileSystemExecutor(), ); expect(result).toEqual({ @@ -187,31 +249,17 @@ describe('launch_app_device plugin (device-shared)', () => { }); it('should handle successful launch with process ID information', async () => { - // Mock fs operations for JSON parsing - const fs = await import('fs'); - const originalReadFile = fs.promises.readFile; - const originalUnlink = fs.promises.unlink; - - const mockReadFile = (path: string) => { - if (path.includes('launch-')) { - return Promise.resolve( - JSON.stringify({ - result: { - process: { - processIdentifier: 12345, - }, + const mockFileSystem = createMockFileSystemExecutor({ + readFile: async () => + JSON.stringify({ + result: { + process: { + processIdentifier: 12345, }, - }), - ); - } - return originalReadFile(path); - }; - - const mockUnlink = () => Promise.resolve(); - - // Replace fs methods - fs.promises.readFile = mockReadFile; - fs.promises.unlink = mockUnlink; + }, + }), + rm: async () => {}, + }); const mockExecutor = createMockExecutor({ success: true, @@ -221,44 +269,45 @@ describe('launch_app_device plugin (device-shared)', () => { const result = await launch_app_deviceLogic( { deviceId: 'test-device-123', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, mockExecutor, + mockFileSystem, ); - // Restore fs methods - fs.promises.readFile = originalReadFile; - fs.promises.unlink = originalUnlink; - expect(result).toEqual({ content: [ { type: 'text', - text: '✅ App launched successfully\n\nApp launched successfully\n\nProcess ID: 12345\n\nNext Steps:\n1. Interact with your app on the device\n2. Stop the app: stop_app_device({ deviceId: "test-device-123", processId: 12345 })', + text: '✅ App launched successfully\n\nApp launched successfully\n\nProcess ID: 12345\n\nInteract with your app on the device.', }, ], + nextStepParams: { + stop_app_device: { deviceId: 'test-device-123', processId: 12345 }, + }, }); }); it('should handle successful launch with command output', async () => { const mockExecutor = createMockExecutor({ success: true, - output: 'App "com.example.app" launched on device "test-device-123"', + output: 'App "io.sentry.app" launched on device "test-device-123"', }); const result = await launch_app_deviceLogic( { deviceId: 'test-device-123', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, mockExecutor, + createMockFileSystemExecutor(), ); expect(result).toEqual({ content: [ { type: 'text', - text: '✅ App launched successfully\n\nApp "com.example.app" launched on device "test-device-123"', + text: '✅ App launched successfully\n\nApp "io.sentry.app" launched on device "test-device-123"', }, ], }); @@ -278,6 +327,7 @@ describe('launch_app_device plugin (device-shared)', () => { bundleId: 'com.nonexistent.app', }, mockExecutor, + createMockFileSystemExecutor(), ); expect(result).toEqual({ @@ -300,9 +350,10 @@ describe('launch_app_device plugin (device-shared)', () => { const result = await launch_app_deviceLogic( { deviceId: 'test-device-invalid', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, mockExecutor, + createMockFileSystemExecutor(), ); expect(result).toEqual({ @@ -322,9 +373,10 @@ describe('launch_app_device plugin (device-shared)', () => { const result = await launch_app_deviceLogic( { deviceId: 'test-device-123', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, mockExecutor, + createMockFileSystemExecutor(), ); expect(result).toEqual({ @@ -344,9 +396,10 @@ describe('launch_app_device plugin (device-shared)', () => { const result = await launch_app_deviceLogic( { deviceId: 'test-device-123', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }, mockExecutor, + createMockFileSystemExecutor(), ); expect(result).toEqual({ diff --git a/src/mcp/tools/device/__tests__/list_devices.test.ts b/src/mcp/tools/device/__tests__/list_devices.test.ts index 588cb4f7..d3769a79 100644 --- a/src/mcp/tools/device/__tests__/list_devices.test.ts +++ b/src/mcp/tools/device/__tests__/list_devices.test.ts @@ -8,12 +8,12 @@ import { describe, it, expect } from 'vitest'; import { + createMockCommandResponse, createMockExecutor, - createMockFileSystemExecutor, } from '../../../../test-utils/mock-executors.ts'; -// Import the logic function and re-export -import listDevices, { list_devicesLogic } from '../list_devices.ts'; +// Import the logic function and named exports +import { schema, handler, list_devicesLogic } from '../list_devices.ts'; describe('list_devices plugin (device-shared)', () => { describe('Export Field Validation (Literal)', () => { @@ -21,22 +21,12 @@ describe('list_devices plugin (device-shared)', () => { expect(typeof list_devicesLogic).toBe('function'); }); - it('should have correct name', () => { - expect(listDevices.name).toBe('list_devices'); - }); - - it('should have correct description', () => { - expect(listDevices.description).toBe( - 'Lists connected physical Apple devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) with their UUIDs, names, and connection status. Use this to discover physical devices for testing.', - ); - }); - it('should have handler function', () => { - expect(typeof listDevices.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have empty schema', () => { - expect(listDevices.schema).toEqual({}); + expect(schema).toEqual({}); }); }); @@ -85,10 +75,11 @@ describe('list_devices plugin (device-shared)', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record }, + _detached?: boolean, ) => { - commandCalls.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + commandCalls.push({ command, logPrefix, useShell, env: opts?.env }); + return mockExecutor(command, logPrefix, useShell, opts, _detached); }; // Create mock path dependencies @@ -98,10 +89,10 @@ describe('list_devices plugin (device-shared)', () => { }; // Create mock filesystem with specific behavior - const mockFsDeps = createMockFileSystemExecutor({ - readFile: async () => JSON.stringify(devicectlJson), + const mockFsDeps = { + readFile: async (_path: string, _encoding?: string) => JSON.stringify(devicectlJson), unlink: async () => {}, - }); + }; await list_devicesLogic({}, trackingExecutor, mockPathDeps, mockFsDeps); @@ -115,7 +106,7 @@ describe('list_devices plugin (device-shared)', () => { '/tmp/devicectl-123.json', ]); expect(commandCalls[0].logPrefix).toBe('List Devices (devicectl with JSON)'); - expect(commandCalls[0].useShell).toBe(true); + expect(commandCalls[0].useShell).toBe(false); expect(commandCalls[0].env).toBeUndefined(); }); @@ -134,27 +125,26 @@ describe('list_devices plugin (device-shared)', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record }, + _detached?: boolean, ) => { callCount++; - commandCalls.push({ command, logPrefix, useShell, env }); + commandCalls.push({ command, logPrefix, useShell, env: opts?.env }); if (callCount === 1) { // First call fails (devicectl) - return { + return createMockCommandResponse({ success: false, output: '', error: 'devicectl failed', - process: { pid: 12345 }, - }; + }); } else { // Second call succeeds (xctrace) - return { + return createMockCommandResponse({ success: true, output: 'iPhone 15 (12345678-1234-1234-1234-123456789012)', error: undefined, - process: { pid: 12345 }, - }; + }); } }; @@ -165,19 +155,19 @@ describe('list_devices plugin (device-shared)', () => { }; // Create mock filesystem that throws for readFile - const mockFsDeps = createMockFileSystemExecutor({ + const mockFsDeps = { readFile: async () => { throw new Error('File not found'); }, unlink: async () => {}, - }); + }; await list_devicesLogic({}, trackingExecutor, mockPathDeps, mockFsDeps); expect(commandCalls).toHaveLength(2); expect(commandCalls[1].command).toEqual(['xcrun', 'xctrace', 'list', 'devices']); expect(commandCalls[1].logPrefix).toBe('List Devices (xctrace)'); - expect(commandCalls[1].useShell).toBe(true); + expect(commandCalls[1].useShell).toBe(false); expect(commandCalls[1].env).toBeUndefined(); }); }); @@ -220,10 +210,10 @@ describe('list_devices plugin (device-shared)', () => { }; // Create mock filesystem with specific behavior - const mockFsDeps = createMockFileSystemExecutor({ - readFile: async () => JSON.stringify(devicectlJson), + const mockFsDeps = { + readFile: async (_path: string, _encoding?: string) => JSON.stringify(devicectlJson), unlink: async () => {}, - }); + }; const result = await list_devicesLogic({}, mockExecutor, mockPathDeps, mockFsDeps); @@ -231,9 +221,14 @@ describe('list_devices plugin (device-shared)', () => { content: [ { type: 'text', - text: "Connected Devices:\n\n✅ Available Devices:\n\n📱 Test iPhone\n UDID: test-device-123\n Model: iPhone15,2\n Product Type: iPhone15,2\n Platform: iOS 17.0\n Connection: USB\n\nNext Steps:\n1. Build for device: build_device({ scheme: 'SCHEME', deviceId: 'DEVICE_UDID' })\n2. Run tests: test_device({ scheme: 'SCHEME', deviceId: 'DEVICE_UDID' })\n3. Get app path: get_device_app_path({ scheme: 'SCHEME' })\n\nNote: Use the device ID/UDID from above when required by other tools.\n", + text: "Connected Devices:\n\n✅ Available Devices:\n\n📱 Test iPhone\n UDID: test-device-123\n Model: iPhone15,2\n Product Type: iPhone15,2\n Platform: iOS 17.0\n Connection: USB\n\nNote: Use the device ID/UDID from above when required by other tools.\nHint: Save a default device with session-set-defaults { deviceId: 'DEVICE_UDID' }.\n", }, ], + nextStepParams: { + build_device: { scheme: 'SCHEME', deviceId: 'DEVICE_UDID' }, + test_device: { scheme: 'SCHEME', deviceId: 'DEVICE_UDID' }, + get_device_app_path: { scheme: 'SCHEME' }, + }, }); }); @@ -241,28 +236,27 @@ describe('list_devices plugin (device-shared)', () => { // Create executor with call count behavior let callCount = 0; const mockExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, + _command: string[], + _logPrefix?: string, + _useShell?: boolean, + _opts?: { env?: Record }, + _detached?: boolean, ) => { callCount++; if (callCount === 1) { // First call fails (devicectl) - return { + return createMockCommandResponse({ success: false, output: '', error: 'devicectl failed', - process: { pid: 12345 }, - }; + }); } else { // Second call succeeds (xctrace) - return { + return createMockCommandResponse({ success: true, output: 'iPhone 15 (12345678-1234-1234-1234-123456789012)', error: undefined, - process: { pid: 12345 }, - }; + }); } }; @@ -273,12 +267,12 @@ describe('list_devices plugin (device-shared)', () => { }; // Create mock filesystem that throws for readFile - const mockFsDeps = createMockFileSystemExecutor({ + const mockFsDeps = { readFile: async () => { throw new Error('File not found'); }, unlink: async () => {}, - }); + }; const result = await list_devicesLogic({}, mockExecutor, mockPathDeps, mockFsDeps); @@ -302,28 +296,27 @@ describe('list_devices plugin (device-shared)', () => { // Create executor with call count behavior let callCount = 0; const mockExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, + _command: string[], + _logPrefix?: string, + _useShell?: boolean, + _opts?: { env?: Record }, + _detached?: boolean, ) => { callCount++; if (callCount === 1) { // First call succeeds (devicectl) - return { + return createMockCommandResponse({ success: true, output: '', error: undefined, - process: { pid: 12345 }, - }; + }); } else { // Second call succeeds (xctrace) with empty output - return { + return createMockCommandResponse({ success: true, output: '', error: undefined, - process: { pid: 12345 }, - }; + }); } }; @@ -334,10 +327,10 @@ describe('list_devices plugin (device-shared)', () => { }; // Create mock filesystem with empty devices response - const mockFsDeps = createMockFileSystemExecutor({ - readFile: async () => JSON.stringify(devicectlJson), + const mockFsDeps = { + readFile: async (_path: string, _encoding?: string) => JSON.stringify(devicectlJson), unlink: async () => {}, - }); + }; const result = await list_devicesLogic({}, mockExecutor, mockPathDeps, mockFsDeps); diff --git a/src/mcp/tools/device/__tests__/re-exports.test.ts b/src/mcp/tools/device/__tests__/re-exports.test.ts index 000b88bb..def310cd 100644 --- a/src/mcp/tools/device/__tests__/re-exports.test.ts +++ b/src/mcp/tools/device/__tests__/re-exports.test.ts @@ -1,88 +1,63 @@ /** - * Tests for device-project re-export files - * These files re-export tools from device-workspace to avoid duplication + * Tests for device tool named exports + * Verifies that device tools export schema and handler as named exports */ import { describe, it, expect } from 'vitest'; -// Import all re-export tools -import launchAppDevice from '../launch_app_device.ts'; -import stopAppDevice from '../stop_app_device.ts'; -import listDevices from '../list_devices.ts'; -import installAppDevice from '../install_app_device.ts'; +// Import all tools as modules to check named exports +import * as launchAppDevice from '../launch_app_device.ts'; +import * as stopAppDevice from '../stop_app_device.ts'; +import * as listDevices from '../list_devices.ts'; +import * as installAppDevice from '../install_app_device.ts'; -describe('device-project re-exports', () => { - describe('launch_app_device re-export', () => { - it('should re-export launch_app_device tool correctly', () => { - expect(launchAppDevice.name).toBe('launch_app_device'); - expect(typeof launchAppDevice.handler).toBe('function'); +describe('device tool named exports', () => { + describe('launch_app_device exports', () => { + it('should export schema and handler', () => { expect(launchAppDevice.schema).toBeDefined(); - expect(typeof launchAppDevice.description).toBe('string'); + expect(typeof launchAppDevice.handler).toBe('function'); }); }); - describe('stop_app_device re-export', () => { - it('should re-export stop_app_device tool correctly', () => { - expect(stopAppDevice.name).toBe('stop_app_device'); - expect(typeof stopAppDevice.handler).toBe('function'); + describe('stop_app_device exports', () => { + it('should export schema and handler', () => { expect(stopAppDevice.schema).toBeDefined(); - expect(typeof stopAppDevice.description).toBe('string'); + expect(typeof stopAppDevice.handler).toBe('function'); }); }); - describe('list_devices re-export', () => { - it('should re-export list_devices tool correctly', () => { - expect(listDevices.name).toBe('list_devices'); - expect(typeof listDevices.handler).toBe('function'); + describe('list_devices exports', () => { + it('should export schema and handler', () => { expect(listDevices.schema).toBeDefined(); - expect(typeof listDevices.description).toBe('string'); + expect(typeof listDevices.handler).toBe('function'); }); }); - describe('install_app_device re-export', () => { - it('should re-export install_app_device tool correctly', () => { - expect(installAppDevice.name).toBe('install_app_device'); - expect(typeof installAppDevice.handler).toBe('function'); + describe('install_app_device exports', () => { + it('should export schema and handler', () => { expect(installAppDevice.schema).toBeDefined(); - expect(typeof installAppDevice.description).toBe('string'); + expect(typeof installAppDevice.handler).toBe('function'); }); }); - describe('All re-exports validation', () => { - const reExports = [ - { tool: launchAppDevice, name: 'launch_app_device' }, - { tool: stopAppDevice, name: 'stop_app_device' }, - { tool: listDevices, name: 'list_devices' }, - { tool: installAppDevice, name: 'install_app_device' }, + describe('All exports validation', () => { + const modules = [ + { mod: launchAppDevice, name: 'launch_app_device' }, + { mod: stopAppDevice, name: 'stop_app_device' }, + { mod: listDevices, name: 'list_devices' }, + { mod: installAppDevice, name: 'install_app_device' }, ]; - it('should have all required tool properties', () => { - reExports.forEach(({ tool, name }) => { - expect(tool).toHaveProperty('name'); - expect(tool).toHaveProperty('description'); - expect(tool).toHaveProperty('schema'); - expect(tool).toHaveProperty('handler'); - expect(tool.name).toBe(name); - }); - }); - it('should have callable handlers', () => { - reExports.forEach(({ tool, name }) => { - expect(typeof tool.handler).toBe('function'); - expect(tool.handler.length).toBeGreaterThanOrEqual(0); + modules.forEach(({ mod }) => { + expect(typeof mod.handler).toBe('function'); + expect(mod.handler.length).toBeGreaterThanOrEqual(0); }); }); it('should have valid schemas', () => { - reExports.forEach(({ tool, name }) => { - expect(tool.schema).toBeDefined(); - expect(typeof tool.schema).toBe('object'); - }); - }); - - it('should have non-empty descriptions', () => { - reExports.forEach(({ tool, name }) => { - expect(typeof tool.description).toBe('string'); - expect(tool.description.length).toBeGreaterThan(0); + modules.forEach(({ mod }) => { + expect(mod.schema).toBeDefined(); + expect(typeof mod.schema).toBe('object'); }); }); }); diff --git a/src/mcp/tools/device/__tests__/stop_app_device.test.ts b/src/mcp/tools/device/__tests__/stop_app_device.test.ts index 8fcd0f76..0ae186c4 100644 --- a/src/mcp/tools/device/__tests__/stop_app_device.test.ts +++ b/src/mcp/tools/device/__tests__/stop_app_device.test.ts @@ -7,7 +7,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; -import stopAppDevice, { stop_app_deviceLogic } from '../stop_app_device.ts'; +import { schema, handler, stop_app_deviceLogic } from '../stop_app_device.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('stop_app_device plugin', () => { @@ -16,31 +16,23 @@ describe('stop_app_device plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(stopAppDevice.name).toBe('stop_app_device'); - }); - - it('should have correct description', () => { - expect(stopAppDevice.description).toBe('Stops a running app on a connected device.'); - }); - it('should have handler function', () => { - expect(typeof stopAppDevice.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should require processId in public schema', () => { - const schema = z.strictObject(stopAppDevice.schema); - expect(schema.safeParse({ processId: 12345 }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ deviceId: 'test-device-123' }).success).toBe(false); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({ processId: 12345 }).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({ deviceId: 'test-device-123' }).success).toBe(false); - expect(Object.keys(stopAppDevice.schema)).toEqual(['processId']); + expect(Object.keys(schema)).toEqual(['processId']); }); }); describe('Handler Requirements', () => { it('should require deviceId when not provided', async () => { - const result = await stopAppDevice.handler({ processId: 12345 }); + const result = await handler({ processId: 12345 }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('deviceId is required'); @@ -49,10 +41,10 @@ describe('stop_app_device plugin', () => { describe('Command Generation', () => { it('should generate correct devicectl command with basic parameters', async () => { - let capturedCommand: unknown[] = []; + let capturedCommand: string[] = []; let capturedDescription: string = ''; let capturedUseShell: boolean = false; - let capturedEnv: unknown = undefined; + let capturedEnv: Record | undefined = undefined; const mockExecutor = createMockExecutor({ success: true, @@ -61,16 +53,17 @@ describe('stop_app_device plugin', () => { }); const trackingExecutor = async ( - command: unknown[], - description: string, - useShell: boolean, - env: unknown, + command: string[], + description?: string, + useShell?: boolean, + opts?: { env?: Record }, + _detached?: boolean, ) => { capturedCommand = command; - capturedDescription = description; - capturedUseShell = useShell; - capturedEnv = env; - return mockExecutor(command, description, useShell, env); + capturedDescription = description ?? ''; + capturedUseShell = !!useShell; + capturedEnv = opts?.env; + return mockExecutor(command, description, useShell, opts, _detached); }; await stop_app_deviceLogic( @@ -93,12 +86,12 @@ describe('stop_app_device plugin', () => { '12345', ]); expect(capturedDescription).toBe('Stop app on device'); - expect(capturedUseShell).toBe(true); + expect(capturedUseShell).toBe(false); expect(capturedEnv).toBe(undefined); }); it('should generate correct command with different device ID and process ID', async () => { - let capturedCommand: unknown[] = []; + let capturedCommand: string[] = []; const mockExecutor = createMockExecutor({ success: true, @@ -106,7 +99,7 @@ describe('stop_app_device plugin', () => { process: { pid: 12345 }, }); - const trackingExecutor = async (command: unknown[]) => { + const trackingExecutor = async (command: string[]) => { capturedCommand = command; return mockExecutor(command); }; @@ -133,7 +126,7 @@ describe('stop_app_device plugin', () => { }); it('should generate correct command with large process ID', async () => { - let capturedCommand: unknown[] = []; + let capturedCommand: string[] = []; const mockExecutor = createMockExecutor({ success: true, @@ -141,7 +134,7 @@ describe('stop_app_device plugin', () => { process: { pid: 12345 }, }); - const trackingExecutor = async (command: unknown[]) => { + const trackingExecutor = async (command: string[]) => { capturedCommand = command; return mockExecutor(command); }; diff --git a/src/mcp/tools/device/__tests__/test_device.test.ts b/src/mcp/tools/device/__tests__/test_device.test.ts index 17f206d5..1179dda4 100644 --- a/src/mcp/tools/device/__tests__/test_device.test.ts +++ b/src/mcp/tools/device/__tests__/test_device.test.ts @@ -8,10 +8,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { + createMockCommandResponse, createMockExecutor, createMockFileSystemExecutor, } from '../../../../test-utils/mock-executors.ts'; -import testDevice, { testDeviceLogic } from '../test_device.ts'; +import { schema, handler, testDeviceLogic } from '../test_device.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('test_device plugin', () => { @@ -20,40 +21,28 @@ describe('test_device plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(testDevice.name).toBe('test_device'); - }); - - it('should have correct description', () => { - expect(testDevice.description).toBe('Runs tests on a physical Apple device.'); - }); - it('should have handler function', () => { - expect(typeof testDevice.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose only session-free fields in public schema', () => { - const schema = z.strictObject(testDevice.schema); + const schemaObj = z.strictObject(schema); expect( - schema.safeParse({ - derivedDataPath: '/path/to/derived-data', + schemaObj.safeParse({ extraArgs: ['--arg1'], - preferXcodebuild: true, - platform: 'iOS', testRunnerEnv: { FOO: 'bar' }, }).success, ).toBe(true); - expect(schema.safeParse({}).success).toBe(true); - expect(schema.safeParse({ projectPath: '/path/to/project.xcodeproj' }).success).toBe(false); - - const schemaKeys = Object.keys(testDevice.schema).sort(); - expect(schemaKeys).toEqual([ - 'derivedDataPath', - 'extraArgs', - 'platform', - 'preferXcodebuild', - 'testRunnerEnv', - ]); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ derivedDataPath: '/path/to/derived-data' }).success).toBe(false); + expect(schemaObj.safeParse({ preferXcodebuild: true }).success).toBe(false); + expect(schemaObj.safeParse({ platform: 'iOS' }).success).toBe(false); + expect(schemaObj.safeParse({ projectPath: '/path/to/project.xcodeproj' }).success).toBe( + false, + ); + + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs', 'testRunnerEnv']); }); it('should validate XOR between projectPath and workspacePath', async () => { @@ -83,7 +72,7 @@ describe('test_device plugin', () => { createMockFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-123', tmpdir: () => '/tmp', - stat: async () => ({ isFile: () => true }), + stat: async () => ({ isDirectory: () => false, mtimeMs: 0 }), rm: async () => {}, }), ); @@ -100,7 +89,7 @@ describe('test_device plugin', () => { createMockFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-456', tmpdir: () => '/tmp', - stat: async () => ({ isFile: () => true }), + stat: async () => ({ isDirectory: () => false, mtimeMs: 0 }), rm: async () => {}, }), ); @@ -110,7 +99,7 @@ describe('test_device plugin', () => { describe('Handler Requirements', () => { it('should require scheme and device defaults', async () => { - const result = await testDevice.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -120,7 +109,7 @@ describe('test_device plugin', () => { it('should require project or workspace when defaults provide scheme and device', async () => { sessionStore.setDefaults({ scheme: 'MyScheme', deviceId: 'test-device-123' }); - const result = await testDevice.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); @@ -129,7 +118,7 @@ describe('test_device plugin', () => { it('should reject mutually exclusive project inputs when defaults satisfy requirements', async () => { sessionStore.setDefaults({ scheme: 'MyScheme', deviceId: 'test-device-123' }); - const result = await testDevice.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', }); @@ -173,7 +162,7 @@ describe('test_device plugin', () => { createMockFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-123456', tmpdir: () => '/tmp', - stat: async () => ({ isFile: () => true }), + stat: async () => ({ isDirectory: () => false, mtimeMs: 0 }), rm: async () => {}, }), ); @@ -219,7 +208,7 @@ describe('test_device plugin', () => { createMockFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-123456', tmpdir: () => '/tmp', - stat: async () => ({ isFile: () => true }), + stat: async () => ({ isDirectory: () => false, mtimeMs: 0 }), rm: async () => {}, }), ); @@ -232,16 +221,22 @@ describe('test_device plugin', () => { it('should handle xcresult parsing failures gracefully', async () => { // Create a multi-call mock that handles different commands let callCount = 0; - const mockExecutor = async (args: string[], description: string) => { + const mockExecutor = async ( + _args: string[], + _description?: string, + _useShell?: boolean, + _opts?: { cwd?: string }, + _detached?: boolean, + ) => { callCount++; // First call is for xcodebuild test (successful) if (callCount === 1) { - return { success: true, output: 'BUILD SUCCEEDED' }; + return createMockCommandResponse({ success: true, output: 'BUILD SUCCEEDED' }); } // Second call is for xcresulttool (fails) - return { success: false, error: 'xcresulttool failed' }; + return createMockCommandResponse({ success: false, error: 'xcresulttool failed' }); }; const result = await testDeviceLogic( @@ -297,7 +292,7 @@ describe('test_device plugin', () => { createMockFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-123456', tmpdir: () => '/tmp', - stat: async () => ({ isFile: () => true }), + stat: async () => ({ isDirectory: () => false, mtimeMs: 0 }), rm: async () => {}, }), ); @@ -336,7 +331,7 @@ describe('test_device plugin', () => { createMockFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-123456', tmpdir: () => '/tmp', - stat: async () => ({ isFile: () => true }), + stat: async () => ({ isDirectory: () => false, mtimeMs: 0 }), rm: async () => {}, }), ); @@ -373,7 +368,7 @@ describe('test_device plugin', () => { createMockFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-workspace-123', tmpdir: () => '/tmp', - stat: async () => ({ isFile: () => true }), + stat: async () => ({ isDirectory: () => false, mtimeMs: 0 }), rm: async () => {}, }), ); diff --git a/src/mcp/tools/device/build_device.ts b/src/mcp/tools/device/build_device.ts index d4bb6cc9..fd649122 100644 --- a/src/mcp/tools/device/build_device.ts +++ b/src/mcp/tools/device/build_device.ts @@ -6,7 +6,8 @@ */ import * as z from 'zod'; -import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; @@ -22,9 +23,9 @@ const baseSchemaObject = z.object({ workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), scheme: z.string().describe('The scheme to build'), configuration: z.string().optional().describe('Build configuration (Debug, Release)'), - derivedDataPath: z.string().optional().describe('Path to derived data directory'), - extraArgs: z.array(z.string()).optional().describe('Additional arguments to pass to xcodebuild'), - preferXcodebuild: z.boolean().optional().describe('Prefer xcodebuild over faster alternatives'), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), + preferXcodebuild: z.boolean().optional(), }); const buildDeviceSchema = z.preprocess( @@ -45,6 +46,8 @@ const publicSchemaObject = baseSchemaObject.omit({ workspacePath: true, scheme: true, configuration: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); /** @@ -72,25 +75,18 @@ export async function buildDeviceLogic( ); } -export default { - name: 'build_device', - description: 'Builds an app for a connected device.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Build Device', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: buildDeviceSchema as unknown as z.ZodType, - logicFunction: buildDeviceLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: buildDeviceSchema as unknown as z.ZodType, + logicFunction: buildDeviceLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/device/clean.ts b/src/mcp/tools/device/clean.ts deleted file mode 100644 index 552c9c17..00000000 --- a/src/mcp/tools/device/clean.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified clean tool for device-project workflow -export { default } from '../utilities/clean.ts'; diff --git a/src/mcp/tools/device/discover_projs.ts b/src/mcp/tools/device/discover_projs.ts deleted file mode 100644 index 58fbf05d..00000000 --- a/src/mcp/tools/device/discover_projs.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/discover_projs.ts'; diff --git a/src/mcp/tools/device/get_app_bundle_id.ts b/src/mcp/tools/device/get_app_bundle_id.ts deleted file mode 100644 index 6c0bfc0d..00000000 --- a/src/mcp/tools/device/get_app_bundle_id.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/get_app_bundle_id.ts'; diff --git a/src/mcp/tools/device/get_device_app_path.ts b/src/mcp/tools/device/get_device_app_path.ts index 63937333..2cad6f51 100644 --- a/src/mcp/tools/device/get_device_app_path.ts +++ b/src/mcp/tools/device/get_device_app_path.ts @@ -6,7 +6,8 @@ */ import * as z from 'zod'; -import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; @@ -21,10 +22,7 @@ import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; const baseOptions = { scheme: z.string().describe('The scheme to use'), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - platform: z - .enum(['iOS', 'watchOS', 'tvOS', 'visionOS']) - .optional() - .describe('Target platform (defaults to iOS)'), + platform: z.enum(['iOS', 'watchOS', 'tvOS', 'visionOS']).optional().describe('default: iOS'), }; const baseSchemaObject = z.object({ @@ -52,6 +50,7 @@ const publicSchemaObject = baseSchemaObject.omit({ workspacePath: true, scheme: true, configuration: true, + platform: true, } as const); export async function get_device_app_pathLogic( @@ -88,25 +87,23 @@ export async function get_device_app_pathLogic( command.push('-scheme', params.scheme); command.push('-configuration', configuration); - // Handle destination based on platform - let destinationString = ''; - - if (platform === XcodePlatform.iOS) { - destinationString = 'generic/platform=iOS'; - } else if (platform === XcodePlatform.watchOS) { - destinationString = 'generic/platform=watchOS'; - } else if (platform === XcodePlatform.tvOS) { - destinationString = 'generic/platform=tvOS'; - } else if (platform === XcodePlatform.visionOS) { - destinationString = 'generic/platform=visionOS'; - } else { + // Map platform to destination string + const destinationMap: Record = { + [XcodePlatform.iOS]: 'generic/platform=iOS', + [XcodePlatform.watchOS]: 'generic/platform=watchOS', + [XcodePlatform.tvOS]: 'generic/platform=tvOS', + [XcodePlatform.visionOS]: 'generic/platform=visionOS', + }; + + const destinationString = destinationMap[platform]; + if (!destinationString) { return createTextResponse(`Unsupported platform: ${platform}`, true); } command.push('-destination', destinationString); // Execute the command directly - const result = await executor(command, 'Get App Path', true); + const result = await executor(command, 'Get App Path', false); if (!result.success) { return createTextResponse(`Failed to get app path: ${result.error}`, true); @@ -131,22 +128,18 @@ export async function get_device_app_pathLogic( const fullProductName = fullProductNameMatch[1].trim(); const appPath = `${builtProductsDir}/${fullProductName}`; - const nextStepsText = `Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${appPath}" }) -2. Install app on device: install_app_device({ deviceId: "DEVICE_UDID", appPath: "${appPath}" }) -3. Launch app on device: launch_app_device({ deviceId: "DEVICE_UDID", bundleId: "BUNDLE_ID" })`; - return { content: [ { type: 'text', text: `✅ App path retrieved successfully: ${appPath}`, }, - { - type: 'text', - text: nextStepsText, - }, ], + nextStepParams: { + get_app_bundle_id: { appPath }, + install_app_device: { deviceId: 'DEVICE_UDID', appPath }, + launch_app_device: { deviceId: 'DEVICE_UDID', bundleId: 'BUNDLE_ID' }, + }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -155,25 +148,18 @@ export async function get_device_app_pathLogic( } } -export default { - name: 'get_device_app_path', - description: 'Retrieves the built app path for a connected device.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Get Device App Path', - readOnlyHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: getDeviceAppPathSchema as unknown as z.ZodType, - logicFunction: get_device_app_pathLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: getDeviceAppPathSchema as unknown as z.ZodType, + logicFunction: get_device_app_pathLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/device/index.ts b/src/mcp/tools/device/index.ts deleted file mode 100644 index 7f0a4cde..00000000 --- a/src/mcp/tools/device/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'iOS Device Development', - description: - 'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Build, test, deploy, and debug apps on real hardware.', -}; diff --git a/src/mcp/tools/device/install_app_device.ts b/src/mcp/tools/device/install_app_device.ts index 9a6d3917..3cd6d133 100644 --- a/src/mcp/tools/device/install_app_device.ts +++ b/src/mcp/tools/device/install_app_device.ts @@ -6,7 +6,7 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; @@ -21,9 +21,7 @@ const installAppDeviceSchema = z.object({ .string() .min(1, { message: 'Device ID cannot be empty' }) .describe('UDID of the device (obtained from list_devices)'), - appPath: z - .string() - .describe('Path to the .app bundle to install (full path to the .app directory)'), + appPath: z.string(), }); const publicSchemaObject = installAppDeviceSchema.omit({ deviceId: true } as const); @@ -46,7 +44,7 @@ export async function install_app_deviceLogic( const result = await executor( ['xcrun', 'devicectl', 'device', 'install', 'app', '--device', deviceId, appPath], 'Install app on device', - true, // useShell + false, // useShell undefined, // env ); @@ -85,21 +83,14 @@ export async function install_app_deviceLogic( } } -export default { - name: 'install_app_device', - description: 'Installs an app on a connected device.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: installAppDeviceSchema, - }), - annotations: { - title: 'Install App Device', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: installAppDeviceSchema as unknown as z.ZodType, - logicFunction: install_app_deviceLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: installAppDeviceSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: installAppDeviceSchema as unknown as z.ZodType, + logicFunction: install_app_deviceLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }], +}); diff --git a/src/mcp/tools/device/launch_app_device.ts b/src/mcp/tools/device/launch_app_device.ts index 299db503..feb1e404 100644 --- a/src/mcp/tools/device/launch_app_device.ts +++ b/src/mcp/tools/device/launch_app_device.ts @@ -6,16 +6,17 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; -import type { CommandExecutor } from '../../../utils/execution/index.ts'; -import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; +import { + getDefaultCommandExecutor, + getDefaultFileSystemExecutor, +} from '../../../utils/execution/index.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; -import { promises as fs } from 'fs'; -import { tmpdir } from 'os'; import { join } from 'path'; // Type for the launch JSON response @@ -30,12 +31,17 @@ type LaunchDataResponse = { // Define schema as ZodObject const launchAppDeviceSchema = z.object({ deviceId: z.string().describe('UDID of the device (obtained from list_devices)'), - bundleId: z - .string() - .describe('Bundle identifier of the app to launch (e.g., "com.example.MyApp")'), + bundleId: z.string(), + env: z + .record(z.string(), z.string()) + .optional() + .describe('Environment variables to pass to the launched app (as key-value dictionary)'), }); -const publicSchemaObject = launchAppDeviceSchema.omit({ deviceId: true } as const); +const publicSchemaObject = launchAppDeviceSchema.omit({ + deviceId: true, + bundleId: true, +} as const); // Use z.infer for type safety type LaunchAppDeviceParams = z.infer; @@ -43,6 +49,7 @@ type LaunchAppDeviceParams = z.infer; export async function launch_app_deviceLogic( params: LaunchAppDeviceParams, executor: CommandExecutor, + fileSystem: FileSystemExecutor, ): Promise { const { deviceId, bundleId } = params; @@ -50,24 +57,31 @@ export async function launch_app_deviceLogic( try { // Use JSON output to capture process ID - const tempJsonPath = join(tmpdir(), `launch-${Date.now()}.json`); + const tempJsonPath = join(fileSystem.tmpdir(), `launch-${Date.now()}.json`); + + const command = [ + 'xcrun', + 'devicectl', + 'device', + 'process', + 'launch', + '--device', + deviceId, + '--json-output', + tempJsonPath, + '--terminate-existing', + ]; + + if (params.env && Object.keys(params.env).length > 0) { + command.push('--environment-variables', JSON.stringify(params.env)); + } + + command.push(bundleId); const result = await executor( - [ - 'xcrun', - 'devicectl', - 'device', - 'process', - 'launch', - '--device', - deviceId, - '--json-output', - tempJsonPath, - '--terminate-existing', - bundleId, - ], + command, 'Launch app on device', - true, // useShell + false, // useShell undefined, // env ); @@ -86,7 +100,7 @@ export async function launch_app_deviceLogic( // Parse JSON to extract process ID let processId: number | undefined; try { - const jsonContent = await fs.readFile(tempJsonPath, 'utf8'); + const jsonContent = await fileSystem.readFile(tempJsonPath, 'utf8'); const parsedData: unknown = JSON.parse(jsonContent); // Type guard to validate the parsed data structure @@ -105,21 +119,15 @@ export async function launch_app_deviceLogic( const launchData = parsedData as LaunchDataResponse; processId = launchData.result?.process?.processIdentifier; } - - // Clean up temp file - await fs.unlink(tempJsonPath).catch(() => {}); } catch (error) { log('warn', `Failed to parse launch JSON output: ${error}`); + } finally { + await fileSystem.rm(tempJsonPath, { force: true }).catch(() => {}); } - let responseText = `✅ App launched successfully\n\n${result.output}`; - - if (processId) { - responseText += `\n\nProcess ID: ${processId}`; - responseText += `\n\nNext Steps:`; - responseText += `\n1. Interact with your app on the device`; - responseText += `\n2. Stop the app: stop_app_device({ deviceId: "${deviceId}", processId: ${processId} })`; - } + const responseText = processId + ? `✅ App launched successfully\n\n${result.output}\n\nProcess ID: ${processId}\n\nInteract with your app on the device.` + : `✅ App launched successfully\n\n${result.output}`; return { content: [ @@ -128,6 +136,7 @@ export async function launch_app_deviceLogic( text: responseText, }, ], + ...(processId ? { nextStepParams: { stop_app_device: { deviceId, processId } } } : {}), }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -144,21 +153,15 @@ export async function launch_app_deviceLogic( } } -export default { - name: 'launch_app_device', - description: 'Launches an app on a connected device.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: launchAppDeviceSchema, - }), - annotations: { - title: 'Launch App Device', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: launchAppDeviceSchema as unknown as z.ZodType, - logicFunction: launch_app_deviceLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: launchAppDeviceSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: launchAppDeviceSchema as unknown as z.ZodType, + logicFunction: (params, executor) => + launch_app_deviceLogic(params, executor, getDefaultFileSystemExecutor()), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['deviceId', 'bundleId'], message: 'Provide deviceId and bundleId' }], +}); diff --git a/src/mcp/tools/device/list_devices.ts b/src/mcp/tools/device/list_devices.ts index f37731dd..d685a6c8 100644 --- a/src/mcp/tools/device/list_devices.ts +++ b/src/mcp/tools/device/list_devices.ts @@ -49,7 +49,7 @@ export async function list_devicesLogic( const result = await executor( ['xcrun', 'devicectl', 'list', 'devices', '--json-output', tempJsonPath], 'List Devices (devicectl with JSON)', - true, + false, undefined, ); @@ -219,18 +219,16 @@ export async function list_devicesLogic( // Determine platform from platformIdentifier let platform = 'Unknown'; const platformId = device.deviceProperties?.platformIdentifier?.toLowerCase() ?? ''; - if (typeof platformId === 'string') { - if (platformId.includes('ios') || platformId.includes('iphone')) { - platform = 'iOS'; - } else if (platformId.includes('ipad')) { - platform = 'iPadOS'; - } else if (platformId.includes('watch')) { - platform = 'watchOS'; - } else if (platformId.includes('tv') || platformId.includes('apple tv')) { - platform = 'tvOS'; - } else if (platformId.includes('vision')) { - platform = 'visionOS'; - } + if (platformId.includes('ios') || platformId.includes('iphone')) { + platform = 'iOS'; + } else if (platformId.includes('ipad')) { + platform = 'iPadOS'; + } else if (platformId.includes('watch')) { + platform = 'watchOS'; + } else if (platformId.includes('tv') || platformId.includes('apple tv')) { + platform = 'tvOS'; + } else if (platformId.includes('vision')) { + platform = 'visionOS'; } // Determine connection state @@ -238,29 +236,23 @@ export async function list_devicesLogic( const tunnelState = device.connectionProperties?.tunnelState ?? ''; const transportType = device.connectionProperties?.transportType ?? ''; - let state = 'Unknown'; - // Consider a device available if it's paired, regardless of tunnel state - // This allows WiFi-connected devices to be used even if tunnelState isn't "connected" - if (pairingState === 'paired') { - if (tunnelState === 'connected') { - state = 'Available'; - } else { - // Device is paired but tunnel state may be different for WiFi connections - // Still mark as available since devicectl commands can work with paired devices - state = 'Available (WiFi)'; - } - } else { + let state: string; + if (pairingState !== 'paired') { state = 'Unpaired'; + } else if (tunnelState === 'connected') { + state = 'Available'; + } else { + state = 'Available (WiFi)'; } devices.push({ name: device.deviceProperties?.name ?? 'Unknown Device', identifier: device.identifier ?? 'Unknown', - platform: platform, + platform, model: device.deviceProperties?.marketingName ?? device.hardwareProperties?.productType, osVersion: device.deviceProperties?.osVersionNumber, - state: state, + state, connectionType: transportType, trustState: pairingState, developerModeStatus: device.deviceProperties?.developerModeStatus, @@ -290,7 +282,7 @@ export async function list_devicesLogic( const result = await executor( ['xcrun', 'xctrace', 'list', 'devices'], 'List Devices (xctrace)', - true, + false, undefined, ); @@ -388,13 +380,18 @@ export async function list_devicesLogic( (d) => d.state === 'Available' || d.state === 'Available (WiFi)' || d.state === 'Connected', ); + let nextStepParams: Record> | undefined; + if (availableDevicesExist) { - responseText += 'Next Steps:\n'; - responseText += - "1. Build for device: build_device({ scheme: 'SCHEME', deviceId: 'DEVICE_UDID' })\n"; - responseText += "2. Run tests: test_device({ scheme: 'SCHEME', deviceId: 'DEVICE_UDID' })\n"; - responseText += "3. Get app path: get_device_app_path({ scheme: 'SCHEME' })\n\n"; responseText += 'Note: Use the device ID/UDID from above when required by other tools.\n'; + responseText += + "Hint: Save a default device with session-set-defaults { deviceId: 'DEVICE_UDID' }.\n"; + + nextStepParams = { + build_device: { scheme: 'SCHEME', deviceId: 'DEVICE_UDID' }, + test_device: { scheme: 'SCHEME', deviceId: 'DEVICE_UDID' }, + get_device_app_path: { scheme: 'SCHEME' }, + }; } else if (uniqueDevices.length > 0) { responseText += 'Note: No devices are currently available for testing. Make sure devices are:\n'; @@ -410,6 +407,7 @@ export async function list_devicesLogic( text: responseText, }, ], + ...(nextStepParams ? { nextStepParams } : {}), }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -426,14 +424,10 @@ export async function list_devicesLogic( } } -export default { - name: 'list_devices', - description: - 'Lists connected physical Apple devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) with their UUIDs, names, and connection status. Use this to discover physical devices for testing.', - schema: listDevicesSchema.shape, // MCP SDK compatibility - annotations: { - title: 'List Devices', - readOnlyHint: true, - }, - handler: createTypedTool(listDevicesSchema, list_devicesLogic, getDefaultCommandExecutor), -}; +export const schema = listDevicesSchema.shape; + +export const handler = createTypedTool( + listDevicesSchema, + list_devicesLogic, + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/device/list_schemes.ts b/src/mcp/tools/device/list_schemes.ts deleted file mode 100644 index b046dde4..00000000 --- a/src/mcp/tools/device/list_schemes.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified list_schemes tool for device-project workflow -export { default } from '../project-discovery/list_schemes.ts'; diff --git a/src/mcp/tools/device/show_build_settings.ts b/src/mcp/tools/device/show_build_settings.ts deleted file mode 100644 index 0e15b943..00000000 --- a/src/mcp/tools/device/show_build_settings.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified tool for device-project workflow -export { default } from '../project-discovery/show_build_settings.ts'; diff --git a/src/mcp/tools/device/start_device_log_cap.ts b/src/mcp/tools/device/start_device_log_cap.ts deleted file mode 100644 index 19dd6c04..00000000 --- a/src/mcp/tools/device/start_device_log_cap.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from logging to complete workflow -export { default } from '../logging/start_device_log_cap.ts'; diff --git a/src/mcp/tools/device/stop_app_device.ts b/src/mcp/tools/device/stop_app_device.ts index 7a719e55..4bbf4319 100644 --- a/src/mcp/tools/device/stop_app_device.ts +++ b/src/mcp/tools/device/stop_app_device.ts @@ -6,7 +6,7 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; @@ -18,7 +18,7 @@ import { // Define schema as ZodObject const stopAppDeviceSchema = z.object({ deviceId: z.string().describe('UDID of the device (obtained from list_devices)'), - processId: z.number().describe('Process ID (PID) of the app to stop'), + processId: z.number(), }); // Use z.infer for type safety @@ -48,7 +48,7 @@ export async function stop_app_deviceLogic( processId.toString(), ], 'Stop app on device', - true, // useShell + false, // useShell undefined, // env ); @@ -87,21 +87,14 @@ export async function stop_app_deviceLogic( } } -export default { - name: 'stop_app_device', - description: 'Stops a running app on a connected device.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: stopAppDeviceSchema, - }), - annotations: { - title: 'Stop App Device', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: stopAppDeviceSchema as unknown as z.ZodType, - logicFunction: stop_app_deviceLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: stopAppDeviceSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: stopAppDeviceSchema as unknown as z.ZodType, + logicFunction: stop_app_deviceLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }], +}); diff --git a/src/mcp/tools/device/stop_device_log_cap.ts b/src/mcp/tools/device/stop_device_log_cap.ts deleted file mode 100644 index 48a20e09..00000000 --- a/src/mcp/tools/device/stop_device_log_cap.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from logging to complete workflow -export { default } from '../logging/stop_device_log_cap.ts'; diff --git a/src/mcp/tools/device/test_device.ts b/src/mcp/tools/device/test_device.ts index 1e78e62c..257c7724 100644 --- a/src/mcp/tools/device/test_device.ts +++ b/src/mcp/tools/device/test_device.ts @@ -7,7 +7,8 @@ import * as z from 'zod'; import { join } from 'path'; -import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; import { createTextResponse } from '../../../utils/responses/index.ts'; @@ -34,13 +35,10 @@ const baseSchemaObject = z.object({ scheme: z.string().describe('The scheme to test'), deviceId: z.string().describe('UDID of the device (obtained from list_devices)'), configuration: z.string().optional().describe('Build configuration (Debug, Release)'), - derivedDataPath: z.string().optional().describe('Path to derived data directory'), - extraArgs: z.array(z.string()).optional().describe('Additional arguments to pass to xcodebuild'), - preferXcodebuild: z.boolean().optional().describe('Prefer xcodebuild over faster alternatives'), - platform: z - .enum(['iOS', 'watchOS', 'tvOS', 'visionOS']) - .optional() - .describe('Target platform (defaults to iOS)'), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), + preferXcodebuild: z.boolean().optional(), + platform: z.enum(['iOS', 'watchOS', 'tvOS', 'visionOS']).optional(), testRunnerEnv: z .record(z.string(), z.string()) .optional() @@ -68,6 +66,9 @@ const publicSchemaObject = baseSchemaObject.omit({ scheme: true, deviceId: true, configuration: true, + derivedDataPath: true, + preferXcodebuild: true, + platform: true, } as const); /** @@ -285,33 +286,26 @@ export async function testDeviceLogic( } } -export default { - name: 'test_device', - description: 'Runs tests on a physical Apple device.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Test Device', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: testDeviceSchema as unknown as z.ZodType, - logicFunction: (params: TestDeviceParams, executor: CommandExecutor) => - testDeviceLogic( - { - ...params, - platform: params.platform ?? 'iOS', - }, - executor, - getDefaultFileSystemExecutor(), - ), - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme', 'deviceId'], message: 'Provide scheme and deviceId' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: testDeviceSchema as unknown as z.ZodType, + logicFunction: (params: TestDeviceParams, executor: CommandExecutor) => + testDeviceLogic( + { + ...params, + platform: params.platform ?? 'iOS', + }, + executor, + getDefaultFileSystemExecutor(), + ), + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme', 'deviceId'], message: 'Provide scheme and deviceId' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/doctor/__tests__/doctor.test.ts b/src/mcp/tools/doctor/__tests__/doctor.test.ts index 70f746b5..862e7bac 100644 --- a/src/mcp/tools/doctor/__tests__/doctor.test.ts +++ b/src/mcp/tools/doctor/__tests__/doctor.test.ts @@ -4,12 +4,14 @@ * Using dependency injection for deterministic testing */ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import doctor, { runDoctor, type DoctorDependencies } from '../doctor.ts'; +import { schema, runDoctor, type DoctorDependencies } from '../doctor.ts'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; function createDeps(overrides?: Partial): DoctorDependencies { const base: DoctorDependencies = { + commandExecutor: createMockExecutor({ output: 'lldb-dap' }), binaryChecker: { async checkBinaryAvailability(binary: string) { // default: all available with generic version @@ -86,10 +88,8 @@ function createDeps(overrides?: Partial): DoctorDependencies runtime: { async getRuntimeToolInfo() { return { - mode: 'runtime' as const, enabledWorkflows: ['doctor'], - enabledTools: ['doctor'], - totalRegistered: 1, + registeredToolCount: 1, }; }, }, @@ -122,35 +122,19 @@ function createDeps(overrides?: Partial): DoctorDependencies } describe('doctor tool', () => { - // Reset any state if needed - - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(doctor.name).toBe('doctor'); - }); - - it('should have correct description', () => { - expect(doctor.description).toBe( - 'Provides comprehensive information about the MCP server environment, available dependencies, and configuration status.', - ); - }); - - it('should have handler function', () => { - expect(typeof doctor.handler).toBe('function'); - }); - + describe('Schema Validation', () => { it('should have correct schema with enabled boolean field', () => { - const schema = z.object(doctor.schema); + const schemaObj = z.object(schema); // Valid inputs - expect(schema.safeParse({ enabled: true }).success).toBe(true); - expect(schema.safeParse({ enabled: false }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(true); // enabled is optional + expect(schemaObj.safeParse({ enabled: true }).success).toBe(true); + expect(schemaObj.safeParse({ enabled: false }).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); // enabled is optional // Invalid inputs - expect(schema.safeParse({ enabled: 'true' }).success).toBe(false); - expect(schema.safeParse({ enabled: 1 }).success).toBe(false); - expect(schema.safeParse({ enabled: null }).success).toBe(false); + expect(schemaObj.safeParse({ enabled: 'true' }).success).toBe(false); + expect(schemaObj.safeParse({ enabled: 1 }).success).toBe(false); + expect(schemaObj.safeParse({ enabled: null }).success).toBe(false); }); }); diff --git a/src/mcp/tools/doctor/__tests__/index.test.ts b/src/mcp/tools/doctor/__tests__/index.test.ts deleted file mode 100644 index 60c4e3ed..00000000 --- a/src/mcp/tools/doctor/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for doctor workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('doctor workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('System Doctor'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Debug tools and system doctor for troubleshooting XcodeBuildMCP server, development environment, and tool availability.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/doctor/doctor.ts b/src/mcp/tools/doctor/doctor.ts index d775bf8d..255ebb1c 100644 --- a/src/mcp/tools/doctor/doctor.ts +++ b/src/mcp/tools/doctor/doctor.ts @@ -9,21 +9,89 @@ import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { version } from '../../../utils/version/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { getConfig } from '../../../utils/config-store.ts'; +import { detectXcodeRuntime } from '../../../utils/xcode-process.ts'; import { type DoctorDependencies, createDoctorDependencies } from './lib/doctor.deps.ts'; +import { peekXcodeToolsBridgeManager } from '../../../integrations/xcode-tools-bridge/index.ts'; +import { getMcpBridgeAvailability } from '../../../integrations/xcode-tools-bridge/core.ts'; // Constants const LOG_PREFIX = '[Doctor]'; // Define schema as ZodObject const doctorSchema = z.object({ - enabled: z.boolean().optional().describe('Optional: dummy parameter to satisfy MCP protocol'), + enabled: z.boolean().optional(), }); // Use z.infer for type safety type DoctorParams = z.infer; +async function checkLldbDapAvailability(executor: CommandExecutor): Promise { + try { + const result = await executor(['xcrun', '--find', 'lldb-dap'], 'Check lldb-dap'); + return result.success && result.output.trim().length > 0; + } catch { + return false; + } +} + +type XcodeToolsBridgeDoctorInfo = + | { + available: true; + workflowEnabled: boolean; + bridgePath: string | null; + xcodeRunning: boolean | null; + connected: boolean; + bridgePid: number | null; + proxiedToolCount: number; + lastError: string | null; + } + | { available: false; reason: string }; + +async function getXcodeToolsBridgeDoctorInfo( + executor: CommandExecutor, + workflowEnabled: boolean, +): Promise { + try { + const manager = peekXcodeToolsBridgeManager(); + if (manager) { + const status = await manager.getStatus(); + return { + available: true, + workflowEnabled: status.workflowEnabled, + bridgePath: status.bridgePath, + xcodeRunning: status.xcodeRunning, + connected: status.connected, + bridgePid: status.bridgePid, + proxiedToolCount: status.proxiedToolCount, + lastError: status.lastError, + }; + } + + const bridgeInfo = await getMcpBridgeAvailability(); + const bridgePath = bridgeInfo.available ? bridgeInfo.path : null; + const xcodeRunningResult = await executor(['pgrep', '-x', 'Xcode'], 'Check Xcode process'); + const xcodeRunning = xcodeRunningResult.success + ? xcodeRunningResult.output.trim().length > 0 + : null; + return { + available: true, + workflowEnabled, + bridgePath, + xcodeRunning, + connected: false, + bridgePid: null, + proxiedToolCount: 0, + lastError: null, + }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return { available: false, reason: message }; + } +} + /** * Run the doctor tool and return the results */ @@ -46,18 +114,35 @@ export async function runDoctor( const envVars = deps.env.getEnvironmentVariables(); const systemInfo = deps.env.getSystemInfo(); const nodeInfo = deps.env.getNodeInfo(); + const xcodeRuntime = await detectXcodeRuntime(deps.commandExecutor); const axeAvailable = deps.features.areAxeToolsAvailable(); const pluginSystemInfo = await deps.plugins.getPluginSystemInfo(); const runtimeInfo = await deps.runtime.getRuntimeToolInfo(); + const runtimeRegistration = runtimeInfo ?? { + enabledWorkflows: [], + registeredToolCount: 0, + }; + const xcodeIdeWorkflowEnabled = runtimeRegistration.enabledWorkflows.includes('xcode-ide'); + const runtimeNote = runtimeInfo ? null : 'Runtime registry unavailable.'; const xcodemakeEnabled = deps.features.isXcodemakeEnabled(); const xcodemakeAvailable = await deps.features.isXcodemakeAvailable(); const makefileExists = deps.features.doesMakefileExist('./'); + const lldbDapAvailable = await checkLldbDapAvailability(deps.commandExecutor); + const selectedDebuggerBackend = getConfig().debuggerBackend; + const dapSelected = selectedDebuggerBackend === 'dap'; + const xcodeToolsBridge = await getXcodeToolsBridgeDoctorInfo( + deps.commandExecutor, + xcodeIdeWorkflowEnabled, + ); const doctorInfo = { - serverVersion: version, + serverVersion: String(version), timestamp: new Date().toISOString(), system: systemInfo, node: nodeInfo, + processTree: xcodeRuntime.processTree, + processTreeError: xcodeRuntime.error, + runningUnderXcode: xcodeRuntime.runningUnderXcode, xcode: xcodeInfo, dependencies: binaryStatus, environmentVariables: envVars, @@ -75,6 +160,12 @@ export async function runDoctor( running_under_mise: Boolean(process.env.XCODEBUILDMCP_RUNNING_UNDER_MISE), available: binaryStatus['mise'].available, }, + debugger: { + dap: { + available: lldbDapAvailable, + selected: selectedDebuggerBackend, + }, + }, }, pluginSystem: pluginSystemInfo, } as const; @@ -161,6 +252,18 @@ export async function runDoctor( `\n## Node.js Information`, ...Object.entries(doctorInfo.node).map(([key, value]) => `- ${key}: ${value}`), + `\n## Process Tree`, + `- Running under Xcode: ${doctorInfo.runningUnderXcode ? '✅ Yes' : '❌ No'}`, + ...(doctorInfo.processTree.length > 0 + ? doctorInfo.processTree.map( + (entry) => + `- ${entry.pid} (ppid ${entry.ppid}): ${entry.name}${ + entry.command ? ` — ${entry.command}` : '' + }`, + ) + : ['- (unavailable)']), + ...(doctorInfo.processTreeError ? [`- Error: ${doctorInfo.processTreeError}`] : []), + `\n## Xcode Information`, ...('error' in doctorInfo.xcode ? [`- Error: ${doctorInfo.xcode.error}`] @@ -196,6 +299,15 @@ export async function runDoctor( `- Running under mise: ${doctorInfo.features.mise.running_under_mise ? '✅ Yes' : '❌ No'}`, `- Mise available: ${doctorInfo.features.mise.available ? '✅ Yes' : '❌ No'}`, + `\n### Debugger Backend (DAP)`, + `- lldb-dap available: ${doctorInfo.features.debugger.dap.available ? '✅ Yes' : '❌ No'}`, + `- Selected backend: ${doctorInfo.features.debugger.dap.selected}`, + ...(dapSelected && !lldbDapAvailable + ? [ + `- Warning: DAP backend selected but lldb-dap not available. Set XCODEBUILDMCP_DEBUGGER_BACKEND=lldb-cli to use the CLI backend.`, + ] + : []), + `\n### Available Tools`, `- Total Plugins: ${'totalPlugins' in doctorInfo.pluginSystem ? doctorInfo.pluginSystem.totalPlugins : 0}`, `- Plugin Directories: ${'pluginDirectories' in doctorInfo.pluginSystem ? doctorInfo.pluginSystem.pluginDirectories : 0}`, @@ -207,14 +319,27 @@ export async function runDoctor( : ['- Plugin directory grouping unavailable in this build']), `\n### Runtime Tool Registration`, - `- Mode: ${runtimeInfo.mode}`, - `- Enabled Workflows: ${runtimeInfo.enabledWorkflows.length}`, - `- Registered Tools: ${runtimeInfo.totalRegistered}`, - ...(runtimeInfo.mode === 'static' ? [`- Note: ${runtimeInfo.note}`] : []), - ...(runtimeInfo.enabledWorkflows.length > 0 - ? [`- Workflows: ${runtimeInfo.enabledWorkflows.join(', ')}`] + `- Enabled Workflows: ${runtimeRegistration.enabledWorkflows.length}`, + `- Registered Tools: ${runtimeRegistration.registeredToolCount}`, + ...(runtimeNote ? [`- Note: ${runtimeNote}`] : []), + ...(runtimeRegistration.enabledWorkflows.length > 0 + ? [`- Workflows: ${runtimeRegistration.enabledWorkflows.join(', ')}`] : []), + `\n### Xcode IDE Bridge (mcpbridge)`, + ...(xcodeToolsBridge.available + ? [ + `- Workflow enabled: ${xcodeToolsBridge.workflowEnabled ? '✅ Yes' : '❌ No'}`, + `- mcpbridge path: ${xcodeToolsBridge.bridgePath ?? '(not found)'}`, + `- Xcode running: ${xcodeToolsBridge.xcodeRunning ?? '(unknown)'}`, + `- Connected: ${xcodeToolsBridge.connected ? '✅ Yes' : '❌ No'}`, + `- Bridge PID: ${xcodeToolsBridge.bridgePid ?? '(none)'}`, + `- Proxied tools: ${xcodeToolsBridge.proxiedToolCount}`, + `- Last error: ${xcodeToolsBridge.lastError ?? '(none)'}`, + `- Note: Bridge debug tools (status/sync/disconnect) are only registered when debug: true`, + ] + : [`- Unavailable: ${xcodeToolsBridge.reason}`]), + `\n## Tool Availability Summary`, `- Build Tools: ${!('error' in doctorInfo.xcode) ? '\u2705 Available' : '\u274c Not available'}`, `- UI Automation Tools: ${doctorInfo.features.axe.uiAutomationSupported ? '\u2705 Available' : '\u274c Not available'}`, @@ -264,16 +389,8 @@ async function doctorMcpHandler( return doctorLogic(params, executor, false); // Always false for MCP } -export default { - name: 'doctor', - description: - 'Provides comprehensive information about the MCP server environment, available dependencies, and configuration status.', - schema: doctorSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Doctor', - readOnlyHint: true, - }, - handler: createTypedTool(doctorSchema, doctorMcpHandler, getDefaultCommandExecutor), -}; +export const schema = doctorSchema.shape; // MCP SDK compatibility + +export const handler = createTypedTool(doctorSchema, doctorMcpHandler, getDefaultCommandExecutor); export type { DoctorDependencies } from './lib/doctor.deps.ts'; diff --git a/src/mcp/tools/doctor/index.ts b/src/mcp/tools/doctor/index.ts deleted file mode 100644 index fbd9e478..00000000 --- a/src/mcp/tools/doctor/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'System Doctor', - description: - 'Debug tools and system doctor for troubleshooting XcodeBuildMCP server, development environment, and tool availability.', -}; diff --git a/src/mcp/tools/doctor/lib/doctor.deps.ts b/src/mcp/tools/doctor/lib/doctor.deps.ts index 3238ecb2..5f1aea4f 100644 --- a/src/mcp/tools/doctor/lib/doctor.deps.ts +++ b/src/mcp/tools/doctor/lib/doctor.deps.ts @@ -1,12 +1,9 @@ import * as os from 'os'; import type { CommandExecutor } from '../../../../utils/execution/index.ts'; -import { loadWorkflowGroups } from '../../../../utils/plugin-registry/index.ts'; -import { getRuntimeRegistration } from '../../../../utils/runtime-registry.ts'; -import { - collectToolNames, - resolveSelectedWorkflows, -} from '../../../../utils/workflow-selection.ts'; -import { areAxeToolsAvailable } from '../../../../utils/axe/index.ts'; +import { loadManifest } from '../../../../core/manifest/load-manifest.ts'; +import type { RuntimeToolInfo } from '../../../../utils/tool-registry.ts'; +import { getRuntimeRegistration } from '../../../../utils/tool-registry.ts'; +import { areAxeToolsAvailable, resolveAxeBinary } from '../../../../utils/axe/index.ts'; import { isXcodemakeEnabled, isXcodemakeAvailable, @@ -62,21 +59,7 @@ export interface PluginInfoProvider { } export interface RuntimeInfoProvider { - getRuntimeToolInfo(): Promise< - | { - mode: 'runtime'; - enabledWorkflows: string[]; - enabledTools: string[]; - totalRegistered: number; - } - | { - mode: 'static'; - enabledWorkflows: string[]; - enabledTools: string[]; - totalRegistered: number; - note: string; - } - >; + getRuntimeToolInfo(): Promise; } export interface FeatureDetector { @@ -87,6 +70,7 @@ export interface FeatureDetector { } export interface DoctorDependencies { + commandExecutor: CommandExecutor; binaryChecker: BinaryChecker; xcode: XcodeInfoProvider; env: EnvironmentInfoProvider; @@ -96,11 +80,29 @@ export interface DoctorDependencies { } export function createDoctorDependencies(executor: CommandExecutor): DoctorDependencies { + const commandExecutor = executor; const binaryChecker: BinaryChecker = { async checkBinaryAvailability(binary: string) { - // If bundled axe is available, reflect that in dependencies even if not on PATH - if (binary === 'axe' && areAxeToolsAvailable()) { - return { available: true, version: 'Bundled' }; + if (binary === 'axe') { + const axeBinary = resolveAxeBinary(); + if (!axeBinary) { + return { available: false }; + } + + let version: string | undefined; + try { + const res = await executor([axeBinary.path, '--version'], 'Get AXe Version'); + if (res.success && res.output) { + version = res.output.trim(); + } + } catch { + // ignore + } + + return { + available: true, + version: version ?? 'Available (version info not available)', + }; } try { const which = await executor(['which', binary], 'Check Binary Availability'); @@ -113,7 +115,6 @@ export function createDoctorDependencies(executor: CommandExecutor): DoctorDepen let version: string | undefined; const versionCommands: Record = { - axe: 'axe --version', mise: 'mise --version', }; @@ -176,11 +177,11 @@ export function createDoctorDependencies(executor: CommandExecutor): DoctorDepen envVars[varName] = process.env[varName]; } - Object.keys(process.env).forEach((key) => { + for (const key of Object.keys(process.env)) { if (key.startsWith('XCODEBUILDMCP_')) { envVars[key] = process.env[key]; } - }); + } return envVars; }, @@ -216,25 +217,27 @@ export function createDoctorDependencies(executor: CommandExecutor): DoctorDepen const plugins: PluginInfoProvider = { async getPluginSystemInfo() { try { - const workflows = await loadWorkflowGroups(); + const manifest = loadManifest(); const pluginsByDirectory: Record = {}; let totalPlugins = 0; - for (const [dirName, wf] of workflows.entries()) { - const toolNames = wf.tools.map((t) => t.name).filter(Boolean) as string[]; + for (const [workflowId, workflow] of manifest.workflows.entries()) { + const toolNames = workflow.tools + .map((toolId) => manifest.tools.get(toolId)?.names.mcp) + .filter((name): name is string => name !== undefined); totalPlugins += toolNames.length; - pluginsByDirectory[dirName] = toolNames; + pluginsByDirectory[workflowId] = toolNames; } return { totalPlugins, - pluginDirectories: workflows.size, + pluginDirectories: manifest.workflows.size, pluginsByDirectory, - systemMode: 'plugin-based', + systemMode: 'manifest-based', }; } catch (error) { return { - error: `Failed to load plugins: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to load manifest: ${error instanceof Error ? error.message : 'Unknown error'}`, systemMode: 'error', }; } @@ -243,29 +246,7 @@ export function createDoctorDependencies(executor: CommandExecutor): DoctorDepen const runtime: RuntimeInfoProvider = { async getRuntimeToolInfo() { - const runtimeInfo = getRuntimeRegistration(); - if (runtimeInfo) { - return runtimeInfo; - } - - const workflows = await loadWorkflowGroups(); - const enabledWorkflowEnv = process.env.XCODEBUILDMCP_ENABLED_WORKFLOWS ?? ''; - const workflowNames = enabledWorkflowEnv - .split(',') - .map((workflow) => workflow.trim()) - .filter(Boolean); - const selection = resolveSelectedWorkflows(workflows, workflowNames); - const enabledWorkflows = selection.selectedWorkflows.map( - (workflow) => workflow.directoryName, - ); - const enabledTools = collectToolNames(selection.selectedWorkflows); - return { - mode: 'static', - enabledWorkflows, - enabledTools, - totalRegistered: enabledTools.length, - note: 'Runtime registry unavailable; showing expected tools from selection rules.', - }; + return getRuntimeRegistration(); }, }; @@ -276,7 +257,7 @@ export function createDoctorDependencies(executor: CommandExecutor): DoctorDepen doesMakefileExist, }; - return { binaryChecker, xcode, env, plugins, runtime, features }; + return { commandExecutor, binaryChecker, xcode, env, plugins, runtime, features }; } export type { CommandExecutor }; diff --git a/src/mcp/tools/logging/__tests__/index.test.ts b/src/mcp/tools/logging/__tests__/index.test.ts deleted file mode 100644 index 1bc97952..00000000 --- a/src/mcp/tools/logging/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for logging workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('logging workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('Log Capture & Management'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Log capture and management tools for iOS simulators and physical devices. Start, stop, and analyze application and system logs during development and testing.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts b/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts index 237f1650..c1fa0f4b 100644 --- a/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts @@ -2,19 +2,39 @@ * Tests for start_device_log_cap plugin * Following CLAUDE.md testing standards with pure dependency injection */ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect, beforeEach } from 'vitest'; import { EventEmitter } from 'events'; +import { Readable } from 'stream'; import type { ChildProcess } from 'child_process'; import * as z from 'zod'; import { createMockExecutor, createMockFileSystemExecutor, } from '../../../../test-utils/mock-executors.ts'; -import plugin, { - start_device_log_capLogic, - activeDeviceLogSessions, -} from '../start_device_log_cap.ts'; +import { schema, handler, start_device_log_capLogic } from '../start_device_log_cap.ts'; +import { activeDeviceLogSessions } from '../../../../utils/log-capture/device-log-sessions.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; +import { + __resetConfigStoreForTests, + initConfigStore, + type RuntimeConfigOverrides, +} from '../../../../utils/config-store.ts'; + +const cwd = '/repo'; + +async function initConfigStoreForTest(overrides?: RuntimeConfigOverrides): Promise { + __resetConfigStoreForTests(); + await initConfigStore({ cwd, fs: createMockFileSystemExecutor(), overrides }); +} + +type Mutable = { + -readonly [K in keyof T]: T[K]; +}; + +type MockChildProcess = Mutable & { + stdout: Readable; + stderr: Readable; +}; describe('start_device_log_cap plugin', () => { // Mock state tracking @@ -27,65 +47,41 @@ describe('start_device_log_cap plugin', () => { let mkdirCalls: string[] = []; let writeFileCalls: Array<{ path: string; content: string }> = []; - // Reset state - commandCalls = []; - mkdirCalls = []; - writeFileCalls = []; - - const originalJsonWaitEnv = process.env.XBMCP_LAUNCH_JSON_WAIT_MS; - - beforeEach(() => { + beforeEach(async () => { sessionStore.clear(); activeDeviceLogSessions.clear(); - process.env.XBMCP_LAUNCH_JSON_WAIT_MS = '25'; - }); - - afterEach(() => { - if (originalJsonWaitEnv === undefined) { - delete process.env.XBMCP_LAUNCH_JSON_WAIT_MS; - } else { - process.env.XBMCP_LAUNCH_JSON_WAIT_MS = originalJsonWaitEnv; - } + await initConfigStoreForTest({ launchJsonWaitMs: 25 }); }); describe('Plugin Structure', () => { - it('should export an object with required properties', () => { - expect(plugin).toHaveProperty('name'); - expect(plugin).toHaveProperty('description'); - expect(plugin).toHaveProperty('schema'); - expect(plugin).toHaveProperty('handler'); - }); - - it('should have correct tool name', () => { - expect(plugin.name).toBe('start_device_log_cap'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe('Starts log capture on a connected device.'); + it('should export schema and handler', () => { + expect(schema).toBeDefined(); + expect(handler).toBeDefined(); }); it('should have correct schema structure', () => { // Schema should be a plain object for MCP protocol compliance - expect(typeof plugin.schema).toBe('object'); - expect(Object.keys(plugin.schema)).toEqual(['bundleId']); + expect(typeof schema).toBe('object'); + expect(Object.keys(schema)).toEqual([]); // Validate that schema fields are Zod types that can be used for validation - const schema = z.strictObject(plugin.schema); - expect(schema.safeParse({ bundleId: 'com.test.app' }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(false); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({ bundleId: 'com.test.app' }).success).toBe(false); + expect(schemaObj.safeParse({}).success).toBe(true); }); it('should have handler as a function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); }); describe('Handler Requirements', () => { - it('should require deviceId when not provided', async () => { - const result = await plugin.handler({ bundleId: 'com.example.MyApp' }); + it('should require deviceId and bundleId when not provided', async () => { + const result = await handler({}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('deviceId is required'); + expect(result.content[0].text).toContain('Missing required session defaults'); + expect(result.content[0].text).toContain('Provide deviceId and bundleId'); }); }); @@ -109,7 +105,7 @@ describe('start_device_log_cap plugin', () => { const result = await start_device_log_capLogic( { deviceId: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', }, mockExecutor, mockFileSystemExecutor, @@ -139,33 +135,34 @@ describe('start_device_log_cap plugin', () => { const result = await start_device_log_capLogic( { deviceId: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', }, mockExecutor, mockFileSystemExecutor, ); - expect(result.content[0].text).toContain('Next Steps:'); - expect(result.content[0].text).toContain('Use stop_device_log_cap'); + expect(result.content[0].text).toContain('Interact with your app'); + const responseText = String(result.content[0].text); + const sessionIdMatch = responseText.match(/Session ID: ([a-f0-9-]{36})/); + expect(sessionIdMatch).not.toBeNull(); + const sessionId = sessionIdMatch?.[1]; + expect(typeof sessionId).toBe('string'); + + expect(result.nextStepParams?.stop_device_log_cap).toBeDefined(); + expect(result.nextStepParams?.stop_device_log_cap).toMatchObject({ + logSessionId: sessionId, + }); }); it('should surface early launch failures when process exits immediately', async () => { - const failingProcess = new EventEmitter() as unknown as ChildProcess & { - exitCode: number | null; - killed: boolean; - kill(signal?: string): boolean; - stdout: NodeJS.ReadableStream & { setEncoding?: (encoding: string) => void }; - stderr: NodeJS.ReadableStream & { setEncoding?: (encoding: string) => void }; - }; + const failingProcess = new EventEmitter() as MockChildProcess; - const stubOutput = new EventEmitter() as NodeJS.ReadableStream & { - setEncoding?: (encoding: string) => void; - }; - stubOutput.setEncoding = () => {}; - const stubError = new EventEmitter() as NodeJS.ReadableStream & { - setEncoding?: (encoding: string) => void; - }; - stubError.setEncoding = () => {}; + const stubOutput = new Readable({ + read() {}, + }); + const stubError = new Readable({ + read() {}, + }); failingProcess.stdout = stubOutput; failingProcess.stderr = stubError; @@ -233,22 +230,14 @@ describe('start_device_log_cap plugin', () => { }, }; - const failingProcess = new EventEmitter() as unknown as ChildProcess & { - exitCode: number | null; - killed: boolean; - kill(signal?: string): boolean; - stdout: NodeJS.ReadableStream & { setEncoding?: (encoding: string) => void }; - stderr: NodeJS.ReadableStream & { setEncoding?: (encoding: string) => void }; - }; + const failingProcess = new EventEmitter() as MockChildProcess; - const stubOutput = new EventEmitter() as NodeJS.ReadableStream & { - setEncoding?: (encoding: string) => void; - }; - stubOutput.setEncoding = () => {}; - const stubError = new EventEmitter() as NodeJS.ReadableStream & { - setEncoding?: (encoding: string) => void; - }; - stubError.setEncoding = () => {}; + const stubOutput = new Readable({ + read() {}, + }); + const stubError = new Readable({ + read() {}, + }); failingProcess.stdout = stubOutput; failingProcess.stderr = stubError; @@ -323,22 +312,14 @@ describe('start_device_log_cap plugin', () => { }, }; - const runningProcess = new EventEmitter() as unknown as ChildProcess & { - exitCode: number | null; - killed: boolean; - kill(signal?: string): boolean; - stdout: NodeJS.ReadableStream & { setEncoding?: (encoding: string) => void }; - stderr: NodeJS.ReadableStream & { setEncoding?: (encoding: string) => void }; - }; + const runningProcess = new EventEmitter() as MockChildProcess; - const stubOutput = new EventEmitter() as NodeJS.ReadableStream & { - setEncoding?: (encoding: string) => void; - }; - stubOutput.setEncoding = () => {}; - const stubError = new EventEmitter() as NodeJS.ReadableStream & { - setEncoding?: (encoding: string) => void; - }; - stubError.setEncoding = () => {}; + const stubOutput = new Readable({ + read() {}, + }); + const stubError = new Readable({ + read() {}, + }); runningProcess.stdout = stubOutput; runningProcess.stderr = stubError; @@ -392,7 +373,7 @@ describe('start_device_log_cap plugin', () => { const result = await start_device_log_capLogic( { deviceId: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', }, mockExecutor, mockFileSystemExecutor, @@ -423,7 +404,7 @@ describe('start_device_log_cap plugin', () => { const result = await start_device_log_capLogic( { deviceId: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', }, mockExecutor, mockFileSystemExecutor, @@ -461,7 +442,7 @@ describe('start_device_log_cap plugin', () => { const result = await start_device_log_capLogic( { deviceId: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', }, mockExecutor, mockFileSystemExecutor, @@ -494,7 +475,7 @@ describe('start_device_log_cap plugin', () => { const result = await start_device_log_capLogic( { deviceId: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', }, mockExecutor, mockFileSystemExecutor, @@ -527,7 +508,7 @@ describe('start_device_log_cap plugin', () => { const result = await start_device_log_capLogic( { deviceId: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', }, mockExecutor, mockFileSystemExecutor, diff --git a/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts b/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts index 874a4f4e..2a3fcc92 100644 --- a/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts @@ -3,58 +3,52 @@ */ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import plugin, { start_sim_log_capLogic } from '../start_sim_log_cap.ts'; +import { schema, handler, start_sim_log_capLogic } from '../start_sim_log_cap.ts'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('start_sim_log_cap plugin', () => { // Reset any test state if needed describe('Export Field Validation (Literal)', () => { - it('should export an object with required properties', () => { - expect(plugin).toHaveProperty('name'); - expect(plugin).toHaveProperty('description'); - expect(plugin).toHaveProperty('schema'); - expect(plugin).toHaveProperty('handler'); - }); - - it('should have correct tool name', () => { - expect(plugin.name).toBe('start_sim_log_cap'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe( - 'Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs.', - ); + it('should export schema and handler', () => { + expect(schema).toBeDefined(); + expect(handler).toBeDefined(); }); it('should have handler as a function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema with valid parameters', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({ bundleId: 'com.example.app' }).success).toBe(true); - expect(schema.safeParse({ bundleId: 'com.example.app', captureConsole: true }).success).toBe( - true, - ); - expect(schema.safeParse({ bundleId: 'com.example.app', captureConsole: false }).success).toBe( - true, - ); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ captureConsole: true }).success).toBe(true); + expect(schemaObj.safeParse({ captureConsole: false }).success).toBe(true); + }); + + it('should validate schema with subsystemFilter parameter', () => { + const schemaObj = z.object(schema); + // Valid enum values + expect(schemaObj.safeParse({ subsystemFilter: 'app' }).success).toBe(true); + expect(schemaObj.safeParse({ subsystemFilter: 'all' }).success).toBe(true); + expect(schemaObj.safeParse({ subsystemFilter: 'swiftui' }).success).toBe(true); + // Valid array of subsystems + expect(schemaObj.safeParse({ subsystemFilter: ['com.apple.UIKit'] }).success).toBe(true); + expect( + schemaObj.safeParse({ subsystemFilter: ['com.apple.UIKit', 'com.apple.CoreData'] }).success, + ).toBe(true); + // Invalid values + expect(schemaObj.safeParse({ subsystemFilter: [] }).success).toBe(false); + expect(schemaObj.safeParse({ subsystemFilter: 'invalid' }).success).toBe(false); + expect(schemaObj.safeParse({ subsystemFilter: 123 }).success).toBe(false); }); it('should reject invalid schema parameters', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({ bundleId: null }).success).toBe(false); - expect(schema.safeParse({ captureConsole: true }).success).toBe(false); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ bundleId: 'com.example.app', captureConsole: 'yes' }).success).toBe( - false, - ); - expect(schema.safeParse({ bundleId: 'com.example.app', captureConsole: 123 }).success).toBe( - false, - ); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({ captureConsole: 'yes' }).success).toBe(false); + expect(schemaObj.safeParse({ captureConsole: 123 }).success).toBe(false); - const withSimId = schema.safeParse({ simulatorId: 'test-uuid', bundleId: 'com.example.app' }); + const withSimId = schemaObj.safeParse({ simulatorId: 'test-uuid' }); expect(withSimId.success).toBe(true); expect('simulatorId' in (withSimId.data as any)).toBe(false); }); @@ -78,7 +72,8 @@ describe('start_sim_log_cap plugin', () => { const result = await start_sim_log_capLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', + subsystemFilter: 'app', }, mockExecutor, logCaptureStub, @@ -102,7 +97,8 @@ describe('start_sim_log_cap plugin', () => { const result = await start_sim_log_capLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', + subsystemFilter: 'app', }, mockExecutor, logCaptureStub, @@ -110,8 +106,89 @@ describe('start_sim_log_cap plugin', () => { expect(result.isError).toBeUndefined(); expect(result.content[0].text).toBe( - "Log capture started successfully. Session ID: test-uuid-123.\n\nNote: Only structured logs are being captured.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.", + 'Log capture started successfully. Session ID: test-uuid-123.\n\nOnly structured logs from the app subsystem are being captured.\n\nInteract with your simulator and app, then stop capture to retrieve logs.', ); + expect(result.nextStepParams?.stop_sim_log_cap).toBeDefined(); + expect(result.nextStepParams?.stop_sim_log_cap).toMatchObject({ + logSessionId: 'test-uuid-123', + }); + }); + + it('should indicate swiftui capture when subsystemFilter is swiftui', async () => { + const mockExecutor = createMockExecutor({ success: true, output: '' }); + const logCaptureStub = (params: any, executor: any) => { + return Promise.resolve({ + sessionId: 'test-uuid-123', + logFilePath: '/tmp/test.log', + processes: [], + error: undefined, + }); + }; + + const result = await start_sim_log_capLogic( + { + simulatorId: 'test-uuid', + bundleId: 'io.sentry.app', + subsystemFilter: 'swiftui', + }, + mockExecutor, + logCaptureStub, + ); + + expect(result.isError).toBeUndefined(); + expect(result.content[0].text).toContain('SwiftUI logs'); + expect(result.content[0].text).toContain('Self._printChanges()'); + }); + + it('should indicate all logs capture when subsystemFilter is all', async () => { + const mockExecutor = createMockExecutor({ success: true, output: '' }); + const logCaptureStub = (params: any, executor: any) => { + return Promise.resolve({ + sessionId: 'test-uuid-123', + logFilePath: '/tmp/test.log', + processes: [], + error: undefined, + }); + }; + + const result = await start_sim_log_capLogic( + { + simulatorId: 'test-uuid', + bundleId: 'io.sentry.app', + subsystemFilter: 'all', + }, + mockExecutor, + logCaptureStub, + ); + + expect(result.isError).toBeUndefined(); + expect(result.content[0].text).toContain('all system logs'); + }); + + it('should indicate custom subsystems when array is provided', async () => { + const mockExecutor = createMockExecutor({ success: true, output: '' }); + const logCaptureStub = (params: any, executor: any) => { + return Promise.resolve({ + sessionId: 'test-uuid-123', + logFilePath: '/tmp/test.log', + processes: [], + error: undefined, + }); + }; + + const result = await start_sim_log_capLogic( + { + simulatorId: 'test-uuid', + bundleId: 'io.sentry.app', + subsystemFilter: ['com.apple.UIKit', 'com.apple.CoreData'], + }, + mockExecutor, + logCaptureStub, + ); + + expect(result.isError).toBeUndefined(); + expect(result.content[0].text).toContain('com.apple.UIKit'); + expect(result.content[0].text).toContain('com.apple.CoreData'); }); it('should indicate console capture when captureConsole is true', async () => { @@ -128,16 +205,16 @@ describe('start_sim_log_cap plugin', () => { const result = await start_sim_log_capLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', captureConsole: true, + subsystemFilter: 'app', }, mockExecutor, logCaptureStub, ); - expect(result.content[0].text).toBe( - "Log capture started successfully. Session ID: test-uuid-123.\n\nNote: Your app was relaunched to capture console output.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.", - ); + expect(result.content[0].text).toContain('Your app was relaunched to capture console output'); + expect(result.content[0].text).toContain('test-uuid-123'); }); it('should create correct spawn commands for console capture', async () => { @@ -188,8 +265,9 @@ describe('start_sim_log_cap plugin', () => { await start_sim_log_capLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', captureConsole: true, + subsystemFilter: 'app', }, mockExecutor, logCaptureStub, @@ -205,7 +283,7 @@ describe('start_sim_log_cap plugin', () => { '--console-pty', '--terminate-running-process', 'test-uuid', - 'com.example.app', + 'io.sentry.app', ], }); expect(spawnCalls[1]).toEqual({ @@ -218,7 +296,7 @@ describe('start_sim_log_cap plugin', () => { 'stream', '--level=debug', '--predicate', - 'subsystem == "com.example.app"', + 'subsystem == "io.sentry.app"', ], }); }); @@ -257,8 +335,9 @@ describe('start_sim_log_cap plugin', () => { await start_sim_log_capLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', captureConsole: false, + subsystemFilter: 'app', }, mockExecutor, logCaptureStub, @@ -276,7 +355,7 @@ describe('start_sim_log_cap plugin', () => { 'stream', '--level=debug', '--predicate', - 'subsystem == "com.example.app"', + 'subsystem == "io.sentry.app"', ], }); }); diff --git a/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts b/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts index 48078135..2ec1888f 100644 --- a/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts @@ -4,8 +4,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { EventEmitter } from 'events'; import * as z from 'zod'; -import plugin, { stop_device_log_capLogic } from '../stop_device_log_cap.ts'; -import { activeDeviceLogSessions, type DeviceLogSession } from '../start_device_log_cap.ts'; +import { schema, handler, stop_device_log_capLogic } from '../stop_device_log_cap.ts'; +import { + activeDeviceLogSessions, + type DeviceLogSession, +} from '../../../../utils/log-capture/device-log-sessions.ts'; import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; // Note: Logger is allowed to execute normally (integration testing pattern) @@ -17,36 +20,24 @@ describe('stop_device_log_cap plugin', () => { }); describe('Plugin Structure', () => { - it('should export an object with required properties', () => { - expect(plugin).toHaveProperty('name'); - expect(plugin).toHaveProperty('description'); - expect(plugin).toHaveProperty('schema'); - expect(plugin).toHaveProperty('handler'); - }); - - it('should have correct tool name', () => { - expect(plugin.name).toBe('stop_device_log_cap'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe( - 'Stops an active Apple device log capture session and returns the captured logs.', - ); + it('should export schema and handler', () => { + expect(schema).toBeDefined(); + expect(handler).toBeDefined(); }); it('should have correct schema structure', () => { // Schema should be a plain object for MCP protocol compliance - expect(typeof plugin.schema).toBe('object'); - expect(plugin.schema).toHaveProperty('logSessionId'); + expect(typeof schema).toBe('object'); + expect(schema).toHaveProperty('logSessionId'); // Validate that schema fields are Zod types that can be used for validation - const schema = z.object(plugin.schema); - expect(schema.safeParse({ logSessionId: 'test-session-id' }).success).toBe(true); - expect(schema.safeParse({ logSessionId: 123 }).success).toBe(false); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({ logSessionId: 'test-session-id' }).success).toBe(true); + expect(schemaObj.safeParse({ logSessionId: 123 }).success).toBe(false); }); it('should have handler as a function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); }); @@ -84,13 +75,13 @@ describe('stop_device_log_cap plugin', () => { const result = await stop_device_log_capLogic( { - logSessionId: 'device-log-00008110-001A2C3D4E5F-com.example.MyApp', + logSessionId: 'device-log-00008110-001A2C3D4E5F-io.sentry.MyApp', }, mockFileSystem, ); expect(result.content[0].text).toBe( - 'Failed to stop device log capture session device-log-00008110-001A2C3D4E5F-com.example.MyApp: Device log capture session not found: device-log-00008110-001A2C3D4E5F-com.example.MyApp', + 'Failed to stop device log capture session device-log-00008110-001A2C3D4E5F-io.sentry.MyApp: Device log capture session not found: device-log-00008110-001A2C3D4E5F-io.sentry.MyApp', ); expect(result.isError).toBe(true); }); @@ -110,7 +101,7 @@ describe('stop_device_log_cap plugin', () => { process: testProcess as unknown as DeviceLogSession['process'], logFilePath: testLogFilePath, deviceUuid: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', hasEnded: false, }); @@ -155,7 +146,7 @@ describe('stop_device_log_cap plugin', () => { process: testProcess as unknown as DeviceLogSession['process'], logFilePath: testLogFilePath, deviceUuid: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', hasEnded: false, }); @@ -197,7 +188,7 @@ describe('stop_device_log_cap plugin', () => { process: testProcess as unknown as DeviceLogSession['process'], logFilePath: testLogFilePath, deviceUuid: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', hasEnded: false, }); @@ -239,7 +230,7 @@ describe('stop_device_log_cap plugin', () => { process: testProcess as unknown as DeviceLogSession['process'], logFilePath: testLogFilePath, deviceUuid: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', hasEnded: false, }); @@ -283,7 +274,7 @@ describe('stop_device_log_cap plugin', () => { process: testProcess as unknown as DeviceLogSession['process'], logFilePath: testLogFilePath, deviceUuid: '00008110-001A2C3D4E5F', - bundleId: 'com.example.MyApp', + bundleId: 'io.sentry.MyApp', hasEnded: false, }); diff --git a/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts b/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts index 56f95e46..ceefa5b2 100644 --- a/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts @@ -11,79 +11,46 @@ * Converted to pure dependency injection without vitest mocking. */ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import stopSimLogCap, { stop_sim_log_capLogic } from '../stop_sim_log_cap.ts'; -import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; -import { activeLogSessions } from '../../../../utils/log_capture.ts'; +import { schema, handler, stop_sim_log_capLogic } from '../stop_sim_log_cap.ts'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; describe('stop_sim_log_cap plugin', () => { - let mockFileSystem: any; - - beforeEach(() => { - mockFileSystem = createMockFileSystemExecutor(); - // Clear any active sessions before each test - activeLogSessions.clear(); - }); - - // Helper function to create a test log session - async function createTestLogSession(sessionId: string, logContent: string = '') { - const mockProcess = { - pid: 12345, - killed: false, - exitCode: null, - kill: () => {}, - }; - - const logFilePath = `/tmp/xcodemcp_sim_log_test_${sessionId}.log`; - - // Create actual file for the test - const fs = await import('fs/promises'); - await fs.writeFile(logFilePath, logContent, 'utf-8'); - - activeLogSessions.set(sessionId, { - processes: [mockProcess as any], - logFilePath: logFilePath, - simulatorUuid: 'test-simulator-uuid', - bundleId: 'com.example.TestApp', - }); - } + const mockExecutor = createMockExecutor({ success: true, output: '' }); + const mockFileSystem = createMockFileSystemExecutor(); describe('Export Field Validation (Literal)', () => { - it('should have correct plugin structure', () => { - expect(stopSimLogCap).toHaveProperty('name'); - expect(stopSimLogCap).toHaveProperty('description'); - expect(stopSimLogCap).toHaveProperty('schema'); - expect(stopSimLogCap).toHaveProperty('handler'); - - expect(stopSimLogCap.name).toBe('stop_sim_log_cap'); - expect(stopSimLogCap.description).toBe( - 'Stops an active simulator log capture session and returns the captured logs.', - ); - expect(typeof stopSimLogCap.handler).toBe('function'); - expect(typeof stopSimLogCap.schema).toBe('object'); + it('should export schema and handler', () => { + expect(schema).toBeDefined(); + expect(handler).toBeDefined(); + expect(typeof handler).toBe('function'); + expect(typeof schema).toBe('object'); }); it('should have correct schema structure', () => { // Schema should be a plain object for MCP protocol compliance - expect(typeof stopSimLogCap.schema).toBe('object'); - expect(stopSimLogCap.schema).toHaveProperty('logSessionId'); + expect(typeof schema).toBe('object'); + expect(schema).toHaveProperty('logSessionId'); // Validate that schema fields are Zod types that can be used for validation - const schema = z.object(stopSimLogCap.schema); - expect(schema.safeParse({ logSessionId: 'test-session-id' }).success).toBe(true); - expect(schema.safeParse({ logSessionId: 123 }).success).toBe(false); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({ logSessionId: 'test-session-id' }).success).toBe(true); + expect(schemaObj.safeParse({ logSessionId: 123 }).success).toBe(false); }); it('should validate schema with valid parameters', () => { - expect(stopSimLogCap.schema.logSessionId.safeParse('test-session-id').success).toBe(true); + expect(schema.logSessionId.safeParse('test-session-id').success).toBe(true); }); it('should reject invalid schema parameters', () => { - expect(stopSimLogCap.schema.logSessionId.safeParse(null).success).toBe(false); - expect(stopSimLogCap.schema.logSessionId.safeParse(undefined).success).toBe(false); - expect(stopSimLogCap.schema.logSessionId.safeParse(123).success).toBe(false); - expect(stopSimLogCap.schema.logSessionId.safeParse(true).success).toBe(false); + expect(schema.logSessionId.safeParse(null).success).toBe(false); + expect(schema.logSessionId.safeParse(undefined).success).toBe(false); + expect(schema.logSessionId.safeParse(123).success).toBe(false); + expect(schema.logSessionId.safeParse(true).success).toBe(false); }); }); @@ -91,12 +58,17 @@ describe('stop_sim_log_cap plugin', () => { it('should handle null logSessionId (validation handled by framework)', async () => { // With typed tool factory, invalid params won't reach the logic function // This test now validates that the logic function works with valid empty strings - await createTestLogSession('', 'Log content for empty session'); + const stopLogCaptureStub = async () => ({ + logContent: 'Log content for empty session', + error: undefined, + }); const result = await stop_sim_log_capLogic( { logSessionId: '', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -109,12 +81,17 @@ describe('stop_sim_log_cap plugin', () => { it('should handle undefined logSessionId (validation handled by framework)', async () => { // With typed tool factory, invalid params won't reach the logic function // This test now validates that the logic function works with valid empty strings - await createTestLogSession('', 'Log content for empty session'); + const stopLogCaptureStub = async () => ({ + logContent: 'Log content for empty session', + error: undefined, + }); const result = await stop_sim_log_capLogic( { logSessionId: '', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -125,12 +102,17 @@ describe('stop_sim_log_cap plugin', () => { }); it('should handle empty string logSessionId', async () => { - await createTestLogSession('', 'Log content for empty session'); + const stopLogCaptureStub = async () => ({ + logContent: 'Log content for empty session', + error: undefined, + }); const result = await stop_sim_log_capLogic( { logSessionId: '', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -143,15 +125,22 @@ describe('stop_sim_log_cap plugin', () => { describe('Function Call Generation', () => { it('should call stopLogCapture with correct parameters', async () => { - await createTestLogSession('test-session-id', 'Mock log content from file'); + let capturedSessionId = ''; + const stopLogCaptureStub = async (logSessionId: string) => { + capturedSessionId = logSessionId; + return { logContent: 'Mock log content from file', error: undefined }; + }; const result = await stop_sim_log_capLogic( { logSessionId: 'test-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); + expect(capturedSessionId).toBe('test-session-id'); expect(result.isError).toBeUndefined(); expect(result.content[0].text).toBe( 'Log capture session test-session-id stopped successfully. Log content follows:\n\nMock log content from file', @@ -159,15 +148,22 @@ describe('stop_sim_log_cap plugin', () => { }); it('should call stopLogCapture with different session ID', async () => { - await createTestLogSession('different-session-id', 'Different log content'); + let capturedSessionId = ''; + const stopLogCaptureStub = async (logSessionId: string) => { + capturedSessionId = logSessionId; + return { logContent: 'Different log content', error: undefined }; + }; const result = await stop_sim_log_capLogic( { logSessionId: 'different-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); + expect(capturedSessionId).toBe('different-session-id'); expect(result.isError).toBeUndefined(); expect(result.content[0].text).toBe( 'Log capture session different-session-id stopped successfully. Log content follows:\n\nDifferent log content', @@ -177,12 +173,17 @@ describe('stop_sim_log_cap plugin', () => { describe('Response Processing', () => { it('should handle successful log capture stop', async () => { - await createTestLogSession('test-session-id', 'Mock log content from file'); + const stopLogCaptureStub = async () => ({ + logContent: 'Mock log content from file', + error: undefined, + }); const result = await stop_sim_log_capLogic( { logSessionId: 'test-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -193,12 +194,17 @@ describe('stop_sim_log_cap plugin', () => { }); it('should handle empty log content', async () => { - await createTestLogSession('test-session-id', ''); + const stopLogCaptureStub = async () => ({ + logContent: '', + error: undefined, + }); const result = await stop_sim_log_capLogic( { logSessionId: 'test-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -209,12 +215,17 @@ describe('stop_sim_log_cap plugin', () => { }); it('should handle multiline log content', async () => { - await createTestLogSession('test-session-id', 'Line 1\nLine 2\nLine 3'); + const stopLogCaptureStub = async () => ({ + logContent: 'Line 1\nLine 2\nLine 3', + error: undefined, + }); const result = await stop_sim_log_capLogic( { logSessionId: 'test-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -225,10 +236,17 @@ describe('stop_sim_log_cap plugin', () => { }); it('should handle log capture stop errors for non-existent session', async () => { + const stopLogCaptureStub = async () => ({ + logContent: '', + error: 'Log capture session not found: non-existent-session', + }); + const result = await stop_sim_log_capLogic( { logSessionId: 'non-existent-session', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -239,25 +257,17 @@ describe('stop_sim_log_cap plugin', () => { }); it('should handle file read errors', async () => { - // Create session but make file reading fail in the log_capture utility - const mockProcess = { - pid: 12345, - killed: false, - exitCode: null, - kill: () => {}, - }; - - activeLogSessions.set('test-session-id', { - processes: [mockProcess as any], - logFilePath: `/tmp/test_file_not_found.log`, - simulatorUuid: 'test-simulator-uuid', - bundleId: 'com.example.TestApp', + const stopLogCaptureStub = async () => ({ + logContent: '', + error: 'ENOENT: no such file or directory', }); const result = await stop_sim_log_capLogic( { logSessionId: 'test-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -268,25 +278,17 @@ describe('stop_sim_log_cap plugin', () => { }); it('should handle permission errors', async () => { - // Create session but make file reading fail in the log_capture utility - const mockProcess = { - pid: 12345, - killed: false, - exitCode: null, - kill: () => {}, - }; - - activeLogSessions.set('test-session-id', { - processes: [mockProcess as any], - logFilePath: `/tmp/test_permission_denied.log`, - simulatorUuid: 'test-simulator-uuid', - bundleId: 'com.example.TestApp', + const stopLogCaptureStub = async () => ({ + logContent: '', + error: 'EACCES: permission denied', }); const result = await stop_sim_log_capLogic( { logSessionId: 'test-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); @@ -297,25 +299,17 @@ describe('stop_sim_log_cap plugin', () => { }); it('should handle various error types', async () => { - // Create session but make file reading fail in the log_capture utility - const mockProcess = { - pid: 12345, - killed: false, - exitCode: null, - kill: () => {}, - }; - - activeLogSessions.set('test-session-id', { - processes: [mockProcess as any], - logFilePath: `/tmp/test_generic_error.log`, - simulatorUuid: 'test-simulator-uuid', - bundleId: 'com.example.TestApp', + const stopLogCaptureStub = async () => ({ + logContent: '', + error: 'Unexpected error', }); const result = await stop_sim_log_capLogic( { logSessionId: 'test-session-id', }, + mockExecutor, + stopLogCaptureStub, mockFileSystem, ); diff --git a/src/mcp/tools/logging/index.ts b/src/mcp/tools/logging/index.ts deleted file mode 100644 index b634c991..00000000 --- a/src/mcp/tools/logging/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'Log Capture & Management', - description: - 'Log capture and management tools for iOS simulators and physical devices. Start, stop, and analyze application and system logs during development and testing.', -}; diff --git a/src/mcp/tools/logging/start_device_log_cap.ts b/src/mcp/tools/logging/start_device_log_cap.ts index c2085a67..1f8c2ee2 100644 --- a/src/mcp/tools/logging/start_device_log_cap.ts +++ b/src/mcp/tools/logging/start_device_log_cap.ts @@ -4,20 +4,28 @@ * Starts capturing logs from a specified Apple device by launching the app with console output. */ -import * as fs from 'fs'; import * as path from 'path'; -import * as os from 'os'; import type { ChildProcess } from 'child_process'; import { v4 as uuidv4 } from 'uuid'; import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; -import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import { + getDefaultCommandExecutor, + getDefaultFileSystemExecutor, +} from '../../../utils/execution/index.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { + activeDeviceLogSessions, + type DeviceLogSession, +} from '../../../utils/log-capture/device-log-sessions.ts'; +import type { WriteStream } from 'fs'; +import { getConfig } from '../../../utils/config-store.ts'; +import { acquireDaemonActivity } from '../../../daemon/activity-registry.ts'; /** * Log file retention policy for device logs: @@ -32,17 +40,6 @@ const DEVICE_LOG_FILE_PREFIX = 'xcodemcp_device_log_'; // - Devices use 'xcrun devicectl' with console output only (no OSLog streaming) // The different command structures and output formats make sharing infrastructure complex. // However, both follow similar patterns for session management and log retention. -export interface DeviceLogSession { - process: ChildProcess; - logFilePath: string; - deviceUuid: string; - bundleId: string; - logStream?: fs.WriteStream; - hasEnded: boolean; -} - -export const activeDeviceLogSessions = new Map(); - const EARLY_FAILURE_WINDOW_MS = 5000; const INITIAL_OUTPUT_LIMIT = 8_192; const DEFAULT_JSON_RESULT_WAIT_MS = 8000; @@ -76,17 +73,11 @@ type DevicectlLaunchJson = { }; function getJsonResultWaitMs(): number { - const raw = process.env.XBMCP_LAUNCH_JSON_WAIT_MS; - if (raw === undefined) { - return DEFAULT_JSON_RESULT_WAIT_MS; - } - - const parsed = Number(raw); - if (!Number.isFinite(parsed) || parsed < 0) { + const configured = getConfig().launchJsonWaitMs; + if (!Number.isFinite(configured) || configured < 0) { return DEFAULT_JSON_RESULT_WAIT_MS; } - - return parsed; + return configured; } function safeParseJson(text: string): DevicectlLaunchJson | null { @@ -158,18 +149,11 @@ function extractJsonOutcome(json: DevicectlLaunchJson | null): JsonOutcome | nul async function removeFileIfExists( targetPath: string, - fileExecutor?: FileSystemExecutor, + fileExecutor: FileSystemExecutor, ): Promise { try { - if (fileExecutor) { - if (fileExecutor.existsSync(targetPath)) { - await fileExecutor.rm(targetPath, { force: true }); - } - return; - } - - if (fs.existsSync(targetPath)) { - await fs.promises.rm(targetPath, { force: true }); + if (fileExecutor.existsSync(targetPath)) { + await fileExecutor.rm(targetPath, { force: true }); } } catch { // Best-effort cleanup only @@ -178,22 +162,20 @@ async function removeFileIfExists( async function pollJsonOutcome( jsonPath: string, - fileExecutor: FileSystemExecutor | undefined, + fileExecutor: FileSystemExecutor, timeoutMs: number, ): Promise { const start = Date.now(); const readOnce = async (): Promise => { try { - const exists = fileExecutor?.existsSync(jsonPath) ?? fs.existsSync(jsonPath); + const exists = fileExecutor.existsSync(jsonPath); if (!exists) { return null; } - const content = fileExecutor - ? await fileExecutor.readFile(jsonPath, 'utf8') - : await fs.promises.readFile(jsonPath, 'utf8'); + const content = await fileExecutor.readFile(jsonPath, 'utf8'); const outcome = extractJsonOutcome(safeParseJson(content)); if (outcome) { @@ -230,7 +212,7 @@ async function pollJsonOutcome( return null; } -type WriteStreamWithClosed = fs.WriteStream & { closed?: boolean }; +type WriteStreamWithClosed = WriteStream & { closed?: boolean }; /** * Start a log capture session for an iOS device by launching the app with console output. @@ -243,31 +225,25 @@ export async function startDeviceLogCapture( bundleId: string; }, executor: CommandExecutor = getDefaultCommandExecutor(), - fileSystemExecutor?: FileSystemExecutor, + fileSystemExecutor: FileSystemExecutor = getDefaultFileSystemExecutor(), ): Promise<{ sessionId: string; error?: string }> { // Clean up old logs before starting a new session - await cleanOldDeviceLogs(); + await cleanOldDeviceLogs(fileSystemExecutor); const { deviceUuid, bundleId } = params; const logSessionId = uuidv4(); const logFileName = `${DEVICE_LOG_FILE_PREFIX}${logSessionId}.log`; - const tempDir = fileSystemExecutor ? fileSystemExecutor.tmpdir() : os.tmpdir(); + const tempDir = fileSystemExecutor.tmpdir(); const logFilePath = path.join(tempDir, logFileName); const launchJsonPath = path.join(tempDir, `devicectl-launch-${logSessionId}.json`); - let logStream: fs.WriteStream | undefined; + let logStream: WriteStream | undefined; try { - // Use injected file system executor or default - if (fileSystemExecutor) { - await fileSystemExecutor.mkdir(tempDir, { recursive: true }); - await fileSystemExecutor.writeFile(logFilePath, ''); - } else { - await fs.promises.mkdir(tempDir, { recursive: true }); - await fs.promises.writeFile(logFilePath, ''); - } + await fileSystemExecutor.mkdir(tempDir, { recursive: true }); + await fileSystemExecutor.writeFile(logFilePath, ''); - logStream = fs.createWriteStream(logFilePath, { flags: 'a' }); + logStream = fileSystemExecutor.createWriteStream(logFilePath, { flags: 'a' }); logStream.write( `\n--- Device log capture for bundle ID: ${bundleId} on device: ${deviceUuid} ---\n`, @@ -456,6 +432,7 @@ export async function startDeviceLogCapture( // For testing purposes, we'll simulate process management // In actual usage, the process would be managed by the executor result + session.releaseActivity = acquireDaemonActivity('logging.device'); activeDeviceLogSessions.set(logSessionId, session); log('info', `Device log capture started with session ID: ${logSessionId}`); @@ -488,13 +465,10 @@ function detectEarlyLaunchFailure( registerImmediateFailure?: (handler: (message: string) => void) => void, ): Promise { if (process.exitCode != null) { - if (process.exitCode === 0) { - const failureFromOutput = extractFailureMessage(getBufferedOutput?.()); - return Promise.resolve( - failureFromOutput ? { exitCode: process.exitCode, errorMessage: failureFromOutput } : null, - ); - } const failureFromOutput = extractFailureMessage(getBufferedOutput?.()); + if (process.exitCode === 0 && !failureFromOutput) { + return Promise.resolve(null); + } return Promise.resolve({ exitCode: process.exitCode, errorMessage: failureFromOutput }); } @@ -516,14 +490,10 @@ function detectEarlyLaunchFailure( const onClose = (code: number | null): void => { const failureFromOutput = extractFailureMessage(getBufferedOutput?.()); - if (code === 0 && failureFromOutput) { - finalize({ exitCode: code ?? null, errorMessage: failureFromOutput }); - return; - } - if (code === 0) { + if (code === 0 && !failureFromOutput) { finalize(null); } else { - finalize({ exitCode: code ?? null, errorMessage: failureFromOutput }); + finalize({ exitCode: code, errorMessage: failureFromOutput }); } }; @@ -594,11 +564,11 @@ function extractFailureMessage(output?: string): string | undefined { */ // Device logs follow the same retention policy as simulator logs but use a different prefix // to avoid conflicts. Both clean up logs older than LOG_RETENTION_DAYS automatically. -async function cleanOldDeviceLogs(): Promise { - const tempDir = os.tmpdir(); - let files; +async function cleanOldDeviceLogs(fileSystemExecutor: FileSystemExecutor): Promise { + const tempDir = fileSystemExecutor.tmpdir(); + let files: unknown[]; try { - files = await fs.promises.readdir(tempDir); + files = await fileSystemExecutor.readdir(tempDir); } catch (err) { log( 'warn', @@ -608,15 +578,17 @@ async function cleanOldDeviceLogs(): Promise { } const now = Date.now(); const retentionMs = LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000; + const fileNames = files.filter((file): file is string => typeof file === 'string'); + await Promise.all( - files + fileNames .filter((f) => f.startsWith(DEVICE_LOG_FILE_PREFIX) && f.endsWith('.log')) .map(async (f) => { const filePath = path.join(tempDir, f); try { - const stat = await fs.promises.stat(filePath); + const stat = await fileSystemExecutor.stat(filePath); if (now - stat.mtimeMs > retentionMs) { - await fs.promises.unlink(filePath); + await fileSystemExecutor.rm(filePath, { force: true }); log('info', `Deleted old device log file: ${filePath}`); } } catch (err) { @@ -632,10 +604,13 @@ async function cleanOldDeviceLogs(): Promise { // Define schema as ZodObject const startDeviceLogCapSchema = z.object({ deviceId: z.string().describe('UDID of the device (obtained from list_devices)'), - bundleId: z.string().describe('Bundle identifier of the app to launch and capture logs for.'), + bundleId: z.string(), }); -const publicSchemaObject = startDeviceLogCapSchema.omit({ deviceId: true } as const); +const publicSchemaObject = startDeviceLogCapSchema.omit({ + deviceId: true, + bundleId: true, +} as const); // Use z.infer for type safety type StartDeviceLogCapParams = z.infer; @@ -650,13 +625,12 @@ export async function start_device_log_capLogic( ): Promise { const { deviceId, bundleId } = params; + const resolvedFileSystemExecutor = fileSystemExecutor ?? getDefaultFileSystemExecutor(); + const { sessionId, error } = await startDeviceLogCapture( - { - deviceUuid: deviceId, - bundleId: bundleId, - }, + { deviceUuid: deviceId, bundleId }, executor, - fileSystemExecutor, + resolvedFileSystemExecutor, ); if (error) { @@ -675,30 +649,23 @@ export async function start_device_log_capLogic( content: [ { type: 'text', - text: `✅ Device log capture started successfully\n\nSession ID: ${sessionId}\n\nNote: The app has been launched on the device with console output capture enabled.\n\nNext Steps:\n1. Interact with your app on the device\n2. Use stop_device_log_cap({ logSessionId: '${sessionId}' }) to stop capture and retrieve logs`, + text: `✅ Device log capture started successfully\n\nSession ID: ${sessionId}\n\nNote: The app has been launched on the device with console output capture enabled.\n\nInteract with your app on the device, then stop capture to retrieve logs.`, }, ], + nextStepParams: { + stop_device_log_cap: { logSessionId: sessionId }, + }, }; } -export default { - name: 'start_device_log_cap', - description: 'Starts log capture on a connected device.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: startDeviceLogCapSchema, - }), - annotations: { - title: 'Start Device Log Capture', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: startDeviceLogCapSchema as unknown as z.ZodType< - StartDeviceLogCapParams, - unknown - >, - logicFunction: start_device_log_capLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['deviceId'], message: 'deviceId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: startDeviceLogCapSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: startDeviceLogCapSchema as unknown as z.ZodType, + logicFunction: start_device_log_capLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['deviceId', 'bundleId'], message: 'Provide deviceId and bundleId' }], +}); diff --git a/src/mcp/tools/logging/start_sim_log_cap.ts b/src/mcp/tools/logging/start_sim_log_cap.ts index 04022e45..3ea427bd 100644 --- a/src/mcp/tools/logging/start_sim_log_cap.ts +++ b/src/mcp/tools/logging/start_sim_log_cap.ts @@ -6,8 +6,11 @@ import * as z from 'zod'; import { startLogCapture } from '../../../utils/log-capture/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; -import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/command.ts'; +import { getDefaultCommandExecutor } from '../../../utils/command.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createTextContent } from '../../../types/common.ts'; +import type { SubsystemFilter } from '../../../utils/log_capture.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -18,65 +21,83 @@ const startSimLogCapSchema = z.object({ simulatorId: z .uuid() .describe('UUID of the simulator to capture logs from (obtained from list_simulators).'), - bundleId: z.string().describe('Bundle identifier of the app to capture logs for.'), - captureConsole: z - .boolean() - .optional() - .describe('Whether to capture console output (requires app relaunch).'), + bundleId: z.string(), + captureConsole: z.boolean().optional(), + subsystemFilter: z + .union([z.enum(['app', 'all', 'swiftui']), z.array(z.string()).min(1)]) + .default('app') + .describe('app|all|swiftui|[subsystem]'), }); // Use z.infer for type safety type StartSimLogCapParams = z.infer; +function buildSubsystemFilterDescription(subsystemFilter: SubsystemFilter): string { + if (subsystemFilter === 'all') { + return 'Capturing all system logs (no subsystem filtering).'; + } + if (subsystemFilter === 'swiftui') { + return 'Capturing app logs + SwiftUI logs (includes Self._printChanges()).'; + } + if (Array.isArray(subsystemFilter)) { + if (subsystemFilter.length === 0) { + return 'Only structured logs from the app subsystem are being captured.'; + } + return `Capturing logs from subsystems: ${subsystemFilter.join(', ')} (plus app bundle ID).`; + } + + return 'Only structured logs from the app subsystem are being captured.'; +} + export async function start_sim_log_capLogic( params: StartSimLogCapParams, _executor: CommandExecutor = getDefaultCommandExecutor(), logCaptureFunction: typeof startLogCapture = startLogCapture, ): Promise { + const { bundleId, simulatorId, subsystemFilter } = params; const captureConsole = params.captureConsole ?? false; - const { sessionId, error } = await logCaptureFunction( - { - simulatorUuid: params.simulatorId, - bundleId: params.bundleId, - captureConsole, - }, - _executor, - ); + const logCaptureParams: Parameters[0] = { + simulatorUuid: simulatorId, + bundleId, + captureConsole, + subsystemFilter, + }; + const { sessionId, error } = await logCaptureFunction(logCaptureParams, _executor); if (error) { return { content: [createTextContent(`Error starting log capture: ${error}`)], isError: true, }; } + + const filterDescription = buildSubsystemFilterDescription(subsystemFilter); + return { content: [ createTextContent( - `Log capture started successfully. Session ID: ${sessionId}.\n\n${captureConsole ? 'Note: Your app was relaunched to capture console output.' : 'Note: Only structured logs are being captured.'}\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID '${sessionId}' to stop capture and retrieve logs.`, + `Log capture started successfully. Session ID: ${sessionId}.\n\n${captureConsole ? 'Note: Your app was relaunched to capture console output.\n' : ''}${filterDescription}\n\nInteract with your simulator and app, then stop capture to retrieve logs.`, ), ], + nextStepParams: { + stop_sim_log_cap: { logSessionId: sessionId }, + }, }; } const publicSchemaObject = z.strictObject( - startSimLogCapSchema.omit({ simulatorId: true } as const).shape, + startSimLogCapSchema.omit({ simulatorId: true, bundleId: true } as const).shape, ); -export default { - name: 'start_sim_log_cap', - description: - 'Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: startSimLogCapSchema, - }), - annotations: { - title: 'Start Simulator Log Capture', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: startSimLogCapSchema as unknown as z.ZodType, - logicFunction: start_sim_log_capLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: startSimLogCapSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: startSimLogCapSchema as unknown as z.ZodType, + logicFunction: start_sim_log_capLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['simulatorId', 'bundleId'], message: 'Provide simulatorId and bundleId' }, + ], +}); diff --git a/src/mcp/tools/logging/stop_device_log_cap.ts b/src/mcp/tools/logging/stop_device_log_cap.ts index 25c50469..4553cefd 100644 --- a/src/mcp/tools/logging/stop_device_log_cap.ts +++ b/src/mcp/tools/logging/stop_device_log_cap.ts @@ -7,15 +7,18 @@ import * as fs from 'fs'; import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; -import { activeDeviceLogSessions, type DeviceLogSession } from './start_device_log_cap.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import { + activeDeviceLogSessions, + type DeviceLogSession, +} from '../../../utils/log-capture/device-log-sessions.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { getDefaultFileSystemExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; -import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import type { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const stopDeviceLogCapSchema = z.object({ - logSessionId: z.string().describe('The session ID returned by start_device_log_cap.'), + logSessionId: z.string(), }); // Use z.infer for type safety @@ -63,6 +66,7 @@ export async function stop_device_log_capLogic( } const logFilePath = session.logFilePath; + session.releaseActivity?.(); activeDeviceLogSessions.delete(logSessionId); // Check file access @@ -176,6 +180,15 @@ function hasExistsSyncMethod(obj: unknown): obj is { existsSync: typeof fs.exist return typeof obj === 'object' && obj !== null && 'existsSync' in obj; } +/** + * Type guard to check if an object has createWriteStream method + */ +function hasCreateWriteStreamMethod( + obj: unknown, +): obj is { createWriteStream: typeof fs.createWriteStream } { + return typeof obj === 'object' && obj !== null && 'createWriteStream' in obj; +} + /** * Legacy support for backward compatibility */ @@ -213,6 +226,12 @@ export async function stopDeviceLogCapture( await fs.promises.writeFile(path, content, encoding); } }, + createWriteStream(path: string, options?: { flags?: string }) { + if (hasCreateWriteStreamMethod(fsToUse)) { + return fsToUse.createWriteStream(path, options); + } + return fs.createWriteStream(path, options); + }, async cp( source: string, destination: string, @@ -257,13 +276,13 @@ export async function stopDeviceLogCapture( return fs.existsSync(path); } }, - async stat(path: string): Promise<{ isDirectory(): boolean }> { + async stat(path: string): Promise<{ isDirectory(): boolean; mtimeMs: number }> { if (hasPromisesInterface(fsToUse)) { const result = await fsToUse.promises.stat(path); - return result as { isDirectory(): boolean }; + return result as { isDirectory(): boolean; mtimeMs: number }; } else { const result = await fs.promises.stat(path); - return result as { isDirectory(): boolean }; + return result as { isDirectory(): boolean; mtimeMs: number }; } }, async mkdtemp(prefix: string): Promise { @@ -308,19 +327,12 @@ export async function stopDeviceLogCapture( return { logContent }; } -export default { - name: 'stop_device_log_cap', - description: 'Stops an active Apple device log capture session and returns the captured logs.', - schema: stopDeviceLogCapSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Stop Device Log Capture', - destructiveHint: true, +export const schema = stopDeviceLogCapSchema.shape; // MCP SDK compatibility + +export const handler = createTypedTool( + stopDeviceLogCapSchema, + (params: StopDeviceLogCapParams) => { + return stop_device_log_capLogic(params, getDefaultFileSystemExecutor()); }, - handler: createTypedTool( - stopDeviceLogCapSchema, - (params: StopDeviceLogCapParams) => { - return stop_device_log_capLogic(params, getDefaultFileSystemExecutor()); - }, - getDefaultCommandExecutor, - ), -}; + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/logging/stop_sim_log_cap.ts b/src/mcp/tools/logging/stop_sim_log_cap.ts index 64523550..c6995b1d 100644 --- a/src/mcp/tools/logging/stop_sim_log_cap.ts +++ b/src/mcp/tools/logging/stop_sim_log_cap.ts @@ -6,13 +6,16 @@ import * as z from 'zod'; import { stopLogCapture as _stopLogCapture } from '../../../utils/log-capture/index.ts'; -import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createTextContent } from '../../../types/common.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; -import { getDefaultCommandExecutor } from '../../../utils/command.ts'; +import type { CommandExecutor } from '../../../utils/command.ts'; +import { getDefaultCommandExecutor, getDefaultFileSystemExecutor } from '../../../utils/command.ts'; +import type { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; // Define schema as ZodObject const stopSimLogCapSchema = z.object({ - logSessionId: z.string().describe('The session ID returned by start_sim_log_cap.'), + logSessionId: z.string(), }); // Use z.infer for type safety @@ -21,8 +24,18 @@ type StopSimLogCapParams = z.infer; /** * Business logic for stopping simulator log capture session */ -export async function stop_sim_log_capLogic(params: StopSimLogCapParams): Promise { - const { logContent, error } = await _stopLogCapture(params.logSessionId); +export type StopLogCaptureFunction = ( + logSessionId: string, + fileSystem?: FileSystemExecutor, +) => Promise<{ logContent: string; error?: string }>; + +export async function stop_sim_log_capLogic( + params: StopSimLogCapParams, + neverExecutor: CommandExecutor = getDefaultCommandExecutor(), + stopLogCaptureFunction: StopLogCaptureFunction = _stopLogCapture, + fileSystem: FileSystemExecutor = getDefaultFileSystemExecutor(), +): Promise { + const { logContent, error } = await stopLogCaptureFunction(params.logSessionId, fileSystem); if (error) { return { content: [ @@ -40,13 +53,11 @@ export async function stop_sim_log_capLogic(params: StopSimLogCapParams): Promis }; } -export default { - name: 'stop_sim_log_cap', - description: 'Stops an active simulator log capture session and returns the captured logs.', - schema: stopSimLogCapSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Stop Simulator Log Capture', - destructiveHint: true, - }, - handler: createTypedTool(stopSimLogCapSchema, stop_sim_log_capLogic, getDefaultCommandExecutor), -}; +export const schema = stopSimLogCapSchema.shape; // MCP SDK compatibility + +export const handler = createTypedTool( + stopSimLogCapSchema, + (params: StopSimLogCapParams, executor: CommandExecutor) => + stop_sim_log_capLogic(params, executor), + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/macos/__tests__/build_macos.test.ts b/src/mcp/tools/macos/__tests__/build_macos.test.ts index 83820a95..38ab2f52 100644 --- a/src/mcp/tools/macos/__tests__/build_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_macos.test.ts @@ -9,7 +9,8 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import buildMacOS, { buildMacOSLogic } from '../build_macos.ts'; +import { schema, handler } from '../build_macos.ts'; +import { buildMacOSLogic } from '../build_macos.ts'; describe('build_macos plugin', () => { beforeEach(() => { @@ -17,42 +18,28 @@ describe('build_macos plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(buildMacOS.name).toBe('build_macos'); - }); - - it('should have correct description', () => { - expect(buildMacOS.description).toBe('Builds a macOS app.'); - }); - it('should have handler function', () => { - expect(typeof buildMacOS.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { - const schema = z.object(buildMacOS.schema); + const zodSchema = z.strictObject(schema); - expect(schema.safeParse({}).success).toBe(true); - expect( - schema.safeParse({ - derivedDataPath: '/path/to/derived-data', - extraArgs: ['--arg1', '--arg2'], - preferXcodebuild: true, - }).success, - ).toBe(true); + expect(zodSchema.safeParse({}).success).toBe(true); + expect(zodSchema.safeParse({ extraArgs: ['--arg1', '--arg2'] }).success).toBe(true); - expect(schema.safeParse({ derivedDataPath: 42 }).success).toBe(false); - expect(schema.safeParse({ extraArgs: ['--ok', 1] }).success).toBe(false); - expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); + expect(zodSchema.safeParse({ derivedDataPath: '/path/to/derived-data' }).success).toBe(false); + expect(zodSchema.safeParse({ extraArgs: ['--ok', 1] }).success).toBe(false); + expect(zodSchema.safeParse({ preferXcodebuild: true }).success).toBe(false); - const schemaKeys = Object.keys(buildMacOS.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs']); }); }); describe('Handler Requirements', () => { it('should require scheme when no defaults provided', async () => { - const result = await buildMacOS.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('scheme is required'); @@ -62,7 +49,7 @@ describe('build_macos plugin', () => { it('should require project or workspace once scheme default exists', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await buildMacOS.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); @@ -71,7 +58,7 @@ describe('build_macos plugin', () => { it('should reject when both projectPath and workspacePath provided explicitly', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await buildMacOS.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', }); @@ -441,13 +428,13 @@ describe('build_macos plugin', () => { describe('XOR Validation', () => { it('should error when neither projectPath nor workspacePath provided', async () => { - const result = await buildMacOS.handler({ scheme: 'MyScheme' }); + const result = await handler({ scheme: 'MyScheme' }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); }); it('should error when both projectPath and workspacePath provided', async () => { - const result = await buildMacOS.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', scheme: 'MyScheme', diff --git a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts index bff9a478..552710a2 100644 --- a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts @@ -1,8 +1,9 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { createMockExecutor, mockProcess } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import tool, { buildRunMacOSLogic } from '../build_run_macos.ts'; +import { schema, handler } from '../build_run_macos.ts'; +import { buildRunMacOSLogic } from '../build_run_macos.ts'; describe('build_run_macos', () => { beforeEach(() => { @@ -10,42 +11,28 @@ describe('build_run_macos', () => { }); describe('Export Field Validation (Literal)', () => { - it('should export the correct name', () => { - expect(tool.name).toBe('build_run_macos'); - }); - - it('should export the correct description', () => { - expect(tool.description).toBe('Builds and runs a macOS app.'); - }); - it('should export a handler function', () => { - expect(typeof tool.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose only non-session fields in schema', () => { - const schema = z.object(tool.schema); - - expect(schema.safeParse({}).success).toBe(true); - expect( - schema.safeParse({ - derivedDataPath: '/tmp/derived', - extraArgs: ['--verbose'], - preferXcodebuild: true, - }).success, - ).toBe(true); - - expect(schema.safeParse({ derivedDataPath: 1 }).success).toBe(false); - expect(schema.safeParse({ extraArgs: ['--ok', 2] }).success).toBe(false); - expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); - - const schemaKeys = Object.keys(tool.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); + const zodSchema = z.strictObject(schema); + + expect(zodSchema.safeParse({}).success).toBe(true); + expect(zodSchema.safeParse({ extraArgs: ['--verbose'] }).success).toBe(true); + + expect(zodSchema.safeParse({ derivedDataPath: '/tmp/derived' }).success).toBe(false); + expect(zodSchema.safeParse({ extraArgs: ['--ok', 2] }).success).toBe(false); + expect(zodSchema.safeParse({ preferXcodebuild: true }).success).toBe(false); + + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs']); }); }); describe('Handler Requirements', () => { it('should require scheme before executing', async () => { - const result = await tool.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('scheme is required'); @@ -54,7 +41,7 @@ describe('build_run_macos', () => { it('should require project or workspace once scheme is set', async () => { sessionStore.setDefaults({ scheme: 'MyApp' }); - const result = await tool.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); @@ -63,7 +50,7 @@ describe('build_run_macos', () => { it('should fail when both project and workspace provided explicitly', async () => { sessionStore.setDefaults({ scheme: 'MyApp' }); - const result = await tool.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', }); @@ -80,12 +67,14 @@ describe('build_run_macos', () => { const executorCalls: any[] = []; const mockExecutor = ( command: string[], - description: string, - logOutput: boolean, - timeout?: number, + description?: string, + logOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { callCount++; - executorCalls.push({ command, description, logOutput, timeout }); + executorCalls.push({ command, description, logOutput, opts }); + void detached; if (callCount === 1) { // First call for build @@ -93,6 +82,7 @@ describe('build_run_macos', () => { success: true, output: 'BUILD SUCCEEDED', error: '', + process: mockProcess, }); } else if (callCount === 2) { // Second call for build settings @@ -100,9 +90,10 @@ describe('build_run_macos', () => { success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: '', + process: mockProcess, }); } - return Promise.resolve({ success: true, output: '', error: '' }); + return Promise.resolve({ success: true, output: '', error: '', process: mockProcess }); }; const args = { @@ -130,8 +121,8 @@ describe('build_run_macos', () => { 'build', ], description: 'macOS Build', - logOutput: true, - timeout: undefined, + logOutput: false, + opts: { cwd: '/path/to' }, }); // Verify build settings command was called @@ -147,8 +138,8 @@ describe('build_run_macos', () => { 'Debug', ], description: 'Get Build Settings for Launch', - logOutput: true, - timeout: undefined, + logOutput: false, + opts: undefined, }); expect(result).toEqual({ @@ -176,12 +167,14 @@ describe('build_run_macos', () => { const executorCalls: any[] = []; const mockExecutor = ( command: string[], - description: string, - logOutput: boolean, - timeout?: number, + description?: string, + logOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { callCount++; - executorCalls.push({ command, description, logOutput, timeout }); + executorCalls.push({ command, description, logOutput, opts }); + void detached; if (callCount === 1) { // First call for build @@ -189,6 +182,7 @@ describe('build_run_macos', () => { success: true, output: 'BUILD SUCCEEDED', error: '', + process: mockProcess, }); } else if (callCount === 2) { // Second call for build settings @@ -196,9 +190,10 @@ describe('build_run_macos', () => { success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: '', + process: mockProcess, }); } - return Promise.resolve({ success: true, output: '', error: '' }); + return Promise.resolve({ success: true, output: '', error: '', process: mockProcess }); }; const args = { @@ -226,8 +221,8 @@ describe('build_run_macos', () => { 'build', ], description: 'macOS Build', - logOutput: true, - timeout: undefined, + logOutput: false, + opts: { cwd: '/path/to' }, }); // Verify build settings command was called @@ -243,8 +238,8 @@ describe('build_run_macos', () => { 'Debug', ], description: 'Get Build Settings for Launch', - logOutput: true, - timeout: undefined, + logOutput: false, + opts: undefined, }); expect(result).toEqual({ @@ -296,17 +291,20 @@ describe('build_run_macos', () => { let callCount = 0; const mockExecutor = ( command: string[], - description: string, - logOutput: boolean, - timeout?: number, + description?: string, + logOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { callCount++; + void detached; if (callCount === 1) { // First call for build succeeds return Promise.resolve({ success: true, output: 'BUILD SUCCEEDED', error: '', + process: mockProcess, }); } else if (callCount === 2) { // Second call for build settings fails @@ -314,9 +312,10 @@ describe('build_run_macos', () => { success: false, output: '', error: 'error: Failed to get settings', + process: mockProcess, }); } - return Promise.resolve({ success: true, output: '', error: '' }); + return Promise.resolve({ success: true, output: '', error: '', process: mockProcess }); }; const args = { @@ -352,17 +351,20 @@ describe('build_run_macos', () => { let callCount = 0; const mockExecutor = ( command: string[], - description: string, - logOutput: boolean, - timeout?: number, + description?: string, + logOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { callCount++; + void detached; if (callCount === 1) { // First call for build succeeds return Promise.resolve({ success: true, output: 'BUILD SUCCEEDED', error: '', + process: mockProcess, }); } else if (callCount === 2) { // Second call for build settings succeeds @@ -370,6 +372,7 @@ describe('build_run_macos', () => { success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: '', + process: mockProcess, }); } else if (callCount === 3) { // Third call for open command fails @@ -377,9 +380,10 @@ describe('build_run_macos', () => { success: false, output: '', error: 'Failed to launch', + process: mockProcess, }); } - return Promise.resolve({ success: true, output: '', error: '' }); + return Promise.resolve({ success: true, output: '', error: '', process: mockProcess }); }; const args = { @@ -413,10 +417,16 @@ describe('build_run_macos', () => { it('should handle spawn error', async () => { const mockExecutor = ( command: string[], - description: string, - logOutput: boolean, - timeout?: number, + description?: string, + logOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { + void command; + void description; + void logOutput; + void opts; + void detached; return Promise.reject(new Error('spawn xcodebuild ENOENT')); }; @@ -443,12 +453,14 @@ describe('build_run_macos', () => { const executorCalls: any[] = []; const mockExecutor = ( command: string[], - description: string, - logOutput: boolean, - timeout?: number, + description?: string, + logOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { callCount++; - executorCalls.push({ command, description, logOutput, timeout }); + executorCalls.push({ command, description, logOutput, opts }); + void detached; if (callCount === 1) { // First call for build @@ -456,6 +468,7 @@ describe('build_run_macos', () => { success: true, output: 'BUILD SUCCEEDED', error: '', + process: mockProcess, }); } else if (callCount === 2) { // Second call for build settings @@ -463,9 +476,10 @@ describe('build_run_macos', () => { success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: '', + process: mockProcess, }); } - return Promise.resolve({ success: true, output: '', error: '' }); + return Promise.resolve({ success: true, output: '', error: '', process: mockProcess }); }; const args = { @@ -492,8 +506,8 @@ describe('build_run_macos', () => { 'build', ], description: 'macOS Build', - logOutput: true, - timeout: undefined, + logOutput: false, + opts: { cwd: '/path/to' }, }); }); }); diff --git a/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts b/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts index cbb7e43a..e2c4da98 100644 --- a/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts +++ b/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts @@ -5,9 +5,14 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + createMockCommandResponse, + createMockExecutor, + type CommandExecutor, +} from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import getMacAppPath, { get_mac_app_pathLogic } from '../get_mac_app_path.ts'; +import { schema, handler } from '../get_mac_app_path.ts'; +import { get_mac_app_pathLogic } from '../get_mac_app_path.ts'; describe('get_mac_app_path plugin', () => { beforeEach(() => { @@ -15,40 +20,32 @@ describe('get_mac_app_path plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(getMacAppPath.name).toBe('get_mac_app_path'); - }); - - it('should have correct description', () => { - expect(getMacAppPath.description).toBe('Retrieves the built macOS app bundle path.'); - }); - it('should have handler function', () => { - expect(typeof getMacAppPath.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { - const schema = z.object(getMacAppPath.schema); + const zodSchema = z.object(schema); - expect(schema.safeParse({}).success).toBe(true); + expect(zodSchema.safeParse({}).success).toBe(true); expect( - schema.safeParse({ + zodSchema.safeParse({ derivedDataPath: '/path/to/derived', extraArgs: ['--verbose'], }).success, ).toBe(true); - expect(schema.safeParse({ derivedDataPath: 7 }).success).toBe(false); - expect(schema.safeParse({ extraArgs: ['--bad', 1] }).success).toBe(false); + expect(zodSchema.safeParse({ derivedDataPath: 7 }).success).toBe(false); + expect(zodSchema.safeParse({ extraArgs: ['--bad', 1] }).success).toBe(false); - const schemaKeys = Object.keys(getMacAppPath.schema).sort(); + const schemaKeys = Object.keys(schema).sort(); expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs'].sort()); }); }); describe('Handler Requirements', () => { it('should require scheme before running', async () => { - const result = await getMacAppPath.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('scheme is required'); @@ -57,7 +54,7 @@ describe('get_mac_app_path plugin', () => { it('should require project or workspace when scheme default exists', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await getMacAppPath.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); @@ -66,7 +63,7 @@ describe('get_mac_app_path plugin', () => { it('should reject when both projectPath and workspacePath provided explicitly', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await getMacAppPath.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', }); @@ -78,7 +75,7 @@ describe('get_mac_app_path plugin', () => { describe('XOR Validation', () => { it('should error when neither projectPath nor workspacePath provided', async () => { - const result = await getMacAppPath.handler({ + const result = await handler({ scheme: 'MyScheme', }); @@ -87,7 +84,7 @@ describe('get_mac_app_path plugin', () => { }); it('should error when both projectPath and workspacePath provided', async () => { - const result = await getMacAppPath.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', scheme: 'MyScheme', @@ -104,12 +101,11 @@ describe('get_mac_app_path plugin', () => { const calls: any[] = []; const mockExecutor: CommandExecutor = async (...args) => { calls.push(args); - return { + return createMockCommandResponse({ success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: undefined, - process: { pid: 12345 }, - }; + }); }; const args = { @@ -133,7 +129,7 @@ describe('get_mac_app_path plugin', () => { 'Debug', ], 'Get App Path', - true, + false, undefined, ]); }); @@ -143,12 +139,11 @@ describe('get_mac_app_path plugin', () => { const calls: any[] = []; const mockExecutor: CommandExecutor = async (...args) => { calls.push(args); - return { + return createMockCommandResponse({ success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: undefined, - process: { pid: 12345 }, - }; + }); }; const args = { @@ -172,7 +167,7 @@ describe('get_mac_app_path plugin', () => { 'Debug', ], 'Get App Path', - true, + false, undefined, ]); }); @@ -182,19 +177,18 @@ describe('get_mac_app_path plugin', () => { const calls: any[] = []; const mockExecutor: CommandExecutor = async (...args) => { calls.push(args); - return { + return createMockCommandResponse({ success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: undefined, - process: { pid: 12345 }, - }; + }); }; const args = { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', configuration: 'Release', - arch: 'arm64', + arch: 'arm64' as const, }; await get_mac_app_pathLogic(args, mockExecutor); @@ -215,7 +209,7 @@ describe('get_mac_app_path plugin', () => { 'platform=macOS,arch=arm64', ], 'Get App Path', - true, + false, undefined, ]); }); @@ -225,19 +219,18 @@ describe('get_mac_app_path plugin', () => { const calls: any[] = []; const mockExecutor: CommandExecutor = async (...args) => { calls.push(args); - return { + return createMockCommandResponse({ success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: undefined, - process: { pid: 12345 }, - }; + }); }; const args = { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', configuration: 'Debug', - arch: 'x86_64', + arch: 'x86_64' as const, }; await get_mac_app_pathLogic(args, mockExecutor); @@ -258,7 +251,7 @@ describe('get_mac_app_path plugin', () => { 'platform=macOS,arch=x86_64', ], 'Get App Path', - true, + false, undefined, ]); }); @@ -268,12 +261,11 @@ describe('get_mac_app_path plugin', () => { const calls: any[] = []; const mockExecutor: CommandExecutor = async (...args) => { calls.push(args); - return { + return createMockCommandResponse({ success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: undefined, - process: { pid: 12345 }, - }; + }); }; const args = { @@ -303,7 +295,7 @@ describe('get_mac_app_path plugin', () => { '--verbose', ], 'Get App Path', - true, + false, undefined, ]); }); @@ -313,18 +305,17 @@ describe('get_mac_app_path plugin', () => { const calls: any[] = []; const mockExecutor: CommandExecutor = async (...args) => { calls.push(args); - return { + return createMockCommandResponse({ success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: undefined, - process: { pid: 12345 }, - }; + }); }; const args = { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', - arch: 'arm64', + arch: 'arm64' as const, }; await get_mac_app_pathLogic(args, mockExecutor); @@ -345,7 +336,7 @@ describe('get_mac_app_path plugin', () => { 'platform=macOS,arch=arm64', ], 'Get App Path', - true, + false, undefined, ]); }); @@ -353,7 +344,7 @@ describe('get_mac_app_path plugin', () => { describe('Handler Behavior (Complete Literal Returns)', () => { it('should return Zod validation error for missing scheme', async () => { - const result = await getMacAppPath.handler({ + const result = await handler({ workspacePath: '/path/to/MyProject.xcworkspace', }); @@ -385,11 +376,17 @@ FULL_PRODUCT_NAME = MyApp.app type: 'text', text: '✅ App path retrieved successfully: /Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app', }, - { - type: 'text', - text: 'Next Steps:\n1. Get bundle ID: get_app_bundle_id({ appPath: "/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app" })\n2. Launch app: launch_mac_app({ appPath: "/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app" })', - }, ], + nextStepParams: { + get_mac_bundle_id: { + appPath: + '/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app', + }, + launch_mac_app: { + appPath: + '/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app', + }, + }, }); }); @@ -416,11 +413,17 @@ FULL_PRODUCT_NAME = MyApp.app type: 'text', text: '✅ App path retrieved successfully: /Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app', }, - { - type: 'text', - text: 'Next Steps:\n1. Get bundle ID: get_app_bundle_id({ appPath: "/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app" })\n2. Launch app: launch_mac_app({ appPath: "/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app" })', - }, ], + nextStepParams: { + get_mac_bundle_id: { + appPath: + '/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app', + }, + launch_mac_app: { + appPath: + '/Users/test/Library/Developer/Xcode/DerivedData/MyApp-abc123/Build/Products/Debug/MyApp.app', + }, + }, }); }); diff --git a/src/mcp/tools/macos/__tests__/index.test.ts b/src/mcp/tools/macos/__tests__/index.test.ts deleted file mode 100644 index 0b6f902d..00000000 --- a/src/mcp/tools/macos/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for macos-project workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('macos-project workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('macOS Development'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts b/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts index b133842f..39e5ee5d 100644 --- a/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts +++ b/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts @@ -9,40 +9,34 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; -import launchMacApp, { launch_mac_appLogic } from '../launch_mac_app.ts'; +import { + createMockCommandResponse, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler } from '../launch_mac_app.ts'; +import { launch_mac_appLogic } from '../launch_mac_app.ts'; describe('launch_mac_app plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(launchMacApp.name).toBe('launch_mac_app'); - }); - - it('should have correct description', () => { - expect(launchMacApp.description).toBe( - "Launches a macOS application. IMPORTANT: You MUST provide the appPath parameter. Example: launch_mac_app({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_launch_macos_app.", - ); - }); - it('should have handler function', () => { - expect(typeof launchMacApp.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema with valid inputs', () => { - const schema = z.object(launchMacApp.schema); + const zodSchema = z.object(schema); expect( - schema.safeParse({ + zodSchema.safeParse({ appPath: '/path/to/MyApp.app', }).success, ).toBe(true); expect( - schema.safeParse({ + zodSchema.safeParse({ appPath: '/Applications/Calculator.app', args: ['--debug'], }).success, ).toBe(true); expect( - schema.safeParse({ + zodSchema.safeParse({ appPath: '/path/to/MyApp.app', args: ['--debug', '--verbose'], }).success, @@ -50,19 +44,19 @@ describe('launch_mac_app plugin', () => { }); it('should validate schema with invalid inputs', () => { - const schema = z.object(launchMacApp.schema); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ appPath: null }).success).toBe(false); - expect(schema.safeParse({ appPath: 123 }).success).toBe(false); - expect(schema.safeParse({ appPath: '/path/to/MyApp.app', args: 'not-array' }).success).toBe( - false, - ); + const zodSchema = z.object(schema); + expect(zodSchema.safeParse({}).success).toBe(false); + expect(zodSchema.safeParse({ appPath: null }).success).toBe(false); + expect(zodSchema.safeParse({ appPath: 123 }).success).toBe(false); + expect( + zodSchema.safeParse({ appPath: '/path/to/MyApp.app', args: 'not-array' }).success, + ).toBe(false); }); }); describe('Input Validation', () => { it('should handle non-existent app path', async () => { - const mockExecutor = async () => Promise.resolve({ stdout: '', stderr: '' }); + const mockExecutor = async () => Promise.resolve(createMockCommandResponse()); const mockFileSystem = createMockFileSystemExecutor({ existsSync: () => false, }); @@ -92,7 +86,7 @@ describe('launch_mac_app plugin', () => { const calls: any[] = []; const mockExecutor = async (command: string[]) => { calls.push({ command }); - return { stdout: '', stderr: '' }; + return createMockCommandResponse(); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -115,7 +109,7 @@ describe('launch_mac_app plugin', () => { const calls: any[] = []; const mockExecutor = async (command: string[]) => { calls.push({ command }); - return { stdout: '', stderr: '' }; + return createMockCommandResponse(); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -145,7 +139,7 @@ describe('launch_mac_app plugin', () => { const calls: any[] = []; const mockExecutor = async (command: string[]) => { calls.push({ command }); - return { stdout: '', stderr: '' }; + return createMockCommandResponse(); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -169,7 +163,7 @@ describe('launch_mac_app plugin', () => { const calls: any[] = []; const mockExecutor = async (command: string[]) => { calls.push({ command }); - return { stdout: '', stderr: '' }; + return createMockCommandResponse(); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -191,7 +185,7 @@ describe('launch_mac_app plugin', () => { describe('Response Processing', () => { it('should return successful launch response', async () => { - const mockExecutor = async () => Promise.resolve({ stdout: '', stderr: '' }); + const mockExecutor = async () => Promise.resolve(createMockCommandResponse()); const mockFileSystem = createMockFileSystemExecutor({ existsSync: () => true, @@ -216,7 +210,7 @@ describe('launch_mac_app plugin', () => { }); it('should return successful launch response with args', async () => { - const mockExecutor = async () => Promise.resolve({ stdout: '', stderr: '' }); + const mockExecutor = async () => Promise.resolve(createMockCommandResponse()); const mockFileSystem = createMockFileSystemExecutor({ existsSync: () => true, diff --git a/src/mcp/tools/macos/__tests__/re-exports.test.ts b/src/mcp/tools/macos/__tests__/re-exports.test.ts index f8f31ca6..ee4540c2 100644 --- a/src/mcp/tools/macos/__tests__/re-exports.test.ts +++ b/src/mcp/tools/macos/__tests__/re-exports.test.ts @@ -1,88 +1,94 @@ /** - * Tests for macos-project re-export files - * These files re-export tools from macos-workspace to avoid duplication + * Tests for macos tool module exports + * Validates that tools export the required named exports (schema, handler) + * Note: name and description are now defined in manifests, not in modules */ import { describe, it, expect } from 'vitest'; -// Import all re-export tools -import testMacos from '../test_macos.ts'; -import buildMacos from '../build_macos.ts'; -import buildRunMacos from '../build_run_macos.ts'; -import getMacAppPath from '../get_mac_app_path.ts'; +// Import all tool modules using named exports +import * as testMacos from '../test_macos.ts'; +import * as buildMacos from '../build_macos.ts'; +import * as buildRunMacos from '../build_run_macos.ts'; +import * as getMacAppPath from '../get_mac_app_path.ts'; +import * as launchMacApp from '../launch_mac_app.ts'; +import * as stopMacApp from '../stop_mac_app.ts'; -describe('macos-project re-exports', () => { - describe('test_macos re-export', () => { - it('should re-export test_macos tool correctly', () => { - expect(testMacos.name).toBe('test_macos'); +describe('macos tool module exports', () => { + describe('test_macos exports', () => { + it('should export schema and handler', () => { expect(typeof testMacos.handler).toBe('function'); expect(testMacos.schema).toBeDefined(); - expect(typeof testMacos.description).toBe('string'); + expect(typeof testMacos.schema).toBe('object'); }); }); - describe('build_macos re-export', () => { - it('should re-export build_macos tool correctly', () => { - expect(buildMacos.name).toBe('build_macos'); + describe('build_macos exports', () => { + it('should export schema and handler', () => { expect(typeof buildMacos.handler).toBe('function'); expect(buildMacos.schema).toBeDefined(); - expect(typeof buildMacos.description).toBe('string'); + expect(typeof buildMacos.schema).toBe('object'); }); }); - describe('build_run_macos re-export', () => { - it('should re-export build_run_macos tool correctly', () => { - expect(buildRunMacos.name).toBe('build_run_macos'); + describe('build_run_macos exports', () => { + it('should export schema and handler', () => { expect(typeof buildRunMacos.handler).toBe('function'); expect(buildRunMacos.schema).toBeDefined(); - expect(typeof buildRunMacos.description).toBe('string'); + expect(typeof buildRunMacos.schema).toBe('object'); }); }); - describe('get_mac_app_path re-export', () => { - it('should re-export get_mac_app_path tool correctly', () => { - expect(getMacAppPath.name).toBe('get_mac_app_path'); + describe('get_mac_app_path exports', () => { + it('should export schema and handler', () => { expect(typeof getMacAppPath.handler).toBe('function'); expect(getMacAppPath.schema).toBeDefined(); - expect(typeof getMacAppPath.description).toBe('string'); + expect(typeof getMacAppPath.schema).toBe('object'); }); }); - describe('All re-exports validation', () => { - const reExports = [ - { tool: testMacos, name: 'test_macos' }, - { tool: buildMacos, name: 'build_macos' }, - { tool: buildRunMacos, name: 'build_run_macos' }, - { tool: getMacAppPath, name: 'get_mac_app_path' }, + describe('launch_mac_app exports', () => { + it('should export schema and handler', () => { + expect(typeof launchMacApp.handler).toBe('function'); + expect(launchMacApp.schema).toBeDefined(); + expect(typeof launchMacApp.schema).toBe('object'); + }); + }); + + describe('stop_mac_app exports', () => { + it('should export schema and handler', () => { + expect(typeof stopMacApp.handler).toBe('function'); + expect(stopMacApp.schema).toBeDefined(); + expect(typeof stopMacApp.schema).toBe('object'); + }); + }); + + describe('All tool modules validation', () => { + const toolModules = [ + { module: testMacos, name: 'test_macos' }, + { module: buildMacos, name: 'build_macos' }, + { module: buildRunMacos, name: 'build_run_macos' }, + { module: getMacAppPath, name: 'get_mac_app_path' }, + { module: launchMacApp, name: 'launch_mac_app' }, + { module: stopMacApp, name: 'stop_mac_app' }, ]; - it('should have all required tool properties', () => { - reExports.forEach(({ tool, name }) => { - expect(tool).toHaveProperty('name'); - expect(tool).toHaveProperty('description'); - expect(tool).toHaveProperty('schema'); - expect(tool).toHaveProperty('handler'); - expect(tool.name).toBe(name); + it('should have all required exports', () => { + toolModules.forEach(({ module, name }) => { + expect(module).toHaveProperty('schema'); + expect(module).toHaveProperty('handler'); }); }); it('should have callable handlers', () => { - reExports.forEach(({ tool, name }) => { - expect(typeof tool.handler).toBe('function'); - expect(tool.handler.length).toBeGreaterThanOrEqual(0); + toolModules.forEach(({ module }) => { + expect(typeof module.handler).toBe('function'); }); }); it('should have valid schemas', () => { - reExports.forEach(({ tool, name }) => { - expect(tool.schema).toBeDefined(); - expect(typeof tool.schema).toBe('object'); - }); - }); - - it('should have non-empty descriptions', () => { - reExports.forEach(({ tool, name }) => { - expect(typeof tool.description).toBe('string'); - expect(tool.description.length).toBeGreaterThan(0); + toolModules.forEach(({ module }) => { + expect(module.schema).toBeDefined(); + expect(typeof module.schema).toBe('object'); }); }); }); diff --git a/src/mcp/tools/macos/__tests__/stop_mac_app.test.ts b/src/mcp/tools/macos/__tests__/stop_mac_app.test.ts index b512ff99..86086966 100644 --- a/src/mcp/tools/macos/__tests__/stop_mac_app.test.ts +++ b/src/mcp/tools/macos/__tests__/stop_mac_app.test.ts @@ -11,35 +11,26 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import stopMacApp, { stop_mac_appLogic } from '../stop_mac_app.ts'; +import { schema, handler } from '../stop_mac_app.ts'; +import { stop_mac_appLogic } from '../stop_mac_app.ts'; describe('stop_mac_app plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(stopMacApp.name).toBe('stop_mac_app'); - }); - - it('should have correct description', () => { - expect(stopMacApp.description).toBe( - 'Stops a running macOS application. Can stop by app name or process ID.', - ); - }); - it('should have handler function', () => { - expect(typeof stopMacApp.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { // Test optional fields - expect(stopMacApp.schema.appName.safeParse('Calculator').success).toBe(true); - expect(stopMacApp.schema.appName.safeParse(undefined).success).toBe(true); - expect(stopMacApp.schema.processId.safeParse(1234).success).toBe(true); - expect(stopMacApp.schema.processId.safeParse(undefined).success).toBe(true); + expect(schema.appName.safeParse('Calculator').success).toBe(true); + expect(schema.appName.safeParse(undefined).success).toBe(true); + expect(schema.processId.safeParse(1234).success).toBe(true); + expect(schema.processId.safeParse(undefined).success).toBe(true); // Test invalid inputs - expect(stopMacApp.schema.appName.safeParse(null).success).toBe(false); - expect(stopMacApp.schema.processId.safeParse('not-number').success).toBe(false); - expect(stopMacApp.schema.processId.safeParse(null).success).toBe(false); + expect(schema.appName.safeParse(null).success).toBe(false); + expect(schema.processId.safeParse('not-number').success).toBe(false); + expect(schema.processId.safeParse(null).success).toBe(false); }); }); diff --git a/src/mcp/tools/macos/__tests__/test_macos.test.ts b/src/mcp/tools/macos/__tests__/test_macos.test.ts index 57eb7a3e..71f6d562 100644 --- a/src/mcp/tools/macos/__tests__/test_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/test_macos.test.ts @@ -5,9 +5,24 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + createMockCommandResponse, + createMockExecutor, + createMockFileSystemExecutor, + type FileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import testMacos, { testMacosLogic } from '../test_macos.ts'; +import { schema, handler } from '../test_macos.ts'; +import { testMacosLogic } from '../test_macos.ts'; + +const createTestFileSystemExecutor = (overrides: Partial = {}) => + createMockFileSystemExecutor({ + mkdtemp: async () => '/tmp/test-123', + rm: async () => {}, + tmpdir: () => '/tmp', + stat: async () => ({ isDirectory: () => true, mtimeMs: 0 }), + ...overrides, + }); describe('test_macos plugin (unified)', () => { beforeEach(() => { @@ -15,46 +30,34 @@ describe('test_macos plugin (unified)', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(testMacos.name).toBe('test_macos'); - }); - - it('should have correct description', () => { - expect(testMacos.description).toBe('Runs tests for a macOS target.'); - }); - it('should have handler function', () => { - expect(typeof testMacos.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { - const schema = z.object(testMacos.schema); + const zodSchema = z.strictObject(schema); - expect(schema.safeParse({}).success).toBe(true); + expect(zodSchema.safeParse({}).success).toBe(true); expect( - schema.safeParse({ - derivedDataPath: '/path/to/derived-data', + zodSchema.safeParse({ extraArgs: ['--arg1', '--arg2'], - preferXcodebuild: true, testRunnerEnv: { FOO: 'BAR' }, }).success, ).toBe(true); - expect(schema.safeParse({ derivedDataPath: 123 }).success).toBe(false); - expect(schema.safeParse({ extraArgs: ['--ok', 1] }).success).toBe(false); - expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); - expect(schema.safeParse({ testRunnerEnv: { FOO: 123 } }).success).toBe(false); + expect(zodSchema.safeParse({ derivedDataPath: '/path/to/derived-data' }).success).toBe(false); + expect(zodSchema.safeParse({ extraArgs: ['--ok', 1] }).success).toBe(false); + expect(zodSchema.safeParse({ preferXcodebuild: true }).success).toBe(false); + expect(zodSchema.safeParse({ testRunnerEnv: { FOO: 123 } }).success).toBe(false); - const schemaKeys = Object.keys(testMacos.schema).sort(); - expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'testRunnerEnv'].sort(), - ); + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs', 'testRunnerEnv'].sort()); }); }); describe('Handler Requirements', () => { it('should require scheme before running', async () => { - const result = await testMacos.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('scheme is required'); @@ -63,7 +66,7 @@ describe('test_macos plugin (unified)', () => { it('should require project or workspace when scheme default exists', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await testMacos.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); @@ -72,7 +75,7 @@ describe('test_macos plugin (unified)', () => { it('should reject when both projectPath and workspacePath provided explicitly', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await testMacos.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', }); @@ -85,7 +88,7 @@ describe('test_macos plugin (unified)', () => { describe('XOR Parameter Validation', () => { it('should validate that either projectPath or workspacePath is provided', async () => { // Should return error response when neither is provided - const result = await testMacos.handler({ + const result = await handler({ scheme: 'MyScheme', }); @@ -95,7 +98,7 @@ describe('test_macos plugin (unified)', () => { it('should validate that both projectPath and workspacePath cannot be provided', async () => { // Should return error response when both are provided - const result = await testMacos.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', scheme: 'MyScheme', @@ -111,12 +114,7 @@ describe('test_macos plugin (unified)', () => { output: 'Test Suite All Tests passed', }); - const mockFileSystemExecutor = { - mkdtemp: async () => '/tmp/test-123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + const mockFileSystemExecutor = createTestFileSystemExecutor(); const result = await testMacosLogic( { @@ -138,12 +136,7 @@ describe('test_macos plugin (unified)', () => { output: 'Test Suite All Tests passed', }); - const mockFileSystemExecutor = { - mkdtemp: async () => '/tmp/test-123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + const mockFileSystemExecutor = createTestFileSystemExecutor(); const result = await testMacosLogic( { @@ -168,12 +161,7 @@ describe('test_macos plugin (unified)', () => { }); // Mock file system dependencies - const mockFileSystemExecutor = { - mkdtemp: async () => '/tmp/test-123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + const mockFileSystemExecutor = createTestFileSystemExecutor(); const result = await testMacosLogic( { @@ -197,12 +185,7 @@ describe('test_macos plugin (unified)', () => { }); // Mock file system dependencies - const mockFileSystemExecutor = { - mkdtemp: async () => '/tmp/test-123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + const mockFileSystemExecutor = createTestFileSystemExecutor(); const result = await testMacosLogic( { @@ -226,12 +209,7 @@ describe('test_macos plugin (unified)', () => { }); // Mock file system dependencies - const mockFileSystemExecutor = { - mkdtemp: async () => '/tmp/test-123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + const mockFileSystemExecutor = createTestFileSystemExecutor(); const result = await testMacosLogic( { @@ -254,12 +232,7 @@ describe('test_macos plugin (unified)', () => { }); // Mock file system dependencies - const mockFileSystemExecutor = { - mkdtemp: async () => '/tmp/test-123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + const mockFileSystemExecutor = createTestFileSystemExecutor(); const result = await testMacosLogic( { @@ -286,12 +259,7 @@ describe('test_macos plugin (unified)', () => { }); // Mock file system dependencies - const mockFileSystemExecutor = { - mkdtemp: async () => '/tmp/test-123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + const mockFileSystemExecutor = createTestFileSystemExecutor(); const result = await testMacosLogic( { @@ -316,13 +284,15 @@ describe('test_macos plugin (unified)', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record }, + detached?: boolean, ) => { - commandCalls.push({ command, logPrefix, useShell, env }); + commandCalls.push({ command, logPrefix, useShell, env: opts?.env }); + void detached; // Handle xcresulttool command if (command.includes('xcresulttool')) { - return { + return createMockCommandResponse({ success: true, output: JSON.stringify({ title: 'Test Results', @@ -334,24 +304,20 @@ describe('test_macos plugin (unified)', () => { expectedFailures: 0, }), error: undefined, - }; + }); } - return { + return createMockCommandResponse({ success: true, output: 'Test Succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; // Mock file system dependencies using approved utility - const mockFileSystemExecutor = { + const mockFileSystemExecutor = createTestFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-abc123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + }); const result = await testMacosLogic( { @@ -380,7 +346,7 @@ describe('test_macos plugin (unified)', () => { 'test', ]); expect(commandCalls[0].logPrefix).toBe('Test Run'); - expect(commandCalls[0].useShell).toBe(true); + expect(commandCalls[0].useShell).toBe(false); // Verify xcresulttool was called expect(commandCalls[1].command).toEqual([ @@ -411,23 +377,27 @@ describe('test_macos plugin (unified)', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record }, + detached?: boolean, ) => { callCount++; + void logPrefix; + void useShell; + void opts; + void detached; // First call is xcodebuild test - fails if (callCount === 1) { - return { + return createMockCommandResponse({ success: false, output: '', error: 'error: Test failed', - process: { pid: 12345 }, - }; + }); } // Second call is xcresulttool if (command.includes('xcresulttool')) { - return { + return createMockCommandResponse({ success: true, output: JSON.stringify({ title: 'Test Results', @@ -439,19 +409,16 @@ describe('test_macos plugin (unified)', () => { expectedFailures: 0, }), error: undefined, - }; + }); } - return { success: true, output: '', error: undefined }; + return createMockCommandResponse({ success: true, output: '', error: undefined }); }; // Mock file system dependencies - const mockFileSystemExecutor = { + const mockFileSystemExecutor = createTestFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-abc123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + }); const result = await testMacosLogic( { @@ -482,13 +449,15 @@ describe('test_macos plugin (unified)', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record }, + detached?: boolean, ) => { - commandCalls.push({ command, logPrefix, useShell, env }); + commandCalls.push({ command, logPrefix, useShell, env: opts?.env }); + void detached; // Handle xcresulttool command if (command.includes('xcresulttool')) { - return { + return createMockCommandResponse({ success: true, output: JSON.stringify({ title: 'Test Results', @@ -500,24 +469,20 @@ describe('test_macos plugin (unified)', () => { expectedFailures: 0, }), error: undefined, - }; + }); } - return { + return createMockCommandResponse({ success: true, output: 'Test Succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; // Mock file system dependencies - const mockFileSystemExecutor = { + const mockFileSystemExecutor = createTestFileSystemExecutor({ mkdtemp: async () => '/tmp/xcodebuild-test-abc123', - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + }); const result = await testMacosLogic( { @@ -550,14 +515,11 @@ describe('test_macos plugin (unified)', () => { }); // Mock file system dependencies - mkdtemp fails - const mockFileSystemExecutor = { + const mockFileSystemExecutor = createTestFileSystemExecutor({ mkdtemp: async () => { throw new Error('Network error'); }, - rm: async () => {}, - tmpdir: () => '/tmp', - stat: async () => ({ isDirectory: () => true }), - }; + }); const result = await testMacosLogic( { diff --git a/src/mcp/tools/macos/build_macos.ts b/src/mcp/tools/macos/build_macos.ts index 66bdab09..b91e4129 100644 --- a/src/mcp/tools/macos/build_macos.ts +++ b/src/mcp/tools/macos/build_macos.ts @@ -8,7 +8,8 @@ import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; -import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { @@ -33,19 +34,13 @@ const baseSchemaObject = z.object({ workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), scheme: z.string().describe('The scheme to use'), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - derivedDataPath: z - .string() - .optional() - .describe('Path where build products and other derived data will go'), + derivedDataPath: z.string().optional(), arch: z .enum(['arm64', 'x86_64']) .optional() .describe('Architecture to build for (arm64 or x86_64). For macOS only.'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), - preferXcodebuild: z - .boolean() - .optional() - .describe('If true, prefers xcodebuild over the experimental incremental build system'), + extraArgs: z.array(z.string()).optional(), + preferXcodebuild: z.boolean().optional(), }); const publicSchemaObject = baseSchemaObject.omit({ @@ -54,6 +49,8 @@ const publicSchemaObject = baseSchemaObject.omit({ scheme: true, configuration: true, arch: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); const buildMacOSSchema = z.preprocess( @@ -99,25 +96,18 @@ export async function buildMacOSLogic( ); } -export default { - name: 'build_macos', - description: 'Builds a macOS app.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Build macOS', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: buildMacOSSchema as unknown as z.ZodType, - logicFunction: buildMacOSLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: buildMacOSSchema as unknown as z.ZodType, + logicFunction: buildMacOSLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/macos/build_run_macos.ts b/src/mcp/tools/macos/build_run_macos.ts index 755629eb..6233b2cb 100644 --- a/src/mcp/tools/macos/build_run_macos.ts +++ b/src/mcp/tools/macos/build_run_macos.ts @@ -9,7 +9,8 @@ import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse } from '../../../utils/responses/index.ts'; import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; -import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { @@ -24,19 +25,13 @@ const baseSchemaObject = z.object({ workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), scheme: z.string().describe('The scheme to use'), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - derivedDataPath: z - .string() - .optional() - .describe('Path where build products and other derived data will go'), + derivedDataPath: z.string().optional(), arch: z .enum(['arm64', 'x86_64']) .optional() .describe('Architecture to build for (arm64 or x86_64). For macOS only.'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), - preferXcodebuild: z - .boolean() - .optional() - .describe('If true, prefers xcodebuild over the experimental incremental build system'), + extraArgs: z.array(z.string()).optional(), + preferXcodebuild: z.boolean().optional(), }); const publicSchemaObject = baseSchemaObject.omit({ @@ -45,6 +40,8 @@ const publicSchemaObject = baseSchemaObject.omit({ scheme: true, configuration: true, arch: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); const buildRunMacOSSchema = z.preprocess( @@ -115,7 +112,7 @@ async function _getAppPathFromBuildSettings( } // Execute the command directly - const result = await executor(command, 'Get Build Settings for Launch', true, undefined); + const result = await executor(command, 'Get Build Settings for Launch', false, undefined); if (!result.success) { return { @@ -180,7 +177,7 @@ export async function buildRunMacOSLogic( log('info', `App path determined as: ${appPath}`); // 4. Launch the app using CommandExecutor - const launchResult = await executor(['open', appPath], 'Launch macOS App', true); + const launchResult = await executor(['open', appPath], 'Launch macOS App', false); if (!launchResult.success) { log('error', `Build succeeded, but failed to launch app ${appPath}: ${launchResult.error}`); @@ -217,25 +214,18 @@ export async function buildRunMacOSLogic( } } -export default { - name: 'build_run_macos', - description: 'Builds and runs a macOS app.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Build Run macOS', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: buildRunMacOSSchema as unknown as z.ZodType, - logicFunction: buildRunMacOSLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: buildRunMacOSSchema as unknown as z.ZodType, + logicFunction: buildRunMacOSLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/macos/clean.ts b/src/mcp/tools/macos/clean.ts deleted file mode 100644 index 5af33211..00000000 --- a/src/mcp/tools/macos/clean.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified clean tool for macos-project workflow -export { default } from '../utilities/clean.ts'; diff --git a/src/mcp/tools/macos/discover_projs.ts b/src/mcp/tools/macos/discover_projs.ts deleted file mode 100644 index 58fbf05d..00000000 --- a/src/mcp/tools/macos/discover_projs.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/discover_projs.ts'; diff --git a/src/mcp/tools/macos/get_mac_app_path.ts b/src/mcp/tools/macos/get_mac_app_path.ts index 5339de05..dfc32c7c 100644 --- a/src/mcp/tools/macos/get_mac_app_path.ts +++ b/src/mcp/tools/macos/get_mac_app_path.ts @@ -6,7 +6,8 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; @@ -16,12 +17,11 @@ import { } from '../../../utils/typed-tool-factory.ts'; import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; -// Unified schema: XOR between projectPath and workspacePath, sharing common options const baseOptions = { scheme: z.string().describe('The scheme to use'), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - derivedDataPath: z.string().optional().describe('Path to derived data directory'), - extraArgs: z.array(z.string()).optional().describe('Additional arguments to pass to xcodebuild'), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), arch: z .enum(['arm64', 'x86_64']) .optional() @@ -53,21 +53,8 @@ const getMacosAppPathSchema = z.preprocess( }), ); -// Use z.infer for type safety type GetMacosAppPathParams = z.infer; -const XcodePlatform = { - iOS: 'iOS', - watchOS: 'watchOS', - tvOS: 'tvOS', - visionOS: 'visionOS', - iOSSimulator: 'iOS Simulator', - watchOSSimulator: 'watchOS Simulator', - tvOSSimulator: 'tvOS Simulator', - visionOSSimulator: 'visionOS Simulator', - macOS: 'macOS', -}; - export async function get_mac_app_pathLogic( params: GetMacosAppPathParams, executor: CommandExecutor, @@ -105,13 +92,12 @@ export async function get_mac_app_pathLogic( command.push('-destination', destinationString); } - // Add extra arguments if provided - if (params.extraArgs && Array.isArray(params.extraArgs)) { + if (params.extraArgs) { command.push(...params.extraArgs); } // Execute the command directly with executor - const result = await executor(command, 'Get App Path', true, undefined); + const result = await executor(command, 'Get App Path', false, undefined); if (!result.success) { return { @@ -157,22 +143,17 @@ export async function get_mac_app_pathLogic( const fullProductName = fullProductNameMatch[1].trim(); const appPath = `${builtProductsDir}/${fullProductName}`; - // Include next steps guidance (following workspace pattern) - const nextStepsText = `Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${appPath}" }) -2. Launch app: launch_mac_app({ appPath: "${appPath}" })`; - return { content: [ { type: 'text', text: `✅ App path retrieved successfully: ${appPath}`, }, - { - type: 'text', - text: nextStepsText, - }, ], + nextStepParams: { + get_mac_bundle_id: { appPath }, + launch_mac_app: { appPath }, + }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -189,25 +170,18 @@ export async function get_mac_app_pathLogic( } } -export default { - name: 'get_mac_app_path', - description: 'Retrieves the built macOS app bundle path.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Get macOS App Path', - readOnlyHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: getMacosAppPathSchema as unknown as z.ZodType, - logicFunction: get_mac_app_pathLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: getMacosAppPathSchema as unknown as z.ZodType, + logicFunction: get_mac_app_pathLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/macos/get_mac_bundle_id.ts b/src/mcp/tools/macos/get_mac_bundle_id.ts deleted file mode 100644 index 9935d53e..00000000 --- a/src/mcp/tools/macos/get_mac_bundle_id.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/get_mac_bundle_id.ts'; diff --git a/src/mcp/tools/macos/index.ts b/src/mcp/tools/macos/index.ts deleted file mode 100644 index 55c5afce..00000000 --- a/src/mcp/tools/macos/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'macOS Development', - description: - 'Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications.', -}; diff --git a/src/mcp/tools/macos/launch_mac_app.ts b/src/mcp/tools/macos/launch_mac_app.ts index c252a55b..0cb65b3c 100644 --- a/src/mcp/tools/macos/launch_mac_app.ts +++ b/src/mcp/tools/macos/launch_mac_app.ts @@ -8,17 +8,15 @@ import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; import { validateFileExists } from '../../../utils/validation/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const launchMacAppSchema = z.object({ - appPath: z - .string() - .describe('Path to the macOS .app bundle to launch (full path to the .app directory)'), - args: z.array(z.string()).optional().describe('Additional arguments to pass to the app'), + appPath: z.string(), + args: z.array(z.string()).optional(), }); // Use z.infer for type safety @@ -74,14 +72,10 @@ export async function launch_mac_appLogic( } } -export default { - name: 'launch_mac_app', - description: - "Launches a macOS application. IMPORTANT: You MUST provide the appPath parameter. Example: launch_mac_app({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_launch_macos_app.", - schema: launchMacAppSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Launch macOS App', - destructiveHint: true, - }, - handler: createTypedTool(launchMacAppSchema, launch_mac_appLogic, getDefaultCommandExecutor), -}; +export const schema = launchMacAppSchema.shape; + +export const handler = createTypedTool( + launchMacAppSchema, + launch_mac_appLogic, + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/macos/list_schemes.ts b/src/mcp/tools/macos/list_schemes.ts deleted file mode 100644 index 67519898..00000000 --- a/src/mcp/tools/macos/list_schemes.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified list_schemes tool for macos-project workflow -export { default } from '../project-discovery/list_schemes.ts'; diff --git a/src/mcp/tools/macos/show_build_settings.ts b/src/mcp/tools/macos/show_build_settings.ts deleted file mode 100644 index 77db451b..00000000 --- a/src/mcp/tools/macos/show_build_settings.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified tool for macos-project workflow -export { default } from '../project-discovery/show_build_settings.ts'; diff --git a/src/mcp/tools/macos/stop_mac_app.ts b/src/mcp/tools/macos/stop_mac_app.ts index b9fec289..6db67748 100644 --- a/src/mcp/tools/macos/stop_mac_app.ts +++ b/src/mcp/tools/macos/stop_mac_app.ts @@ -1,17 +1,14 @@ import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const stopMacAppSchema = z.object({ - appName: z - .string() - .optional() - .describe('Name of the application to stop (e.g., "Calculator" or "MyApp")'), - processId: z.number().optional().describe('Process ID (PID) of the application to stop'), + appName: z.string().optional(), + processId: z.number().optional(), }); // Use z.infer for type safety @@ -78,13 +75,10 @@ export async function stop_mac_appLogic( } } -export default { - name: 'stop_mac_app', - description: 'Stops a running macOS application. Can stop by app name or process ID.', - schema: stopMacAppSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Stop macOS App', - destructiveHint: true, - }, - handler: createTypedTool(stopMacAppSchema, stop_mac_appLogic, getDefaultCommandExecutor), -}; +export const schema = stopMacAppSchema.shape; + +export const handler = createTypedTool( + stopMacAppSchema, + stop_mac_appLogic, + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/macos/test_macos.ts b/src/mcp/tools/macos/test_macos.ts index 238aacea..09120fd9 100644 --- a/src/mcp/tools/macos/test_macos.ts +++ b/src/mcp/tools/macos/test_macos.ts @@ -7,7 +7,8 @@ import * as z from 'zod'; import { join } from 'path'; -import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; import { createTextResponse } from '../../../utils/responses/index.ts'; @@ -33,15 +34,9 @@ const baseSchemaObject = z.object({ workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), scheme: z.string().describe('The scheme to use'), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - derivedDataPath: z - .string() - .optional() - .describe('Path where build products and other derived data will go'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), - preferXcodebuild: z - .boolean() - .optional() - .describe('If true, prefers xcodebuild over the experimental incremental build system'), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), + preferXcodebuild: z.boolean().optional(), testRunnerEnv: z .record(z.string(), z.string()) .optional() @@ -55,6 +50,8 @@ const publicSchemaObject = baseSchemaObject.omit({ workspacePath: true, scheme: true, configuration: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); const testMacosSchema = z.preprocess( @@ -327,26 +324,19 @@ export async function testMacosLogic( } } -export default { - name: 'test_macos', - description: 'Runs tests for a macOS target.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Test macOS', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: testMacosSchema as unknown as z.ZodType, - logicFunction: (params, executor) => - testMacosLogic(params, executor, getDefaultFileSystemExecutor()), - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: testMacosSchema as unknown as z.ZodType, + logicFunction: (params, executor) => + testMacosLogic(params, executor, getDefaultFileSystemExecutor()), + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts b/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts index f534ab9c..f45e9da9 100644 --- a/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts @@ -9,7 +9,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import plugin, { discover_projsLogic } from '../discover_projs.ts'; +import { schema, handler, discover_projsLogic } from '../discover_projs.ts'; import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; describe('discover_projs plugin', () => { @@ -17,36 +17,26 @@ describe('discover_projs plugin', () => { // Create mock file system executor mockFileSystemExecutor = createMockFileSystemExecutor({ - stat: async () => ({ isDirectory: () => true }), + stat: async () => ({ isDirectory: () => true, mtimeMs: 0 }), readdir: async () => [], }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('discover_projs'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe( - 'Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files.', - ); - }); - it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema with valid inputs', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({ workspaceRoot: '/path/to/workspace' }).success).toBe(true); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({ workspaceRoot: '/path/to/workspace' }).success).toBe(true); expect( - schema.safeParse({ workspaceRoot: '/path/to/workspace', scanPath: 'subdir' }).success, + schemaObj.safeParse({ workspaceRoot: '/path/to/workspace', scanPath: 'subdir' }).success, ).toBe(true); - expect(schema.safeParse({ workspaceRoot: '/path/to/workspace', maxDepth: 3 }).success).toBe( - true, - ); expect( - schema.safeParse({ + schemaObj.safeParse({ workspaceRoot: '/path/to/workspace', maxDepth: 3 }).success, + ).toBe(true); + expect( + schemaObj.safeParse({ workspaceRoot: '/path/to/workspace', scanPath: 'subdir', maxDepth: 5, @@ -55,19 +45,21 @@ describe('discover_projs plugin', () => { }); it('should validate schema with invalid inputs', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ workspaceRoot: 123 }).success).toBe(false); - expect(schema.safeParse({ workspaceRoot: '/path', scanPath: 123 }).success).toBe(false); - expect(schema.safeParse({ workspaceRoot: '/path', maxDepth: 'invalid' }).success).toBe(false); - expect(schema.safeParse({ workspaceRoot: '/path', maxDepth: -1 }).success).toBe(false); - expect(schema.safeParse({ workspaceRoot: '/path', maxDepth: 1.5 }).success).toBe(false); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({ workspaceRoot: 123 }).success).toBe(false); + expect(schemaObj.safeParse({ workspaceRoot: '/path', scanPath: 123 }).success).toBe(false); + expect(schemaObj.safeParse({ workspaceRoot: '/path', maxDepth: 'invalid' }).success).toBe( + false, + ); + expect(schemaObj.safeParse({ workspaceRoot: '/path', maxDepth: -1 }).success).toBe(false); + expect(schemaObj.safeParse({ workspaceRoot: '/path', maxDepth: 1.5 }).success).toBe(false); }); }); describe('Handler Behavior (Complete Literal Returns)', () => { it('should handle workspaceRoot parameter correctly when provided', async () => { - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => []; const result = await discover_projsLogic( @@ -107,7 +99,7 @@ describe('discover_projs plugin', () => { }); it('should return error when scan path is not a directory', async () => { - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => false }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => false, mtimeMs: 0 }); const result = await discover_projsLogic( { @@ -125,7 +117,7 @@ describe('discover_projs plugin', () => { }); it('should return success with no projects found', async () => { - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => []; const result = await discover_projsLogic( @@ -144,7 +136,7 @@ describe('discover_projs plugin', () => { }); it('should return success with projects found', async () => { - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => [ { name: 'MyApp.xcodeproj', isDirectory: () => true, isSymbolicLink: () => false }, { name: 'MyWorkspace.xcworkspace', isDirectory: () => true, isSymbolicLink: () => false }, @@ -164,6 +156,10 @@ describe('discover_projs plugin', () => { { type: 'text', text: 'Discovery finished. Found 1 projects and 1 workspaces.' }, { type: 'text', text: 'Projects found:\n - /workspace/MyApp.xcodeproj' }, { type: 'text', text: 'Workspaces found:\n - /workspace/MyWorkspace.xcworkspace' }, + { + type: 'text', + text: "Hint: Save a default with session-set-defaults { projectPath: '...' } or { workspacePath: '...' }.", + }, ], isError: false, }); @@ -219,7 +215,7 @@ describe('discover_projs plugin', () => { }); it('should handle workspaceRoot parameter correctly', async () => { - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => []; const result = await discover_projsLogic( @@ -237,7 +233,7 @@ describe('discover_projs plugin', () => { it('should handle scan path outside workspace root', async () => { // Mock path normalization to simulate path outside workspace root - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => []; const result = await discover_projsLogic( @@ -284,7 +280,7 @@ describe('discover_projs plugin', () => { it('should handle max depth reached during recursive scan', async () => { let readdirCallCount = 0; - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => { readdirCallCount++; if (readdirCallCount <= 3) { @@ -315,7 +311,7 @@ describe('discover_projs plugin', () => { }); it('should handle skipped directory types during scan', async () => { - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => [ { name: 'build', isDirectory: () => true, isSymbolicLink: () => false }, { name: 'DerivedData', isDirectory: () => true, isSymbolicLink: () => false }, @@ -340,7 +336,7 @@ describe('discover_projs plugin', () => { }); it('should handle error during recursive directory reading', async () => { - mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true }); + mockFileSystemExecutor.stat = async () => ({ isDirectory: () => true, mtimeMs: 0 }); mockFileSystemExecutor.readdir = async () => { const readError = new Error('Permission denied'); (readError as any).code = 'EACCES'; diff --git a/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts b/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts index 21fa55b2..f808f0dc 100644 --- a/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts @@ -10,7 +10,7 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import plugin, { get_app_bundle_idLogic } from '../get_app_bundle_id.ts'; +import { schema, handler, get_app_bundle_idLogic } from '../get_app_bundle_id.ts'; import { createMockFileSystemExecutor, createCommandMatchingMockExecutor, @@ -32,39 +32,29 @@ describe('get_app_bundle_id plugin', () => { }; describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('get_app_bundle_id'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe( - "Extracts the bundle identifier from an app bundle (.app) for any Apple platform (iOS, iPadOS, watchOS, tvOS, visionOS). IMPORTANT: You MUST provide the appPath parameter. Example: get_app_bundle_id({ appPath: '/path/to/your/app.app' })", - ); - }); - it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema with valid inputs', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({ appPath: '/path/to/MyApp.app' }).success).toBe(true); - expect(schema.safeParse({ appPath: '/Users/dev/MyApp.app' }).success).toBe(true); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({ appPath: '/path/to/MyApp.app' }).success).toBe(true); + expect(schemaObj.safeParse({ appPath: '/Users/dev/MyApp.app' }).success).toBe(true); }); it('should validate schema with invalid inputs', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ appPath: 123 }).success).toBe(false); - expect(schema.safeParse({ appPath: null }).success).toBe(false); - expect(schema.safeParse({ appPath: undefined }).success).toBe(false); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({ appPath: 123 }).success).toBe(false); + expect(schemaObj.safeParse({ appPath: null }).success).toBe(false); + expect(schemaObj.safeParse({ appPath: undefined }).success).toBe(false); }); }); describe('Handler Behavior (Complete Literal Returns)', () => { it('should return error when appPath validation fails', async () => { // Test validation through the handler which uses Zod validation - const result = await plugin.handler({}); + const result = await handler({}); expect(result).toEqual({ content: [ @@ -102,7 +92,7 @@ describe('get_app_bundle_id plugin', () => { it('should return success with bundle ID using defaults read', async () => { const mockExecutor = createMockExecutorForCommands({ - 'defaults read "/path/to/MyApp.app/Info" CFBundleIdentifier': 'com.example.MyApp', + 'defaults read "/path/to/MyApp.app/Info" CFBundleIdentifier': 'io.sentry.MyApp', }); const mockFileSystemExecutor = createMockFileSystemExecutor({ existsSync: () => true, @@ -118,15 +108,15 @@ describe('get_app_bundle_id plugin', () => { content: [ { type: 'text', - text: '✅ Bundle ID: com.example.MyApp', - }, - { - type: 'text', - text: `Next Steps: -- Simulator: install_app_sim + launch_app_sim -- Device: install_app_device + launch_app_device`, + text: '✅ Bundle ID: io.sentry.MyApp', }, ], + nextStepParams: { + install_app_sim: { simulatorId: 'SIMULATOR_UUID', appPath: '/path/to/MyApp.app' }, + launch_app_sim: { simulatorId: 'SIMULATOR_UUID', bundleId: 'io.sentry.MyApp' }, + install_app_device: { deviceId: 'DEVICE_UDID', appPath: '/path/to/MyApp.app' }, + launch_app_device: { deviceId: 'DEVICE_UDID', bundleId: 'io.sentry.MyApp' }, + }, isError: false, }); }); @@ -137,7 +127,7 @@ describe('get_app_bundle_id plugin', () => { 'defaults read failed', ), '/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "/path/to/MyApp.app/Info.plist"': - 'com.example.MyApp', + 'io.sentry.MyApp', }); const mockFileSystemExecutor = createMockFileSystemExecutor({ existsSync: () => true, @@ -153,15 +143,15 @@ describe('get_app_bundle_id plugin', () => { content: [ { type: 'text', - text: '✅ Bundle ID: com.example.MyApp', - }, - { - type: 'text', - text: `Next Steps: -- Simulator: install_app_sim + launch_app_sim -- Device: install_app_device + launch_app_device`, + text: '✅ Bundle ID: io.sentry.MyApp', }, ], + nextStepParams: { + install_app_sim: { simulatorId: 'SIMULATOR_UUID', appPath: '/path/to/MyApp.app' }, + launch_app_sim: { simulatorId: 'SIMULATOR_UUID', bundleId: 'io.sentry.MyApp' }, + install_app_device: { deviceId: 'DEVICE_UDID', appPath: '/path/to/MyApp.app' }, + launch_app_device: { deviceId: 'DEVICE_UDID', bundleId: 'io.sentry.MyApp' }, + }, isError: false, }); }); @@ -267,7 +257,7 @@ describe('get_app_bundle_id plugin', () => { it('should handle schema validation error when appPath is null', async () => { // Test validation through the handler which uses Zod validation - const result = await plugin.handler({ appPath: null }); + const result = await handler({ appPath: null }); expect(result).toEqual({ content: [ @@ -282,7 +272,7 @@ describe('get_app_bundle_id plugin', () => { it('should handle schema validation with missing appPath', async () => { // Test validation through the handler which uses Zod validation - const result = await plugin.handler({}); + const result = await handler({}); expect(result).toEqual({ content: [ @@ -297,7 +287,7 @@ describe('get_app_bundle_id plugin', () => { it('should handle schema validation with undefined appPath', async () => { // Test validation through the handler which uses Zod validation - const result = await plugin.handler({ appPath: undefined }); + const result = await handler({ appPath: undefined }); expect(result).toEqual({ content: [ @@ -312,7 +302,7 @@ describe('get_app_bundle_id plugin', () => { it('should handle schema validation with number type appPath', async () => { // Test validation through the handler which uses Zod validation - const result = await plugin.handler({ appPath: 123 }); + const result = await handler({ appPath: 123 }); expect(result).toEqual({ content: [ diff --git a/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts b/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts index 65f1bfd6..367dfd05 100644 --- a/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import plugin, { get_mac_bundle_idLogic } from '../get_mac_bundle_id.ts'; +import { schema, handler, get_mac_bundle_idLogic } from '../get_mac_bundle_id.ts'; import { createMockFileSystemExecutor, createCommandMatchingMockExecutor, @@ -22,32 +22,22 @@ describe('get_mac_bundle_id plugin', () => { }; describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('get_mac_bundle_id'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe( - "Extracts the bundle identifier from a macOS app bundle (.app). IMPORTANT: You MUST provide the appPath parameter. Example: get_mac_bundle_id({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_get_macos_bundle_id.", - ); - }); - it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema with valid inputs', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({ appPath: '/Applications/TextEdit.app' }).success).toBe(true); - expect(schema.safeParse({ appPath: '/Users/dev/MyApp.app' }).success).toBe(true); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({ appPath: '/Applications/TextEdit.app' }).success).toBe(true); + expect(schemaObj.safeParse({ appPath: '/Users/dev/MyApp.app' }).success).toBe(true); }); it('should validate schema with invalid inputs', () => { - const schema = z.object(plugin.schema); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ appPath: 123 }).success).toBe(false); - expect(schema.safeParse({ appPath: null }).success).toBe(false); - expect(schema.safeParse({ appPath: undefined }).success).toBe(false); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({ appPath: 123 }).success).toBe(false); + expect(schemaObj.safeParse({ appPath: null }).success).toBe(false); + expect(schemaObj.safeParse({ appPath: undefined }).success).toBe(false); }); }); @@ -81,7 +71,7 @@ describe('get_mac_bundle_id plugin', () => { it('should return success with bundle ID using defaults read', async () => { const mockExecutor = createMockExecutorForCommands({ 'defaults read "/Applications/MyApp.app/Contents/Info" CFBundleIdentifier': - 'com.example.MyMacApp', + 'io.sentry.MyMacApp', }); const mockFileSystemExecutor = createMockFileSystemExecutor({ existsSync: () => true, @@ -97,15 +87,13 @@ describe('get_mac_bundle_id plugin', () => { content: [ { type: 'text', - text: '✅ Bundle ID: com.example.MyMacApp', - }, - { - type: 'text', - text: `Next Steps: -- Launch: launch_mac_app({ appPath: "/Applications/MyApp.app" }) -- Build again: build_macos({ scheme: "SCHEME_NAME" })`, + text: '✅ Bundle ID: io.sentry.MyMacApp', }, ], + nextStepParams: { + launch_mac_app: { appPath: '/Applications/MyApp.app' }, + build_macos: { scheme: 'SCHEME_NAME' }, + }, isError: false, }); }); @@ -116,7 +104,7 @@ describe('get_mac_bundle_id plugin', () => { 'defaults read failed', ), '/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "/Applications/MyApp.app/Contents/Info.plist"': - 'com.example.MyMacApp', + 'io.sentry.MyMacApp', }); const mockFileSystemExecutor = createMockFileSystemExecutor({ existsSync: () => true, @@ -132,15 +120,13 @@ describe('get_mac_bundle_id plugin', () => { content: [ { type: 'text', - text: '✅ Bundle ID: com.example.MyMacApp', - }, - { - type: 'text', - text: `Next Steps: -- Launch: launch_mac_app({ appPath: "/Applications/MyApp.app" }) -- Build again: build_macos({ scheme: "SCHEME_NAME" })`, + text: '✅ Bundle ID: io.sentry.MyMacApp', }, ], + nextStepParams: { + launch_mac_app: { appPath: '/Applications/MyApp.app' }, + build_macos: { scheme: 'SCHEME_NAME' }, + }, isError: false, }); }); diff --git a/src/mcp/tools/project-discovery/__tests__/index.test.ts b/src/mcp/tools/project-discovery/__tests__/index.test.ts deleted file mode 100644 index 603cac67..00000000 --- a/src/mcp/tools/project-discovery/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for project-discovery workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('project-discovery workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('Project Discovery'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts b/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts index 924a03c8..4d9f1ff3 100644 --- a/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts @@ -6,8 +6,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; -import plugin, { listSchemesLogic } from '../list_schemes.ts'; +import { + createMockCommandResponse, + createMockExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, listSchemesLogic } from '../list_schemes.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('list_schemes plugin', () => { @@ -16,23 +19,17 @@ describe('list_schemes plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('list_schemes'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe('Lists schemes for a project or workspace.'); - }); - it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose an empty public schema', () => { - const schema = z.strictObject(plugin.schema); - expect(schema.safeParse({}).success).toBe(true); - expect(schema.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe(false); - expect(Object.keys(plugin.schema)).toEqual([]); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe( + false, + ); + expect(Object.keys(schema)).toEqual([]); }); }); @@ -71,12 +68,23 @@ describe('list_schemes plugin', () => { }, { type: 'text', - text: `Next Steps: -1. Build the app: build_macos({ projectPath: "/path/to/MyProject.xcodeproj", scheme: "MyProject" }) - or for iOS: build_sim({ projectPath: "/path/to/MyProject.xcodeproj", scheme: "MyProject", simulatorName: "iPhone 16" }) -2. Show build settings: show_build_settings({ projectPath: "/path/to/MyProject.xcodeproj", scheme: "MyProject" })`, + text: 'Hint: Consider saving a default scheme with session-set-defaults { scheme: "MyProject" } to avoid repeating it.', }, ], + nextStepParams: { + build_macos: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyProject' }, + build_run_sim: { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyProject', + simulatorName: 'iPhone 16', + }, + build_sim: { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyProject', + simulatorName: 'iPhone 16', + }, + show_build_settings: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyProject' }, + }, isError: false, }); }); @@ -146,10 +154,6 @@ describe('list_schemes plugin', () => { type: 'text', text: '', }, - { - type: 'text', - text: '', - }, ], isError: false, }); @@ -191,12 +195,14 @@ describe('list_schemes plugin', () => { const calls: any[] = []; const mockExecutor = async ( command: string[], - action: string, - showOutput: boolean, - workingDir?: string, + action?: string, + showOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { - calls.push([command, action, showOutput, workingDir]); - return { + calls.push([command, action, showOutput, opts?.cwd]); + void detached; + return createMockCommandResponse({ success: true, output: `Information about project "MyProject": Targets: @@ -209,8 +215,7 @@ describe('list_schemes plugin', () => { Schemes: MyProject`, error: undefined, - process: { pid: 12345 }, - }; + }); }; await listSchemesLogic({ projectPath: '/path/to/MyProject.xcodeproj' }, mockExecutor); @@ -219,7 +224,7 @@ describe('list_schemes plugin', () => { [ ['xcodebuild', '-list', '-project', '/path/to/MyProject.xcodeproj'], 'List Schemes', - true, + false, undefined, ], ]); @@ -228,7 +233,7 @@ describe('list_schemes plugin', () => { it('should handle validation when testing with missing projectPath via plugin handler', async () => { // Note: Direct logic function calls bypass Zod validation, so we test the actual plugin handler // to verify Zod validation works properly. The createTypedTool wrapper handles validation. - const result = await plugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); expect(result.content[0].text).toContain('Provide a project or workspace'); @@ -237,14 +242,14 @@ describe('list_schemes plugin', () => { describe('XOR Validation', () => { it('should error when neither projectPath nor workspacePath provided', async () => { - const result = await plugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); expect(result.content[0].text).toContain('Provide a project or workspace'); }); it('should error when both projectPath and workspacePath provided', async () => { - const result = await plugin.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace.xcworkspace', }); @@ -253,7 +258,7 @@ describe('list_schemes plugin', () => { }); it('should handle empty strings as undefined', async () => { - const result = await plugin.handler({ + const result = await handler({ projectPath: '', workspacePath: '', }); @@ -290,12 +295,23 @@ describe('list_schemes plugin', () => { }, { type: 'text', - text: `Next Steps: -1. Build the app: build_macos({ workspacePath: "/path/to/MyProject.xcworkspace", scheme: "MyApp" }) - or for iOS: build_sim({ workspacePath: "/path/to/MyProject.xcworkspace", scheme: "MyApp", simulatorName: "iPhone 16" }) -2. Show build settings: show_build_settings({ workspacePath: "/path/to/MyProject.xcworkspace", scheme: "MyApp" })`, + text: 'Hint: Consider saving a default scheme with session-set-defaults { scheme: "MyApp" } to avoid repeating it.', }, ], + nextStepParams: { + build_macos: { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyApp' }, + build_run_sim: { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyApp', + simulatorName: 'iPhone 16', + }, + build_sim: { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyApp', + simulatorName: 'iPhone 16', + }, + show_build_settings: { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyApp' }, + }, isError: false, }); }); @@ -304,19 +320,20 @@ describe('list_schemes plugin', () => { const calls: any[] = []; const mockExecutor = async ( command: string[], - action: string, - showOutput: boolean, - workingDir?: string, + action?: string, + showOutput?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { - calls.push([command, action, showOutput, workingDir]); - return { + calls.push([command, action, showOutput, opts?.cwd]); + void detached; + return createMockCommandResponse({ success: true, output: `Information about workspace "MyWorkspace": Schemes: MyApp`, error: undefined, - process: { pid: 12345 }, - }; + }); }; await listSchemesLogic({ workspacePath: '/path/to/MyProject.xcworkspace' }, mockExecutor); @@ -325,7 +342,7 @@ describe('list_schemes plugin', () => { [ ['xcodebuild', '-list', '-workspace', '/path/to/MyProject.xcworkspace'], 'List Schemes', - true, + false, undefined, ], ]); diff --git a/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts b/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts index 3f492494..eca48309 100644 --- a/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; -import plugin, { showBuildSettingsLogic } from '../show_build_settings.ts'; +import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, showBuildSettingsLogic } from '../show_build_settings.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('show_build_settings plugin', () => { @@ -9,24 +9,16 @@ describe('show_build_settings plugin', () => { sessionStore.clear(); }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('show_build_settings'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe('Shows xcodebuild build settings.'); - }); - it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose an empty public schema', () => { - const schema = z.strictObject(plugin.schema); - expect(schema.safeParse({}).success).toBe(true); - expect(schema.safeParse({ projectPath: '/path.xcodeproj' }).success).toBe(false); - expect(schema.safeParse({ scheme: 'App' }).success).toBe(false); - expect(Object.keys(plugin.schema)).toEqual([]); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ projectPath: '/path.xcodeproj' }).success).toBe(false); + expect(schemaObj.safeParse({ scheme: 'App' }).success).toBe(false); + expect(Object.keys(schema)).toEqual([]); }); }); @@ -49,7 +41,7 @@ describe('show_build_settings plugin', () => { it('should test Zod validation through handler', async () => { // Test the actual tool handler which includes Zod validation - const result = await plugin.handler({ + const result = await handler({ projectPath: null, scheme: 'MyScheme', }); @@ -68,7 +60,7 @@ describe('show_build_settings plugin', () => { BUILD_DIR = /Users/dev/Build/Products CONFIGURATION = Debug DEVELOPMENT_TEAM = ABC123DEF4 - PRODUCT_BUNDLE_IDENTIFIER = com.example.MyApp + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MyApp PRODUCT_NAME = MyApp SUPPORTED_PLATFORMS = iphoneos iphonesimulator`, error: undefined, @@ -76,7 +68,7 @@ describe('show_build_settings plugin', () => { }); // Wrap mockExecutor to track calls - const wrappedExecutor = (...args: any[]) => { + const wrappedExecutor: CommandExecutor = (...args) => { calls.push(args); return mockExecutor(...args); }; @@ -100,7 +92,7 @@ describe('show_build_settings plugin', () => { 'MyScheme', ], 'Show Build Settings', - true, + false, ]); expect(result).toEqual({ @@ -116,11 +108,20 @@ describe('show_build_settings plugin', () => { BUILD_DIR = /Users/dev/Build/Products CONFIGURATION = Debug DEVELOPMENT_TEAM = ABC123DEF4 - PRODUCT_BUNDLE_IDENTIFIER = com.example.MyApp + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MyApp PRODUCT_NAME = MyApp SUPPORTED_PLATFORMS = iphoneos iphonesimulator`, }, ], + nextStepParams: { + build_macos: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' }, + build_sim: { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + simulatorName: 'iPhone 16', + }, + list_schemes: { projectPath: '/path/to/MyProject.xcodeproj' }, + }, isError: false, }); }); @@ -169,7 +170,7 @@ describe('show_build_settings plugin', () => { describe('XOR Validation', () => { it('should error when neither projectPath nor workspacePath provided', async () => { - const result = await plugin.handler({ + const result = await handler({ scheme: 'MyScheme', }); @@ -179,7 +180,7 @@ describe('show_build_settings plugin', () => { }); it('should error when both projectPath and workspacePath provided', async () => { - const result = await plugin.handler({ + const result = await handler({ projectPath: '/path/project.xcodeproj', workspacePath: '/path/workspace.xcworkspace', scheme: 'MyScheme', @@ -222,7 +223,7 @@ describe('show_build_settings plugin', () => { describe('Session requirement handling', () => { it('should require scheme when not provided', async () => { - const result = await plugin.handler({ + const result = await handler({ projectPath: '/path/to/MyProject.xcodeproj', } as any); @@ -234,7 +235,7 @@ describe('show_build_settings plugin', () => { it('should surface project/workspace requirement even with scheme default', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await plugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -252,7 +253,7 @@ describe('show_build_settings plugin', () => { BUILD_DIR = /Users/dev/Build/Products CONFIGURATION = Debug DEVELOPMENT_TEAM = ABC123DEF4 - PRODUCT_BUNDLE_IDENTIFIER = com.example.MyApp + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MyApp PRODUCT_NAME = MyApp SUPPORTED_PLATFORMS = iphoneos iphonesimulator`, error: undefined, @@ -260,7 +261,7 @@ describe('show_build_settings plugin', () => { }); // Wrap mockExecutor to track calls - const wrappedExecutor = (...args: any[]) => { + const wrappedExecutor: CommandExecutor = (...args) => { calls.push(args); return mockExecutor(...args); }; @@ -284,7 +285,7 @@ describe('show_build_settings plugin', () => { 'MyScheme', ], 'Show Build Settings', - true, + false, ]); expect(result).toEqual({ @@ -300,11 +301,20 @@ describe('show_build_settings plugin', () => { BUILD_DIR = /Users/dev/Build/Products CONFIGURATION = Debug DEVELOPMENT_TEAM = ABC123DEF4 - PRODUCT_BUNDLE_IDENTIFIER = com.example.MyApp + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MyApp PRODUCT_NAME = MyApp SUPPORTED_PLATFORMS = iphoneos iphonesimulator`, }, ], + nextStepParams: { + build_macos: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' }, + build_sim: { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + simulatorName: 'iPhone 16', + }, + list_schemes: { projectPath: '/path/to/MyProject.xcodeproj' }, + }, isError: false, }); }); diff --git a/src/mcp/tools/project-discovery/discover_projs.ts b/src/mcp/tools/project-discovery/discover_projs.ts index 187eac05..f0f3c9f3 100644 --- a/src/mcp/tools/project-discovery/discover_projs.ts +++ b/src/mcp/tools/project-discovery/discover_projs.ts @@ -8,9 +8,10 @@ import * as z from 'zod'; import * as path from 'node:path'; import { log } from '../../../utils/logging/index.ts'; -import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createTextContent } from '../../../types/common.ts'; import { getDefaultFileSystemExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; -import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import type { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Constants @@ -134,17 +135,9 @@ async function _findProjectsRecursive( // Define schema as ZodObject const discoverProjsSchema = z.object({ - workspaceRoot: z.string().describe('The absolute path of the workspace root to scan within.'), - scanPath: z - .string() - .optional() - .describe('Optional: Path relative to workspace root to scan. Defaults to workspace root.'), - maxDepth: z - .number() - .int() - .nonnegative() - .optional() - .describe(`Optional: Maximum directory depth to scan. Defaults to ${DEFAULT_MAX_DEPTH}.`), + workspaceRoot: z.string(), + scanPath: z.string().optional(), + maxDepth: z.number().int().nonnegative().optional(), }); // Use z.infer for type safety @@ -263,26 +256,26 @@ export async function discover_projsLogic( ); } + if (results.projects.length > 0 || results.workspaces.length > 0) { + responseContent.push( + createTextContent( + "Hint: Save a default with session-set-defaults { projectPath: '...' } or { workspacePath: '...' }.", + ), + ); + } + return { content: responseContent, isError: false, }; } -export default { - name: 'discover_projs', - description: - 'Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files.', - schema: discoverProjsSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Discover Projects', - readOnlyHint: true, +export const schema = discoverProjsSchema.shape; + +export const handler = createTypedTool( + discoverProjsSchema, + (params: DiscoverProjsParams) => { + return discover_projsLogic(params, getDefaultFileSystemExecutor()); }, - handler: createTypedTool( - discoverProjsSchema, - (params: DiscoverProjsParams) => { - return discover_projsLogic(params, getDefaultFileSystemExecutor()); - }, - getDefaultCommandExecutor, - ), -}; + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/project-discovery/get_app_bundle_id.ts b/src/mcp/tools/project-discovery/get_app_bundle_id.ts index bd22ee2e..2da85cad 100644 --- a/src/mcp/tools/project-discovery/get_app_bundle_id.ts +++ b/src/mcp/tools/project-discovery/get_app_bundle_id.ts @@ -7,22 +7,15 @@ import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; -import { - CommandExecutor, - getDefaultFileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/command.ts'; -import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/command.ts'; +import { getDefaultFileSystemExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; +import type { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const getAppBundleIdSchema = z.object({ - appPath: z - .string() - .describe( - 'Path to the .app bundle to extract bundle ID from (full path to the .app directory)', - ), + appPath: z.string().describe('Path to the .app bundle'), }); // Use z.infer for type safety @@ -94,13 +87,13 @@ export async function get_app_bundle_idLogic( type: 'text', text: `✅ Bundle ID: ${bundleId}`, }, - { - type: 'text', - text: `Next Steps: -- Simulator: install_app_sim + launch_app_sim -- Device: install_app_device + launch_app_device`, - }, ], + nextStepParams: { + install_app_sim: { simulatorId: 'SIMULATOR_UUID', appPath }, + launch_app_sim: { simulatorId: 'SIMULATOR_UUID', bundleId: bundleId.trim() }, + install_app_device: { deviceId: 'DEVICE_UDID', appPath }, + launch_app_device: { deviceId: 'DEVICE_UDID', bundleId: bundleId.trim() }, + }, isError: false, }; } catch (error) { @@ -123,19 +116,11 @@ export async function get_app_bundle_idLogic( } } -export default { - name: 'get_app_bundle_id', - description: - "Extracts the bundle identifier from an app bundle (.app) for any Apple platform (iOS, iPadOS, watchOS, tvOS, visionOS). IMPORTANT: You MUST provide the appPath parameter. Example: get_app_bundle_id({ appPath: '/path/to/your/app.app' })", - schema: getAppBundleIdSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Get App Bundle ID', - readOnlyHint: true, - }, - handler: createTypedTool( - getAppBundleIdSchema, - (params: GetAppBundleIdParams) => - get_app_bundle_idLogic(params, getDefaultCommandExecutor(), getDefaultFileSystemExecutor()), - getDefaultCommandExecutor, - ), -}; +export const schema = getAppBundleIdSchema.shape; + +export const handler = createTypedTool( + getAppBundleIdSchema, + (params: GetAppBundleIdParams) => + get_app_bundle_idLogic(params, getDefaultCommandExecutor(), getDefaultFileSystemExecutor()), + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/project-discovery/get_mac_bundle_id.ts b/src/mcp/tools/project-discovery/get_mac_bundle_id.ts index 57c1de6b..e396c986 100644 --- a/src/mcp/tools/project-discovery/get_mac_bundle_id.ts +++ b/src/mcp/tools/project-discovery/get_mac_bundle_id.ts @@ -6,13 +6,10 @@ import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; -import { - CommandExecutor, - getDefaultFileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/command.ts'; -import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/command.ts'; +import { getDefaultFileSystemExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; +import type { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; /** @@ -28,11 +25,7 @@ async function executeSyncCommand(command: string, executor: CommandExecutor): P // Define schema as ZodObject const getMacBundleIdSchema = z.object({ - appPath: z - .string() - .describe( - 'Path to the macOS .app bundle to extract bundle ID from (full path to the .app directory)', - ), + appPath: z.string().describe('Path to the .app bundle'), }); // Use z.infer for type safety @@ -91,13 +84,11 @@ export async function get_mac_bundle_idLogic( type: 'text', text: `✅ Bundle ID: ${bundleId}`, }, - { - type: 'text', - text: `Next Steps: -- Launch: launch_mac_app({ appPath: "${appPath}" }) -- Build again: build_macos({ scheme: "SCHEME_NAME" })`, - }, ], + nextStepParams: { + launch_mac_app: { appPath }, + build_macos: { scheme: 'SCHEME_NAME' }, + }, isError: false, }; } catch (error) { @@ -120,19 +111,11 @@ export async function get_mac_bundle_idLogic( } } -export default { - name: 'get_mac_bundle_id', - description: - "Extracts the bundle identifier from a macOS app bundle (.app). IMPORTANT: You MUST provide the appPath parameter. Example: get_mac_bundle_id({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_get_macos_bundle_id.", - schema: getMacBundleIdSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Get Mac Bundle ID', - readOnlyHint: true, - }, - handler: createTypedTool( - getMacBundleIdSchema, - (params: GetMacBundleIdParams) => - get_mac_bundle_idLogic(params, getDefaultCommandExecutor(), getDefaultFileSystemExecutor()), - getDefaultCommandExecutor, - ), -}; +export const schema = getMacBundleIdSchema.shape; + +export const handler = createTypedTool( + getMacBundleIdSchema, + (params: GetMacBundleIdParams) => + get_mac_bundle_idLogic(params, getDefaultCommandExecutor(), getDefaultFileSystemExecutor()), + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/project-discovery/index.ts b/src/mcp/tools/project-discovery/index.ts deleted file mode 100644 index 995888a2..00000000 --- a/src/mcp/tools/project-discovery/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'Project Discovery', - description: - 'Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information.', -}; diff --git a/src/mcp/tools/project-discovery/list_schemes.ts b/src/mcp/tools/project-discovery/list_schemes.ts index 7acb970e..fabd68c8 100644 --- a/src/mcp/tools/project-discovery/list_schemes.ts +++ b/src/mcp/tools/project-discovery/list_schemes.ts @@ -10,7 +10,7 @@ import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTextResponse } from '../../../utils/responses/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -36,6 +36,8 @@ const listSchemesSchema = z.preprocess( export type ListSchemesParams = z.infer; +const createTextBlock = (text: string) => ({ type: 'text', text }) as const; + /** * Business logic for listing schemes in a project or workspace. * Exported for direct testing and reuse. @@ -61,7 +63,7 @@ export async function listSchemesLogic( command.push('-workspace', params.workspacePath!); } - const result = await executor(command, 'List Schemes', true); + const result = await executor(command, 'List Schemes', false); if (!result.success) { return createTextResponse(`Failed to list schemes: ${result.error}`, true); @@ -77,33 +79,41 @@ export async function listSchemesLogic( const schemeLines = schemesMatch[1].trim().split('\n'); const schemes = schemeLines.map((line) => line.trim()).filter((line) => line); - // Prepare next steps with the first scheme if available - let nextStepsText = ''; + // Prepare next-step params with the first scheme if available + let nextStepParams: Record> | undefined; + let hintText = ''; + if (schemes.length > 0) { const firstScheme = schemes[0]; - // Note: After Phase 2, these will be unified tool names too - nextStepsText = `Next Steps: -1. Build the app: build_macos({ ${projectOrWorkspace}Path: "${path}", scheme: "${firstScheme}" }) - or for iOS: build_sim({ ${projectOrWorkspace}Path: "${path}", scheme: "${firstScheme}", simulatorName: "iPhone 16" }) -2. Show build settings: show_build_settings({ ${projectOrWorkspace}Path: "${path}", scheme: "${firstScheme}" })`; + nextStepParams = { + build_macos: { [`${projectOrWorkspace}Path`]: path!, scheme: firstScheme }, + build_run_sim: { + [`${projectOrWorkspace}Path`]: path!, + scheme: firstScheme, + simulatorName: 'iPhone 16', + }, + build_sim: { + [`${projectOrWorkspace}Path`]: path!, + scheme: firstScheme, + simulatorName: 'iPhone 16', + }, + show_build_settings: { [`${projectOrWorkspace}Path`]: path!, scheme: firstScheme }, + }; + + hintText = + `Hint: Consider saving a default scheme with session-set-defaults ` + + `{ scheme: "${firstScheme}" } to avoid repeating it.`; + } + + const content = [createTextBlock('✅ Available schemes:'), createTextBlock(schemes.join('\n'))]; + if (hintText.length > 0) { + content.push(createTextBlock(hintText)); } return { - content: [ - { - type: 'text', - text: `✅ Available schemes:`, - }, - { - type: 'text', - text: schemes.join('\n'), - }, - { - type: 'text', - text: nextStepsText, - }, - ], + content, + ...(nextStepParams ? { nextStepParams } : {}), isError: false, }; } catch (error) { @@ -118,24 +128,17 @@ const publicSchemaObject = baseSchemaObject.omit({ workspacePath: true, } as const); -export default { - name: 'list_schemes', - description: 'Lists schemes for a project or workspace.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'List Schemes', - readOnlyHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: listSchemesSchema as unknown as z.ZodType, - logicFunction: listSchemesLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: listSchemesSchema as unknown as z.ZodType, + logicFunction: listSchemesLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/project-discovery/show_build_settings.ts b/src/mcp/tools/project-discovery/show_build_settings.ts index 46de6c0d..676d7321 100644 --- a/src/mcp/tools/project-discovery/show_build_settings.ts +++ b/src/mcp/tools/project-discovery/show_build_settings.ts @@ -10,7 +10,7 @@ import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTextResponse } from '../../../utils/responses/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -64,13 +64,13 @@ export async function showBuildSettingsLogic( command.push('-scheme', params.scheme); // Execute the command directly - const result = await executor(command, 'Show Build Settings', true); + const result = await executor(command, 'Show Build Settings', false); if (!result.success) { return createTextResponse(`Failed to show build settings: ${result.error}`, true); } - // Create response based on which type was used (similar to workspace version with next steps) + // Create response based on which type was used const content: Array<{ type: 'text'; text: string }> = [ { type: 'text', @@ -84,19 +84,21 @@ export async function showBuildSettingsLogic( }, ]; - // Add next steps for workspace (similar to original workspace implementation) - if (!hasProjectPath && path) { - content.push({ - type: 'text', - text: `Next Steps: -- Build the workspace: build_macos({ workspacePath: "${path}", scheme: "${params.scheme}" }) -- For iOS: build_sim({ workspacePath: "${path}", scheme: "${params.scheme}", simulatorName: "iPhone 16" }) -- List schemes: list_schemes({ workspacePath: "${path}" })`, - }); + // Build next step params + let nextStepParams: Record> | undefined; + + if (path) { + const pathKey = hasProjectPath ? 'projectPath' : 'workspacePath'; + nextStepParams = { + build_macos: { [pathKey]: path, scheme: params.scheme }, + build_sim: { [pathKey]: path, scheme: params.scheme, simulatorName: 'iPhone 16' }, + list_schemes: { [pathKey]: path }, + }; } return { content, + ...(nextStepParams ? { nextStepParams } : {}), isError: false, }; } catch (error) { @@ -112,28 +114,18 @@ const publicSchemaObject = baseSchemaObject.omit({ scheme: true, } as const); -export default { - name: 'show_build_settings', - description: 'Shows xcodebuild build settings.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Show Build Settings', - readOnlyHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: showBuildSettingsSchema as unknown as z.ZodType< - ShowBuildSettingsParams, - unknown - >, - logicFunction: showBuildSettingsLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: showBuildSettingsSchema as unknown as z.ZodType, + logicFunction: showBuildSettingsLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/project-scaffolding/__tests__/index.test.ts b/src/mcp/tools/project-scaffolding/__tests__/index.test.ts deleted file mode 100644 index 5755664c..00000000 --- a/src/mcp/tools/project-scaffolding/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for project-scaffolding workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('project-scaffolding workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('Project Scaffolding'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Tools for creating new iOS and macOS projects from templates. Bootstrap new applications with best practices, standard configurations, and modern project structures.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts b/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts index 08033bd1..4facdd74 100644 --- a/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts +++ b/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts @@ -9,18 +9,29 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as z from 'zod'; -import scaffoldIosProject, { scaffold_ios_projectLogic } from '../scaffold_ios_project.ts'; +import { schema, handler, scaffold_ios_projectLogic } from '../scaffold_ios_project.ts'; import { createMockExecutor, createMockFileSystemExecutor, } from '../../../../test-utils/mock-executors.ts'; +import { + __resetConfigStoreForTests, + initConfigStore, + type RuntimeConfigOverrides, +} from '../../../../utils/config-store.ts'; + +const cwd = '/repo'; + +async function initConfigStoreForTest(overrides?: RuntimeConfigOverrides): Promise { + __resetConfigStoreForTests(); + await initConfigStore({ cwd, fs: createMockFileSystemExecutor(), overrides }); +} describe('scaffold_ios_project plugin', () => { let mockCommandExecutor: any; let mockFileSystemExecutor: any; - let originalEnv: string | undefined; - beforeEach(() => { + beforeEach(async () => { // Create mock executor using approved utility mockCommandExecutor = createMockExecutor({ success: true, @@ -48,45 +59,23 @@ describe('scaffold_ios_project plugin', () => { rm: async () => {}, cp: async () => {}, writeFile: async () => {}, - stat: async () => ({ isDirectory: () => true }), + stat: async () => ({ isDirectory: () => true, mtimeMs: 0 }), }); - // Store original environment for cleanup - originalEnv = process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; - // Set local template path to avoid download and chdir issues - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path'; - }); - - afterEach(() => { - // Restore original environment - if (originalEnv !== undefined) { - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = originalEnv; - } else { - delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; - } + await initConfigStoreForTest({ iosTemplatePath: '/mock/template/path' }); }); describe('Export Field Validation (Literal)', () => { - it('should have correct name field', () => { - expect(scaffoldIosProject.name).toBe('scaffold_ios_project'); - }); - - it('should have correct description field', () => { - expect(scaffoldIosProject.description).toBe( - 'Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration.', - ); - }); - it('should have handler as function', () => { - expect(typeof scaffoldIosProject.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have valid schema with required fields', () => { - const schema = z.object(scaffoldIosProject.schema); + const schemaObj = z.object(schema); // Test valid input expect( - schema.safeParse({ + schemaObj.safeParse({ projectName: 'MyTestApp', outputPath: '/path/to/output', bundleIdentifier: 'com.test.myapp', @@ -103,7 +92,7 @@ describe('scaffold_ios_project plugin', () => { // Test minimal valid input expect( - schema.safeParse({ + schemaObj.safeParse({ projectName: 'MyTestApp', outputPath: '/path/to/output', }).success, @@ -111,21 +100,21 @@ describe('scaffold_ios_project plugin', () => { // Test invalid input - missing projectName expect( - schema.safeParse({ + schemaObj.safeParse({ outputPath: '/path/to/output', }).success, ).toBe(false); // Test invalid input - missing outputPath expect( - schema.safeParse({ + schemaObj.safeParse({ projectName: 'MyTestApp', }).success, ).toBe(false); // Test invalid input - wrong type for customizeNames expect( - schema.safeParse({ + schemaObj.safeParse({ projectName: 'MyTestApp', outputPath: '/path/to/output', customizeNames: 'true', @@ -134,7 +123,7 @@ describe('scaffold_ios_project plugin', () => { // Test invalid input - wrong enum value for targetedDeviceFamily expect( - schema.safeParse({ + schemaObj.safeParse({ projectName: 'MyTestApp', outputPath: '/path/to/output', targetedDeviceFamily: ['invalid-device'], @@ -143,7 +132,7 @@ describe('scaffold_ios_project plugin', () => { // Test invalid input - wrong enum value for supportedOrientations expect( - schema.safeParse({ + schemaObj.safeParse({ projectName: 'MyTestApp', outputPath: '/path/to/output', supportedOrientations: ['invalid-orientation'], @@ -154,8 +143,7 @@ describe('scaffold_ios_project plugin', () => { describe('Command Generation Tests', () => { it('should generate correct curl command for iOS template download', async () => { - // Temporarily disable local template to force download - delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; + await initConfigStoreForTest({ iosTemplatePath: '' }); // Track commands executed let capturedCommands: string[][] = []; @@ -172,6 +160,7 @@ describe('scaffold_ios_project plugin', () => { await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, capturingExecutor, @@ -188,17 +177,15 @@ describe('scaffold_ios_project plugin', () => { '-o', expect.stringMatching(/template\.zip$/), expect.stringMatching( - /https:\/\/github\.com\/cameroncooke\/XcodeBuildMCP-iOS-Template\/releases\/download\/v\d+\.\d+\.\d+\/XcodeBuildMCP-iOS-Template-\d+\.\d+\.\d+\.zip/, + /https:\/\/github\.com\/getsentry\/XcodeBuildMCP-iOS-Template\/releases\/download\/v\d+\.\d+\.\d+\/XcodeBuildMCP-iOS-Template-\d+\.\d+\.\d+\.zip/, ), ]); - // Restore environment variable - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path'; + await initConfigStoreForTest({ iosTemplatePath: '/mock/template/path' }); }); it.skip('should generate correct unzip command for iOS template extraction', async () => { - // Temporarily disable local template to force download - delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; + await initConfigStoreForTest({ iosTemplatePath: '' }); // Create a mock that returns false for local template paths to force download const downloadMockFileSystemExecutor = createMockFileSystemExecutor({ @@ -219,7 +206,7 @@ describe('scaffold_ios_project plugin', () => { rm: async () => {}, cp: async () => {}, writeFile: async () => {}, - stat: async () => ({ isDirectory: () => true }), + stat: async () => ({ isDirectory: () => true, mtimeMs: 0 }), }); // Track commands executed @@ -237,6 +224,7 @@ describe('scaffold_ios_project plugin', () => { await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, capturingExecutor, @@ -248,17 +236,11 @@ describe('scaffold_ios_project plugin', () => { expect(unzipCommand).toBeDefined(); expect(unzipCommand).toEqual(['unzip', '-q', expect.stringMatching(/template\.zip$/)]); - // Restore environment variable - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path'; + await initConfigStoreForTest({ iosTemplatePath: '/mock/template/path' }); }); it('should generate correct commands when using custom template version', async () => { - // Temporarily disable local template to force download - delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; - - // Set custom template version - const originalVersion = process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION; - process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION = 'v2.0.0'; + await initConfigStoreForTest({ iosTemplatePath: '', iosTemplateVersion: 'v2.0.0' }); // Track commands executed let capturedCommands: string[][] = []; @@ -275,6 +257,7 @@ describe('scaffold_ios_project plugin', () => { await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, capturingExecutor, @@ -290,23 +273,14 @@ describe('scaffold_ios_project plugin', () => { '-f', '-o', expect.stringMatching(/template\.zip$/), - 'https://github.com/cameroncooke/XcodeBuildMCP-iOS-Template/releases/download/v2.0.0/XcodeBuildMCP-iOS-Template-2.0.0.zip', + 'https://github.com/getsentry/XcodeBuildMCP-iOS-Template/releases/download/v2.0.0/XcodeBuildMCP-iOS-Template-2.0.0.zip', ]); - // Restore original version - if (originalVersion) { - process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION = originalVersion; - } else { - delete process.env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION; - } - - // Restore environment variable - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path'; + await initConfigStoreForTest({ iosTemplatePath: '/mock/template/path' }); }); it.skip('should generate correct commands with no command executor passed', async () => { - // Temporarily disable local template to force download - delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; + await initConfigStoreForTest({ iosTemplatePath: '' }); // Create a mock that returns false for local template paths to force download const downloadMockFileSystemExecutor = createMockFileSystemExecutor({ @@ -327,7 +301,7 @@ describe('scaffold_ios_project plugin', () => { rm: async () => {}, cp: async () => {}, writeFile: async () => {}, - stat: async () => ({ isDirectory: () => true }), + stat: async () => ({ isDirectory: () => true, mtimeMs: 0 }), }); // Track commands executed - using default executor path @@ -345,6 +319,7 @@ describe('scaffold_ios_project plugin', () => { await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, capturingExecutor, @@ -359,11 +334,13 @@ describe('scaffold_ios_project plugin', () => { expect(curlCommand).toBeDefined(); expect(unzipCommand).toBeDefined(); + if (!curlCommand || !unzipCommand) { + throw new Error('Expected curl and unzip commands to be captured'); + } expect(curlCommand[0]).toBe('curl'); expect(unzipCommand[0]).toBe('unzip'); - // Restore environment variable - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path'; + await initConfigStoreForTest({ iosTemplatePath: '/mock/template/path' }); }); }); @@ -372,6 +349,7 @@ describe('scaffold_ios_project plugin', () => { const result = await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', bundleIdentifier: 'com.test.iosapp', }, @@ -389,17 +367,24 @@ describe('scaffold_ios_project plugin', () => { projectPath: '/tmp/test-projects', platform: 'iOS', message: 'Successfully scaffolded iOS project "TestIOSApp" in /tmp/test-projects', - nextSteps: [ - 'Important: Before working on the project make sure to read the README.md file in the workspace root directory.', - 'Build for simulator: build_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })', - 'Build and run on simulator: build_run_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })', - ], }, null, 2, ), }, ], + nextStepParams: { + build_sim: { + workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', + scheme: 'TestIOSApp', + simulatorName: 'iPhone 16', + }, + build_run_sim: { + workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', + scheme: 'TestIOSApp', + simulatorName: 'iPhone 16', + }, + }, }); }); @@ -407,12 +392,12 @@ describe('scaffold_ios_project plugin', () => { const result = await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', bundleIdentifier: 'com.test.iosapp', displayName: 'Test iOS App', marketingVersion: '2.0', currentProjectVersion: '5', - customizeNames: true, deploymentTarget: '17.0', targetedDeviceFamily: ['iphone'], supportedOrientations: ['portrait'], @@ -432,17 +417,24 @@ describe('scaffold_ios_project plugin', () => { projectPath: '/tmp/test-projects', platform: 'iOS', message: 'Successfully scaffolded iOS project "TestIOSApp" in /tmp/test-projects', - nextSteps: [ - 'Important: Before working on the project make sure to read the README.md file in the workspace root directory.', - 'Build for simulator: build_sim({ workspacePath: "/tmp/test-projects/TestIOSApp.xcworkspace", scheme: "TestIOSApp", simulatorName: "iPhone 16" })', - 'Build and run on simulator: build_run_sim({ workspacePath: "/tmp/test-projects/TestIOSApp.xcworkspace", scheme: "TestIOSApp", simulatorName: "iPhone 16" })', - ], }, null, 2, ), }, ], + nextStepParams: { + build_sim: { + workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', + scheme: 'TestIOSApp', + simulatorName: 'iPhone 16', + }, + build_run_sim: { + workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', + scheme: 'TestIOSApp', + simulatorName: 'iPhone 16', + }, + }, }); }); @@ -467,17 +459,24 @@ describe('scaffold_ios_project plugin', () => { projectPath: '/tmp/test-projects', platform: 'iOS', message: 'Successfully scaffolded iOS project "TestIOSApp" in /tmp/test-projects', - nextSteps: [ - 'Important: Before working on the project make sure to read the README.md file in the workspace root directory.', - 'Build for simulator: build_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })', - 'Build and run on simulator: build_run_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })', - ], }, null, 2, ), }, ], + nextStepParams: { + build_sim: { + workspacePath: '/tmp/test-projects/MyProject.xcworkspace', + scheme: 'MyProject', + simulatorName: 'iPhone 16', + }, + build_run_sim: { + workspacePath: '/tmp/test-projects/MyProject.xcworkspace', + scheme: 'MyProject', + simulatorName: 'iPhone 16', + }, + }, }); }); @@ -485,6 +484,7 @@ describe('scaffold_ios_project plugin', () => { const result = await scaffold_ios_projectLogic( { projectName: '123InvalidName', + customizeNames: true, outputPath: '/tmp/test-projects', }, mockCommandExecutor, @@ -524,6 +524,7 @@ describe('scaffold_ios_project plugin', () => { const result = await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, mockCommandExecutor, @@ -549,8 +550,7 @@ describe('scaffold_ios_project plugin', () => { }); it('should return error response for template download failure', async () => { - // Temporarily disable local template to force download - delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; + await initConfigStoreForTest({ iosTemplatePath: '' }); // Mock command executor to fail for curl commands const failingMockCommandExecutor = createMockExecutor({ @@ -562,6 +562,7 @@ describe('scaffold_ios_project plugin', () => { const result = await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, failingMockCommandExecutor, @@ -586,13 +587,11 @@ describe('scaffold_ios_project plugin', () => { isError: true, }); - // Restore environment variable - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path'; + await initConfigStoreForTest({ iosTemplatePath: '/mock/template/path' }); }); it.skip('should return error response for template extraction failure', async () => { - // Temporarily disable local template to force download - delete process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; + await initConfigStoreForTest({ iosTemplatePath: '' }); // Create a mock that returns false for local template paths to force download const downloadMockFileSystemExecutor = createMockFileSystemExecutor({ @@ -613,7 +612,7 @@ describe('scaffold_ios_project plugin', () => { rm: async () => {}, cp: async () => {}, writeFile: async () => {}, - stat: async () => ({ isDirectory: () => true }), + stat: async () => ({ isDirectory: () => true, mtimeMs: 0 }), }); // Mock command executor to fail for unzip commands @@ -626,6 +625,7 @@ describe('scaffold_ios_project plugin', () => { const result = await scaffold_ios_projectLogic( { projectName: 'TestIOSApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, failingMockCommandExecutor, @@ -650,8 +650,7 @@ describe('scaffold_ios_project plugin', () => { isError: true, }); - // Restore environment variable - process.env.XCODEBUILDMCP_IOS_TEMPLATE_PATH = '/mock/template/path'; + await initConfigStoreForTest({ iosTemplatePath: '/mock/template/path' }); }); }); }); diff --git a/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts b/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts index 69acc1ba..1bb6f1cd 100644 --- a/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts +++ b/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts @@ -14,9 +14,22 @@ import { createMockFileSystemExecutor, createNoopExecutor, createMockExecutor, + createMockCommandResponse, } from '../../../../test-utils/mock-executors.ts'; -import plugin, { scaffold_macos_projectLogic } from '../scaffold_macos_project.ts'; +import { schema, handler, scaffold_macos_projectLogic } from '../scaffold_macos_project.ts'; import { TemplateManager } from '../../../../utils/template/index.ts'; +import { + __resetConfigStoreForTests, + initConfigStore, + type RuntimeConfigOverrides, +} from '../../../../utils/config-store.ts'; + +const cwd = '/repo'; + +async function initConfigStoreForTest(overrides?: RuntimeConfigOverrides): Promise { + __resetConfigStoreForTests(); + await initConfigStore({ cwd, fs: createMockFileSystemExecutor(), overrides }); +} // ONLY ALLOWED MOCKING: createMockFileSystemExecutor @@ -81,31 +94,23 @@ describe('scaffold_macos_project plugin', () => { // Replace the real TemplateManager with our stub for most tests (TemplateManager as any).getTemplatePath = templateManagerStub.getTemplatePath; (TemplateManager as any).cleanup = templateManagerStub.cleanup; + + await initConfigStoreForTest(); }); describe('Export Field Validation (Literal)', () => { - it('should have correct name field', () => { - expect(plugin.name).toBe('scaffold_macos_project'); - }); - - it('should have correct description field', () => { - expect(plugin.description).toBe( - 'Scaffold a new macOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper macOS configuration.', - ); - }); - it('should have handler as function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have valid schema with required fields', () => { // Test the schema object exists - expect(plugin.schema).toBeDefined(); - expect(plugin.schema.projectName).toBeDefined(); - expect(plugin.schema.outputPath).toBeDefined(); - expect(plugin.schema.bundleIdentifier).toBeDefined(); - expect(plugin.schema.customizeNames).toBeDefined(); - expect(plugin.schema.deploymentTarget).toBeDefined(); + expect(schema).toBeDefined(); + expect(schema.projectName).toBeDefined(); + expect(schema.outputPath).toBeDefined(); + expect(schema.bundleIdentifier).toBeDefined(); + expect(schema.customizeNames).toBeDefined(); + expect(schema.deploymentTarget).toBeDefined(); }); }); @@ -114,7 +119,7 @@ describe('scaffold_macos_project plugin', () => { // This test validates that the curl command would be generated correctly // by verifying the URL construction logic const expectedUrl = - 'https://github.com/cameroncooke/XcodeBuildMCP-macOS-Template/releases/download/'; + 'https://github.com/getsentry/XcodeBuildMCP-macOS-Template/releases/download/'; // The curl command should be structured correctly for macOS template expect(expectedUrl).toContain('XcodeBuildMCP-macOS-Template'); @@ -151,7 +156,7 @@ describe('scaffold_macos_project plugin', () => { it('should generate correct commands for template with version', async () => { // This test validates that the curl command would be generated correctly with version const testVersion = 'v1.0.0'; - const expectedUrlWithVersion = `https://github.com/cameroncooke/XcodeBuildMCP-macOS-Template/releases/download/${testVersion}/`; + const expectedUrlWithVersion = `https://github.com/getsentry/XcodeBuildMCP-macOS-Template/releases/download/${testVersion}/`; // The URL should contain the specific version expect(expectedUrlWithVersion).toContain(testVersion); @@ -163,7 +168,7 @@ describe('scaffold_macos_project plugin', () => { // The full URL should be correctly constructed expect(expectedUrlWithVersion).toBe( - `https://github.com/cameroncooke/XcodeBuildMCP-macOS-Template/releases/download/${testVersion}/`, + `https://github.com/getsentry/XcodeBuildMCP-macOS-Template/releases/download/${testVersion}/`, ); }); @@ -171,24 +176,18 @@ describe('scaffold_macos_project plugin', () => { let capturedCommands: string[][] = []; const trackingExecutor = async (command: string[]) => { capturedCommands.push(command); - return { + return createMockCommandResponse({ success: true, output: 'Command successful', - error: undefined, - process: { pid: 12345 }, - }; + }); }; - // Store original environment variable - const originalEnv = process.env.XCODEBUILDMCP_MACOS_TEMPLATE_PATH; - // Mock local template path exists mockFileSystemExecutor.existsSync = (path: string) => { return path === '/local/template/path' || path === '/local/template/path/template'; }; - // Set environment variable for local template path - process.env.XCODEBUILDMCP_MACOS_TEMPLATE_PATH = '/local/template/path'; + await initConfigStoreForTest({ macosTemplatePath: '/local/template/path' }); // Restore original TemplateManager for command generation tests const { TemplateManager: OriginalTemplateManager } = await import( @@ -200,6 +199,7 @@ describe('scaffold_macos_project plugin', () => { await scaffold_macos_projectLogic( { projectName: 'TestMacApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, trackingExecutor, @@ -214,9 +214,6 @@ describe('scaffold_macos_project plugin', () => { expect.arrayContaining(['unzip', expect.anything(), expect.anything()]), ); - // Clean up environment variable - process.env.XCODEBUILDMCP_MACOS_TEMPLATE_PATH = originalEnv; - // Restore stub after test (TemplateManager as any).getTemplatePath = templateManagerStub.getTemplatePath; (TemplateManager as any).cleanup = templateManagerStub.cleanup; @@ -228,9 +225,9 @@ describe('scaffold_macos_project plugin', () => { const result = await scaffold_macos_projectLogic( { projectName: 'TestMacApp', + customizeNames: true, outputPath: '/tmp/test-projects', bundleIdentifier: 'com.test.macapp', - customizeNames: false, }, createNoopExecutor(), mockFileSystemExecutor, @@ -246,17 +243,22 @@ describe('scaffold_macos_project plugin', () => { projectPath: '/tmp/test-projects', platform: 'macOS', message: 'Successfully scaffolded macOS project "TestMacApp" in /tmp/test-projects', - nextSteps: [ - 'Important: Before working on the project make sure to read the README.md file in the workspace root directory.', - 'Build for macOS: build_macos({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject" })', - 'Build & Run on macOS: build_run_macos({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject" })', - ], }, null, 2, ), }, ], + nextStepParams: { + build_macos: { + workspacePath: '/tmp/test-projects/TestMacApp.xcworkspace', + scheme: 'TestMacApp', + }, + build_run_macos: { + workspacePath: '/tmp/test-projects/TestMacApp.xcworkspace', + scheme: 'TestMacApp', + }, + }, }); // Verify template manager calls using manual tracking @@ -286,17 +288,22 @@ describe('scaffold_macos_project plugin', () => { projectPath: '/tmp/test-projects', platform: 'macOS', message: 'Successfully scaffolded macOS project "TestMacApp" in /tmp/test-projects', - nextSteps: [ - 'Important: Before working on the project make sure to read the README.md file in the workspace root directory.', - 'Build for macOS: build_macos({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject" })', - 'Build & Run on macOS: build_run_macos({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject" })', - ], }, null, 2, ), }, ], + nextStepParams: { + build_macos: { + workspacePath: '/tmp/test-projects/MyProject.xcworkspace', + scheme: 'MyProject', + }, + build_run_macos: { + workspacePath: '/tmp/test-projects/MyProject.xcworkspace', + scheme: 'MyProject', + }, + }, }); }); @@ -304,6 +311,7 @@ describe('scaffold_macos_project plugin', () => { const result = await scaffold_macos_projectLogic( { projectName: '123InvalidName', + customizeNames: true, outputPath: '/tmp/test-projects', }, createNoopExecutor(), @@ -336,6 +344,7 @@ describe('scaffold_macos_project plugin', () => { const result = await scaffold_macos_projectLogic( { projectName: 'TestMacApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, createNoopExecutor(), @@ -366,6 +375,7 @@ describe('scaffold_macos_project plugin', () => { const result = await scaffold_macos_projectLogic( { projectName: 'TestMacApp', + customizeNames: true, outputPath: '/tmp/test-projects', }, createNoopExecutor(), @@ -396,8 +406,8 @@ describe('scaffold_macos_project plugin', () => { await scaffold_macos_projectLogic( { projectName: 'TestApp', - outputPath: '/tmp/test', customizeNames: true, + outputPath: '/tmp/test', }, createNoopExecutor(), mockFileSystemExecutor, diff --git a/src/mcp/tools/project-scaffolding/index.ts b/src/mcp/tools/project-scaffolding/index.ts deleted file mode 100644 index d10cefd2..00000000 --- a/src/mcp/tools/project-scaffolding/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Project Scaffolding workflow - * - * Provides tools for creating new iOS and macOS projects from templates. - * These tools are used at project inception to bootstrap new applications - * with best practices and standard configurations. - */ - -export const workflow = { - name: 'Project Scaffolding', - description: - 'Tools for creating new iOS and macOS projects from templates. Bootstrap new applications with best practices, standard configurations, and modern project structures.', -}; diff --git a/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts b/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts index c98a43fe..67935049 100644 --- a/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts +++ b/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts @@ -14,56 +14,29 @@ import { getDefaultCommandExecutor, getDefaultFileSystemExecutor, } from '../../../utils/execution/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; // Common base schema for both iOS and macOS const BaseScaffoldSchema = z.object({ - projectName: z.string().min(1).describe('Name of the new project'), - outputPath: z.string().describe('Path where the project should be created'), - bundleIdentifier: z - .string() - .optional() - .describe( - 'Bundle identifier (e.g., com.example.myapp). If not provided, will use com.example.projectname', - ), - displayName: z - .string() - .optional() - .describe( - 'App display name (shown on home screen/dock). If not provided, will use projectName', - ), - marketingVersion: z - .string() - .optional() - .describe('Marketing version (e.g., 1.0, 2.1.3). If not provided, will use 1.0'), - currentProjectVersion: z - .string() - .optional() - .describe('Build number (e.g., 1, 42, 100). If not provided, will use 1'), - customizeNames: z - .boolean() - .default(true) - .describe('Whether to customize project names and identifiers. Default is true.'), + projectName: z.string().min(1), + outputPath: z.string(), + bundleIdentifier: z.string().optional(), + displayName: z.string().optional(), + marketingVersion: z.string().optional(), + currentProjectVersion: z.string().optional(), + customizeNames: z.boolean().default(true), }); // iOS-specific schema const ScaffoldiOSProjectSchema = BaseScaffoldSchema.extend({ - deploymentTarget: z - .string() - .optional() - .describe('iOS deployment target (e.g., 18.4, 17.0). If not provided, will use 18.4'), - targetedDeviceFamily: z - .array(z.enum(['iphone', 'ipad', 'universal'])) - .optional() - .describe('Targeted device families'), + deploymentTarget: z.string().optional(), + targetedDeviceFamily: z.array(z.enum(['iphone', 'ipad', 'universal'])).optional(), supportedOrientations: z .array(z.enum(['portrait', 'landscape-left', 'landscape-right', 'portrait-upside-down'])) - .optional() - .describe('Supported orientations for iPhone'), + .optional(), supportedOrientationsIpad: z .array(z.enum(['portrait', 'landscape-left', 'landscape-right', 'portrait-upside-down'])) - .optional() - .describe('Supported orientations for iPad'), + .optional(), }); /** @@ -156,7 +129,7 @@ function updateXCConfigFile(content: string, params: Record): s ); result = result.replace( /PRODUCT_BUNDLE_IDENTIFIER = .+/g, - `PRODUCT_BUNDLE_IDENTIFIER = ${bundleIdentifier ?? `com.example.${projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`}`, + `PRODUCT_BUNDLE_IDENTIFIER = ${bundleIdentifier ?? `io.sentry.${projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`}`, ); result = result.replace( /MARKETING_VERSION = .+/g, @@ -317,8 +290,7 @@ async function processFile( } else { // Use standard placeholder replacement const bundleIdentifier = - bundleIdentifierParam ?? - `com.example.${projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`; + bundleIdentifierParam ?? `io.sentry.${projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`; processedContent = replacePlaceholders(content, projectName, bundleIdentifier); } @@ -386,16 +358,14 @@ export async function scaffold_ios_projectLogic( const projectParams = { ...params, platform: 'iOS' }; const projectPath = await scaffoldProject(projectParams, commandExecutor, fileSystemExecutor); + const generatedProjectName = params.customizeNames === false ? 'MyProject' : params.projectName; + const workspacePath = `${projectPath}/${generatedProjectName}.xcworkspace`; + const response = { success: true, projectPath, platform: 'iOS', message: `Successfully scaffolded iOS project "${params.projectName}" in ${projectPath}`, - nextSteps: [ - `Important: Before working on the project make sure to read the README.md file in the workspace root directory.`, - `Build for simulator: build_sim({ workspacePath: "${projectPath}/${params.customizeNames ? params.projectName : 'MyProject'}.xcworkspace", scheme: "${params.customizeNames ? params.projectName : 'MyProject'}", simulatorName: "iPhone 16" })`, - `Build and run on simulator: build_run_sim({ workspacePath: "${projectPath}/${params.customizeNames ? params.projectName : 'MyProject'}.xcworkspace", scheme: "${params.customizeNames ? params.projectName : 'MyProject'}", simulatorName: "iPhone 16" })`, - ], }; return { @@ -405,6 +375,18 @@ export async function scaffold_ios_projectLogic( text: JSON.stringify(response, null, 2), }, ], + nextStepParams: { + build_sim: { + workspacePath, + scheme: generatedProjectName, + simulatorName: 'iPhone 16', + }, + build_run_sim: { + workspacePath, + scheme: generatedProjectName, + simulatorName: 'iPhone 16', + }, + }, }; } catch (error) { log( @@ -496,21 +478,13 @@ async function scaffoldProject( } } -export default { - name: 'scaffold_ios_project', - description: - 'Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration.', - schema: ScaffoldiOSProjectSchema.shape, - annotations: { - title: 'Scaffold iOS Project', - destructiveHint: true, - }, - async handler(args: Record): Promise { - const params = ScaffoldiOSProjectSchema.parse(args); - return scaffold_ios_projectLogic( - params, - getDefaultCommandExecutor(), - getDefaultFileSystemExecutor(), - ); - }, -}; +export const schema = ScaffoldiOSProjectSchema.shape; + +export async function handler(args: Record): Promise { + const params = ScaffoldiOSProjectSchema.parse(args); + return scaffold_ios_projectLogic( + params, + getDefaultCommandExecutor(), + getDefaultFileSystemExecutor(), + ); +} diff --git a/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts b/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts index cc7841bd..f32f4cc9 100644 --- a/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts +++ b/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts @@ -9,50 +9,25 @@ import { join, dirname, basename } from 'path'; import { log } from '../../../utils/logging/index.ts'; import { ValidationError } from '../../../utils/responses/index.ts'; import { TemplateManager } from '../../../utils/template/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; -import { - CommandExecutor, - getDefaultCommandExecutor, - getDefaultFileSystemExecutor, -} from '../../../utils/command.ts'; -import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/command.ts'; +import { getDefaultCommandExecutor, getDefaultFileSystemExecutor } from '../../../utils/command.ts'; +import type { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; // Common base schema for both iOS and macOS const BaseScaffoldSchema = z.object({ - projectName: z.string().min(1).describe('Name of the new project'), - outputPath: z.string().describe('Path where the project should be created'), - bundleIdentifier: z - .string() - .optional() - .describe( - 'Bundle identifier (e.g., com.example.myapp). If not provided, will use com.example.projectname', - ), - displayName: z - .string() - .optional() - .describe( - 'App display name (shown on home screen/dock). If not provided, will use projectName', - ), - marketingVersion: z - .string() - .optional() - .describe('Marketing version (e.g., 1.0, 2.1.3). If not provided, will use 1.0'), - currentProjectVersion: z - .string() - .optional() - .describe('Build number (e.g., 1, 42, 100). If not provided, will use 1'), - customizeNames: z - .boolean() - .default(true) - .describe('Whether to customize project names and identifiers. Default is true.'), + projectName: z.string().min(1), + outputPath: z.string(), + bundleIdentifier: z.string().optional(), + displayName: z.string().optional(), + marketingVersion: z.string().optional(), + currentProjectVersion: z.string().optional(), + customizeNames: z.boolean().default(true), }); // macOS-specific schema const ScaffoldmacOSProjectSchema = BaseScaffoldSchema.extend({ - deploymentTarget: z - .string() - .optional() - .describe('macOS deployment target (e.g., 15.4, 14.0). If not provided, will use 15.4'), + deploymentTarget: z.string().optional(), }); // Use z.infer for type safety @@ -105,7 +80,7 @@ function updateXCConfigFile( ); result = result.replace( /PRODUCT_BUNDLE_IDENTIFIER = .+/g, - `PRODUCT_BUNDLE_IDENTIFIER = ${params.bundleIdentifier ?? `com.example.${params.projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`}`, + `PRODUCT_BUNDLE_IDENTIFIER = ${params.bundleIdentifier ?? `io.sentry.${params.projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`}`, ); result = result.replace( /MARKETING_VERSION = .+/g, @@ -229,7 +204,7 @@ async function processFile( // Use standard placeholder replacement const bundleIdentifier = params.bundleIdentifier ?? - `com.example.${params.projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`; + `io.sentry.${params.projectName.toLowerCase().replace(/[^a-z0-9]/g, '')}`; processedContent = replacePlaceholders(content, params.projectName, bundleIdentifier); } @@ -357,16 +332,14 @@ export async function scaffold_macos_projectLogic( const projectParams = { ...params, platform: 'macOS' as const }; const projectPath = await scaffoldProject(projectParams, commandExecutor, fileSystemExecutor); + const generatedProjectName = params.customizeNames === false ? 'MyProject' : params.projectName; + const workspacePath = `${projectPath}/${generatedProjectName}.xcworkspace`; + const response = { success: true, projectPath, platform: 'macOS', message: `Successfully scaffolded macOS project "${params.projectName}" in ${projectPath}`, - nextSteps: [ - `Important: Before working on the project make sure to read the README.md file in the workspace root directory.`, - `Build for macOS: build_macos({ workspacePath: "${projectPath}/${params.customizeNames ? params.projectName : 'MyProject'}.xcworkspace", scheme: "${params.customizeNames ? params.projectName : 'MyProject'}" })`, - `Build & Run on macOS: build_run_macos({ workspacePath: "${projectPath}/${params.customizeNames ? params.projectName : 'MyProject'}.xcworkspace", scheme: "${params.customizeNames ? params.projectName : 'MyProject'}" })`, - ], }; return { @@ -376,6 +349,16 @@ export async function scaffold_macos_projectLogic( text: JSON.stringify(response, null, 2), }, ], + nextStepParams: { + build_macos: { + workspacePath, + scheme: generatedProjectName, + }, + build_run_macos: { + workspacePath, + scheme: generatedProjectName, + }, + }, }; } catch (error) { log( @@ -402,22 +385,13 @@ export async function scaffold_macos_projectLogic( } } -export default { - name: 'scaffold_macos_project', - description: - 'Scaffold a new macOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper macOS configuration.', - schema: ScaffoldmacOSProjectSchema.shape, - annotations: { - title: 'Scaffold macOS Project', - destructiveHint: true, - }, - async handler(args: Record): Promise { - // Validate the arguments against the schema before processing - const validatedArgs = ScaffoldmacOSProjectSchema.parse(args); - return scaffold_macos_projectLogic( - validatedArgs, - getDefaultCommandExecutor(), - getDefaultFileSystemExecutor(), - ); - }, -}; +export const schema = ScaffoldmacOSProjectSchema.shape; + +export async function handler(args: Record): Promise { + const validatedArgs = ScaffoldmacOSProjectSchema.parse(args); + return scaffold_macos_projectLogic( + validatedArgs, + getDefaultCommandExecutor(), + getDefaultFileSystemExecutor(), + ); +} diff --git a/src/mcp/tools/session-management/__tests__/index.test.ts b/src/mcp/tools/session-management/__tests__/index.test.ts deleted file mode 100644 index eaf33553..00000000 --- a/src/mcp/tools/session-management/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for session-management workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('session-management workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('session-management'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Manage session defaults for projectPath/workspacePath, scheme, configuration, simulatorName/simulatorId, deviceId, useLatestOS and arch. These defaults are required by many tools and must be set before attempting to call tools that would depend on these values.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts b/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts index 7d4a06df..c93bc5ef 100644 --- a/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts +++ b/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { sessionStore } from '../../../../utils/session-store.ts'; -import plugin, { sessionClearDefaultsLogic } from '../session_clear_defaults.ts'; +import { schema, handler, sessionClearDefaultsLogic } from '../session_clear_defaults.ts'; describe('session-clear-defaults tool', () => { beforeEach(() => { @@ -12,6 +12,7 @@ describe('session-clear-defaults tool', () => { deviceId: 'DEVICE-123', useLatestOS: true, arch: 'arm64', + derivedDataPath: '/tmp/derived-data', }); }); @@ -19,58 +20,95 @@ describe('session-clear-defaults tool', () => { sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('session-clear-defaults'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe('Clear selected or all session defaults.'); - }); - + describe('Export Field Validation', () => { it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have schema object', () => { - expect(plugin.schema).toBeDefined(); - expect(typeof plugin.schema).toBe('object'); + expect(schema).toBeDefined(); + expect(typeof schema).toBe('object'); }); }); describe('Handler Behavior', () => { it('should clear specific keys when provided', async () => { - const result = await sessionClearDefaultsLogic({ keys: ['scheme', 'deviceId'] }); + const result = await sessionClearDefaultsLogic({ + keys: ['scheme', 'deviceId', 'derivedDataPath'], + }); expect(result.isError).toBe(false); expect(result.content[0].text).toContain('Session defaults cleared'); const current = sessionStore.getAll(); expect(current.scheme).toBeUndefined(); expect(current.deviceId).toBeUndefined(); + expect(current.derivedDataPath).toBeUndefined(); expect(current.projectPath).toBe('/path/to/proj.xcodeproj'); expect(current.simulatorName).toBe('iPhone 16'); expect(current.useLatestOS).toBe(true); expect(current.arch).toBe('arm64'); }); - it('should clear all when all=true', async () => { + it('should clear all profiles only when all=true', async () => { + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'IOS' }); + sessionStore.setActiveProfile(null); const result = await sessionClearDefaultsLogic({ all: true }); expect(result.isError).toBe(false); - expect(result.content[0].text).toBe('Session defaults cleared'); + expect(result.content[0].text).toBe('All session defaults cleared'); const current = sessionStore.getAll(); expect(Object.keys(current).length).toBe(0); + expect(sessionStore.listProfiles()).toEqual([]); + expect(sessionStore.getActiveProfile()).toBeNull(); }); - it('should clear all when no params provided', async () => { + it('should clear only active profile when no params provided', async () => { + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'IOS', projectPath: '/ios/project.xcodeproj' }); + sessionStore.setActiveProfile(null); + sessionStore.setDefaults({ scheme: 'Global' }); + sessionStore.setActiveProfile('ios'); + const result = await sessionClearDefaultsLogic({}); expect(result.isError).toBe(false); - const current = sessionStore.getAll(); - expect(Object.keys(current).length).toBe(0); + + expect(sessionStore.getAll()).toEqual({}); + expect(sessionStore.listProfiles()).toEqual([]); + + sessionStore.setActiveProfile(null); + expect(sessionStore.getAll().scheme).toBe('Global'); + }); + + it('should clear a specific profile when profile is provided', async () => { + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'IOS' }); + sessionStore.setActiveProfile('watch'); + sessionStore.setDefaults({ scheme: 'Watch' }); + sessionStore.setActiveProfile('watch'); + + const result = await sessionClearDefaultsLogic({ profile: 'ios' }); + expect(result.isError).toBe(false); + expect(result.content[0].text).toContain('profile "ios"'); + + expect(sessionStore.listProfiles()).toEqual(['watch']); + expect(sessionStore.getAll().scheme).toBe('Watch'); + }); + + it('should error when the specified profile does not exist', async () => { + const result = await sessionClearDefaultsLogic({ profile: 'missing' }); + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('does not exist'); + }); + + it('should reject all=true when combined with scoped arguments', async () => { + const result = await sessionClearDefaultsLogic({ all: true, profile: 'ios' }); + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('cannot be combined'); }); it('should validate keys enum', async () => { - const result = (await plugin.handler({ keys: ['invalid' as any] })) as any; + const result = (await handler({ keys: ['invalid' as any] })) as any; expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Parameter validation failed'); expect(result.content[0].text).toContain('keys'); diff --git a/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts b/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts index df9ef581..7c28e6fe 100644 --- a/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts +++ b/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts @@ -1,54 +1,84 @@ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import path from 'node:path'; +import { parse as parseYaml } from 'yaml'; +import { __resetConfigStoreForTests, initConfigStore } from '../../../../utils/config-store.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import plugin, { sessionSetDefaultsLogic } from '../session_set_defaults.ts'; +import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, sessionSetDefaultsLogic } from '../session_set_defaults.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; describe('session-set-defaults tool', () => { beforeEach(() => { + __resetConfigStoreForTests(); sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('session-set-defaults'); - }); + const cwd = '/repo'; + const configPath = path.join(cwd, '.xcodebuildmcp', 'config.yaml'); - it('should have correct description', () => { - expect(plugin.description).toBe( - 'Set the session defaults needed by many tools. Most tools require one or more session defaults to be set before they can be used. Agents should set all relevant defaults up front in a single call (e.g., project/workspace, scheme, simulator or device ID, useLatestOS) to avoid iterative prompts; only set the keys your workflow needs.', - ); + // Mock executor that simulates successful simulator lookup + function createMockExecutor(): CommandExecutor { + return vi.fn().mockImplementation(async (command: string[]) => { + if (command.includes('simctl') && command.includes('list')) { + return { + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { udid: 'RESOLVED-SIM-UUID', name: 'iPhone 16' }, + { udid: 'OTHER-SIM-UUID', name: 'iPhone 15' }, + ], + }, + }), + }; + } + return { success: true, output: '' }; }); + } + + function createContext() { + return { + executor: createMockExecutor(), + }; + } + describe('Export Field Validation', () => { it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have schema object', () => { - expect(plugin.schema).toBeDefined(); - expect(typeof plugin.schema).toBe('object'); + expect(schema).toBeDefined(); + expect(typeof schema).toBe('object'); }); }); describe('Handler Behavior', () => { it('should set provided defaults and return updated state', async () => { - const result = await sessionSetDefaultsLogic({ - scheme: 'MyScheme', - simulatorName: 'iPhone 16', - useLatestOS: true, - arch: 'arm64', - }); + const result = await sessionSetDefaultsLogic( + { + scheme: 'MyScheme', + simulatorName: 'iPhone 16', + useLatestOS: true, + arch: 'arm64', + }, + createContext(), + ); - expect(result.isError).toBe(false); + expect(result.isError).toBeFalsy(); expect(result.content[0].text).toContain('Defaults updated:'); const current = sessionStore.getAll(); expect(current.scheme).toBe('MyScheme'); expect(current.simulatorName).toBe('iPhone 16'); + // simulatorId resolution happens in background; immediate update keeps explicit inputs only + expect(current.simulatorId).toBeUndefined(); expect(current.useLatestOS).toBe(true); expect(current.arch).toBe('arm64'); }); it('should validate parameter types via Zod', async () => { - const result = await plugin.handler({ + const result = await handler({ useLatestOS: 'yes' as unknown as boolean, }); @@ -59,54 +89,279 @@ describe('session-set-defaults tool', () => { it('should clear workspacePath when projectPath is set', async () => { sessionStore.setDefaults({ workspacePath: '/old/App.xcworkspace' }); - await sessionSetDefaultsLogic({ projectPath: '/new/App.xcodeproj' }); + const result = await sessionSetDefaultsLogic( + { projectPath: '/new/App.xcodeproj' }, + createContext(), + ); const current = sessionStore.getAll(); expect(current.projectPath).toBe('/new/App.xcodeproj'); expect(current.workspacePath).toBeUndefined(); + expect(result.content[0].text).toContain( + 'Cleared workspacePath because projectPath was set.', + ); }); it('should clear projectPath when workspacePath is set', async () => { sessionStore.setDefaults({ projectPath: '/old/App.xcodeproj' }); - await sessionSetDefaultsLogic({ workspacePath: '/new/App.xcworkspace' }); + const result = await sessionSetDefaultsLogic( + { workspacePath: '/new/App.xcworkspace' }, + createContext(), + ); const current = sessionStore.getAll(); expect(current.workspacePath).toBe('/new/App.xcworkspace'); expect(current.projectPath).toBeUndefined(); + expect(result.content[0].text).toContain( + 'Cleared projectPath because workspacePath was set.', + ); }); - it('should clear simulatorName when simulatorId is set', async () => { - sessionStore.setDefaults({ simulatorName: 'iPhone 16' }); - await sessionSetDefaultsLogic({ simulatorId: 'SIM-UUID' }); + it('should clear stale simulatorName when simulatorId is explicitly set', async () => { + sessionStore.setDefaults({ simulatorName: 'Old Name' }); + const result = await sessionSetDefaultsLogic( + { simulatorId: 'RESOLVED-SIM-UUID' }, + createContext(), + ); const current = sessionStore.getAll(); - expect(current.simulatorId).toBe('SIM-UUID'); + expect(current.simulatorId).toBe('RESOLVED-SIM-UUID'); expect(current.simulatorName).toBeUndefined(); + expect(result.content[0].text).toContain( + 'Cleared simulatorName because simulatorId was set; background resolution will repopulate it.', + ); }); - it('should clear simulatorId when simulatorName is set', async () => { - sessionStore.setDefaults({ simulatorId: 'SIM-UUID' }); - await sessionSetDefaultsLogic({ simulatorName: 'iPhone 16' }); + it('should clear stale simulatorId when only simulatorName is set', async () => { + sessionStore.setDefaults({ simulatorId: 'OLD-SIM-UUID' }); + const result = await sessionSetDefaultsLogic({ simulatorName: 'iPhone 16' }, createContext()); const current = sessionStore.getAll(); + // simulatorId resolution happens in background; stale id is cleared immediately expect(current.simulatorName).toBe('iPhone 16'); expect(current.simulatorId).toBeUndefined(); + expect(result.content[0].text).toContain( + 'Cleared simulatorId because simulatorName was set; background resolution will repopulate it.', + ); }); - it('should reject when both projectPath and workspacePath are provided', async () => { - const res = await plugin.handler({ - projectPath: '/app/App.xcodeproj', - workspacePath: '/app/App.xcworkspace', + it('does not claim simulatorName was cleared when none existed', async () => { + sessionStore.setDefaults({ simulatorId: 'RESOLVED-SIM-UUID' }); + const result = await sessionSetDefaultsLogic( + { simulatorId: 'RESOLVED-SIM-UUID' }, + createContext(), + ); + + expect(result.content[0].text).not.toContain('Cleared simulatorName'); + }); + + it('should not fail when simulatorName cannot be resolved immediately', async () => { + const contextWithFailingExecutor = { + executor: vi.fn().mockImplementation(async (command: string[]) => { + if (command.includes('simctl') && command.includes('list')) { + return { + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { udid: 'OTHER-SIM-UUID', name: 'iPhone 15' }, + ], + }, + }), + }; + } + return { success: true, output: '' }; + }), + }; + + const result = await sessionSetDefaultsLogic( + { simulatorName: 'NonExistentSimulator' }, + contextWithFailingExecutor, + ); + expect(result.isError).toBe(false); + expect(sessionStore.getAll().simulatorName).toBe('NonExistentSimulator'); + }); + + it('should prefer workspacePath when both projectPath and workspacePath are provided', async () => { + const res = await sessionSetDefaultsLogic( + { + projectPath: '/app/App.xcodeproj', + workspacePath: '/app/App.xcworkspace', + }, + createContext(), + ); + const current = sessionStore.getAll(); + expect(current.workspacePath).toBe('/app/App.xcworkspace'); + expect(current.projectPath).toBeUndefined(); + expect(res.content[0].text).toContain( + 'Both projectPath and workspacePath were provided; keeping workspacePath and ignoring projectPath.', + ); + }); + + it('should keep both simulatorId and simulatorName when both are provided', async () => { + const res = await sessionSetDefaultsLogic( + { + simulatorId: 'SIM-1', + simulatorName: 'iPhone 16', + }, + createContext(), + ); + const current = sessionStore.getAll(); + // Both are kept, simulatorId takes precedence for tools + expect(current.simulatorId).toBe('SIM-1'); + expect(current.simulatorName).toBe('iPhone 16'); + expect(res.content[0].text).toContain( + 'Both simulatorId and simulatorName were provided; simulatorId will be used by tools.', + ); + }); + + it('should persist defaults when persist is true', async () => { + const yaml = [ + 'schemaVersion: 1', + 'sessionDefaults:', + ' projectPath: "/old/App.xcodeproj"', + ' simulatorName: "OldSim"', + '', + ].join('\n'); + + const writes: { path: string; content: string }[] = []; + const fs = createMockFileSystemExecutor({ + existsSync: (targetPath: string) => targetPath === configPath, + readFile: async (targetPath: string) => { + if (targetPath !== configPath) { + throw new Error(`Unexpected readFile path: ${targetPath}`); + } + return yaml; + }, + writeFile: async (targetPath: string, content: string) => { + writes.push({ path: targetPath, content }); + }, }); - expect(res.isError).toBe(true); - expect(res.content[0].text).toContain('Parameter validation failed'); - expect(res.content[0].text).toContain('projectPath and workspacePath are mutually exclusive'); + + await initConfigStore({ cwd, fs }); + + const result = await sessionSetDefaultsLogic( + { + workspacePath: '/new/App.xcworkspace', + simulatorId: 'RESOLVED-SIM-UUID', + persist: true, + }, + createContext(), + ); + + expect(result.content[0].text).toContain('Persisted defaults to'); + expect(writes.length).toBe(1); + expect(writes[0].path).toBe(configPath); + + const parsed = parseYaml(writes[0].content) as { + sessionDefaults?: Record; + }; + expect(parsed.sessionDefaults?.workspacePath).toBe('/new/App.xcworkspace'); + expect(parsed.sessionDefaults?.projectPath).toBeUndefined(); + expect(parsed.sessionDefaults?.simulatorId).toBe('RESOLVED-SIM-UUID'); + expect(parsed.sessionDefaults?.simulatorName).toBeUndefined(); + }); + + it('sets defaults on existing named profile and activates it', async () => { + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'OldIOS' }); + sessionStore.setActiveProfile(null); + + const result = await sessionSetDefaultsLogic( + { + profile: 'ios', + scheme: 'NewIOS', + simulatorName: 'iPhone 16', + }, + createContext(), + ); + + expect(result.isError).toBe(false); + expect(result.content[0].text).toContain('Activated profile "ios".'); + expect(sessionStore.getActiveProfile()).toBe('ios'); + expect(sessionStore.getAll().scheme).toBe('NewIOS'); + expect(sessionStore.getAll().simulatorName).toBe('iPhone 16'); + }); + + it('returns error when profile does not exist and createIfNotExists is false', async () => { + const result = await sessionSetDefaultsLogic( + { + profile: 'missing', + scheme: 'NewIOS', + }, + createContext(), + ); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('Profile "missing" does not exist'); + expect(result.content[0].text).toContain('createIfNotExists=true'); + }); + + it('creates profile when createIfNotExists is true and activates it', async () => { + const result = await sessionSetDefaultsLogic( + { + profile: 'ios', + createIfNotExists: true, + scheme: 'NewIOS', + }, + createContext(), + ); + + expect(result.isError).toBe(false); + expect(result.content[0].text).toContain('Created and activated profile "ios".'); + expect(sessionStore.getActiveProfile()).toBe('ios'); + expect(sessionStore.getAll().scheme).toBe('NewIOS'); }); - it('should reject when both simulatorId and simulatorName are provided', async () => { - const res = await plugin.handler({ - simulatorId: 'SIM-1', - simulatorName: 'iPhone 16', + it('persists defaults and active profile when profile is provided', async () => { + const yaml = [ + 'schemaVersion: 1', + 'sessionDefaultsProfiles:', + ' ios:', + ' scheme: "Old"', + '', + ].join('\n'); + + const writes: { path: string; content: string }[] = []; + let persistedYaml = yaml; + const fs = createMockFileSystemExecutor({ + existsSync: (targetPath: string) => targetPath === configPath, + readFile: async (targetPath: string) => { + if (targetPath !== configPath) { + throw new Error(`Unexpected readFile path: ${targetPath}`); + } + return persistedYaml; + }, + writeFile: async (targetPath: string, content: string) => { + writes.push({ path: targetPath, content }); + persistedYaml = content; + }, }); - expect(res.isError).toBe(true); - expect(res.content[0].text).toContain('Parameter validation failed'); - expect(res.content[0].text).toContain('simulatorId and simulatorName are mutually exclusive'); + + await initConfigStore({ cwd, fs }); + sessionStore.setActiveProfile('ios'); + sessionStore.setActiveProfile(null); + + await sessionSetDefaultsLogic( + { + profile: 'ios', + scheme: 'NewIOS', + simulatorName: 'iPhone 16', + persist: true, + }, + createContext(), + ); + + expect(writes.length).toBe(2); + const parsed = parseYaml(writes[writes.length - 1].content) as { + sessionDefaultsProfiles?: Record>; + activeSessionDefaultsProfile?: string; + }; + expect(parsed.sessionDefaultsProfiles?.ios?.scheme).toBe('NewIOS'); + expect(parsed.sessionDefaultsProfiles?.ios?.simulatorName).toBe('iPhone 16'); + expect(parsed.activeSessionDefaultsProfile).toBe('ios'); + }); + + it('should not persist when persist is true but no defaults were provided', async () => { + const result = await sessionSetDefaultsLogic({ persist: true }, createContext()); + + expect(result.content[0].text).toContain('No defaults provided to persist'); }); }); }); diff --git a/src/mcp/tools/session-management/__tests__/session_show_defaults.test.ts b/src/mcp/tools/session-management/__tests__/session_show_defaults.test.ts index e4162556..b61488cb 100644 --- a/src/mcp/tools/session-management/__tests__/session_show_defaults.test.ts +++ b/src/mcp/tools/session-management/__tests__/session_show_defaults.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { sessionStore } from '../../../../utils/session-store.ts'; -import plugin from '../session_show_defaults.ts'; +import { schema, handler } from '../session_show_defaults.ts'; describe('session-show-defaults tool', () => { beforeEach(() => { @@ -12,38 +12,44 @@ describe('session-show-defaults tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(plugin.name).toBe('session-show-defaults'); - }); - - it('should have correct description', () => { - expect(plugin.description).toBe('Show current session defaults.'); - }); - it('should have handler function', () => { - expect(typeof plugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have empty schema', () => { - expect(plugin.schema).toEqual({}); + expect(schema).toEqual({}); }); }); describe('Handler Behavior', () => { it('should return empty defaults when none set', async () => { - const result = await plugin.handler({}); + const result = await handler(); expect(result.isError).toBe(false); - const parsed = JSON.parse(result.content[0].text); + expect(result.content).toHaveLength(1); + expect(typeof result.content[0].text).toBe('string'); + const parsed = JSON.parse(result.content[0].text as string); expect(parsed).toEqual({}); }); it('should return current defaults when set', async () => { sessionStore.setDefaults({ scheme: 'MyScheme', simulatorId: 'SIM-123' }); - const result = await plugin.handler({}); + const result = await handler(); expect(result.isError).toBe(false); - const parsed = JSON.parse(result.content[0].text); + expect(result.content).toHaveLength(1); + expect(typeof result.content[0].text).toBe('string'); + const parsed = JSON.parse(result.content[0].text as string); expect(parsed.scheme).toBe('MyScheme'); expect(parsed.simulatorId).toBe('SIM-123'); }); + + it('shows defaults from the active profile', async () => { + sessionStore.setDefaults({ scheme: 'GlobalScheme' }); + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'IOSScheme' }); + + const result = await handler(); + const parsed = JSON.parse(result.content[0].text as string); + expect(parsed.scheme).toBe('IOSScheme'); + }); }); }); diff --git a/src/mcp/tools/session-management/__tests__/session_use_defaults_profile.test.ts b/src/mcp/tools/session-management/__tests__/session_use_defaults_profile.test.ts new file mode 100644 index 00000000..66e6cb83 --- /dev/null +++ b/src/mcp/tools/session-management/__tests__/session_use_defaults_profile.test.ts @@ -0,0 +1,109 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import path from 'node:path'; +import { parse as parseYaml } from 'yaml'; +import { __resetConfigStoreForTests, initConfigStore } from '../../../../utils/config-store.ts'; +import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; +import { sessionStore } from '../../../../utils/session-store.ts'; +import { + handler, + schema, + sessionUseDefaultsProfileLogic, +} from '../session_use_defaults_profile.ts'; + +describe('session-use-defaults-profile tool', () => { + beforeEach(() => { + __resetConfigStoreForTests(); + sessionStore.clear(); + }); + + const cwd = '/repo'; + const configPath = path.join(cwd, '.xcodebuildmcp', 'config.yaml'); + + it('exports handler and schema', () => { + expect(typeof handler).toBe('function'); + expect(schema).toBeDefined(); + expect(typeof schema).toBe('object'); + }); + + it('activates an existing named profile', async () => { + sessionStore.setActiveProfile('ios'); + sessionStore.setActiveProfile(null); + + const result = await sessionUseDefaultsProfileLogic({ profile: 'ios' }); + expect(result.isError).toBe(false); + expect(sessionStore.getActiveProfile()).toBe('ios'); + expect(sessionStore.listProfiles()).toContain('ios'); + }); + + it('switches back to global profile', async () => { + sessionStore.setActiveProfile('watch'); + const result = await sessionUseDefaultsProfileLogic({ global: true }); + expect(result.isError).toBe(false); + expect(sessionStore.getActiveProfile()).toBeNull(); + }); + + it('returns error when both global and profile are provided', async () => { + const result = await sessionUseDefaultsProfileLogic({ global: true, profile: 'ios' }); + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('either global=true or profile'); + }); + + it('returns error when profile does not exist', async () => { + const result = await sessionUseDefaultsProfileLogic({ profile: 'macos' }); + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('does not exist'); + }); + + it('returns error when profile name is blank after trimming', async () => { + const result = await sessionUseDefaultsProfileLogic({ profile: ' ' }); + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('Profile name cannot be empty'); + }); + + it('returns status for empty args', async () => { + const result = await sessionUseDefaultsProfileLogic({}); + expect(result.isError).toBe(false); + expect(result.content[0].text).toContain('Active defaults profile: global'); + }); + + it('persists active profile when persist=true', async () => { + const writes: { path: string; content: string }[] = []; + const fs = createMockFileSystemExecutor({ + existsSync: (targetPath: string) => targetPath === configPath, + readFile: async () => ['schemaVersion: 1', ''].join('\n'), + writeFile: async (targetPath: string, content: string) => { + writes.push({ path: targetPath, content }); + }, + }); + await initConfigStore({ cwd, fs }); + + sessionStore.setActiveProfile('ios'); + sessionStore.setActiveProfile(null); + + const result = await sessionUseDefaultsProfileLogic({ profile: 'ios', persist: true }); + expect(result.isError).toBe(false); + expect(result.content[0].text).toContain('Persisted active profile selection'); + expect(writes).toHaveLength(1); + const parsed = parseYaml(writes[0].content) as { activeSessionDefaultsProfile?: string }; + expect(parsed.activeSessionDefaultsProfile).toBe('ios'); + }); + + it('removes active profile from config when persisting global selection', async () => { + const writes: { path: string; content: string }[] = []; + const yaml = ['schemaVersion: 1', 'activeSessionDefaultsProfile: "ios"', ''].join('\n'); + const fs = createMockFileSystemExecutor({ + existsSync: (targetPath: string) => targetPath === configPath, + readFile: async () => yaml, + writeFile: async (targetPath: string, content: string) => { + writes.push({ path: targetPath, content }); + }, + }); + await initConfigStore({ cwd, fs }); + + const result = await sessionUseDefaultsProfileLogic({ global: true, persist: true }); + expect(result.isError).toBe(false); + expect(writes).toHaveLength(1); + const parsed = parseYaml(writes[0].content) as { activeSessionDefaultsProfile?: string }; + expect(parsed.activeSessionDefaultsProfile).toBeUndefined(); + }); +}); diff --git a/src/mcp/tools/session-management/index.ts b/src/mcp/tools/session-management/index.ts deleted file mode 100644 index 64854c8c..00000000 --- a/src/mcp/tools/session-management/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'session-management', - description: - 'Manage session defaults for projectPath/workspacePath, scheme, configuration, simulatorName/simulatorId, deviceId, useLatestOS and arch. These defaults are required by many tools and must be set before attempting to call tools that would depend on these values.', -}; diff --git a/src/mcp/tools/session-management/session_clear_defaults.ts b/src/mcp/tools/session-management/session_clear_defaults.ts index d6a58983..6c6a550f 100644 --- a/src/mcp/tools/session-management/session_clear_defaults.ts +++ b/src/mcp/tools/session-management/session_clear_defaults.ts @@ -1,41 +1,88 @@ import * as z from 'zod'; import { sessionStore } from '../../../utils/session-store.ts'; +import { sessionDefaultKeys } from '../../../utils/session-defaults-schema.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import type { ToolResponse } from '../../../types/common.ts'; -const keys = [ - 'projectPath', - 'workspacePath', - 'scheme', - 'configuration', - 'simulatorName', - 'simulatorId', - 'deviceId', - 'useLatestOS', - 'arch', -] as const; +const keys = sessionDefaultKeys; const schemaObj = z.object({ keys: z.array(z.enum(keys)).optional(), - all: z.boolean().optional(), + profile: z + .string() + .min(1) + .optional() + .describe('Clear defaults for this named profile instead of the active profile.'), + all: z + .boolean() + .optional() + .describe( + 'Clear all defaults across global and named profiles. Cannot be combined with keys/profile.', + ), }); type Params = z.infer; export async function sessionClearDefaultsLogic(params: Params): Promise { - if (params.all || !params.keys) sessionStore.clear(); - else sessionStore.clear(params.keys); + if (params.all) { + if (params.profile !== undefined || params.keys !== undefined) { + return { + content: [ + { + type: 'text', + text: 'all=true cannot be combined with profile or keys.', + }, + ], + isError: true, + }; + } + + sessionStore.clear(); + return { content: [{ type: 'text', text: 'All session defaults cleared' }], isError: false }; + } + + const profile = params.profile?.trim(); + if (profile !== undefined) { + if (profile.length === 0) { + return { + content: [{ type: 'text', text: 'Profile name cannot be empty.' }], + isError: true, + }; + } + + if (!sessionStore.listProfiles().includes(profile)) { + return { + content: [{ type: 'text', text: `Profile "${profile}" does not exist.` }], + isError: true, + }; + } + + if (params.keys) { + sessionStore.clearForProfile(profile, params.keys); + } else { + sessionStore.clearForProfile(profile); + } + + return { + content: [{ type: 'text', text: `Session defaults cleared for profile "${profile}"` }], + isError: false, + }; + } + + if (params.keys) { + sessionStore.clear(params.keys); + } else { + sessionStore.clearForProfile(sessionStore.getActiveProfile()); + } + return { content: [{ type: 'text', text: 'Session defaults cleared' }], isError: false }; } -export default { - name: 'session-clear-defaults', - description: 'Clear selected or all session defaults.', - schema: schemaObj.shape, - annotations: { - title: 'Clear Session Defaults', - destructiveHint: true, - }, - handler: createTypedTool(schemaObj, sessionClearDefaultsLogic, getDefaultCommandExecutor), -}; +export const schema = schemaObj.shape; + +export const handler = createTypedTool( + schemaObj, + sessionClearDefaultsLogic, + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/session-management/session_set_defaults.ts b/src/mcp/tools/session-management/session_set_defaults.ts index d1e80c1d..da2686fd 100644 --- a/src/mcp/tools/session-management/session_set_defaults.ts +++ b/src/mcp/tools/session-management/session_set_defaults.ts @@ -1,65 +1,231 @@ import * as z from 'zod'; +import { + persistActiveSessionDefaultsProfile, + persistSessionDefaultsPatch, +} from '../../../utils/config-store.ts'; +import { removeUndefined } from '../../../utils/remove-undefined.ts'; +import { scheduleSimulatorDefaultsRefresh } from '../../../utils/simulator-defaults-refresh.ts'; import { sessionStore, type SessionDefaults } from '../../../utils/session-store.ts'; -import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; -import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { sessionDefaultsSchema } from '../../../utils/session-defaults-schema.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; import type { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; -const baseSchema = z.object({ - projectPath: z.string().optional(), - workspacePath: z.string().optional(), - scheme: z.string().optional(), - configuration: z.string().optional(), - simulatorName: z.string().optional(), - simulatorId: z.string().optional(), - deviceId: z.string().optional(), - useLatestOS: z.boolean().optional(), - arch: z.enum(['arm64', 'x86_64']).optional(), - suppressWarnings: z +const schemaObj = sessionDefaultsSchema.extend({ + profile: z + .string() + .min(1) + .optional() + .describe('Set defaults for this named profile and make it active for the current session.'), + createIfNotExists: z + .boolean() + .optional() + .default(false) + .describe('Create the named profile if it does not exist. Defaults to false.'), + persist: z .boolean() .optional() - .describe('When true, warning messages are filtered from build output to conserve context'), + .describe('Persist provided defaults to .xcodebuildmcp/config.yaml'), }); -const schemaObj = baseSchema - .refine((v) => !(v.projectPath && v.workspacePath), { - message: 'projectPath and workspacePath are mutually exclusive', - path: ['projectPath'], - }) - .refine((v) => !(v.simulatorId && v.simulatorName), { - message: 'simulatorId and simulatorName are mutually exclusive', - path: ['simulatorId'], - }); +type Params = z.input; + +type SessionSetDefaultsContext = { + executor: CommandExecutor; +}; + +export async function sessionSetDefaultsLogic( + params: Params, + context: SessionSetDefaultsContext, +): Promise { + const notices: string[] = []; + let activeProfile = sessionStore.getActiveProfile(); + const { + persist, + profile: rawProfile, + createIfNotExists: rawCreateIfNotExists, + ...rawParams + } = params; + const createIfNotExists = rawCreateIfNotExists ?? false; + + if (rawProfile !== undefined) { + const profile = rawProfile.trim(); + if (profile.length === 0) { + return { + content: [{ type: 'text', text: 'Profile name cannot be empty.' }], + isError: true, + }; + } -type Params = z.infer; + const profileExists = sessionStore.listProfiles().includes(profile); + if (!profileExists && !createIfNotExists) { + return { + content: [ + { + type: 'text', + text: `Profile "${profile}" does not exist. Pass createIfNotExists=true to create it.`, + }, + ], + isError: true, + }; + } + + sessionStore.setActiveProfile(profile); + activeProfile = profile; + if (!profileExists) { + notices.push(`Created and activated profile "${profile}".`); + } else { + notices.push(`Activated profile "${profile}".`); + } + } + + const current = sessionStore.getAll(); + const nextParams = removeUndefined( + rawParams as Record, + ) as Partial; + + const hasProjectPath = + Object.prototype.hasOwnProperty.call(nextParams, 'projectPath') && + nextParams.projectPath !== undefined; + const hasWorkspacePath = + Object.prototype.hasOwnProperty.call(nextParams, 'workspacePath') && + nextParams.workspacePath !== undefined; + const hasSimulatorId = + Object.prototype.hasOwnProperty.call(nextParams, 'simulatorId') && + nextParams.simulatorId !== undefined; + const hasSimulatorName = + Object.prototype.hasOwnProperty.call(nextParams, 'simulatorName') && + nextParams.simulatorName !== undefined; + + if (hasProjectPath && hasWorkspacePath) { + delete nextParams.projectPath; + notices.push( + 'Both projectPath and workspacePath were provided; keeping workspacePath and ignoring projectPath.', + ); + } -export async function sessionSetDefaultsLogic(params: Params): Promise { // Clear mutually exclusive counterparts before merging new defaults const toClear = new Set(); - if (Object.prototype.hasOwnProperty.call(params, 'projectPath')) toClear.add('workspacePath'); - if (Object.prototype.hasOwnProperty.call(params, 'workspacePath')) toClear.add('projectPath'); - if (Object.prototype.hasOwnProperty.call(params, 'simulatorId')) toClear.add('simulatorName'); - if (Object.prototype.hasOwnProperty.call(params, 'simulatorName')) toClear.add('simulatorId'); + if ( + Object.prototype.hasOwnProperty.call(nextParams, 'projectPath') && + nextParams.projectPath !== undefined + ) { + toClear.add('workspacePath'); + if (current.workspacePath !== undefined) { + notices.push('Cleared workspacePath because projectPath was set.'); + } + } + if ( + Object.prototype.hasOwnProperty.call(nextParams, 'workspacePath') && + nextParams.workspacePath !== undefined + ) { + toClear.add('projectPath'); + if (current.projectPath !== undefined) { + notices.push('Cleared projectPath because workspacePath was set.'); + } + } + + const selectorProvided = hasSimulatorId || hasSimulatorName; + const simulatorIdChanged = hasSimulatorId && nextParams.simulatorId !== current.simulatorId; + const simulatorNameChanged = + hasSimulatorName && nextParams.simulatorName !== current.simulatorName; + + if (hasSimulatorId && hasSimulatorName) { + // Both provided - keep both, simulatorId takes precedence for tools + notices.push( + 'Both simulatorId and simulatorName were provided; simulatorId will be used by tools.', + ); + } else if (hasSimulatorId && !hasSimulatorName) { + toClear.add('simulatorName'); + if (current.simulatorName !== undefined) { + notices.push( + 'Cleared simulatorName because simulatorId was set; background resolution will repopulate it.', + ); + } + if (simulatorIdChanged) { + notices.push( + `Set simulatorId to "${nextParams.simulatorId}". Simulator name and platform refresh scheduled in background.`, + ); + } + } else if (hasSimulatorName && !hasSimulatorId) { + toClear.add('simulatorId'); + if (current.simulatorId !== undefined) { + notices.push( + 'Cleared simulatorId because simulatorName was set; background resolution will repopulate it.', + ); + } + if (simulatorNameChanged) { + notices.push( + `Set simulatorName to "${nextParams.simulatorName}". Simulator ID and platform refresh scheduled in background.`, + ); + } + } + + if (selectorProvided) { + const selectorChanged = simulatorIdChanged || simulatorNameChanged; + if (selectorChanged) { + toClear.add('simulatorPlatform'); + notices.push('Cleared simulatorPlatform because simulator selector changed.'); + } + } if (toClear.size > 0) { sessionStore.clear(Array.from(toClear)); } - sessionStore.setDefaults(params as Partial); - const current = sessionStore.getAll(); + if (Object.keys(nextParams).length > 0) { + sessionStore.setDefaults(nextParams as Partial); + } + + if (persist) { + if (Object.keys(nextParams).length === 0 && toClear.size === 0) { + notices.push('No defaults provided to persist.'); + } else { + const { path } = await persistSessionDefaultsPatch({ + patch: nextParams, + deleteKeys: Array.from(toClear), + profile: activeProfile, + }); + notices.push(`Persisted defaults to ${path}`); + } + + if (rawProfile !== undefined) { + const { path } = await persistActiveSessionDefaultsProfile(activeProfile); + notices.push(`Persisted active profile selection to ${path}`); + } + } + + const revision = sessionStore.getRevision(); + if (selectorProvided) { + const defaultsForRefresh = sessionStore.getAll(); + scheduleSimulatorDefaultsRefresh({ + executor: context.executor, + expectedRevision: revision, + reason: 'session-set-defaults', + profile: activeProfile, + persist: Boolean(persist), + simulatorId: defaultsForRefresh.simulatorId, + simulatorName: defaultsForRefresh.simulatorName, + recomputePlatform: true, + }); + } + + const updated = sessionStore.getAll(); + const noticeText = notices.length > 0 ? `\nNotices:\n- ${notices.join('\n- ')}` : ''; return { - content: [{ type: 'text', text: `Defaults updated:\n${JSON.stringify(current, null, 2)}` }], + content: [ + { + type: 'text', + text: `Defaults updated:\n${JSON.stringify(updated, null, 2)}${noticeText}`, + }, + ], isError: false, }; } -export default { - name: 'session-set-defaults', - description: - 'Set the session defaults needed by many tools. Most tools require one or more session defaults to be set before they can be used. Agents should set all relevant defaults up front in a single call (e.g., project/workspace, scheme, simulator or device ID, useLatestOS) to avoid iterative prompts; only set the keys your workflow needs.', - schema: baseSchema.shape, - annotations: { - title: 'Set Session Defaults', - destructiveHint: true, - }, - handler: createTypedTool(schemaObj, sessionSetDefaultsLogic, getDefaultCommandExecutor), -}; +export const schema = schemaObj.shape; + +export const handler = createTypedToolWithContext(schemaObj, sessionSetDefaultsLogic, () => ({ + executor: getDefaultCommandExecutor(), +})); diff --git a/src/mcp/tools/session-management/session_show_defaults.ts b/src/mcp/tools/session-management/session_show_defaults.ts index fc04eff3..03ce99da 100644 --- a/src/mcp/tools/session-management/session_show_defaults.ts +++ b/src/mcp/tools/session-management/session_show_defaults.ts @@ -1,16 +1,9 @@ import { sessionStore } from '../../../utils/session-store.ts'; import type { ToolResponse } from '../../../types/common.ts'; -export default { - name: 'session-show-defaults', - description: 'Show current session defaults.', - schema: {}, - annotations: { - title: 'Show Session Defaults', - readOnlyHint: true, - }, - handler: async (): Promise => { - const current = sessionStore.getAll(); - return { content: [{ type: 'text', text: JSON.stringify(current, null, 2) }], isError: false }; - }, +export const schema = {}; + +export const handler = async (): Promise => { + const current = sessionStore.getAll(); + return { content: [{ type: 'text', text: JSON.stringify(current, null, 2) }], isError: false }; }; diff --git a/src/mcp/tools/session-management/session_use_defaults_profile.ts b/src/mcp/tools/session-management/session_use_defaults_profile.ts new file mode 100644 index 00000000..ab132168 --- /dev/null +++ b/src/mcp/tools/session-management/session_use_defaults_profile.ts @@ -0,0 +1,108 @@ +import * as z from 'zod'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { persistActiveSessionDefaultsProfile } from '../../../utils/config-store.ts'; +import { sessionStore } from '../../../utils/session-store.ts'; +import type { ToolResponse } from '../../../types/common.ts'; + +const schemaObj = z.object({ + profile: z + .string() + .min(1) + .optional() + .describe('Activate a named session defaults profile (example: ios or watch).'), + global: z.boolean().optional().describe('Activate the global unnamed defaults profile.'), + persist: z + .boolean() + .optional() + .describe('Persist activeSessionDefaultsProfile to .xcodebuildmcp/config.yaml.'), +}); + +type Params = z.input; + +function normalizeProfileName(profile: string): string { + return profile.trim(); +} + +function errorResponse(text: string): ToolResponse { + return { + content: [{ type: 'text', text }], + isError: true, + }; +} + +function resolveProfileToActivate(params: Params): string | null | undefined { + if (params.global === true) return null; + if (params.profile === undefined) return undefined; + return normalizeProfileName(params.profile); +} + +function validateProfileActivation( + profileToActivate: string | null | undefined, +): ToolResponse | null { + if (profileToActivate === undefined || profileToActivate === null) { + return null; + } + + if (profileToActivate.length === 0) { + return errorResponse('Profile name cannot be empty.'); + } + + const profileExists = sessionStore.listProfiles().includes(profileToActivate); + if (!profileExists) { + return errorResponse(`Profile "${profileToActivate}" does not exist.`); + } + + return null; +} + +export async function sessionUseDefaultsProfileLogic(params: Params): Promise { + const notices: string[] = []; + + if (params.global === true && params.profile !== undefined) { + return errorResponse('Provide either global=true or profile, not both.'); + } + + const profileToActivate = resolveProfileToActivate(params); + const validationError = validateProfileActivation(profileToActivate); + if (validationError) { + return validationError; + } + + if (profileToActivate !== undefined) { + sessionStore.setActiveProfile(profileToActivate); + } + + const active = sessionStore.getActiveProfile(); + if (params.persist) { + const { path } = await persistActiveSessionDefaultsProfile(active); + notices.push(`Persisted active profile selection to ${path}`); + } + + const activeLabel = active ?? 'global'; + const profiles = sessionStore.listProfiles(); + const current = sessionStore.getAll(); + + return { + content: [ + { + type: 'text', + text: [ + `Active defaults profile: ${activeLabel}`, + `Known profiles: ${profiles.length > 0 ? profiles.join(', ') : '(none)'}`, + `Current defaults: ${JSON.stringify(current, null, 2)}`, + ...(notices.length > 0 ? [`Notices:`, ...notices.map((notice) => `- ${notice}`)] : []), + ].join('\n'), + }, + ], + isError: false, + }; +} + +export const schema = schemaObj.shape; + +export const handler = createTypedTool( + schemaObj, + sessionUseDefaultsProfileLogic, + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/simulator-management/__tests__/erase_sims.test.ts b/src/mcp/tools/simulator-management/__tests__/erase_sims.test.ts index e621e1e7..f830a41b 100644 --- a/src/mcp/tools/simulator-management/__tests__/erase_sims.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/erase_sims.test.ts @@ -1,26 +1,14 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import eraseSims, { erase_simsLogic } from '../erase_sims.ts'; +import { schema, erase_simsLogic } from '../erase_sims.ts'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('erase_sims tool (single simulator)', () => { - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(eraseSims.name).toBe('erase_sims'); - }); - - it('should have correct description', () => { - expect(eraseSims.description).toBe('Erases a simulator by UDID.'); - }); - - it('should have handler function', () => { - expect(typeof eraseSims.handler).toBe('function'); - }); - + describe('Schema Validation', () => { it('should validate schema fields (shape only)', () => { - const schema = z.object(eraseSims.schema); - expect(schema.safeParse({ shutdownFirst: true }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(true); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({ shutdownFirst: true }).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); }); }); diff --git a/src/mcp/tools/simulator-management/__tests__/index.test.ts b/src/mcp/tools/simulator-management/__tests__/index.test.ts deleted file mode 100644 index 5d1da4ec..00000000 --- a/src/mcp/tools/simulator-management/__tests__/index.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Tests for simulator-management workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('simulator-management workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('Simulator Management'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance.', - ); - }); - }); -}); diff --git a/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts b/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts index 20107195..131aeee1 100644 --- a/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts @@ -1,30 +1,16 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import resetSimLocationPlugin, { reset_sim_locationLogic } from '../reset_sim_location.ts'; +import { schema, reset_sim_locationLogic } from '../reset_sim_location.ts'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('reset_sim_location plugin', () => { - describe('Export Field Validation (Literal)', () => { - it('should have correct name field', () => { - expect(resetSimLocationPlugin.name).toBe('reset_sim_location'); - }); - - it('should have correct description field', () => { - expect(resetSimLocationPlugin.description).toBe( - "Resets the simulator's location to default.", - ); - }); - - it('should have handler function', () => { - expect(typeof resetSimLocationPlugin.handler).toBe('function'); - }); - + describe('Schema Validation', () => { it('should hide simulatorId from public schema', () => { - const schema = z.object(resetSimLocationPlugin.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); - const withSimId = schema.safeParse({ simulatorId: 'abc123' }); + const withSimId = schemaObj.safeParse({ simulatorId: 'abc123' }); expect(withSimId.success).toBe(true); expect('simulatorId' in (withSimId.data as any)).toBe(false); }); diff --git a/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts b/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts index 4fbd2805..a5b3a0a5 100644 --- a/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts @@ -1,34 +1,27 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import setSimAppearancePlugin, { set_sim_appearanceLogic } from '../set_sim_appearance.ts'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, set_sim_appearanceLogic } from '../set_sim_appearance.ts'; +import { + createMockCommandResponse, + createMockExecutor, +} from '../../../../test-utils/mock-executors.ts'; describe('set_sim_appearance plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name field', () => { - expect(setSimAppearancePlugin.name).toBe('set_sim_appearance'); - }); - - it('should have correct description field', () => { - expect(setSimAppearancePlugin.description).toBe( - 'Sets the appearance mode (dark/light) of an iOS simulator.', - ); - }); - it('should have handler function', () => { - expect(typeof setSimAppearancePlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose public schema without simulatorId field', () => { - const schema = z.object(setSimAppearancePlugin.schema); + const schemaObject = z.object(schema); - expect(schema.safeParse({ mode: 'dark' }).success).toBe(true); - expect(schema.safeParse({ mode: 'light' }).success).toBe(true); - expect(schema.safeParse({ mode: 'invalid' }).success).toBe(false); + expect(schemaObject.safeParse({ mode: 'dark' }).success).toBe(true); + expect(schemaObject.safeParse({ mode: 'light' }).success).toBe(true); + expect(schemaObject.safeParse({ mode: 'invalid' }).success).toBe(false); - const withSimId = schema.safeParse({ simulatorId: 'abc123', mode: 'dark' }); + const withSimId = schemaObject.safeParse({ simulatorId: 'abc123', mode: 'dark' }); expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as any)).toBe(false); + expect('simulatorId' in (withSimId.data as object)).toBe(false); }); }); @@ -83,7 +76,7 @@ describe('set_sim_appearance plugin', () => { }); it('should surface session default requirement when simulatorId is missing', async () => { - const result = await setSimAppearancePlugin.handler({ mode: 'dark' }); + const result = await handler({ mode: 'dark' }); const message = result.content?.[0]?.text ?? ''; expect(message).toContain('Error: Missing required session defaults'); @@ -116,12 +109,13 @@ describe('set_sim_appearance plugin', () => { const commandCalls: any[] = []; const mockExecutor = (...args: any[]) => { commandCalls.push(args); - return Promise.resolve({ - success: true, - output: '', - error: '', - process: { pid: 12345 }, - }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: '', + error: '', + }), + ); }; await set_sim_appearanceLogic( @@ -136,7 +130,7 @@ describe('set_sim_appearance plugin', () => { [ ['xcrun', 'simctl', 'ui', 'test-uuid-123', 'appearance', 'dark'], 'Set Simulator Appearance', - true, + false, undefined, ], ]); diff --git a/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts b/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts index 4179565f..bdffd902 100644 --- a/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts @@ -1,44 +1,38 @@ /** - * Tests for set_sim_location plugin + * Tests for set_sim_location tool * Following CLAUDE.md testing standards with literal validation * Using pure dependency injection for deterministic testing */ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; -import setSimLocation, { set_sim_locationLogic } from '../set_sim_location.ts'; +import { + createMockCommandResponse, + createMockExecutor, + createNoopExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, set_sim_locationLogic } from '../set_sim_location.ts'; describe('set_sim_location tool', () => { - // No mocks to clear since we use pure dependency injection - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(setSimLocation.name).toBe('set_sim_location'); - }); - - it('should have correct description', () => { - expect(setSimLocation.description).toBe('Sets a custom GPS location for the simulator.'); - }); - it('should have handler function', () => { - expect(typeof setSimLocation.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose public schema without simulatorId field', () => { - const schema = z.object(setSimLocation.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ latitude: 37.7749, longitude: -122.4194 }).success).toBe(true); - expect(schema.safeParse({ latitude: 0, longitude: 0 }).success).toBe(true); - expect(schema.safeParse({ latitude: 37.7749 }).success).toBe(false); - expect(schema.safeParse({ longitude: -122.4194 }).success).toBe(false); - const withSimId = schema.safeParse({ + expect(schemaObj.safeParse({ latitude: 37.7749, longitude: -122.4194 }).success).toBe(true); + expect(schemaObj.safeParse({ latitude: 0, longitude: 0 }).success).toBe(true); + expect(schemaObj.safeParse({ latitude: 37.7749 }).success).toBe(false); + expect(schemaObj.safeParse({ longitude: -122.4194 }).success).toBe(false); + const withSimId = schemaObj.safeParse({ simulatorId: 'test-uuid-123', latitude: 37.7749, longitude: -122.4194, }); expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as any)).toBe(false); + expect('simulatorId' in (withSimId.data as Record)).toBe(false); }); }); @@ -48,12 +42,11 @@ describe('set_sim_location tool', () => { const mockExecutor = async (command: string[]) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'Location set successfully', error: undefined, - process: { pid: 12345 }, - }; + }); }; await set_sim_locationLogic( @@ -80,12 +73,11 @@ describe('set_sim_location tool', () => { const mockExecutor = async (command: string[]) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'Location set successfully', error: undefined, - process: { pid: 12345 }, - }; + }); }; await set_sim_locationLogic( @@ -112,12 +104,11 @@ describe('set_sim_location tool', () => { const mockExecutor = async (command: string[]) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'Location set successfully', error: undefined, - process: { pid: 12345 }, - }; + }); }; await set_sim_locationLogic( @@ -360,12 +351,11 @@ describe('set_sim_location tool', () => { const mockExecutor = async (...args: any[]) => { capturedArgs = args; - return { + return createMockCommandResponse({ success: true, output: 'Location set successfully', error: undefined, - process: { pid: 12345 }, - }; + }); }; await set_sim_locationLogic( @@ -380,7 +370,7 @@ describe('set_sim_location tool', () => { expect(capturedArgs).toEqual([ ['xcrun', 'simctl', 'location', 'test-uuid-123', 'set', '37.7749,-122.4194'], 'Set Simulator Location', - true, + false, {}, ]); }); diff --git a/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts b/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts index b4795c7e..89d576e5 100644 --- a/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts @@ -6,35 +6,25 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts'; -import simStatusbar, { sim_statusbarLogic } from '../sim_statusbar.ts'; +import { + createMockCommandResponse, + createMockExecutor, + type CommandExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, sim_statusbarLogic } from '../sim_statusbar.ts'; describe('sim_statusbar tool', () => { - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(simStatusbar.name).toBe('sim_statusbar'); - }); - - it('should have correct description', () => { - expect(simStatusbar.description).toBe( - 'Sets the data network indicator in the iOS simulator status bar. Use "clear" to reset all overrides, or specify a network type (hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc).', - ); - }); - - it('should have handler function', () => { - expect(typeof simStatusbar.handler).toBe('function'); - }); - + describe('Schema Validation', () => { it('should expose public schema without simulatorId field', () => { - const schema = z.object(simStatusbar.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ dataNetwork: 'wifi' }).success).toBe(true); - expect(schema.safeParse({ dataNetwork: 'clear' }).success).toBe(true); - expect(schema.safeParse({ dataNetwork: 'invalid' }).success).toBe(false); + expect(schemaObj.safeParse({ dataNetwork: 'wifi' }).success).toBe(true); + expect(schemaObj.safeParse({ dataNetwork: 'clear' }).success).toBe(true); + expect(schemaObj.safeParse({ dataNetwork: 'invalid' }).success).toBe(false); - const withSimId = schema.safeParse({ simulatorId: 'test-uuid', dataNetwork: 'wifi' }); + const withSimId = schemaObj.safeParse({ simulatorId: 'test-uuid', dataNetwork: 'wifi' }); expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as any)).toBe(false); + expect('simulatorId' in (withSimId.data as object)).toBe(false); }); }); @@ -161,24 +151,25 @@ describe('sim_statusbar tool', () => { it('should verify command generation with mock executor for override', async () => { const calls: Array<{ command: string[]; - operationDescription: string; - keepAlive: boolean; - timeout: number | undefined; + operationDescription?: string; + keepAlive?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor: CommandExecutor = async ( command, operationDescription, keepAlive, - timeout, + opts, + detached, ) => { - calls.push({ command, operationDescription, keepAlive, timeout }); - return { + calls.push({ command, operationDescription, keepAlive, opts }); + void detached; + return createMockCommandResponse({ success: true, output: 'Status bar set successfully', error: undefined, - process: { pid: 12345 }, - }; + }); }; await sim_statusbarLogic( @@ -201,32 +192,33 @@ describe('sim_statusbar tool', () => { 'wifi', ], operationDescription: 'Set Status Bar', - keepAlive: true, - timeout: undefined, + keepAlive: false, + opts: undefined, }); }); it('should verify command generation for clear operation', async () => { const calls: Array<{ command: string[]; - operationDescription: string; - keepAlive: boolean; - timeout: number | undefined; + operationDescription?: string; + keepAlive?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor: CommandExecutor = async ( command, operationDescription, keepAlive, - timeout, + opts, + detached, ) => { - calls.push({ command, operationDescription, keepAlive, timeout }); - return { + calls.push({ command, operationDescription, keepAlive, opts }); + void detached; + return createMockCommandResponse({ success: true, output: 'Status bar cleared successfully', error: undefined, - process: { pid: 12345 }, - }; + }); }; await sim_statusbarLogic( @@ -241,8 +233,8 @@ describe('sim_statusbar tool', () => { expect(calls[0]).toEqual({ command: ['xcrun', 'simctl', 'status_bar', 'test-uuid-123', 'clear'], operationDescription: 'Set Status Bar', - keepAlive: true, - timeout: undefined, + keepAlive: false, + opts: undefined, }); }); diff --git a/src/mcp/tools/simulator-management/boot_sim.ts b/src/mcp/tools/simulator-management/boot_sim.ts deleted file mode 100644 index 174a6c68..00000000 --- a/src/mcp/tools/simulator-management/boot_sim.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from simulator to avoid duplication -export { default } from '../simulator/boot_sim.ts'; diff --git a/src/mcp/tools/simulator-management/erase_sims.ts b/src/mcp/tools/simulator-management/erase_sims.ts index 3ee50c05..2fa20467 100644 --- a/src/mcp/tools/simulator-management/erase_sims.ts +++ b/src/mcp/tools/simulator-management/erase_sims.ts @@ -1,7 +1,8 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -10,10 +11,7 @@ import { const eraseSimsBaseSchema = z .object({ simulatorId: z.uuid().describe('UDID of the simulator to erase.'), - shutdownFirst: z - .boolean() - .optional() - .describe('If true, shuts down the simulator before erasing.'), + shutdownFirst: z.boolean().optional(), }) .passthrough(); @@ -83,21 +81,14 @@ export async function erase_simsLogic( const publicSchemaObject = eraseSimsSchema.omit({ simulatorId: true } as const).passthrough(); -export default { - name: 'erase_sims', - description: 'Erases a simulator by UDID.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: eraseSimsSchema, - }), - annotations: { - title: 'Erase Simulators', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: eraseSimsSchema as unknown as z.ZodType, - logicFunction: erase_simsLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: eraseSimsSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: eraseSimsSchema as unknown as z.ZodType, + logicFunction: erase_simsLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); diff --git a/src/mcp/tools/simulator-management/index.ts b/src/mcp/tools/simulator-management/index.ts deleted file mode 100644 index 66e8dbb5..00000000 --- a/src/mcp/tools/simulator-management/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Simulator Management workflow - * - * Provides tools for working with simulators like booting and opening simulators, launching apps, - * listing sims, stopping apps, erasing simulator content and settings, and setting sim environment - * options like location, network, statusbar and appearance. - */ - -export const workflow = { - name: 'Simulator Management', - description: - 'Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance.', -}; diff --git a/src/mcp/tools/simulator-management/list_sims.ts b/src/mcp/tools/simulator-management/list_sims.ts deleted file mode 100644 index 3c5a2ff0..00000000 --- a/src/mcp/tools/simulator-management/list_sims.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from simulator to avoid duplication -export { default } from '../simulator/list_sims.ts'; diff --git a/src/mcp/tools/simulator-management/open_sim.ts b/src/mcp/tools/simulator-management/open_sim.ts deleted file mode 100644 index 43a8857f..00000000 --- a/src/mcp/tools/simulator-management/open_sim.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from simulator to avoid duplication -export { default } from '../simulator/open_sim.ts'; diff --git a/src/mcp/tools/simulator-management/reset_sim_location.ts b/src/mcp/tools/simulator-management/reset_sim_location.ts index f9aef57b..fc7a15c4 100644 --- a/src/mcp/tools/simulator-management/reset_sim_location.ts +++ b/src/mcp/tools/simulator-management/reset_sim_location.ts @@ -1,7 +1,8 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -35,7 +36,7 @@ async function executeSimctlCommandAndRespond( try { const command = ['xcrun', 'simctl', ...simctlSubCommand]; - const result = await executor(command, operationDescriptionForXcodeCommand, true, {}); + const result = await executor(command, operationDescriptionForXcodeCommand, false, {}); if (!result.success) { const fullFailureMessage = `${failureMessagePrefix}: ${result.error}`; @@ -89,24 +90,17 @@ const publicSchemaObject = z.strictObject( resetSimulatorLocationSchema.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'reset_sim_location', - description: "Resets the simulator's location to default.", - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: resetSimulatorLocationSchema, - }), - annotations: { - title: 'Reset Simulator Location', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: resetSimulatorLocationSchema as unknown as z.ZodType< - ResetSimulatorLocationParams, - unknown - >, - logicFunction: reset_sim_locationLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: resetSimulatorLocationSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: resetSimulatorLocationSchema as unknown as z.ZodType< + ResetSimulatorLocationParams, + unknown + >, + logicFunction: reset_sim_locationLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); diff --git a/src/mcp/tools/simulator-management/set_sim_appearance.ts b/src/mcp/tools/simulator-management/set_sim_appearance.ts index 94dec95d..cb272b30 100644 --- a/src/mcp/tools/simulator-management/set_sim_appearance.ts +++ b/src/mcp/tools/simulator-management/set_sim_appearance.ts @@ -1,7 +1,8 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -10,7 +11,7 @@ import { // Define schema as ZodObject const setSimAppearanceSchema = z.object({ simulatorId: z.uuid().describe('UUID of the simulator to use (obtained from list_simulators)'), - mode: z.enum(['dark', 'light']).describe('The appearance mode to set (either "dark" or "light")'), + mode: z.enum(['dark', 'light']).describe('dark|light'), }); // Use z.infer for type safety @@ -36,7 +37,7 @@ async function executeSimctlCommandAndRespond( try { const command = ['xcrun', 'simctl', ...simctlSubCommand]; - const result = await executor(command, operationDescriptionForXcodeCommand, true, undefined); + const result = await executor(command, operationDescriptionForXcodeCommand, false, undefined); if (!result.success) { const fullFailureMessage = `${failureMessagePrefix}: ${result.error}`; @@ -91,21 +92,14 @@ const publicSchemaObject = z.strictObject( setSimAppearanceSchema.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'set_sim_appearance', - description: 'Sets the appearance mode (dark/light) of an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: setSimAppearanceSchema, - }), - annotations: { - title: 'Set Simulator Appearance', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: setSimAppearanceSchema as unknown as z.ZodType, - logicFunction: set_sim_appearanceLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: setSimAppearanceSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: setSimAppearanceSchema as unknown as z.ZodType, + logicFunction: set_sim_appearanceLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); diff --git a/src/mcp/tools/simulator-management/set_sim_location.ts b/src/mcp/tools/simulator-management/set_sim_location.ts index d4208cda..f2fc0a99 100644 --- a/src/mcp/tools/simulator-management/set_sim_location.ts +++ b/src/mcp/tools/simulator-management/set_sim_location.ts @@ -1,7 +1,8 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -10,8 +11,8 @@ import { // Define schema as ZodObject const setSimulatorLocationSchema = z.object({ simulatorId: z.uuid().describe('UUID of the simulator to use (obtained from list_simulators)'), - latitude: z.number().describe('The latitude for the custom location.'), - longitude: z.number().describe('The longitude for the custom location.'), + latitude: z.number(), + longitude: z.number(), }); // Use z.infer for type safety @@ -37,7 +38,7 @@ async function executeSimctlCommandAndRespond( try { const command = ['xcrun', 'simctl', ...simctlSubCommand]; - const result = await executor(command, operationDescriptionForXcodeCommand, true, {}); + const result = await executor(command, operationDescriptionForXcodeCommand, false, {}); if (!result.success) { const fullFailureMessage = `${failureMessagePrefix}: ${result.error}`; @@ -119,24 +120,17 @@ const publicSchemaObject = z.strictObject( setSimulatorLocationSchema.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'set_sim_location', - description: 'Sets a custom GPS location for the simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: setSimulatorLocationSchema, - }), - annotations: { - title: 'Set Simulator Location', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: setSimulatorLocationSchema as unknown as z.ZodType< - SetSimulatorLocationParams, - unknown - >, - logicFunction: set_sim_locationLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: setSimulatorLocationSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: setSimulatorLocationSchema as unknown as z.ZodType< + SetSimulatorLocationParams, + unknown + >, + logicFunction: set_sim_locationLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); diff --git a/src/mcp/tools/simulator-management/sim_statusbar.ts b/src/mcp/tools/simulator-management/sim_statusbar.ts index 1dca37be..6ee5390f 100644 --- a/src/mcp/tools/simulator-management/sim_statusbar.ts +++ b/src/mcp/tools/simulator-management/sim_statusbar.ts @@ -1,7 +1,8 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -25,9 +26,7 @@ const simStatusbarSchema = z.object({ '5g-uwb', '5g-uc', ]) - .describe( - 'Data network type to display in status bar. Use "clear" to reset all overrides. Valid values: clear, hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc.', - ), + .describe('clear|hide|wifi|3g|4g|lte|lte-a|lte+|5g|5g+|5g-uwb|5g-uc'), }); // Use z.infer for type safety @@ -62,7 +61,7 @@ export async function sim_statusbarLogic( successMessage = `Successfully set simulator ${params.simulatorId} status bar data network to ${params.dataNetwork}`; } - const result = await executor(command, 'Set Status Bar', true, undefined); + const result = await executor(command, 'Set Status Bar', false, undefined); if (!result.success) { const failureMessage = `Failed to set status bar: ${result.error}`; @@ -92,22 +91,14 @@ const publicSchemaObject = z.strictObject( simStatusbarSchema.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'sim_statusbar', - description: - 'Sets the data network indicator in the iOS simulator status bar. Use "clear" to reset all overrides, or specify a network type (hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc).', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: simStatusbarSchema, - }), // MCP SDK compatibility - annotations: { - title: 'Simulator Statusbar', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: simStatusbarSchema as unknown as z.ZodType, - logicFunction: sim_statusbarLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: simStatusbarSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: simStatusbarSchema as unknown as z.ZodType, + logicFunction: sim_statusbarLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); diff --git a/src/mcp/tools/simulator/__tests__/boot_sim.test.ts b/src/mcp/tools/simulator/__tests__/boot_sim.test.ts index 939eccec..1ad047db 100644 --- a/src/mcp/tools/simulator/__tests__/boot_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/boot_sim.test.ts @@ -5,9 +5,12 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + createMockCommandResponse, + createMockExecutor, +} from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import bootSim, { boot_simLogic } from '../boot_sim.ts'; +import { schema, handler, boot_simLogic } from '../boot_sim.ts'; describe('boot_sim tool', () => { beforeEach(() => { @@ -15,20 +18,12 @@ describe('boot_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(bootSim.name).toBe('boot_sim'); - }); - - it('should have concise description', () => { - expect(bootSim.description).toBe('Boots an iOS simulator.'); - }); - it('should expose empty public schema', () => { - const schema = z.object(bootSim.schema); - expect(schema.safeParse({}).success).toBe(true); - expect(Object.keys(bootSim.schema)).toHaveLength(0); + const schemaObj = z.object(schema); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(Object.keys(schema)).toHaveLength(0); - const withSimId = schema.safeParse({ simulatorId: 'abc' }); + const withSimId = schemaObj.safeParse({ simulatorId: 'abc' }); expect(withSimId.success).toBe(true); expect('simulatorId' in (withSimId.data as Record)).toBe(false); }); @@ -36,12 +31,12 @@ describe('boot_sim tool', () => { describe('Handler Requirements', () => { it('should require simulatorId when not provided', async () => { - const result = await bootSim.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); const message = result.content[0].text; expect(message).toContain('Missing required session defaults'); - expect(message).toContain('simulatorId is required'); + expect(message).toContain('Provide simulatorId or simulatorName'); expect(message).toContain('session-set-defaults'); }); }); @@ -59,9 +54,14 @@ describe('boot_sim tool', () => { content: [ { type: 'text', - text: `✅ Simulator booted successfully. To make it visible, use: open_sim()\n\nNext steps:\n1. Open the Simulator app (makes it visible): open_sim()\n2. Install an app: install_app_sim({ simulatorId: "test-uuid-123", appPath: "PATH_TO_YOUR_APP" })\n3. Launch an app: launch_app_sim({ simulatorId: "test-uuid-123", bundleId: "YOUR_APP_BUNDLE_ID" })`, + text: 'Simulator booted successfully.', }, ], + nextStepParams: { + open_sim: {}, + install_app_sim: { simulatorId: 'test-uuid-123', appPath: 'PATH_TO_YOUR_APP' }, + launch_app_sim: { simulatorId: 'test-uuid-123', bundleId: 'YOUR_APP_BUNDLE_ID' }, + }, }); }); @@ -120,23 +120,24 @@ describe('boot_sim tool', () => { it('should verify command generation with mock executor', async () => { const calls: Array<{ command: string[]; - description: string; - allowStderr: boolean; - timeout?: number; + description?: string; + allowStderr?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor = async ( command: string[], - description: string, - allowStderr: boolean, - timeout?: number, + description?: string, + allowStderr?: boolean, + opts?: { cwd?: string }, + detached?: boolean, ) => { - calls.push({ command, description, allowStderr, timeout }); - return { + calls.push({ command, description, allowStderr, opts }); + void detached; + return createMockCommandResponse({ success: true, output: 'Simulator booted successfully', error: undefined, - process: { pid: 12345 }, - }; + }); }; await boot_simLogic({ simulatorId: 'test-uuid-123' }, mockExecutor); @@ -145,8 +146,8 @@ describe('boot_sim tool', () => { expect(calls[0]).toEqual({ command: ['xcrun', 'simctl', 'boot', 'test-uuid-123'], description: 'Boot Simulator', - allowStderr: true, - timeout: undefined, + allowStderr: false, + opts: undefined, }); }); }); diff --git a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts index 30f37000..98901872 100644 --- a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts @@ -5,9 +5,13 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + createMockExecutor, + createMockCommandResponse, +} from '../../../../test-utils/mock-executors.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import buildRunSim, { build_run_simLogic } from '../build_run_sim.ts'; +import { schema, handler, build_run_simLogic } from '../build_run_sim.ts'; describe('build_run_sim tool', () => { beforeEach(() => { @@ -15,37 +19,27 @@ describe('build_run_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(buildRunSim.name).toBe('build_run_sim'); - }); - - it('should have correct description', () => { - expect(buildRunSim.description).toBe('Builds and runs an app on an iOS simulator.'); - }); - it('should have handler function', () => { - expect(typeof buildRunSim.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose only non-session fields in public schema', () => { - const schema = z.object(buildRunSim.schema); + const schemaObj = z.strictObject(schema); - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); expect( - schema.safeParse({ - derivedDataPath: '/path/to/derived', + schemaObj.safeParse({ extraArgs: ['--verbose'], - preferXcodebuild: false, }).success, ).toBe(true); - expect(schema.safeParse({ derivedDataPath: 123 }).success).toBe(false); - expect(schema.safeParse({ extraArgs: [123] }).success).toBe(false); - expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); + expect(schemaObj.safeParse({ derivedDataPath: '/path/to/derived' }).success).toBe(false); + expect(schemaObj.safeParse({ extraArgs: [123] }).success).toBe(false); + expect(schemaObj.safeParse({ preferXcodebuild: false }).success).toBe(false); - const schemaKeys = Object.keys(buildRunSim.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs']); expect(schemaKeys).not.toContain('scheme'); expect(schemaKeys).not.toContain('simulatorName'); expect(schemaKeys).not.toContain('projectPath'); @@ -58,28 +52,37 @@ describe('build_run_sim tool', () => { it('should handle simulator not found', async () => { let callCount = 0; - const mockExecutor = async (command: string[]) => { + const mockExecutor: CommandExecutor = async (command) => { callCount++; if (callCount === 1) { - // First call: build succeeds - return { + // First call: runtime lookup succeeds + return createMockCommandResponse({ success: true, - output: 'BUILD SUCCEEDED', - process: { pid: 12345 }, - }; + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { udid: 'SIM-UUID', name: 'iPhone 16', isAvailable: true }, + ], + }, + }), + }); } else if (callCount === 2) { - // Second call: showBuildSettings fails to get app path - return { + // Second call: build succeeds + return createMockCommandResponse({ + success: true, + output: 'BUILD SUCCEEDED', + }); + } else if (callCount === 3) { + // Third call: showBuildSettings fails to get app path + return createMockCommandResponse({ success: false, error: 'Could not get build settings', - process: { pid: 12345 }, - }; + }); } - return { + return createMockCommandResponse({ success: false, error: 'Unexpected call', - process: { pid: 12345 }, - }; + }); }; const result = await build_run_simLogic( @@ -125,26 +128,24 @@ describe('build_run_sim tool', () => { it('should handle successful build and run', async () => { // Create a mock executor that simulates full successful flow let callCount = 0; - const mockExecutor = async (command: string[], logPrefix?: string) => { + const mockExecutor: CommandExecutor = async (command) => { callCount++; if (command.includes('xcodebuild') && command.includes('build')) { // First call: build succeeds - return { + return createMockCommandResponse({ success: true, output: 'BUILD SUCCEEDED', - process: { pid: 12345 }, - }; + }); } else if (command.includes('xcodebuild') && command.includes('-showBuildSettings')) { // Second call: build settings to get app path - return { + return createMockCommandResponse({ success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app\n', - process: { pid: 12345 }, - }; + }); } else if (command.includes('simctl') && command.includes('list')) { // Find simulator calls - return { + return createMockCommandResponse({ success: true, output: JSON.stringify({ devices: { @@ -158,26 +159,23 @@ describe('build_run_sim tool', () => { ], }, }), - process: { pid: 12345 }, - }; + }); } else if ( command.includes('plutil') || command.includes('PlistBuddy') || command.includes('defaults') ) { // Bundle ID extraction - return { + return createMockCommandResponse({ success: true, - output: 'com.example.MyApp', - process: { pid: 12345 }, - }; + output: 'io.sentry.MyApp', + }); } else { // All other commands (boot, open, install, launch) succeed - return { + return createMockCommandResponse({ success: true, output: 'Success', - process: { pid: 12345 }, - }; + }); } }; @@ -237,42 +235,34 @@ describe('build_run_sim tool', () => { }); describe('Command Generation', () => { - it('should generate correct simctl list command with minimal parameters', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; - - // Create tracking executor - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return { + const SIMCTL_LIST_COMMAND = ['xcrun', 'simctl', 'list', 'devices', 'available', '--json']; + + function createTrackingExecutor(callHistory: Array<{ command: string[]; logPrefix?: string }>) { + return async (command: string[], logPrefix?: string) => { + callHistory.push({ command, logPrefix }); + return createMockCommandResponse({ success: false, output: '', error: 'Test error to stop execution early', - process: { pid: 12345 }, - }; + }); }; + } - const result = await build_run_simLogic( + it('should generate correct simctl list command with minimal parameters', async () => { + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; + + await build_run_simLogic( { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', simulatorName: 'iPhone 16', }, - trackingExecutor, + createTrackingExecutor(callHistory), ); - // Should generate the initial build command - expect(callHistory).toHaveLength(1); - expect(callHistory[0].command).toEqual([ + expect(callHistory).toHaveLength(2); + expect(callHistory[0].command).toEqual(SIMCTL_LIST_COMMAND); + expect(callHistory[1].command).toEqual([ 'xcodebuild', '-workspace', '/path/to/MyProject.xcworkspace', @@ -285,58 +275,38 @@ describe('build_run_sim tool', () => { 'platform=iOS Simulator,name=iPhone 16,OS=latest', 'build', ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); + expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); }); it('should generate correct build command after finding simulator', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; let callCount = 0; - // Create tracking executor that succeeds on first call (list) and fails on second - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); + const trackingExecutor: CommandExecutor = async (command, logPrefix) => { + callHistory.push({ command, logPrefix }); callCount++; if (callCount === 1) { - // First call: simulator list succeeds - return { + return createMockCommandResponse({ success: true, output: JSON.stringify({ devices: { - 'iOS 16.0': [ - { - udid: 'test-uuid-123', - name: 'iPhone 16', - state: 'Booted', - }, + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { udid: 'test-uuid-123', name: 'iPhone 16', isAvailable: true }, ], }, }), - error: undefined, - process: { pid: 12345 }, - }; - } else { - // Second call: build command fails to stop execution - return { - success: false, - output: '', - error: 'Test error to stop execution', - process: { pid: 12345 }, - }; + }); } + + return createMockCommandResponse({ + success: false, + output: '', + error: 'Test error to stop execution', + }); }; - const result = await build_run_simLogic( + await build_run_simLogic( { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', @@ -345,11 +315,9 @@ describe('build_run_sim tool', () => { trackingExecutor, ); - // Should generate build command and then build settings command expect(callHistory).toHaveLength(2); - - // First call: build command - expect(callHistory[0].command).toEqual([ + expect(callHistory[0].command).toEqual(SIMCTL_LIST_COMMAND); + expect(callHistory[1].command).toEqual([ 'xcodebuild', '-workspace', '/path/to/MyProject.xcworkspace', @@ -362,81 +330,44 @@ describe('build_run_sim tool', () => { 'platform=iOS Simulator,name=iPhone 16,OS=latest', 'build', ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); - - // Second call: build settings command to get app path - expect(callHistory[1].command).toEqual([ - 'xcodebuild', - '-showBuildSettings', - '-workspace', - '/path/to/MyProject.xcworkspace', - '-scheme', - 'MyScheme', - '-configuration', - 'Debug', - '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', - ]); - expect(callHistory[1].logPrefix).toBe('Get App Path'); + expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); }); it('should generate correct build settings command after successful build', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; let callCount = 0; - // Create tracking executor that succeeds on first two calls and fails on third - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); + const trackingExecutor: CommandExecutor = async (command, logPrefix) => { + callHistory.push({ command, logPrefix }); callCount++; if (callCount === 1) { - // First call: simulator list succeeds - return { + return createMockCommandResponse({ success: true, output: JSON.stringify({ devices: { - 'iOS 16.0': [ - { - udid: 'test-uuid-123', - name: 'iPhone 16', - state: 'Booted', - }, + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { udid: 'test-uuid-123', name: 'iPhone 16', isAvailable: true }, ], }, }), - error: undefined, - process: { pid: 12345 }, - }; - } else if (callCount === 2) { - // Second call: build command succeeds - return { + }); + } + if (callCount === 2) { + return createMockCommandResponse({ success: true, output: 'BUILD SUCCEEDED', - error: undefined, - process: { pid: 12345 }, - }; - } else { - // Third call: build settings command fails to stop execution - return { - success: false, - output: '', - error: 'Test error to stop execution', - process: { pid: 12345 }, - }; + }); } + + return createMockCommandResponse({ + success: false, + output: '', + error: 'Test error to stop execution', + }); }; - const result = await build_run_simLogic( + await build_run_simLogic( { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', @@ -447,11 +378,9 @@ describe('build_run_sim tool', () => { trackingExecutor, ); - // Should generate build command and build settings command - expect(callHistory).toHaveLength(2); - - // First call: build command - expect(callHistory[0].command).toEqual([ + expect(callHistory).toHaveLength(3); + expect(callHistory[0].command).toEqual(SIMCTL_LIST_COMMAND); + expect(callHistory[1].command).toEqual([ 'xcodebuild', '-workspace', '/path/to/MyProject.xcworkspace', @@ -464,10 +393,8 @@ describe('build_run_sim tool', () => { 'platform=iOS Simulator,name=iPhone 16', 'build', ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); - - // Second call: build settings command - expect(callHistory[1].command).toEqual([ + expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); + expect(callHistory[2].command).toEqual([ 'xcodebuild', '-showBuildSettings', '-workspace', @@ -479,45 +406,24 @@ describe('build_run_sim tool', () => { '-destination', 'platform=iOS Simulator,name=iPhone 16', ]); - expect(callHistory[1].logPrefix).toBe('Get App Path'); + expect(callHistory[2].logPrefix).toBe('Get App Path'); }); it('should handle paths with spaces in command generation', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; - - // Create tracking executor - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return { - success: false, - output: '', - error: 'Test error to stop execution early', - process: { pid: 12345 }, - }; - }; + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; - const result = await build_run_simLogic( + await build_run_simLogic( { workspacePath: '/Users/dev/My Project/MyProject.xcworkspace', scheme: 'My Scheme', simulatorName: 'iPhone 16 Pro', }, - trackingExecutor, + createTrackingExecutor(callHistory), ); - // Should generate build command first - expect(callHistory).toHaveLength(1); - expect(callHistory[0].command).toEqual([ + expect(callHistory).toHaveLength(2); + expect(callHistory[0].command).toEqual(SIMCTL_LIST_COMMAND); + expect(callHistory[1].command).toEqual([ 'xcodebuild', '-workspace', '/Users/dev/My Project/MyProject.xcworkspace', @@ -530,13 +436,43 @@ describe('build_run_sim tool', () => { 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest', 'build', ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); + expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); + }); + + it('should infer tvOS platform from simulator name for build command', async () => { + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; + + await build_run_simLogic( + { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyTVScheme', + simulatorName: 'Apple TV 4K', + }, + createTrackingExecutor(callHistory), + ); + + expect(callHistory).toHaveLength(2); + expect(callHistory[0].command).toEqual(SIMCTL_LIST_COMMAND); + expect(callHistory[1].command).toEqual([ + 'xcodebuild', + '-workspace', + '/path/to/MyProject.xcworkspace', + '-scheme', + 'MyTVScheme', + '-configuration', + 'Debug', + '-skipMacroValidation', + '-destination', + 'platform=tvOS Simulator,name=Apple TV 4K,OS=latest', + 'build', + ]); + expect(callHistory[1].logPrefix).toBe('tvOS Simulator Build'); }); }); describe('XOR Validation', () => { it('should error when neither projectPath nor workspacePath provided', async () => { - const result = await buildRunSim.handler({ + const result = await handler({ scheme: 'MyScheme', simulatorName: 'iPhone 16', }); @@ -546,7 +482,7 @@ describe('build_run_sim tool', () => { }); it('should error when both projectPath and workspacePath provided', async () => { - const result = await buildRunSim.handler({ + const result = await handler({ projectPath: '/path/project.xcodeproj', workspacePath: '/path/workspace.xcworkspace', scheme: 'MyScheme', diff --git a/src/mcp/tools/simulator/__tests__/build_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_sim.test.ts index 24b6f32e..ad9b05d0 100644 --- a/src/mcp/tools/simulator/__tests__/build_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_sim.test.ts @@ -1,10 +1,14 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + createMockExecutor, + createMockCommandResponse, +} from '../../../../test-utils/mock-executors.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -// Import the plugin and logic function -import buildSim, { build_simLogic } from '../build_sim.ts'; +// Import the named exports and logic function +import { schema, handler, build_simLogic } from '../build_sim.ts'; describe('build_sim tool', () => { beforeEach(() => { @@ -12,43 +16,33 @@ describe('build_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(buildSim.name).toBe('build_sim'); - }); - - it('should have correct description', () => { - expect(buildSim.description).toBe('Builds an app for an iOS simulator.'); - }); - it('should have handler function', () => { - expect(typeof buildSim.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have correct public schema (only non-session fields)', () => { - const schema = z.object(buildSim.schema); + const schemaObj = z.strictObject(schema); // Public schema should allow empty input - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); // Valid public inputs expect( - schema.safeParse({ - derivedDataPath: '/path/to/derived', + schemaObj.safeParse({ extraArgs: ['--verbose'], - preferXcodebuild: false, }).success, ).toBe(true); - // Invalid types on public inputs - expect(schema.safeParse({ derivedDataPath: 123 }).success).toBe(false); - expect(schema.safeParse({ extraArgs: [123] }).success).toBe(false); - expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); + // Invalid types or unknown fields on public inputs + expect(schemaObj.safeParse({ derivedDataPath: '/path/to/derived' }).success).toBe(false); + expect(schemaObj.safeParse({ extraArgs: [123] }).success).toBe(false); + expect(schemaObj.safeParse({ preferXcodebuild: false }).success).toBe(false); }); }); describe('Parameter Validation', () => { it('should handle missing both projectPath and workspacePath', async () => { - const result = await buildSim.handler({ + const result = await handler({ scheme: 'MyScheme', simulatorName: 'iPhone 16', }); @@ -59,7 +53,7 @@ describe('build_sim tool', () => { }); it('should handle both projectPath and workspacePath provided', async () => { - const result = await buildSim.handler({ + const result = await handler({ projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace', scheme: 'MyScheme', @@ -99,7 +93,7 @@ describe('build_sim tool', () => { }); it('should handle missing scheme parameter', async () => { - const result = await buildSim.handler({ + const result = await handler({ workspacePath: '/path/to/workspace', simulatorName: 'iPhone 16', }); @@ -135,7 +129,7 @@ describe('build_sim tool', () => { }); it('should handle missing both simulatorId and simulatorName', async () => { - const result = await buildSim.handler({ + const result = await handler({ workspacePath: '/path/to/workspace', scheme: 'MyScheme', }); @@ -149,7 +143,7 @@ describe('build_sim tool', () => { const mockExecutor = createMockExecutor({ success: true, output: 'Build succeeded' }); // Should fail with XOR validation - const result = await buildSim.handler({ + const result = await handler({ workspacePath: '/path/to/workspace', scheme: 'MyScheme', simulatorId: 'ABC-123', @@ -188,133 +182,96 @@ describe('build_sim tool', () => { }); describe('Command Generation', () => { - it('should generate correct build command with minimal parameters (workspace)', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; - - // Create tracking executor - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return { + const SIMCTL_LIST_COMMAND = ['xcrun', 'simctl', 'list', 'devices', 'available', '--json']; + + function createTrackingExecutor(callHistory: Array<{ command: string[]; logPrefix?: string }>) { + return async (command: string[], logPrefix?: string) => { + callHistory.push({ command, logPrefix }); + return createMockCommandResponse({ success: false, output: '', error: 'Test error to stop execution early', - process: { pid: 12345 }, - }; + }); }; + } + + function expectRuntimeLookupThenBuild( + callHistory: Array<{ command: string[]; logPrefix?: string }>, + expectedBuildCommand: string[], + expectedLogPrefix: string, + ) { + expect(callHistory).toHaveLength(2); + expect(callHistory[0].command).toEqual(SIMCTL_LIST_COMMAND); + expect(callHistory[1].command).toEqual(expectedBuildCommand); + expect(callHistory[1].logPrefix).toBe(expectedLogPrefix); + } - const result = await build_simLogic( + it('should generate correct build command with minimal parameters (workspace)', async () => { + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; + + await build_simLogic( { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', simulatorName: 'iPhone 16', }, - trackingExecutor, + createTrackingExecutor(callHistory), ); - // Should generate one build command - expect(callHistory).toHaveLength(1); - expect(callHistory[0].command).toEqual([ - 'xcodebuild', - '-workspace', - '/path/to/MyProject.xcworkspace', - '-scheme', - 'MyScheme', - '-configuration', - 'Debug', - '-skipMacroValidation', - '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', - 'build', - ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); + expectRuntimeLookupThenBuild( + callHistory, + [ + 'xcodebuild', + '-workspace', + '/path/to/MyProject.xcworkspace', + '-scheme', + 'MyScheme', + '-configuration', + 'Debug', + '-skipMacroValidation', + '-destination', + 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'build', + ], + 'iOS Simulator Build', + ); }); it('should generate correct build command with minimal parameters (project)', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; - - // Create tracking executor - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return { - success: false, - output: '', - error: 'Test error to stop execution early', - process: { pid: 12345 }, - }; - }; + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; - const result = await build_simLogic( + await build_simLogic( { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme', simulatorName: 'iPhone 16', }, - trackingExecutor, + createTrackingExecutor(callHistory), ); - // Should generate one build command - expect(callHistory).toHaveLength(1); - expect(callHistory[0].command).toEqual([ - 'xcodebuild', - '-project', - '/path/to/MyProject.xcodeproj', - '-scheme', - 'MyScheme', - '-configuration', - 'Debug', - '-skipMacroValidation', - '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', - 'build', - ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); + expectRuntimeLookupThenBuild( + callHistory, + [ + 'xcodebuild', + '-project', + '/path/to/MyProject.xcodeproj', + '-scheme', + 'MyScheme', + '-configuration', + 'Debug', + '-skipMacroValidation', + '-destination', + 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'build', + ], + 'iOS Simulator Build', + ); }); it('should generate correct build command with all optional parameters', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; - - // Create tracking executor - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return { - success: false, - output: '', - error: 'Test error to stop execution early', - process: { pid: 12345 }, - }; - }; + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; - const result = await build_simLogic( + await build_simLogic( { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', @@ -324,131 +281,123 @@ describe('build_sim tool', () => { extraArgs: ['--verbose'], useLatestOS: false, }, - trackingExecutor, + createTrackingExecutor(callHistory), ); - // Should generate one build command with all parameters - expect(callHistory).toHaveLength(1); - expect(callHistory[0].command).toEqual([ - 'xcodebuild', - '-workspace', - '/path/to/MyProject.xcworkspace', - '-scheme', - 'MyScheme', - '-configuration', - 'Release', - '-skipMacroValidation', - '-destination', - 'platform=iOS Simulator,name=iPhone 16', - '-derivedDataPath', - '/custom/derived/path', - '--verbose', - 'build', - ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); + expectRuntimeLookupThenBuild( + callHistory, + [ + 'xcodebuild', + '-workspace', + '/path/to/MyProject.xcworkspace', + '-scheme', + 'MyScheme', + '-configuration', + 'Release', + '-skipMacroValidation', + '-destination', + 'platform=iOS Simulator,name=iPhone 16', + '-derivedDataPath', + '/custom/derived/path', + '--verbose', + 'build', + ], + 'iOS Simulator Build', + ); }); it('should handle paths with spaces in command generation', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; - - // Create tracking executor - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return { - success: false, - output: '', - error: 'Test error to stop execution early', - process: { pid: 12345 }, - }; - }; + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; - const result = await build_simLogic( + await build_simLogic( { workspacePath: '/Users/dev/My Project/MyProject.xcworkspace', scheme: 'My Scheme', simulatorName: 'iPhone 16 Pro', }, - trackingExecutor, + createTrackingExecutor(callHistory), ); - // Should generate one build command with paths containing spaces - expect(callHistory).toHaveLength(1); - expect(callHistory[0].command).toEqual([ - 'xcodebuild', - '-workspace', - '/Users/dev/My Project/MyProject.xcworkspace', - '-scheme', - 'My Scheme', - '-configuration', - 'Debug', - '-skipMacroValidation', - '-destination', - 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest', - 'build', - ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); + expectRuntimeLookupThenBuild( + callHistory, + [ + 'xcodebuild', + '-workspace', + '/Users/dev/My Project/MyProject.xcworkspace', + '-scheme', + 'My Scheme', + '-configuration', + 'Debug', + '-skipMacroValidation', + '-destination', + 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest', + 'build', + ], + 'iOS Simulator Build', + ); }); it('should generate correct build command with useLatestOS set to true', async () => { - const callHistory: Array<{ - command: string[]; - logPrefix?: string; - useShell?: boolean; - env?: any; - }> = []; - - // Create tracking executor - const trackingExecutor = async ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return { - success: false, - output: '', - error: 'Test error to stop execution early', - process: { pid: 12345 }, - }; - }; + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; - const result = await build_simLogic( + await build_simLogic( { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', simulatorName: 'iPhone 16', useLatestOS: true, }, - trackingExecutor, + createTrackingExecutor(callHistory), ); - // Should generate one build command with OS=latest - expect(callHistory).toHaveLength(1); - expect(callHistory[0].command).toEqual([ - 'xcodebuild', - '-workspace', - '/path/to/MyProject.xcworkspace', - '-scheme', - 'MyScheme', - '-configuration', - 'Debug', - '-skipMacroValidation', - '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', - 'build', - ]); - expect(callHistory[0].logPrefix).toBe('iOS Simulator Build'); + expectRuntimeLookupThenBuild( + callHistory, + [ + 'xcodebuild', + '-workspace', + '/path/to/MyProject.xcworkspace', + '-scheme', + 'MyScheme', + '-configuration', + 'Debug', + '-skipMacroValidation', + '-destination', + 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'build', + ], + 'iOS Simulator Build', + ); + }); + + it('should infer watchOS platform from simulator name', async () => { + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; + + await build_simLogic( + { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyWatchScheme', + simulatorName: 'Apple Watch Ultra 2', + }, + createTrackingExecutor(callHistory), + ); + + expectRuntimeLookupThenBuild( + callHistory, + [ + 'xcodebuild', + '-workspace', + '/path/to/MyProject.xcworkspace', + '-scheme', + 'MyWatchScheme', + '-configuration', + 'Debug', + '-skipMacroValidation', + '-destination', + 'platform=watchOS Simulator,name=Apple Watch Ultra 2,OS=latest', + 'build', + ], + 'watchOS Simulator Build', + ); }); }); diff --git a/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts b/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts index 0611ed21..0d650d2c 100644 --- a/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts +++ b/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts @@ -8,8 +8,9 @@ import { ChildProcess } from 'child_process'; import * as z from 'zod'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import getSimAppPath, { get_sim_app_pathLogic } from '../get_sim_app_path.ts'; +import { schema, handler, get_sim_app_pathLogic } from '../get_sim_app_path.ts'; import type { CommandExecutor } from '../../../../utils/CommandExecutor.ts'; +import { XcodePlatform } from '../../../../types/common.ts'; describe('get_sim_app_path tool', () => { beforeEach(() => { @@ -17,34 +18,26 @@ describe('get_sim_app_path tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(getSimAppPath.name).toBe('get_sim_app_path'); - }); - - it('should have concise description', () => { - expect(getSimAppPath.description).toBe('Retrieves the built app path for an iOS simulator.'); - }); - it('should have handler function', () => { - expect(typeof getSimAppPath.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose only platform in public schema', () => { - const schema = z.object(getSimAppPath.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ platform: 'iOS Simulator' }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ platform: 'iOS' }).success).toBe(false); + expect(schemaObj.safeParse({ platform: XcodePlatform.iOSSimulator }).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({ platform: 'iOS' }).success).toBe(false); - const schemaKeys = Object.keys(getSimAppPath.schema).sort(); + const schemaKeys = Object.keys(schema).sort(); expect(schemaKeys).toEqual(['platform']); }); }); describe('Handler Requirements', () => { it('should require scheme when not provided', async () => { - const result = await getSimAppPath.handler({ - platform: 'iOS Simulator', + const result = await handler({ + platform: XcodePlatform.iOSSimulator, }); expect(result.isError).toBe(true); @@ -54,8 +47,8 @@ describe('get_sim_app_path tool', () => { it('should require project or workspace when scheme default exists', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await getSimAppPath.handler({ - platform: 'iOS Simulator', + const result = await handler({ + platform: XcodePlatform.iOSSimulator, }); expect(result.isError).toBe(true); @@ -68,8 +61,8 @@ describe('get_sim_app_path tool', () => { projectPath: '/path/to/project.xcodeproj', }); - const result = await getSimAppPath.handler({ - platform: 'iOS Simulator', + const result = await handler({ + platform: XcodePlatform.iOSSimulator, }); expect(result.isError).toBe(true); @@ -79,8 +72,8 @@ describe('get_sim_app_path tool', () => { it('should error when both projectPath and workspacePath provided explicitly', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await getSimAppPath.handler({ - platform: 'iOS Simulator', + const result = await handler({ + platform: XcodePlatform.iOSSimulator, projectPath: '/path/project.xcodeproj', workspacePath: '/path/workspace.xcworkspace', }); @@ -97,8 +90,8 @@ describe('get_sim_app_path tool', () => { workspacePath: '/path/to/workspace.xcworkspace', }); - const result = await getSimAppPath.handler({ - platform: 'iOS Simulator', + const result = await handler({ + platform: XcodePlatform.iOSSimulator, simulatorId: 'SIM-UUID', simulatorName: 'iPhone 16', }); @@ -142,7 +135,7 @@ describe('get_sim_app_path tool', () => { { workspacePath: '/path/to/workspace.xcworkspace', scheme: 'MyScheme', - platform: 'iOS Simulator', + platform: XcodePlatform.iOSSimulator, simulatorName: 'iPhone 16', useLatestOS: true, }, @@ -151,7 +144,7 @@ describe('get_sim_app_path tool', () => { expect(callHistory).toHaveLength(1); expect(callHistory[0].logPrefix).toBe('Get App Path'); - expect(callHistory[0].useShell).toBe(true); + expect(callHistory[0].useShell).toBe(false); expect(callHistory[0].command).toEqual([ 'xcodebuild', '-showBuildSettings', @@ -181,7 +174,7 @@ describe('get_sim_app_path tool', () => { { projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme', - platform: 'iOS Simulator', + platform: XcodePlatform.iOSSimulator, simulatorId: 'SIM-UUID', }, mockExecutor, diff --git a/src/mcp/tools/simulator/__tests__/index.test.ts b/src/mcp/tools/simulator/__tests__/index.test.ts deleted file mode 100644 index a698604f..00000000 --- a/src/mcp/tools/simulator/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for simulator-project workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('simulator-project workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('iOS Simulator Development'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts index 5989b0a1..b983d856 100644 --- a/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts @@ -4,9 +4,11 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, + createMockCommandResponse, } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import installAppSim, { install_app_simLogic } from '../install_app_sim.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; +import { schema, handler, install_app_simLogic } from '../install_app_sim.ts'; describe('install_app_sim tool', () => { beforeEach(() => { @@ -14,24 +16,16 @@ describe('install_app_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(installAppSim.name).toBe('install_app_sim'); - }); - - it('should have concise description', () => { - expect(installAppSim.description).toBe('Installs an app in an iOS simulator.'); - }); - it('should expose public schema with only appPath', () => { - const schema = z.object(installAppSim.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ appPath: '/path/to/app.app' }).success).toBe(true); - expect(schema.safeParse({ appPath: 42 }).success).toBe(false); - expect(schema.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({ appPath: '/path/to/app.app' }).success).toBe(true); + expect(schemaObj.safeParse({ appPath: 42 }).success).toBe(false); + expect(schemaObj.safeParse({}).success).toBe(false); - expect(Object.keys(installAppSim.schema)).toEqual(['appPath']); + expect(Object.keys(schema)).toEqual(['appPath']); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: 'test-uuid-123', appPath: '/path/app.app', }); @@ -42,18 +36,18 @@ describe('install_app_sim tool', () => { describe('Handler Requirements', () => { it('should require simulatorId when not provided', async () => { - const result = await installAppSim.handler({ appPath: '/path/to/app.app' }); + const result = await handler({ appPath: '/path/to/app.app' }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); - expect(result.content[0].text).toContain('simulatorId is required'); + expect(result.content[0].text).toContain('Provide simulatorId or simulatorName'); expect(result.content[0].text).toContain('session-set-defaults'); }); it('should validate appPath when simulatorId default exists', async () => { sessionStore.setDefaults({ simulatorId: 'SIM-UUID' }); - const result = await installAppSim.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Parameter validation failed'); @@ -65,15 +59,15 @@ describe('install_app_sim tool', () => { describe('Command Generation', () => { it('should generate correct simctl install command', async () => { - const executorCalls: unknown[] = []; - const mockExecutor = (...args: unknown[]) => { + const executorCalls: Array> = []; + const mockExecutor: CommandExecutor = (...args) => { executorCalls.push(args); - return Promise.resolve({ - success: true, - output: 'App installed', - error: undefined, - process: { pid: 12345 }, - }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'App installed', + }), + ); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -93,7 +87,7 @@ describe('install_app_sim tool', () => { [ ['xcrun', 'simctl', 'install', 'test-uuid-123', '/path/to/app.app'], 'Install App in Simulator', - true, + false, undefined, ], [ @@ -106,15 +100,15 @@ describe('install_app_sim tool', () => { }); it('should generate command with different simulator identifier', async () => { - const executorCalls: unknown[] = []; - const mockExecutor = (...args: unknown[]) => { + const executorCalls: Array> = []; + const mockExecutor: CommandExecutor = (...args) => { executorCalls.push(args); - return Promise.resolve({ - success: true, - output: 'App installed', - error: undefined, - process: { pid: 12345 }, - }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'App installed', + }), + ); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -134,7 +128,7 @@ describe('install_app_sim tool', () => { [ ['xcrun', 'simctl', 'install', 'different-uuid-456', '/different/path/MyApp.app'], 'Install App in Simulator', - true, + false, undefined, ], [ @@ -174,27 +168,29 @@ describe('install_app_sim tool', () => { }); it('should handle bundle id extraction failure gracefully', async () => { - const bundleIdCalls: unknown[] = []; - const mockExecutor = (...args: unknown[]) => { + const bundleIdCalls: Array> = []; + const mockExecutor: CommandExecutor = (...args) => { bundleIdCalls.push(args); if ( Array.isArray(args[0]) && (args[0] as string[])[0] === 'xcrun' && (args[0] as string[])[1] === 'simctl' ) { - return Promise.resolve({ - success: true, - output: 'App installed', - error: undefined, - process: { pid: 12345 }, - }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'App installed', + error: undefined, + }), + ); } - return Promise.resolve({ - success: false, - output: '', - error: 'Failed to read bundle ID', - process: { pid: 12345 }, - }); + return Promise.resolve( + createMockCommandResponse({ + success: false, + output: '', + error: 'Failed to read bundle ID', + }), + ); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -214,41 +210,41 @@ describe('install_app_sim tool', () => { content: [ { type: 'text', - text: 'App installed successfully in simulator test-uuid-123', - }, - { - type: 'text', - text: `Next Steps: -1. Open the Simulator app: open_sim({}) -2. Launch the app: launch_app_sim({ simulatorId: "test-uuid-123", bundleId: "YOUR_APP_BUNDLE_ID" })`, + text: 'App installed successfully in simulator test-uuid-123.', }, ], + nextStepParams: { + open_sim: {}, + launch_app_sim: { simulatorId: 'test-uuid-123', bundleId: 'YOUR_APP_BUNDLE_ID' }, + }, }); expect(bundleIdCalls).toHaveLength(2); }); it('should include bundle id when extraction succeeds', async () => { - const bundleIdCalls: unknown[] = []; - const mockExecutor = (...args: unknown[]) => { + const bundleIdCalls: Array> = []; + const mockExecutor: CommandExecutor = (...args) => { bundleIdCalls.push(args); if ( Array.isArray(args[0]) && (args[0] as string[])[0] === 'xcrun' && (args[0] as string[])[1] === 'simctl' ) { - return Promise.resolve({ + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'App installed', + error: undefined, + }), + ); + } + return Promise.resolve( + createMockCommandResponse({ success: true, - output: 'App installed', + output: 'io.sentry.myapp', error: undefined, - process: { pid: 12345 }, - }); - } - return Promise.resolve({ - success: true, - output: 'com.example.myapp', - error: undefined, - process: { pid: 12345 }, - }); + }), + ); }; const mockFileSystem = createMockFileSystemExecutor({ @@ -268,27 +264,26 @@ describe('install_app_sim tool', () => { content: [ { type: 'text', - text: 'App installed successfully in simulator test-uuid-123', - }, - { - type: 'text', - text: `Next Steps: -1. Open the Simulator app: open_sim({}) -2. Launch the app: launch_app_sim({ simulatorId: "test-uuid-123", bundleId: "com.example.myapp" })`, + text: 'App installed successfully in simulator test-uuid-123.', }, ], + nextStepParams: { + open_sim: {}, + launch_app_sim: { simulatorId: 'test-uuid-123', bundleId: 'io.sentry.myapp' }, + }, }); expect(bundleIdCalls).toHaveLength(2); }); it('should handle command failure', async () => { - const mockExecutor = () => - Promise.resolve({ - success: false, - output: '', - error: 'Install failed', - process: { pid: 12345 }, - }); + const mockExecutor: CommandExecutor = () => + Promise.resolve( + createMockCommandResponse({ + success: false, + output: '', + error: 'Install failed', + }), + ); const mockFileSystem = createMockFileSystemExecutor({ existsSync: () => true, diff --git a/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts b/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts index 28d0983f..32c9177a 100644 --- a/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts @@ -5,9 +5,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import launchAppLogsSim, { +import { + schema, + handler, launch_app_logs_simLogic, - LogCaptureFunction, + type LogCaptureFunction, } from '../launch_app_logs_sim.ts'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; @@ -18,54 +20,41 @@ describe('launch_app_logs_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should expose correct metadata', () => { - expect(launchAppLogsSim.name).toBe('launch_app_logs_sim'); - expect(launchAppLogsSim.description).toBe( - 'Launches an app in an iOS simulator and captures its logs.', - ); - }); - it('should expose only non-session fields in public schema', () => { - const schema = z.object(launchAppLogsSim.schema); + const schemaObj = z.strictObject(schema); - expect(schema.safeParse({ bundleId: 'com.example.app' }).success).toBe(true); - expect(schema.safeParse({ bundleId: 'com.example.app', args: ['--debug'] }).success).toBe( - true, - ); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ bundleId: 42 }).success).toBe(false); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ args: ['--debug'] }).success).toBe(true); + expect(schemaObj.safeParse({ bundleId: 'io.sentry.app' }).success).toBe(false); + expect(schemaObj.safeParse({ bundleId: 42 }).success).toBe(false); - expect(Object.keys(launchAppLogsSim.schema).sort()).toEqual(['args', 'bundleId'].sort()); + expect(Object.keys(schema).sort()).toEqual(['args', 'env']); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: 'abc123', - bundleId: 'com.example.app', }); - expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as Record)).toBe(false); + expect(withSimId.success).toBe(false); }); }); describe('Handler Requirements', () => { it('should require simulatorId when not provided', async () => { - const result = await launchAppLogsSim.handler({ bundleId: 'com.example.testapp' }); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); - expect(result.content[0].text).toContain('simulatorId is required'); + expect(result.content[0].text).toContain('Provide simulatorId or simulatorName'); expect(result.content[0].text).toContain('session-set-defaults'); }); - it('should validate bundleId when simulatorId default exists', async () => { + it('should require bundleId when simulatorId default exists', async () => { sessionStore.setDefaults({ simulatorId: 'SIM-UUID' }); - const result = await launchAppLogsSim.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Parameter validation failed'); - expect(result.content[0].text).toContain( - 'bundleId: Invalid input: expected string, received undefined', - ); + expect(result.content[0].text).toContain('Missing required session defaults'); + expect(result.content[0].text).toContain('bundleId is required'); }); }); @@ -87,7 +76,7 @@ describe('launch_app_logs_sim tool', () => { const result = await launch_app_logs_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', }, mockExecutor, logCaptureStub, @@ -97,15 +86,18 @@ describe('launch_app_logs_sim tool', () => { content: [ { type: 'text', - text: `App launched successfully in simulator test-uuid-123 with log capture enabled.\n\nLog capture session ID: test-session-123\n\nNext Steps:\n1. Interact with your app in the simulator.\n2. Use 'stop_and_get_simulator_log({ logSessionId: "test-session-123" })' to stop capture and retrieve logs.`, + text: 'App launched successfully in simulator test-uuid-123 with log capture enabled.\n\nLog capture session ID: test-session-123\n\nInteract with your app in the simulator, then stop capture to retrieve logs.', }, ], + nextStepParams: { + stop_sim_log_cap: { logSessionId: 'test-session-123' }, + }, isError: false, }); expect(capturedParams).toEqual({ simulatorUuid: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', captureConsole: true, }); }); @@ -127,7 +119,7 @@ describe('launch_app_logs_sim tool', () => { await launch_app_logs_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', args: ['--debug'], }, mockExecutor, @@ -136,12 +128,74 @@ describe('launch_app_logs_sim tool', () => { expect(capturedParams).toEqual({ simulatorUuid: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', captureConsole: true, args: ['--debug'], }); }); + it('should pass env vars through to log capture function', async () => { + let capturedParams: unknown = null; + const logCaptureStub: LogCaptureFunction = async (params) => { + capturedParams = params; + return { + sessionId: 'test-session-789', + logFilePath: '/tmp/xcodemcp_sim_log_test-session-789.log', + processes: [], + error: undefined, + }; + }; + + const mockExecutor = createMockExecutor({ success: true, output: '' }); + + await launch_app_logs_simLogic( + { + simulatorId: 'test-uuid-123', + bundleId: 'io.sentry.testapp', + env: { STAGING_ENABLED: '1' }, + }, + mockExecutor, + logCaptureStub, + ); + + expect(capturedParams).toEqual({ + simulatorUuid: 'test-uuid-123', + bundleId: 'io.sentry.testapp', + captureConsole: true, + env: { STAGING_ENABLED: '1' }, + }); + }); + + it('should not include env in capture params when env is undefined', async () => { + let capturedParams: unknown = null; + const logCaptureStub: LogCaptureFunction = async (params) => { + capturedParams = params; + return { + sessionId: 'test-session-101', + logFilePath: '/tmp/xcodemcp_sim_log_test-session-101.log', + processes: [], + error: undefined, + }; + }; + + const mockExecutor = createMockExecutor({ success: true, output: '' }); + + await launch_app_logs_simLogic( + { + simulatorId: 'test-uuid-123', + bundleId: 'io.sentry.testapp', + }, + mockExecutor, + logCaptureStub, + ); + + expect(capturedParams).toEqual({ + simulatorUuid: 'test-uuid-123', + bundleId: 'io.sentry.testapp', + captureConsole: true, + }); + }); + it('should surface log capture failure', async () => { const logCaptureStub: LogCaptureFunction = async () => ({ sessionId: '', @@ -155,7 +209,7 @@ describe('launch_app_logs_sim tool', () => { const result = await launch_app_logs_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', }, mockExecutor, logCaptureStub, @@ -165,7 +219,7 @@ describe('launch_app_logs_sim tool', () => { content: [ { type: 'text', - text: 'App was launched but log capture failed: Failed to start log capture', + text: 'Failed to launch app with log capture: Failed to start log capture', }, ], isError: true, diff --git a/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts index 45bf74e1..6c732c79 100644 --- a/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import launchAppSim, { launch_app_simLogic } from '../launch_app_sim.ts'; +import { schema, handler, launch_app_simLogic } from '../launch_app_sim.ts'; describe('launch_app_sim tool', () => { beforeEach(() => { @@ -10,48 +10,33 @@ describe('launch_app_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should expose correct name and description', () => { - expect(launchAppSim.name).toBe('launch_app_sim'); - expect(launchAppSim.description).toBe('Launches an app in an iOS simulator.'); - }); - it('should expose only non-session fields in public schema', () => { - const schema = z.object(launchAppSim.schema); + const schemaObj = z.strictObject(schema); - expect( - schema.safeParse({ - bundleId: 'com.example.testapp', - }).success, - ).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); expect( - schema.safeParse({ - bundleId: 'com.example.testapp', + schemaObj.safeParse({ args: ['--debug'], }).success, ).toBe(true); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ bundleId: 123 }).success).toBe(false); - expect(schema.safeParse({ args: ['--debug'] }).success).toBe(false); + expect(schemaObj.safeParse({ bundleId: 'io.sentry.testapp' }).success).toBe(false); + expect(schemaObj.safeParse({ bundleId: 123 }).success).toBe(false); - expect(Object.keys(launchAppSim.schema).sort()).toEqual(['args', 'bundleId'].sort()); + expect(Object.keys(schema).sort()).toEqual(['args', 'env']); - const withSimDefaults = schema.safeParse({ + const withSimDefaults = schemaObj.safeParse({ simulatorId: 'sim-default', simulatorName: 'iPhone 16', - bundleId: 'com.example.testapp', }); - expect(withSimDefaults.success).toBe(true); - const parsed = withSimDefaults.data as Record; - expect(parsed.simulatorId).toBeUndefined(); - expect(parsed.simulatorName).toBeUndefined(); + expect(withSimDefaults.success).toBe(false); }); }); describe('Handler Requirements', () => { it('should require simulator identifier when not provided', async () => { - const result = await launchAppSim.handler({ bundleId: 'com.example.testapp' }); + const result = await handler({ bundleId: 'io.sentry.testapp' }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -59,23 +44,21 @@ describe('launch_app_sim tool', () => { expect(result.content[0].text).toContain('session-set-defaults'); }); - it('should validate bundleId when simulatorId default exists', async () => { + it('should require bundleId when simulatorId default exists', async () => { sessionStore.setDefaults({ simulatorId: 'SIM-UUID' }); - const result = await launchAppSim.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Parameter validation failed'); - expect(result.content[0].text).toContain( - 'bundleId: Invalid input: expected string, received undefined', - ); + expect(result.content[0].text).toContain('Missing required session defaults'); + expect(result.content[0].text).toContain('bundleId is required'); }); it('should reject when both simulatorId and simulatorName provided explicitly', async () => { - const result = await launchAppSim.handler({ + const result = await handler({ simulatorId: 'SIM-UUID', simulatorName: 'iPhone 16', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', }); expect(result.isError).toBe(true); @@ -109,7 +92,7 @@ describe('launch_app_sim tool', () => { const result = await launch_app_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', }, sequencedExecutor, ); @@ -118,9 +101,20 @@ describe('launch_app_sim tool', () => { content: [ { type: 'text', - text: `✅ App launched successfully in simulator test-uuid-123.\n\nNext Steps:\n1. To see simulator: open_sim()\n2. Log capture: start_sim_log_cap({ simulatorId: "test-uuid-123", bundleId: "com.example.testapp" })\n With console: start_sim_log_cap({ simulatorId: "test-uuid-123", bundleId: "com.example.testapp", captureConsole: true })\n3. Stop logs: stop_sim_log_cap({ logSessionId: 'SESSION_ID' })`, + text: 'App launched successfully in simulator test-uuid-123.', }, ], + nextStepParams: { + open_sim: {}, + start_sim_log_cap: [ + { simulatorId: 'test-uuid-123', bundleId: 'io.sentry.testapp' }, + { + simulatorId: 'test-uuid-123', + bundleId: 'io.sentry.testapp', + captureConsole: true, + }, + ], + }, }); }); @@ -150,48 +144,65 @@ describe('launch_app_sim tool', () => { await launch_app_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', args: ['--debug', '--verbose'], }, sequencedExecutor, ); expect(commands).toEqual([ - ['xcrun', 'simctl', 'get_app_container', 'test-uuid-123', 'com.example.testapp', 'app'], - [ - 'xcrun', - 'simctl', - 'launch', - 'test-uuid-123', - 'com.example.testapp', - '--debug', - '--verbose', - ], + ['xcrun', 'simctl', 'get_app_container', 'test-uuid-123', 'io.sentry.testapp', 'app'], + ['xcrun', 'simctl', 'launch', 'test-uuid-123', 'io.sentry.testapp', '--debug', '--verbose'], ]); }); - it('should surface error when simulatorId missing after lookup', async () => { - const result = await launch_app_simLogic( - { - simulatorId: undefined, - bundleId: 'com.example.testapp', - } as any, - async () => ({ + it('should display friendly name when simulatorName is provided alongside resolved simulatorId', async () => { + let callCount = 0; + const sequencedExecutor = async (command: string[]) => { + callCount++; + if (callCount === 1) { + return { + success: true, + output: '/path/to/app/container', + error: '', + process: {} as any, + }; + } + return { success: true, - output: '', + output: 'App launched successfully', error: '', process: {} as any, - }), + }; + }; + + const result = await launch_app_simLogic( + { + simulatorId: 'resolved-uuid', + simulatorName: 'iPhone 16', + bundleId: 'io.sentry.testapp', + }, + sequencedExecutor, ); expect(result).toEqual({ content: [ { type: 'text', - text: 'No simulator identifier provided', + text: 'App launched successfully in simulator "iPhone 16" (resolved-uuid).', }, ], - isError: true, + nextStepParams: { + open_sim: {}, + start_sim_log_cap: [ + { simulatorId: 'resolved-uuid', bundleId: 'io.sentry.testapp' }, + { + simulatorId: 'resolved-uuid', + bundleId: 'io.sentry.testapp', + captureConsole: true, + }, + ], + }, }); }); @@ -216,7 +227,7 @@ describe('launch_app_sim tool', () => { const result = await launch_app_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', }, mockExecutor, ); @@ -248,7 +259,7 @@ describe('launch_app_sim tool', () => { const result = await launch_app_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', }, mockExecutor, ); @@ -287,7 +298,7 @@ describe('launch_app_sim tool', () => { const result = await launch_app_simLogic( { simulatorId: 'test-uuid-123', - bundleId: 'com.example.testapp', + bundleId: 'io.sentry.testapp', }, mockExecutor, ); @@ -302,30 +313,19 @@ describe('launch_app_sim tool', () => { }); }); - it('should launch using simulatorName by resolving UUID', async () => { + it('should pass env vars with SIMCTL_CHILD_ prefix to executor opts', async () => { let callCount = 0; - const sequencedExecutor = async (command: string[]) => { + const capturedOpts: (Record | undefined)[] = []; + + const sequencedExecutor = async ( + command: string[], + _logPrefix?: string, + _useShell?: boolean, + opts?: { env?: Record }, + ) => { callCount++; + capturedOpts.push(opts); if (callCount === 1) { - return { - success: true, - output: JSON.stringify({ - devices: { - 'iOS 17.0': [ - { - name: 'iPhone 16', - udid: 'resolved-uuid', - isAvailable: true, - state: 'Shutdown', - }, - ], - }, - }), - error: '', - process: {} as any, - }; - } - if (callCount === 2) { return { success: true, output: '/path/to/app/container', @@ -341,75 +341,62 @@ describe('launch_app_sim tool', () => { }; }; - const result = await launch_app_simLogic( + await launch_app_simLogic( { - simulatorName: 'iPhone 16', - bundleId: 'com.example.testapp', + simulatorId: 'test-uuid-123', + bundleId: 'io.sentry.testapp', + env: { STAGING_ENABLED: '1', DEBUG: 'true' }, }, sequencedExecutor, ); - expect(result).toEqual({ - content: [ - { - type: 'text', - text: `✅ App launched successfully in simulator "iPhone 16" (resolved-uuid).\n\nNext Steps:\n1. To see simulator: open_sim()\n2. Log capture: start_sim_log_cap({ simulatorName: "iPhone 16", bundleId: "com.example.testapp" })\n With console: start_sim_log_cap({ simulatorName: "iPhone 16", bundleId: "com.example.testapp", captureConsole: true })\n3. Stop logs: stop_sim_log_cap({ logSessionId: 'SESSION_ID' })`, - }, - ], - }); - }); - - it('should return error when simulator name is not found', async () => { - const mockListExecutor = async () => ({ - success: true, - output: JSON.stringify({ devices: {} }), - error: '', - process: {} as any, - }); - - const result = await launch_app_simLogic( - { - simulatorName: 'Missing Simulator', - bundleId: 'com.example.testapp', + // First call is get_app_container (no env), second is launch (with env) + expect(capturedOpts[1]).toEqual({ + env: { + SIMCTL_CHILD_STAGING_ENABLED: '1', + SIMCTL_CHILD_DEBUG: 'true', }, - mockListExecutor, - ); - - expect(result).toEqual({ - content: [ - { - type: 'text', - text: 'Simulator named "Missing Simulator" not found. Use list_sims to see available simulators.', - }, - ], - isError: true, }); }); - it('should return error when simctl list fails', async () => { - const mockExecutor = createMockExecutor({ - success: false, - output: '', - error: 'simctl list failed', - }); + it('should not pass env opts when env is undefined', async () => { + let callCount = 0; + const capturedOpts: (Record | undefined)[] = []; + + const sequencedExecutor = async ( + command: string[], + _logPrefix?: string, + _useShell?: boolean, + opts?: { env?: Record }, + ) => { + callCount++; + capturedOpts.push(opts); + if (callCount === 1) { + return { + success: true, + output: '/path/to/app/container', + error: '', + process: {} as any, + }; + } + return { + success: true, + output: 'App launched successfully', + error: '', + process: {} as any, + }; + }; - const result = await launch_app_simLogic( + await launch_app_simLogic( { - simulatorName: 'iPhone 16', - bundleId: 'com.example.testapp', + simulatorId: 'test-uuid-123', + bundleId: 'io.sentry.testapp', }, - mockExecutor, + sequencedExecutor, ); - expect(result).toEqual({ - content: [ - { - type: 'text', - text: 'Failed to list simulators: simctl list failed', - }, - ], - isError: true, - }); + // Launch call opts should be undefined when no env provided + expect(capturedOpts[1]).toBeUndefined(); }); }); }); diff --git a/src/mcp/tools/simulator/__tests__/list_sims.test.ts b/src/mcp/tools/simulator/__tests__/list_sims.test.ts index c0846482..9acbd0fb 100644 --- a/src/mcp/tools/simulator/__tests__/list_sims.test.ts +++ b/src/mcp/tools/simulator/__tests__/list_sims.test.ts @@ -1,12 +1,12 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { + createMockCommandResponse, createMockExecutor, - createMockFileSystemExecutor, } from '../../../../test-utils/mock-executors.ts'; -// Import the plugin and logic function -import listSims, { list_simsLogic } from '../list_sims.ts'; +// Import the named exports and logic function +import { schema, handler, list_simsLogic } from '../list_sims.ts'; describe('list_sims tool', () => { let callHistory: Array<{ @@ -19,31 +19,23 @@ describe('list_sims tool', () => { callHistory = []; describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(listSims.name).toBe('list_sims'); - }); - - it('should have correct description', () => { - expect(listSims.description).toBe('Lists available iOS simulators with their UUIDs. '); - }); - it('should have handler function', () => { - expect(typeof listSims.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have correct schema with enabled boolean field', () => { - const schema = z.object(listSims.schema); + const schemaObj = z.object(schema); // Valid inputs - expect(schema.safeParse({ enabled: true }).success).toBe(true); - expect(schema.safeParse({ enabled: false }).success).toBe(true); - expect(schema.safeParse({ enabled: undefined }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ enabled: true }).success).toBe(true); + expect(schemaObj.safeParse({ enabled: false }).success).toBe(true); + expect(schemaObj.safeParse({ enabled: undefined }).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); // Invalid inputs - expect(schema.safeParse({ enabled: 'yes' }).success).toBe(false); - expect(schema.safeParse({ enabled: 1 }).success).toBe(false); - expect(schema.safeParse({ enabled: null }).success).toBe(false); + expect(schemaObj.safeParse({ enabled: 'yes' }).success).toBe(false); + expect(schemaObj.safeParse({ enabled: 1 }).success).toBe(false); + expect(schemaObj.safeParse({ enabled: null }).success).toBe(false); }); }); @@ -71,27 +63,27 @@ describe('list_sims tool', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record }, + detached?: boolean, ) => { - callHistory.push({ command, logPrefix, useShell, env }); + callHistory.push({ command, logPrefix, useShell, env: opts?.env }); + void detached; // Return JSON output for JSON command if (command.includes('--json')) { - return { + return createMockCommandResponse({ success: true, output: mockJsonOutput, error: undefined, - process: { pid: 12345 }, - }; + }); } // Return text output for text command - return { + return createMockCommandResponse({ success: true, output: mockTextOutput, error: undefined, - process: { pid: 12345 }, - }; + }); }; const result = await list_simsLogic({ enabled: true }, mockExecutor); @@ -101,13 +93,13 @@ describe('list_sims tool', () => { expect(callHistory[0]).toEqual({ command: ['xcrun', 'simctl', 'list', 'devices', '--json'], logPrefix: 'List Simulators (JSON)', - useShell: true, + useShell: false, env: undefined, }); expect(callHistory[1]).toEqual({ command: ['xcrun', 'simctl', 'list', 'devices'], logPrefix: 'List Simulators (Text)', - useShell: true, + useShell: false, env: undefined, }); @@ -120,13 +112,19 @@ describe('list_sims tool', () => { iOS 17.0: - iPhone 15 (test-uuid-123) -Next Steps: -1. Boot a simulator: boot_sim({ simulatorId: 'UUID_FROM_ABOVE' }) -2. Open the simulator UI: open_sim({}) -3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }) -4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })`, +Hint: Save a default simulator with session-set-defaults { simulatorId: 'UUID_FROM_ABOVE' } (or simulatorName).`, }, ], + nextStepParams: { + boot_sim: { simulatorId: 'UUID_FROM_ABOVE' }, + open_sim: {}, + build_sim: { scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }, + get_sim_app_path: { + scheme: 'YOUR_SCHEME', + platform: 'iOS Simulator', + simulatorId: 'UUID_FROM_ABOVE', + }, + }, }); }); @@ -150,19 +148,17 @@ Next Steps: const mockExecutor = async (command: string[]) => { if (command.includes('--json')) { - return { + return createMockCommandResponse({ success: true, output: mockJsonOutput, error: undefined, - process: { pid: 12345 }, - }; + }); } - return { + return createMockCommandResponse({ success: true, output: mockTextOutput, error: undefined, - process: { pid: 12345 }, - }; + }); }; const result = await list_simsLogic({ enabled: true }, mockExecutor); @@ -176,13 +172,19 @@ Next Steps: iOS 17.0: - iPhone 15 (test-uuid-123) [Booted] -Next Steps: -1. Boot a simulator: boot_sim({ simulatorId: 'UUID_FROM_ABOVE' }) -2. Open the simulator UI: open_sim({}) -3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }) -4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })`, +Hint: Save a default simulator with session-set-defaults { simulatorId: 'UUID_FROM_ABOVE' } (or simulatorName).`, }, ], + nextStepParams: { + boot_sim: { simulatorId: 'UUID_FROM_ABOVE' }, + open_sim: {}, + build_sim: { scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }, + get_sim_app_path: { + scheme: 'YOUR_SCHEME', + platform: 'iOS Simulator', + simulatorId: 'UUID_FROM_ABOVE', + }, + }, }); }); @@ -208,19 +210,17 @@ Next Steps: const mockExecutor = async (command: string[]) => { if (command.includes('--json')) { - return { + return createMockCommandResponse({ success: true, output: mockJsonOutput, error: undefined, - process: { pid: 12345 }, - }; + }); } - return { + return createMockCommandResponse({ success: true, output: mockTextOutput, error: undefined, - process: { pid: 12345 }, - }; + }); }; const result = await list_simsLogic({ enabled: true }, mockExecutor); @@ -238,13 +238,19 @@ iOS 18.6: iOS 26.0: - iPhone 17 Pro (text-uuid-456) -Next Steps: -1. Boot a simulator: boot_sim({ simulatorId: 'UUID_FROM_ABOVE' }) -2. Open the simulator UI: open_sim({}) -3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }) -4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })`, +Hint: Save a default simulator with session-set-defaults { simulatorId: 'UUID_FROM_ABOVE' } (or simulatorName).`, }, ], + nextStepParams: { + boot_sim: { simulatorId: 'UUID_FROM_ABOVE' }, + open_sim: {}, + build_sim: { scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }, + get_sim_app_path: { + scheme: 'YOUR_SCHEME', + platform: 'iOS Simulator', + simulatorId: 'UUID_FROM_ABOVE', + }, + }, }); }); @@ -276,21 +282,19 @@ Next Steps: const mockExecutor = async (command: string[]) => { // JSON command returns invalid JSON if (command.includes('--json')) { - return { + return createMockCommandResponse({ success: true, output: 'invalid json', error: undefined, - process: { pid: 12345 }, - }; + }); } // Text command returns valid text output - return { + return createMockCommandResponse({ success: true, output: mockTextOutput, error: undefined, - process: { pid: 12345 }, - }; + }); }; const result = await list_simsLogic({ enabled: true }, mockExecutor); @@ -305,13 +309,19 @@ Next Steps: iOS 17.0: - iPhone 15 (test-uuid-456) -Next Steps: -1. Boot a simulator: boot_sim({ simulatorId: 'UUID_FROM_ABOVE' }) -2. Open the simulator UI: open_sim({}) -3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }) -4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })`, +Hint: Save a default simulator with session-set-defaults { simulatorId: 'UUID_FROM_ABOVE' } (or simulatorName).`, }, ], + nextStepParams: { + boot_sim: { simulatorId: 'UUID_FROM_ABOVE' }, + open_sim: {}, + build_sim: { scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }, + get_sim_app_path: { + scheme: 'YOUR_SCHEME', + platform: 'iOS Simulator', + simulatorId: 'UUID_FROM_ABOVE', + }, + }, }); }); diff --git a/src/mcp/tools/simulator/__tests__/open_sim.test.ts b/src/mcp/tools/simulator/__tests__/open_sim.test.ts index 624221de..d7dda558 100644 --- a/src/mcp/tools/simulator/__tests__/open_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/open_sim.test.ts @@ -6,38 +6,34 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts'; -import openSim, { open_simLogic } from '../open_sim.ts'; +import { + createMockCommandResponse, + createMockExecutor, + type CommandExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, open_simLogic } from '../open_sim.ts'; describe('open_sim tool', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name field', () => { - expect(openSim.name).toBe('open_sim'); - }); - - it('should have correct description field', () => { - expect(openSim.description).toBe('Opens the iOS Simulator app.'); - }); - it('should have handler function', () => { - expect(typeof openSim.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have correct schema validation', () => { - const schema = z.object(openSim.schema); + const schemaObj = z.object(schema); // Schema is empty, so any object should pass - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); expect( - schema.safeParse({ + schemaObj.safeParse({ anyProperty: 'value', }).success, ).toBe(true); // Empty schema should accept anything expect( - schema.safeParse({ + schemaObj.safeParse({ enabled: true, }).success, ).toBe(true); @@ -57,22 +53,17 @@ describe('open_sim tool', () => { content: [ { type: 'text', - text: 'Simulator app opened successfully', - }, - { - type: 'text', - text: `Next Steps: -1. Boot a simulator if needed: boot_sim({ simulatorId: 'UUID_FROM_LIST_SIMULATORS' }) -2. Launch your app and interact with it -3. Log capture options: - - Option 1: Capture structured logs only (app continues running): - start_sim_log_cap({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' }) - - Option 2: Capture both console and structured logs (app will restart): - start_sim_log_cap({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID', captureConsole: true }) - - Option 3: Launch app with logs in one step: - launch_app_logs_sim({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' })`, + text: 'Simulator app opened successfully.', }, ], + nextStepParams: { + boot_sim: { simulatorId: 'UUID_FROM_LIST_SIMS' }, + start_sim_log_cap: [ + { simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' }, + { simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID', captureConsole: true }, + ], + launch_app_logs_sim: { simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' }, + }, }); }); @@ -131,24 +122,25 @@ describe('open_sim tool', () => { it('should verify command generation with mock executor', async () => { const calls: Array<{ command: string[]; - description: string; - hideOutput: boolean; - workingDirectory: string | undefined; + description?: string; + hideOutput?: boolean; + opts?: { cwd?: string }; }> = []; const mockExecutor: CommandExecutor = async ( command, description, hideOutput, - workingDirectory, + opts, + detached, ) => { - calls.push({ command, description, hideOutput, workingDirectory }); - return { + calls.push({ command, description, hideOutput, opts }); + void detached; + return createMockCommandResponse({ success: true, output: '', error: undefined, - process: { pid: 12345 }, - }; + }); }; await open_simLogic({}, mockExecutor); @@ -157,8 +149,8 @@ describe('open_sim tool', () => { expect(calls[0]).toEqual({ command: ['open', '-a', 'Simulator'], description: 'Open Simulator', - hideOutput: true, - workingDirectory: undefined, + hideOutput: false, + opts: undefined, }); }); }); diff --git a/src/mcp/tools/simulator/__tests__/record_sim_video.test.ts b/src/mcp/tools/simulator/__tests__/record_sim_video.test.ts index fbd8d65e..b02e3345 100644 --- a/src/mcp/tools/simulator/__tests__/record_sim_video.test.ts +++ b/src/mcp/tools/simulator/__tests__/record_sim_video.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; // Import the tool and logic -import tool, { record_sim_videoLogic } from '../record_sim_video.ts'; +import { schema, handler, record_sim_videoLogic } from '../record_sim_video.ts'; import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; const DUMMY_EXECUTOR: any = (async () => ({ success: true })) as any; // CommandExecutor stub @@ -13,7 +13,7 @@ afterEach(() => { describe('record_sim_video tool - validation', () => { it('errors when start and stop are both true (mutually exclusive)', async () => { - const res = await tool.handler({ + const res = await handler({ simulatorId: VALID_SIM_ID, start: true, stop: true, @@ -25,7 +25,7 @@ describe('record_sim_video tool - validation', () => { }); it('errors when stop=true but outputFile is missing', async () => { - const res = await tool.handler({ + const res = await handler({ simulatorId: VALID_SIM_ID, stop: true, } as any); @@ -53,7 +53,7 @@ describe('record_sim_video logic - start behavior', () => { areAxeToolsAvailable: () => true, isAxeAtLeastVersion: async () => true, createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'AXe not available' }], + content: [{ type: 'text' as const, text: 'AXe not available' }], isError: true, }), }; @@ -76,12 +76,14 @@ describe('record_sim_video logic - start behavior', () => { expect(res.isError).toBe(false); const texts = (res.content ?? []).map((c: any) => c.text).join('\n'); - expect(texts).toContain('🎥'); expect(texts).toMatch(/30\s*fps/i); expect(texts.toLowerCase()).toContain('outputfile is ignored'); - expect(texts).toContain('Next Steps'); - expect(texts).toContain('stop: true'); - expect(texts).toContain('outputFile'); + + // Check nextStepParams instead of embedded text + expect(res.nextStepParams).toBeDefined(); + expect(res.nextStepParams?.record_sim_video).toBeDefined(); + expect(res.nextStepParams?.record_sim_video).toHaveProperty('stop', true); + expect(res.nextStepParams?.record_sim_video).toHaveProperty('outputFile'); }); }); @@ -105,7 +107,7 @@ describe('record_sim_video logic - end-to-end stop with rename', () => { areAxeToolsAvailable: () => true, isAxeAtLeastVersion: async () => true, createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'AXe not available' }], + content: [{ type: 'text' as const, text: 'AXe not available' }], isError: true, }), }; @@ -153,7 +155,7 @@ describe('record_sim_video logic - version gate', () => { areAxeToolsAvailable: () => true, isAxeAtLeastVersion: async () => false, createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'AXe not available' }], + content: [{ type: 'text' as const, text: 'AXe not available' }], isError: true, }), }; diff --git a/src/mcp/tools/simulator/__tests__/screenshot.test.ts b/src/mcp/tools/simulator/__tests__/screenshot.test.ts index 3c10dfc3..0f249472 100644 --- a/src/mcp/tools/simulator/__tests__/screenshot.test.ts +++ b/src/mcp/tools/simulator/__tests__/screenshot.test.ts @@ -10,10 +10,12 @@ import { createMockExecutor, createMockFileSystemExecutor, createCommandMatchingMockExecutor, + mockProcess, } from '../../../../test-utils/mock-executors.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; import { SystemError } from '../../../../utils/responses/index.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import screenshotPlugin, { screenshotLogic } from '../../ui-testing/screenshot.ts'; +import { schema, handler, screenshotLogic } from '../../ui-automation/screenshot.ts'; describe('screenshot plugin', () => { beforeEach(() => { @@ -21,26 +23,16 @@ describe('screenshot plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name field', () => { - expect(screenshotPlugin.name).toBe('screenshot'); - }); - - it('should have correct description field', () => { - expect(screenshotPlugin.description).toBe( - "Captures screenshot for visual verification. For UI coordinates, use describe_ui instead (don't determine coordinates from screenshots).", - ); - }); - it('should have handler function', () => { - expect(typeof screenshotPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should have correct schema validation', () => { - const schema = z.object(screenshotPlugin.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: '550e8400-e29b-41d4-a716-446655440000', }); expect(withSimId.success).toBe(true); @@ -49,18 +41,34 @@ describe('screenshot plugin', () => { }); describe('Command Generation', () => { + // Mock device list JSON for proper device name lookup + const mockDeviceListJson = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-17-2': [ + { udid: 'test-uuid', name: 'iPhone 15 Pro', state: 'Booted' }, + { udid: 'another-uuid', name: 'iPhone 15', state: 'Booted' }, + ], + }, + }); + it('should generate correct simctl and sips commands', async () => { const capturedCommands: string[][] = []; - const mockExecutor = createCommandMatchingMockExecutor({ - 'xcrun simctl': { success: true, output: 'Screenshot saved' }, - sips: { success: true, output: 'Image optimized' }, - }); - - // Wrap to capture both commands + // Wrap to capture commands and return appropriate mock responses const capturingExecutor = async (command: string[], ...args: any[]) => { capturedCommands.push(command); - return mockExecutor(command, ...args); + const cmdStr = command.join(' '); + // Return device list JSON for list command + if (cmdStr.includes('simctl list devices')) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Return success for all other commands + return { success: true, output: '', error: undefined, process: mockProcess }; }; const mockFileSystemExecutor = createMockFileSystemExecutor({ @@ -86,8 +94,8 @@ describe('screenshot plugin', () => { mockUuidDeps, ); - // Should execute both commands in sequence - expect(capturedCommands).toHaveLength(2); + // Should execute all commands in sequence: screenshot, list devices, orientation detection, optimization + expect(capturedCommands).toHaveLength(4); // First command: xcrun simctl screenshot expect(capturedCommands[0]).toEqual([ @@ -99,8 +107,17 @@ describe('screenshot plugin', () => { '/tmp/screenshot_mock-uuid-123.png', ]); - // Second command: sips optimization - expect(capturedCommands[1]).toEqual([ + // Second command: xcrun simctl list devices (to get device name) + expect(capturedCommands[1][0]).toBe('xcrun'); + expect(capturedCommands[1][1]).toBe('simctl'); + expect(capturedCommands[1][2]).toBe('list'); + + // Third command: swift orientation detection + expect(capturedCommands[2][0]).toBe('swift'); + expect(capturedCommands[2][1]).toBe('-e'); + + // Fourth command: sips optimization + expect(capturedCommands[3]).toEqual([ 'sips', '-Z', '800', @@ -119,15 +136,21 @@ describe('screenshot plugin', () => { it('should generate correct path with different uuid', async () => { const capturedCommands: string[][] = []; - const mockExecutor = createCommandMatchingMockExecutor({ - 'xcrun simctl': { success: true, output: 'Screenshot saved' }, - sips: { success: true, output: 'Image optimized' }, - }); - - // Wrap to capture both commands + // Wrap to capture commands and return appropriate mock responses const capturingExecutor = async (command: string[], ...args: any[]) => { capturedCommands.push(command); - return mockExecutor(command, ...args); + const cmdStr = command.join(' '); + // Return device list JSON for list command + if (cmdStr.includes('simctl list devices')) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Return success for all other commands + return { success: true, output: '', error: undefined, process: mockProcess }; }; const mockFileSystemExecutor = createMockFileSystemExecutor({ @@ -153,8 +176,8 @@ describe('screenshot plugin', () => { mockUuidDeps, ); - // Should execute both commands in sequence - expect(capturedCommands).toHaveLength(2); + // Should execute all commands in sequence: screenshot, list devices, orientation detection, optimization + expect(capturedCommands).toHaveLength(4); // First command: xcrun simctl screenshot expect(capturedCommands[0]).toEqual([ @@ -166,8 +189,17 @@ describe('screenshot plugin', () => { '/tmp/screenshot_different-uuid-456.png', ]); - // Second command: sips optimization - expect(capturedCommands[1]).toEqual([ + // Second command: xcrun simctl list devices (to get device name) + expect(capturedCommands[1][0]).toBe('xcrun'); + expect(capturedCommands[1][1]).toBe('simctl'); + expect(capturedCommands[1][2]).toBe('list'); + + // Third command: swift orientation detection + expect(capturedCommands[2][0]).toBe('swift'); + expect(capturedCommands[2][1]).toBe('-e'); + + // Fourth command: sips optimization + expect(capturedCommands[3]).toEqual([ 'sips', '-Z', '800', @@ -186,15 +218,21 @@ describe('screenshot plugin', () => { it('should use default dependencies when not provided', async () => { const capturedCommands: string[][] = []; - const mockExecutor = createCommandMatchingMockExecutor({ - 'xcrun simctl': { success: true, output: 'Screenshot saved' }, - sips: { success: true, output: 'Image optimized' }, - }); - - // Wrap to capture both commands + // Wrap to capture commands and return appropriate mock responses const capturingExecutor = async (command: string[], ...args: any[]) => { capturedCommands.push(command); - return mockExecutor(command, ...args); + const cmdStr = command.join(' '); + // Return device list JSON for list command + if (cmdStr.includes('simctl list devices')) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Return success for all other commands + return { success: true, output: '', error: undefined, process: mockProcess }; }; const mockFileSystemExecutor = createMockFileSystemExecutor({ @@ -209,8 +247,8 @@ describe('screenshot plugin', () => { mockFileSystemExecutor, ); - // Should execute both commands in sequence - expect(capturedCommands).toHaveLength(2); + // Should execute all commands in sequence: screenshot, list devices, orientation detection, optimization + expect(capturedCommands).toHaveLength(4); // First command should be generated with real os.tmpdir, path.join, and uuidv4 const firstCommand = capturedCommands[0]; @@ -222,14 +260,23 @@ describe('screenshot plugin', () => { expect(firstCommand[4]).toBe('screenshot'); expect(firstCommand[5]).toMatch(/\/.*\/screenshot_.*\.png/); - // Second command should be sips optimization - const secondCommand = capturedCommands[1]; - expect(secondCommand[0]).toBe('sips'); - expect(secondCommand[1]).toBe('-Z'); - expect(secondCommand[2]).toBe('800'); + // Second command should be xcrun simctl list devices + expect(capturedCommands[1][0]).toBe('xcrun'); + expect(capturedCommands[1][1]).toBe('simctl'); + expect(capturedCommands[1][2]).toBe('list'); + + // Third command should be swift orientation detection + expect(capturedCommands[2][0]).toBe('swift'); + expect(capturedCommands[2][1]).toBe('-e'); + + // Fourth command should be sips optimization + const thirdCommand = capturedCommands[3]; + expect(thirdCommand[0]).toBe('sips'); + expect(thirdCommand[1]).toBe('-Z'); + expect(thirdCommand[2]).toBe('800'); // Should have proper PNG input and JPG output paths - expect(secondCommand[secondCommand.length - 3]).toMatch(/\/.*\/screenshot_.*\.png/); - expect(secondCommand[secondCommand.length - 1]).toMatch(/\/.*\/screenshot_optimized_.*\.jpg/); + expect(thirdCommand[thirdCommand.length - 3]).toMatch(/\/.*\/screenshot_.*\.png/); + expect(thirdCommand[thirdCommand.length - 1]).toMatch(/\/.*\/screenshot_optimized_.*\.jpg/); }); }); @@ -279,7 +326,7 @@ describe('screenshot plugin', () => { }); it('should handle missing simulatorId via handler', async () => { - const result = await screenshotPlugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -371,15 +418,31 @@ describe('screenshot plugin', () => { it('should call correct command with direct execution', async () => { const capturedArgs: any[][] = []; - const mockExecutor = createCommandMatchingMockExecutor({ - 'xcrun simctl': { success: true, output: 'Screenshot saved' }, - sips: { success: true, output: 'Image optimized' }, + // Mock device list JSON for proper device name lookup + const mockDeviceListJson = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-17-2': [ + { udid: 'test-uuid', name: 'iPhone 15 Pro', state: 'Booted' }, + ], + }, }); - // Wrap to capture both command executions - const capturingExecutor = async (...args: any[]) => { + // Capture all command executions and return appropriate mock responses + const capturingExecutor: CommandExecutor = async (...args) => { capturedArgs.push(args); - return mockExecutor(...args); + const command = args[0] as string[]; + const cmdStr = command.join(' '); + // Return device list JSON for list command + if (cmdStr.includes('simctl list devices')) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Return success for all other commands + return { success: true, output: '', error: undefined, process: mockProcess }; }; const mockFileSystemExecutor = createMockFileSystemExecutor({ @@ -405,8 +468,8 @@ describe('screenshot plugin', () => { mockUuidDeps, ); - // Should capture both command executions - expect(capturedArgs).toHaveLength(2); + // Should capture all command executions: screenshot, list devices, orientation detection, optimization + expect(capturedArgs).toHaveLength(4); // First call: xcrun simctl screenshot (3 args: command, logPrefix, useShell) expect(capturedArgs[0]).toEqual([ @@ -415,8 +478,21 @@ describe('screenshot plugin', () => { false, ]); - // Second call: sips optimization (3 args: command, logPrefix, useShell) - expect(capturedArgs[1]).toEqual([ + // Second call: xcrun simctl list devices (to get device name) + expect(capturedArgs[1][0][0]).toBe('xcrun'); + expect(capturedArgs[1][0][1]).toBe('simctl'); + expect(capturedArgs[1][0][2]).toBe('list'); + expect(capturedArgs[1][1]).toBe('[Screenshot]: list devices'); + expect(capturedArgs[1][2]).toBe(false); + + // Third call: swift orientation detection + expect(capturedArgs[2][0][0]).toBe('swift'); + expect(capturedArgs[2][0][1]).toBe('-e'); + expect(capturedArgs[2][1]).toBe('[Screenshot]: detect orientation'); + expect(capturedArgs[2][2]).toBe(false); + + // Fourth call: sips optimization (3 args: command, logPrefix, useShell) + expect(capturedArgs[3]).toEqual([ [ 'sips', '-Z', diff --git a/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts index c8572670..2ad91e7b 100644 --- a/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts @@ -1,8 +1,12 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + createMockExecutor, + createMockCommandResponse, +} from '../../../../test-utils/mock-executors.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import plugin, { stop_app_simLogic } from '../stop_app_sim.ts'; +import { schema, handler, stop_app_simLogic } from '../stop_app_sim.ts'; describe('stop_app_sim tool', () => { beforeEach(() => { @@ -10,23 +14,17 @@ describe('stop_app_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should expose correct metadata', () => { - expect(plugin.name).toBe('stop_app_sim'); - expect(plugin.description).toBe('Stops an app running in an iOS simulator.'); - }); - - it('should expose public schema with only bundleId', () => { - const schema = z.object(plugin.schema); + it('should expose empty public schema', () => { + const schemaObj = z.object(schema); - expect(schema.safeParse({ bundleId: 'com.example.app' }).success).toBe(true); - expect(schema.safeParse({}).success).toBe(false); - expect(schema.safeParse({ bundleId: 42 }).success).toBe(false); - expect(Object.keys(plugin.schema)).toEqual(['bundleId']); + expect(schemaObj.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({ bundleId: 'io.sentry.app' }).success).toBe(true); + expect(schemaObj.safeParse({ bundleId: 42 }).success).toBe(true); + expect(Object.keys(schema)).toEqual([]); - const withSessionDefaults = schema.safeParse({ + const withSessionDefaults = schemaObj.safeParse({ simulatorId: 'SIM-UUID', simulatorName: 'iPhone 16', - bundleId: 'com.example.app', }); expect(withSessionDefaults.success).toBe(true); const parsed = withSessionDefaults.data as Record; @@ -37,7 +35,7 @@ describe('stop_app_sim tool', () => { describe('Handler Requirements', () => { it('should require simulator identifier when not provided', async () => { - const result = await plugin.handler({ bundleId: 'com.example.app' }); + const result = await handler({ bundleId: 'io.sentry.app' }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -45,23 +43,21 @@ describe('stop_app_sim tool', () => { expect(result.content[0].text).toContain('session-set-defaults'); }); - it('should validate bundleId when simulatorId default exists', async () => { + it('should require bundleId when simulatorId default exists', async () => { sessionStore.setDefaults({ simulatorId: 'SIM-UUID' }); - const result = await plugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Parameter validation failed'); - expect(result.content[0].text).toContain( - 'bundleId: Invalid input: expected string, received undefined', - ); + expect(result.content[0].text).toContain('Missing required session defaults'); + expect(result.content[0].text).toContain('bundleId is required'); }); it('should reject mutually exclusive simulator parameters', async () => { - const result = await plugin.handler({ + const result = await handler({ simulatorId: 'SIM-UUID', simulatorName: 'iPhone 16', - bundleId: 'com.example.app', + bundleId: 'io.sentry.app', }); expect(result.isError).toBe(true); @@ -78,7 +74,7 @@ describe('stop_app_sim tool', () => { const result = await stop_app_simLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.App', + bundleId: 'io.sentry.App', }, mockExecutor, ); @@ -87,104 +83,31 @@ describe('stop_app_sim tool', () => { content: [ { type: 'text', - text: '✅ App com.example.App stopped successfully in simulator test-uuid', - }, - ], - }); - }); - - it('should stop app successfully when resolving simulatorName', async () => { - let callCount = 0; - const sequencedExecutor = async (command: string[]) => { - callCount++; - if (callCount === 1) { - return { - success: true, - output: JSON.stringify({ - devices: { - 'iOS 17.0': [ - { name: 'iPhone 16', udid: 'resolved-uuid', isAvailable: true, state: 'Booted' }, - ], - }, - }), - error: '', - process: {} as any, - }; - } - return { - success: true, - output: '', - error: '', - process: {} as any, - }; - }; - - const result = await stop_app_simLogic( - { - simulatorName: 'iPhone 16', - bundleId: 'com.example.App', - }, - sequencedExecutor, - ); - - expect(result).toEqual({ - content: [ - { - type: 'text', - text: '✅ App com.example.App stopped successfully in simulator "iPhone 16" (resolved-uuid)', + text: 'App io.sentry.App stopped successfully in simulator test-uuid', }, ], }); }); - it('should surface error when simulator name is missing', async () => { - const result = await stop_app_simLogic( - { - simulatorName: 'Missing Simulator', - bundleId: 'com.example.App', - }, - async () => ({ - success: true, - output: JSON.stringify({ devices: {} }), - error: '', - process: {} as any, - }), - ); - - expect(result).toEqual({ - content: [ - { - type: 'text', - text: 'Simulator named "Missing Simulator" not found. Use list_sims to see available simulators.', - }, - ], - isError: true, - }); - }); - - it('should handle simulator list command failure', async () => { - const listExecutor = createMockExecutor({ - success: false, - output: '', - error: 'simctl list failed', - }); + it('should display friendly name when simulatorName is provided alongside resolved simulatorId', async () => { + const mockExecutor = createMockExecutor({ success: true, output: '' }); const result = await stop_app_simLogic( { + simulatorId: 'resolved-uuid', simulatorName: 'iPhone 16', - bundleId: 'com.example.App', + bundleId: 'io.sentry.App', }, - listExecutor, + mockExecutor, ); expect(result).toEqual({ content: [ { type: 'text', - text: 'Failed to list simulators: simctl list failed', + text: 'App io.sentry.App stopped successfully in simulator "iPhone 16" (resolved-uuid)', }, ], - isError: true, }); }); @@ -198,7 +121,7 @@ describe('stop_app_sim tool', () => { const result = await stop_app_simLogic( { simulatorId: 'invalid-uuid', - bundleId: 'com.example.App', + bundleId: 'io.sentry.App', }, terminateExecutor, ); @@ -222,7 +145,7 @@ describe('stop_app_sim tool', () => { const result = await stop_app_simLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.App', + bundleId: 'io.sentry.App', }, throwingExecutor, ); @@ -241,40 +164,42 @@ describe('stop_app_sim tool', () => { it('should call correct terminate command', async () => { const calls: Array<{ command: string[]; - description: string; - suppressErrorLogging: boolean; - timeout?: number; + logPrefix?: string; + useShell?: boolean; + opts?: { env?: Record; cwd?: string }; + detached?: boolean; }> = []; - const trackingExecutor = async ( - command: string[], - description: string, - suppressErrorLogging: boolean, - timeout?: number, + const trackingExecutor: CommandExecutor = async ( + command, + logPrefix, + useShell, + opts, + detached, ) => { - calls.push({ command, description, suppressErrorLogging, timeout }); - return { + calls.push({ command, logPrefix, useShell, opts, detached }); + return createMockCommandResponse({ success: true, output: '', error: undefined, - process: { pid: 12345 }, - }; + }); }; await stop_app_simLogic( { simulatorId: 'test-uuid', - bundleId: 'com.example.App', + bundleId: 'io.sentry.App', }, trackingExecutor, ); expect(calls).toEqual([ { - command: ['xcrun', 'simctl', 'terminate', 'test-uuid', 'com.example.App'], - description: 'Stop App in Simulator', - suppressErrorLogging: true, - timeout: undefined, + command: ['xcrun', 'simctl', 'terminate', 'test-uuid', 'io.sentry.App'], + logPrefix: 'Stop App in Simulator', + useShell: false, + opts: undefined, + detached: undefined, }, ]); }); diff --git a/src/mcp/tools/simulator/__tests__/test_sim.test.ts b/src/mcp/tools/simulator/__tests__/test_sim.test.ts index 69bf3ab8..bcb17b16 100644 --- a/src/mcp/tools/simulator/__tests__/test_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/test_sim.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { sessionStore } from '../../../../utils/session-store.ts'; -import testSim from '../test_sim.ts'; +import { schema, handler, test_simLogic } from '../test_sim.ts'; describe('test_sim tool', () => { beforeEach(() => { @@ -14,46 +14,34 @@ describe('test_sim tool', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(testSim.name).toBe('test_sim'); - }); - - it('should have concise description', () => { - expect(testSim.description).toBe('Runs tests on an iOS simulator.'); - }); - it('should have handler function', () => { - expect(typeof testSim.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose only non-session fields in public schema', () => { - const schema = z.object(testSim.schema); + const schemaObj = z.strictObject(schema); - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObj.safeParse({}).success).toBe(true); expect( - schema.safeParse({ - derivedDataPath: '/tmp/derived', + schemaObj.safeParse({ extraArgs: ['--quiet'], - preferXcodebuild: true, testRunnerEnv: { FOO: 'BAR' }, }).success, ).toBe(true); - expect(schema.safeParse({ derivedDataPath: 123 }).success).toBe(false); - expect(schema.safeParse({ extraArgs: ['--ok', 42] }).success).toBe(false); - expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); - expect(schema.safeParse({ testRunnerEnv: { FOO: 123 } }).success).toBe(false); + expect(schemaObj.safeParse({ derivedDataPath: 123 }).success).toBe(false); + expect(schemaObj.safeParse({ extraArgs: ['--ok', 42] }).success).toBe(false); + expect(schemaObj.safeParse({ preferXcodebuild: true }).success).toBe(false); + expect(schemaObj.safeParse({ testRunnerEnv: { FOO: 123 } }).success).toBe(false); - const schemaKeys = Object.keys(testSim.schema).sort(); - expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'testRunnerEnv'].sort(), - ); + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs', 'testRunnerEnv'].sort()); }); }); describe('Handler Requirements', () => { it('should require scheme when not provided', async () => { - const result = await testSim.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('scheme is required'); @@ -62,7 +50,7 @@ describe('test_sim tool', () => { it('should require project or workspace when scheme default exists', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await testSim.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide a project or workspace'); @@ -74,7 +62,7 @@ describe('test_sim tool', () => { projectPath: '/path/to/project.xcodeproj', }); - const result = await testSim.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Provide simulatorId or simulatorName'); @@ -86,7 +74,7 @@ describe('test_sim tool', () => { workspacePath: '/path/to/workspace.xcworkspace', }); - const result = await testSim.handler({ + const result = await handler({ simulatorId: 'SIM-UUID', simulatorName: 'iPhone 16', }); diff --git a/src/mcp/tools/simulator/boot_sim.ts b/src/mcp/tools/simulator/boot_sim.ts index 95c112c1..997050ca 100644 --- a/src/mcp/tools/simulator/boot_sim.ts +++ b/src/mcp/tools/simulator/boot_sim.ts @@ -1,5 +1,5 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; @@ -8,15 +8,33 @@ import { getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; -const bootSimSchemaObject = z.object({ - simulatorId: z.string().describe('UUID of the simulator to use (obtained from list_sims)'), +const baseSchemaObject = z.object({ + simulatorId: z + .string() + .optional() + .describe( + 'UUID of the simulator to use (obtained from list_sims). Provide EITHER this OR simulatorName, not both', + ), + simulatorName: z + .string() + .optional() + .describe( + "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + ), }); -type BootSimParams = z.infer; +// Internal schema requires simulatorId (factory resolves simulatorName → simulatorId) +const internalSchemaObject = z.object({ + simulatorId: z.string(), + simulatorName: z.string().optional(), +}); + +type BootSimParams = z.infer; const publicSchemaObject = z.strictObject( - bootSimSchemaObject.omit({ + baseSchemaObject.omit({ simulatorId: true, + simulatorName: true, } as const).shape, ); @@ -28,7 +46,7 @@ export async function boot_simLogic( try { const command = ['xcrun', 'simctl', 'boot', params.simulatorId]; - const result = await executor(command, 'Boot Simulator', true); + const result = await executor(command, 'Boot Simulator', false); if (!result.success) { return { @@ -45,14 +63,14 @@ export async function boot_simLogic( content: [ { type: 'text', - text: `✅ Simulator booted successfully. To make it visible, use: open_sim() - -Next steps: -1. Open the Simulator app (makes it visible): open_sim() -2. Install an app: install_app_sim({ simulatorId: "${params.simulatorId}", appPath: "PATH_TO_YOUR_APP" }) -3. Launch an app: launch_app_sim({ simulatorId: "${params.simulatorId}", bundleId: "YOUR_APP_BUNDLE_ID" })`, + text: `Simulator booted successfully.`, }, ], + nextStepParams: { + open_sim: {}, + install_app_sim: { simulatorId: params.simulatorId, appPath: 'PATH_TO_YOUR_APP' }, + launch_app_sim: { simulatorId: params.simulatorId, bundleId: 'YOUR_APP_BUNDLE_ID' }, + }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -68,21 +86,17 @@ Next steps: } } -export default { - name: 'boot_sim', - description: 'Boots an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: bootSimSchemaObject, - }), - annotations: { - title: 'Boot Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: bootSimSchemaObject as unknown as z.ZodType, - logicFunction: boot_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: internalSchemaObject as unknown as z.ZodType, + logicFunction: boot_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + ], + exclusivePairs: [['simulatorId', 'simulatorName']], +}); diff --git a/src/mcp/tools/simulator/build_run_sim.ts b/src/mcp/tools/simulator/build_run_sim.ts index 84dba281..b8adab08 100644 --- a/src/mcp/tools/simulator/build_run_sim.ts +++ b/src/mcp/tools/simulator/build_run_sim.ts @@ -7,7 +7,8 @@ */ import * as z from 'zod'; -import { ToolResponse, SharedBuildParams, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse, SharedBuildParams } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { @@ -19,6 +20,8 @@ import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { determineSimulatorUuid } from '../../../utils/simulator-utils.ts'; import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { inferPlatform } from '../../../utils/infer-platform.ts'; +import { constructDestinationString } from '../../../utils/xcode.ts'; // Unified schema: XOR between projectPath and workspacePath, and XOR between simulatorId and simulatorName const baseOptions = { @@ -36,21 +39,13 @@ const baseOptions = { "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", ), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - derivedDataPath: z - .string() - .optional() - .describe('Path where build products and other derived data will go'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), useLatestOS: z .boolean() .optional() .describe('Whether to use the latest OS version for the named simulator'), - preferXcodebuild: z - .boolean() - .optional() - .describe( - 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', - ), + preferXcodebuild: z.boolean().optional(), }; const baseSchemaObject = z.object({ @@ -89,7 +84,7 @@ async function _handleSimulatorBuildLogic( params: BuildRunSimulatorParams, executor: CommandExecutor, executeXcodeBuildCommandFn: typeof executeXcodeBuildCommand = executeXcodeBuildCommand, -): Promise { +): Promise<{ response: ToolResponse; detectedPlatform: XcodePlatform }> { const projectType = params.projectPath ? 'project' : 'workspace'; const filePath = params.projectPath ?? params.workspacePath; @@ -101,10 +96,22 @@ async function _handleSimulatorBuildLogic( ); } - log( - 'info', - `Starting iOS Simulator build for scheme ${params.scheme} from ${projectType}: ${filePath}`, + const inferred = await inferPlatform( + { + projectPath: params.projectPath, + workspacePath: params.workspacePath, + scheme: params.scheme, + simulatorId: params.simulatorId, + simulatorName: params.simulatorName, + }, + executor, ); + const detectedPlatform = inferred.platform; + const platformName = detectedPlatform.replace(' Simulator', ''); + const logPrefix = `${platformName} Simulator Build`; + + log('info', `Starting ${logPrefix} for scheme ${params.scheme} from ${projectType}: ${filePath}`); + log('info', `Inferred simulator platform: ${detectedPlatform} (source: ${inferred.source})`); // Create SharedBuildParams object with required configuration property const sharedBuildParams: SharedBuildParams = { @@ -116,19 +123,21 @@ async function _handleSimulatorBuildLogic( extraArgs: params.extraArgs, }; - return executeXcodeBuildCommandFn( + const response = await executeXcodeBuildCommandFn( sharedBuildParams, { - platform: XcodePlatform.iOSSimulator, + platform: detectedPlatform, simulatorId: params.simulatorId, simulatorName: params.simulatorName, useLatestOS: params.simulatorId ? false : params.useLatestOS, - logPrefix: 'iOS Simulator Build', + logPrefix, }, params.preferXcodebuild as boolean, 'build', executor, ); + + return { response, detectedPlatform }; } // Exported business logic function for building and running iOS Simulator apps. @@ -142,12 +151,12 @@ export async function build_run_simLogic( log( 'info', - `Starting iOS Simulator build and run for scheme ${params.scheme} from ${projectType}: ${filePath}`, + `Starting Simulator build and run for scheme ${params.scheme} from ${projectType}: ${filePath}`, ); try { // --- Build Step --- - const buildResult = await _handleSimulatorBuildLogic( + const { response: buildResult, detectedPlatform } = await _handleSimulatorBuildLogic( params, executor, executeXcodeBuildCommandFn, @@ -157,6 +166,8 @@ export async function build_run_simLogic( return buildResult; // Return the build error } + const platformName = detectedPlatform.replace(' Simulator', ''); + // --- Get App Path Step --- // Create the command array for xcodebuild with -showBuildSettings option const command = ['xcodebuild', '-showBuildSettings']; @@ -175,12 +186,21 @@ export async function build_run_simLogic( // Handle destination for simulator let destinationString: string; if (params.simulatorId) { - destinationString = `platform=iOS Simulator,id=${params.simulatorId}`; + destinationString = constructDestinationString( + detectedPlatform, + undefined, + params.simulatorId, + ); } else if (params.simulatorName) { - destinationString = `platform=iOS Simulator,name=${params.simulatorName}${(params.useLatestOS ?? true) ? ',OS=latest' : ''}`; + destinationString = constructDestinationString( + detectedPlatform, + params.simulatorName, + undefined, + params.useLatestOS ?? true, + ); } else { // This shouldn't happen due to validation, but handle it - destinationString = 'platform=iOS Simulator'; + destinationString = constructDestinationString(detectedPlatform); } command.push('-destination', destinationString); @@ -195,7 +215,7 @@ export async function build_run_simLogic( } // Execute the command directly - const result = await executor(command, 'Get App Path', true, undefined); + const result = await executor(command, 'Get App Path', false, undefined); // If there was an error with the command execution, return it if (!result.success) { @@ -457,7 +477,7 @@ export async function build_run_simLogic( } // --- Success --- - log('info', '✅ iOS simulator build & run succeeded.'); + log('info', `${platformName} simulator build & run succeeded.`); const target = params.simulatorId ? `simulator UUID '${params.simulatorId}'` @@ -469,28 +489,22 @@ export async function build_run_simLogic( content: [ { type: 'text', - text: `✅ iOS simulator build and run succeeded for scheme ${params.scheme} from ${sourceType} ${sourcePath} targeting ${target}. - -The app (${bundleId}) is now running in the iOS Simulator. -If you don't see the simulator window, it may be hidden behind other windows. The Simulator app should be open. - -Next Steps: -- Option 1: Capture structured logs only (app continues running): - start_simulator_log_capture({ simulatorId: '${simulatorId}', bundleId: '${bundleId}' }) -- Option 2: Capture both console and structured logs (app will restart): - start_simulator_log_capture({ simulatorId: '${simulatorId}', bundleId: '${bundleId}', captureConsole: true }) -- Option 3: Launch app with logs in one step (for a fresh start): - launch_app_with_logs_in_simulator({ simulatorId: '${simulatorId}', bundleId: '${bundleId}' }) - -When done with any option, use: stop_sim_log_cap({ logSessionId: 'SESSION_ID' })`, + text: `${platformName} simulator build and run succeeded for scheme ${params.scheme} from ${sourceType} ${sourcePath} targeting ${target}.\n\nThe app (${bundleId}) is now running in the ${platformName} Simulator.\nIf you don't see the simulator window, it may be hidden behind other windows. The Simulator app should be open.`, }, ], + nextStepParams: { + start_sim_log_cap: [ + { simulatorId, bundleId }, + { simulatorId, bundleId, captureConsole: true }, + ], + launch_app_logs_sim: { simulatorId, bundleId }, + }, isError: false, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - log('error', `Error in iOS Simulator build and run: ${errorMessage}`); - return createTextResponse(`Error in iOS Simulator build and run: ${errorMessage}`, true); + log('error', `Error in Simulator build and run: ${errorMessage}`); + return createTextResponse(`Error in Simulator build and run: ${errorMessage}`, true); } } @@ -502,34 +516,26 @@ const publicSchemaObject = baseSchemaObject.omit({ simulatorId: true, simulatorName: true, useLatestOS: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); -export default { - name: 'build_run_sim', - description: 'Builds and runs an app on an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Build Run Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: buildRunSimulatorSchema as unknown as z.ZodType< - BuildRunSimulatorParams, - unknown - >, - logicFunction: build_run_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, - ], - exclusivePairs: [ - ['projectPath', 'workspacePath'], - ['simulatorId', 'simulatorName'], - ], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: buildRunSimulatorSchema as unknown as z.ZodType, + logicFunction: build_run_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + ], + exclusivePairs: [ + ['projectPath', 'workspacePath'], + ['simulatorId', 'simulatorName'], + ], +}); diff --git a/src/mcp/tools/simulator/build_sim.ts b/src/mcp/tools/simulator/build_sim.ts index 2280b3ec..3eef8b42 100644 --- a/src/mcp/tools/simulator/build_sim.ts +++ b/src/mcp/tools/simulator/build_sim.ts @@ -9,7 +9,7 @@ import * as z from 'zod'; import { log } from '../../../utils/logging/index.ts'; import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; -import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { @@ -17,6 +17,7 @@ import { getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { inferPlatform } from '../../../utils/infer-platform.ts'; // Unified schema: XOR between projectPath and workspacePath, and XOR between simulatorId and simulatorName const baseOptions = { @@ -34,21 +35,13 @@ const baseOptions = { "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", ), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - derivedDataPath: z - .string() - .optional() - .describe('Path where build products and other derived data will go'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), useLatestOS: z .boolean() .optional() .describe('Whether to use the latest OS version for the named simulator'), - preferXcodebuild: z - .boolean() - .optional() - .describe( - 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', - ), + preferXcodebuild: z.boolean().optional(), }; const baseSchemaObject = z.object({ @@ -98,10 +91,22 @@ async function _handleSimulatorBuildLogic( ); } - log( - 'info', - `Starting iOS Simulator build for scheme ${params.scheme} from ${projectType}: ${filePath}`, + const inferred = await inferPlatform( + { + projectPath: params.projectPath, + workspacePath: params.workspacePath, + scheme: params.scheme, + simulatorId: params.simulatorId, + simulatorName: params.simulatorName, + }, + executor, ); + const detectedPlatform = inferred.platform; + const platformName = detectedPlatform.replace(' Simulator', ''); + const logPrefix = `${platformName} Simulator Build`; + + log('info', `Starting ${logPrefix} for scheme ${params.scheme} from ${projectType}: ${filePath}`); + log('info', `Inferred simulator platform: ${detectedPlatform} (source: ${inferred.source})`); // Ensure configuration has a default value for SharedBuildParams compatibility const sharedBuildParams = { @@ -113,11 +118,11 @@ async function _handleSimulatorBuildLogic( return executeXcodeBuildCommand( sharedBuildParams, { - platform: XcodePlatform.iOSSimulator, + platform: detectedPlatform, simulatorName: params.simulatorName, simulatorId: params.simulatorId, useLatestOS: params.simulatorId ? false : params.useLatestOS, // Ignore useLatestOS with ID - logPrefix: 'iOS Simulator Build', + logPrefix, }, params.preferXcodebuild ?? false, 'build', @@ -149,31 +154,26 @@ const publicSchemaObject = baseSchemaObject.omit({ simulatorId: true, simulatorName: true, useLatestOS: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); -export default { - name: 'build_sim', - description: 'Builds an app for an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), // MCP SDK compatibility (public inputs only) - annotations: { - title: 'Build Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: buildSimulatorSchema as unknown as z.ZodType, - logicFunction: build_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, - ], - exclusivePairs: [ - ['projectPath', 'workspacePath'], - ['simulatorId', 'simulatorName'], - ], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: buildSimulatorSchema as unknown as z.ZodType, + logicFunction: build_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + ], + exclusivePairs: [ + ['projectPath', 'workspacePath'], + ['simulatorId', 'simulatorName'], + ], +}); diff --git a/src/mcp/tools/simulator/clean.ts b/src/mcp/tools/simulator/clean.ts deleted file mode 100644 index 76494c98..00000000 --- a/src/mcp/tools/simulator/clean.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified clean tool for simulator-project workflow -export { default } from '../utilities/clean.ts'; diff --git a/src/mcp/tools/simulator/describe_ui.ts b/src/mcp/tools/simulator/describe_ui.ts deleted file mode 100644 index 3208a22d..00000000 --- a/src/mcp/tools/simulator/describe_ui.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from ui-testing to avoid duplication -export { default } from '../ui-testing/describe_ui.ts'; diff --git a/src/mcp/tools/simulator/discover_projs.ts b/src/mcp/tools/simulator/discover_projs.ts deleted file mode 100644 index 58fbf05d..00000000 --- a/src/mcp/tools/simulator/discover_projs.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/discover_projs.ts'; diff --git a/src/mcp/tools/simulator/get_app_bundle_id.ts b/src/mcp/tools/simulator/get_app_bundle_id.ts deleted file mode 100644 index 6c0bfc0d..00000000 --- a/src/mcp/tools/simulator/get_app_bundle_id.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/get_app_bundle_id.ts'; diff --git a/src/mcp/tools/simulator/get_sim_app_path.ts b/src/mcp/tools/simulator/get_sim_app_path.ts index e111feed..27d103cb 100644 --- a/src/mcp/tools/simulator/get_sim_app_path.ts +++ b/src/mcp/tools/simulator/get_sim_app_path.ts @@ -11,75 +11,21 @@ import { log } from '../../../utils/logging/index.ts'; import { createTextResponse } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; +import { constructDestinationString } from '../../../utils/xcode.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; -const XcodePlatform = { - macOS: 'macOS', - iOS: 'iOS', - iOSSimulator: 'iOS Simulator', - watchOS: 'watchOS', - watchOSSimulator: 'watchOS Simulator', - tvOS: 'tvOS', - tvOSSimulator: 'tvOS Simulator', - visionOS: 'visionOS', - visionOSSimulator: 'visionOS Simulator', -}; - -function constructDestinationString( - platform: string, - simulatorName: string, - simulatorId: string, - useLatest: boolean = true, - arch?: string, -): string { - const isSimulatorPlatform = [ - XcodePlatform.iOSSimulator, - XcodePlatform.watchOSSimulator, - XcodePlatform.tvOSSimulator, - XcodePlatform.visionOSSimulator, - ].includes(platform); - - // If ID is provided for a simulator, it takes precedence and uniquely identifies it. - if (isSimulatorPlatform && simulatorId) { - return `platform=${platform},id=${simulatorId}`; - } - - // If name is provided for a simulator - if (isSimulatorPlatform && simulatorName) { - return `platform=${platform},name=${simulatorName}${useLatest ? ',OS=latest' : ''}`; - } - - // If it's a simulator platform but neither ID nor name is provided (should be prevented by callers now) - if (isSimulatorPlatform && !simulatorId && !simulatorName) { - log( - 'warning', - `Constructing generic destination for ${platform} without name or ID. This might not be specific enough.`, - ); - throw new Error(`Simulator name or ID is required for specific ${platform} operations`); - } - - // Handle non-simulator platforms - switch (platform) { - case XcodePlatform.macOS: - return arch ? `platform=macOS,arch=${arch}` : 'platform=macOS'; - case XcodePlatform.iOS: - return 'generic/platform=iOS'; - case XcodePlatform.watchOS: - return 'generic/platform=watchOS'; - case XcodePlatform.tvOS: - return 'generic/platform=tvOS'; - case XcodePlatform.visionOS: - return 'generic/platform=visionOS'; - } - // Fallback just in case (shouldn't be reached with enum) - log('error', `Reached unexpected point in constructDestinationString for platform: ${platform}`); - return `platform=${platform}`; -} +const SIMULATOR_PLATFORMS = [ + XcodePlatform.iOSSimulator, + XcodePlatform.watchOSSimulator, + XcodePlatform.tvOSSimulator, + XcodePlatform.visionOSSimulator, +] as const; // Define base schema const baseGetSimulatorAppPathSchema = z.object({ @@ -92,9 +38,7 @@ const baseGetSimulatorAppPathSchema = z.object({ .optional() .describe('Path to .xcworkspace file. Provide EITHER this OR projectPath, not both'), scheme: z.string().describe('The scheme to use (Required)'), - platform: z - .enum(['iOS Simulator', 'watchOS Simulator', 'tvOS Simulator', 'visionOS Simulator']) - .describe('Target simulator platform (Required)'), + platform: z.enum(SIMULATOR_PLATFORMS), simulatorId: z .string() .optional() @@ -112,7 +56,6 @@ const baseGetSimulatorAppPathSchema = z.object({ .boolean() .optional() .describe('Whether to use the latest OS version for the named simulator'), - arch: z.string().optional().describe('Optional architecture'), }); // Add XOR validation with preprocessing @@ -152,7 +95,6 @@ export async function get_sim_app_pathLogic( const simulatorName = params.simulatorName; const configuration = params.configuration ?? 'Debug'; const useLatestOS = params.useLatestOS ?? true; - const arch = params.arch; // Log warning if useLatestOS is provided with simulatorId if (simulatorId && params.useLatestOS !== undefined) { @@ -179,45 +121,29 @@ export async function get_sim_app_pathLogic( command.push('-scheme', scheme); command.push('-configuration', configuration); - // Handle destination based on platform - const isSimulatorPlatform = [ - XcodePlatform.iOSSimulator, - XcodePlatform.watchOSSimulator, - XcodePlatform.tvOSSimulator, - XcodePlatform.visionOSSimulator, - ].includes(platform); - + // Handle destination for simulator platforms let destinationString = ''; - if (isSimulatorPlatform) { - if (simulatorId) { - destinationString = `platform=${platform},id=${simulatorId}`; - } else if (simulatorName) { - destinationString = `platform=${platform},name=${simulatorName}${(simulatorId ? false : useLatestOS) ? ',OS=latest' : ''}`; - } else { - return createTextResponse( - `For ${platform} platform, either simulatorId or simulatorName must be provided`, - true, - ); - } - } else if (platform === XcodePlatform.macOS) { - destinationString = constructDestinationString(platform, '', '', false, arch); - } else if (platform === XcodePlatform.iOS) { - destinationString = 'generic/platform=iOS'; - } else if (platform === XcodePlatform.watchOS) { - destinationString = 'generic/platform=watchOS'; - } else if (platform === XcodePlatform.tvOS) { - destinationString = 'generic/platform=tvOS'; - } else if (platform === XcodePlatform.visionOS) { - destinationString = 'generic/platform=visionOS'; + if (simulatorId) { + destinationString = constructDestinationString(platform, undefined, simulatorId); + } else if (simulatorName) { + destinationString = constructDestinationString( + platform, + simulatorName, + undefined, + useLatestOS, + ); } else { - return createTextResponse(`Unsupported platform: ${platform}`, true); + return createTextResponse( + `For ${platform} platform, either simulatorId or simulatorName must be provided`, + true, + ); } command.push('-destination', destinationString); // Execute the command directly - const result = await executor(command, 'Get App Path', true, undefined); + const result = await executor(command, 'Get App Path', false, undefined); if (!result.success) { return createTextResponse(`Failed to get app path: ${result.error}`, true); @@ -242,35 +168,12 @@ export async function get_sim_app_pathLogic( const fullProductName = fullProductNameMatch[1].trim(); const appPath = `${builtProductsDir}/${fullProductName}`; - let nextStepsText = ''; - if (platform === XcodePlatform.macOS) { - nextStepsText = `Next Steps: -1. Get bundle ID: get_mac_bundle_id({ appPath: "${appPath}" }) -2. Launch the app: launch_mac_app({ appPath: "${appPath}" })`; - } else if (isSimulatorPlatform) { - nextStepsText = `Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${appPath}" }) -2. Boot simulator: boot_sim({ simulatorId: "SIMULATOR_UUID" }) -3. Install app: install_app_sim({ simulatorId: "SIMULATOR_UUID", appPath: "${appPath}" }) -4. Launch app: launch_app_sim({ simulatorId: "SIMULATOR_UUID", bundleId: "BUNDLE_ID" })`; - } else if ( - [ - XcodePlatform.iOS, - XcodePlatform.watchOS, - XcodePlatform.tvOS, - XcodePlatform.visionOS, - ].includes(platform) - ) { - nextStepsText = `Next Steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "${appPath}" }) -2. Install app on device: install_app_device({ deviceId: "DEVICE_UDID", appPath: "${appPath}" }) -3. Launch app on device: launch_app_device({ deviceId: "DEVICE_UDID", bundleId: "BUNDLE_ID" })`; - } else { - // For other platforms - nextStepsText = `Next Steps: -1. The app has been built for ${platform} -2. Use platform-specific deployment tools to install and run the app`; - } + const nextStepParams: Record> = { + get_app_bundle_id: { appPath }, + boot_sim: { simulatorId: 'SIMULATOR_UUID' }, + install_app_sim: { simulatorId: 'SIMULATOR_UUID', appPath }, + launch_app_sim: { simulatorId: 'SIMULATOR_UUID', bundleId: 'BUNDLE_ID' }, + }; return { content: [ @@ -278,11 +181,8 @@ export async function get_sim_app_pathLogic( type: 'text', text: `✅ App path retrieved successfully: ${appPath}`, }, - { - type: 'text', - text: nextStepsText, - }, ], + nextStepParams, isError: false, }; } catch (error) { @@ -300,32 +200,24 @@ const publicSchemaObject = baseGetSimulatorAppPathSchema.omit({ simulatorName: true, configuration: true, useLatestOS: true, - arch: true, } as const); -export default { - name: 'get_sim_app_path', - description: 'Retrieves the built app path for an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseGetSimulatorAppPathSchema, - }), - annotations: { - title: 'Get Simulator App Path', - readOnlyHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: getSimulatorAppPathSchema as unknown as z.ZodType, - logicFunction: get_sim_app_pathLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, - ], - exclusivePairs: [ - ['projectPath', 'workspacePath'], - ['simulatorId', 'simulatorName'], - ], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseGetSimulatorAppPathSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: getSimulatorAppPathSchema as unknown as z.ZodType, + logicFunction: get_sim_app_pathLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + ], + exclusivePairs: [ + ['projectPath', 'workspacePath'], + ['simulatorId', 'simulatorName'], + ], +}); diff --git a/src/mcp/tools/simulator/index.ts b/src/mcp/tools/simulator/index.ts deleted file mode 100644 index 51c14874..00000000 --- a/src/mcp/tools/simulator/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'iOS Simulator Development', - description: - 'Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators.', -}; diff --git a/src/mcp/tools/simulator/install_app_sim.ts b/src/mcp/tools/simulator/install_app_sim.ts index 6d515dd1..ba0b297d 100644 --- a/src/mcp/tools/simulator/install_app_sim.ts +++ b/src/mcp/tools/simulator/install_app_sim.ts @@ -1,5 +1,5 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { validateFileExists } from '../../../utils/validation/index.ts'; import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; @@ -9,18 +9,35 @@ import { getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; -const installAppSimSchemaObject = z.object({ - simulatorId: z.string().describe('UUID of the simulator to use (obtained from list_sims)'), - appPath: z +const baseSchemaObject = z.object({ + simulatorId: z .string() - .describe('Path to the .app bundle to install (full path to the .app directory)'), + .optional() + .describe( + 'UUID of the simulator to use (obtained from list_sims). Provide EITHER this OR simulatorName, not both', + ), + simulatorName: z + .string() + .optional() + .describe( + "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + ), + appPath: z.string().describe('Path to the .app bundle to install'), +}); + +// Internal schema requires simulatorId (factory resolves simulatorName → simulatorId) +const internalSchemaObject = z.object({ + simulatorId: z.string(), + simulatorName: z.string().optional(), + appPath: z.string(), }); -type InstallAppSimParams = z.infer; +type InstallAppSimParams = z.infer; const publicSchemaObject = z.strictObject( - installAppSimSchemaObject.omit({ + baseSchemaObject.omit({ simulatorId: true, + simulatorName: true, } as const).shape, ); @@ -38,7 +55,7 @@ export async function install_app_simLogic( try { const command = ['xcrun', 'simctl', 'install', params.simulatorId, params.appPath]; - const result = await executor(command, 'Install App in Simulator', true, undefined); + const result = await executor(command, 'Install App in Simulator', false, undefined); if (!result.success) { return { @@ -70,17 +87,16 @@ export async function install_app_simLogic( content: [ { type: 'text', - text: `App installed successfully in simulator ${params.simulatorId}`, - }, - { - type: 'text', - text: `Next Steps: -1. Open the Simulator app: open_sim({}) -2. Launch the app: launch_app_sim({ simulatorId: "${params.simulatorId}"${ - bundleId ? `, bundleId: "${bundleId}"` : ', bundleId: "YOUR_APP_BUNDLE_ID"' - } })`, + text: `App installed successfully in simulator ${params.simulatorId}.`, }, ], + nextStepParams: { + open_sim: {}, + launch_app_sim: { + simulatorId: params.simulatorId, + bundleId: bundleId || 'YOUR_APP_BUNDLE_ID', + }, + }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -96,21 +112,17 @@ export async function install_app_simLogic( } } -export default { - name: 'install_app_sim', - description: 'Installs an app in an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: installAppSimSchemaObject, - }), - annotations: { - title: 'Install App Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: installAppSimSchemaObject as unknown as z.ZodType, - logicFunction: install_app_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: internalSchemaObject as unknown as z.ZodType, + logicFunction: install_app_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + ], + exclusivePairs: [['simulatorId', 'simulatorName']], +}); diff --git a/src/mcp/tools/simulator/launch_app_logs_sim.ts b/src/mcp/tools/simulator/launch_app_logs_sim.ts index 991acb2d..fb5a5c7a 100644 --- a/src/mcp/tools/simulator/launch_app_logs_sim.ts +++ b/src/mcp/tools/simulator/launch_app_logs_sim.ts @@ -1,5 +1,6 @@ import * as z from 'zod'; -import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createTextContent } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { startLogCapture } from '../../../utils/log-capture/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; @@ -15,23 +16,55 @@ export type LogCaptureFunction = ( bundleId: string; captureConsole?: boolean; args?: string[]; + env?: Record; }, executor: CommandExecutor, ) => Promise<{ sessionId: string; logFilePath: string; processes: unknown[]; error?: string }>; -const launchAppLogsSimSchemaObject = z.object({ - simulatorId: z.string().describe('UUID of the simulator to use (obtained from list_sims)'), - bundleId: z +const baseSchemaObject = z.object({ + simulatorId: z .string() - .describe("Bundle identifier of the app to launch (e.g., 'com.example.MyApp')"), - args: z.array(z.string()).optional().describe('Additional arguments to pass to the app'), + .optional() + .describe( + 'UUID of the simulator to use (obtained from list_sims). Provide EITHER this OR simulatorName, not both', + ), + simulatorName: z + .string() + .optional() + .describe( + "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + ), + bundleId: z.string().describe('Bundle identifier of the app to launch'), + args: z.array(z.string()).optional().describe('Optional arguments to pass to the app'), + env: z + .record(z.string(), z.string()) + .optional() + .describe( + 'Environment variables to pass to the launched app (SIMCTL_CHILD_ prefix added automatically)', + ), +}); + +// Internal schema requires simulatorId (factory resolves simulatorName → simulatorId) +const internalSchemaObject = z.object({ + simulatorId: z.string(), + simulatorName: z.string().optional(), + bundleId: z.string(), + args: z.array(z.string()).optional(), + env: z + .record(z.string(), z.string()) + .optional() + .describe( + 'Environment variables to pass to the launched app (SIMCTL_CHILD_ prefix added automatically)', + ), }); -type LaunchAppLogsSimParams = z.infer; +type LaunchAppLogsSimParams = z.infer; const publicSchemaObject = z.strictObject( - launchAppLogsSimSchemaObject.omit({ + baseSchemaObject.omit({ simulatorId: true, + simulatorName: true, + bundleId: true, } as const).shape, ); @@ -46,13 +79,14 @@ export async function launch_app_logs_simLogic( simulatorUuid: params.simulatorId, bundleId: params.bundleId, captureConsole: true, - ...(params.args && params.args.length > 0 ? { args: params.args } : {}), - } as const; + args: params.args?.length ? params.args : undefined, + env: params.env, + }; const { sessionId, error } = await logCaptureFunction(captureParams, executor); if (error) { return { - content: [createTextContent(`App was launched but log capture failed: ${error}`)], + content: [createTextContent(`Failed to launch app with log capture: ${error}`)], isError: true, }; } @@ -60,31 +94,28 @@ export async function launch_app_logs_simLogic( return { content: [ createTextContent( - `App launched successfully in simulator ${params.simulatorId} with log capture enabled.\n\nLog capture session ID: ${sessionId}\n\nNext Steps:\n1. Interact with your app in the simulator.\n2. Use 'stop_and_get_simulator_log({ logSessionId: "${sessionId}" })' to stop capture and retrieve logs.`, + `App launched successfully in simulator ${params.simulatorId} with log capture enabled.\n\nLog capture session ID: ${sessionId}\n\nInteract with your app in the simulator, then stop capture to retrieve logs.`, ), ], + nextStepParams: { + stop_sim_log_cap: { logSessionId: sessionId }, + }, isError: false, }; } -export default { - name: 'launch_app_logs_sim', - description: 'Launches an app in an iOS simulator and captures its logs.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: launchAppLogsSimSchemaObject, - }), - annotations: { - title: 'Launch App Logs Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: launchAppLogsSimSchemaObject as unknown as z.ZodType< - LaunchAppLogsSimParams, - unknown - >, - logicFunction: launch_app_logs_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: internalSchemaObject as unknown as z.ZodType, + logicFunction: launch_app_logs_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + { allOf: ['bundleId'], message: 'bundleId is required' }, + ], + exclusivePairs: [['simulatorId', 'simulatorName']], +}); diff --git a/src/mcp/tools/simulator/launch_app_sim.ts b/src/mcp/tools/simulator/launch_app_sim.ts index d3f18bdf..3f4a1c0f 100644 --- a/src/mcp/tools/simulator/launch_app_sim.ts +++ b/src/mcp/tools/simulator/launch_app_sim.ts @@ -1,13 +1,13 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { normalizeSimctlChildEnv } from '../../../utils/environment.ts'; const baseSchemaObject = z.object({ simulatorId: z @@ -22,93 +22,40 @@ const baseSchemaObject = z.object({ .describe( "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", ), - bundleId: z - .string() - .describe("Bundle identifier of the app to launch (e.g., 'com.example.MyApp')"), - args: z.array(z.string()).optional().describe('Additional arguments to pass to the app'), + bundleId: z.string().describe('Bundle identifier of the app to launch'), + args: z.array(z.string()).optional().describe('Optional arguments to pass to the app'), + env: z + .record(z.string(), z.string()) + .optional() + .describe( + 'Environment variables to pass to the launched app (SIMCTL_CHILD_ prefix added automatically)', + ), }); -const launchAppSimSchema = z.preprocess( - nullifyEmptyStrings, - baseSchemaObject - .refine((val) => val.simulatorId !== undefined || val.simulatorName !== undefined, { - message: 'Either simulatorId or simulatorName is required.', - }) - .refine((val) => !(val.simulatorId !== undefined && val.simulatorName !== undefined), { - message: 'simulatorId and simulatorName are mutually exclusive. Provide only one.', - }), -); +// Internal schema requires simulatorId (factory resolves simulatorName → simulatorId) +const internalSchemaObject = z.object({ + simulatorId: z.string(), + simulatorName: z.string().optional(), + bundleId: z.string(), + args: z.array(z.string()).optional(), + env: z + .record(z.string(), z.string()) + .optional() + .describe( + 'Environment variables to pass to the launched app (SIMCTL_CHILD_ prefix added automatically)', + ), +}); -export type LaunchAppSimParams = z.infer; +export type LaunchAppSimParams = z.infer; export async function launch_app_simLogic( params: LaunchAppSimParams, executor: CommandExecutor, ): Promise { - let simulatorId = params.simulatorId; - let simulatorDisplayName = simulatorId ?? ''; - - if (params.simulatorName && !simulatorId) { - log('info', `Looking up simulator by name: ${params.simulatorName}`); - - const simulatorListResult = await executor( - ['xcrun', 'simctl', 'list', 'devices', 'available', '--json'], - 'List Simulators', - true, - ); - if (!simulatorListResult.success) { - return { - content: [ - { - type: 'text', - text: `Failed to list simulators: ${simulatorListResult.error}`, - }, - ], - isError: true, - }; - } - - const simulatorsData = JSON.parse(simulatorListResult.output) as { - devices: Record>; - }; - - let foundSimulator: { udid: string; name: string } | null = null; - for (const runtime in simulatorsData.devices) { - const devices = simulatorsData.devices[runtime]; - const simulator = devices.find((device) => device.name === params.simulatorName); - if (simulator) { - foundSimulator = simulator; - break; - } - } - - if (!foundSimulator) { - return { - content: [ - { - type: 'text', - text: `Simulator named "${params.simulatorName}" not found. Use list_sims to see available simulators.`, - }, - ], - isError: true, - }; - } - - simulatorId = foundSimulator.udid; - simulatorDisplayName = `"${params.simulatorName}" (${foundSimulator.udid})`; - } - - if (!simulatorId) { - return { - content: [ - { - type: 'text', - text: 'No simulator identifier provided', - }, - ], - isError: true, - }; - } + const simulatorId = params.simulatorId; + const simulatorDisplayName = params.simulatorName + ? `"${params.simulatorName}" (${simulatorId})` + : simulatorId; log('info', `Starting xcrun simctl launch request for simulator ${simulatorId}`); @@ -124,7 +71,7 @@ export async function launch_app_simLogic( const getAppContainerResult = await executor( getAppContainerCmd, 'Check App Installed', - true, + false, undefined, ); if (!getAppContainerResult.success) { @@ -156,7 +103,8 @@ export async function launch_app_simLogic( command.push(...params.args); } - const result = await executor(command, 'Launch App in Simulator', true, undefined); + const execOpts = params.env ? { env: normalizeSimctlChildEnv(params.env) } : undefined; + const result = await executor(command, 'Launch App in Simulator', false, execOpts); if (!result.success) { return { @@ -169,16 +117,20 @@ export async function launch_app_simLogic( }; } - const userParamName = params.simulatorId ? 'simulatorId' : 'simulatorName'; - const userParamValue = params.simulatorId ?? params.simulatorName ?? simulatorId; - return { content: [ { type: 'text', - text: `✅ App launched successfully in simulator ${simulatorDisplayName || simulatorId}.\n\nNext Steps:\n1. To see simulator: open_sim()\n2. Log capture: start_sim_log_cap({ ${userParamName}: "${userParamValue}", bundleId: "${params.bundleId}" })\n With console: start_sim_log_cap({ ${userParamName}: "${userParamValue}", bundleId: "${params.bundleId}", captureConsole: true })\n3. Stop logs: stop_sim_log_cap({ logSessionId: 'SESSION_ID' })`, + text: `App launched successfully in simulator ${simulatorDisplayName}.`, }, ], + nextStepParams: { + open_sim: {}, + start_sim_log_cap: [ + { simulatorId, bundleId: params.bundleId }, + { simulatorId, bundleId: params.bundleId, captureConsole: true }, + ], + }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -198,27 +150,22 @@ const publicSchemaObject = z.strictObject( baseSchemaObject.omit({ simulatorId: true, simulatorName: true, + bundleId: true, } as const).shape, ); -export default { - name: 'launch_app_sim', - description: 'Launches an app in an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Launch App Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: launchAppSimSchema as unknown as z.ZodType, - logicFunction: launch_app_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, - ], - exclusivePairs: [['simulatorId', 'simulatorName']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: internalSchemaObject as unknown as z.ZodType, + logicFunction: launch_app_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + { allOf: ['bundleId'], message: 'bundleId is required' }, + ], + exclusivePairs: [['simulatorId', 'simulatorName']], +}); diff --git a/src/mcp/tools/simulator/list_schemes.ts b/src/mcp/tools/simulator/list_schemes.ts deleted file mode 100644 index 1ecdf67f..00000000 --- a/src/mcp/tools/simulator/list_schemes.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified list_schemes tool for simulator-project workflow -export { default } from '../project-discovery/list_schemes.ts'; diff --git a/src/mcp/tools/simulator/list_sims.ts b/src/mcp/tools/simulator/list_sims.ts index ac2b1282..c4f10985 100644 --- a/src/mcp/tools/simulator/list_sims.ts +++ b/src/mcp/tools/simulator/list_sims.ts @@ -7,7 +7,7 @@ import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const listSimsSchema = z.object({ - enabled: z.boolean().optional().describe('Optional flag to enable the listing operation.'), + enabled: z.boolean().optional(), }); // Use z.infer for type safety @@ -108,7 +108,7 @@ export async function list_simsLogic( try { // Try JSON first for structured data const jsonCommand = ['xcrun', 'simctl', 'list', 'devices', '--json']; - const jsonResult = await executor(jsonCommand, 'List Simulators (JSON)', true); + const jsonResult = await executor(jsonCommand, 'List Simulators (JSON)', false); if (!jsonResult.success) { return { @@ -134,7 +134,7 @@ export async function list_simsLogic( // Fallback to text parsing for Apple simctl bugs (duplicate runtime IDs in iOS 26.0 beta) const textCommand = ['xcrun', 'simctl', 'list', 'devices']; - const textResult = await executor(textCommand, 'List Simulators (Text)', true); + const textResult = await executor(textCommand, 'List Simulators (Text)', false); const textDevices = textResult.success ? parseTextOutput(textResult.output) : []; @@ -183,13 +183,8 @@ export async function list_simsLogic( responseText += '\n'; } - responseText += 'Next Steps:\n'; - responseText += "1. Boot a simulator: boot_sim({ simulatorId: 'UUID_FROM_ABOVE' })\n"; - responseText += '2. Open the simulator UI: open_sim({})\n'; responseText += - "3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' })\n"; - responseText += - "4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })"; + "Hint: Save a default simulator with session-set-defaults { simulatorId: 'UUID_FROM_ABOVE' } (or simulatorName)."; return { content: [ @@ -198,6 +193,16 @@ export async function list_simsLogic( text: responseText, }, ], + nextStepParams: { + boot_sim: { simulatorId: 'UUID_FROM_ABOVE' }, + open_sim: {}, + build_sim: { scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' }, + get_sim_app_path: { + scheme: 'YOUR_SCHEME', + platform: 'iOS Simulator', + simulatorId: 'UUID_FROM_ABOVE', + }, + }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -213,13 +218,6 @@ export async function list_simsLogic( } } -export default { - name: 'list_sims', - description: 'Lists available iOS simulators with their UUIDs. ', - schema: listSimsSchema.shape, // MCP SDK compatibility - annotations: { - title: 'List Simulators', - readOnlyHint: true, - }, - handler: createTypedTool(listSimsSchema, list_simsLogic, getDefaultCommandExecutor), -}; +export const schema = listSimsSchema.shape; + +export const handler = createTypedTool(listSimsSchema, list_simsLogic, getDefaultCommandExecutor); diff --git a/src/mcp/tools/simulator/open_sim.ts b/src/mcp/tools/simulator/open_sim.ts index 65cd12dd..cfde7a7b 100644 --- a/src/mcp/tools/simulator/open_sim.ts +++ b/src/mcp/tools/simulator/open_sim.ts @@ -1,5 +1,5 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; @@ -19,7 +19,7 @@ export async function open_simLogic( try { const command = ['open', '-a', 'Simulator']; - const result = await executor(command, 'Open Simulator', true); + const result = await executor(command, 'Open Simulator', false); if (!result.success) { return { @@ -36,22 +36,17 @@ export async function open_simLogic( content: [ { type: 'text', - text: `Simulator app opened successfully`, - }, - { - type: 'text', - text: `Next Steps: -1. Boot a simulator if needed: boot_sim({ simulatorId: 'UUID_FROM_LIST_SIMULATORS' }) -2. Launch your app and interact with it -3. Log capture options: - - Option 1: Capture structured logs only (app continues running): - start_sim_log_cap({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' }) - - Option 2: Capture both console and structured logs (app will restart): - start_sim_log_cap({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID', captureConsole: true }) - - Option 3: Launch app with logs in one step: - launch_app_logs_sim({ simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' })`, + text: `Simulator app opened successfully.`, }, ], + nextStepParams: { + boot_sim: { simulatorId: 'UUID_FROM_LIST_SIMS' }, + start_sim_log_cap: [ + { simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' }, + { simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID', captureConsole: true }, + ], + launch_app_logs_sim: { simulatorId: 'UUID', bundleId: 'YOUR_APP_BUNDLE_ID' }, + }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -67,13 +62,6 @@ export async function open_simLogic( } } -export default { - name: 'open_sim', - description: 'Opens the iOS Simulator app.', - schema: openSimSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Open Simulator', - destructiveHint: true, - }, - handler: createTypedTool(openSimSchema, open_simLogic, getDefaultCommandExecutor), -}; +export const schema = openSimSchema.shape; + +export const handler = createTypedTool(openSimSchema, open_simLogic, getDefaultCommandExecutor); diff --git a/src/mcp/tools/simulator/record_sim_video.ts b/src/mcp/tools/simulator/record_sim_video.ts index 4d416f60..9ec863a3 100644 --- a/src/mcp/tools/simulator/record_sim_video.ts +++ b/src/mcp/tools/simulator/record_sim_video.ts @@ -26,13 +26,10 @@ const recordSimVideoSchemaObject = z.object({ simulatorId: z .uuid({ message: 'Invalid Simulator UUID format' }) .describe('UUID of the simulator to record'), - start: z.boolean().optional().describe('Start recording if true'), - stop: z.boolean().optional().describe('Stop recording if true'), - fps: z.number().int().min(1).max(120).optional().describe('Frames per second (default 30)'), - outputFile: z - .string() - .optional() - .describe('Destination MP4 path to move the recorded video to on stop'), + start: z.boolean().optional(), + stop: z.boolean().optional(), + fps: z.number().int().min(1).max(120).optional().describe('default: 30'), + outputFile: z.string().optional().describe('Path to write MP4 file'), }); // Schema enforcing mutually exclusive start/stop and requiring outputFile on stop @@ -89,10 +86,8 @@ export async function record_sim_videoLogic( ); } - // using injected fs executor - if (params.start) { - const fpsUsed = Number.isFinite(params.fps as number) ? Number(params.fps) : 30; + const fpsUsed = params.fps ?? 30; const startRes = await video.startSimulatorVideoCapture( { simulatorUuid: params.simulatorId, fps: fpsUsed }, executor, @@ -115,15 +110,11 @@ export async function record_sim_videoLogic( notes.push(startRes.warning); } - const nextSteps = `Next Steps: -Stop and save the recording: -record_sim_video({ simulatorId: "${params.simulatorId}", stop: true, outputFile: "/path/to/output.mp4" })`; - return { content: [ { type: 'text', - text: `🎥 Video recording started for simulator ${params.simulatorId} at ${fpsUsed} fps.\nSession: ${startRes.sessionId}`, + text: `Video recording started for simulator ${params.simulatorId} at ${fpsUsed} fps.\nSession: ${startRes.sessionId}`, }, ...(notes.length > 0 ? [ @@ -133,11 +124,14 @@ record_sim_video({ simulatorId: "${params.simulatorId}", stop: true, outputFile: }, ] : []), - { - type: 'text', - text: nextSteps, - }, ], + nextStepParams: { + record_sim_video: { + simulatorId: params.simulatorId, + stop: true, + outputFile: '/path/to/output.mp4', + }, + }, isError: false, }; } @@ -224,21 +218,14 @@ const publicSchemaObject = z.strictObject( recordSimVideoSchemaObject.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'record_sim_video', - description: 'Starts or stops video capture for an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: recordSimVideoSchemaObject, - }), - annotations: { - title: 'Record Simulator Video', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: recordSimVideoSchema as unknown as z.ZodType, - logicFunction: record_sim_videoLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: recordSimVideoSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: recordSimVideoSchema as unknown as z.ZodType, + logicFunction: record_sim_videoLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); diff --git a/src/mcp/tools/simulator/screenshot.ts b/src/mcp/tools/simulator/screenshot.ts deleted file mode 100644 index a4cde5a0..00000000 --- a/src/mcp/tools/simulator/screenshot.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from ui-testing to avoid duplication -export { default } from '../ui-testing/screenshot.ts'; diff --git a/src/mcp/tools/simulator/show_build_settings.ts b/src/mcp/tools/simulator/show_build_settings.ts deleted file mode 100644 index 14d779c0..00000000 --- a/src/mcp/tools/simulator/show_build_settings.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export unified tool for simulator-project workflow -export { default } from '../project-discovery/show_build_settings.ts'; diff --git a/src/mcp/tools/simulator/stop_app_sim.ts b/src/mcp/tools/simulator/stop_app_sim.ts index 172ce07a..26d4fd1b 100644 --- a/src/mcp/tools/simulator/stop_app_sim.ts +++ b/src/mcp/tools/simulator/stop_app_sim.ts @@ -1,9 +1,8 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -22,96 +21,32 @@ const baseSchemaObject = z.object({ .describe( "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", ), - bundleId: z.string().describe("Bundle identifier of the app to stop (e.g., 'com.example.MyApp')"), + bundleId: z.string().describe('Bundle identifier of the app to stop'), }); -const stopAppSimSchema = z.preprocess( - nullifyEmptyStrings, - baseSchemaObject - .refine((val) => val.simulatorId !== undefined || val.simulatorName !== undefined, { - message: 'Either simulatorId or simulatorName is required.', - }) - .refine((val) => !(val.simulatorId !== undefined && val.simulatorName !== undefined), { - message: 'simulatorId and simulatorName are mutually exclusive. Provide only one.', - }), -); +// Internal schema requires simulatorId (factory resolves simulatorName → simulatorId) +const internalSchemaObject = z.object({ + simulatorId: z.string(), + simulatorName: z.string().optional(), + bundleId: z.string(), +}); -export type StopAppSimParams = z.infer; +export type StopAppSimParams = z.infer; export async function stop_app_simLogic( params: StopAppSimParams, executor: CommandExecutor, ): Promise { - let simulatorId = params.simulatorId; - let simulatorDisplayName = simulatorId ?? ''; - - if (params.simulatorName && !simulatorId) { - log('info', `Looking up simulator by name: ${params.simulatorName}`); - - const simulatorListResult = await executor( - ['xcrun', 'simctl', 'list', 'devices', 'available', '--json'], - 'List Simulators', - true, - ); - if (!simulatorListResult.success) { - return { - content: [ - { - type: 'text', - text: `Failed to list simulators: ${simulatorListResult.error}`, - }, - ], - isError: true, - }; - } - - const simulatorsData = JSON.parse(simulatorListResult.output) as { - devices: Record>; - }; - - let foundSimulator: { udid: string; name: string } | null = null; - for (const runtime in simulatorsData.devices) { - const devices = simulatorsData.devices[runtime]; - const simulator = devices.find((device) => device.name === params.simulatorName); - if (simulator) { - foundSimulator = simulator; - break; - } - } - - if (!foundSimulator) { - return { - content: [ - { - type: 'text', - text: `Simulator named "${params.simulatorName}" not found. Use list_sims to see available simulators.`, - }, - ], - isError: true, - }; - } - - simulatorId = foundSimulator.udid; - simulatorDisplayName = `"${params.simulatorName}" (${foundSimulator.udid})`; - } - - if (!simulatorId) { - return { - content: [ - { - type: 'text', - text: 'No simulator identifier provided', - }, - ], - isError: true, - }; - } + const simulatorId = params.simulatorId; + const simulatorDisplayName = params.simulatorName + ? `"${params.simulatorName}" (${simulatorId})` + : simulatorId; log('info', `Stopping app ${params.bundleId} in simulator ${simulatorId}`); try { const command = ['xcrun', 'simctl', 'terminate', simulatorId, params.bundleId]; - const result = await executor(command, 'Stop App in Simulator', true, undefined); + const result = await executor(command, 'Stop App in Simulator', false, undefined); if (!result.success) { return { @@ -129,7 +64,7 @@ export async function stop_app_simLogic( content: [ { type: 'text', - text: `✅ App ${params.bundleId} stopped successfully in simulator ${simulatorDisplayName || simulatorId}`, + text: `App ${params.bundleId} stopped successfully in simulator ${simulatorDisplayName}`, }, ], }; @@ -152,27 +87,22 @@ const publicSchemaObject = z.strictObject( baseSchemaObject.omit({ simulatorId: true, simulatorName: true, + bundleId: true, } as const).shape, ); -export default { - name: 'stop_app_sim', - description: 'Stops an app running in an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Stop App Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: stopAppSimSchema as unknown as z.ZodType, - logicFunction: stop_app_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, - ], - exclusivePairs: [['simulatorId', 'simulatorName']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: internalSchemaObject as unknown as z.ZodType, + logicFunction: stop_app_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + { allOf: ['bundleId'], message: 'bundleId is required' }, + ], + exclusivePairs: [['simulatorId', 'simulatorName']], +}); diff --git a/src/mcp/tools/simulator/test_sim.ts b/src/mcp/tools/simulator/test_sim.ts index 66d40a3d..5f4bd2b3 100644 --- a/src/mcp/tools/simulator/test_sim.ts +++ b/src/mcp/tools/simulator/test_sim.ts @@ -9,8 +9,7 @@ import * as z from 'zod'; import { handleTestLogic } from '../../../utils/test/index.ts'; import { log } from '../../../utils/logging/index.ts'; -import { XcodePlatform } from '../../../types/common.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; @@ -18,6 +17,7 @@ import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { inferPlatform } from '../../../utils/infer-platform.ts'; // Define base schema object with all fields const baseSchemaObject = z.object({ @@ -43,21 +43,13 @@ const baseSchemaObject = z.object({ "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", ), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), - derivedDataPath: z - .string() - .optional() - .describe('Path where build products and other derived data will go'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), useLatestOS: z .boolean() .optional() .describe('Whether to use the latest OS version for the named simulator'), - preferXcodebuild: z - .boolean() - .optional() - .describe( - 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', - ), + preferXcodebuild: z.boolean().optional(), testRunnerEnv: z .record(z.string(), z.string()) .optional() @@ -99,6 +91,21 @@ export async function test_simLogic( ); } + const inferred = await inferPlatform( + { + projectPath: params.projectPath, + workspacePath: params.workspacePath, + scheme: params.scheme, + simulatorId: params.simulatorId, + simulatorName: params.simulatorName, + }, + executor, + ); + log( + 'info', + `Inferred simulator platform for tests: ${inferred.platform} (source: ${inferred.source})`, + ); + return handleTestLogic( { projectPath: params.projectPath, @@ -111,7 +118,7 @@ export async function test_simLogic( extraArgs: params.extraArgs, useLatestOS: params.simulatorId ? false : (params.useLatestOS ?? false), preferXcodebuild: params.preferXcodebuild ?? false, - platform: XcodePlatform.iOSSimulator, + platform: inferred.platform, testRunnerEnv: params.testRunnerEnv, }, executor, @@ -126,31 +133,26 @@ const publicSchemaObject = baseSchemaObject.omit({ simulatorName: true, configuration: true, useLatestOS: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); -export default { - name: 'test_sim', - description: 'Runs tests on an iOS simulator.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Test Simulator', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: testSimulatorSchema as unknown as z.ZodType, - logicFunction: test_simLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, - ], - exclusivePairs: [ - ['projectPath', 'workspacePath'], - ['simulatorId', 'simulatorName'], - ], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: testSimulatorSchema as unknown as z.ZodType, + logicFunction: test_simLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { allOf: ['scheme'], message: 'scheme is required' }, + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, + ], + exclusivePairs: [ + ['projectPath', 'workspacePath'], + ['simulatorId', 'simulatorName'], + ], +}); diff --git a/src/mcp/tools/swift-package/__tests__/active-processes.test.ts b/src/mcp/tools/swift-package/__tests__/active-processes.test.ts index e10114a5..06eb57ba 100644 --- a/src/mcp/tools/swift-package/__tests__/active-processes.test.ts +++ b/src/mcp/tools/swift-package/__tests__/active-processes.test.ts @@ -12,11 +12,16 @@ import { clearAllProcesses, type ProcessInfo, } from '../active-processes.ts'; +import { + clearDaemonActivityRegistry, + getDaemonActivitySnapshot, +} from '../../../../daemon/activity-registry.ts'; describe('active-processes module', () => { // Clear the map before each test beforeEach(() => { clearAllProcesses(); + clearDaemonActivityRegistry(); }); describe('activeProcesses Map', () => { @@ -127,6 +132,25 @@ describe('active-processes module', () => { expect(activeProcesses.size).toBe(0); expect(activeProcesses.get(54321)).toBe(undefined); }); + + it('should release daemon activity when removing process', () => { + let releaseCalls = 0; + + addProcess(321, { + process: { + kill: () => {}, + on: () => {}, + pid: 321, + }, + startedAt: new Date('2023-03-20T09:15:00.000Z'), + releaseActivity: () => { + releaseCalls += 1; + }, + }); + + removeProcess(321); + expect(releaseCalls).toBe(1); + }); }); describe('clearAllProcesses function', () => { @@ -152,6 +176,36 @@ describe('active-processes module', () => { expect(activeProcesses.size).toBe(0); }); + it('should release daemon activity for all tracked processes', () => { + const calls = { first: 0, second: 0 }; + addProcess(1111, { + process: { + kill: () => {}, + on: () => {}, + pid: 1111, + }, + startedAt: new Date(), + releaseActivity: () => { + calls.first += 1; + }, + }); + addProcess(2222, { + process: { + kill: () => {}, + on: () => {}, + pid: 2222, + }, + startedAt: new Date(), + releaseActivity: () => { + calls.second += 1; + }, + }); + + clearAllProcesses(); + expect(calls).toEqual({ first: 1, second: 1 }); + expect(getDaemonActivitySnapshot().activeOperationCount).toBe(0); + }); + it('should work on already empty map', () => { expect(activeProcesses.size).toBe(0); clearAllProcesses(); diff --git a/src/mcp/tools/swift-package/__tests__/index.test.ts b/src/mcp/tools/swift-package/__tests__/index.test.ts deleted file mode 100644 index 6dcf752c..00000000 --- a/src/mcp/tools/swift-package/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for swift-package workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('swift-package workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('Swift Package Manager'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Swift Package Manager operations for building, testing, running, and managing Swift packages and dependencies. Complete SPM workflow support.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts index 180d4baa..d44c25a8 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts @@ -5,48 +5,51 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; +import * as z from 'zod'; import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, + createMockCommandResponse, } from '../../../../test-utils/mock-executors.ts'; -import swiftPackageBuild, { swift_package_buildLogic } from '../swift_package_build.ts'; +import { schema, handler, swift_package_buildLogic } from '../swift_package_build.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; describe('swift_package_build plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(swiftPackageBuild.name).toBe('swift_package_build'); - }); - - it('should have correct description', () => { - expect(swiftPackageBuild.description).toBe('Builds a Swift Package with swift build'); - }); - it('should have handler function', () => { - expect(typeof swiftPackageBuild.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { - // Test required fields - expect(swiftPackageBuild.schema.packagePath.safeParse('/test/package').success).toBe(true); - expect(swiftPackageBuild.schema.packagePath.safeParse('').success).toBe(true); - - // Test optional fields - expect(swiftPackageBuild.schema.targetName.safeParse('MyTarget').success).toBe(true); - expect(swiftPackageBuild.schema.targetName.safeParse(undefined).success).toBe(true); - expect(swiftPackageBuild.schema.configuration.safeParse('debug').success).toBe(true); - expect(swiftPackageBuild.schema.configuration.safeParse('release').success).toBe(true); - expect(swiftPackageBuild.schema.configuration.safeParse(undefined).success).toBe(true); - expect(swiftPackageBuild.schema.architectures.safeParse(['arm64']).success).toBe(true); - expect(swiftPackageBuild.schema.architectures.safeParse(undefined).success).toBe(true); - expect(swiftPackageBuild.schema.parseAsLibrary.safeParse(true).success).toBe(true); - expect(swiftPackageBuild.schema.parseAsLibrary.safeParse(undefined).success).toBe(true); - - // Test invalid inputs - expect(swiftPackageBuild.schema.packagePath.safeParse(null).success).toBe(false); - expect(swiftPackageBuild.schema.configuration.safeParse('invalid').success).toBe(false); - expect(swiftPackageBuild.schema.architectures.safeParse('not-array').success).toBe(false); - expect(swiftPackageBuild.schema.parseAsLibrary.safeParse('yes').success).toBe(false); + const strictSchema = z.strictObject(schema); + + expect(strictSchema.safeParse({ packagePath: '/test/package' }).success).toBe(true); + expect(strictSchema.safeParse({ packagePath: '' }).success).toBe(true); + + expect( + strictSchema.safeParse({ + packagePath: '/test/package', + targetName: 'MyTarget', + architectures: ['arm64'], + parseAsLibrary: true, + }).success, + ).toBe(true); + + expect(strictSchema.safeParse({ packagePath: null }).success).toBe(false); + expect( + strictSchema.safeParse({ packagePath: '/test/package', configuration: 'release' }).success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: '/test/package', architectures: 'not-array' }) + .success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: '/test/package', parseAsLibrary: 'yes' }).success, + ).toBe(false); + + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['architectures', 'packagePath', 'parseAsLibrary', 'targetName']); }); }); @@ -58,14 +61,13 @@ describe('swift_package_build plugin', () => { describe('Command Generation Testing', () => { it('should build correct command for basic build', async () => { - const executor = async (args: any, description: any, useShell: any, cwd: any) => { - executorCalls.push({ args, description, useShell, cwd }); - return { + const executor: CommandExecutor = async (args, description, useShell, opts) => { + executorCalls.push({ args, description, useShell, cwd: opts?.cwd }); + return createMockCommandResponse({ success: true, output: 'Build succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; await swift_package_buildLogic( @@ -79,21 +81,20 @@ describe('swift_package_build plugin', () => { { args: ['swift', 'build', '--package-path', '/test/package'], description: 'Swift Package Build', - useShell: true, + useShell: false, cwd: undefined, }, ]); }); it('should build correct command with release configuration', async () => { - const executor = async (args: any, description: any, useShell: any, cwd: any) => { - executorCalls.push({ args, description, useShell, cwd }); - return { + const executor: CommandExecutor = async (args, description, useShell, opts) => { + executorCalls.push({ args, description, useShell, cwd: opts?.cwd }); + return createMockCommandResponse({ success: true, output: 'Build succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; await swift_package_buildLogic( @@ -108,21 +109,20 @@ describe('swift_package_build plugin', () => { { args: ['swift', 'build', '--package-path', '/test/package', '-c', 'release'], description: 'Swift Package Build', - useShell: true, + useShell: false, cwd: undefined, }, ]); }); it('should build correct command with all parameters', async () => { - const executor = async (args: any, description: any, useShell: any, cwd: any) => { - executorCalls.push({ args, description, useShell, cwd }); - return { + const executor: CommandExecutor = async (args, description, useShell, opts) => { + executorCalls.push({ args, description, useShell, cwd: opts?.cwd }); + return createMockCommandResponse({ success: true, output: 'Build succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; await swift_package_buildLogic( @@ -155,7 +155,7 @@ describe('swift_package_build plugin', () => { '-parse-as-library', ], description: 'Swift Package Build', - useShell: true, + useShell: false, cwd: undefined, }, ]); diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts index d443a1b1..f739054a 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts @@ -9,33 +9,25 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, + createMockCommandResponse, } from '../../../../test-utils/mock-executors.ts'; -import swiftPackageClean, { swift_package_cleanLogic } from '../swift_package_clean.ts'; +import { schema, handler, swift_package_cleanLogic } from '../swift_package_clean.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; describe('swift_package_clean plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(swiftPackageClean.name).toBe('swift_package_clean'); - }); - - it('should have correct description', () => { - expect(swiftPackageClean.description).toBe( - 'Cleans Swift Package build artifacts and derived data', - ); - }); - it('should have handler function', () => { - expect(typeof swiftPackageClean.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { // Test required fields - expect(swiftPackageClean.schema.packagePath.safeParse('/test/package').success).toBe(true); - expect(swiftPackageClean.schema.packagePath.safeParse('').success).toBe(true); + expect(schema.packagePath.safeParse('/test/package').success).toBe(true); + expect(schema.packagePath.safeParse('').success).toBe(true); // Test invalid inputs - expect(swiftPackageClean.schema.packagePath.safeParse(null).success).toBe(false); - expect(swiftPackageClean.schema.packagePath.safeParse(undefined).success).toBe(false); + expect(schema.packagePath.safeParse(null).success).toBe(false); + expect(schema.packagePath.safeParse(undefined).success).toBe(false); }); }); @@ -43,24 +35,18 @@ describe('swift_package_clean plugin', () => { it('should build correct command for clean', async () => { const calls: Array<{ command: string[]; - description: string; - showOutput: boolean; - workingDirectory: string | undefined; + description?: string; + useShell?: boolean; + opts?: { env?: Record; cwd?: string }; }> = []; - const mockExecutor = async ( - command: string[], - description: string, - showOutput: boolean, - workingDirectory?: string, - ) => { - calls.push({ command, description, showOutput, workingDirectory }); - return { + const mockExecutor: CommandExecutor = async (command, description, useShell, opts) => { + calls.push({ command, description, useShell, opts }); + return createMockCommandResponse({ success: true, output: 'Clean succeeded', error: undefined, - process: { pid: 12345 }, - }; + }); }; await swift_package_cleanLogic( @@ -74,8 +60,8 @@ describe('swift_package_clean plugin', () => { expect(calls[0]).toEqual({ command: ['swift', 'package', '--package-path', '/test/package', 'clean'], description: 'Swift Package Clean', - showOutput: true, - workingDirectory: undefined, + useShell: false, + opts: undefined, }); }); }); diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts index b4f1d4be..32a0b22c 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts @@ -5,28 +5,20 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; -import swiftPackageList, { swift_package_listLogic } from '../swift_package_list.ts'; +import { schema, handler, swift_package_listLogic } from '../swift_package_list.ts'; describe('swift_package_list plugin', () => { // No mocks to clear with pure dependency injection describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(swiftPackageList.name).toBe('swift_package_list'); - }); - - it('should have correct description', () => { - expect(swiftPackageList.description).toBe('Lists currently running Swift Package processes'); - }); - it('should have handler function', () => { - expect(typeof swiftPackageList.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { // The schema is an empty object, so any input should be valid - expect(typeof swiftPackageList.schema).toBe('object'); - expect(Object.keys(swiftPackageList.schema)).toEqual([]); + expect(typeof schema).toBe('object'); + expect(Object.keys(schema)).toEqual([]); }); }); @@ -203,7 +195,10 @@ describe('swift_package_list plugin', () => { }; // Create mock process map with multiple processes - const mockProcessMap = new Map([ + const mockProcessMap = new Map< + number, + { executableName?: string; packagePath: string; startedAt: Date } + >([ [12345, mockProcess1], [12346, mockProcess2], ]); @@ -231,16 +226,19 @@ describe('swift_package_list plugin', () => { }); }); - it('should handle process with null executableName', async () => { + it('should handle process with missing executableName', async () => { const startedAt = new Date('2023-01-01T10:00:00.000Z'); const mockProcess = { - executableName: null, // Test null executable name + executableName: undefined, // Test missing executable name packagePath: '/test/package', startedAt: startedAt, }; // Create mock process map with one process - const mockProcessMap = new Map([[12345, mockProcess]]); + const mockProcessMap = new Map< + number, + { executableName?: string; packagePath: string; startedAt: Date } + >([[12345, mockProcess]]); // Use pure dependency injection with stub functions const mockArrayFrom = (mapEntries: any) => Array.from(mapEntries); diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts index 3c0af421..0e55cb84 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts @@ -6,62 +6,67 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; -import swiftPackageRun, { swift_package_runLogic } from '../swift_package_run.ts'; +import { + createMockExecutor, + createNoopExecutor, + createMockCommandResponse, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, swift_package_runLogic } from '../swift_package_run.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; describe('swift_package_run plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(swiftPackageRun.name).toBe('swift_package_run'); - }); - - it('should have correct description', () => { - expect(swiftPackageRun.description).toBe( - 'Runs an executable target from a Swift Package with swift run', - ); - }); - it('should have handler function', () => { - expect(typeof swiftPackageRun.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { - // Test packagePath (required string) - expect(swiftPackageRun.schema.packagePath.safeParse('valid/path').success).toBe(true); - expect(swiftPackageRun.schema.packagePath.safeParse(null).success).toBe(false); - - // Test executableName (optional string) - expect(swiftPackageRun.schema.executableName.safeParse('MyExecutable').success).toBe(true); - expect(swiftPackageRun.schema.executableName.safeParse(undefined).success).toBe(true); - expect(swiftPackageRun.schema.executableName.safeParse(123).success).toBe(false); - - // Test arguments (optional array of strings) - expect(swiftPackageRun.schema.arguments.safeParse(['arg1', 'arg2']).success).toBe(true); - expect(swiftPackageRun.schema.arguments.safeParse(undefined).success).toBe(true); - expect(swiftPackageRun.schema.arguments.safeParse(['arg1', 123]).success).toBe(false); - - // Test configuration (optional enum) - expect(swiftPackageRun.schema.configuration.safeParse('debug').success).toBe(true); - expect(swiftPackageRun.schema.configuration.safeParse('release').success).toBe(true); - expect(swiftPackageRun.schema.configuration.safeParse(undefined).success).toBe(true); - expect(swiftPackageRun.schema.configuration.safeParse('invalid').success).toBe(false); - - // Test timeout (optional number) - expect(swiftPackageRun.schema.timeout.safeParse(30).success).toBe(true); - expect(swiftPackageRun.schema.timeout.safeParse(undefined).success).toBe(true); - expect(swiftPackageRun.schema.timeout.safeParse('30').success).toBe(false); - - // Test background (optional boolean) - expect(swiftPackageRun.schema.background.safeParse(true).success).toBe(true); - expect(swiftPackageRun.schema.background.safeParse(false).success).toBe(true); - expect(swiftPackageRun.schema.background.safeParse(undefined).success).toBe(true); - expect(swiftPackageRun.schema.background.safeParse('true').success).toBe(false); - - // Test parseAsLibrary (optional boolean) - expect(swiftPackageRun.schema.parseAsLibrary.safeParse(true).success).toBe(true); - expect(swiftPackageRun.schema.parseAsLibrary.safeParse(false).success).toBe(true); - expect(swiftPackageRun.schema.parseAsLibrary.safeParse(undefined).success).toBe(true); - expect(swiftPackageRun.schema.parseAsLibrary.safeParse('true').success).toBe(false); + const strictSchema = z.strictObject(schema); + + expect(strictSchema.safeParse({ packagePath: 'valid/path' }).success).toBe(true); + expect(strictSchema.safeParse({ packagePath: null }).success).toBe(false); + + expect( + strictSchema.safeParse({ + packagePath: 'valid/path', + executableName: 'MyExecutable', + arguments: ['arg1', 'arg2'], + timeout: 30, + background: true, + parseAsLibrary: true, + }).success, + ).toBe(true); + + expect( + strictSchema.safeParse({ packagePath: 'valid/path', executableName: 123 }).success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: 'valid/path', arguments: ['arg1', 123] }).success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: 'valid/path', configuration: 'release' }).success, + ).toBe(false); + expect(strictSchema.safeParse({ packagePath: 'valid/path', timeout: '30' }).success).toBe( + false, + ); + expect( + strictSchema.safeParse({ packagePath: 'valid/path', background: 'true' }).success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: 'valid/path', parseAsLibrary: 'true' }).success, + ).toBe(false); + + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual( + [ + 'arguments', + 'background', + 'executableName', + 'packagePath', + 'parseAsLibrary', + 'timeout', + ].sort(), + ); }); }); @@ -73,19 +78,15 @@ describe('swift_package_run plugin', () => { describe('Command Generation Testing', () => { it('should build correct command for basic run (foreground mode)', async () => { - const mockExecutor = ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: any, - ) => { - executorCalls.push({ command, logPrefix, useShell, env }); - return Promise.resolve({ - success: true, - output: 'Process completed', - error: undefined, - process: { pid: 12345 }, - }); + const mockExecutor: CommandExecutor = (command, logPrefix, useShell, opts) => { + executorCalls.push({ command, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'Process completed', + error: undefined, + }), + ); }; await swift_package_runLogic( @@ -98,25 +99,21 @@ describe('swift_package_run plugin', () => { expect(executorCalls[0]).toEqual({ command: ['swift', 'run', '--package-path', '/test/package'], logPrefix: 'Swift Package Run', - useShell: true, - env: undefined, + useShell: false, + opts: undefined, }); }); it('should build correct command with release configuration', async () => { - const mockExecutor = ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: any, - ) => { - executorCalls.push({ command, logPrefix, useShell, env }); - return Promise.resolve({ - success: true, - output: 'Process completed', - error: undefined, - process: { pid: 12345 }, - }); + const mockExecutor: CommandExecutor = (command, logPrefix, useShell, opts) => { + executorCalls.push({ command, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'Process completed', + error: undefined, + }), + ); }; await swift_package_runLogic( @@ -130,25 +127,21 @@ describe('swift_package_run plugin', () => { expect(executorCalls[0]).toEqual({ command: ['swift', 'run', '--package-path', '/test/package', '-c', 'release'], logPrefix: 'Swift Package Run', - useShell: true, - env: undefined, + useShell: false, + opts: undefined, }); }); it('should build correct command with executable name', async () => { - const mockExecutor = ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: any, - ) => { - executorCalls.push({ command, logPrefix, useShell, env }); - return Promise.resolve({ - success: true, - output: 'Process completed', - error: undefined, - process: { pid: 12345 }, - }); + const mockExecutor: CommandExecutor = (command, logPrefix, useShell, opts) => { + executorCalls.push({ command, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'Process completed', + error: undefined, + }), + ); }; await swift_package_runLogic( @@ -162,25 +155,21 @@ describe('swift_package_run plugin', () => { expect(executorCalls[0]).toEqual({ command: ['swift', 'run', '--package-path', '/test/package', 'MyApp'], logPrefix: 'Swift Package Run', - useShell: true, - env: undefined, + useShell: false, + opts: undefined, }); }); it('should build correct command with arguments', async () => { - const mockExecutor = ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: any, - ) => { - executorCalls.push({ command, logPrefix, useShell, env }); - return Promise.resolve({ - success: true, - output: 'Process completed', - error: undefined, - process: { pid: 12345 }, - }); + const mockExecutor: CommandExecutor = (command, logPrefix, useShell, opts) => { + executorCalls.push({ command, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'Process completed', + error: undefined, + }), + ); }; await swift_package_runLogic( @@ -194,25 +183,21 @@ describe('swift_package_run plugin', () => { expect(executorCalls[0]).toEqual({ command: ['swift', 'run', '--package-path', '/test/package', '--', 'arg1', 'arg2'], logPrefix: 'Swift Package Run', - useShell: true, - env: undefined, + useShell: false, + opts: undefined, }); }); it('should build correct command with parseAsLibrary flag', async () => { - const mockExecutor = ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: any, - ) => { - executorCalls.push({ command, logPrefix, useShell, env }); - return Promise.resolve({ - success: true, - output: 'Process completed', - error: undefined, - process: { pid: 12345 }, - }); + const mockExecutor: CommandExecutor = (command, logPrefix, useShell, opts) => { + executorCalls.push({ command, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'Process completed', + error: undefined, + }), + ); }; await swift_package_runLogic( @@ -233,25 +218,21 @@ describe('swift_package_run plugin', () => { '-parse-as-library', ], logPrefix: 'Swift Package Run', - useShell: true, - env: undefined, + useShell: false, + opts: undefined, }); }); it('should build correct command with all parameters', async () => { - const mockExecutor = ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: any, - ) => { - executorCalls.push({ command, logPrefix, useShell, env }); - return Promise.resolve({ - success: true, - output: 'Process completed', - error: undefined, - process: { pid: 12345 }, - }); + const mockExecutor: CommandExecutor = (command, logPrefix, useShell, opts) => { + executorCalls.push({ command, logPrefix, useShell, opts }); + return Promise.resolve( + createMockCommandResponse({ + success: true, + output: 'Process completed', + error: undefined, + }), + ); }; await swift_package_runLogic( @@ -280,8 +261,8 @@ describe('swift_package_run plugin', () => { 'arg1', ], logPrefix: 'Swift Package Run', - useShell: true, - env: undefined, + useShell: false, + opts: undefined, }); }); @@ -306,7 +287,7 @@ describe('swift_package_run plugin', () => { it('should return validation error for missing packagePath', async () => { // Since the tool now uses createTypedTool, Zod validation happens at the handler level // Test the handler directly to see Zod validation - const result = await swiftPackageRun.handler({}); + const result = await handler({}); expect(result).toEqual({ content: [ diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts index 0df124d1..0ba8c883 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts @@ -6,7 +6,9 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import swiftPackageStop, { +import { + schema, + handler, createMockProcessManager, swift_package_stopLogic, type ProcessManager, @@ -51,32 +53,22 @@ class MockProcess { describe('swift_package_stop plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(swiftPackageStop.name).toBe('swift_package_stop'); - }); - - it('should have correct description', () => { - expect(swiftPackageStop.description).toBe( - 'Stops a running Swift Package executable started with swift_package_run', - ); - }); - it('should have handler function', () => { - expect(typeof swiftPackageStop.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { // Test valid inputs - expect(swiftPackageStop.schema.pid.safeParse(12345).success).toBe(true); - expect(swiftPackageStop.schema.pid.safeParse(0).success).toBe(true); - expect(swiftPackageStop.schema.pid.safeParse(-1).success).toBe(true); + expect(schema.pid.safeParse(12345).success).toBe(true); + expect(schema.pid.safeParse(0).success).toBe(true); + expect(schema.pid.safeParse(-1).success).toBe(true); // Test invalid inputs - expect(swiftPackageStop.schema.pid.safeParse('not-a-number').success).toBe(false); - expect(swiftPackageStop.schema.pid.safeParse(null).success).toBe(false); - expect(swiftPackageStop.schema.pid.safeParse(undefined).success).toBe(false); - expect(swiftPackageStop.schema.pid.safeParse({}).success).toBe(false); - expect(swiftPackageStop.schema.pid.safeParse([]).success).toBe(false); + expect(schema.pid.safeParse('not-a-number').success).toBe(false); + expect(schema.pid.safeParse(null).success).toBe(false); + expect(schema.pid.safeParse(undefined).success).toBe(false); + expect(schema.pid.safeParse({}).success).toBe(false); + expect(schema.pid.safeParse([]).success).toBe(false); }); }); diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts index 4ad8c6c3..2e12f9cd 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts @@ -5,72 +5,82 @@ */ import { describe, it, expect } from 'vitest'; +import * as z from 'zod'; import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, + createMockCommandResponse, } from '../../../../test-utils/mock-executors.ts'; -import swiftPackageTest, { swift_package_testLogic } from '../swift_package_test.ts'; +import { schema, handler, swift_package_testLogic } from '../swift_package_test.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; describe('swift_package_test plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(swiftPackageTest.name).toBe('swift_package_test'); - }); - - it('should have correct description', () => { - expect(swiftPackageTest.description).toBe('Runs tests for a Swift Package with swift test'); - }); - it('should have handler function', () => { - expect(typeof swiftPackageTest.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema correctly', () => { - // Test required fields - expect(swiftPackageTest.schema.packagePath.safeParse('/test/package').success).toBe(true); - expect(swiftPackageTest.schema.packagePath.safeParse('').success).toBe(true); + const strictSchema = z.strictObject(schema); + + expect(strictSchema.safeParse({ packagePath: '/test/package' }).success).toBe(true); + expect(strictSchema.safeParse({ packagePath: '' }).success).toBe(true); + + expect( + strictSchema.safeParse({ + packagePath: '/test/package', + testProduct: 'MyTests', + filter: 'Test.*', + parallel: true, + showCodecov: true, + parseAsLibrary: true, + }).success, + ).toBe(true); - // Test optional fields - expect(swiftPackageTest.schema.testProduct.safeParse('MyTests').success).toBe(true); - expect(swiftPackageTest.schema.testProduct.safeParse(undefined).success).toBe(true); - expect(swiftPackageTest.schema.filter.safeParse('Test.*').success).toBe(true); - expect(swiftPackageTest.schema.filter.safeParse(undefined).success).toBe(true); - expect(swiftPackageTest.schema.configuration.safeParse('debug').success).toBe(true); - expect(swiftPackageTest.schema.configuration.safeParse('release').success).toBe(true); - expect(swiftPackageTest.schema.configuration.safeParse(undefined).success).toBe(true); - expect(swiftPackageTest.schema.parallel.safeParse(true).success).toBe(true); - expect(swiftPackageTest.schema.parallel.safeParse(undefined).success).toBe(true); - expect(swiftPackageTest.schema.showCodecov.safeParse(true).success).toBe(true); - expect(swiftPackageTest.schema.showCodecov.safeParse(undefined).success).toBe(true); - expect(swiftPackageTest.schema.parseAsLibrary.safeParse(true).success).toBe(true); - expect(swiftPackageTest.schema.parseAsLibrary.safeParse(undefined).success).toBe(true); + expect(strictSchema.safeParse({ packagePath: null }).success).toBe(false); + expect( + strictSchema.safeParse({ packagePath: '/test/package', configuration: 'release' }).success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: '/test/package', parallel: 'yes' }).success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: '/test/package', showCodecov: 'yes' }).success, + ).toBe(false); + expect( + strictSchema.safeParse({ packagePath: '/test/package', parseAsLibrary: 'yes' }).success, + ).toBe(false); - // Test invalid inputs - expect(swiftPackageTest.schema.packagePath.safeParse(null).success).toBe(false); - expect(swiftPackageTest.schema.configuration.safeParse('invalid').success).toBe(false); - expect(swiftPackageTest.schema.parallel.safeParse('yes').success).toBe(false); - expect(swiftPackageTest.schema.showCodecov.safeParse('yes').success).toBe(false); - expect(swiftPackageTest.schema.parseAsLibrary.safeParse('yes').success).toBe(false); + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual( + [ + 'filter', + 'packagePath', + 'parseAsLibrary', + 'parallel', + 'showCodecov', + 'testProduct', + ].sort(), + ); }); }); describe('Command Generation Testing', () => { it('should build correct command for basic test', async () => { - const calls: any[] = []; - const mockExecutor = async ( - args: string[], - name: string, - hideOutput: boolean, - workingDir: string | undefined, - ) => { - calls.push({ args, name, hideOutput, workingDir }); - return { + const calls: Array<{ + args: string[]; + name?: string; + hideOutput?: boolean; + opts?: { env?: Record; cwd?: string }; + }> = []; + const mockExecutor: CommandExecutor = async (args, name, hideOutput, opts) => { + calls.push({ args, name, hideOutput, opts }); + return createMockCommandResponse({ success: true, output: 'Test Passed', error: undefined, - process: { pid: 12345 }, - }; + }); }; await swift_package_testLogic( @@ -84,26 +94,25 @@ describe('swift_package_test plugin', () => { expect(calls[0]).toEqual({ args: ['swift', 'test', '--package-path', '/test/package'], name: 'Swift Package Test', - hideOutput: true, - workingDir: undefined, + hideOutput: false, + opts: undefined, }); }); it('should build correct command with all parameters', async () => { - const calls: any[] = []; - const mockExecutor = async ( - args: string[], - name: string, - hideOutput: boolean, - workingDir: string | undefined, - ) => { - calls.push({ args, name, hideOutput, workingDir }); - return { + const calls: Array<{ + args: string[]; + name?: string; + hideOutput?: boolean; + opts?: { env?: Record; cwd?: string }; + }> = []; + const mockExecutor: CommandExecutor = async (args, name, hideOutput, opts) => { + calls.push({ args, name, hideOutput, opts }); + return createMockCommandResponse({ success: true, output: 'Tests completed', error: undefined, - process: { pid: 12345 }, - }; + }); }; await swift_package_testLogic( @@ -138,8 +147,8 @@ describe('swift_package_test plugin', () => { '-parse-as-library', ], name: 'Swift Package Test', - hideOutput: true, - workingDir: undefined, + hideOutput: false, + opts: undefined, }); }); }); diff --git a/src/mcp/tools/swift-package/active-processes.ts b/src/mcp/tools/swift-package/active-processes.ts index eefa4afb..7dd450c3 100644 --- a/src/mcp/tools/swift-package/active-processes.ts +++ b/src/mcp/tools/swift-package/active-processes.ts @@ -11,6 +11,9 @@ export interface ProcessInfo { pid?: number; }; startedAt: Date; + executableName?: string; + packagePath?: string; + releaseActivity?: () => void; } // Global map to track active processes @@ -22,13 +25,20 @@ export const getProcess = (pid: number): ProcessInfo | undefined => { }; export const addProcess = (pid: number, processInfo: ProcessInfo): void => { + const existing = activeProcesses.get(pid); + existing?.releaseActivity?.(); activeProcesses.set(pid, processInfo); }; export const removeProcess = (pid: number): boolean => { + const existing = activeProcesses.get(pid); + existing?.releaseActivity?.(); return activeProcesses.delete(pid); }; export const clearAllProcesses = (): void => { + for (const processInfo of activeProcesses.values()) { + processInfo.releaseActivity?.(); + } activeProcesses.clear(); }; diff --git a/src/mcp/tools/swift-package/index.ts b/src/mcp/tools/swift-package/index.ts deleted file mode 100644 index 18ec53a2..00000000 --- a/src/mcp/tools/swift-package/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'Swift Package Manager', - description: - 'Swift Package Manager operations for building, testing, running, and managing Swift packages and dependencies. Complete SPM workflow support.', -}; diff --git a/src/mcp/tools/swift-package/swift_package_build.ts b/src/mcp/tools/swift-package/swift_package_build.ts index e17e68ab..96383809 100644 --- a/src/mcp/tools/swift-package/swift_package_build.ts +++ b/src/mcp/tools/swift-package/swift_package_build.ts @@ -4,21 +4,27 @@ import { createErrorResponse } from '../../../utils/responses/index.ts'; import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; -import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { + createSessionAwareTool, + getSessionAwareToolSchemaShape, +} from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject -const swiftPackageBuildSchema = z.object({ - packagePath: z.string().describe('Path to the Swift package root (Required)'), - targetName: z.string().optional().describe('Optional target to build'), - configuration: z - .enum(['debug', 'release']) - .optional() - .describe('Swift package configuration (debug, release)'), - architectures: z.array(z.string()).optional().describe('Target architectures to build for'), - parseAsLibrary: z.boolean().optional().describe('Build as library instead of executable'), +const baseSchemaObject = z.object({ + packagePath: z.string(), + targetName: z.string().optional(), + configuration: z.enum(['debug', 'release', 'Debug', 'Release']).optional(), + architectures: z.array(z.string()).optional(), + parseAsLibrary: z.boolean().optional(), }); +const publicSchemaObject = baseSchemaObject.omit({ + configuration: true, +} as const); + +const swiftPackageBuildSchema = baseSchemaObject; + // Use z.infer for type safety type SwiftPackageBuildParams = z.infer; @@ -29,7 +35,7 @@ export async function swift_package_buildLogic( const resolvedPath = path.resolve(params.packagePath); const swiftArgs = ['build', '--package-path', resolvedPath]; - if (params.configuration && params.configuration.toLowerCase() === 'release') { + if (params.configuration?.toLowerCase() === 'release') { swiftArgs.push('-c', 'release'); } @@ -49,7 +55,7 @@ export async function swift_package_buildLogic( log('info', `Running swift ${swiftArgs.join(' ')}`); try { - const result = await executor(['swift', ...swiftArgs], 'Swift Package Build', true, undefined); + const result = await executor(['swift', ...swiftArgs], 'Swift Package Build', false, undefined); if (!result.success) { const errorMessage = result.error ?? result.output ?? 'Unknown error'; return createErrorResponse('Swift package build failed', errorMessage); @@ -73,17 +79,13 @@ export async function swift_package_buildLogic( } } -export default { - name: 'swift_package_build', - description: 'Builds a Swift Package with swift build', - schema: swiftPackageBuildSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Swift Package Build', - destructiveHint: true, - }, - handler: createTypedTool( - swiftPackageBuildSchema, - swift_package_buildLogic, - getDefaultCommandExecutor, - ), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: swiftPackageBuildSchema, + logicFunction: swift_package_buildLogic, + getExecutor: getDefaultCommandExecutor, +}); diff --git a/src/mcp/tools/swift-package/swift_package_clean.ts b/src/mcp/tools/swift-package/swift_package_clean.ts index 1d973526..4d8b47aa 100644 --- a/src/mcp/tools/swift-package/swift_package_clean.ts +++ b/src/mcp/tools/swift-package/swift_package_clean.ts @@ -4,12 +4,12 @@ import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createErrorResponse } from '../../../utils/responses/index.ts'; import { log } from '../../../utils/logging/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const swiftPackageCleanSchema = z.object({ - packagePath: z.string().describe('Path to the Swift package root (Required)'), + packagePath: z.string(), }); // Use z.infer for type safety @@ -24,7 +24,7 @@ export async function swift_package_cleanLogic( log('info', `Running swift ${swiftArgs.join(' ')}`); try { - const result = await executor(['swift', ...swiftArgs], 'Swift Package Clean', true, undefined); + const result = await executor(['swift', ...swiftArgs], 'Swift Package Clean', false, undefined); if (!result.success) { const errorMessage = result.error ?? result.output ?? 'Unknown error'; return createErrorResponse('Swift package clean failed', errorMessage); @@ -48,17 +48,10 @@ export async function swift_package_cleanLogic( } } -export default { - name: 'swift_package_clean', - description: 'Cleans Swift Package build artifacts and derived data', - schema: swiftPackageCleanSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Swift Package Clean', - destructiveHint: true, - }, - handler: createTypedTool( - swiftPackageCleanSchema, - swift_package_cleanLogic, - getDefaultCommandExecutor, - ), -}; +export const schema = swiftPackageCleanSchema.shape; + +export const handler = createTypedTool( + swiftPackageCleanSchema, + swift_package_cleanLogic, + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/swift-package/swift_package_list.ts b/src/mcp/tools/swift-package/swift_package_list.ts index abd1d948..42d14de0 100644 --- a/src/mcp/tools/swift-package/swift_package_list.ts +++ b/src/mcp/tools/swift-package/swift_package_list.ts @@ -4,23 +4,23 @@ // Import the shared activeProcesses map from swift_package_run // This maintains the same behavior as the original implementation import * as z from 'zod'; -import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createTextContent } from '../../../types/common.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; import { getDefaultCommandExecutor } from '../../../utils/command.ts'; - -interface ProcessInfo { - executableName?: string; - startedAt: Date; - packagePath: string; -} - -const activeProcesses = new Map(); +import { activeProcesses } from './active-processes.ts'; /** * Process list dependencies for dependency injection */ +type ListProcessInfo = { + executableName?: string; + packagePath?: string; + startedAt: Date; +}; + export interface ProcessListDependencies { - processMap?: Map; + processMap?: Map; arrayFrom?: typeof Array.from; dateNow?: typeof Date.now; } @@ -35,7 +35,18 @@ export async function swift_package_listLogic( params?: unknown, dependencies?: ProcessListDependencies, ): Promise { - const processMap = dependencies?.processMap ?? activeProcesses; + const processMap = + dependencies?.processMap ?? + new Map( + Array.from(activeProcesses.entries()).map(([pid, info]) => [ + pid, + { + executableName: info.executableName, + packagePath: info.packagePath, + startedAt: info.startedAt, + }, + ]), + ); const arrayFrom = dependencies?.arrayFrom ?? Array.from; const dateNow = dependencies?.dateNow ?? Date.now; @@ -57,10 +68,9 @@ export async function swift_package_listLogic( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const executableName = info.executableName || 'default'; const runtime = Math.max(1, Math.round((dateNow() - info.startedAt.getTime()) / 1000)); + const packagePath = info.packagePath ?? 'unknown package'; content.push( - createTextContent( - ` • PID ${pid}: ${executableName} (${info.packagePath}) - running ${runtime}s`, - ), + createTextContent(` • PID ${pid}: ${executableName} (${packagePath}) - running ${runtime}s`), ); } @@ -75,19 +85,12 @@ const swiftPackageListSchema = z.object({}); // Use z.infer for type safety type SwiftPackageListParams = z.infer; -export default { - name: 'swift_package_list', - description: 'Lists currently running Swift Package processes', - schema: swiftPackageListSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Swift Package List', - readOnlyHint: true, +export const schema = swiftPackageListSchema.shape; + +export const handler = createTypedTool( + swiftPackageListSchema, + (params: SwiftPackageListParams) => { + return swift_package_listLogic(params); }, - handler: createTypedTool( - swiftPackageListSchema, - (params: SwiftPackageListParams) => { - return swift_package_listLogic(params); - }, - getDefaultCommandExecutor, - ), -}; + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/swift-package/swift_package_run.ts b/src/mcp/tools/swift-package/swift_package_run.ts index 52a951b6..451733a7 100644 --- a/src/mcp/tools/swift-package/swift_package_run.ts +++ b/src/mcp/tools/swift-package/swift_package_run.ts @@ -4,33 +4,32 @@ import { createTextResponse, createErrorResponse } from '../../../utils/response import { log } from '../../../utils/logging/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; -import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createTextContent } from '../../../types/common.ts'; import { addProcess } from './active-processes.ts'; -import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { + createSessionAwareTool, + getSessionAwareToolSchemaShape, +} from '../../../utils/typed-tool-factory.ts'; +import { acquireDaemonActivity } from '../../../daemon/activity-registry.ts'; // Define schema as ZodObject -const swiftPackageRunSchema = z.object({ - packagePath: z.string().describe('Path to the Swift package root (Required)'), - executableName: z - .string() - .optional() - .describe('Name of executable to run (defaults to package name)'), - arguments: z.array(z.string()).optional().describe('Arguments to pass to the executable'), - configuration: z - .enum(['debug', 'release']) - .optional() - .describe("Build configuration: 'debug' (default) or 'release'"), - timeout: z.number().optional().describe('Timeout in seconds (default: 30, max: 300)'), - background: z - .boolean() - .optional() - .describe('Run in background and return immediately (default: false)'), - parseAsLibrary: z - .boolean() - .optional() - .describe('Add -parse-as-library flag for @main support (default: false)'), +const baseSchemaObject = z.object({ + packagePath: z.string(), + executableName: z.string().optional(), + arguments: z.array(z.string()).optional(), + configuration: z.enum(['debug', 'release', 'Debug', 'Release']).optional(), + timeout: z.number().optional(), + background: z.boolean().optional(), + parseAsLibrary: z.boolean().optional(), }); +const publicSchemaObject = baseSchemaObject.omit({ + configuration: true, +} as const); + +const swiftPackageRunSchema = baseSchemaObject; + // Use z.infer for type safety type SwiftPackageRunParams = z.infer; @@ -46,7 +45,7 @@ export async function swift_package_runLogic( const swiftArgs = ['run', '--package-path', resolvedPath]; - if (params.configuration && params.configuration.toLowerCase() === 'release') { + if (params.configuration?.toLowerCase() === 'release') { swiftArgs.push('-c', 'release'); } else if (params.configuration && params.configuration.toLowerCase() !== 'debug') { return createTextResponse("Invalid configuration. Use 'debug' or 'release'.", true); @@ -92,7 +91,7 @@ export async function swift_package_runLogic( const result = await executor( command, 'Swift Package Run (Background)', - true, + false, cleanEnv, true, ); @@ -115,6 +114,9 @@ export async function swift_package_runLogic( pid: result.process.pid, }, startedAt: new Date(), + executableName: params.executableName, + packagePath: resolvedPath, + releaseActivity: acquireDaemonActivity('swift-package.background-process'), }); return { @@ -141,7 +143,7 @@ export async function swift_package_runLogic( const command = ['swift', ...swiftArgs]; // Create a promise that will either complete with the command result or timeout - const commandPromise = executor(command, 'Swift Package Run', true, undefined); + const commandPromise = executor(command, 'Swift Package Run', false, undefined); const timeoutPromise = new Promise<{ success: boolean; @@ -219,17 +221,13 @@ export async function swift_package_runLogic( } } -export default { - name: 'swift_package_run', - description: 'Runs an executable target from a Swift Package with swift run', - schema: swiftPackageRunSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Swift Package Run', - destructiveHint: true, - }, - handler: createTypedTool( - swiftPackageRunSchema, - swift_package_runLogic, - getDefaultCommandExecutor, - ), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: swiftPackageRunSchema, + logicFunction: swift_package_runLogic, + getExecutor: getDefaultCommandExecutor, +}); diff --git a/src/mcp/tools/swift-package/swift_package_stop.ts b/src/mcp/tools/swift-package/swift_package_stop.ts index f71b5f61..916546d7 100644 --- a/src/mcp/tools/swift-package/swift_package_stop.ts +++ b/src/mcp/tools/swift-package/swift_package_stop.ts @@ -1,11 +1,11 @@ import * as z from 'zod'; import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; import { getProcess, removeProcess, type ProcessInfo } from './active-processes.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; // Define schema as ZodObject const swiftPackageStopSchema = z.object({ - pid: z.number().describe('Process ID (PID) of the running executable'), + pid: z.number(), }); // Use z.infer for type safety @@ -101,24 +101,16 @@ export async function swift_package_stopLogic( } } -export default { - name: 'swift_package_stop', - description: 'Stops a running Swift Package executable started with swift_package_run', - schema: swiftPackageStopSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Swift Package Stop', - destructiveHint: true, - }, - async handler(args: Record): Promise { - // Validate parameters using Zod - const parseResult = swiftPackageStopSchema.safeParse(args); - if (!parseResult.success) { - return createErrorResponse( - 'Parameter validation failed', - parseResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '), - ); - } - - return swift_package_stopLogic(parseResult.data); - }, -}; +export const schema = swiftPackageStopSchema.shape; + +export async function handler(args: Record): Promise { + const parseResult = swiftPackageStopSchema.safeParse(args); + if (!parseResult.success) { + return createErrorResponse( + 'Parameter validation failed', + parseResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '), + ); + } + + return swift_package_stopLogic(parseResult.data); +} diff --git a/src/mcp/tools/swift-package/swift_package_test.ts b/src/mcp/tools/swift-package/swift_package_test.ts index 7780029b..25579b1e 100644 --- a/src/mcp/tools/swift-package/swift_package_test.ts +++ b/src/mcp/tools/swift-package/swift_package_test.ts @@ -4,26 +4,29 @@ import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; import { log } from '../../../utils/logging/index.ts'; -import { ToolResponse } from '../../../types/common.ts'; -import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { + createSessionAwareTool, + getSessionAwareToolSchemaShape, +} from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject -const swiftPackageTestSchema = z.object({ - packagePath: z.string().describe('Path to the Swift package root (Required)'), - testProduct: z.string().optional().describe('Optional specific test product to run'), - filter: z.string().optional().describe('Filter tests by name (regex pattern)'), - configuration: z - .enum(['debug', 'release']) - .optional() - .describe('Swift package configuration (debug, release)'), - parallel: z.boolean().optional().describe('Run tests in parallel (default: true)'), - showCodecov: z.boolean().optional().describe('Show code coverage (default: false)'), - parseAsLibrary: z - .boolean() - .optional() - .describe('Add -parse-as-library flag for @main support (default: false)'), +const baseSchemaObject = z.object({ + packagePath: z.string(), + testProduct: z.string().optional(), + filter: z.string().optional().describe('regex: pattern'), + configuration: z.enum(['debug', 'release', 'Debug', 'Release']).optional(), + parallel: z.boolean().optional(), + showCodecov: z.boolean().optional(), + parseAsLibrary: z.boolean().optional(), }); +const publicSchemaObject = baseSchemaObject.omit({ + configuration: true, +} as const); + +const swiftPackageTestSchema = baseSchemaObject; + // Use z.infer for type safety type SwiftPackageTestParams = z.infer; @@ -34,7 +37,7 @@ export async function swift_package_testLogic( const resolvedPath = path.resolve(params.packagePath); const swiftArgs = ['test', '--package-path', resolvedPath]; - if (params.configuration && params.configuration.toLowerCase() === 'release') { + if (params.configuration?.toLowerCase() === 'release') { swiftArgs.push('-c', 'release'); } else if (params.configuration && params.configuration.toLowerCase() !== 'debug') { return createTextResponse("Invalid configuration. Use 'debug' or 'release'.", true); @@ -62,7 +65,7 @@ export async function swift_package_testLogic( log('info', `Running swift ${swiftArgs.join(' ')}`); try { - const result = await executor(['swift', ...swiftArgs], 'Swift Package Test', true, undefined); + const result = await executor(['swift', ...swiftArgs], 'Swift Package Test', false, undefined); if (!result.success) { const errorMessage = result.error ?? result.output ?? 'Unknown error'; return createErrorResponse('Swift package tests failed', errorMessage); @@ -86,17 +89,13 @@ export async function swift_package_testLogic( } } -export default { - name: 'swift_package_test', - description: 'Runs tests for a Swift Package with swift test', - schema: swiftPackageTestSchema.shape, // MCP SDK compatibility - annotations: { - title: 'Swift Package Test', - destructiveHint: true, - }, - handler: createTypedTool( - swiftPackageTestSchema, - swift_package_testLogic, - getDefaultCommandExecutor, - ), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: swiftPackageTestSchema, + logicFunction: swift_package_testLogic, + getExecutor: getDefaultCommandExecutor, +}); diff --git a/src/mcp/tools/ui-testing/__tests__/button.test.ts b/src/mcp/tools/ui-automation/__tests__/button.test.ts similarity index 78% rename from src/mcp/tools/ui-testing/__tests__/button.test.ts rename to src/mcp/tools/ui-automation/__tests__/button.test.ts index 0f978be1..c64d426b 100644 --- a/src/mcp/tools/ui-testing/__tests__/button.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/button.test.ts @@ -4,62 +4,57 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; -import buttonPlugin, { buttonLogic } from '../button.ts'; +import { + createMockExecutor, + createNoopExecutor, + createMockCommandResponse, +} from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, buttonLogic } from '../button.ts'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; describe('Button Plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(buttonPlugin.name).toBe('button'); - }); - - it('should have correct description', () => { - expect(buttonPlugin.description).toBe( - 'Press hardware button on iOS simulator. Supported buttons: apple-pay, home, lock, side-button, siri', - ); - }); - it('should have handler function', () => { - expect(typeof buttonPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose public schema without simulatorId field', () => { - const schema = z.object(buttonPlugin.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ buttonType: 'home' }).success).toBe(true); - expect(schema.safeParse({ buttonType: 'home', duration: 2.5 }).success).toBe(true); - expect(schema.safeParse({ buttonType: 'invalid-button' }).success).toBe(false); - expect(schema.safeParse({ buttonType: 'home', duration: -1 }).success).toBe(false); + expect(schemaObj.safeParse({ buttonType: 'home' }).success).toBe(true); + expect(schemaObj.safeParse({ buttonType: 'home', duration: 2.5 }).success).toBe(true); + expect(schemaObj.safeParse({ buttonType: 'invalid-button' }).success).toBe(false); + expect(schemaObj.safeParse({ buttonType: 'home', duration: -1 }).success).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', buttonType: 'home', }); expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as any)).toBe(false); + expect('simulatorId' in (withSimId.data as Record)).toBe(false); - expect(schema.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({}).success).toBe(false); }); }); describe('Command Generation', () => { it('should generate correct axe command for basic button press', async () => { let capturedCommand: string[] = []; - const trackingExecutor = async (command: string[]) => { + const trackingExecutor: CommandExecutor = async (command) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'button press completed', error: undefined, - process: { pid: 12345 }, - }; + }); }; const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -84,21 +79,20 @@ describe('Button Plugin', () => { it('should generate correct axe command for button press with duration', async () => { let capturedCommand: string[] = []; - const trackingExecutor = async (command: string[]) => { + const trackingExecutor: CommandExecutor = async (command) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'button press completed', error: undefined, - process: { pid: 12345 }, - }; + }); }; const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -126,21 +120,20 @@ describe('Button Plugin', () => { it('should generate correct axe command for different button types', async () => { let capturedCommand: string[] = []; - const trackingExecutor = async (command: string[]) => { + const trackingExecutor: CommandExecutor = async (command) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'button press completed', error: undefined, - process: { pid: 12345 }, - }; + }); }; const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -165,19 +158,22 @@ describe('Button Plugin', () => { it('should generate correct axe command with bundled axe path', async () => { let capturedCommand: string[] = []; - const trackingExecutor = async (command: string[]) => { + const trackingExecutor: CommandExecutor = async (command) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'button press completed', error: undefined, - process: { pid: 12345 }, - }; + }); }; const mockAxeHelpers = { getAxePath: () => '/path/to/bundled/axe', getBundledAxeEnvironment: () => ({ AXE_PATH: '/some/path' }), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'axe not available' }], + isError: true, + }), }; await buttonLogic( @@ -201,7 +197,7 @@ describe('Button Plugin', () => { describe('Handler Behavior (Complete Literal Returns)', () => { it('should surface session default requirement when simulatorId is missing', async () => { - const result = await buttonPlugin.handler({ buttonType: 'home' }); + const result = await handler({ buttonType: 'home' }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -209,7 +205,7 @@ describe('Button Plugin', () => { }); it('should return error for missing buttonType', async () => { - const result = await buttonPlugin.handler({ + const result = await handler({ simulatorId: '12345678-1234-4234-8234-123456789012', }); @@ -221,7 +217,7 @@ describe('Button Plugin', () => { }); it('should return error for invalid simulatorId format', async () => { - const result = await buttonPlugin.handler({ + const result = await handler({ simulatorId: 'invalid-uuid-format', buttonType: 'home', }); @@ -232,7 +228,7 @@ describe('Button Plugin', () => { }); it('should return error for invalid buttonType', async () => { - const result = await buttonPlugin.handler({ + const result = await handler({ simulatorId: '12345678-1234-4234-8234-123456789012', buttonType: 'invalid-button', }); @@ -242,7 +238,7 @@ describe('Button Plugin', () => { }); it('should return error for negative duration', async () => { - const result = await buttonPlugin.handler({ + const result = await handler({ simulatorId: '12345678-1234-4234-8234-123456789012', buttonType: 'home', duration: -1, @@ -265,7 +261,7 @@ describe('Button Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -280,7 +276,7 @@ describe('Button Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: "Hardware button 'home' pressed successfully." }], + content: [{ type: 'text' as const, text: "Hardware button 'home' pressed successfully." }], isError: false, }); }); @@ -297,7 +293,7 @@ describe('Button Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -313,7 +309,9 @@ describe('Button Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: "Hardware button 'side-button' pressed successfully." }], + content: [ + { type: 'text' as const, text: "Hardware button 'side-button' pressed successfully." }, + ], isError: false, }); }); @@ -325,8 +323,8 @@ describe('Button Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -345,8 +343,8 @@ describe('Button Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -365,7 +363,7 @@ describe('Button Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -382,7 +380,7 @@ describe('Button Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: "Error: Failed to press button 'home': axe command 'button' failed.\nDetails: axe command failed", }, ], @@ -399,7 +397,7 @@ describe('Button Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -428,7 +426,7 @@ describe('Button Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -457,7 +455,7 @@ describe('Button Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'axe not available' }], + content: [{ type: 'text' as const, text: 'axe not available' }], isError: true, }), }; @@ -474,7 +472,7 @@ describe('Button Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Error: System error executing axe: Failed to execute axe command: String error', }, ], diff --git a/src/mcp/tools/ui-testing/__tests__/gesture.test.ts b/src/mcp/tools/ui-automation/__tests__/gesture.test.ts similarity index 78% rename from src/mcp/tools/ui-testing/__tests__/gesture.test.ts rename to src/mcp/tools/ui-automation/__tests__/gesture.test.ts index 0591d801..d05e8600 100644 --- a/src/mcp/tools/ui-testing/__tests__/gesture.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/gesture.test.ts @@ -6,11 +6,12 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { createMockExecutor, - createMockFileSystemExecutor, createNoopExecutor, + mockProcess, } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import gesturePlugin, { gestureLogic } from '../gesture.ts'; +import { schema, handler, gestureLogic } from '../gesture.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; describe('Gesture Plugin', () => { beforeEach(() => { @@ -18,26 +19,16 @@ describe('Gesture Plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(gesturePlugin.name).toBe('gesture'); - }); - - it('should have correct description', () => { - expect(gesturePlugin.description).toBe( - 'Perform gesture on iOS simulator using preset gestures: scroll-up, scroll-down, scroll-left, scroll-right, swipe-from-left-edge, swipe-from-right-edge, swipe-from-top-edge, swipe-from-bottom-edge', - ); - }); - it('should have handler function', () => { - expect(typeof gesturePlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose public schema without simulatorId field', () => { - const schema = z.object(gesturePlugin.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ preset: 'scroll-up' }).success).toBe(true); + expect(schemaObj.safeParse({ preset: 'scroll-up' }).success).toBe(true); expect( - schema.safeParse({ + schemaObj.safeParse({ preset: 'scroll-up', screenWidth: 375, screenHeight: 667, @@ -47,22 +38,22 @@ describe('Gesture Plugin', () => { postDelay: 0.2, }).success, ).toBe(true); - expect(schema.safeParse({ preset: 'invalid-preset' }).success).toBe(false); - expect(schema.safeParse({ preset: 'scroll-up', screenWidth: 0 }).success).toBe(false); - expect(schema.safeParse({ preset: 'scroll-up', duration: -1 }).success).toBe(false); + expect(schemaObj.safeParse({ preset: 'invalid-preset' }).success).toBe(false); + expect(schemaObj.safeParse({ preset: 'scroll-up', screenWidth: 0 }).success).toBe(false); + expect(schemaObj.safeParse({ preset: 'scroll-up', duration: -1 }).success).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', preset: 'scroll-up', }); expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as any)).toBe(false); + expect('simulatorId' in (withSimId.data as object)).toBe(false); }); }); describe('Handler Requirements', () => { it('should require simulatorId session default when not provided', async () => { - const result = await gesturePlugin.handler({ preset: 'scroll-up' }); + const result = await handler({ preset: 'scroll-up' }); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -74,7 +65,7 @@ describe('Gesture Plugin', () => { it('should surface validation errors once simulator defaults exist', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await gesturePlugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -94,13 +85,17 @@ describe('Gesture Plugin', () => { success: true, output: 'gesture completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; await gestureLogic( @@ -129,13 +124,17 @@ describe('Gesture Plugin', () => { success: true, output: 'gesture completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; await gestureLogic( @@ -170,13 +169,17 @@ describe('Gesture Plugin', () => { success: true, output: 'gesture completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; await gestureLogic( @@ -223,13 +226,17 @@ describe('Gesture Plugin', () => { success: true, output: 'gesture completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; await gestureLogic( @@ -261,12 +268,16 @@ describe('Gesture Plugin', () => { success: true, output: 'gesture completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }); const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; const result = await gestureLogic( @@ -279,7 +290,7 @@ describe('Gesture Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: "Gesture 'scroll-up' executed successfully." }], + content: [{ type: 'text' as const, text: "Gesture 'scroll-up' executed successfully." }], isError: false, }); }); @@ -289,12 +300,16 @@ describe('Gesture Plugin', () => { success: true, output: 'gesture completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }); const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; const result = await gestureLogic( @@ -313,7 +328,9 @@ describe('Gesture Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: "Gesture 'swipe-from-left-edge' executed successfully." }], + content: [ + { type: 'text' as const, text: "Gesture 'swipe-from-left-edge' executed successfully." }, + ], isError: false, }); }); @@ -325,8 +342,8 @@ describe('Gesture Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -345,8 +362,8 @@ describe('Gesture Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -358,12 +375,16 @@ describe('Gesture Plugin', () => { success: false, output: '', error: 'axe command failed', - process: { pid: 12345 }, + process: mockProcess, }); const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; const result = await gestureLogic( @@ -378,7 +399,7 @@ describe('Gesture Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: "Error: Failed to execute gesture 'scroll-up': axe command 'gesture' failed.\nDetails: axe command failed", }, ], @@ -392,6 +413,10 @@ describe('Gesture Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; const result = await gestureLogic( @@ -415,6 +440,10 @@ describe('Gesture Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; const result = await gestureLogic( @@ -438,6 +467,10 @@ describe('Gesture Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'AXe CLI is not available.' }], + isError: true, + }), }; const result = await gestureLogic( @@ -452,7 +485,7 @@ describe('Gesture Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Error: System error executing axe: Failed to execute axe command: String error', }, ], diff --git a/src/mcp/tools/ui-testing/__tests__/key_press.test.ts b/src/mcp/tools/ui-automation/__tests__/key_press.test.ts similarity index 55% rename from src/mcp/tools/ui-testing/__tests__/key_press.test.ts rename to src/mcp/tools/ui-automation/__tests__/key_press.test.ts index 9304c39d..bb0e8275 100644 --- a/src/mcp/tools/ui-testing/__tests__/key_press.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/key_press.test.ts @@ -1,60 +1,68 @@ /** - * Tests for key_press tool plugin + * Tests for key_press tool */ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { + createMockCommandResponse, createMockExecutor, - createMockFileSystemExecutor, createNoopExecutor, + mockProcess, } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import keyPressPlugin, { key_pressLogic } from '../key_press.ts'; +import { schema, handler, key_pressLogic } from '../key_press.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; + +function createDefaultMockAxeHelpers() { + return { + getAxePath: () => '/usr/local/bin/axe', + getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [ + { + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, + }, + ], + isError: true, + }), + }; +} -describe('Key Press Plugin', () => { +describe('Key Press Tool', () => { beforeEach(() => { sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(keyPressPlugin.name).toBe('key_press'); - }); - - it('should have correct description', () => { - expect(keyPressPlugin.description).toBe( - 'Press a single key by keycode on the simulator. Common keycodes: 40=Return, 42=Backspace, 43=Tab, 44=Space, 58-67=F1-F10.', - ); - }); - + describe('Schema Validation', () => { it('should have handler function', () => { - expect(typeof keyPressPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose public schema without simulatorId field', () => { - const schema = z.object(keyPressPlugin.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ keyCode: 40 }).success).toBe(true); - expect(schema.safeParse({ keyCode: 40, duration: 1.5 }).success).toBe(true); - expect(schema.safeParse({ keyCode: 'invalid' }).success).toBe(false); - expect(schema.safeParse({ keyCode: -1 }).success).toBe(false); - expect(schema.safeParse({ keyCode: 256 }).success).toBe(false); + expect(schemaObj.safeParse({ keyCode: 40 }).success).toBe(true); + expect(schemaObj.safeParse({ keyCode: 40, duration: 1.5 }).success).toBe(true); + expect(schemaObj.safeParse({ keyCode: 'invalid' }).success).toBe(false); + expect(schemaObj.safeParse({ keyCode: -1 }).success).toBe(false); + expect(schemaObj.safeParse({ keyCode: 256 }).success).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', keyCode: 40, }); expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as any)).toBe(false); + expect('simulatorId' in (withSimId.data as object)).toBe(false); - expect(schema.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({}).success).toBe(false); }); }); describe('Handler Requirements', () => { it('should require simulatorId session default when not provided', async () => { - const result = await keyPressPlugin.handler({ keyCode: 40 }); + const result = await handler({ keyCode: 40 }); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -66,7 +74,7 @@ describe('Key Press Plugin', () => { it('should surface validation errors once simulator default exists', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await keyPressPlugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -80,27 +88,15 @@ describe('Key Press Plugin', () => { let capturedCommand: string[] = []; const trackingExecutor = async (command: string[]) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'key press completed', error: undefined, - process: { pid: 12345 }, - }; + process: mockProcess, + }); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); await key_pressLogic( { @@ -124,27 +120,15 @@ describe('Key Press Plugin', () => { let capturedCommand: string[] = []; const trackingExecutor = async (command: string[]) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'key press completed', error: undefined, - process: { pid: 12345 }, - }; + process: mockProcess, + }); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); await key_pressLogic( { @@ -171,27 +155,15 @@ describe('Key Press Plugin', () => { let capturedCommand: string[] = []; const trackingExecutor = async (command: string[]) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'key press completed', error: undefined, - process: { pid: 12345 }, - }; + process: mockProcess, + }); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); await key_pressLogic( { @@ -215,12 +187,12 @@ describe('Key Press Plugin', () => { let capturedCommand: string[] = []; const trackingExecutor = async (command: string[]) => { capturedCommand = command; - return { + return createMockCommandResponse({ success: true, output: 'key press completed', error: undefined, - process: { pid: 12345 }, - }; + process: mockProcess, + }); }; const mockAxeHelpers = { @@ -229,8 +201,8 @@ describe('Key Press Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -267,19 +239,7 @@ describe('Key Press Plugin', () => { error: '', }); - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -291,7 +251,7 @@ describe('Key Press Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: 'Key press (code: 40) simulated successfully.' }], + content: [{ type: 'text' as const, text: 'Key press (code: 40) simulated successfully.' }], isError: false, }); }); @@ -303,19 +263,7 @@ describe('Key Press Plugin', () => { error: '', }); - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -328,7 +276,7 @@ describe('Key Press Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: 'Key press (code: 42) simulated successfully.' }], + content: [{ type: 'text' as const, text: 'Key press (code: 42) simulated successfully.' }], isError: false, }); }); @@ -340,8 +288,8 @@ describe('Key Press Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -360,11 +308,8 @@ describe('Key Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: - 'Bundled axe tool not found. UI automation features are not available.\n\n' + - 'This is likely an installation issue with the npm package.\n' + - 'Please reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -378,19 +323,7 @@ describe('Key Press Plugin', () => { error: 'axe command failed', }); - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -404,7 +337,7 @@ describe('Key Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: "Error: Failed to simulate key press (code: 40): axe command 'key' failed.\nDetails: axe command failed", }, ], @@ -417,19 +350,7 @@ describe('Key Press Plugin', () => { throw new Error('System error occurred'); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -451,19 +372,7 @@ describe('Key Press Plugin', () => { throw new Error('Unexpected error'); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -485,19 +394,7 @@ describe('Key Press Plugin', () => { throw 'String error'; }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -511,7 +408,7 @@ describe('Key Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Error: System error executing axe: Failed to execute axe command: String error', }, ], diff --git a/src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts b/src/mcp/tools/ui-automation/__tests__/key_sequence.test.ts similarity index 71% rename from src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts rename to src/mcp/tools/ui-automation/__tests__/key_sequence.test.ts index 904e308e..47443638 100644 --- a/src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/key_sequence.test.ts @@ -1,57 +1,52 @@ /** - * Tests for key_sequence plugin + * Tests for key_sequence tool */ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + createMockExecutor, + createNoopExecutor, + mockProcess, +} from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import keySequencePlugin, { key_sequenceLogic } from '../key_sequence.ts'; +import { schema, handler, key_sequenceLogic } from '../key_sequence.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; -describe('Key Sequence Plugin', () => { +describe('Key Sequence Tool', () => { beforeEach(() => { sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(keySequencePlugin.name).toBe('key_sequence'); - }); - - it('should have correct description', () => { - expect(keySequencePlugin.description).toBe( - 'Press key sequence using HID keycodes on iOS simulator with configurable delay', - ); - }); - + describe('Schema Validation', () => { it('should have handler function', () => { - expect(typeof keySequencePlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose public schema without simulatorId field', () => { - const schema = z.object(keySequencePlugin.schema); + const schemaObj = z.object(schema); - expect(schema.safeParse({ keyCodes: [40, 42, 44] }).success).toBe(true); - expect(schema.safeParse({ keyCodes: [40], delay: 0.1 }).success).toBe(true); - expect(schema.safeParse({ keyCodes: [] }).success).toBe(false); - expect(schema.safeParse({ keyCodes: [-1] }).success).toBe(false); - expect(schema.safeParse({ keyCodes: [256] }).success).toBe(false); - expect(schema.safeParse({ keyCodes: [40], delay: -0.1 }).success).toBe(false); + expect(schemaObj.safeParse({ keyCodes: [40, 42, 44] }).success).toBe(true); + expect(schemaObj.safeParse({ keyCodes: [40], delay: 0.1 }).success).toBe(true); + expect(schemaObj.safeParse({ keyCodes: [] }).success).toBe(false); + expect(schemaObj.safeParse({ keyCodes: [-1] }).success).toBe(false); + expect(schemaObj.safeParse({ keyCodes: [256] }).success).toBe(false); + expect(schemaObj.safeParse({ keyCodes: [40], delay: -0.1 }).success).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', keyCodes: [40], }); expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as any)).toBe(false); + expect('simulatorId' in (withSimId.data as Record)).toBe(false); - expect(schema.safeParse({}).success).toBe(false); + expect(schemaObj.safeParse({}).success).toBe(false); }); }); describe('Handler Requirements', () => { it('should require simulatorId session default when not provided', async () => { - const result = await keySequencePlugin.handler({ keyCodes: [40] }); + const result = await handler({ keyCodes: [40] }); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -63,7 +58,7 @@ describe('Key Sequence Plugin', () => { it('should surface validation errors once simulator defaults exist', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await keySequencePlugin.handler({ keyCodes: [] }); + const result = await handler({ keyCodes: [] }); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -81,7 +76,7 @@ describe('Key Sequence Plugin', () => { success: true, output: 'key sequence completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -91,8 +86,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -126,7 +121,7 @@ describe('Key Sequence Plugin', () => { success: true, output: 'key sequence completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -136,8 +131,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -174,7 +169,7 @@ describe('Key Sequence Plugin', () => { success: true, output: 'key sequence completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -184,8 +179,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -219,7 +214,7 @@ describe('Key Sequence Plugin', () => { success: true, output: 'key sequence completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -229,8 +224,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -262,7 +257,7 @@ describe('Key Sequence Plugin', () => { describe('Handler Behavior (Complete Literal Returns)', () => { it('should surface session default requirement when simulatorId is missing', async () => { - const result = await keySequencePlugin.handler({ keyCodes: [40] }); + const result = await handler({ keyCodes: [40] }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -282,8 +277,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -301,7 +296,9 @@ describe('Key Sequence Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: 'Key sequence [40,42,44] executed successfully.' }], + content: [ + { type: 'text' as const, text: 'Key sequence [40,42,44] executed successfully.' }, + ], isError: false, }); }); @@ -319,8 +316,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -337,7 +334,7 @@ describe('Key Sequence Plugin', () => { ); expect(result).toEqual({ - content: [{ type: 'text', text: 'Key sequence [40] executed successfully.' }], + content: [{ type: 'text' as const, text: 'Key sequence [40] executed successfully.' }], isError: false, }); }); @@ -349,8 +346,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -369,8 +366,8 @@ describe('Key Sequence Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -390,8 +387,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -410,7 +407,7 @@ describe('Key Sequence Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: "Error: Failed to execute key sequence: axe command 'key-sequence' failed.\nDetails: Simulator not found", }, ], @@ -429,8 +426,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -463,8 +460,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -497,8 +494,8 @@ describe('Key Sequence Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -517,7 +514,7 @@ describe('Key Sequence Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Error: System error executing axe: Failed to execute axe command: String error', }, ], diff --git a/src/mcp/tools/ui-testing/__tests__/long_press.test.ts b/src/mcp/tools/ui-automation/__tests__/long_press.test.ts similarity index 82% rename from src/mcp/tools/ui-testing/__tests__/long_press.test.ts rename to src/mcp/tools/ui-automation/__tests__/long_press.test.ts index 2acee9e2..3cbe9dc4 100644 --- a/src/mcp/tools/ui-testing/__tests__/long_press.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/long_press.test.ts @@ -4,9 +4,10 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { createMockExecutor, mockProcess } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import longPressPlugin, { long_pressLogic } from '../long_press.ts'; +import { schema, handler, long_pressLogic } from '../long_press.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; describe('Long Press Plugin', () => { beforeEach(() => { @@ -14,25 +15,15 @@ describe('Long Press Plugin', () => { }); describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(longPressPlugin.name).toBe('long_press'); - }); - - it('should have correct description', () => { - expect(longPressPlugin.description).toBe( - "Long press at specific coordinates for given duration (ms). Use describe_ui for precise coordinates (don't guess from screenshots).", - ); - }); - it('should have handler function', () => { - expect(typeof longPressPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema fields with safeParse', () => { - const schema = z.object(longPressPlugin.schema); + const schemaObject = z.object(schema); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 200, duration: 1500, @@ -40,7 +31,7 @@ describe('Long Press Plugin', () => { ).toBe(true); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100.5, y: 200, duration: 1500, @@ -48,7 +39,7 @@ describe('Long Press Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 200.5, duration: 1500, @@ -56,7 +47,7 @@ describe('Long Press Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 200, duration: 0, @@ -64,14 +55,14 @@ describe('Long Press Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 200, duration: -100, }).success, ).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObject.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', x: 100, y: 200, @@ -84,7 +75,7 @@ describe('Long Press Plugin', () => { describe('Handler Requirements', () => { it('should require simulatorId session default', async () => { - const result = await longPressPlugin.handler({ x: 100, y: 200, duration: 1500 }); + const result = await handler({ x: 100, y: 200, duration: 1500 }); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -96,7 +87,7 @@ describe('Long Press Plugin', () => { it('should surface validation errors once simulator default exists', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await longPressPlugin.handler({ x: 100, y: 200, duration: 0 }); + const result = await handler({ x: 100, y: 200, duration: 0 }); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -114,7 +105,7 @@ describe('Long Press Plugin', () => { success: true, output: 'long press completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -122,7 +113,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -162,7 +153,7 @@ describe('Long Press Plugin', () => { success: true, output: 'long press completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -170,7 +161,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -210,7 +201,7 @@ describe('Long Press Plugin', () => { success: true, output: 'long press completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -218,7 +209,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -258,7 +249,7 @@ describe('Long Press Plugin', () => { success: true, output: 'long press completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -266,7 +257,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/path/to/bundled/axe', getBundledAxeEnvironment: () => ({ AXE_PATH: '/some/path' }), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -311,7 +302,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -330,8 +321,8 @@ describe('Long Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Long press at (100, 200) for 1500ms simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + type: 'text' as const, + text: 'Long press at (100, 200) for 1500ms simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -343,7 +334,7 @@ describe('Long Press Plugin', () => { success: true, output: '', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }); const mockAxeHelpers = { @@ -352,8 +343,8 @@ describe('Long Press Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -374,8 +365,8 @@ describe('Long Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -387,14 +378,14 @@ describe('Long Press Plugin', () => { success: false, output: '', error: 'axe command failed', - process: { pid: 12345 }, + process: mockProcess, }); const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -413,7 +404,7 @@ describe('Long Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: "Error: Failed to simulate long press at (100, 200): axe command 'touch' failed.\nDetails: axe command failed", }, ], @@ -430,7 +421,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -449,7 +440,7 @@ describe('Long Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: expect.stringContaining( 'Error: System error executing axe: Failed to execute axe command: ENOENT: no such file or directory', ), @@ -468,7 +459,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -487,7 +478,7 @@ describe('Long Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: expect.stringContaining( 'Error: System error executing axe: Failed to execute axe command: Unexpected error', ), @@ -506,7 +497,7 @@ describe('Long Press Plugin', () => { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'Mock axe not available' }], + content: [{ type: 'text' as const, text: 'Mock axe not available' }], isError: true, }), }; @@ -525,7 +516,7 @@ describe('Long Press Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Error: System error executing axe: Failed to execute axe command: String error', }, ], diff --git a/src/mcp/tools/ui-automation/__tests__/screenshot.test.ts b/src/mcp/tools/ui-automation/__tests__/screenshot.test.ts new file mode 100644 index 00000000..dda945c6 --- /dev/null +++ b/src/mcp/tools/ui-automation/__tests__/screenshot.test.ts @@ -0,0 +1,877 @@ +/** + * Tests for screenshot tool plugin + */ + +import { describe, it, expect, beforeEach } from 'vitest'; +import * as z from 'zod'; +import { + createMockExecutor, + createMockFileSystemExecutor, + mockProcess, +} from '../../../../test-utils/mock-executors.ts'; +import { SystemError } from '../../../../utils/responses/index.ts'; +import { sessionStore } from '../../../../utils/session-store.ts'; +import { + schema, + handler, + screenshotLogic, + detectLandscapeMode, + rotateImage, +} from '../screenshot.ts'; + +describe('Screenshot Plugin', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation (Literal)', () => { + it('should have handler function', () => { + expect(typeof handler).toBe('function'); + }); + + it('should validate schema fields with safeParse', () => { + const schemaObj = z.object(schema); + + // Public schema is empty; ensure extra fields are stripped + expect(schemaObj.safeParse({}).success).toBe(true); + + const withSimId = schemaObj.safeParse({ + simulatorId: '12345678-1234-4234-8234-123456789012', + }); + expect(withSimId.success).toBe(true); + expect('simulatorId' in (withSimId.data as Record)).toBe(false); + }); + }); + + describe('Plugin Handler Validation', () => { + it('should require simulatorId session default when not provided', async () => { + const result = await handler({}); + + expect(result.isError).toBe(true); + const message = result.content[0].text; + expect(message).toContain('Missing required session defaults'); + expect(message).toContain('simulatorId is required'); + expect(message).toContain('session-set-defaults'); + }); + + it('should validate inline simulatorId overrides', async () => { + const result = await handler({ + simulatorId: 'invalid-uuid', + }); + + expect(result.isError).toBe(true); + const message = result.content[0].text; + expect(message).toContain('Parameter validation failed'); + expect(message).toContain('simulatorId: Invalid Simulator UUID format'); + }); + }); + + describe('Command Generation', () => { + it('should generate correct xcrun simctl command for basic screenshot', async () => { + const capturedCommands: string[][] = []; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + }; + + const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => mockImageBuffer.toString('utf8'), + }); + + await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + trackingExecutor, + mockFileSystemExecutor, + { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, + { v4: () => 'test-uuid' }, + ); + + // Should capture the screenshot command first + expect(capturedCommands[0]).toEqual([ + 'xcrun', + 'simctl', + 'io', + '12345678-1234-4234-8234-123456789012', + 'screenshot', + '/tmp/screenshot_test-uuid.png', + ]); + }); + + it('should generate correct xcrun simctl command with different simulator UUID', async () => { + const capturedCommands: string[][] = []; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + }; + + const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => mockImageBuffer.toString('utf8'), + }); + + await screenshotLogic( + { + simulatorId: 'ABCDEF12-3456-7890-ABCD-ABCDEFABCDEF', + }, + trackingExecutor, + mockFileSystemExecutor, + { tmpdir: () => '/var/tmp', join: (...paths) => paths.join('/') }, + { v4: () => 'another-uuid' }, + ); + + expect(capturedCommands[0]).toEqual([ + 'xcrun', + 'simctl', + 'io', + 'ABCDEF12-3456-7890-ABCD-ABCDEFABCDEF', + 'screenshot', + '/var/tmp/screenshot_another-uuid.png', + ]); + }); + + it('should generate correct xcrun simctl command with custom path dependencies', async () => { + const capturedCommands: string[][] = []; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + }; + + const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => mockImageBuffer.toString('utf8'), + }); + + await screenshotLogic( + { + simulatorId: '98765432-1098-7654-3210-987654321098', + }, + trackingExecutor, + mockFileSystemExecutor, + { + tmpdir: () => '/custom/temp/dir', + join: (...paths) => paths.join('\\'), // Windows-style path joining + }, + { v4: () => 'custom-uuid' }, + ); + + expect(capturedCommands[0]).toEqual([ + 'xcrun', + 'simctl', + 'io', + '98765432-1098-7654-3210-987654321098', + 'screenshot', + '/custom/temp/dir\\screenshot_custom-uuid.png', + ]); + }); + + it('should generate correct xcrun simctl command with generated UUID when no UUID deps provided', async () => { + const capturedCommands: string[][] = []; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + }; + + const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => mockImageBuffer.toString('utf8'), + }); + + await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + trackingExecutor, + mockFileSystemExecutor, + { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, + // No UUID deps provided - should use real uuidv4() + ); + + // Verify the command structure but not the exact UUID since it's generated + expect(capturedCommands[0].slice(0, 5)).toEqual([ + 'xcrun', + 'simctl', + 'io', + '12345678-1234-4234-8234-123456789012', + 'screenshot', + ]); + expect(capturedCommands[0][5]).toMatch(/^\/tmp\/screenshot_[a-f0-9-]+\.png$/); + }); + }); + + describe('Handler Behavior (Complete Literal Returns)', () => { + it('should handle parameter validation via plugin handler (not logic function)', async () => { + // Note: With Zod validation in createTypedTool, the screenshotLogic function + // will never receive invalid parameters - validation happens at the handler level. + // This test documents that screenshotLogic assumes valid parameters. + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + createMockExecutor({ + success: true, + output: 'Screenshot saved', + error: undefined, + }), + createMockFileSystemExecutor({ + readFile: async () => Buffer.from('fake-image-data', 'utf8').toString('utf8'), + }), + ); + + expect(result.isError).toBe(false); + expect(result.content[0].type).toBe('image'); + }); + + it('should return success for valid screenshot capture', async () => { + const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); + + const mockExecutor = createMockExecutor({ + success: true, + output: 'Screenshot saved', + error: undefined, + }); + + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => mockImageBuffer.toString('utf8'), + }); + + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + mockExecutor, + mockFileSystemExecutor, + ); + + expect(result.isError).toBe(false); + expect(result.content[0].type).toBe('image'); + }); + + it('should handle command execution failure', async () => { + const mockExecutor = createMockExecutor({ + success: false, + output: '', + error: 'Simulator not found', + }); + + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + mockExecutor, + createMockFileSystemExecutor(), + ); + + expect(result).toEqual({ + content: [ + { + type: 'text' as const, + text: 'Error: System error executing screenshot: Failed to capture screenshot: Simulator not found', + }, + ], + isError: true, + }); + }); + + it('should handle file reading errors', async () => { + const mockExecutor = createMockExecutor({ + success: true, + output: 'Screenshot saved', + error: undefined, + }); + + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => { + throw new Error('File not found'); + }, + }); + + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + returnFormat: 'base64', + }, + mockExecutor, + mockFileSystemExecutor, + ); + + expect(result).toEqual({ + content: [ + { + type: 'text' as const, + text: 'Error: Screenshot captured but failed to process image file: File not found', + }, + ], + isError: true, + }); + }); + + it('should handle file cleanup errors gracefully', async () => { + const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); + + const mockExecutor = createMockExecutor({ + success: true, + output: 'Screenshot saved', + error: undefined, + }); + + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => mockImageBuffer.toString('utf8'), + // unlink method is not overridden, so it will use the default (no-op) + // which simulates the cleanup failure being caught and logged + }); + + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + returnFormat: 'base64', + }, + mockExecutor, + mockFileSystemExecutor, + ); + + // Should still return successful result despite cleanup failure + expect(result).toEqual({ + content: [ + { + type: 'image', + data: 'fake-image-data', + mimeType: 'image/jpeg', + }, + ], + isError: false, + }); + }); + + it('should handle SystemError from command execution', async () => { + const mockExecutor = async () => { + throw new SystemError('System error occurred'); + }; + + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + mockExecutor, + createMockFileSystemExecutor(), + ); + + expect(result).toEqual({ + content: [ + { + type: 'text' as const, + text: 'Error: System error executing screenshot: System error occurred', + }, + ], + isError: true, + }); + }); + + it('should handle unexpected Error objects', async () => { + const mockExecutor = async () => { + throw new Error('Unexpected error'); + }; + + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + mockExecutor, + createMockFileSystemExecutor(), + ); + + expect(result).toEqual({ + content: [ + { type: 'text' as const, text: 'Error: An unexpected error occurred: Unexpected error' }, + ], + isError: true, + }); + }); + + it('should handle unexpected string errors', async () => { + const mockExecutor = async () => { + throw 'String error'; + }; + + const result = await screenshotLogic( + { + simulatorId: '12345678-1234-4234-8234-123456789012', + }, + mockExecutor, + createMockFileSystemExecutor(), + ); + + expect(result).toEqual({ + content: [ + { type: 'text' as const, text: 'Error: An unexpected error occurred: String error' }, + ], + isError: true, + }); + }); + }); + + describe('Landscape Detection', () => { + it('should detect landscape mode when window width > height', async () => { + const mockExecutor = async () => ({ + success: true, + output: '844,390', + error: undefined, + process: mockProcess, + }); + + const result = await detectLandscapeMode(mockExecutor, 'iPhone 15 Pro'); + + expect(result).toBe(true); + }); + + it('should detect portrait mode when window height > width', async () => { + const mockExecutor = async () => ({ + success: true, + output: '390,844', + error: undefined, + process: mockProcess, + }); + + const result = await detectLandscapeMode(mockExecutor, 'iPhone 15 Pro'); + + expect(result).toBe(false); + }); + + it('should return false when swift command fails', async () => { + const mockExecutor = async () => ({ + success: false, + output: '', + error: 'Command failed', + process: mockProcess, + }); + + const result = await detectLandscapeMode(mockExecutor, 'iPhone 15 Pro'); + + expect(result).toBe(false); + }); + + it('should return false when output format is unexpected', async () => { + const mockExecutor = async () => ({ + success: true, + output: 'invalid output', + error: undefined, + process: mockProcess, + }); + + const result = await detectLandscapeMode(mockExecutor, 'iPhone 15 Pro'); + + expect(result).toBe(false); + }); + + it('should return false when executor throws an error', async () => { + const mockExecutor = async () => { + throw new Error('Execution failed'); + }; + + const result = await detectLandscapeMode(mockExecutor, 'iPhone 15 Pro'); + + expect(result).toBe(false); + }); + + it('should handle output with whitespace and newlines', async () => { + const mockExecutor = async () => ({ + success: true, + output: '\n 844,390 \n', + error: undefined, + process: mockProcess, + }); + + const result = await detectLandscapeMode(mockExecutor, 'iPhone 15 Pro'); + + expect(result).toBe(true); + }); + + it('should return false when no device name is provided', async () => { + const mockExecutor = async () => ({ + success: true, + output: '844,390', + error: undefined, + process: mockProcess, + }); + + // When no device name is provided, should skip orientation detection + const result = await detectLandscapeMode(mockExecutor); + + expect(result).toBe(false); + }); + }); + + describe('Image Rotation', () => { + it('should call sips with correct rotation arguments', async () => { + const capturedCommands: string[][] = []; + const mockExecutor = async (command: string[]) => { + capturedCommands.push(command); + return { + success: true, + output: '', + error: undefined, + process: mockProcess, + }; + }; + + await rotateImage('/tmp/test.png', 90, mockExecutor); + + expect(capturedCommands[0]).toEqual(['sips', '--rotate', '90', '/tmp/test.png']); + }); + + it('should return true on successful rotation', async () => { + const mockExecutor = async () => ({ + success: true, + output: '', + error: undefined, + process: mockProcess, + }); + + const result = await rotateImage('/tmp/test.png', 90, mockExecutor); + + expect(result).toBe(true); + }); + + it('should return false when rotation command fails', async () => { + const mockExecutor = async () => ({ + success: false, + output: '', + error: 'sips: error', + process: mockProcess, + }); + + const result = await rotateImage('/tmp/test.png', 90, mockExecutor); + + expect(result).toBe(false); + }); + + it('should return false when executor throws an error', async () => { + const mockExecutor = async () => { + throw new Error('Execution failed'); + }; + + const result = await rotateImage('/tmp/test.png', 90, mockExecutor); + + expect(result).toBe(false); + }); + + it('should handle different rotation angles', async () => { + const capturedCommands: string[][] = []; + const mockExecutor = async (command: string[]) => { + capturedCommands.push(command); + return { + success: true, + output: '', + error: undefined, + process: mockProcess, + }; + }; + + await rotateImage('/tmp/test.png', 270, mockExecutor); + + expect(capturedCommands[0]).toEqual(['sips', '--rotate', '270', '/tmp/test.png']); + }); + }); + + describe('Landscape Screenshot Integration', () => { + // Mock device list JSON response + const mockDeviceListJson = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-17-2': [ + { + udid: '12345678-1234-4234-8234-123456789012', + name: 'iPhone 15 Pro', + state: 'Booted', + }, + ], + }, + }); + + it('should rotate screenshot when landscape mode is detected', async () => { + const capturedCommands: string[][] = []; + let commandIndex = 0; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + const idx = commandIndex++; + + // First call: screenshot command + if (idx === 0) { + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + } + // Second call: list devices to get device name + if (idx === 1) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Third call: swift orientation detection (simulate landscape) + if (idx === 2) { + return { + success: true, + output: '844,390', + error: undefined, + process: mockProcess, + }; + } + // Fourth call: sips rotation + if (idx === 3) { + return { + success: true, + output: '', + error: undefined, + process: mockProcess, + }; + } + // Fifth call: sips optimization + return { + success: true, + output: '', + error: undefined, + process: mockProcess, + }; + }; + + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => 'fake-image-data', + }); + + await screenshotLogic( + { simulatorId: '12345678-1234-4234-8234-123456789012' }, + trackingExecutor, + mockFileSystemExecutor, + { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, + { v4: () => 'test-uuid' }, + ); + + // Verify rotation command was called with +90 degrees (index 3) + expect(capturedCommands[3]).toEqual([ + 'sips', + '--rotate', + '90', + '/tmp/screenshot_test-uuid.png', + ]); + }); + + it('should not rotate screenshot when portrait mode is detected', async () => { + const capturedCommands: string[][] = []; + let commandIndex = 0; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + const idx = commandIndex++; + + // First call: screenshot command + if (idx === 0) { + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + } + // Second call: list devices to get device name + if (idx === 1) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Third call: swift orientation detection (simulate portrait) + if (idx === 2) { + return { + success: true, + output: '390,844', + error: undefined, + process: mockProcess, + }; + } + // Fourth call: sips optimization (no rotation in portrait) + return { + success: true, + output: '', + error: undefined, + process: mockProcess, + }; + }; + + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => 'fake-image-data', + }); + + await screenshotLogic( + { simulatorId: '12345678-1234-4234-8234-123456789012' }, + trackingExecutor, + mockFileSystemExecutor, + { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, + { v4: () => 'test-uuid' }, + ); + + // Should have: screenshot, list devices, orientation detection, optimization (no rotation) + expect(capturedCommands.length).toBe(4); + // Fourth command should be optimization, not rotation + expect(capturedCommands[3][0]).toBe('sips'); + expect(capturedCommands[3]).toContain('-Z'); + }); + + it('should continue without rotation if orientation detection fails', async () => { + const capturedCommands: string[][] = []; + let commandIndex = 0; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + const idx = commandIndex++; + + // First call: screenshot command + if (idx === 0) { + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + } + // Second call: list devices to get device name + if (idx === 1) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Third call: swift orientation detection (fails) + if (idx === 2) { + return { + success: false, + output: '', + error: 'Swift not found', + process: mockProcess, + }; + } + // Fourth call: sips optimization + return { + success: true, + output: '', + error: undefined, + process: mockProcess, + }; + }; + + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => 'fake-image-data', + }); + + const result = await screenshotLogic( + { simulatorId: '12345678-1234-4234-8234-123456789012' }, + trackingExecutor, + mockFileSystemExecutor, + { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, + { v4: () => 'test-uuid' }, + ); + + // Should still succeed + expect(result.isError).toBe(false); + // Should have: screenshot, list devices, failed orientation detection, optimization + expect(capturedCommands.length).toBe(4); + }); + + it('should continue if rotation fails but still return image', async () => { + const capturedCommands: string[][] = []; + let commandIndex = 0; + const trackingExecutor = async (command: string[]) => { + capturedCommands.push(command); + const idx = commandIndex++; + + // First call: screenshot command + if (idx === 0) { + return { + success: true, + output: 'Screenshot saved', + error: undefined, + process: mockProcess, + }; + } + // Second call: list devices to get device name + if (idx === 1) { + return { + success: true, + output: mockDeviceListJson, + error: undefined, + process: mockProcess, + }; + } + // Third call: swift orientation detection (landscape) + if (idx === 2) { + return { + success: true, + output: '844,390', + error: undefined, + process: mockProcess, + }; + } + // Fourth call: sips rotation (fails) + if (idx === 3) { + return { + success: false, + output: '', + error: 'sips failed', + process: mockProcess, + }; + } + // Fifth call: sips optimization + return { + success: true, + output: '', + error: undefined, + process: mockProcess, + }; + }; + + const mockFileSystemExecutor = createMockFileSystemExecutor({ + readFile: async () => 'fake-image-data', + }); + + const result = await screenshotLogic( + { simulatorId: '12345678-1234-4234-8234-123456789012', returnFormat: 'base64' }, + trackingExecutor, + mockFileSystemExecutor, + { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, + { v4: () => 'test-uuid' }, + ); + + // Should still succeed even if rotation failed + expect(result.isError).toBe(false); + expect(result.content[0].type).toBe('image'); + }); + }); +}); diff --git a/src/mcp/tools/ui-testing/__tests__/describe_ui.test.ts b/src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts similarity index 69% rename from src/mcp/tools/ui-testing/__tests__/describe_ui.test.ts rename to src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts index 5df25f14..6abbf952 100644 --- a/src/mcp/tools/ui-testing/__tests__/describe_ui.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts @@ -1,38 +1,28 @@ /** - * Tests for describe_ui tool plugin + * Tests for snapshot_ui tool plugin */ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; -import describeUIPlugin, { describe_uiLogic } from '../describe_ui.ts'; - -describe('Describe UI Plugin', () => { - let mockCalls: any[] = []; - - mockCalls = []; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; +import { schema, handler, snapshot_uiLogic } from '../snapshot_ui.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; +describe('Snapshot UI Plugin', () => { describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(describeUIPlugin.name).toBe('describe_ui'); - }); - - it('should have correct description', () => { - expect(describeUIPlugin.description).toBe( - 'Gets entire view hierarchy with precise frame coordinates (x, y, width, height) for all visible elements. Use this before UI interactions or after layout changes - do NOT guess coordinates from screenshots. Returns JSON tree with frame data for accurate automation.', - ); - }); - it('should have handler function', () => { - expect(typeof describeUIPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should expose public schema without simulatorId field', () => { - const schema = z.object(describeUIPlugin.schema); + const schemaObject = z.object(schema); - expect(schema.safeParse({}).success).toBe(true); + expect(schemaObject.safeParse({}).success).toBe(true); - const withSimId = schema.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012' }); + const withSimId = schemaObject.safeParse({ + simulatorId: '12345678-1234-4234-8234-123456789012', + }); expect(withSimId.success).toBe(true); expect('simulatorId' in (withSimId.data as any)).toBe(false); }); @@ -40,7 +30,7 @@ describe('Describe UI Plugin', () => { describe('Handler Behavior (Complete Literal Returns)', () => { it('should surface session default requirement when simulatorId is missing', async () => { - const result = await describeUIPlugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -49,7 +39,7 @@ describe('Describe UI Plugin', () => { it('should handle invalid simulatorId format via schema validation', async () => { // Test the actual handler with invalid UUID format - const result = await describeUIPlugin.handler({ + const result = await handler({ simulatorId: 'invalid-uuid-format', }); @@ -58,7 +48,7 @@ describe('Describe UI Plugin', () => { expect(result.content[0].text).toContain('Invalid Simulator UUID format'); }); - it('should return success for valid describe_ui execution', async () => { + it('should return success for valid snapshot_ui execution', async () => { const uiHierarchy = '{"elements": [{"type": "Button", "frame": {"x": 100, "y": 200, "width": 50, "height": 30}}]}'; @@ -73,16 +63,20 @@ describe('Describe UI Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'axe not available' }], + isError: true, + }), }; // Wrap executor to track calls const executorCalls: any[] = []; - const trackingExecutor = async (...args: any[]) => { + const trackingExecutor: CommandExecutor = async (...args) => { executorCalls.push(args); return mockExecutor(...args); }; - const result = await describe_uiLogic( + const result = await snapshot_uiLogic( { simulatorId: '12345678-1234-4234-8234-123456789012', }, @@ -94,23 +88,25 @@ describe('Describe UI Plugin', () => { ['/usr/local/bin/axe', 'describe-ui', '--udid', '12345678-1234-4234-8234-123456789012'], '[AXe]: describe-ui', false, - {}, + { env: {} }, ]); expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Accessibility hierarchy retrieved successfully:\n```json\n{"elements": [{"type": "Button", "frame": {"x": 100, "y": 200, "width": 50, "height": 30}}]}\n```', }, { - type: 'text', - text: `Next Steps: -- Use frame coordinates for tap/swipe (center: x+width/2, y+height/2) -- Re-run describe_ui after layout changes -- Screenshots are for visual verification only`, + type: 'text' as const, + text: 'Tips:\n- Use frame coordinates for tap/swipe (center: x+width/2, y+height/2)\n- If a debugger is attached, ensure the app is running (not stopped on breakpoints)\n- Screenshots are for visual verification only', }, ], + nextStepParams: { + snapshot_ui: { simulatorId: '12345678-1234-4234-8234-123456789012' }, + tap: { simulatorId: '12345678-1234-4234-8234-123456789012', x: 0, y: 0 }, + screenshot: { simulatorId: '12345678-1234-4234-8234-123456789012' }, + }, }); }); @@ -122,15 +118,15 @@ describe('Describe UI Plugin', () => { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, }), }; - const result = await describe_uiLogic( + const result = await snapshot_uiLogic( { simulatorId: '12345678-1234-4234-8234-123456789012', }, @@ -141,8 +137,8 @@ describe('Describe UI Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -161,9 +157,13 @@ describe('Describe UI Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'axe not available' }], + isError: true, + }), }; - const result = await describe_uiLogic( + const result = await snapshot_uiLogic( { simulatorId: '12345678-1234-4234-8234-123456789012', }, @@ -174,7 +174,7 @@ describe('Describe UI Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: "Error: Failed to get accessibility hierarchy: axe command 'describe-ui' failed.\nDetails: axe command failed", }, ], @@ -189,9 +189,13 @@ describe('Describe UI Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'axe not available' }], + isError: true, + }), }; - const result = await describe_uiLogic( + const result = await snapshot_uiLogic( { simulatorId: '12345678-1234-4234-8234-123456789012', }, @@ -202,7 +206,7 @@ describe('Describe UI Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: expect.stringContaining( 'Error: System error executing axe: Failed to execute axe command: ENOENT: no such file or directory', ), @@ -219,9 +223,13 @@ describe('Describe UI Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'axe not available' }], + isError: true, + }), }; - const result = await describe_uiLogic( + const result = await snapshot_uiLogic( { simulatorId: '12345678-1234-4234-8234-123456789012', }, @@ -232,7 +240,7 @@ describe('Describe UI Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: expect.stringContaining( 'Error: System error executing axe: Failed to execute axe command: Unexpected error', ), @@ -249,9 +257,13 @@ describe('Describe UI Plugin', () => { const mockAxeHelpers = { getAxePath: () => '/usr/local/bin/axe', getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [{ type: 'text' as const, text: 'axe not available' }], + isError: true, + }), }; - const result = await describe_uiLogic( + const result = await snapshot_uiLogic( { simulatorId: '12345678-1234-4234-8234-123456789012', }, @@ -262,7 +274,7 @@ describe('Describe UI Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Error: System error executing axe: Failed to execute axe command: String error', }, ], diff --git a/src/mcp/tools/ui-testing/__tests__/swipe.test.ts b/src/mcp/tools/ui-automation/__tests__/swipe.test.ts similarity index 83% rename from src/mcp/tools/ui-testing/__tests__/swipe.test.ts rename to src/mcp/tools/ui-automation/__tests__/swipe.test.ts index ae2cb174..165327b6 100644 --- a/src/mcp/tools/ui-testing/__tests__/swipe.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/swipe.test.ts @@ -1,15 +1,15 @@ /** - * Tests for swipe tool plugin + * Tests for swipe tool */ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; -import { SystemError, DependencyError } from '../../../../utils/responses/index.ts'; +import { createMockExecutor, mockProcess } from '../../../../test-utils/mock-executors.ts'; +import { SystemError } from '../../../../utils/responses/index.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -// Import the plugin module to test -import swipePlugin, { AxeHelpers, swipeLogic, SwipeParams } from '../swipe.ts'; +import { schema, handler, type AxeHelpers, swipeLogic, type SwipeParams } from '../swipe.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; // Helper function to create mock axe helpers function createMockAxeHelpers(): AxeHelpers { @@ -19,8 +19,8 @@ function createMockAxeHelpers(): AxeHelpers { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -36,8 +36,8 @@ function createMockAxeHelpersWithNullPath(): AxeHelpers { createAxeNotAvailableResponse: () => ({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -45,30 +45,21 @@ function createMockAxeHelpersWithNullPath(): AxeHelpers { }; } -describe('Swipe Plugin', () => { +describe('Swipe Tool', () => { beforeEach(() => { sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(swipePlugin.name).toBe('swipe'); - }); - - it('should have correct description', () => { - expect(swipePlugin.description).toBe( - "Swipe from one point to another. Use describe_ui for precise coordinates (don't guess from screenshots). Supports configurable timing.", - ); - }); + describe('Schema Validation', () => { it('should have handler function', () => { - expect(typeof swipePlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema fields with safeParse', () => { - const schema = z.object(swipePlugin.schema); + const schemaObject = z.object(schema); expect( - schema.safeParse({ + schemaObject.safeParse({ x1: 100, y1: 200, x2: 300, @@ -77,7 +68,7 @@ describe('Swipe Plugin', () => { ).toBe(true); expect( - schema.safeParse({ + schemaObject.safeParse({ x1: 100.5, y1: 200, x2: 300, @@ -86,7 +77,7 @@ describe('Swipe Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x1: 100, y1: 200, x2: 300, @@ -96,7 +87,7 @@ describe('Swipe Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x1: 100, y1: 200, x2: 300, @@ -108,7 +99,7 @@ describe('Swipe Plugin', () => { }).success, ).toBe(true); - const withSimId = schema.safeParse({ + const withSimId = schemaObject.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', x1: 100, y1: 200, @@ -129,7 +120,7 @@ describe('Swipe Plugin', () => { success: true, output: 'swipe completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -171,7 +162,7 @@ describe('Swipe Plugin', () => { success: true, output: 'swipe completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -216,7 +207,7 @@ describe('Swipe Plugin', () => { success: true, output: 'swipe completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -270,7 +261,7 @@ describe('Swipe Plugin', () => { success: true, output: 'swipe completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -278,7 +269,7 @@ describe('Swipe Plugin', () => { getAxePath: () => '/path/to/bundled/axe', getBundledAxeEnvironment: () => ({ AXE_PATH: '/some/path' }), createAxeNotAvailableResponse: () => ({ - content: [{ type: 'text', text: 'AXe tools not available' }], + content: [{ type: 'text' as const, text: 'AXe tools not available' }], isError: true, }), }; @@ -315,9 +306,9 @@ describe('Swipe Plugin', () => { }); }); - describe('Handler Behavior (Complete Literal Returns)', () => { + describe('Handler Behavior', () => { it('should return error for missing simulatorId via handler', async () => { - const result = await swipePlugin.handler({ x1: 100, y1: 200, x2: 300, y2: 400 }); + const result = await handler({ x1: 100, y1: 200, x2: 300, y2: 400 }); expect(result.isError).toBe(true); expect(result.content[0].type).toBe('text'); @@ -329,7 +320,7 @@ describe('Swipe Plugin', () => { it('should return validation error for missing x1 once simulator default exists', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await swipePlugin.handler({ + const result = await handler({ y1: 200, x2: 300, y2: 400, @@ -367,8 +358,8 @@ describe('Swipe Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Swipe from (100, 200) to (300, 400) simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + type: 'text' as const, + text: 'Swipe from (100, 200) to (300, 400) simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -400,8 +391,8 @@ describe('Swipe Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Swipe from (100, 200) to (300, 400) duration=1.5s simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + type: 'text' as const, + text: 'Swipe from (100, 200) to (300, 400) duration=1.5s simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -432,8 +423,8 @@ describe('Swipe Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + type: 'text' as const, + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -464,7 +455,7 @@ describe('Swipe Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: "Error: Failed to simulate swipe: axe command 'swipe' failed.\nDetails: axe command failed", }, ], @@ -551,7 +542,7 @@ describe('Swipe Plugin', () => { expect(result).toEqual({ content: [ { - type: 'text', + type: 'text' as const, text: 'Error: System error executing axe: Failed to execute axe command: String error', }, ], diff --git a/src/mcp/tools/ui-testing/__tests__/tap.test.ts b/src/mcp/tools/ui-automation/__tests__/tap.test.ts similarity index 80% rename from src/mcp/tools/ui-testing/__tests__/tap.test.ts rename to src/mcp/tools/ui-automation/__tests__/tap.test.ts index 045dbc3b..9f19de5a 100644 --- a/src/mcp/tools/ui-testing/__tests__/tap.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/tap.test.ts @@ -7,7 +7,8 @@ import * as z from 'zod'; import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import tapPlugin, { AxeHelpers, tapLogic } from '../tap.ts'; +import { schema, handler, type AxeHelpers, tapLogic } from '../tap.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; // Helper function to create mock axe helpers function createMockAxeHelpers(): AxeHelpers { @@ -18,7 +19,7 @@ function createMockAxeHelpers(): AxeHelpers { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -35,7 +36,7 @@ function createMockAxeHelpersWithNullPath(): AxeHelpers { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -48,38 +49,28 @@ describe('Tap Plugin', () => { sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(tapPlugin.name).toBe('tap'); - }); - - it('should have correct description', () => { - expect(tapPlugin.description).toBe( - "Tap at specific coordinates or target elements by accessibility id or label. Use describe_ui to get precise element coordinates prior to using x/y parameters (don't guess from screenshots). Supports optional timing delays.", - ); - }); - + describe('Schema Validation', () => { it('should have handler function', () => { - expect(typeof tapPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema fields with safeParse', () => { - const schema = z.object(tapPlugin.schema); + const schemaObject = z.object(schema); - expect(schema.safeParse({ x: 100, y: 200 }).success).toBe(true); + expect(schemaObject.safeParse({ x: 100, y: 200 }).success).toBe(true); - expect(schema.safeParse({ id: 'loginButton' }).success).toBe(true); + expect(schemaObject.safeParse({ id: 'loginButton' }).success).toBe(true); - expect(schema.safeParse({ label: 'Log in' }).success).toBe(true); + expect(schemaObject.safeParse({ label: 'Log in' }).success).toBe(true); - expect(schema.safeParse({ x: 100, y: 200, id: 'loginButton' }).success).toBe(true); + expect(schemaObject.safeParse({ x: 100, y: 200, id: 'loginButton' }).success).toBe(true); - expect(schema.safeParse({ x: 100, y: 200, id: 'loginButton', label: 'Log in' }).success).toBe( - true, - ); + expect( + schemaObject.safeParse({ x: 100, y: 200, id: 'loginButton', label: 'Log in' }).success, + ).toBe(true); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 200, preDelay: 0.5, @@ -88,21 +79,21 @@ describe('Tap Plugin', () => { ).toBe(true); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 3.14, y: 200, }).success, ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 3.14, }).success, ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 200, preDelay: -1, @@ -110,14 +101,14 @@ describe('Tap Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ x: 100, y: 200, postDelay: -1, }).success, ).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObject.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', x: 100, y: 200, @@ -132,7 +123,7 @@ describe('Tap Plugin', () => { command: string[]; logPrefix?: string; useShell?: boolean; - env?: Record; + opts?: { env?: Record; cwd?: string }; }>; beforeEach(() => { @@ -149,10 +140,10 @@ describe('Tap Plugin', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record; cwd?: string }, ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + callHistory.push({ command, logPrefix, useShell, opts }); + return mockExecutor(command, logPrefix, useShell, opts); }; const mockAxeHelpers = createMockAxeHelpers(); @@ -181,7 +172,7 @@ describe('Tap Plugin', () => { ], logPrefix: '[AXe]: tap', useShell: false, - env: { SOME_ENV: 'value' }, + opts: { env: { SOME_ENV: 'value' } }, }); }); @@ -195,10 +186,10 @@ describe('Tap Plugin', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record; cwd?: string }, ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + callHistory.push({ command, logPrefix, useShell, opts }); + return mockExecutor(command, logPrefix, useShell, opts); }; const mockAxeHelpers = createMockAxeHelpers(); @@ -224,7 +215,7 @@ describe('Tap Plugin', () => { ], logPrefix: '[AXe]: tap', useShell: false, - env: { SOME_ENV: 'value' }, + opts: { env: { SOME_ENV: 'value' } }, }); }); @@ -238,10 +229,10 @@ describe('Tap Plugin', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record; cwd?: string }, ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + callHistory.push({ command, logPrefix, useShell, opts }); + return mockExecutor(command, logPrefix, useShell, opts); }; const mockAxeHelpers = createMockAxeHelpers(); @@ -267,7 +258,7 @@ describe('Tap Plugin', () => { ], logPrefix: '[AXe]: tap', useShell: false, - env: { SOME_ENV: 'value' }, + opts: { env: { SOME_ENV: 'value' } }, }); }); @@ -281,10 +272,10 @@ describe('Tap Plugin', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record; cwd?: string }, ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + callHistory.push({ command, logPrefix, useShell, opts }); + return mockExecutor(command, logPrefix, useShell, opts); }; const mockAxeHelpers = createMockAxeHelpers(); @@ -314,7 +305,7 @@ describe('Tap Plugin', () => { ], logPrefix: '[AXe]: tap', useShell: false, - env: { SOME_ENV: 'value' }, + opts: { env: { SOME_ENV: 'value' } }, }); }); @@ -328,10 +319,10 @@ describe('Tap Plugin', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record; cwd?: string }, ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + callHistory.push({ command, logPrefix, useShell, opts }); + return mockExecutor(command, logPrefix, useShell, opts); }; const mockAxeHelpers = createMockAxeHelpers(); @@ -363,7 +354,7 @@ describe('Tap Plugin', () => { ], logPrefix: '[AXe]: tap', useShell: false, - env: { SOME_ENV: 'value' }, + opts: { env: { SOME_ENV: 'value' } }, }); }); @@ -377,10 +368,10 @@ describe('Tap Plugin', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record; cwd?: string }, ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + callHistory.push({ command, logPrefix, useShell, opts }); + return mockExecutor(command, logPrefix, useShell, opts); }; const mockAxeHelpers = createMockAxeHelpers(); @@ -412,7 +403,7 @@ describe('Tap Plugin', () => { ], logPrefix: '[AXe]: tap', useShell: false, - env: { SOME_ENV: 'value' }, + opts: { env: { SOME_ENV: 'value' } }, }); }); @@ -426,10 +417,10 @@ describe('Tap Plugin', () => { command: string[], logPrefix?: string, useShell?: boolean, - env?: Record, + opts?: { env?: Record; cwd?: string }, ) => { - callHistory.push({ command, logPrefix, useShell, env }); - return mockExecutor(command, logPrefix, useShell, env); + callHistory.push({ command, logPrefix, useShell, opts }); + return mockExecutor(command, logPrefix, useShell, opts); }; const mockAxeHelpers = createMockAxeHelpers(); @@ -464,7 +455,7 @@ describe('Tap Plugin', () => { ], logPrefix: '[AXe]: tap', useShell: false, - env: { SOME_ENV: 'value' }, + opts: { env: { SOME_ENV: 'value' } }, }); }); }); @@ -492,14 +483,14 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Tap at (100, 200) simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Tap at (100, 200) simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, }); }); - it('should return successful response with coordinate warning when describe_ui not called', async () => { + it('should return successful response with coordinate warning when snapshot_ui not called', async () => { const mockExecutor = createMockExecutor({ success: true, output: 'Tap completed', @@ -521,7 +512,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Tap at (150, 300) simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Tap at (150, 300) simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -552,7 +543,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Tap at (250, 400) simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Tap at (250, 400) simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -581,7 +572,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Tap at (0, 0) simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Tap at (0, 0) simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -610,7 +601,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Tap at (1920, 1080) simulated successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Tap at (1920, 1080) simulated successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -676,7 +667,7 @@ describe('Tap Plugin', () => { describe('Plugin Handler Validation', () => { it('should require simulatorId session default when not provided', async () => { - const result = await tapPlugin.handler({ + const result = await handler({ x: 100, y: 200, }); @@ -691,7 +682,7 @@ describe('Tap Plugin', () => { it('should return validation error for missing x coordinate', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await tapPlugin.handler({ + const result = await handler({ y: 200, }); @@ -704,7 +695,7 @@ describe('Tap Plugin', () => { it('should return validation error for missing y coordinate', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await tapPlugin.handler({ + const result = await handler({ x: 100, }); @@ -717,7 +708,7 @@ describe('Tap Plugin', () => { it('should return validation error when both id and label are provided without coordinates', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await tapPlugin.handler({ + const result = await handler({ id: 'loginButton', label: 'Log in', }); @@ -731,7 +722,7 @@ describe('Tap Plugin', () => { it('should return validation error for non-integer x coordinate', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await tapPlugin.handler({ + const result = await handler({ x: 3.14, y: 200, }); @@ -745,7 +736,7 @@ describe('Tap Plugin', () => { it('should return validation error for non-integer y coordinate', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await tapPlugin.handler({ + const result = await handler({ x: 100, y: 3.14, }); @@ -759,7 +750,7 @@ describe('Tap Plugin', () => { it('should return validation error for negative preDelay', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await tapPlugin.handler({ + const result = await handler({ x: 100, y: 200, preDelay: -1, @@ -774,7 +765,7 @@ describe('Tap Plugin', () => { it('should return validation error for negative postDelay', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await tapPlugin.handler({ + const result = await handler({ x: 100, y: 200, postDelay: -1, @@ -813,7 +804,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -843,7 +834,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -873,7 +864,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -901,7 +892,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -929,7 +920,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -957,7 +948,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, diff --git a/src/mcp/tools/ui-testing/__tests__/touch.test.ts b/src/mcp/tools/ui-automation/__tests__/touch.test.ts similarity index 78% rename from src/mcp/tools/ui-testing/__tests__/touch.test.ts rename to src/mcp/tools/ui-automation/__tests__/touch.test.ts index a76ddd01..3f83d031 100644 --- a/src/mcp/tools/ui-testing/__tests__/touch.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/touch.test.ts @@ -5,35 +5,26 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { createMockExecutor, mockProcess } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import touchPlugin, { touchLogic } from '../touch.ts'; +import { schema, handler, touchLogic } from '../touch.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; describe('Touch Plugin', () => { beforeEach(() => { sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(touchPlugin.name).toBe('touch'); - }); - - it('should have correct description', () => { - expect(touchPlugin.description).toBe( - "Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots).", - ); - }); - + describe('Schema Validation', () => { it('should have handler function', () => { - expect(typeof touchPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema fields with safeParse', () => { - const schema = z.object(touchPlugin.schema); + const schemaObj = z.object(schema); expect( - schema.safeParse({ + schemaObj.safeParse({ x: 100, y: 200, down: true, @@ -41,7 +32,7 @@ describe('Touch Plugin', () => { ).toBe(true); expect( - schema.safeParse({ + schemaObj.safeParse({ x: 100, y: 200, up: true, @@ -49,7 +40,7 @@ describe('Touch Plugin', () => { ).toBe(true); expect( - schema.safeParse({ + schemaObj.safeParse({ x: 100.5, y: 200, down: true, @@ -57,7 +48,7 @@ describe('Touch Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObj.safeParse({ x: 100, y: 200.5, down: true, @@ -65,7 +56,7 @@ describe('Touch Plugin', () => { ).toBe(false); expect( - schema.safeParse({ + schemaObj.safeParse({ x: 100, y: 200, down: true, @@ -73,7 +64,7 @@ describe('Touch Plugin', () => { }).success, ).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObj.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', x: 100, y: 200, @@ -86,7 +77,7 @@ describe('Touch Plugin', () => { describe('Handler Requirements', () => { it('should require simulatorId session default', async () => { - const result = await touchPlugin.handler({ + const result = await handler({ x: 100, y: 200, down: true, @@ -102,7 +93,7 @@ describe('Touch Plugin', () => { it('should surface parameter validation errors when defaults exist', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await touchPlugin.handler({ + const result = await handler({ y: 200, down: true, }); @@ -123,7 +114,7 @@ describe('Touch Plugin', () => { success: true, output: 'touch completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -134,7 +125,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -173,7 +164,7 @@ describe('Touch Plugin', () => { success: true, output: 'touch completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -184,7 +175,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -223,7 +214,7 @@ describe('Touch Plugin', () => { success: true, output: 'touch completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -234,7 +225,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -275,7 +266,7 @@ describe('Touch Plugin', () => { success: true, output: 'touch completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -286,7 +277,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -330,7 +321,7 @@ describe('Touch Plugin', () => { success: true, output: 'touch completed', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -377,7 +368,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -399,7 +390,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -415,7 +406,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -437,7 +428,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Touch event (touch down) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Touch event (touch down) at (100, 200) executed successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -453,7 +444,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -475,7 +466,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Touch event (touch up) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Touch event (touch up) at (100, 200) executed successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -514,7 +505,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -536,7 +527,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Touch event (touch down) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Touch event (touch down) at (100, 200) executed successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -557,7 +548,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -579,7 +570,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Touch event (touch up) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Touch event (touch up) at (100, 200) executed successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -600,7 +591,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -623,7 +614,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Touch event (touch down+up) at (100, 200) executed successfully.\n\nWarning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.', + text: 'Touch event (touch down+up) at (100, 200) executed successfully.\n\nWarning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.', }, ], isError: false, @@ -640,7 +631,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -662,7 +653,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -683,7 +674,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -724,7 +715,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -767,7 +758,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -810,7 +801,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, diff --git a/src/mcp/tools/ui-testing/__tests__/type_text.test.ts b/src/mcp/tools/ui-automation/__tests__/type_text.test.ts similarity index 88% rename from src/mcp/tools/ui-testing/__tests__/type_text.test.ts rename to src/mcp/tools/ui-automation/__tests__/type_text.test.ts index 7a6adccf..3910667a 100644 --- a/src/mcp/tools/ui-testing/__tests__/type_text.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/type_text.test.ts @@ -1,16 +1,17 @@ /** - * Tests for type_text plugin + * Tests for type_text tool */ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { createMockExecutor, - createMockFileSystemExecutor, createNoopExecutor, + mockProcess, } from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import typeTextPlugin, { type_textLogic } from '../type_text.ts'; +import { schema, handler, type_textLogic } from '../type_text.ts'; +import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts'; // Mock axe helpers for dependency injection function createMockAxeHelpers( @@ -20,12 +21,18 @@ function createMockAxeHelpers( } = {}, ) { return { - getAxePath: () => { - return Object.prototype.hasOwnProperty.call(overrides, 'getAxePathReturn') - ? overrides.getAxePathReturn - : '/usr/local/bin/axe'; - }, + getAxePath: () => + overrides.getAxePathReturn !== undefined ? overrides.getAxePathReturn : '/usr/local/bin/axe', getBundledAxeEnvironment: () => overrides.getBundledAxeEnvironmentReturn ?? {}, + createAxeNotAvailableResponse: () => ({ + content: [ + { + type: 'text', + text: AXE_NOT_AVAILABLE_MESSAGE, + }, + ], + isError: true, + }), }; } @@ -36,50 +43,40 @@ function createRejectingExecutor(error: any) { }; } -describe('Type Text Plugin', () => { +describe('Type Text Tool', () => { beforeEach(() => { sessionStore.clear(); }); - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(typeTextPlugin.name).toBe('type_text'); - }); - - it('should have correct description', () => { - expect(typeTextPlugin.description).toBe( - 'Type text (supports US keyboard characters). Use describe_ui to find text field, tap to focus, then type.', - ); - }); - + describe('Schema Validation', () => { it('should have handler function', () => { - expect(typeof typeTextPlugin.handler).toBe('function'); + expect(typeof handler).toBe('function'); }); it('should validate schema fields with safeParse', () => { - const schema = z.object(typeTextPlugin.schema); + const schemaObject = z.object(schema); expect( - schema.safeParse({ + schemaObject.safeParse({ text: 'Hello World', }).success, ).toBe(true); expect( - schema.safeParse({ + schemaObject.safeParse({ text: '', }).success, ).toBe(false); expect( - schema.safeParse({ + schemaObject.safeParse({ text: 123, }).success, ).toBe(false); - expect(schema.safeParse({}).success).toBe(false); + expect(schemaObject.safeParse({}).success).toBe(false); - const withSimId = schema.safeParse({ + const withSimId = schemaObject.safeParse({ simulatorId: '12345678-1234-4234-8234-123456789012', text: 'Hello World', }); @@ -90,7 +87,7 @@ describe('Type Text Plugin', () => { describe('Handler Requirements', () => { it('should require simulatorId session default', async () => { - const result = await typeTextPlugin.handler({ text: 'Hello' }); + const result = await handler({ text: 'Hello' }); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -102,7 +99,7 @@ describe('Type Text Plugin', () => { it('should surface validation errors when defaults exist', async () => { sessionStore.setDefaults({ simulatorId: '12345678-1234-4234-8234-123456789012' }); - const result = await typeTextPlugin.handler({}); + const result = await handler({}); expect(result.isError).toBe(true); const message = result.content[0].text; @@ -120,7 +117,7 @@ describe('Type Text Plugin', () => { success: true, output: 'Text typed successfully', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -155,7 +152,7 @@ describe('Type Text Plugin', () => { success: true, output: 'Text typed successfully', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -190,7 +187,7 @@ describe('Type Text Plugin', () => { success: true, output: 'Text typed successfully', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -225,7 +222,7 @@ describe('Type Text Plugin', () => { success: true, output: 'Text typed successfully', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -263,7 +260,7 @@ describe('Type Text Plugin', () => { success: true, output: 'Text typed successfully', error: undefined, - process: { pid: 12345 }, + process: mockProcess, }; }; @@ -310,7 +307,7 @@ describe('Type Text Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, @@ -388,7 +385,7 @@ describe('Type Text Plugin', () => { content: [ { type: 'text', - text: 'Bundled axe tool not found. UI automation features are not available.\n\nThis is likely an installation issue with the npm package.\nPlease reinstall xcodebuildmcp or report this issue.', + text: AXE_NOT_AVAILABLE_MESSAGE, }, ], isError: true, diff --git a/src/mcp/tools/ui-testing/button.ts b/src/mcp/tools/ui-automation/button.ts similarity index 71% rename from src/mcp/tools/ui-testing/button.ts rename to src/mcp/tools/ui-automation/button.ts index 3449cec8..26d36daa 100644 --- a/src/mcp/tools/ui-testing/button.ts +++ b/src/mcp/tools/ui-automation/button.ts @@ -4,6 +4,9 @@ import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -18,8 +21,14 @@ import { // Define schema as ZodObject const buttonSchema = z.object({ simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), - buttonType: z.enum(['apple-pay', 'home', 'lock', 'side-button', 'siri']), - duration: z.number().min(0, { message: 'Duration must be non-negative' }).optional(), + buttonType: z + .enum(['apple-pay', 'home', 'lock', 'side-button', 'siri']) + .describe('apple-pay|home|lock|side-button|siri'), + duration: z + .number() + .min(0, { message: 'Duration must be non-negative' }) + .optional() + .describe('seconds'), }); // Use z.infer for type safety @@ -41,9 +50,18 @@ export async function buttonLogic( getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'button'; const { simulatorId, buttonType, duration } = params; + + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + const commandArgs = ['button', buttonType]; if (duration !== undefined) { commandArgs.push('--duration', String(duration)); @@ -54,7 +72,11 @@ export async function buttonLogic( try { await executeAxeCommand(commandArgs, simulatorId, 'button', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - return createTextResponse(`Hardware button '${buttonType}' pressed successfully.`); + const message = `Hardware button '${buttonType}' pressed successfully.`; + if (guard.warningText) { + return createTextResponse(`${message}\n\n${guard.warningText}`); + } + return createTextResponse(message); } catch (error) { log('error', `${LOG_PREFIX}/${toolName}: Failed - ${error}`); if (error instanceof DependencyError) { @@ -78,30 +100,22 @@ export async function buttonLogic( const publicSchemaObject = z.strictObject(buttonSchema.omit({ simulatorId: true } as const).shape); -export default { - name: 'button', - description: - 'Press hardware button on iOS simulator. Supported buttons: apple-pay, home, lock, side-button, siri', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: buttonSchema, - }), - annotations: { - title: 'Hardware Button', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: buttonSchema as unknown as z.ZodType, - logicFunction: (params: ButtonParams, executor: CommandExecutor) => - buttonLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: buttonSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: buttonSchema as unknown as z.ZodType, + logicFunction: (params: ButtonParams, executor: CommandExecutor) => + buttonLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -127,7 +141,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/gesture.ts b/src/mcp/tools/ui-automation/gesture.ts similarity index 73% rename from src/mcp/tools/ui-testing/gesture.ts rename to src/mcp/tools/ui-automation/gesture.ts index 137d6a93..2cc2c66a 100644 --- a/src/mcp/tools/ui-testing/gesture.ts +++ b/src/mcp/tools/ui-automation/gesture.ts @@ -6,7 +6,7 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, @@ -17,6 +17,9 @@ import { } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -42,7 +45,7 @@ const gestureSchema = z.object({ 'swipe-from-bottom-edge', ]) .describe( - 'The gesture preset to perform. Must be one of: scroll-up, scroll-down, scroll-left, scroll-right, swipe-from-left-edge, swipe-from-right-edge, swipe-from-top-edge, swipe-from-bottom-edge.', + 'scroll-up|scroll-down|scroll-left|scroll-right|swipe-from-left-edge|swipe-from-right-edge|swipe-from-top-edge|swipe-from-bottom-edge', ), screenWidth: z .number() @@ -50,7 +53,7 @@ const gestureSchema = z.object({ .min(1) .optional() .describe( - 'Optional: Screen width in pixels. Used for gesture calculations. Auto-detected if not provided.', + 'Screen width in pixels. Used for gesture calculations. Auto-detected if not provided.', ), screenHeight: z .number() @@ -58,28 +61,28 @@ const gestureSchema = z.object({ .min(1) .optional() .describe( - 'Optional: Screen height in pixels. Used for gesture calculations. Auto-detected if not provided.', + 'Screen height in pixels. Used for gesture calculations. Auto-detected if not provided.', ), duration: z .number() .min(0, { message: 'Duration must be non-negative' }) .optional() - .describe('Optional: Duration of the gesture in seconds.'), + .describe('Duration of the gesture in seconds.'), delta: z .number() .min(0, { message: 'Delta must be non-negative' }) .optional() - .describe('Optional: Distance to move in pixels.'), + .describe('Distance to move in pixels.'), preDelay: z .number() .min(0, { message: 'Pre-delay must be non-negative' }) .optional() - .describe('Optional: Delay before starting the gesture in seconds.'), + .describe('Delay before starting the gesture in seconds.'), postDelay: z .number() .min(0, { message: 'Post-delay must be non-negative' }) .optional() - .describe('Optional: Delay after completing the gesture in seconds.'), + .describe('Delay after completing the gesture in seconds.'), }); // Use z.infer for type safety @@ -101,10 +104,17 @@ export async function gestureLogic( getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'gesture'; const { simulatorId, preset, screenWidth, screenHeight, duration, delta, preDelay, postDelay } = params; + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; const commandArgs = ['gesture', preset]; if (screenWidth !== undefined) { @@ -131,7 +141,11 @@ export async function gestureLogic( try { await executeAxeCommand(commandArgs, simulatorId, 'gesture', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - return createTextResponse(`Gesture '${preset}' executed successfully.`); + const message = `Gesture '${preset}' executed successfully.`; + if (guard.warningText) { + return createTextResponse(`${message}\n\n${guard.warningText}`); + } + return createTextResponse(message); } catch (error) { log('error', `${LOG_PREFIX}/${toolName}: Failed - ${error}`); if (error instanceof DependencyError) { @@ -155,30 +169,22 @@ export async function gestureLogic( const publicSchemaObject = z.strictObject(gestureSchema.omit({ simulatorId: true } as const).shape); -export default { - name: 'gesture', - description: - 'Perform gesture on iOS simulator using preset gestures: scroll-up, scroll-down, scroll-left, scroll-right, swipe-from-left-edge, swipe-from-right-edge, swipe-from-top-edge, swipe-from-bottom-edge', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: gestureSchema, - }), - annotations: { - title: 'Gesture', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: gestureSchema as unknown as z.ZodType, - logicFunction: (params: GestureParams, executor: CommandExecutor) => - gestureLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: gestureSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: gestureSchema as unknown as z.ZodType, + logicFunction: (params: GestureParams, executor: CommandExecutor) => + gestureLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -204,7 +210,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/key_press.ts b/src/mcp/tools/ui-automation/key_press.ts similarity index 70% rename from src/mcp/tools/ui-testing/key_press.ts rename to src/mcp/tools/ui-automation/key_press.ts index f8fd4b80..aaa048a3 100644 --- a/src/mcp/tools/ui-testing/key_press.ts +++ b/src/mcp/tools/ui-automation/key_press.ts @@ -1,5 +1,5 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, @@ -10,6 +10,9 @@ import { } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -23,8 +26,17 @@ import { // Define schema as ZodObject const keyPressSchema = z.object({ simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), - keyCode: z.number().int({ message: 'HID keycode to press (0-255)' }).min(0).max(255), - duration: z.number().min(0, { message: 'Duration must be non-negative' }).optional(), + keyCode: z + .number() + .int({ message: 'HID keycode to press (0-255)' }) + .min(0) + .max(255) + .describe('HID keycode'), + duration: z + .number() + .min(0, { message: 'Duration must be non-negative' }) + .optional() + .describe('seconds'), }); // Use z.infer for type safety @@ -46,9 +58,18 @@ export async function key_pressLogic( getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'key_press'; const { simulatorId, keyCode, duration } = params; + + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + const commandArgs = ['key', String(keyCode)]; if (duration !== undefined) { commandArgs.push('--duration', String(duration)); @@ -59,7 +80,11 @@ export async function key_pressLogic( try { await executeAxeCommand(commandArgs, simulatorId, 'key', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - return createTextResponse(`Key press (code: ${keyCode}) simulated successfully.`); + const message = `Key press (code: ${keyCode}) simulated successfully.`; + if (guard.warningText) { + return createTextResponse(`${message}\n\n${guard.warningText}`); + } + return createTextResponse(message); } catch (error) { log('error', `${LOG_PREFIX}/${toolName}: Failed - ${error}`); if (error instanceof DependencyError) { @@ -85,30 +110,22 @@ const publicSchemaObject = z.strictObject( keyPressSchema.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'key_press', - description: - 'Press a single key by keycode on the simulator. Common keycodes: 40=Return, 42=Backspace, 43=Tab, 44=Space, 58-67=F1-F10.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: keyPressSchema, - }), - annotations: { - title: 'Key Press', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: keyPressSchema as unknown as z.ZodType, - logicFunction: (params: KeyPressParams, executor: CommandExecutor) => - key_pressLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: keyPressSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: keyPressSchema as unknown as z.ZodType, + logicFunction: (params: KeyPressParams, executor: CommandExecutor) => + key_pressLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -134,7 +151,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/key_sequence.ts b/src/mcp/tools/ui-automation/key_sequence.ts similarity index 73% rename from src/mcp/tools/ui-testing/key_sequence.ts rename to src/mcp/tools/ui-automation/key_sequence.ts index 55fe7a1e..96bc9d9d 100644 --- a/src/mcp/tools/ui-testing/key_sequence.ts +++ b/src/mcp/tools/ui-automation/key_sequence.ts @@ -5,7 +5,7 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, @@ -16,6 +16,9 @@ import { } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -31,7 +34,8 @@ const keySequenceSchema = z.object({ simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), keyCodes: z .array(z.number().int().min(0).max(255)) - .min(1, { message: 'At least one key code required' }), + .min(1, { message: 'At least one key code required' }) + .describe('HID keycodes'), delay: z.number().min(0, { message: 'Delay must be non-negative' }).optional(), }); @@ -54,9 +58,18 @@ export async function key_sequenceLogic( getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'key_sequence'; const { simulatorId, keyCodes, delay } = params; + + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + const commandArgs = ['key-sequence', '--keycodes', keyCodes.join(',')]; if (delay !== undefined) { commandArgs.push('--delay', String(delay)); @@ -70,7 +83,11 @@ export async function key_sequenceLogic( try { await executeAxeCommand(commandArgs, simulatorId, 'key-sequence', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - return createTextResponse(`Key sequence [${keyCodes.join(',')}] executed successfully.`); + const message = `Key sequence [${keyCodes.join(',')}] executed successfully.`; + if (guard.warningText) { + return createTextResponse(`${message}\n\n${guard.warningText}`); + } + return createTextResponse(message); } catch (error) { log('error', `${LOG_PREFIX}/${toolName}: Failed - ${error}`); if (error instanceof DependencyError) { @@ -96,29 +113,22 @@ const publicSchemaObject = z.strictObject( keySequenceSchema.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'key_sequence', - description: 'Press key sequence using HID keycodes on iOS simulator with configurable delay', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: keySequenceSchema, - }), - annotations: { - title: 'Key Sequence', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: keySequenceSchema as unknown as z.ZodType, - logicFunction: (params: KeySequenceParams, executor: CommandExecutor) => - key_sequenceLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: keySequenceSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: keySequenceSchema as unknown as z.ZodType, + logicFunction: (params: KeySequenceParams, executor: CommandExecutor) => + key_sequenceLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -144,7 +154,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/long_press.ts b/src/mcp/tools/ui-automation/long_press.ts similarity index 69% rename from src/mcp/tools/ui-testing/long_press.ts rename to src/mcp/tools/ui-automation/long_press.ts index e823c544..f6429729 100644 --- a/src/mcp/tools/ui-testing/long_press.ts +++ b/src/mcp/tools/ui-automation/long_press.ts @@ -2,11 +2,11 @@ * UI Testing Plugin: Long Press * * Long press at specific coordinates for given duration (ms). - * Use describe_ui for precise coordinates (don't guess from screenshots). + * Use snapshot_ui for precise coordinates (don't guess from screenshots). */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, @@ -17,6 +17,9 @@ import { } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -26,13 +29,17 @@ import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { getSnapshotUiWarning } from './shared/snapshot-ui-state.ts'; // Define schema as ZodObject const longPressSchema = z.object({ simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), x: z.number().int({ message: 'X coordinate for the long press' }), y: z.number().int({ message: 'Y coordinate for the long press' }), - duration: z.number().positive({ message: 'Duration of the long press in milliseconds' }), + duration: z + .number() + .positive({ message: 'Duration of the long press in milliseconds' }) + .describe('milliseconds'), }); // Use z.infer for type safety @@ -58,9 +65,18 @@ export async function long_pressLogic( getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'long_press'; const { simulatorId, x, y, duration } = params; + + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + // AXe uses touch command with --down, --up, and --delay for long press const delayInSeconds = Number(duration) / 1000; // Convert ms to seconds const commandArgs = [ @@ -84,11 +100,12 @@ export async function long_pressLogic( await executeAxeCommand(commandArgs, simulatorId, 'touch', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - const warning = getCoordinateWarning(simulatorId); + const coordinateWarning = getSnapshotUiWarning(simulatorId); const message = `Long press at (${x}, ${y}) for ${duration}ms simulated successfully.`; + const warnings = [guard.warningText, coordinateWarning].filter(Boolean).join('\n\n'); - if (warning) { - return createTextResponse(`${message}\n\n${warning}`); + if (warnings) { + return createTextResponse(`${message}\n\n${warnings}`); } return createTextResponse(message); @@ -113,54 +130,22 @@ export async function long_pressLogic( } } -export default { - name: 'long_press', - description: - "Long press at specific coordinates for given duration (ms). Use describe_ui for precise coordinates (don't guess from screenshots).", - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: longPressSchema, - }), - annotations: { - title: 'Long Press', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: longPressSchema as unknown as z.ZodType, - logicFunction: (params: LongPressParams, executor: CommandExecutor) => - long_pressLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; - -// Session tracking for describe_ui warnings -interface DescribeUISession { - timestamp: number; - simulatorId: string; -} - -const describeUITimestamps = new Map(); -const DESCRIBE_UI_WARNING_TIMEOUT = 60000; // 60 seconds - -function getCoordinateWarning(simulatorId: string): string | null { - const session = describeUITimestamps.get(simulatorId); - if (!session) { - return 'Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.'; - } - - const timeSinceDescribe = Date.now() - session.timestamp; - if (timeSinceDescribe > DESCRIBE_UI_WARNING_TIMEOUT) { - const secondsAgo = Math.round(timeSinceDescribe / 1000); - return `Warning: describe_ui was last called ${secondsAgo} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`; - } +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: longPressSchema, +}); - return null; -} +export const handler = createSessionAwareTool({ + internalSchema: longPressSchema as unknown as z.ZodType, + logicFunction: (params: LongPressParams, executor: CommandExecutor) => + long_pressLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -186,7 +171,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-automation/screenshot.ts b/src/mcp/tools/ui-automation/screenshot.ts new file mode 100644 index 00000000..c129fa25 --- /dev/null +++ b/src/mcp/tools/ui-automation/screenshot.ts @@ -0,0 +1,351 @@ +/** + * Screenshot tool plugin - Capture screenshots from iOS Simulator + * + * Note: The simctl screenshot command captures the raw framebuffer in portrait orientation + * regardless of the device's actual rotation. When the simulator is in landscape mode, + * this results in a rotated image. This plugin detects the simulator window orientation + * and applies a +90° rotation to correct landscape screenshots. + */ +import * as path from 'path'; +import { tmpdir } from 'os'; +import * as z from 'zod'; +import { v4 as uuidv4 } from 'uuid'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createImageContent } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { + createErrorResponse, + createTextResponse, + SystemError, +} from '../../../utils/responses/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; +import { + getDefaultFileSystemExecutor, + getDefaultCommandExecutor, +} from '../../../utils/execution/index.ts'; +import { + createSessionAwareTool, + getSessionAwareToolSchemaShape, +} from '../../../utils/typed-tool-factory.ts'; + +const LOG_PREFIX = '[Screenshot]'; + +/** + * Type for simctl device list response + */ +interface SimctlDevice { + udid: string; + name: string; + state?: string; +} + +interface SimctlDeviceList { + devices: Record; +} + +function escapeSwiftStringLiteral(value: string): string { + return value + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t'); +} + +/** + * Generates Swift code to detect simulator window dimensions via CoreGraphics. + * Filters by device name to handle multiple open simulators correctly. + * Returns "width,height" of the matching simulator window. + */ +function getWindowDetectionSwiftCode(deviceName: string): string { + const escapedDeviceName = escapeSwiftStringLiteral(deviceName); + // Match by title separator (en-dash) to avoid "iPhone 15" matching "iPhone 15 Pro" + // Window titles are formatted like "iPhone 15 Pro \u{2013} iOS 17.2" + return ` +import Cocoa +import CoreGraphics +let deviceName = "${escapedDeviceName}" +let opts = CGWindowListOption(arrayLiteral: .optionOnScreenOnly, .excludeDesktopElements) +if let wins = CGWindowListCopyWindowInfo(opts, kCGNullWindowID) as? [[String: Any]] { + for w in wins { + if let o = w[kCGWindowOwnerName as String] as? String, o == "Simulator", + let b = w[kCGWindowBounds as String] as? [String: Any], + let n = w[kCGWindowName as String] as? String { + // Check for exact match: name equals deviceName or is followed by the title separator + // Window titles use en-dash: "iPhone 15 Pro \u{2013} iOS 17.2" + let isMatch = n == deviceName || n.hasPrefix(deviceName + " \\u{2013}") || n.hasPrefix(deviceName + " -") + if isMatch { + print("\\(b["Width"] as? Int ?? 0),\\(b["Height"] as? Int ?? 0)") + break + } + } + } +}`.trim(); +} + +/** + * Gets the device name for a simulator ID using simctl. + * Returns the device name or null if not found. + */ +export async function getDeviceNameForSimulatorId( + simulatorId: string, + executor: CommandExecutor, +): Promise { + try { + const listCommand = ['xcrun', 'simctl', 'list', 'devices', '-j']; + const result = await executor(listCommand, `${LOG_PREFIX}: list devices`, false); + + if (result.success && result.output) { + const data = JSON.parse(result.output) as SimctlDeviceList; + const devices = data.devices; + + for (const runtime of Object.keys(devices)) { + for (const device of devices[runtime]) { + if (device.udid === simulatorId) { + log('info', `${LOG_PREFIX}: Found device name "${device.name}" for ${simulatorId}`); + return device.name; + } + } + } + } + log('warning', `${LOG_PREFIX}: Could not find device name for ${simulatorId}`); + return null; + } catch (error) { + log('warning', `${LOG_PREFIX}: Failed to get device name: ${error}`); + return null; + } +} + +/** + * Detects if the simulator window is in landscape orientation. + * Uses the device name to filter when multiple simulators are open. + * Returns true if width > height, indicating landscape mode. + */ +export async function detectLandscapeMode( + executor: CommandExecutor, + deviceName?: string, +): Promise { + try { + // If no device name available, skip orientation detection to avoid incorrect rotation + // This is safer than guessing, as we don't know if it's iPhone or iPad + if (!deviceName) { + log('warning', `${LOG_PREFIX}: No device name available, skipping orientation detection`); + return false; + } + const swiftCode = getWindowDetectionSwiftCode(deviceName); + const swiftCommand = ['swift', '-e', swiftCode]; + const result = await executor(swiftCommand, `${LOG_PREFIX}: detect orientation`, false); + + if (result.success && result.output) { + const match = result.output.trim().match(/(\d+),(\d+)/); + if (match) { + const width = parseInt(match[1], 10); + const height = parseInt(match[2], 10); + const isLandscape = width > height; + log( + 'info', + `${LOG_PREFIX}: Window dimensions ${width}x${height}, landscape=${isLandscape}`, + ); + return isLandscape; + } + } + log('warning', `${LOG_PREFIX}: Could not detect window orientation, assuming portrait`); + return false; + } catch (error) { + log('warning', `${LOG_PREFIX}: Orientation detection failed: ${error}`); + return false; + } +} + +/** + * Rotates an image by the specified degrees using sips. + */ +export async function rotateImage( + imagePath: string, + degrees: number, + executor: CommandExecutor, +): Promise { + try { + const rotateArgs = ['sips', '--rotate', degrees.toString(), imagePath]; + const result = await executor(rotateArgs, `${LOG_PREFIX}: rotate image`, false); + return result.success; + } catch (error) { + log('warning', `${LOG_PREFIX}: Image rotation failed: ${error}`); + return false; + } +} + +// Define schema as ZodObject +const screenshotSchema = z.object({ + simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), + returnFormat: z + .enum(['path', 'base64']) + .optional() + .describe('Return image path or base64 data (path|base64)'), +}); + +// Use z.infer for type safety +type ScreenshotParams = z.infer; + +const publicSchemaObject = z.strictObject( + screenshotSchema.omit({ simulatorId: true } as const).shape, +); + +export async function screenshotLogic( + params: ScreenshotParams, + executor: CommandExecutor, + fileSystemExecutor: FileSystemExecutor = getDefaultFileSystemExecutor(), + pathUtils: { tmpdir: () => string; join: (...paths: string[]) => string } = { ...path, tmpdir }, + uuidUtils: { v4: () => string } = { v4: uuidv4 }, +): Promise { + const { simulatorId } = params; + const runtime = process.env.XCODEBUILDMCP_RUNTIME; + const defaultFormat = runtime === 'cli' || runtime === 'daemon' ? 'path' : 'base64'; + const returnFormat = params.returnFormat ?? defaultFormat; + const tempDir = pathUtils.tmpdir(); + const screenshotFilename = `screenshot_${uuidUtils.v4()}.png`; + const screenshotPath = pathUtils.join(tempDir, screenshotFilename); + const optimizedFilename = `screenshot_optimized_${uuidUtils.v4()}.jpg`; + const optimizedPath = pathUtils.join(tempDir, optimizedFilename); + // Use xcrun simctl to take screenshot + const commandArgs: string[] = [ + 'xcrun', + 'simctl', + 'io', + simulatorId, + 'screenshot', + screenshotPath, + ]; + + log('info', `${LOG_PREFIX}/screenshot: Starting capture to ${screenshotPath} on ${simulatorId}`); + + try { + // Execute the screenshot command + const result = await executor(commandArgs, `${LOG_PREFIX}: screenshot`, false); + + if (!result.success) { + throw new SystemError(`Failed to capture screenshot: ${result.error ?? result.output}`); + } + + log('info', `${LOG_PREFIX}/screenshot: Success for ${simulatorId}`); + + try { + // Fix landscape orientation: simctl captures in portrait orientation regardless of device rotation + // Get device name to identify the correct simulator window when multiple are open + const deviceName = await getDeviceNameForSimulatorId(simulatorId, executor); + // Detect if simulator window is landscape and rotate the image +90° to correct + const isLandscape = await detectLandscapeMode(executor, deviceName ?? undefined); + if (isLandscape) { + log('info', `${LOG_PREFIX}/screenshot: Landscape mode detected, rotating +90°`); + const rotated = await rotateImage(screenshotPath, 90, executor); + if (!rotated) { + log('warning', `${LOG_PREFIX}/screenshot: Rotation failed, continuing with original`); + } + } + + // Optimize the image for LLM consumption: resize to max 800px width and convert to JPEG + const optimizeArgs = [ + 'sips', + '-Z', + '800', // Resize to max 800px (maintains aspect ratio) + '-s', + 'format', + 'jpeg', // Convert to JPEG + '-s', + 'formatOptions', + '75', // 75% quality compression + screenshotPath, + '--out', + optimizedPath, + ]; + + const optimizeResult = await executor(optimizeArgs, `${LOG_PREFIX}: optimize image`, false); + + if (!optimizeResult.success) { + log('warning', `${LOG_PREFIX}/screenshot: Image optimization failed, using original PNG`); + if (returnFormat === 'base64') { + // Fallback to original PNG if optimization fails + const base64Image = await fileSystemExecutor.readFile(screenshotPath, 'base64'); + + // Clean up + try { + await fileSystemExecutor.rm(screenshotPath); + } catch (err) { + log('warning', `${LOG_PREFIX}/screenshot: Failed to delete temp file: ${err}`); + } + + return { + content: [createImageContent(base64Image, 'image/png')], + isError: false, + }; + } + + return createTextResponse( + `Screenshot captured: ${screenshotPath} (image/png, optimization failed)`, + ); + } + + log('info', `${LOG_PREFIX}/screenshot: Image optimized successfully`); + + if (returnFormat === 'base64') { + // Read the optimized image file as base64 + const base64Image = await fileSystemExecutor.readFile(optimizedPath, 'base64'); + + log('info', `${LOG_PREFIX}/screenshot: Successfully encoded image as Base64`); + + // Clean up both temporary files + try { + await fileSystemExecutor.rm(screenshotPath); + await fileSystemExecutor.rm(optimizedPath); + } catch (err) { + log('warning', `${LOG_PREFIX}/screenshot: Failed to delete temporary files: ${err}`); + } + + // Return the optimized image (JPEG format, smaller size) + return { + content: [createImageContent(base64Image, 'image/jpeg')], + isError: false, + }; + } + + // Keep optimized file on disk for path-based return + try { + await fileSystemExecutor.rm(screenshotPath); + } catch (err) { + log('warning', `${LOG_PREFIX}/screenshot: Failed to delete temp file: ${err}`); + } + + return createTextResponse(`Screenshot captured: ${optimizedPath} (image/jpeg)`); + } catch (fileError) { + log('error', `${LOG_PREFIX}/screenshot: Failed to process image file: ${fileError}`); + return createErrorResponse( + `Screenshot captured but failed to process image file: ${fileError instanceof Error ? fileError.message : String(fileError)}`, + ); + } + } catch (_error) { + log('error', `${LOG_PREFIX}/screenshot: Failed - ${_error}`); + if (_error instanceof SystemError) { + return createErrorResponse( + `System error executing screenshot: ${_error.message}`, + _error.originalError?.stack, + ); + } + return createErrorResponse( + `An unexpected error occurred: ${_error instanceof Error ? _error.message : String(_error)}`, + ); + } +} + +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: screenshotSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: screenshotSchema as unknown as z.ZodType, + logicFunction: (params: ScreenshotParams, executor: CommandExecutor) => { + return screenshotLogic(params, executor); + }, + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); diff --git a/src/mcp/tools/ui-automation/shared/snapshot-ui-state.ts b/src/mcp/tools/ui-automation/shared/snapshot-ui-state.ts new file mode 100644 index 00000000..cd0fa28c --- /dev/null +++ b/src/mcp/tools/ui-automation/shared/snapshot-ui-state.ts @@ -0,0 +1,22 @@ +const SNAPSHOT_UI_WARNING_TIMEOUT_MS = 60000; // 60 seconds + +const snapshotUiTimestamps = new Map(); + +export function recordSnapshotUiCall(simulatorId: string): void { + snapshotUiTimestamps.set(simulatorId, Date.now()); +} + +export function getSnapshotUiWarning(simulatorId: string): string | null { + const timestamp = snapshotUiTimestamps.get(simulatorId); + if (!timestamp) { + return 'Warning: snapshot_ui has not been called yet. Consider using snapshot_ui for precise coordinates instead of guessing from screenshots.'; + } + + const timeSinceDescribe = Date.now() - timestamp; + if (timeSinceDescribe > SNAPSHOT_UI_WARNING_TIMEOUT_MS) { + const secondsAgo = Math.round(timeSinceDescribe / 1000); + return `Warning: snapshot_ui was last called ${secondsAgo} seconds ago. Consider refreshing UI coordinates with snapshot_ui instead of using potentially stale coordinates.`; + } + + return null; +} diff --git a/src/mcp/tools/ui-testing/describe_ui.ts b/src/mcp/tools/ui-automation/snapshot_ui.ts similarity index 60% rename from src/mcp/tools/ui-testing/describe_ui.ts rename to src/mcp/tools/ui-automation/snapshot_ui.ts index 0ce9df52..6d07efaf 100644 --- a/src/mcp/tools/ui-testing/describe_ui.ts +++ b/src/mcp/tools/ui-automation/snapshot_ui.ts @@ -1,10 +1,13 @@ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createErrorResponse } from '../../../utils/responses/index.ts'; import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -14,14 +17,15 @@ import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { recordSnapshotUiCall } from './shared/snapshot-ui-state.ts'; // Define schema as ZodObject -const describeUiSchema = z.object({ +const snapshotUiSchema = z.object({ simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), }); // Use z.infer for type safety -type DescribeUiParams = z.infer; +type SnapshotUiParams = z.infer; export interface AxeHelpers { getAxePath: () => string | null; @@ -31,32 +35,30 @@ export interface AxeHelpers { const LOG_PREFIX = '[AXe]'; -// Session tracking for describe_ui warnings (shared across UI tools) -const describeUITimestamps = new Map(); - -function recordDescribeUICall(simulatorId: string): void { - describeUITimestamps.set(simulatorId, { - timestamp: Date.now(), - simulatorId, - }); -} - /** - * Core business logic for describe_ui functionality + * Core business logic for snapshot_ui functionality */ -export async function describe_uiLogic( - params: DescribeUiParams, +export async function snapshot_uiLogic( + params: SnapshotUiParams, executor: CommandExecutor, axeHelpers: AxeHelpers = { getAxePath, getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { - const toolName = 'describe_ui'; + const toolName = 'snapshot_ui'; const { simulatorId } = params; const commandArgs = ['describe-ui']; + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + log('info', `${LOG_PREFIX}/${toolName}: Starting for ${simulatorId}`); try { @@ -68,11 +70,11 @@ export async function describe_uiLogic( axeHelpers, ); - // Record the describe_ui call for warning system - recordDescribeUICall(simulatorId); + // Record the snapshot_ui call for warning system + recordSnapshotUiCall(simulatorId); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - return { + const response: ToolResponse = { content: [ { type: 'text', @@ -81,13 +83,19 @@ export async function describe_uiLogic( }, { type: 'text', - text: `Next Steps: -- Use frame coordinates for tap/swipe (center: x+width/2, y+height/2) -- Re-run describe_ui after layout changes -- Screenshots are for visual verification only`, + text: `Tips:\n- Use frame coordinates for tap/swipe (center: x+width/2, y+height/2)\n- If a debugger is attached, ensure the app is running (not stopped on breakpoints)\n- Screenshots are for visual verification only`, }, ], + nextStepParams: { + snapshot_ui: { simulatorId }, + tap: { simulatorId, x: 0, y: 0 }, + screenshot: { simulatorId }, + }, }; + if (guard.warningText) { + response.content.push({ type: 'text', text: guard.warningText }); + } + return response; } catch (error) { log('error', `${LOG_PREFIX}/${toolName}: Failed - ${error}`); if (error instanceof DependencyError) { @@ -110,33 +118,25 @@ export async function describe_uiLogic( } const publicSchemaObject = z.strictObject( - describeUiSchema.omit({ simulatorId: true } as const).shape, + snapshotUiSchema.omit({ simulatorId: true } as const).shape, ); -export default { - name: 'describe_ui', - description: - 'Gets entire view hierarchy with precise frame coordinates (x, y, width, height) for all visible elements. Use this before UI interactions or after layout changes - do NOT guess coordinates from screenshots. Returns JSON tree with frame data for accurate automation.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: describeUiSchema, - }), - annotations: { - title: 'Describe UI', - readOnlyHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: describeUiSchema as unknown as z.ZodType, - logicFunction: (params: DescribeUiParams, executor: CommandExecutor) => - describe_uiLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: snapshotUiSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: snapshotUiSchema as unknown as z.ZodType, + logicFunction: (params: SnapshotUiParams, executor: CommandExecutor) => + snapshot_uiLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -162,7 +162,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( @@ -183,16 +188,11 @@ async function executeAxeCommand( return result.output.trim(); } catch (error) { - if (error instanceof Error) { - if (error instanceof AxeError) { - throw error; - } - - // Otherwise wrap it in a SystemError - throw new SystemError(`Failed to execute axe command: ${error.message}`, error); + if (error instanceof AxeError) { + throw error; } - - // For any other type of error - throw new SystemError(`Failed to execute axe command: ${String(error)}`); + const message = error instanceof Error ? error.message : String(error); + const cause = error instanceof Error ? error : undefined; + throw new SystemError(`Failed to execute axe command: ${message}`, cause); } } diff --git a/src/mcp/tools/ui-testing/swipe.ts b/src/mcp/tools/ui-automation/swipe.ts similarity index 69% rename from src/mcp/tools/ui-testing/swipe.ts rename to src/mcp/tools/ui-automation/swipe.ts index fea104fa..58672d8a 100644 --- a/src/mcp/tools/ui-testing/swipe.ts +++ b/src/mcp/tools/ui-automation/swipe.ts @@ -5,12 +5,15 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -20,6 +23,7 @@ import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { getSnapshotUiWarning } from './shared/snapshot-ui-state.ts'; // Define schema as ZodObject const swipeSchema = z.object({ @@ -28,14 +32,26 @@ const swipeSchema = z.object({ y1: z.number().int({ message: 'Start Y coordinate' }), x2: z.number().int({ message: 'End X coordinate' }), y2: z.number().int({ message: 'End Y coordinate' }), - duration: z.number().min(0, { message: 'Duration must be non-negative' }).optional(), + duration: z + .number() + .min(0, { message: 'Duration must be non-negative' }) + .optional() + .describe('seconds'), delta: z.number().min(0, { message: 'Delta must be non-negative' }).optional(), - preDelay: z.number().min(0, { message: 'Pre-delay must be non-negative' }).optional(), - postDelay: z.number().min(0, { message: 'Post-delay must be non-negative' }).optional(), + preDelay: z + .number() + .min(0, { message: 'Pre-delay must be non-negative' }) + .optional() + .describe('seconds'), + postDelay: z + .number() + .min(0, { message: 'Post-delay must be non-negative' }) + .optional() + .describe('seconds'), }); // Use z.infer for type safety -type SwipeParams = z.infer; +export type SwipeParams = z.infer; const publicSchemaObject = z.strictObject(swipeSchema.omit({ simulatorId: true } as const).shape); @@ -58,10 +74,18 @@ export async function swipeLogic( getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'swipe'; const { simulatorId, x1, y1, x2, y2, duration, delta, preDelay, postDelay } = params; + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + const commandArgs = [ 'swipe', '--start-x', @@ -96,11 +120,12 @@ export async function swipeLogic( await executeAxeCommand(commandArgs, simulatorId, 'swipe', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - const warning = getCoordinateWarning(simulatorId); + const coordinateWarning = getSnapshotUiWarning(simulatorId); const message = `Swipe from (${x1}, ${y1}) to (${x2}, ${y2})${optionsText} simulated successfully.`; + const warnings = [guard.warningText, coordinateWarning].filter(Boolean).join('\n\n'); - if (warning) { - return createTextResponse(`${message}\n\n${warning}`); + if (warnings) { + return createTextResponse(`${message}\n\n${warnings}`); } return createTextResponse(message); @@ -122,54 +147,22 @@ export async function swipeLogic( } } -export default { - name: 'swipe', - description: - "Swipe from one point to another. Use describe_ui for precise coordinates (don't guess from screenshots). Supports configurable timing.", - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: swipeSchema, - }), - annotations: { - title: 'Swipe', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: swipeSchema as unknown as z.ZodType, - logicFunction: (params: SwipeParams, executor: CommandExecutor) => - swipeLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; - -// Session tracking for describe_ui warnings -interface DescribeUISession { - timestamp: number; - simulatorId: string; -} - -const describeUITimestamps = new Map(); -const DESCRIBE_UI_WARNING_TIMEOUT = 60000; // 60 seconds - -function getCoordinateWarning(simulatorId: string): string | null { - const session = describeUITimestamps.get(simulatorId); - if (!session) { - return 'Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.'; - } - - const timeSinceDescribe = Date.now() - session.timestamp; - if (timeSinceDescribe > DESCRIBE_UI_WARNING_TIMEOUT) { - const secondsAgo = Math.round(timeSinceDescribe / 1000); - return `Warning: describe_ui was last called ${secondsAgo} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`; - } +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: swipeSchema, +}); - return null; -} +export const handler = createSessionAwareTool({ + internalSchema: swipeSchema as unknown as z.ZodType, + logicFunction: (params: SwipeParams, executor: CommandExecutor) => + swipeLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -195,7 +188,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/tap.ts b/src/mcp/tools/ui-automation/tap.ts similarity index 70% rename from src/mcp/tools/ui-testing/tap.ts rename to src/mcp/tools/ui-automation/tap.ts index d53c3b7d..f033b592 100644 --- a/src/mcp/tools/ui-testing/tap.ts +++ b/src/mcp/tools/ui-automation/tap.ts @@ -4,6 +4,9 @@ import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -14,6 +17,7 @@ import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { getSnapshotUiWarning } from './shared/snapshot-ui-state.ts'; export interface AxeHelpers { getAxePath: () => string | null; @@ -24,12 +28,40 @@ export interface AxeHelpers { // Define schema as ZodObject const baseTapSchema = z.object({ simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), - x: z.number().int({ message: 'X coordinate must be an integer' }).optional(), - y: z.number().int({ message: 'Y coordinate must be an integer' }).optional(), - id: z.string().min(1, { message: 'Id must be non-empty' }).optional(), - label: z.string().min(1, { message: 'Label must be non-empty' }).optional(), - preDelay: z.number().min(0, { message: 'Pre-delay must be non-negative' }).optional(), - postDelay: z.number().min(0, { message: 'Post-delay must be non-negative' }).optional(), + x: z + .number() + .int({ message: 'X coordinate must be an integer' }) + .optional() + .describe( + 'Fallback tap X coordinate. Prefer label/id targeting first; use coordinates when accessibility targeting is unavailable.', + ), + y: z + .number() + .int({ message: 'Y coordinate must be an integer' }) + .optional() + .describe( + 'Fallback tap Y coordinate. Prefer label/id targeting first; use coordinates when accessibility targeting is unavailable.', + ), + id: z + .string() + .min(1, { message: 'Id must be non-empty' }) + .optional() + .describe('Recommended tap target: accessibility element id (AXUniqueId).'), + label: z + .string() + .min(1, { message: 'Label must be non-empty' }) + .optional() + .describe('Recommended when unique: accessibility label (AXLabel).'), + preDelay: z + .number() + .min(0, { message: 'Pre-delay must be non-negative' }) + .optional() + .describe('seconds'), + postDelay: z + .number() + .min(0, { message: 'Post-delay must be non-negative' }) + .optional() + .describe('seconds'), }); const tapSchema = baseTapSchema.superRefine((values, ctx) => { @@ -67,7 +99,7 @@ const tapSchema = baseTapSchema.superRefine((values, ctx) => { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['x'], - message: 'Provide x/y coordinates or an element id/label.', + message: 'Provide an element id/label (recommended) or x/y coordinates as fallback.', }); } }); @@ -79,25 +111,6 @@ const publicSchemaObject = z.strictObject(baseTapSchema.omit({ simulatorId: true const LOG_PREFIX = '[AXe]'; -// Session tracking for describe_ui warnings (shared across UI tools) -const describeUITimestamps = new Map(); -const DESCRIBE_UI_WARNING_TIMEOUT = 60000; // 60 seconds - -function getCoordinateWarning(simulatorId: string): string | null { - const session = describeUITimestamps.get(simulatorId); - if (!session) { - return 'Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.'; - } - - const timeSinceDescribe = Date.now() - session.timestamp; - if (timeSinceDescribe > DESCRIBE_UI_WARNING_TIMEOUT) { - const secondsAgo = Math.round(timeSinceDescribe / 1000); - return `Warning: describe_ui was last called ${secondsAgo} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`; - } - - return null; -} - export async function tapLogic( params: TapParams, executor: CommandExecutor, @@ -106,10 +119,18 @@ export async function tapLogic( getBundledAxeEnvironment, createAxeNotAvailableResponse, }, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'tap'; const { simulatorId, x, y, id, label, preDelay, postDelay } = params; + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + let targetDescription = ''; let actionDescription = ''; let usesCoordinates = false; @@ -148,11 +169,12 @@ export async function tapLogic( await executeAxeCommand(commandArgs, simulatorId, 'tap', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - const warning = usesCoordinates ? getCoordinateWarning(simulatorId) : null; + const coordinateWarning = usesCoordinates ? getSnapshotUiWarning(simulatorId) : null; const message = `${actionDescription} simulated successfully.`; + const warnings = [guard.warningText, coordinateWarning].filter(Boolean).join('\n\n'); - if (warning) { - return createTextResponse(`${message}\n\n${warning}`); + if (warnings) { + return createTextResponse(`${message}\n\n${warnings}`); } return createTextResponse(message); @@ -178,30 +200,22 @@ export async function tapLogic( } } -export default { - name: 'tap', - description: - "Tap at specific coordinates or target elements by accessibility id or label. Use describe_ui to get precise element coordinates prior to using x/y parameters (don't guess from screenshots). Supports optional timing delays.", - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseTapSchema, - }), - annotations: { - title: 'Tap', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: tapSchema as unknown as z.ZodType, - logicFunction: (params: TapParams, executor: CommandExecutor) => - tapLogic(params, executor, { - getAxePath, - getBundledAxeEnvironment, - createAxeNotAvailableResponse, - }), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseTapSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: tapSchema as unknown as z.ZodType, + logicFunction: (params: TapParams, executor: CommandExecutor) => + tapLogic(params, executor, { + getAxePath, + getBundledAxeEnvironment, + createAxeNotAvailableResponse, + }), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -227,7 +241,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/touch.ts b/src/mcp/tools/ui-automation/touch.ts similarity index 71% rename from src/mcp/tools/ui-testing/touch.ts rename to src/mcp/tools/ui-automation/touch.ts index e425a9ec..beab8c71 100644 --- a/src/mcp/tools/ui-testing/touch.ts +++ b/src/mcp/tools/ui-automation/touch.ts @@ -2,7 +2,7 @@ * UI Testing Plugin: Touch * * Perform touch down/up events at specific coordinates. - * Use describe_ui for precise coordinates (don't guess from screenshots). + * Use snapshot_ui for precise coordinates (don't guess from screenshots). */ import * as z from 'zod'; @@ -11,16 +11,20 @@ import { createTextResponse, createErrorResponse } from '../../../utils/response import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, } from '../../../utils/axe-helpers.ts'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, } from '../../../utils/typed-tool-factory.ts'; +import { getSnapshotUiWarning } from './shared/snapshot-ui-state.ts'; // Define schema as ZodObject const touchSchema = z.object({ @@ -29,7 +33,11 @@ const touchSchema = z.object({ y: z.number().int({ message: 'Y coordinate must be an integer' }), down: z.boolean().optional(), up: z.boolean().optional(), - delay: z.number().min(0, { message: 'Delay must be non-negative' }).optional(), + delay: z + .number() + .min(0, { message: 'Delay must be non-negative' }) + .optional() + .describe('seconds'), }); // Use z.infer for type safety @@ -48,6 +56,7 @@ export async function touchLogic( params: TouchParams, executor: CommandExecutor, axeHelpers?: AxeHelpers, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'touch'; @@ -59,6 +68,13 @@ export async function touchLogic( return createErrorResponse('At least one of "down" or "up" must be true'); } + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + const commandArgs = ['touch', '-x', String(x), '-y', String(y)]; if (down) { commandArgs.push('--down'); @@ -80,11 +96,12 @@ export async function touchLogic( await executeAxeCommand(commandArgs, simulatorId, 'touch', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - const warning = getCoordinateWarning(simulatorId); + const coordinateWarning = getSnapshotUiWarning(simulatorId); const message = `Touch event (${actionText}) at (${x}, ${y}) executed successfully.`; + const warnings = [guard.warningText, coordinateWarning].filter(Boolean).join('\n\n'); - if (warning) { - return createTextResponse(`${message}\n\n${warning}`); + if (warnings) { + return createTextResponse(`${message}\n\n${warnings}`); } return createTextResponse(message); @@ -112,49 +129,17 @@ export async function touchLogic( } } -export default { - name: 'touch', - description: - "Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots).", - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: touchSchema, - }), - annotations: { - title: 'Touch', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: touchSchema as unknown as z.ZodType, - logicFunction: (params: TouchParams, executor: CommandExecutor) => touchLogic(params, executor), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; - -// Session tracking for describe_ui warnings -interface DescribeUISession { - timestamp: number; - simulatorId: string; -} - -const describeUITimestamps = new Map(); -const DESCRIBE_UI_WARNING_TIMEOUT = 60000; // 60 seconds - -function getCoordinateWarning(simulatorId: string): string | null { - const session = describeUITimestamps.get(simulatorId); - if (!session) { - return 'Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.'; - } - - const timeSinceDescribe = Date.now() - session.timestamp; - if (timeSinceDescribe > DESCRIBE_UI_WARNING_TIMEOUT) { - const secondsAgo = Math.round(timeSinceDescribe / 1000); - return `Warning: describe_ui was last called ${secondsAgo} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`; - } +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: touchSchema, +}); - return null; -} +export const handler = createSessionAwareTool({ + internalSchema: touchSchema as unknown as z.ZodType, + logicFunction: (params: TouchParams, executor: CommandExecutor) => touchLogic(params, executor), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -183,7 +168,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? helpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/type_text.ts b/src/mcp/tools/ui-automation/type_text.ts similarity index 75% rename from src/mcp/tools/ui-testing/type_text.ts rename to src/mcp/tools/ui-automation/type_text.ts index f0152189..1a1d6e85 100644 --- a/src/mcp/tools/ui-testing/type_text.ts +++ b/src/mcp/tools/ui-automation/type_text.ts @@ -6,12 +6,15 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../../../types/common.ts'; +import type { ToolResponse } from '../../../types/common.ts'; import { log } from '../../../utils/logging/index.ts'; import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts'; +import type { DebuggerManager } from '../../../utils/debugger/debugger-manager.ts'; +import { guardUiAutomationAgainstStoppedDebugger } from '../../../utils/debugger/ui-automation-guard.ts'; import { createAxeNotAvailableResponse, getAxePath, @@ -46,11 +49,19 @@ export async function type_textLogic( params: TypeTextParams, executor: CommandExecutor, axeHelpers?: AxeHelpers, + debuggerManager: DebuggerManager = getDefaultDebuggerManager(), ): Promise { const toolName = 'type_text'; // Params are already validated by the factory, use directly const { simulatorId, text } = params; + const guard = await guardUiAutomationAgainstStoppedDebugger({ + debugger: debuggerManager, + simulatorId, + toolName, + }); + if (guard.blockedResponse) return guard.blockedResponse; + const commandArgs = ['type', text]; log( @@ -61,7 +72,11 @@ export async function type_textLogic( try { await executeAxeCommand(commandArgs, simulatorId, 'type', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); - return createTextResponse('Text typing simulated successfully.'); + const message = 'Text typing simulated successfully.'; + if (guard.warningText) { + return createTextResponse(`${message}\n\n${guard.warningText}`); + } + return createTextResponse(message); } catch (error) { log( 'error', @@ -86,26 +101,18 @@ export async function type_textLogic( } } -export default { - name: 'type_text', - description: - 'Type text (supports US keyboard characters). Use describe_ui to find text field, tap to focus, then type.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: typeTextSchema, - }), - annotations: { - title: 'Type Text', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: typeTextSchema as unknown as z.ZodType, - logicFunction: (params: TypeTextParams, executor: CommandExecutor) => - type_textLogic(params, executor), - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), // Safe factory -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: typeTextSchema, +}); + +export const handler = createSessionAwareTool({ + internalSchema: typeTextSchema as unknown as z.ZodType, + logicFunction: (params: TypeTextParams, executor: CommandExecutor) => + type_textLogic(params, executor), + getExecutor: getDefaultCommandExecutor, + requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], +}); // Helper function for executing axe commands (inlined from src/tools/axe/index.ts) async function executeAxeCommand( @@ -134,7 +141,12 @@ async function executeAxeCommand( // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? helpers.getBundledAxeEnvironment() : undefined; - const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); + const result = await executor( + fullCommand, + `${LOG_PREFIX}: ${commandName}`, + false, + axeEnv ? { env: axeEnv } : undefined, + ); if (!result.success) { throw new AxeError( diff --git a/src/mcp/tools/ui-testing/__tests__/index.test.ts b/src/mcp/tools/ui-testing/__tests__/index.test.ts deleted file mode 100644 index 288c3d58..00000000 --- a/src/mcp/tools/ui-testing/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for ui-testing workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('ui-testing workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('UI Testing & Automation'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/ui-testing/__tests__/screenshot.test.ts b/src/mcp/tools/ui-testing/__tests__/screenshot.test.ts deleted file mode 100644 index a670d937..00000000 --- a/src/mcp/tools/ui-testing/__tests__/screenshot.test.ts +++ /dev/null @@ -1,437 +0,0 @@ -/** - * Tests for screenshot tool plugin - */ - -import { describe, it, expect, beforeEach } from 'vitest'; -import * as z from 'zod'; -import { - createMockExecutor, - createMockFileSystemExecutor, - createNoopExecutor, -} from '../../../../test-utils/mock-executors.ts'; -import { SystemError } from '../../../../utils/responses/index.ts'; -import { sessionStore } from '../../../../utils/session-store.ts'; -import screenshotPlugin, { screenshotLogic } from '../screenshot.ts'; - -describe('Screenshot Plugin', () => { - beforeEach(() => { - sessionStore.clear(); - }); - - describe('Export Field Validation (Literal)', () => { - it('should have correct name', () => { - expect(screenshotPlugin.name).toBe('screenshot'); - }); - - it('should have correct description', () => { - expect(screenshotPlugin.description).toBe( - "Captures screenshot for visual verification. For UI coordinates, use describe_ui instead (don't determine coordinates from screenshots).", - ); - }); - - it('should have handler function', () => { - expect(typeof screenshotPlugin.handler).toBe('function'); - }); - - it('should validate schema fields with safeParse', () => { - const schema = z.object(screenshotPlugin.schema); - - // Public schema is empty; ensure extra fields are stripped - expect(schema.safeParse({}).success).toBe(true); - - const withSimId = schema.safeParse({ - simulatorId: '12345678-1234-4234-8234-123456789012', - }); - expect(withSimId.success).toBe(true); - expect('simulatorId' in (withSimId.data as Record)).toBe(false); - }); - }); - - describe('Plugin Handler Validation', () => { - it('should require simulatorId session default when not provided', async () => { - const result = await screenshotPlugin.handler({}); - - expect(result.isError).toBe(true); - const message = result.content[0].text; - expect(message).toContain('Missing required session defaults'); - expect(message).toContain('simulatorId is required'); - expect(message).toContain('session-set-defaults'); - }); - - it('should validate inline simulatorId overrides', async () => { - const result = await screenshotPlugin.handler({ - simulatorId: 'invalid-uuid', - }); - - expect(result.isError).toBe(true); - const message = result.content[0].text; - expect(message).toContain('Parameter validation failed'); - expect(message).toContain('simulatorId: Invalid Simulator UUID format'); - }); - }); - - describe('Command Generation', () => { - it('should generate correct xcrun simctl command for basic screenshot', async () => { - const capturedCommands: string[][] = []; - const trackingExecutor = async (command: string[]) => { - capturedCommands.push(command); - return { - success: true, - output: 'Screenshot saved', - error: undefined, - process: { pid: 12345 }, - }; - }; - - const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); - const mockFileSystemExecutor = createMockFileSystemExecutor({ - readFile: async () => mockImageBuffer.toString('utf8'), - }); - - await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - trackingExecutor, - mockFileSystemExecutor, - { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, - { v4: () => 'test-uuid' }, - ); - - // Should capture the screenshot command first - expect(capturedCommands[0]).toEqual([ - 'xcrun', - 'simctl', - 'io', - '12345678-1234-4234-8234-123456789012', - 'screenshot', - '/tmp/screenshot_test-uuid.png', - ]); - }); - - it('should generate correct xcrun simctl command with different simulator UUID', async () => { - const capturedCommands: string[][] = []; - const trackingExecutor = async (command: string[]) => { - capturedCommands.push(command); - return { - success: true, - output: 'Screenshot saved', - error: undefined, - process: { pid: 12345 }, - }; - }; - - const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); - const mockFileSystemExecutor = createMockFileSystemExecutor({ - readFile: async () => mockImageBuffer.toString('utf8'), - }); - - await screenshotLogic( - { - simulatorId: 'ABCDEF12-3456-7890-ABCD-ABCDEFABCDEF', - }, - trackingExecutor, - mockFileSystemExecutor, - { tmpdir: () => '/var/tmp', join: (...paths) => paths.join('/') }, - { v4: () => 'another-uuid' }, - ); - - expect(capturedCommands[0]).toEqual([ - 'xcrun', - 'simctl', - 'io', - 'ABCDEF12-3456-7890-ABCD-ABCDEFABCDEF', - 'screenshot', - '/var/tmp/screenshot_another-uuid.png', - ]); - }); - - it('should generate correct xcrun simctl command with custom path dependencies', async () => { - const capturedCommands: string[][] = []; - const trackingExecutor = async (command: string[]) => { - capturedCommands.push(command); - return { - success: true, - output: 'Screenshot saved', - error: undefined, - process: { pid: 12345 }, - }; - }; - - const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); - const mockFileSystemExecutor = createMockFileSystemExecutor({ - readFile: async () => mockImageBuffer.toString('utf8'), - }); - - await screenshotLogic( - { - simulatorId: '98765432-1098-7654-3210-987654321098', - }, - trackingExecutor, - mockFileSystemExecutor, - { - tmpdir: () => '/custom/temp/dir', - join: (...paths) => paths.join('\\'), // Windows-style path joining - }, - { v4: () => 'custom-uuid' }, - ); - - expect(capturedCommands[0]).toEqual([ - 'xcrun', - 'simctl', - 'io', - '98765432-1098-7654-3210-987654321098', - 'screenshot', - '/custom/temp/dir\\screenshot_custom-uuid.png', - ]); - }); - - it('should generate correct xcrun simctl command with generated UUID when no UUID deps provided', async () => { - const capturedCommands: string[][] = []; - const trackingExecutor = async (command: string[]) => { - capturedCommands.push(command); - return { - success: true, - output: 'Screenshot saved', - error: undefined, - process: { pid: 12345 }, - }; - }; - - const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); - const mockFileSystemExecutor = createMockFileSystemExecutor({ - readFile: async () => mockImageBuffer.toString('utf8'), - }); - - await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - trackingExecutor, - mockFileSystemExecutor, - { tmpdir: () => '/tmp', join: (...paths) => paths.join('/') }, - // No UUID deps provided - should use real uuidv4() - ); - - // Verify the command structure but not the exact UUID since it's generated - expect(capturedCommands[0].slice(0, 5)).toEqual([ - 'xcrun', - 'simctl', - 'io', - '12345678-1234-4234-8234-123456789012', - 'screenshot', - ]); - expect(capturedCommands[0][5]).toMatch(/^\/tmp\/screenshot_[a-f0-9-]+\.png$/); - }); - }); - - describe('Handler Behavior (Complete Literal Returns)', () => { - it('should handle parameter validation via plugin handler (not logic function)', async () => { - // Note: With Zod validation in createTypedTool, the screenshotLogic function - // will never receive invalid parameters - validation happens at the handler level. - // This test documents that screenshotLogic assumes valid parameters. - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - createMockExecutor({ - success: true, - output: 'Screenshot saved', - error: undefined, - }), - createMockFileSystemExecutor({ - readFile: async () => Buffer.from('fake-image-data', 'utf8').toString('utf8'), - }), - ); - - expect(result.isError).toBe(false); - expect(result.content[0].type).toBe('image'); - }); - - it('should return success for valid screenshot capture', async () => { - const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); - - const mockExecutor = createMockExecutor({ - success: true, - output: 'Screenshot saved', - error: undefined, - }); - - const mockFileSystemExecutor = createMockFileSystemExecutor({ - readFile: async () => mockImageBuffer.toString('utf8'), - }); - - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - mockExecutor, - mockFileSystemExecutor, - ); - - expect(result).toEqual({ - content: [ - { - type: 'image', - data: 'fake-image-data', - mimeType: 'image/jpeg', - }, - ], - isError: false, - }); - }); - - it('should handle command execution failure', async () => { - const mockExecutor = createMockExecutor({ - success: false, - output: '', - error: 'Simulator not found', - }); - - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - mockExecutor, - createMockFileSystemExecutor(), - ); - - expect(result).toEqual({ - content: [ - { - type: 'text', - text: 'Error: System error executing screenshot: Failed to capture screenshot: Simulator not found', - }, - ], - isError: true, - }); - }); - - it('should handle file reading errors', async () => { - const mockExecutor = createMockExecutor({ - success: true, - output: 'Screenshot saved', - error: undefined, - }); - - const mockFileSystemExecutor = createMockFileSystemExecutor({ - readFile: async () => { - throw new Error('File not found'); - }, - }); - - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - mockExecutor, - mockFileSystemExecutor, - ); - - expect(result).toEqual({ - content: [ - { - type: 'text', - text: 'Error: Screenshot captured but failed to process image file: File not found', - }, - ], - isError: true, - }); - }); - - it('should handle file cleanup errors gracefully', async () => { - const mockImageBuffer = Buffer.from('fake-image-data', 'utf8'); - - const mockExecutor = createMockExecutor({ - success: true, - output: 'Screenshot saved', - error: undefined, - }); - - const mockFileSystemExecutor = createMockFileSystemExecutor({ - readFile: async () => mockImageBuffer.toString('utf8'), - // unlink method is not overridden, so it will use the default (no-op) - // which simulates the cleanup failure being caught and logged - }); - - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - mockExecutor, - mockFileSystemExecutor, - ); - - // Should still return successful result despite cleanup failure - expect(result).toEqual({ - content: [ - { - type: 'image', - data: 'fake-image-data', - mimeType: 'image/jpeg', - }, - ], - isError: false, - }); - }); - - it('should handle SystemError from command execution', async () => { - const mockExecutor = async () => { - throw new SystemError('System error occurred'); - }; - - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - mockExecutor, - createMockFileSystemExecutor(), - ); - - expect(result).toEqual({ - content: [ - { type: 'text', text: 'Error: System error executing screenshot: System error occurred' }, - ], - isError: true, - }); - }); - - it('should handle unexpected Error objects', async () => { - const mockExecutor = async () => { - throw new Error('Unexpected error'); - }; - - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - mockExecutor, - createMockFileSystemExecutor(), - ); - - expect(result).toEqual({ - content: [{ type: 'text', text: 'Error: An unexpected error occurred: Unexpected error' }], - isError: true, - }); - }); - - it('should handle unexpected string errors', async () => { - const mockExecutor = async () => { - throw 'String error'; - }; - - const result = await screenshotLogic( - { - simulatorId: '12345678-1234-4234-8234-123456789012', - }, - mockExecutor, - createMockFileSystemExecutor(), - ); - - expect(result).toEqual({ - content: [{ type: 'text', text: 'Error: An unexpected error occurred: String error' }], - isError: true, - }); - }); - }); -}); diff --git a/src/mcp/tools/ui-testing/index.ts b/src/mcp/tools/ui-testing/index.ts deleted file mode 100644 index 0cfb4e9e..00000000 --- a/src/mcp/tools/ui-testing/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'UI Testing & Automation', - description: - 'UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows.', -}; diff --git a/src/mcp/tools/ui-testing/screenshot.ts b/src/mcp/tools/ui-testing/screenshot.ts deleted file mode 100644 index e07d582c..00000000 --- a/src/mcp/tools/ui-testing/screenshot.ts +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Screenshot tool plugin - Capture screenshots from iOS Simulator - */ -import * as path from 'path'; -import { tmpdir } from 'os'; -import * as z from 'zod'; -import { v4 as uuidv4 } from 'uuid'; -import { ToolResponse, createImageContent } from '../../../types/common.ts'; -import { log } from '../../../utils/logging/index.ts'; -import { createErrorResponse, SystemError } from '../../../utils/responses/index.ts'; -import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; -import { - getDefaultFileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/execution/index.ts'; -import { - createSessionAwareTool, - getSessionAwareToolSchemaShape, -} from '../../../utils/typed-tool-factory.ts'; - -const LOG_PREFIX = '[Screenshot]'; - -// Define schema as ZodObject -const screenshotSchema = z.object({ - simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }), -}); - -// Use z.infer for type safety -type ScreenshotParams = z.infer; - -const publicSchemaObject = z.strictObject( - screenshotSchema.omit({ simulatorId: true } as const).shape, -); - -export async function screenshotLogic( - params: ScreenshotParams, - executor: CommandExecutor, - fileSystemExecutor: FileSystemExecutor = getDefaultFileSystemExecutor(), - pathUtils: { tmpdir: () => string; join: (...paths: string[]) => string } = { ...path, tmpdir }, - uuidUtils: { v4: () => string } = { v4: uuidv4 }, -): Promise { - const { simulatorId } = params; - const tempDir = pathUtils.tmpdir(); - const screenshotFilename = `screenshot_${uuidUtils.v4()}.png`; - const screenshotPath = pathUtils.join(tempDir, screenshotFilename); - const optimizedFilename = `screenshot_optimized_${uuidUtils.v4()}.jpg`; - const optimizedPath = pathUtils.join(tempDir, optimizedFilename); - // Use xcrun simctl to take screenshot - const commandArgs: string[] = [ - 'xcrun', - 'simctl', - 'io', - simulatorId, - 'screenshot', - screenshotPath, - ]; - - log('info', `${LOG_PREFIX}/screenshot: Starting capture to ${screenshotPath} on ${simulatorId}`); - - try { - // Execute the screenshot command - const result = await executor(commandArgs, `${LOG_PREFIX}: screenshot`, false); - - if (!result.success) { - throw new SystemError(`Failed to capture screenshot: ${result.error ?? result.output}`); - } - - log('info', `${LOG_PREFIX}/screenshot: Success for ${simulatorId}`); - - try { - // Optimize the image for LLM consumption: resize to max 800px width and convert to JPEG - const optimizeArgs = [ - 'sips', - '-Z', - '800', // Resize to max 800px (maintains aspect ratio) - '-s', - 'format', - 'jpeg', // Convert to JPEG - '-s', - 'formatOptions', - '75', // 75% quality compression - screenshotPath, - '--out', - optimizedPath, - ]; - - const optimizeResult = await executor(optimizeArgs, `${LOG_PREFIX}: optimize image`, false); - - if (!optimizeResult.success) { - log('warning', `${LOG_PREFIX}/screenshot: Image optimization failed, using original PNG`); - // Fallback to original PNG if optimization fails - const base64Image = await fileSystemExecutor.readFile(screenshotPath, 'base64'); - - // Clean up - try { - await fileSystemExecutor.rm(screenshotPath); - } catch (err) { - log('warning', `${LOG_PREFIX}/screenshot: Failed to delete temp file: ${err}`); - } - - return { - content: [createImageContent(base64Image, 'image/png')], - isError: false, - }; - } - - log('info', `${LOG_PREFIX}/screenshot: Image optimized successfully`); - - // Read the optimized image file as base64 - const base64Image = await fileSystemExecutor.readFile(optimizedPath, 'base64'); - - log('info', `${LOG_PREFIX}/screenshot: Successfully encoded image as Base64`); - - // Clean up both temporary files - try { - await fileSystemExecutor.rm(screenshotPath); - await fileSystemExecutor.rm(optimizedPath); - } catch (err) { - log('warning', `${LOG_PREFIX}/screenshot: Failed to delete temporary files: ${err}`); - } - - // Return the optimized image (JPEG format, smaller size) - return { - content: [createImageContent(base64Image, 'image/jpeg')], - isError: false, - }; - } catch (fileError) { - log('error', `${LOG_PREFIX}/screenshot: Failed to process image file: ${fileError}`); - return createErrorResponse( - `Screenshot captured but failed to process image file: ${fileError instanceof Error ? fileError.message : String(fileError)}`, - ); - } - } catch (_error) { - log('error', `${LOG_PREFIX}/screenshot: Failed - ${_error}`); - if (_error instanceof SystemError) { - return createErrorResponse( - `System error executing screenshot: ${_error.message}`, - _error.originalError?.stack, - ); - } - return createErrorResponse( - `An unexpected error occurred: ${_error instanceof Error ? _error.message : String(_error)}`, - ); - } -} - -export default { - name: 'screenshot', - description: - "Captures screenshot for visual verification. For UI coordinates, use describe_ui instead (don't determine coordinates from screenshots).", - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: screenshotSchema, - }), - annotations: { - title: 'Screenshot', - readOnlyHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: screenshotSchema as unknown as z.ZodType, - logicFunction: (params: ScreenshotParams, executor: CommandExecutor) => { - return screenshotLogic(params, executor); - }, - getExecutor: getDefaultCommandExecutor, - requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], - }), -}; diff --git a/src/mcp/tools/utilities/__tests__/clean.test.ts b/src/mcp/tools/utilities/__tests__/clean.test.ts index 48d934f1..707a67cc 100644 --- a/src/mcp/tools/utilities/__tests__/clean.test.ts +++ b/src/mcp/tools/utilities/__tests__/clean.test.ts @@ -1,7 +1,10 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; -import tool, { cleanLogic } from '../clean.ts'; -import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { schema, handler, cleanLogic } from '../clean.ts'; +import { + createMockExecutor, + createMockCommandResponse, +} from '../../../../test-utils/mock-executors.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; describe('clean (unified) tool', () => { @@ -9,31 +12,27 @@ describe('clean (unified) tool', () => { sessionStore.clear(); }); - it('exports correct name/description/schema/handler', () => { - expect(tool.name).toBe('clean'); - expect(tool.description).toBe('Cleans build products with xcodebuild.'); - expect(typeof tool.handler).toBe('function'); + it('exports correct schema/handler', () => { + expect(typeof handler).toBe('function'); - const schema = z.strictObject(tool.schema); - expect(schema.safeParse({}).success).toBe(true); + const schemaObj = z.strictObject(schema); + expect(schemaObj.safeParse({}).success).toBe(true); expect( - schema.safeParse({ - derivedDataPath: '/tmp/Derived', + schemaObj.safeParse({ extraArgs: ['--quiet'], - preferXcodebuild: true, platform: 'iOS Simulator', }).success, ).toBe(true); - expect(schema.safeParse({ configuration: 'Debug' }).success).toBe(false); + expect(schemaObj.safeParse({ derivedDataPath: '/tmp/Derived' }).success).toBe(false); + expect(schemaObj.safeParse({ preferXcodebuild: true }).success).toBe(false); + expect(schemaObj.safeParse({ configuration: 'Debug' }).success).toBe(false); - const schemaKeys = Object.keys(tool.schema).sort(); - expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'platform', 'preferXcodebuild'].sort(), - ); + const schemaKeys = Object.keys(schema).sort(); + expect(schemaKeys).toEqual(['extraArgs', 'platform'].sort()); }); it('handler validation: error when neither projectPath nor workspacePath provided', async () => { - const result = await (tool as any).handler({}); + const result = await handler({}); expect(result.isError).toBe(true); const text = String(result.content?.[0]?.text ?? ''); expect(text).toContain('Missing required session defaults'); @@ -41,7 +40,7 @@ describe('clean (unified) tool', () => { }); it('handler validation: error when both projectPath and workspacePath provided', async () => { - const result = await (tool as any).handler({ + const result = await handler({ projectPath: '/p.xcodeproj', workspacePath: '/w.xcworkspace', }); @@ -66,7 +65,7 @@ describe('clean (unified) tool', () => { }); it('handler validation: requires scheme when workspacePath is provided', async () => { - const result = await (tool as any).handler({ workspacePath: '/w.xcworkspace' }); + const result = await handler({ workspacePath: '/w.xcworkspace' }); expect(result.isError).toBe(true); const text = String(result.content?.[0]?.text ?? ''); expect(text).toContain('Parameter validation failed'); @@ -77,7 +76,7 @@ describe('clean (unified) tool', () => { let capturedCommand: string[] = []; const mockExecutor = async (command: string[]) => { capturedCommand = command; - return { success: true, output: 'clean success' }; + return createMockCommandResponse({ success: true, output: 'clean success' }); }; const result = await cleanLogic( @@ -96,7 +95,7 @@ describe('clean (unified) tool', () => { let capturedCommand: string[] = []; const mockExecutor = async (command: string[]) => { capturedCommand = command; - return { success: true, output: 'clean success' }; + return createMockCommandResponse({ success: true, output: 'clean success' }); }; const result = await cleanLogic( @@ -119,7 +118,7 @@ describe('clean (unified) tool', () => { let capturedCommand: string[] = []; const mockExecutor = async (command: string[]) => { capturedCommand = command; - return { success: true, output: 'clean success' }; + return createMockCommandResponse({ success: true, output: 'clean success' }); }; const result = await cleanLogic( @@ -139,7 +138,7 @@ describe('clean (unified) tool', () => { }); it('handler validation: rejects invalid platform values', async () => { - const result = await (tool as any).handler({ + const result = await handler({ projectPath: '/p.xcodeproj', scheme: 'App', platform: 'InvalidPlatform', diff --git a/src/mcp/tools/utilities/__tests__/index.test.ts b/src/mcp/tools/utilities/__tests__/index.test.ts deleted file mode 100644 index b772dbd7..00000000 --- a/src/mcp/tools/utilities/__tests__/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Tests for utilities workflow metadata - */ -import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.ts'; - -describe('utilities workflow metadata', () => { - describe('Workflow Structure', () => { - it('should export workflow object with required properties', () => { - expect(workflow).toHaveProperty('name'); - expect(workflow).toHaveProperty('description'); - }); - - it('should have correct workflow name', () => { - expect(workflow.name).toBe('Project Utilities'); - }); - - it('should have correct description', () => { - expect(workflow.description).toBe( - 'Essential project maintenance utilities for cleaning and managing existing projects. Provides clean operations for both .xcodeproj and .xcworkspace files.', - ); - }); - }); - - describe('Workflow Validation', () => { - it('should have valid string properties', () => { - expect(typeof workflow.name).toBe('string'); - expect(typeof workflow.description).toBe('string'); - expect(workflow.name.length).toBeGreaterThan(0); - expect(workflow.description.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/mcp/tools/utilities/clean.ts b/src/mcp/tools/utilities/clean.ts index e701e74b..1d683d1e 100644 --- a/src/mcp/tools/utilities/clean.ts +++ b/src/mcp/tools/utilities/clean.ts @@ -13,7 +13,8 @@ import { import type { CommandExecutor } from '../../../utils/execution/index.ts'; import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; -import { ToolResponse, SharedBuildParams, XcodePlatform } from '../../../types/common.ts'; +import type { ToolResponse, SharedBuildParams } from '../../../types/common.ts'; +import { XcodePlatform } from '../../../types/common.ts'; import { createErrorResponse } from '../../../utils/responses/index.ts'; import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; @@ -24,17 +25,9 @@ const baseOptions = { .string() .optional() .describe('Optional: Build configuration to clean (Debug, Release, etc.)'), - derivedDataPath: z - .string() - .optional() - .describe('Optional: Path where derived data might be located'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), - preferXcodebuild: z - .boolean() - .optional() - .describe( - 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', - ), + derivedDataPath: z.string().optional(), + extraArgs: z.array(z.string()).optional(), + preferXcodebuild: z.boolean().optional(), platform: z .enum([ 'macOS', @@ -47,10 +40,7 @@ const baseOptions = { 'visionOS', 'visionOS Simulator', ]) - .optional() - .describe( - 'Optional: Platform to clean for (defaults to iOS). Choose from macOS, iOS, iOS Simulator, watchOS, watchOS Simulator, tvOS, tvOS Simulator, visionOS, visionOS Simulator', - ), + .optional(), }; const baseSchemaObject = z.object({ @@ -155,26 +145,21 @@ const publicSchemaObject = baseSchemaObject.omit({ workspacePath: true, scheme: true, configuration: true, + derivedDataPath: true, + preferXcodebuild: true, } as const); -export default { - name: 'clean', - description: 'Cleans build products with xcodebuild.', - schema: getSessionAwareToolSchemaShape({ - sessionAware: publicSchemaObject, - legacy: baseSchemaObject, - }), - annotations: { - title: 'Clean', - destructiveHint: true, - }, - handler: createSessionAwareTool({ - internalSchema: cleanSchema as unknown as z.ZodType, - logicFunction: cleanLogic, - getExecutor: getDefaultCommandExecutor, - requirements: [ - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], - }), -}; +export const schema = getSessionAwareToolSchemaShape({ + sessionAware: publicSchemaObject, + legacy: baseSchemaObject, +}); + +export const handler = createSessionAwareTool({ + internalSchema: cleanSchema as unknown as z.ZodType, + logicFunction: cleanLogic, + getExecutor: getDefaultCommandExecutor, + requirements: [ + { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, + ], + exclusivePairs: [['projectPath', 'workspacePath']], +}); diff --git a/src/mcp/tools/utilities/index.ts b/src/mcp/tools/utilities/index.ts deleted file mode 100644 index 905e7541..00000000 --- a/src/mcp/tools/utilities/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const workflow = { - name: 'Project Utilities', - description: - 'Essential project maintenance utilities for cleaning and managing existing projects. Provides clean operations for both .xcodeproj and .xcworkspace files.', -}; diff --git a/src/mcp/tools/workflow-discovery/__tests__/manage_workflows.test.ts b/src/mcp/tools/workflow-discovery/__tests__/manage_workflows.test.ts new file mode 100644 index 00000000..c610c1ce --- /dev/null +++ b/src/mcp/tools/workflow-discovery/__tests__/manage_workflows.test.ts @@ -0,0 +1,89 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('../../../../utils/tool-registry.ts', () => ({ + applyWorkflowSelectionFromManifest: vi.fn(), + getRegisteredWorkflows: vi.fn(), + getMcpPredicateContext: vi.fn().mockReturnValue({ + runtime: 'mcp', + config: { debug: false }, + runningUnderXcode: false, + }), +})); + +vi.mock('../../../../utils/config-store.ts', () => ({ + getConfig: vi.fn().mockReturnValue({ + debug: false, + experimentalWorkflowDiscovery: false, + enabledWorkflows: [], + }), +})); + +import { manage_workflowsLogic } from '../manage_workflows.ts'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { + applyWorkflowSelectionFromManifest, + getRegisteredWorkflows, +} from '../../../../utils/tool-registry.ts'; + +describe('manage_workflows tool', () => { + beforeEach(() => { + vi.mocked(applyWorkflowSelectionFromManifest).mockReset(); + vi.mocked(getRegisteredWorkflows).mockReset(); + }); + + it('merges new workflows with current set when enable is true', async () => { + vi.mocked(getRegisteredWorkflows).mockReturnValue(['simulator']); + vi.mocked(applyWorkflowSelectionFromManifest).mockResolvedValue({ + enabledWorkflows: ['simulator', 'device'], + registeredToolCount: 0, + }); + + const executor = createMockExecutor({ success: true, output: '' }); + const result = await manage_workflowsLogic( + { workflowNames: ['device'], enable: true }, + executor, + ); + + expect(vi.mocked(applyWorkflowSelectionFromManifest)).toHaveBeenCalledWith( + ['simulator', 'device'], + expect.objectContaining({ runtime: 'mcp' }), + ); + expect(result.content[0].text).toBe('Workflows enabled: simulator, device'); + }); + + it('removes requested workflows when enable is false', async () => { + vi.mocked(getRegisteredWorkflows).mockReturnValue(['simulator', 'device']); + vi.mocked(applyWorkflowSelectionFromManifest).mockResolvedValue({ + enabledWorkflows: ['simulator'], + registeredToolCount: 0, + }); + + const executor = createMockExecutor({ success: true, output: '' }); + const result = await manage_workflowsLogic( + { workflowNames: ['device'], enable: false }, + executor, + ); + + expect(vi.mocked(applyWorkflowSelectionFromManifest)).toHaveBeenCalledWith( + ['simulator'], + expect.objectContaining({ runtime: 'mcp' }), + ); + expect(result.content[0].text).toBe('Workflows enabled: simulator'); + }); + + it('accepts workflowName as an array', async () => { + vi.mocked(getRegisteredWorkflows).mockReturnValue(['simulator']); + vi.mocked(applyWorkflowSelectionFromManifest).mockResolvedValue({ + enabledWorkflows: ['simulator', 'device', 'logging'], + registeredToolCount: 0, + }); + + const executor = createMockExecutor({ success: true, output: '' }); + await manage_workflowsLogic({ workflowNames: ['device', 'logging'], enable: true }, executor); + + expect(vi.mocked(applyWorkflowSelectionFromManifest)).toHaveBeenCalledWith( + ['simulator', 'device', 'logging'], + expect.objectContaining({ runtime: 'mcp' }), + ); + }); +}); diff --git a/src/mcp/tools/workflow-discovery/manage_workflows.ts b/src/mcp/tools/workflow-discovery/manage_workflows.ts new file mode 100644 index 00000000..fc082c26 --- /dev/null +++ b/src/mcp/tools/workflow-discovery/manage_workflows.ts @@ -0,0 +1,51 @@ +import * as z from 'zod'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { getDefaultCommandExecutor, type CommandExecutor } from '../../../utils/execution/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import type { ToolResponse } from '../../../types/common.ts'; +import { + applyWorkflowSelectionFromManifest, + getRegisteredWorkflows, + getMcpPredicateContext, +} from '../../../utils/tool-registry.ts'; + +const baseSchemaObject = z.object({ + workflowNames: z.array(z.string()).describe('Workflow directory name(s).'), + enable: z.boolean().describe('Enable or disable the selected workflows.'), +}); + +const manageWorkflowsSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); + +export type ManageWorkflowsParams = z.infer; + +export async function manage_workflowsLogic( + params: ManageWorkflowsParams, + _neverExecutor: CommandExecutor, +): Promise { + const workflowNames = params.workflowNames; + const currentWorkflows = getRegisteredWorkflows(); + const requestedSet = new Set( + workflowNames.map((name) => name.trim().toLowerCase()).filter(Boolean), + ); + let nextWorkflows: string[]; + if (params.enable === false) { + nextWorkflows = currentWorkflows.filter((name) => !requestedSet.has(name.toLowerCase())); + } else { + nextWorkflows = [...new Set([...currentWorkflows, ...workflowNames])]; + } + + const ctx = getMcpPredicateContext(); + + const registryState = await applyWorkflowSelectionFromManifest(nextWorkflows, ctx); + + return createTextResponse(`Workflows enabled: ${registryState.enabledWorkflows.join(', ')}`); +} + +export const schema = baseSchemaObject.shape; + +export const handler = createTypedTool( + manageWorkflowsSchema, + manage_workflowsLogic, + getDefaultCommandExecutor, +); diff --git a/src/mcp/tools/xcode-ide/__tests__/bridge_tools.test.ts b/src/mcp/tools/xcode-ide/__tests__/bridge_tools.test.ts new file mode 100644 index 00000000..d183cb82 --- /dev/null +++ b/src/mcp/tools/xcode-ide/__tests__/bridge_tools.test.ts @@ -0,0 +1,118 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +vi.mock('../../../../server/server-state.ts', () => ({ + getServer: vi.fn(), +})); + +vi.mock('../../../../integrations/xcode-tools-bridge/core.ts', () => ({ + buildXcodeToolsBridgeStatus: vi.fn(), + classifyBridgeError: vi.fn(() => 'XCODE_MCP_UNAVAILABLE'), + getMcpBridgeAvailability: vi.fn(), + serializeBridgeTool: vi.fn((tool) => tool), +})); + +const clientMocks = { + connectOnce: vi.fn(), + listTools: vi.fn(), + callTool: vi.fn(), + disconnect: vi.fn(), + getStatus: vi.fn(), +}; + +vi.mock('../../../../integrations/xcode-tools-bridge/client.ts', () => ({ + XcodeToolsBridgeClient: vi.fn().mockImplementation(() => clientMocks), +})); + +import { handler as statusHandler } from '../xcode_tools_bridge_status.ts'; +import { handler as syncHandler } from '../xcode_tools_bridge_sync.ts'; +import { handler as disconnectHandler } from '../xcode_tools_bridge_disconnect.ts'; +import { handler as listHandler } from '../xcode_ide_list_tools.ts'; +import { handler as callHandler } from '../xcode_ide_call_tool.ts'; +import { getServer } from '../../../../server/server-state.ts'; +import { shutdownXcodeToolsBridge } from '../../../../integrations/xcode-tools-bridge/index.ts'; +import { + buildXcodeToolsBridgeStatus, + getMcpBridgeAvailability, +} from '../../../../integrations/xcode-tools-bridge/core.ts'; + +describe('xcode-ide bridge tools (standalone fallback)', () => { + beforeEach(async () => { + await shutdownXcodeToolsBridge(); + + vi.mocked(getServer).mockReset(); + vi.mocked(buildXcodeToolsBridgeStatus).mockReset(); + vi.mocked(getMcpBridgeAvailability).mockReset(); + clientMocks.connectOnce.mockReset(); + clientMocks.listTools.mockReset(); + clientMocks.disconnect.mockReset(); + clientMocks.getStatus.mockReset(); + clientMocks.callTool.mockReset(); + + vi.mocked(getServer).mockReturnValue(undefined); + clientMocks.getStatus.mockReturnValue({ + connected: false, + bridgePid: null, + lastError: null, + }); + vi.mocked(buildXcodeToolsBridgeStatus).mockResolvedValue({ + workflowEnabled: false, + bridgeAvailable: true, + bridgePath: '/usr/bin/mcpbridge', + xcodeRunning: true, + connected: false, + bridgePid: null, + proxiedToolCount: 0, + lastError: null, + xcodePid: null, + xcodeSessionId: null, + }); + vi.mocked(getMcpBridgeAvailability).mockResolvedValue({ + available: true, + path: '/usr/bin/mcpbridge', + }); + clientMocks.listTools.mockResolvedValue([{ name: 'toolA' }, { name: 'toolB' }]); + clientMocks.connectOnce.mockResolvedValue(undefined); + clientMocks.callTool.mockResolvedValue({ + content: [{ type: 'text', text: 'ok' }], + isError: false, + }); + clientMocks.disconnect.mockResolvedValue(undefined); + }); + + it('status handler returns bridge status without MCP server instance', async () => { + const result = await statusHandler(); + const payload = JSON.parse(result.content[0].text as string); + expect(payload.bridgeAvailable).toBe(true); + expect(buildXcodeToolsBridgeStatus).toHaveBeenCalledOnce(); + }); + + it('sync handler uses direct bridge client when MCP server is not initialized', async () => { + const result = await syncHandler(); + const payload = JSON.parse(result.content[0].text as string); + expect(payload.sync.total).toBe(2); + expect(clientMocks.connectOnce).toHaveBeenCalledOnce(); + expect(clientMocks.listTools).toHaveBeenCalledOnce(); + expect(clientMocks.disconnect).toHaveBeenCalledOnce(); + }); + + it('disconnect handler succeeds without MCP server instance', async () => { + const result = await disconnectHandler(); + const payload = JSON.parse(result.content[0].text as string); + expect(payload.connected).toBe(false); + expect(clientMocks.disconnect).toHaveBeenCalledOnce(); + }); + + it('list handler returns bridge tools without MCP server instance', async () => { + const result = await listHandler({ refresh: true }); + const payload = JSON.parse(result.content[0].text as string); + expect(payload.toolCount).toBe(2); + expect(payload.tools).toHaveLength(2); + expect(clientMocks.listTools).toHaveBeenCalledOnce(); + }); + + it('call handler forwards remote tool calls without MCP server instance', async () => { + const result = await callHandler({ remoteTool: 'toolA', arguments: { foo: 'bar' } }); + expect(result.isError).toBe(false); + expect(clientMocks.callTool).toHaveBeenCalledWith('toolA', { foo: 'bar' }, {}); + }); +}); diff --git a/src/mcp/tools/xcode-ide/__tests__/sync_xcode_defaults.test.ts b/src/mcp/tools/xcode-ide/__tests__/sync_xcode_defaults.test.ts new file mode 100644 index 00000000..5a1b9ff8 --- /dev/null +++ b/src/mcp/tools/xcode-ide/__tests__/sync_xcode_defaults.test.ts @@ -0,0 +1,172 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { existsSync } from 'fs'; +import { join } from 'path'; +import { sessionStore } from '../../../../utils/session-store.ts'; +import { createCommandMatchingMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import { schema, syncXcodeDefaultsLogic } from '../sync_xcode_defaults.ts'; + +// Path to the example project (used as test fixture) +const EXAMPLE_PROJECT_PATH = join(process.cwd(), 'example_projects/iOS/MCPTest.xcodeproj'); +const EXAMPLE_XCUSERSTATE = join( + EXAMPLE_PROJECT_PATH, + 'project.xcworkspace/xcuserdata/johndoe.xcuserdatad/UserInterfaceState.xcuserstate', +); + +describe('sync_xcode_defaults tool', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + describe('Export Field Validation', () => { + it('should have schema object', () => { + expect(schema).toBeDefined(); + expect(typeof schema).toBe('object'); + }); + }); + + describe('syncXcodeDefaultsLogic', () => { + it('returns error when no project found', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '' }, + }); + + const result = await syncXcodeDefaultsLogic({}, { executor, cwd: '/test/project' }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('Failed to read Xcode IDE state'); + }); + + it('returns error when xcuserstate file not found', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '/test/project/MyApp.xcworkspace\n' }, + stat: { success: false, error: 'No such file' }, + }); + + const result = await syncXcodeDefaultsLogic({}, { executor, cwd: '/test/project' }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('Failed to read Xcode IDE state'); + }); + }); + + describe('syncXcodeDefaultsLogic integration', () => { + // These tests use the actual example project fixture + + it.skipIf(!existsSync(EXAMPLE_XCUSERSTATE))( + 'syncs scheme and simulator from example project', + async () => { + const simctlOutput = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.xrOS-2-0': [ + { udid: 'B38FE93D-578B-454B-BE9A-C6FA0CE5F096', name: 'Apple Vision Pro' }, + ], + }, + }); + + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'johndoe\n' }, + find: { output: `${EXAMPLE_PROJECT_PATH}\n` }, + stat: { output: '1704067200\n' }, + 'xcrun simctl': { output: simctlOutput }, + xcodebuild: { output: ' PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MCPTest\n' }, + }); + + const result = await syncXcodeDefaultsLogic( + {}, + { executor, cwd: join(process.cwd(), 'example_projects/iOS') }, + ); + + expect(result.isError).toBe(false); + expect(result.content[0].text).toContain('Synced session defaults from Xcode IDE'); + expect(result.content[0].text).toContain('Scheme: MCPTest'); + expect(result.content[0].text).toContain( + 'Simulator ID: B38FE93D-578B-454B-BE9A-C6FA0CE5F096', + ); + expect(result.content[0].text).toContain('Simulator Name: Apple Vision Pro'); + expect(result.content[0].text).toContain('Bundle ID: io.sentry.MCPTest'); + + const defaults = sessionStore.getAll(); + expect(defaults.scheme).toBe('MCPTest'); + expect(defaults.simulatorId).toBe('B38FE93D-578B-454B-BE9A-C6FA0CE5F096'); + expect(defaults.simulatorName).toBe('Apple Vision Pro'); + expect(defaults.bundleId).toBe('io.sentry.MCPTest'); + }, + ); + + it.skipIf(!existsSync(EXAMPLE_XCUSERSTATE))('syncs using configured projectPath', async () => { + const simctlOutput = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.xrOS-2-0': [ + { udid: 'B38FE93D-578B-454B-BE9A-C6FA0CE5F096', name: 'Apple Vision Pro' }, + ], + }, + }); + + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'johndoe\n' }, + 'test -f': { success: true }, + 'xcrun simctl': { output: simctlOutput }, + xcodebuild: { output: ' PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MCPTest\n' }, + }); + + const result = await syncXcodeDefaultsLogic( + {}, + { + executor, + cwd: '/some/other/path', + projectPath: EXAMPLE_PROJECT_PATH, + }, + ); + + expect(result.isError).toBe(false); + expect(result.content[0].text).toContain('Scheme: MCPTest'); + + const defaults = sessionStore.getAll(); + expect(defaults.scheme).toBe('MCPTest'); + expect(defaults.simulatorId).toBe('B38FE93D-578B-454B-BE9A-C6FA0CE5F096'); + expect(defaults.bundleId).toBe('io.sentry.MCPTest'); + }); + + it.skipIf(!existsSync(EXAMPLE_XCUSERSTATE))('updates existing session defaults', async () => { + // Set some existing defaults + sessionStore.setDefaults({ + scheme: 'OldScheme', + simulatorId: 'OLD-SIM-UUID', + projectPath: '/some/project.xcodeproj', + }); + + const simctlOutput = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.xrOS-2-0': [ + { udid: 'B38FE93D-578B-454B-BE9A-C6FA0CE5F096', name: 'Apple Vision Pro' }, + ], + }, + }); + + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'johndoe\n' }, + find: { output: `${EXAMPLE_PROJECT_PATH}\n` }, + stat: { output: '1704067200\n' }, + 'xcrun simctl': { output: simctlOutput }, + xcodebuild: { output: ' PRODUCT_BUNDLE_IDENTIFIER = io.sentry.MCPTest\n' }, + }); + + const result = await syncXcodeDefaultsLogic( + {}, + { executor, cwd: join(process.cwd(), 'example_projects/iOS') }, + ); + + expect(result.isError).toBe(false); + + const defaults = sessionStore.getAll(); + expect(defaults.scheme).toBe('MCPTest'); + expect(defaults.simulatorId).toBe('B38FE93D-578B-454B-BE9A-C6FA0CE5F096'); + expect(defaults.simulatorName).toBe('Apple Vision Pro'); + expect(defaults.bundleId).toBe('io.sentry.MCPTest'); + // Original projectPath should be preserved + expect(defaults.projectPath).toBe('/some/project.xcodeproj'); + }); + }); +}); diff --git a/src/mcp/tools/xcode-ide/shared.ts b/src/mcp/tools/xcode-ide/shared.ts new file mode 100644 index 00000000..15de889d --- /dev/null +++ b/src/mcp/tools/xcode-ide/shared.ts @@ -0,0 +1,15 @@ +import type { ToolResponse } from '../../../types/common.ts'; +import type { XcodeToolsBridgeToolHandler } from '../../../integrations/xcode-tools-bridge/index.ts'; +import { getServer } from '../../../server/server-state.ts'; +import { getXcodeToolsBridgeToolHandler } from '../../../integrations/xcode-tools-bridge/index.ts'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; + +export async function withBridgeToolHandler( + callback: (bridge: XcodeToolsBridgeToolHandler) => Promise, +): Promise { + const bridge = getXcodeToolsBridgeToolHandler(getServer()); + if (!bridge) { + return createErrorResponse('Bridge unavailable', 'Unable to initialize xcode tools bridge'); + } + return callback(bridge); +} diff --git a/src/mcp/tools/xcode-ide/sync_xcode_defaults.ts b/src/mcp/tools/xcode-ide/sync_xcode_defaults.ts new file mode 100644 index 00000000..4c9b91fb --- /dev/null +++ b/src/mcp/tools/xcode-ide/sync_xcode_defaults.ts @@ -0,0 +1,121 @@ +/** + * Sync Xcode Defaults Tool + * + * Reads Xcode's IDE state (active scheme and run destination) and updates + * session defaults to match. This allows the agent to re-sync if the user + * changes their selection in Xcode mid-session. + * + * Only visible when running under Xcode's coding agent. + */ + +import type { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedToolWithContext } from '../../../utils/typed-tool-factory.ts'; +import { sessionStore } from '../../../utils/session-store.ts'; +import { readXcodeIdeState } from '../../../utils/xcode-state-reader.ts'; +import { lookupBundleId } from '../../../utils/xcode-state-watcher.ts'; +import * as z from 'zod'; + +const schemaObj = z.object({}); + +type Params = z.infer; + +interface SyncXcodeDefaultsContext { + executor: CommandExecutor; + cwd: string; + projectPath?: string; + workspacePath?: string; +} + +export async function syncXcodeDefaultsLogic( + _params: Params, + ctx: SyncXcodeDefaultsContext, +): Promise { + const xcodeState = await readXcodeIdeState({ + executor: ctx.executor, + cwd: ctx.cwd, + projectPath: ctx.projectPath, + workspacePath: ctx.workspacePath, + }); + + if (xcodeState.error) { + return { + content: [ + { + type: 'text', + text: `Failed to read Xcode IDE state: ${xcodeState.error}`, + }, + ], + isError: true, + }; + } + + const synced: Record = {}; + const notices: string[] = []; + + if (xcodeState.scheme) { + synced.scheme = xcodeState.scheme; + notices.push(`Scheme: ${xcodeState.scheme}`); + } + + if (xcodeState.simulatorId) { + synced.simulatorId = xcodeState.simulatorId; + notices.push(`Simulator ID: ${xcodeState.simulatorId}`); + } + + if (xcodeState.simulatorName) { + synced.simulatorName = xcodeState.simulatorName; + notices.push(`Simulator Name: ${xcodeState.simulatorName}`); + } + + // Look up bundle ID if we have a scheme + if (xcodeState.scheme) { + const bundleId = await lookupBundleId( + ctx.executor, + xcodeState.scheme, + ctx.projectPath, + ctx.workspacePath, + ); + if (bundleId) { + synced.bundleId = bundleId; + notices.push(`Bundle ID: ${bundleId}`); + } + } + + if (Object.keys(synced).length === 0) { + return { + content: [ + { + type: 'text', + text: 'No scheme or simulator selection detected in Xcode IDE state.', + }, + ], + isError: false, + }; + } + + sessionStore.setDefaults(synced); + + return { + content: [ + { + type: 'text', + text: `Synced session defaults from Xcode IDE:\n- ${notices.join('\n- ')}`, + }, + ], + isError: false, + }; +} + +export const schema = schemaObj.shape; + +export const handler = createTypedToolWithContext(schemaObj, syncXcodeDefaultsLogic, () => { + const { projectPath, workspacePath } = sessionStore.getAll(); + return { + executor: getDefaultCommandExecutor(), + cwd: process.cwd(), + projectPath, + workspacePath, + }; +}); diff --git a/src/mcp/tools/xcode-ide/xcode_ide_call_tool.ts b/src/mcp/tools/xcode-ide/xcode_ide_call_tool.ts new file mode 100644 index 00000000..f15e2694 --- /dev/null +++ b/src/mcp/tools/xcode-ide/xcode_ide_call_tool.ts @@ -0,0 +1,48 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; +import { withBridgeToolHandler } from './shared.ts'; + +const schemaObject = z.object({ + remoteTool: z.string().min(1).describe('Exact remote Xcode MCP tool name.'), + arguments: z + .record(z.string(), z.unknown()) + .optional() + .default({}) + .describe('Arguments payload to forward to the remote Xcode MCP tool.'), + timeoutMs: z + .number() + .int() + .min(100) + .max(120000) + .optional() + .describe('Optional timeout override in milliseconds for this single tool call.'), +}); + +type Params = z.infer; + +export async function xcodeIdeCallToolLogic(params: Params): Promise { + return withBridgeToolHandler((bridge) => + bridge.callToolTool({ + remoteTool: params.remoteTool, + arguments: params.arguments ?? {}, + timeoutMs: params.timeoutMs, + }), + ); +} + +export const schema = schemaObject.shape; + +export const handler = async (args: Record = {}): Promise => { + const parsed = schemaObject.safeParse(args); + if (!parsed.success) { + const details = parsed.error.issues + .map((issue) => { + const path = issue.path.length > 0 ? issue.path.join('.') : 'root'; + return `${path}: ${issue.message}`; + }) + .join('\n'); + return createErrorResponse('Parameter validation failed', details); + } + return xcodeIdeCallToolLogic(parsed.data); +}; diff --git a/src/mcp/tools/xcode-ide/xcode_ide_list_tools.ts b/src/mcp/tools/xcode-ide/xcode_ide_list_tools.ts new file mode 100644 index 00000000..152d4715 --- /dev/null +++ b/src/mcp/tools/xcode-ide/xcode_ide_list_tools.ts @@ -0,0 +1,33 @@ +import * as z from 'zod'; +import type { ToolResponse } from '../../../types/common.ts'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; +import { withBridgeToolHandler } from './shared.ts'; + +const schemaObject = z.object({ + refresh: z + .boolean() + .optional() + .describe('When true (default), refreshes from Xcode bridge before returning tool list.'), +}); + +type Params = z.infer; + +export async function xcodeIdeListToolsLogic(params: Params): Promise { + return withBridgeToolHandler(async (bridge) => bridge.listToolsTool({ refresh: params.refresh })); +} + +export const schema = schemaObject.shape; + +export const handler = async (args: Record = {}): Promise => { + const parsed = schemaObject.safeParse(args); + if (!parsed.success) { + const details = parsed.error.issues + .map((issue) => { + const path = issue.path.length > 0 ? issue.path.join('.') : 'root'; + return `${path}: ${issue.message}`; + }) + .join('\n'); + return createErrorResponse('Parameter validation failed', details); + } + return xcodeIdeListToolsLogic(parsed.data); +}; diff --git a/src/mcp/tools/xcode-ide/xcode_tools_bridge_disconnect.ts b/src/mcp/tools/xcode-ide/xcode_tools_bridge_disconnect.ts new file mode 100644 index 00000000..d81f1750 --- /dev/null +++ b/src/mcp/tools/xcode-ide/xcode_tools_bridge_disconnect.ts @@ -0,0 +1,8 @@ +import type { ToolResponse } from '../../../types/common.ts'; +import { withBridgeToolHandler } from './shared.ts'; + +export const schema = {}; + +export const handler = async (): Promise => { + return withBridgeToolHandler(async (bridge) => bridge.disconnectTool()); +}; diff --git a/src/mcp/tools/xcode-ide/xcode_tools_bridge_status.ts b/src/mcp/tools/xcode-ide/xcode_tools_bridge_status.ts new file mode 100644 index 00000000..f3dae68e --- /dev/null +++ b/src/mcp/tools/xcode-ide/xcode_tools_bridge_status.ts @@ -0,0 +1,8 @@ +import type { ToolResponse } from '../../../types/common.ts'; +import { withBridgeToolHandler } from './shared.ts'; + +export const schema = {}; + +export const handler = async (): Promise => { + return withBridgeToolHandler(async (bridge) => bridge.statusTool()); +}; diff --git a/src/mcp/tools/xcode-ide/xcode_tools_bridge_sync.ts b/src/mcp/tools/xcode-ide/xcode_tools_bridge_sync.ts new file mode 100644 index 00000000..af609325 --- /dev/null +++ b/src/mcp/tools/xcode-ide/xcode_tools_bridge_sync.ts @@ -0,0 +1,8 @@ +import type { ToolResponse } from '../../../types/common.ts'; +import { withBridgeToolHandler } from './shared.ts'; + +export const schema = {}; + +export const handler = async (): Promise => { + return withBridgeToolHandler(async (bridge) => bridge.syncTool()); +}; diff --git a/src/runtime/__tests__/bootstrap-runtime.test.ts b/src/runtime/__tests__/bootstrap-runtime.test.ts new file mode 100644 index 00000000..c845506f --- /dev/null +++ b/src/runtime/__tests__/bootstrap-runtime.test.ts @@ -0,0 +1,127 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import path from 'node:path'; +import { bootstrapRuntime, type RuntimeKind } from '../bootstrap-runtime.ts'; +import { __resetConfigStoreForTests } from '../../utils/config-store.ts'; +import { sessionStore } from '../../utils/session-store.ts'; +import { createMockFileSystemExecutor } from '../../test-utils/mock-executors.ts'; + +const cwd = '/repo'; +const configPath = path.join(cwd, '.xcodebuildmcp', 'config.yaml'); + +function createFsWithSessionDefaults() { + const yaml = [ + 'schemaVersion: 1', + 'sessionDefaults:', + ' scheme: "AppScheme"', + ' simulatorId: "SIM-UUID"', + ' simulatorName: "iPhone 16"', + '', + ].join('\n'); + + return createMockFileSystemExecutor({ + existsSync: (targetPath: string) => targetPath === configPath, + readFile: async (targetPath: string) => { + if (targetPath !== configPath) { + throw new Error(`Unexpected readFile path: ${targetPath}`); + } + return yaml; + }, + }); +} + +function createFsWithSchemeOnlySessionDefaults() { + const yaml = ['schemaVersion: 1', 'sessionDefaults:', ' scheme: "AppScheme"', ''].join('\n'); + + return createMockFileSystemExecutor({ + existsSync: (targetPath: string) => targetPath === configPath, + readFile: async (targetPath: string) => { + if (targetPath !== configPath) { + throw new Error(`Unexpected readFile path: ${targetPath}`); + } + return yaml; + }, + }); +} + +function createFsWithProfiles() { + const yaml = [ + 'schemaVersion: 1', + 'sessionDefaults:', + ' scheme: "GlobalScheme"', + 'sessionDefaultsProfiles:', + ' ios:', + ' scheme: "IOSScheme"', + ' simulatorName: "iPhone 16"', + 'activeSessionDefaultsProfile: "ios"', + '', + ].join('\n'); + + return createMockFileSystemExecutor({ + existsSync: (targetPath: string) => targetPath === configPath, + readFile: async (targetPath: string) => { + if (targetPath !== configPath) { + throw new Error(`Unexpected readFile path: ${targetPath}`); + } + return yaml; + }, + }); +} + +describe('bootstrapRuntime', () => { + beforeEach(() => { + __resetConfigStoreForTests(); + sessionStore.clear(); + }); + + it('hydrates session defaults for mcp runtime', async () => { + const result = await bootstrapRuntime({ + runtime: 'mcp', + cwd, + fs: createFsWithSessionDefaults(), + }); + + expect(result.runtime.config.sessionDefaults?.scheme).toBe('AppScheme'); + expect(sessionStore.getAll()).toMatchObject({ + scheme: 'AppScheme', + simulatorId: 'SIM-UUID', + simulatorName: 'iPhone 16', + }); + }); + + it('hydrates non-simulator session defaults for mcp runtime', async () => { + const result = await bootstrapRuntime({ + runtime: 'mcp', + cwd, + fs: createFsWithSchemeOnlySessionDefaults(), + }); + + expect(result.runtime.config.sessionDefaults?.scheme).toBe('AppScheme'); + expect(sessionStore.getAll()).toMatchObject({ + scheme: 'AppScheme', + }); + expect(sessionStore.getAll().simulatorId).toBeUndefined(); + expect(sessionStore.getAll().simulatorName).toBeUndefined(); + }); + + it.each(['cli', 'daemon'] as const)( + 'does not hydrate session defaults for %s runtime', + async (runtime: RuntimeKind) => { + const result = await bootstrapRuntime({ runtime, cwd, fs: createFsWithSessionDefaults() }); + + expect(result.runtime.config.sessionDefaults?.scheme).toBe('AppScheme'); + expect(sessionStore.getAll()).toEqual({}); + }, + ); + + it('hydrates the active session defaults profile for mcp runtime', async () => { + await bootstrapRuntime({ + runtime: 'mcp', + cwd, + fs: createFsWithProfiles(), + }); + + expect(sessionStore.getActiveProfile()).toBe('ios'); + expect(sessionStore.getAll().scheme).toBe('IOSScheme'); + expect(sessionStore.getAll().simulatorName).toBe('iPhone 16'); + }); +}); diff --git a/src/runtime/__tests__/tool-invoker.test.ts b/src/runtime/__tests__/tool-invoker.test.ts new file mode 100644 index 00000000..5aae7aae --- /dev/null +++ b/src/runtime/__tests__/tool-invoker.test.ts @@ -0,0 +1,553 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { z } from 'zod'; +import type { ToolResponse } from '../../types/common.ts'; +import type { ToolDefinition } from '../types.ts'; +import { createToolCatalog } from '../tool-catalog.ts'; +import { DefaultToolInvoker } from '../tool-invoker.ts'; +import { ensureDaemonRunning } from '../../cli/daemon-control.ts'; + +const daemonClientMock = { + isRunning: vi.fn<() => Promise>(), + invokeXcodeIdeTool: + vi.fn<(name: string, args: Record) => Promise>(), + invokeTool: vi.fn<(name: string, args: Record) => Promise>(), + listTools: vi.fn<() => Promise>>(), +}; + +vi.mock('../../cli/daemon-client.ts', () => ({ + DaemonClient: vi.fn().mockImplementation(() => daemonClientMock), +})); + +vi.mock('../../cli/daemon-control.ts', () => ({ + ensureDaemonRunning: vi.fn(), + DEFAULT_DAEMON_STARTUP_TIMEOUT_MS: 5000, +})); + +function textResponse(text: string): ToolResponse { + return { + content: [{ type: 'text', text }], + }; +} + +function makeTool(opts: { + cliName: string; + mcpName?: string; + id?: string; + nextStepTemplates?: ToolDefinition['nextStepTemplates']; + workflow: string; + stateful: boolean; + handler: ToolDefinition['handler']; + xcodeIdeRemoteToolName?: string; +}): ToolDefinition { + return { + id: opts.id, + cliName: opts.cliName, + mcpName: opts.mcpName ?? opts.cliName.replace(/-/g, '_'), + nextStepTemplates: opts.nextStepTemplates, + workflow: opts.workflow, + description: `${opts.cliName} tool`, + mcpSchema: { value: z.string().optional() }, + cliSchema: { value: z.string().optional() }, + stateful: opts.stateful, + xcodeIdeRemoteToolName: opts.xcodeIdeRemoteToolName, + handler: opts.handler, + }; +} + +describe('DefaultToolInvoker CLI routing', () => { + beforeEach(() => { + vi.clearAllMocks(); + daemonClientMock.isRunning.mockResolvedValue(true); + daemonClientMock.invokeXcodeIdeTool.mockResolvedValue(textResponse('daemon-xcode-ide-result')); + daemonClientMock.invokeTool.mockResolvedValue(textResponse('daemon-result')); + daemonClientMock.listTools.mockResolvedValue([]); + }); + + it('uses direct invocation for stateless tools', async () => { + const directHandler = vi.fn().mockResolvedValue(textResponse('direct-result')); + const catalog = createToolCatalog([ + makeTool({ + cliName: 'list-sims', + workflow: 'simulator', + stateful: false, + handler: directHandler, + }), + ]); + const invoker = new DefaultToolInvoker(catalog); + + const response = await invoker.invoke( + 'list-sims', + { value: 'hello' }, + { + runtime: 'cli', + socketPath: '/tmp/xcodebuildmcp.sock', + }, + ); + + expect(directHandler).toHaveBeenCalledWith({ value: 'hello' }); + expect(daemonClientMock.isRunning).not.toHaveBeenCalled(); + expect(daemonClientMock.invokeTool).not.toHaveBeenCalled(); + expect(response.content[0].text).toBe('direct-result'); + }); + + it('routes stateful tools through daemon and auto-starts when needed', async () => { + daemonClientMock.isRunning.mockResolvedValue(false); + const directHandler = vi.fn().mockResolvedValue(textResponse('direct-result')); + const catalog = createToolCatalog([ + makeTool({ + cliName: 'start-sim-log-cap', + workflow: 'logging', + stateful: true, + handler: directHandler, + }), + ]); + const invoker = new DefaultToolInvoker(catalog); + + const response = await invoker.invoke( + 'start-sim-log-cap', + { value: 'hello' }, + { + runtime: 'cli', + socketPath: '/tmp/xcodebuildmcp.sock', + workspaceRoot: '/repo', + }, + ); + + expect(ensureDaemonRunning).toHaveBeenCalledWith( + expect.objectContaining({ + socketPath: '/tmp/xcodebuildmcp.sock', + workspaceRoot: '/repo', + env: undefined, + }), + ); + expect(daemonClientMock.invokeTool).toHaveBeenCalledWith('start_sim_log_cap', { + value: 'hello', + }); + expect(directHandler).not.toHaveBeenCalled(); + expect(response.content[0].text).toBe('daemon-result'); + }); +}); + +describe('DefaultToolInvoker xcode-ide dynamic routing', () => { + beforeEach(() => { + vi.clearAllMocks(); + daemonClientMock.isRunning.mockResolvedValue(true); + daemonClientMock.invokeXcodeIdeTool.mockResolvedValue(textResponse('daemon-result')); + daemonClientMock.invokeTool.mockResolvedValue(textResponse('daemon-generic')); + daemonClientMock.listTools.mockResolvedValue([]); + }); + + it('routes dynamic xcode-ide tools through daemon xcode-ide invoke API', async () => { + daemonClientMock.isRunning.mockResolvedValue(false); + const directHandler = vi.fn().mockResolvedValue(textResponse('direct-result')); + const catalog = createToolCatalog([ + makeTool({ + cliName: 'xcode-ide-alpha', + workflow: 'xcode-ide', + stateful: false, + xcodeIdeRemoteToolName: 'Alpha', + handler: directHandler, + }), + ]); + const invoker = new DefaultToolInvoker(catalog); + + const response = await invoker.invoke( + 'xcode-ide-alpha', + { value: 'hello' }, + { + runtime: 'cli', + socketPath: '/tmp/xcodebuildmcp.sock', + workspaceRoot: '/repo', + cliExposedWorkflowIds: ['simulator', 'xcode-ide'], + }, + ); + + expect(ensureDaemonRunning).toHaveBeenCalledWith( + expect.objectContaining({ + socketPath: '/tmp/xcodebuildmcp.sock', + workspaceRoot: '/repo', + env: undefined, + }), + ); + expect(daemonClientMock.invokeXcodeIdeTool).toHaveBeenCalledWith('Alpha', { value: 'hello' }); + expect(directHandler).not.toHaveBeenCalled(); + expect(response.content[0].text).toBe('daemon-result'); + }); + + it('fails for dynamic xcode-ide tools when socket path is missing', async () => { + const directHandler = vi.fn().mockResolvedValue(textResponse('direct-result')); + const catalog = createToolCatalog([ + makeTool({ + cliName: 'xcode-ide-alpha', + workflow: 'xcode-ide', + stateful: false, + xcodeIdeRemoteToolName: 'Alpha', + handler: directHandler, + }), + ]); + const invoker = new DefaultToolInvoker(catalog); + + const response = await invoker.invoke( + 'xcode-ide-alpha', + { value: 'hello' }, + { + runtime: 'cli', + }, + ); + + expect(response.isError).toBe(true); + expect(response.content[0].text).toContain('No socket path configured'); + expect(directHandler).not.toHaveBeenCalled(); + expect(daemonClientMock.invokeXcodeIdeTool).not.toHaveBeenCalled(); + }); +}); + +describe('DefaultToolInvoker next steps post-processing', () => { + beforeEach(() => { + vi.clearAllMocks(); + daemonClientMock.isRunning.mockResolvedValue(true); + }); + + it('enriches canonical next-step tool names in CLI runtime', async () => { + const directHandler = vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'ok' }], + nextSteps: [ + { + tool: 'screenshot', + label: 'Take screenshot', + params: { simulatorId: '123' }, + }, + ], + } satisfies ToolResponse); + + const catalog = createToolCatalog([ + makeTool({ + cliName: 'snapshot-ui', + mcpName: 'snapshot_ui', + workflow: 'ui-automation', + stateful: false, + handler: directHandler, + }), + makeTool({ + id: 'screenshot', + cliName: 'screenshot', + mcpName: 'screenshot', + workflow: 'ui-automation', + stateful: false, + handler: vi.fn().mockResolvedValue(textResponse('screenshot')), + }), + ]); + + const invoker = new DefaultToolInvoker(catalog); + const response = await invoker.invoke('snapshot-ui', {}, { runtime: 'cli' }); + + expect(response.nextSteps).toEqual([ + { + tool: 'screenshot', + label: 'Take screenshot', + params: { simulatorId: '123' }, + workflow: 'ui-automation', + cliTool: 'screenshot', + }, + ]); + }); + + it('injects manifest template next steps from dynamic nextStepParams when response omits nextSteps', async () => { + const directHandler = vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'ok' }], + nextStepParams: { + snapshot_ui: { simulatorId: '12345678-1234-4234-8234-123456789012' }, + tap: { simulatorId: '12345678-1234-4234-8234-123456789012', x: 0, y: 0 }, + }, + } satisfies ToolResponse); + const catalog = createToolCatalog([ + makeTool({ + id: 'snapshot_ui', + cliName: 'snapshot-ui', + mcpName: 'snapshot_ui', + workflow: 'ui-automation', + stateful: false, + nextStepTemplates: [ + { + label: 'Refresh', + toolId: 'snapshot_ui', + params: { simulatorId: 'SIMULATOR_UUID' }, + }, + { + label: 'Visually verify hierarchy output', + }, + { + label: 'Tap on element', + toolId: 'tap', + params: { simulatorId: 'SIMULATOR_UUID', x: 0, y: 0 }, + }, + ], + handler: directHandler, + }), + makeTool({ + id: 'tap', + cliName: 'tap', + mcpName: 'tap', + workflow: 'ui-automation', + stateful: false, + handler: vi.fn().mockResolvedValue(textResponse('tap')), + }), + ]); + + const invoker = new DefaultToolInvoker(catalog); + const response = await invoker.invoke('snapshot-ui', {}, { runtime: 'cli' }); + + expect(response.nextSteps).toEqual([ + { + tool: 'snapshot_ui', + label: 'Refresh', + params: { simulatorId: '12345678-1234-4234-8234-123456789012' }, + workflow: 'ui-automation', + cliTool: 'snapshot-ui', + }, + { + label: 'Visually verify hierarchy output', + }, + { + tool: 'tap', + label: 'Tap on element', + params: { simulatorId: '12345678-1234-4234-8234-123456789012', x: 0, y: 0 }, + workflow: 'ui-automation', + cliTool: 'tap', + }, + ]); + }); + + it('prefers manifest templates over tool-provided next-step labels and tools', async () => { + const directHandler = vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'ok' }], + nextSteps: [ + { + tool: 'legacy_stop_sim_log_cap', + label: 'Old label', + params: { logSessionId: 'session-123' }, + priority: 99, + }, + ], + nextStepParams: { + stop_sim_log_cap: { logSessionId: 'session-123' }, + }, + } satisfies ToolResponse); + + const catalog = createToolCatalog([ + makeTool({ + id: 'start_sim_log_cap', + cliName: 'start-simulator-log-capture', + mcpName: 'start_sim_log_cap', + workflow: 'logging', + stateful: false, + nextStepTemplates: [ + { + label: 'Stop capture and retrieve logs', + toolId: 'stop_sim_log_cap', + priority: 1, + }, + ], + handler: directHandler, + }), + makeTool({ + id: 'stop_sim_log_cap', + cliName: 'stop-simulator-log-capture', + mcpName: 'stop_sim_log_cap', + workflow: 'logging', + stateful: true, + handler: vi.fn().mockResolvedValue(textResponse('stop')), + }), + ]); + + const invoker = new DefaultToolInvoker(catalog); + const response = await invoker.invoke('start-simulator-log-capture', {}, { runtime: 'cli' }); + + expect(response.nextSteps).toEqual([ + { + tool: 'stop_sim_log_cap', + label: 'Stop capture and retrieve logs', + params: { logSessionId: 'session-123' }, + priority: 1, + workflow: 'logging', + cliTool: 'stop-simulator-log-capture', + }, + ]); + }); + + it('overrides unresolved template placeholders with dynamic next-step params', async () => { + const directHandler = vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'ok' }], + nextStepParams: { + boot_sim: { simulatorId: 'ABC-123' }, + }, + } satisfies ToolResponse); + + const catalog = createToolCatalog([ + makeTool({ + id: 'launch_app_sim', + cliName: 'launch-app-sim', + mcpName: 'launch_app_sim', + workflow: 'simulator', + stateful: false, + nextStepTemplates: [ + { + label: 'Boot simulator', + toolId: 'boot_sim', + params: { simulatorId: '${simulatorId}' }, + }, + ], + handler: directHandler, + }), + makeTool({ + id: 'boot_sim', + cliName: 'boot-sim', + mcpName: 'boot_sim', + workflow: 'simulator', + stateful: false, + handler: vi.fn().mockResolvedValue(textResponse('boot')), + }), + ]); + + const invoker = new DefaultToolInvoker(catalog); + const response = await invoker.invoke('launch-app-sim', {}, { runtime: 'cli' }); + + expect(response.nextSteps).toEqual([ + { + tool: 'boot_sim', + label: 'Boot simulator', + params: { simulatorId: 'ABC-123' }, + workflow: 'simulator', + cliTool: 'boot-sim', + }, + ]); + }); + + it('maps dynamic params to the correct template tool after catalog filtering', async () => { + const directHandler = vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'ok' }], + nextStepParams: { + stop_sim_log_cap: { logSessionId: 'session-123' }, + }, + } satisfies ToolResponse); + + const catalog = createToolCatalog([ + makeTool({ + id: 'start_sim_log_cap', + cliName: 'start-simulator-log-capture', + mcpName: 'start_sim_log_cap', + workflow: 'logging', + stateful: false, + nextStepTemplates: [ + { + label: 'Unavailable step', + toolId: 'missing_tool', + }, + { + label: 'Stop capture and retrieve logs', + toolId: 'stop_sim_log_cap', + priority: 1, + }, + ], + handler: directHandler, + }), + makeTool({ + id: 'stop_sim_log_cap', + cliName: 'stop-simulator-log-capture', + mcpName: 'stop_sim_log_cap', + workflow: 'logging', + stateful: true, + handler: vi.fn().mockResolvedValue(textResponse('stop')), + }), + ]); + + const invoker = new DefaultToolInvoker(catalog); + const response = await invoker.invoke('start-simulator-log-capture', {}, { runtime: 'cli' }); + + expect(response.nextSteps).toEqual([ + { + tool: 'stop_sim_log_cap', + label: 'Stop capture and retrieve logs', + params: { logSessionId: 'session-123' }, + priority: 1, + workflow: 'logging', + cliTool: 'stop-simulator-log-capture', + }, + ]); + }); + + it('always uses manifest templates when they exist', async () => { + const directHandler = vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'ok' }], + nextSteps: [ + { + tool: 'launch_app_sim', + label: 'Launch app (platform-specific)', + params: { simulatorId: '123', bundleId: 'com.example.app' }, + priority: 1, + }, + ], + } satisfies ToolResponse); + + const catalog = createToolCatalog([ + makeTool({ + id: 'get_sim_app_path', + cliName: 'get-app-path', + mcpName: 'get_sim_app_path', + workflow: 'simulator', + stateful: false, + nextStepTemplates: [ + { label: 'Get bundle ID', toolId: 'get_app_bundle_id', priority: 1 }, + { label: 'Boot simulator', toolId: 'boot_sim', priority: 2 }, + ], + handler: directHandler, + }), + makeTool({ + id: 'launch_app_sim', + cliName: 'launch-app', + mcpName: 'launch_app_sim', + workflow: 'simulator', + stateful: false, + handler: vi.fn().mockResolvedValue(textResponse('launch')), + }), + makeTool({ + id: 'get_app_bundle_id', + cliName: 'get-app-bundle-id', + mcpName: 'get_app_bundle_id', + workflow: 'project-discovery', + stateful: false, + handler: vi.fn().mockResolvedValue(textResponse('bundle')), + }), + makeTool({ + id: 'boot_sim', + cliName: 'boot', + mcpName: 'boot_sim', + workflow: 'simulator', + stateful: false, + handler: vi.fn().mockResolvedValue(textResponse('boot')), + }), + ]); + + const invoker = new DefaultToolInvoker(catalog); + const response = await invoker.invoke('get-app-path', {}, { runtime: 'cli' }); + + expect(response.nextSteps).toEqual([ + { + tool: 'get_app_bundle_id', + label: 'Get bundle ID', + params: {}, + priority: 1, + workflow: 'project-discovery', + cliTool: 'get-app-bundle-id', + }, + { + tool: 'boot_sim', + label: 'Boot simulator', + params: {}, + priority: 2, + workflow: 'simulator', + cliTool: 'boot', + }, + ]); + }); +}); diff --git a/src/runtime/bootstrap-runtime.ts b/src/runtime/bootstrap-runtime.ts new file mode 100644 index 00000000..5ab888c8 --- /dev/null +++ b/src/runtime/bootstrap-runtime.ts @@ -0,0 +1,138 @@ +import process from 'node:process'; +import { + initConfigStore, + getConfig, + type RuntimeConfigOverrides, + type ResolvedRuntimeConfig, +} from '../utils/config-store.ts'; +import { sessionStore, type SessionDefaults } from '../utils/session-store.ts'; +import { getDefaultFileSystemExecutor } from '../utils/command.ts'; +import { log } from '../utils/logger.ts'; +import type { FileSystemExecutor } from '../utils/FileSystemExecutor.ts'; +import { scheduleSimulatorDefaultsRefresh } from '../utils/simulator-defaults-refresh.ts'; + +export type RuntimeKind = 'cli' | 'daemon' | 'mcp'; + +export interface BootstrapRuntimeOptions { + runtime: RuntimeKind; + cwd?: string; + fs?: FileSystemExecutor; + configOverrides?: RuntimeConfigOverrides; +} + +export interface BootstrappedRuntime { + runtime: RuntimeKind; + cwd: string; + config: ResolvedRuntimeConfig; +} + +export interface BootstrapRuntimeResult { + runtime: BootstrappedRuntime; + configFound: boolean; + configPath?: string; + notices: string[]; +} + +interface MCPSessionHydrationResult { + hydrated: boolean; + refreshScheduled: boolean; +} + +/** + * Hydrates MCP session defaults and reports whether a background simulator refresh was scheduled. + */ +function hydrateSessionDefaultsForMcp( + defaults: Partial | undefined, + profiles: Record> | undefined, + activeProfile: string | undefined, +): MCPSessionHydrationResult { + const hydratedDefaults = { ...(defaults ?? {}) }; + const hydratedProfiles = profiles ?? {}; + const hasHydratedDefaults = Object.keys(hydratedDefaults).length > 0; + const hydratedProfileEntries = Object.entries(hydratedProfiles); + if (!hasHydratedDefaults && hydratedProfileEntries.length === 0) { + return { hydrated: false, refreshScheduled: false }; + } + + if (hasHydratedDefaults) { + sessionStore.setDefaultsForProfile(null, hydratedDefaults); + } + for (const [profileName, profileDefaults] of hydratedProfileEntries) { + const trimmedName = profileName.trim(); + if (!trimmedName) continue; + sessionStore.setDefaultsForProfile(trimmedName, profileDefaults); + } + const normalizedActiveProfile = activeProfile?.trim(); + if (normalizedActiveProfile) { + sessionStore.setActiveProfile(normalizedActiveProfile); + } + + const activeDefaults = sessionStore.getAll(); + const revision = sessionStore.getRevision(); + const refreshScheduled = scheduleSimulatorDefaultsRefresh({ + expectedRevision: revision, + reason: 'startup-hydration', + profile: sessionStore.getActiveProfile(), + persist: true, + simulatorId: activeDefaults.simulatorId, + simulatorName: activeDefaults.simulatorName, + recomputePlatform: true, + }); + + return { hydrated: true, refreshScheduled }; +} + +function logHydrationResult(hydration: MCPSessionHydrationResult): void { + if (!hydration.hydrated) { + return; + } + + if (hydration.refreshScheduled) { + log('info', '[Session] Hydrated MCP session defaults; simulator metadata refresh scheduled.'); + return; + } + + log('info', '[Session] Hydrated MCP session defaults; simulator metadata refresh not scheduled.'); +} + +export async function bootstrapRuntime( + opts: BootstrapRuntimeOptions, +): Promise { + process.env.XCODEBUILDMCP_RUNTIME = opts.runtime; + const cwd = opts.cwd ?? process.cwd(); + const fs = opts.fs ?? getDefaultFileSystemExecutor(); + + const configResult = await initConfigStore({ + cwd, + fs, + overrides: opts.configOverrides, + }); + + if (configResult.found) { + log('info', `Loaded project config from ${configResult.path} (cwd: ${cwd})`); + } else { + log('info', `No project config found (cwd: ${cwd}).`); + } + + const config = getConfig(); + + if (opts.runtime === 'mcp') { + const hydration = hydrateSessionDefaultsForMcp( + config.sessionDefaults, + config.sessionDefaultsProfiles, + config.activeSessionDefaultsProfile, + ); + logHydrationResult(hydration); + } + + return { + runtime: { + runtime: opts.runtime, + cwd, + config, + }, + configFound: configResult.found, + configPath: configResult.path, + notices: configResult.notices, + }; +} diff --git a/src/runtime/naming.ts b/src/runtime/naming.ts new file mode 100644 index 00000000..415381e2 --- /dev/null +++ b/src/runtime/naming.ts @@ -0,0 +1,50 @@ +/** + * Convert a tool name to kebab-case for CLI usage. + * Examples: + * build_sim -> build-sim + * startSimLogCap -> start-sim-log-cap + * BuildSimulator -> build-simulator + */ +export function toKebabCase(name: string): string { + return ( + name + .trim() + // Replace underscores with hyphens + .replace(/_/g, '-') + // Insert hyphen before uppercase letters (for camelCase/PascalCase) + .replace(/([a-z])([A-Z])/g, '$1-$2') + // Replace spaces with hyphens + .replace(/\s+/g, '-') + // Convert to lowercase + .toLowerCase() + // Remove any duplicate hyphens + .replace(/-+/g, '-') + // Trim leading/trailing hyphens + .replace(/^-|-$/g, '') + ); +} + +/** + * Convert kebab-case CLI flag back to camelCase for tool params. + * Examples: + * project-path -> projectPath + * simulator-name -> simulatorName + */ +export function toCamelCase(kebab: string): string { + return kebab.replace(/-([a-z])/g, (_match: string, letter: string) => letter.toUpperCase()); +} + +/** + * Convert CLI argv keys (kebab-case) back to tool param keys (camelCase). + */ +export function convertArgvToToolParams(argv: Record): Record { + const result: Record = {}; + for (const [key, value] of Object.entries(argv)) { + // Skip yargs internal keys + if (key === '_' || key === '$0') continue; + // Convert kebab-case to camelCase + const camelKey = toCamelCase(key); + result[camelKey] = value; + } + return result; +} diff --git a/src/runtime/tool-catalog.ts b/src/runtime/tool-catalog.ts new file mode 100644 index 00000000..834708d3 --- /dev/null +++ b/src/runtime/tool-catalog.ts @@ -0,0 +1,254 @@ +import type { ToolCatalog, ToolDefinition, ToolResolution } from './types.ts'; +import { toKebabCase } from './naming.ts'; +import { loadManifest, type WorkflowManifestEntry } from '../core/manifest/load-manifest.ts'; +import { getEffectiveCliName } from '../core/manifest/schema.ts'; +import { importToolModule } from '../core/manifest/import-tool-module.ts'; +import type { PredicateContext, RuntimeKind } from '../visibility/predicate-types.ts'; +import { + isWorkflowAvailableForRuntime, + isToolAvailableForRuntime, + isWorkflowEnabledForRuntime, + isToolExposedForRuntime, +} from '../visibility/exposure.ts'; +import { getConfig } from '../utils/config-store.ts'; +import { log } from '../utils/logging/index.ts'; + +export function createToolCatalog(tools: ToolDefinition[]): ToolCatalog { + // Build lookup maps for fast resolution, deduplicating by mcpName so that + // tools shared across multiple workflows don't cause ambiguous resolution. + const byCliName = new Map(); + const byMcpName = new Map(); + const byToolId = new Map(); + const byMcpKebab = new Map(); + const seenMcpNames = new Set(); + + for (const tool of tools) { + const mcpKey = tool.mcpName.toLowerCase(); + if (seenMcpNames.has(mcpKey)) continue; + seenMcpNames.add(mcpKey); + + byCliName.set(tool.cliName, tool); + byMcpName.set(mcpKey, tool); + if (tool.id) { + byToolId.set(tool.id, tool); + } + + const mcpKebab = toKebabCase(tool.mcpName); + let kebabGroup = byMcpKebab.get(mcpKebab); + if (!kebabGroup) { + kebabGroup = []; + byMcpKebab.set(mcpKebab, kebabGroup); + } + kebabGroup.push(tool); + } + + return { + tools, + + getByCliName(name: string): ToolDefinition | null { + return byCliName.get(name) ?? null; + }, + + getByMcpName(name: string): ToolDefinition | null { + return byMcpName.get(name.toLowerCase().trim()) ?? null; + }, + + getByToolId(toolId: string): ToolDefinition | null { + return byToolId.get(toolId) ?? null; + }, + + resolve(input: string): ToolResolution { + const normalized = input.toLowerCase().trim(); + + // Try exact CLI name match first + const exact = byCliName.get(normalized); + if (exact) { + return { tool: exact }; + } + + // Try kebab-case of MCP name (alias) + const mcpKebab = toKebabCase(normalized); + const aliasMatches = byMcpKebab.get(mcpKebab); + if (aliasMatches && aliasMatches.length === 1) { + return { tool: aliasMatches[0] }; + } + if (aliasMatches && aliasMatches.length > 1) { + return { ambiguous: aliasMatches.map((t) => t.cliName) }; + } + + // Try matching by MCP name directly (for underscore-style names) + const byMcpDirect = tools.find((t) => t.mcpName.toLowerCase() === normalized); + if (byMcpDirect) { + return { tool: byMcpDirect }; + } + + return { notFound: true }; + }, + }; +} + +/** + * Get a list of all available tool names for display. + */ +export function listToolNames(catalog: ToolCatalog): string[] { + return catalog.tools.map((t) => t.cliName).sort(); +} + +/** + * Get tools grouped by workflow for display. + */ +export function groupToolsByWorkflow(catalog: ToolCatalog): Map { + const groups = new Map(); + + for (const tool of catalog.tools) { + let group = groups.get(tool.workflow); + if (!group) { + group = []; + groups.set(tool.workflow, group); + } + group.push(tool); + } + + return groups; +} + +/** + * Build a tool catalog from the YAML manifest system. + */ +export async function buildToolCatalogFromManifest(opts: { + runtime: RuntimeKind; + ctx: PredicateContext; + enabledWorkflows?: string[]; + excludeWorkflows?: string[]; +}): Promise { + const manifest = loadManifest(); + const excludeSet = new Set(opts.excludeWorkflows?.map((w) => w.toLowerCase()) ?? []); + + // Get workflows to include + let workflowsToInclude: WorkflowManifestEntry[]; + if (opts.enabledWorkflows && opts.enabledWorkflows.length > 0) { + // Use specified workflows + workflowsToInclude = opts.enabledWorkflows + .map((id) => manifest.workflows.get(id)) + .filter((wf): wf is WorkflowManifestEntry => wf !== undefined); + } else { + // Use all workflows available for the runtime + workflowsToInclude = Array.from(manifest.workflows.values()); + } + + const filteredWorkflows = workflowsToInclude.filter( + (wf) => + !excludeSet.has(wf.id.toLowerCase()) && + isWorkflowAvailableForRuntime(wf, opts.runtime) && + isWorkflowEnabledForRuntime(wf, opts.ctx), + ); + + // Cache imported modules to avoid re-importing the same tool + const moduleCache = new Map>>(); + const tools: ToolDefinition[] = []; + + for (const workflow of filteredWorkflows) { + for (const toolId of workflow.tools) { + const toolManifest = manifest.tools.get(toolId); + if (!toolManifest) continue; + + // Check tool availability for runtime + if (!isToolAvailableForRuntime(toolManifest, opts.runtime)) continue; + + // Check tool predicates + if (!isToolExposedForRuntime(toolManifest, opts.ctx)) continue; + + // Import the tool module (cached) + let toolModule = moduleCache.get(toolId); + if (!toolModule) { + try { + toolModule = await importToolModule(toolManifest.module); + moduleCache.set(toolId, toolModule); + } catch (err) { + log('warning', `Failed to import tool module ${toolManifest.module}: ${err}`); + continue; + } + } + + const cliName = getEffectiveCliName(toolManifest); + tools.push({ + id: toolManifest.id, + cliName, + mcpName: toolManifest.names.mcp, + workflow: workflow.id, + description: toolManifest.description, + annotations: toolManifest.annotations, + nextStepTemplates: toolManifest.nextSteps, + mcpSchema: toolModule.schema, + cliSchema: toolModule.schema, + stateful: toolManifest.routing?.stateful ?? false, + handler: toolModule.handler as ToolDefinition['handler'], + }); + } + } + + return createToolCatalog(tools); +} + +/** + * Build a CLI tool catalog from the manifest system. + * CLI visibility is determined by manifest availability and predicates. + */ +export async function buildCliToolCatalogFromManifest(opts?: { + excludeWorkflows?: string[]; +}): Promise { + const ctx = await buildCliPredicateContext(); + return buildToolCatalogFromManifest({ + runtime: 'cli', + ctx, + excludeWorkflows: opts?.excludeWorkflows, + }); +} + +export async function listCliWorkflowIdsFromManifest(opts?: { + excludeWorkflows?: string[]; +}): Promise { + const manifest = loadManifest(); + const excludeSet = new Set(opts?.excludeWorkflows?.map((name) => name.toLowerCase()) ?? []); + const ctx = await buildCliPredicateContext(); + + return Array.from(manifest.workflows.values()) + .filter((workflow) => !excludeSet.has(workflow.id.toLowerCase())) + .filter((workflow) => isWorkflowEnabledForRuntime(workflow, ctx)) + .map((workflow) => workflow.id) + .sort((a, b) => a.localeCompare(b)); +} + +/** + * Build a daemon tool catalog from the manifest system. + * Daemon visibility is determined by manifest availability and predicates. + */ +export async function buildDaemonToolCatalogFromManifest(opts?: { + excludeWorkflows?: string[]; +}): Promise { + const excludeWorkflows = opts?.excludeWorkflows ?? []; + + // Daemon context: not running under Xcode, no Xcode tools active + const ctx: PredicateContext = { + runtime: 'daemon', + config: getConfig(), + runningUnderXcode: false, + }; + + return buildToolCatalogFromManifest({ + runtime: 'daemon', + ctx, + excludeWorkflows, + }); +} + +async function buildCliPredicateContext(): Promise { + // Skip bridge availability check in CLI mode — xcode-ide workflow has + // availability.cli: false so the bridge result is unused, and the + // xcrun --find mcpbridge call triggers an unwanted Xcode auth prompt. + return { + runtime: 'cli', + config: getConfig(), + runningUnderXcode: false, + }; +} diff --git a/src/runtime/tool-invoker.ts b/src/runtime/tool-invoker.ts new file mode 100644 index 00000000..a0023b6a --- /dev/null +++ b/src/runtime/tool-invoker.ts @@ -0,0 +1,400 @@ +import type { ToolCatalog, ToolDefinition, ToolInvoker, InvokeOptions } from './types.ts'; +import type { NextStep, NextStepParams, NextStepParamsMap, ToolResponse } from '../types/common.ts'; +import { createErrorResponse } from '../utils/responses/index.ts'; +import { DaemonClient } from '../cli/daemon-client.ts'; +import { ensureDaemonRunning, DEFAULT_DAEMON_STARTUP_TIMEOUT_MS } from '../cli/daemon-control.ts'; +import { log } from '../utils/logger.ts'; +import { + recordInternalErrorMetric, + recordToolInvocationMetric, + type SentryToolInvocationOutcome, + type SentryToolRuntime, + type SentryToolTransport, +} from '../utils/sentry.ts'; + +type BuiltTemplateNextStep = { + step: NextStep; + templateToolId?: string; +}; + +function buildTemplateNextSteps( + tool: ToolDefinition, + catalog: ToolCatalog, +): BuiltTemplateNextStep[] { + if (!tool.nextStepTemplates || tool.nextStepTemplates.length === 0) { + return []; + } + + const built: BuiltTemplateNextStep[] = []; + for (const template of tool.nextStepTemplates) { + if (!template.toolId) { + built.push({ + step: { + label: template.label, + priority: template.priority, + }, + }); + continue; + } + + const target = catalog.getByToolId(template.toolId); + if (!target) { + continue; + } + + built.push({ + step: { + tool: target.mcpName, + label: template.label, + params: template.params ?? {}, + priority: template.priority, + }, + templateToolId: template.toolId, + }); + } + + return built; +} + +function consumeDynamicParams( + nextStepParams: NextStepParamsMap | undefined, + toolId: string, + consumedCounts: Map, +): NextStepParams | undefined { + const candidate = nextStepParams?.[toolId]; + if (!candidate) { + return undefined; + } + + if (Array.isArray(candidate)) { + const current = consumedCounts.get(toolId) ?? 0; + consumedCounts.set(toolId, current + 1); + return candidate[current]; + } + + return candidate; +} + +function mergeTemplateAndResponseNextSteps( + templateSteps: BuiltTemplateNextStep[], + responseParamsMap: NextStepParamsMap | undefined, +): NextStep[] { + const consumedCounts = new Map(); + + return templateSteps.map((builtTemplateStep) => { + const templateStep = builtTemplateStep.step; + if (!builtTemplateStep.templateToolId || !templateStep.tool) { + return templateStep; + } + + const paramsFromMap = consumeDynamicParams( + responseParamsMap, + builtTemplateStep.templateToolId, + consumedCounts, + ); + if (!paramsFromMap) { + return templateStep; + } + + return { + ...templateStep, + params: { + ...(templateStep.params ?? {}), + ...paramsFromMap, + }, + }; + }); +} + +function normalizeNextSteps( + response: ToolResponse, + catalog: ToolCatalog, + runtime: InvokeOptions['runtime'], +): ToolResponse { + if (!response.nextSteps || response.nextSteps.length === 0) { + return response; + } + + return { + ...response, + nextSteps: response.nextSteps.map((step) => { + if (!step.tool) { + return step; + } + + const target = catalog.getByMcpName(step.tool); + if (!target) { + return step; + } + + return runtime === 'cli' + ? { + ...step, + tool: target.mcpName, + workflow: target.workflow, + cliTool: target.cliName, + } + : { + ...step, + tool: target.mcpName, + }; + }), + }; +} + +export function postProcessToolResponse(params: { + tool: ToolDefinition; + response: ToolResponse; + catalog: ToolCatalog; + runtime: InvokeOptions['runtime']; +}): ToolResponse { + const { tool, response, catalog, runtime } = params; + + const templateSteps = buildTemplateNextSteps(tool, catalog); + + const withTemplates = + templateSteps.length > 0 + ? { + ...response, + nextSteps: mergeTemplateAndResponseNextSteps(templateSteps, response.nextStepParams), + } + : response; + + const result = normalizeNextSteps(withTemplates, catalog, runtime); + delete result.nextStepParams; + return result; +} + +function buildDaemonEnvOverrides(opts: InvokeOptions): Record | undefined { + const envOverrides: Record = {}; + + if (opts.logLevel) { + envOverrides.XCODEBUILDMCP_DAEMON_LOG_LEVEL = opts.logLevel; + } + + return Object.keys(envOverrides).length > 0 ? envOverrides : undefined; +} + +function getErrorKind(error: unknown): string { + if (error instanceof Error) { + return error.name || 'Error'; + } + return typeof error; +} + +function mapRuntimeToSentryToolRuntime(runtime: InvokeOptions['runtime']): SentryToolRuntime { + switch (runtime) { + case 'daemon': + case 'mcp': + return runtime; + default: + return 'cli'; + } +} + +export class DefaultToolInvoker implements ToolInvoker { + constructor(private catalog: ToolCatalog) {} + + async invoke( + toolName: string, + args: Record, + opts: InvokeOptions, + ): Promise { + const resolved = this.catalog.resolve(toolName); + + if (resolved.ambiguous) { + return createErrorResponse( + 'Ambiguous tool name', + `Multiple tools match '${toolName}'. Use one of:\n- ${resolved.ambiguous.join('\n- ')}`, + ); + } + + if (resolved.notFound || !resolved.tool) { + return createErrorResponse( + 'Tool not found', + `Unknown tool '${toolName}'. Run 'xcodebuildmcp tools' to see available tools.`, + ); + } + + return this.executeTool(resolved.tool, args, opts); + } + + /** + * Invoke a tool directly, bypassing catalog resolution. + * Used by CLI where the correct ToolDefinition is already known + * from workflow-scoped yargs routing. + */ + async invokeDirect( + tool: ToolDefinition, + args: Record, + opts: InvokeOptions, + ): Promise { + return this.executeTool(tool, args, opts); + } + + private buildPostProcessParams( + tool: ToolDefinition, + runtime: InvokeOptions['runtime'], + ): { + tool: ToolDefinition; + catalog: ToolCatalog; + runtime: InvokeOptions['runtime']; + } { + return { tool, catalog: this.catalog, runtime }; + } + + private async invokeViaDaemon( + opts: InvokeOptions, + invoke: (client: DaemonClient) => Promise, + context: { + label: string; + errorTitle: string; + captureInfraErrorMetric: (error: unknown) => void; + captureInvocationMetric: (outcome: SentryToolInvocationOutcome) => void; + postProcessParams: { + tool: ToolDefinition; + catalog: ToolCatalog; + runtime: InvokeOptions['runtime']; + }; + }, + ): Promise { + const socketPath = opts.socketPath; + if (!socketPath) { + const error = new Error('SocketPathMissing'); + context.captureInfraErrorMetric(error); + context.captureInvocationMetric('infra_error'); + return createErrorResponse( + 'Socket path required', + 'No socket path configured for daemon communication.', + ); + } + + const client = new DaemonClient({ socketPath }); + const isRunning = await client.isRunning(); + + if (!isRunning) { + try { + await ensureDaemonRunning({ + socketPath, + workspaceRoot: opts.workspaceRoot, + startupTimeoutMs: opts.daemonStartupTimeoutMs ?? DEFAULT_DAEMON_STARTUP_TIMEOUT_MS, + env: buildDaemonEnvOverrides(opts), + }); + } catch (error) { + log( + 'error', + `[infra/tool-invoker] ${context.label} daemon auto-start failed (${getErrorKind(error)})`, + { sentry: true }, + ); + context.captureInfraErrorMetric(error); + context.captureInvocationMetric('infra_error'); + return createErrorResponse( + 'Daemon auto-start failed', + (error instanceof Error ? error.message : String(error)) + + '\n\nYou can try starting the daemon manually:\n' + + ' xcodebuildmcp daemon start', + ); + } + } + + try { + const response = await invoke(client); + context.captureInvocationMetric('completed'); + return postProcessToolResponse({ + ...context.postProcessParams, + response, + }); + } catch (error) { + log( + 'error', + `[infra/tool-invoker] ${context.label} transport failed (${getErrorKind(error)})`, + { sentry: true }, + ); + context.captureInfraErrorMetric(error); + context.captureInvocationMetric('infra_error'); + return createErrorResponse( + context.errorTitle, + error instanceof Error ? error.message : String(error), + ); + } + } + + private async executeTool( + tool: ToolDefinition, + args: Record, + opts: InvokeOptions, + ): Promise { + const startedAt = Date.now(); + const runtime = mapRuntimeToSentryToolRuntime(opts.runtime); + let transport: SentryToolTransport = 'direct'; + + const captureInvocationMetric = (outcome: SentryToolInvocationOutcome): void => { + recordToolInvocationMetric({ + toolName: tool.mcpName, + runtime, + transport, + outcome, + durationMs: Date.now() - startedAt, + }); + }; + + const captureInfraErrorMetric = (error: unknown): void => { + recordInternalErrorMetric({ + component: 'tool-invoker', + runtime, + errorKind: getErrorKind(error), + }); + }; + + const postProcessParams = this.buildPostProcessParams(tool, opts.runtime); + const xcodeIdeRemoteToolName = tool.xcodeIdeRemoteToolName; + const isDynamicXcodeIdeTool = + tool.workflow === 'xcode-ide' && typeof xcodeIdeRemoteToolName === 'string'; + + if (opts.runtime === 'cli' && isDynamicXcodeIdeTool) { + transport = 'xcode-ide-daemon'; + return this.invokeViaDaemon( + opts, + (client) => client.invokeXcodeIdeTool(xcodeIdeRemoteToolName, args), + { + label: 'xcode-ide', + errorTitle: 'Xcode IDE invocation failed', + captureInfraErrorMetric, + captureInvocationMetric, + postProcessParams, + }, + ); + } + + if (opts.runtime === 'cli' && tool.stateful) { + transport = 'daemon'; + return this.invokeViaDaemon(opts, (client) => client.invokeTool(tool.mcpName, args), { + label: `daemon/${tool.mcpName}`, + errorTitle: 'Daemon invocation failed', + captureInfraErrorMetric, + captureInvocationMetric, + postProcessParams, + }); + } + + // Direct invocation (CLI stateless or daemon internal) + try { + const response = await tool.handler(args); + captureInvocationMetric('completed'); + return postProcessToolResponse({ + ...postProcessParams, + response, + }); + } catch (error) { + log( + 'error', + `[infra/tool-invoker] direct tool handler failed for ${tool.mcpName} (${getErrorKind(error)})`, + { sentry: true }, + ); + captureInfraErrorMetric(error); + captureInvocationMetric('infra_error'); + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse('Tool execution failed', message); + } + } +} diff --git a/src/runtime/types.ts b/src/runtime/types.ts new file mode 100644 index 00000000..ef9a505d --- /dev/null +++ b/src/runtime/types.ts @@ -0,0 +1,104 @@ +import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; +import type { ToolResponse } from '../types/common.ts'; +import type { ToolSchemaShape, PluginMeta } from '../core/plugin-types.ts'; + +export interface NextStepTemplate { + label: string; + toolId?: string; + params?: Record; + priority?: number; +} + +export type RuntimeKind = 'cli' | 'daemon' | 'mcp'; + +export interface ToolDefinition { + /** Stable manifest tool id for static tools loaded from YAML */ + id?: string; + + /** Stable CLI command name (kebab-case, disambiguated) */ + cliName: string; + + /** Original MCP tool name as declared (unchanged) */ + mcpName: string; + + /** Workflow directory name (e.g., "simulator", "device", "logging") */ + workflow: string; + + description?: string; + annotations?: ToolAnnotations; + + /** Static next-step templates declared in the manifest */ + nextStepTemplates?: NextStepTemplate[]; + + /** + * Schema shape used to generate yargs flags for CLI. + * Must include ALL parameters (not the session-default-hidden version). + */ + cliSchema: ToolSchemaShape; + + /** + * Schema shape used for MCP registration. + */ + mcpSchema: ToolSchemaShape; + + /** + * Whether CLI MUST route this tool to the daemon (stateful operations). + */ + stateful: boolean; + + /** + * For daemon-backed xcode-ide dynamic tools, identifies the remote bridge tool. + */ + xcodeIdeRemoteToolName?: string; + + /** + * Shared handler (same used by MCP). No duplication. + */ + handler: PluginMeta['handler']; +} + +export interface ToolResolution { + tool?: ToolDefinition; + ambiguous?: string[]; + notFound?: boolean; +} + +export interface ToolCatalog { + tools: ToolDefinition[]; + + /** Exact match on cliName */ + getByCliName(name: string): ToolDefinition | null; + + /** Exact match on MCP name */ + getByMcpName(name: string): ToolDefinition | null; + + /** Exact match on stable manifest tool id */ + getByToolId(toolId: string): ToolDefinition | null; + + /** Resolve user input with ambiguity reporting */ + resolve(input: string): ToolResolution; +} + +export interface InvokeOptions { + runtime: RuntimeKind; + /** CLI-exposed workflow IDs used for daemon environment overrides */ + cliExposedWorkflowIds?: string[]; + /** @deprecated Use cliExposedWorkflowIds instead */ + enabledWorkflows?: string[]; + /** Socket path override */ + socketPath?: string; + /** Timeout in ms for daemon startup when auto-starting (default: 5000) */ + daemonStartupTimeoutMs?: number; + /** Workspace root for daemon auto-start context */ + workspaceRoot?: string; + /** Log level override for daemon auto-start */ + logLevel?: string; +} + +export interface ToolInvoker { + invoke( + toolName: string, + args: Record, + opts: InvokeOptions, + ): Promise; +} diff --git a/src/server/bootstrap.ts b/src/server/bootstrap.ts index 06780f84..7aaa2c4d 100644 --- a/src/server/bootstrap.ts +++ b/src/server/bootstrap.ts @@ -1,25 +1,38 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SetLevelRequestSchema } from '@modelcontextprotocol/sdk/types.js'; -import process from 'node:process'; import { registerResources } from '../core/resources.ts'; +import type { FileSystemExecutor } from '../utils/FileSystemExecutor.ts'; import { log, setLogLevel, type LogLevel } from '../utils/logger.ts'; -import { registerWorkflows } from '../utils/tool-registry.ts'; +import type { RuntimeConfigOverrides } from '../utils/config-store.ts'; +import { getRegisteredWorkflows, registerWorkflowsFromManifest } from '../utils/tool-registry.ts'; +import { bootstrapRuntime } from '../runtime/bootstrap-runtime.ts'; +import { getXcodeToolsBridgeManager } from '../integrations/xcode-tools-bridge/index.ts'; +import { resolveWorkspaceRoot } from '../daemon/socket-path.ts'; +import { detectXcodeRuntime } from '../utils/xcode-process.ts'; +import { readXcodeIdeState } from '../utils/xcode-state-reader.ts'; +import { sessionStore } from '../utils/session-store.ts'; +import { startXcodeStateWatcher, lookupBundleId } from '../utils/xcode-state-watcher.ts'; +import { getDefaultCommandExecutor } from '../utils/command.ts'; +import type { PredicateContext } from '../visibility/predicate-types.ts'; +import { createStartupProfiler, getStartupProfileNowMs } from './startup-profiler.ts'; export interface BootstrapOptions { enabledWorkflows?: string[]; + configOverrides?: RuntimeConfigOverrides; + fileSystemExecutor?: FileSystemExecutor; + cwd?: string; } -function parseEnabledWorkflows(value: string): string[] { - return value - .split(',') - .map((name) => name.trim()) - .filter(Boolean); +export interface BootstrapResult { + runDeferredInitialization: () => Promise; } export async function bootstrapServer( server: McpServer, options: BootstrapOptions = {}, -): Promise { +): Promise { + const profiler = createStartupProfiler('bootstrap'); + server.server.setRequestHandler(SetLevelRequestSchema, async (request) => { const { level } = request.params; setLogLevel(level as LogLevel); @@ -27,19 +40,140 @@ export async function bootstrapServer( return {}; }); - const enabledWorkflows = options.enabledWorkflows?.length - ? options.enabledWorkflows - : process.env.XCODEBUILDMCP_ENABLED_WORKFLOWS - ? parseEnabledWorkflows(process.env.XCODEBUILDMCP_ENABLED_WORKFLOWS) - : []; - - if (enabledWorkflows.length > 0) { - log('info', `🚀 Initializing server with selected workflows: ${enabledWorkflows.join(', ')}`); - await registerWorkflows(server, enabledWorkflows); - } else { - log('info', '🚀 Initializing server with all tools...'); - await registerWorkflows(server); + const hasLegacyEnabledWorkflows = Object.prototype.hasOwnProperty.call( + options, + 'enabledWorkflows', + ); + let overrides: RuntimeConfigOverrides | undefined; + if (options.configOverrides !== undefined) { + overrides = { ...options.configOverrides }; + } + if (hasLegacyEnabledWorkflows) { + overrides ??= {}; + overrides.enabledWorkflows = options.enabledWorkflows ?? []; + } + + let stageStartMs = getStartupProfileNowMs(); + const result = await bootstrapRuntime({ + runtime: 'mcp', + cwd: options.cwd, + fs: options.fileSystemExecutor, + configOverrides: overrides, + }); + profiler.mark('bootstrapRuntime', stageStartMs); + + if (result.configFound) { + for (const notice of result.notices) { + log('info', `[ProjectConfig] ${notice}`); + } } + const enabledWorkflows = result.runtime.config.enabledWorkflows; + const workspaceRoot = resolveWorkspaceRoot({ + cwd: result.runtime.cwd, + projectConfigPath: result.configPath, + }); + + log('info', `🚀 Initializing server...`); + + const executor = getDefaultCommandExecutor(); + stageStartMs = getStartupProfileNowMs(); + const xcodeDetection = await detectXcodeRuntime(executor); + profiler.mark('detectXcodeRuntime', stageStartMs); + + const ctx: PredicateContext = { + runtime: 'mcp', + config: result.runtime.config, + runningUnderXcode: xcodeDetection.runningUnderXcode, + }; + + stageStartMs = getStartupProfileNowMs(); + await registerWorkflowsFromManifest(enabledWorkflows, ctx); + profiler.mark('registerWorkflowsFromManifest', stageStartMs); + + const resolvedWorkflows = getRegisteredWorkflows(); + const xcodeIdeEnabled = resolvedWorkflows.includes('xcode-ide'); + const xcodeToolsBridge = xcodeIdeEnabled ? getXcodeToolsBridgeManager(server) : null; + xcodeToolsBridge?.setWorkflowEnabled(xcodeIdeEnabled); + + stageStartMs = getStartupProfileNowMs(); await registerResources(server); + profiler.mark('registerResources', stageStartMs); + + return { + runDeferredInitialization: async (): Promise => { + const deferredProfiler = createStartupProfiler('bootstrap-deferred'); + + if (!xcodeDetection.runningUnderXcode) { + return; + } + + log('info', `[xcode] Running under Xcode agent environment`); + + const { projectPath, workspacePath } = sessionStore.getAll(); + + let deferredStageStartMs = getStartupProfileNowMs(); + const xcodeState = await readXcodeIdeState({ + executor, + cwd: result.runtime.cwd, + searchRoot: workspaceRoot, + projectPath, + workspacePath, + }); + deferredProfiler.mark('readXcodeIdeState', deferredStageStartMs); + + if (xcodeState.error) { + log('debug', `[xcode] Could not read Xcode IDE state: ${xcodeState.error}`); + } else { + const syncedDefaults: Record = {}; + if (xcodeState.scheme) { + syncedDefaults.scheme = xcodeState.scheme; + } + if (xcodeState.simulatorId) { + syncedDefaults.simulatorId = xcodeState.simulatorId; + } + if (xcodeState.simulatorName) { + syncedDefaults.simulatorName = xcodeState.simulatorName; + } + + if (Object.keys(syncedDefaults).length > 0) { + sessionStore.setDefaults(syncedDefaults); + log( + 'info', + `[xcode] Synced session defaults from Xcode: ${JSON.stringify(syncedDefaults)}`, + ); + } + + if (xcodeState.scheme) { + lookupBundleId(executor, xcodeState.scheme, projectPath, workspacePath) + .then((bundleId) => { + if (bundleId) { + sessionStore.setDefaults({ bundleId }); + log('info', `[xcode] Bundle ID resolved: "${bundleId}"`); + } + }) + .catch((e) => { + log('debug', `[xcode] Failed to lookup bundle ID: ${e}`); + }); + } + } + + if (!result.runtime.config.disableXcodeAutoSync) { + deferredStageStartMs = getStartupProfileNowMs(); + const watcherStarted = await startXcodeStateWatcher({ + executor, + cwd: result.runtime.cwd, + searchRoot: workspaceRoot, + projectPath, + workspacePath, + }); + deferredProfiler.mark('startXcodeStateWatcher', deferredStageStartMs); + if (watcherStarted) { + log('info', `[xcode] Started file watcher for automatic sync`); + } + } else { + log('info', `[xcode] Automatic Xcode sync disabled via config`); + } + }, + }; } diff --git a/src/server/server-state.ts b/src/server/server-state.ts new file mode 100644 index 00000000..573a8c13 --- /dev/null +++ b/src/server/server-state.ts @@ -0,0 +1,17 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; + +let serverInstance: McpServer | undefined; + +export function getServer(): McpServer | undefined { + return serverInstance; +} + +export function setServer(server: McpServer): void { + serverInstance = server; +} + +export function __resetServerStateForTests(): void { + serverInstance = undefined; +} + +export { serverInstance as server }; diff --git a/src/server/server.ts b/src/server/server.ts index a7f8bdd4..a880da63 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -14,22 +14,37 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import * as Sentry from '@sentry/node'; import { log } from '../utils/logger.ts'; import { version } from '../version.ts'; -import * as Sentry from '@sentry/node'; +import { getServer, setServer } from './server-state.ts'; -/** - * Create and configure the MCP server - * @returns Configured MCP server instance - */ -export function createServer(): McpServer { - // Create server instance - const baseServer = new McpServer( +function createBaseServerInstance(): McpServer { + return new McpServer( { name: 'xcodebuildmcp', - version, + version: String(version), }, { + instructions: `XcodeBuildMCP provides comprehensive tooling for Apple platform development (iOS, macOS, watchOS, tvOS, visionOS). + +Prefer XcodeBuildMCP tools over shell commands for Apple platform tasks when available. + +Capabilities: +- Session defaults: Configure project, scheme, simulator, and device defaults to avoid repetitive parameters +- Project discovery: Find Xcode projects/workspaces, list schemes, inspect build settings +- Simulator workflows: Build, run, test, install, and launch apps on iOS simulators; manage simulator state (boot, erase, location, appearance) +- Device workflows: Build, test, install, and launch apps on physical devices with code signing +- macOS workflows: Build, run, and test macOS applications +- Log capture: Stream and capture logs from simulators and devices +- LLDB debugging: Attach debugger, set breakpoints, inspect stack traces and variables, execute LLDB commands +- UI automation: Capture screenshots, inspect view hierarchy with coordinates, perform taps/swipes/gestures, type text, press hardware buttons +- SwiftPM: Build, run, test, and manage Swift Package Manager projects +- Project scaffolding: Generate new iOS/macOS project templates + +Only simulator workflow tools are enabled by default. If capabilities like device, macOS, debugging, or UI automation are not available, the user must configure XcodeBuildMCP to enable them. See https://github.com/getsentry/XcodeBuildMCP/blob/main/docs/CONFIGURATION.md for workflow configuration. + +Always start by calling session_show_defaults to see current configuration, then use discovery tools to find projects and set appropriate defaults.`, capabilities: { tools: { listChanged: true, @@ -41,13 +56,26 @@ export function createServer(): McpServer { logging: {}, }, }, - ); + ) as unknown as McpServer; +} + +/** + * Create and configure the MCP server + * @returns Configured MCP server instance + */ +export function createServer(): McpServer { + if (getServer()) { + throw new Error('MCP server already initialized.'); + } + const baseServer = createBaseServerInstance(); + const server = Sentry.wrapMcpServerWithSentry(baseServer, { + recordInputs: false, + recordOutputs: false, + }); - // Wrap server with Sentry for MCP instrumentation - const server = Sentry.wrapMcpServerWithSentry(baseServer); + setServer(server); - // Log server initialization - log('info', `Server initialized with Sentry MCP instrumentation (version ${version})`); + log('info', `Server initialized (version ${version})`); return server; } diff --git a/src/server/start-mcp-server.ts b/src/server/start-mcp-server.ts new file mode 100644 index 00000000..79c78586 --- /dev/null +++ b/src/server/start-mcp-server.ts @@ -0,0 +1,128 @@ +#!/usr/bin/env node + +/** + * MCP Server Startup Module + * + * This module provides the logic to start the XcodeBuildMCP server. + * It can be invoked from the CLI via the `mcp` subcommand. + */ + +import { createServer, startServer } from './server.ts'; +import { log, setLogLevel } from '../utils/logger.ts'; +import { + enrichSentryContext, + flushAndCloseSentry, + initSentry, + setSentryRuntimeContext, +} from '../utils/sentry.ts'; +import { getDefaultDebuggerManager } from '../utils/debugger/index.ts'; +import { version } from '../version.ts'; +import process from 'node:process'; +import { bootstrapServer } from './bootstrap.ts'; +import { shutdownXcodeToolsBridge } from '../integrations/xcode-tools-bridge/index.ts'; +import { createStartupProfiler, getStartupProfileNowMs } from './startup-profiler.ts'; +import { getConfig } from '../utils/config-store.ts'; +import { getRegisteredWorkflows } from '../utils/tool-registry.ts'; + +/** + * Start the MCP server. + * This function initializes Sentry, creates and bootstraps the server, + * sets up signal handlers for graceful shutdown, and starts the server. + */ +export async function startMcpServer(): Promise { + try { + const profiler = createStartupProfiler('start-mcp-server'); + + // MCP mode defaults to info level logging + // Clients can override via logging/setLevel MCP request + setLogLevel('info'); + + let stageStartMs = getStartupProfileNowMs(); + initSentry({ mode: 'mcp' }); + profiler.mark('initSentry', stageStartMs); + + stageStartMs = getStartupProfileNowMs(); + const server = createServer(); + profiler.mark('createServer', stageStartMs); + + stageStartMs = getStartupProfileNowMs(); + const bootstrap = await bootstrapServer(server); + profiler.mark('bootstrapServer', stageStartMs); + + stageStartMs = getStartupProfileNowMs(); + await startServer(server); + profiler.mark('startServer', stageStartMs); + + const config = getConfig(); + const enabledWorkflows = getRegisteredWorkflows(); + setSentryRuntimeContext({ + mode: 'mcp', + enabledWorkflows, + disableSessionDefaults: config.disableSessionDefaults, + disableXcodeAutoSync: config.disableXcodeAutoSync, + incrementalBuildsEnabled: config.incrementalBuildsEnabled, + debugEnabled: config.debug, + uiDebuggerGuardMode: config.uiDebuggerGuardMode, + xcodeIdeWorkflowEnabled: enabledWorkflows.includes('xcode-ide'), + }); + + void bootstrap.runDeferredInitialization().catch((error) => { + log( + 'warning', + `Deferred bootstrap initialization failed: ${error instanceof Error ? error.message : String(error)}`, + ); + }); + setImmediate(() => { + enrichSentryContext(); + }); + + let shuttingDown = false; + const shutdown = async (signal: NodeJS.Signals): Promise => { + if (shuttingDown) return; + shuttingDown = true; + + log('info', `Received ${signal}; shutting down MCP server`); + + let exitCode = 0; + + try { + await shutdownXcodeToolsBridge(); + } catch (error) { + exitCode = 1; + log('error', `Failed to shutdown Xcode tools bridge: ${String(error)}`, { sentry: true }); + } + + try { + await getDefaultDebuggerManager().disposeAll(); + } catch (error) { + exitCode = 1; + log('error', `Failed to dispose debugger sessions: ${String(error)}`, { sentry: true }); + } + + try { + await server.close(); + } catch (error) { + exitCode = 1; + log('error', `Failed to close MCP server: ${String(error)}`, { sentry: true }); + } + + await flushAndCloseSentry(2000); + process.exit(exitCode); + }; + + process.once('SIGTERM', () => { + void shutdown('SIGTERM'); + }); + + process.once('SIGINT', () => { + void shutdown('SIGINT'); + }); + + log('info', `XcodeBuildMCP server (version ${version}) started successfully`); + } catch (error) { + log('error', `Fatal error in startMcpServer(): ${String(error)}`, { sentry: true }); + console.error('Fatal error in startMcpServer():', error); + await flushAndCloseSentry(2000); + process.exit(1); + } +} diff --git a/src/server/startup-profiler.ts b/src/server/startup-profiler.ts new file mode 100644 index 00000000..7d116bdb --- /dev/null +++ b/src/server/startup-profiler.ts @@ -0,0 +1,38 @@ +import { performance } from 'node:perf_hooks'; +import { log } from '../utils/logger.ts'; + +const PROFILE_ENV = 'XCODEBUILDMCP_STARTUP_PROFILE'; + +function isEnabled(): boolean { + const value = process.env[PROFILE_ENV]?.toLowerCase(); + return value === '1' || value === 'true'; +} + +export interface StartupProfiler { + readonly enabled: boolean; + readonly startedAtMs: number; + mark(stage: string, startedAtMs: number): void; +} + +export function createStartupProfiler(scope: string): StartupProfiler { + const enabled = isEnabled(); + const startedAtMs = performance.now(); + + return { + enabled, + startedAtMs, + mark(stage: string, stageStartedAtMs: number): void { + if (!enabled) return; + const elapsedMs = performance.now() - stageStartedAtMs; + const totalMs = performance.now() - startedAtMs; + log( + 'info', + `[startup-profile] scope=${scope} stage=${stage} ms=${elapsedMs.toFixed(1)} totalMs=${totalMs.toFixed(1)}`, + ); + }, + }; +} + +export function getStartupProfileNowMs(): number { + return performance.now(); +} diff --git a/src/smithery.ts b/src/smithery.ts deleted file mode 100644 index 9d8986a1..00000000 --- a/src/smithery.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { z } from 'zod'; -import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { bootstrapServer } from './server/bootstrap.ts'; -import { createServer } from './server/server.ts'; -import { log } from './utils/logger.ts'; -import { initSentry } from './utils/sentry.ts'; - -export const configSchema = z.object({ - incrementalBuildsEnabled: z - .boolean() - .default(false) - .describe('Enable incremental builds via xcodemake (true/false).'), - enabledWorkflows: z - .string() - .default('') - .describe('Comma-separated list of workflows to load at startup.'), - sentryDisabled: z.boolean().default(false).describe('Disable Sentry error reporting.'), - debug: z.boolean().default(false).describe('Enable debug logging.'), -}); - -export type SmitheryConfig = z.infer; - -function applyConfig(config: SmitheryConfig): string[] { - process.env.INCREMENTAL_BUILDS_ENABLED = config.incrementalBuildsEnabled ? '1' : '0'; - process.env.XCODEBUILDMCP_ENABLED_WORKFLOWS = config.enabledWorkflows; - process.env.XCODEBUILDMCP_SENTRY_DISABLED = config.sentryDisabled ? 'true' : 'false'; - process.env.XCODEBUILDMCP_DEBUG = config.debug ? 'true' : 'false'; - - return config.enabledWorkflows - .split(',') - .map((name) => name.trim()) - .filter(Boolean); -} - -export default function createSmitheryServer({ config }: { config: SmitheryConfig }): McpServer { - const workflowNames = applyConfig(config); - - initSentry(); - - const server = createServer(); - const bootstrapPromise = bootstrapServer(server, { enabledWorkflows: workflowNames }).catch( - (error) => { - log( - 'error', - `Failed to bootstrap Smithery server: ${error instanceof Error ? error.message : String(error)}`, - ); - throw error; - }, - ); - - const handler: ProxyHandler = { - get(target, prop, receiver): unknown { - if (prop === 'connect') { - return async (...args: unknown[]): Promise => { - await bootstrapPromise; - const connect = target.connect.bind(target) as (...connectArgs: unknown[]) => unknown; - return connect(...args); - }; - } - return Reflect.get(target, prop, receiver) as unknown; - }, - }; - - return new Proxy(server, handler); -} diff --git a/src/smoke-tests/__tests__/cli-surface.test.ts b/src/smoke-tests/__tests__/cli-surface.test.ts new file mode 100644 index 00000000..cfbea240 --- /dev/null +++ b/src/smoke-tests/__tests__/cli-surface.test.ts @@ -0,0 +1,136 @@ +import { describe, it, expect } from 'vitest'; +import { execFileSync } from 'child_process'; +import { resolve } from 'path'; + +const CLI = resolve(__dirname, '../../../build/cli.js'); +const cliEnv = (() => { + const env: Record = { ...process.env, NO_COLOR: '1' }; + // Remove test environment markers so the CLI binary runs in production mode + delete env.VITEST; + delete env.NODE_ENV; + return env; +})(); +const run = (args: string): string => { + const argv = args.trim() ? args.trim().split(/\s+/) : []; + return execFileSync('node', [CLI, ...argv], { + encoding: 'utf8', + timeout: 15_000, + env: cliEnv, + }); +}; + +const runMayFail = (args: string): { stdout: string; status: number } => { + try { + const stdout = run(args); + return { stdout, status: 0 }; + } catch (err: unknown) { + const error = err as NodeJS.ErrnoException & { + stdout?: string; + stderr?: string; + status?: number; + }; + return { + stdout: (error.stdout ?? '') + (error.stderr ?? ''), + status: error.status ?? 1, + }; + } +}; + +describe('CLI Surface (e2e)', () => { + describe('top-level', () => { + it('--help shows usage info', () => { + const output = run('--help'); + expect(output).toContain('Usage:'); + expect(output).toContain('xcodebuildmcp'); + expect(output).toContain('Commands:'); + }); + + it('--version prints a semver string', () => { + const output = run('--version').trim(); + expect(output).toMatch(/^\d+\.\d+\.\d+/); + }); + + it('tools command lists available tools', () => { + const output = run('tools'); + expect(output).toContain('Available tools'); + expect(output).toContain('simulator:'); + expect(output).toContain('build'); + }); + }); + + describe('workflow subcommands', () => { + const workflows = [ + 'simulator', + 'simulator-management', + 'device', + 'macos', + 'project-discovery', + 'project-scaffolding', + 'swift-package', + 'logging', + 'debugging', + 'ui-automation', + 'utilities', + ]; + + it.each(workflows)('%s --help shows workflow help', (workflow) => { + const output = run(`${workflow} --help`); + expect(output).toContain('Commands:'); + }); + }); + + describe('tool-specific help', () => { + const toolCases = [ + { workflow: 'simulator', tool: 'build', expected: '--scheme' }, + { workflow: 'simulator', tool: 'list-sims', expected: '--help' }, + { workflow: 'device', tool: 'build', expected: '--scheme' }, + { workflow: 'swift-package', tool: 'build', expected: '--package-path' }, + { workflow: 'project-discovery', tool: 'list-schemes', expected: '--project-path' }, + { workflow: 'ui-automation', tool: 'tap', expected: '--simulator-id' }, + { workflow: 'utilities', tool: 'clean', expected: '--scheme' }, + ]; + + it.each(toolCases)( + '$workflow $tool --help shows parameter docs', + ({ workflow, tool, expected }) => { + const output = run(`${workflow} ${tool} --help`); + expect(output).toContain(expected); + }, + ); + }); + + describe('tool invocation', () => { + it('invalid tool returns error', () => { + const result = runMayFail('simulator nonexistent-tool'); + expect(result.status).not.toBe(0); + }); + + it('tool with --output json returns valid JSON', () => { + // list_sims is a good candidate -- it will fail to run xcrun but should + // return structured JSON output even on error + const result = runMayFail('simulator list-sims --output json'); + const output = result.stdout.trim(); + expect(output.length).toBeGreaterThan(0); + // Even if the tool fails (no xcrun), a successful run should be JSON + if (result.status === 0) { + const parsed = JSON.parse(output); + expect(parsed).toBeDefined(); + } + // If it fails, that's acceptable on non-macOS platforms as long as output is present + }); + + it('missing required args produces user-friendly error', () => { + // build requires --scheme + const result = runMayFail('simulator build'); + const output = result.stdout.toLowerCase(); + // Should mention the missing requirement + expect( + output.includes('required') || + output.includes('scheme') || + output.includes('error') || + output.includes('must provide') || + output.includes('missing'), + ).toBe(true); + }); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-device-macos.test.ts b/src/smoke-tests/__tests__/e2e-mcp-device-macos.test.ts new file mode 100644 index 00000000..4642665a --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-device-macos.test.ts @@ -0,0 +1,367 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { isErrorResponse, expectContent } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + xcodebuild: { success: true, output: 'Build Succeeded' }, + devicectl: { success: true, output: '{}' }, + 'xctrace list devices': { success: true, output: 'No devices found.' }, + open: { success: true, output: '' }, + kill: { success: true, output: '' }, + pkill: { success: true, output: '' }, + 'defaults read': { success: true, output: 'io.sentry.MyApp' }, + PlistBuddy: { success: true, output: 'io.sentry.MyApp' }, + xcresulttool: { success: true, output: '{}' }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +describe('MCP Device and macOS Tool Invocation (e2e)', () => { + describe('device tools', () => { + it('build_device captures xcodebuild command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyApp', + projectPath: '/path/to/MyApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'build_device', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild') && c.includes('MyApp'))).toBe(true); + }); + + it('test_device captures xcodebuild test command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyApp', + projectPath: '/path/to/MyApp.xcodeproj', + deviceId: 'AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'test_device', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild') && c.includes('test'))).toBe(true); + }); + + it('launch_app_device captures devicectl launch command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + deviceId: 'AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE', + bundleId: 'io.sentry.MyApp', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'launch_app_device', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('devicectl') && c.includes('launch'))).toBe(true); + }); + + it('stop_app_device captures devicectl terminate command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + deviceId: 'AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'stop_app_device', + arguments: { processId: 12345 }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('devicectl') && c.includes('terminate'))).toBe( + true, + ); + }); + + it('install_app_device captures devicectl install command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + deviceId: 'AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'install_app_device', + arguments: { appPath: '/path/to/MyApp.app' }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('devicectl') && c.includes('install'))).toBe(true); + }); + + it('get_device_app_path captures xcodebuild showBuildSettings command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyApp', + projectPath: '/path/to/MyApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'get_device_app_path', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some((c) => c.includes('xcodebuild') && c.includes('-showBuildSettings')), + ).toBe(true); + }); + + it('list_devices captures devicectl or xctrace command', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'list_devices', + arguments: {}, + }); + + expectContent(result); + + expect(harness.capturedCommands.length).toBeGreaterThan(0); + }); + }); + + describe('project discovery tools', () => { + it('discover_projs responds with content', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'discover_projs', + arguments: { workspaceRoot: '/path/to/workspace' }, + }); + + expectContent(result); + }); + + it('get_app_bundle_id responds with content', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'get_app_bundle_id', + arguments: { appPath: '/path/to/MyApp.app' }, + }); + + expectContent(result); + }); + + it('get_mac_bundle_id responds with content', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'get_mac_bundle_id', + arguments: { appPath: '/path/to/MyApp.app' }, + }); + + expectContent(result); + }); + }); + + describe('macOS tools', () => { + it('build_macos captures xcodebuild command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyMacApp', + projectPath: '/path/to/MyMacApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'build_macos', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild') && c.includes('MyMacApp'))).toBe( + true, + ); + }); + + it('build_run_macos captures xcodebuild and open commands', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyMacApp', + projectPath: '/path/to/MyMacApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'build_run_macos', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild'))).toBe(true); + }); + + it('test_macos captures xcodebuild test command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyMacApp', + projectPath: '/path/to/MyMacApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'test_macos', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild') && c.includes('test'))).toBe(true); + }); + + it('launch_mac_app responds with content', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'launch_mac_app', + arguments: { appPath: '/path/to/MyMacApp.app' }, + }); + + expectContent(result); + }); + + it('stop_mac_app captures kill command with processId', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'stop_mac_app', + arguments: { processId: 54321 }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('kill') && c.includes('54321'))).toBe(true); + }); + + it('stop_mac_app captures pkill command with appName', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'stop_mac_app', + arguments: { appName: 'MyMacApp' }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('MyMacApp'))).toBe(true); + }); + + it('get_mac_app_path captures xcodebuild showBuildSettings command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyMacApp', + projectPath: '/path/to/MyMacApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'get_mac_app_path', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some((c) => c.includes('xcodebuild') && c.includes('-showBuildSettings')), + ).toBe(true); + }); + }); + + describe('error handling', () => { + it('build_device returns error when session defaults missing', async () => { + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { all: true }, + }); + + const result = await harness.client.callTool({ + name: 'build_device', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + + it('build_macos returns error when session defaults missing', async () => { + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { all: true }, + }); + + const result = await harness.client.callTool({ + name: 'build_macos', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + + it('stop_mac_app returns error when no appName or processId provided', async () => { + const result = await harness.client.callTool({ + name: 'stop_mac_app', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-discovery.test.ts b/src/smoke-tests/__tests__/e2e-mcp-discovery.test.ts new file mode 100644 index 00000000..5d2304db --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-discovery.test.ts @@ -0,0 +1,200 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { loadManifest } from '../../core/manifest/load-manifest.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness(); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +describe('MCP Discovery (e2e)', () => { + it('responds to listTools', async () => { + const result = await harness.client.listTools(); + expect(result.tools).toBeDefined(); + expect(result.tools.length).toBeGreaterThan(0); + }); + + it('returns the expected number of tools for all-workflows config', async () => { + const result = await harness.client.listTools(); + + // Count expected MCP-visible tools from manifest (static tools only) + const manifest = loadManifest(); + let manifestMcpTools = 0; + for (const tool of manifest.tools.values()) { + if (tool.availability.mcp) { + manifestMcpTools++; + } + } + + // Actual count may exceed manifest count due to dynamic tool registration + // (e.g., xcode-tools bridge) and may be less due to predicate filtering. + // Assert a reasonable lower bound to catch registration regressions. + expect(result.tools.length).toBeGreaterThan(50); + // Every manifest MCP tool should be registered (minus predicate-gated ones) + expect(result.tools.length).toBeGreaterThanOrEqual(manifestMcpTools - 10); + }); + + it('every tool has an inputSchema with type "object"', async () => { + const result = await harness.client.listTools(); + for (const tool of result.tools) { + expect(tool.inputSchema).toBeDefined(); + expect(tool.inputSchema.type).toBe('object'); + } + }); + + it('every tool has a non-empty description', async () => { + const result = await harness.client.listTools(); + for (const tool of result.tools) { + expect(tool.description).toBeTruthy(); + expect(tool.description!.length).toBeGreaterThan(0); + } + }); + + it('every tool has a non-empty name', async () => { + const result = await harness.client.listTools(); + for (const tool of result.tools) { + expect(tool.name).toBeTruthy(); + expect(tool.name.length).toBeGreaterThan(0); + } + }); + + it('includes session management tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('session_set_defaults'); + expect(names).toContain('session_show_defaults'); + expect(names).toContain('session_clear_defaults'); + expect(names).toContain('session_use_defaults_profile'); + }); + + it('excludes workflow discovery when experimentalWorkflowDiscovery is disabled', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + // manage-workflows requires experimentalWorkflowDiscovery predicate + // which is disabled by default -- it should NOT appear + expect(names).not.toContain('manage-workflows'); + }); + + it('includes simulator workflow tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('build_sim'); + expect(names).toContain('list_sims'); + expect(names).toContain('boot_sim'); + }); + + it('includes swift package tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('swift_package_build'); + expect(names).toContain('swift_package_test'); + }); + + it('includes device workflow tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('build_device'); + expect(names).toContain('list_devices'); + }); + + it('includes macOS workflow tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('build_macos'); + expect(names).toContain('build_run_macos'); + }); + + it('includes ui-automation tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('tap'); + expect(names).toContain('swipe'); + expect(names).toContain('screenshot'); + expect(names).toContain('snapshot_ui'); + }); + + it('includes project discovery tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('discover_projs'); + expect(names).toContain('list_schemes'); + }); + + it('includes debugging tools when debug is enabled', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('debug_attach_sim'); + expect(names).toContain('debug_breakpoint_add'); + expect(names).toContain('debug_stack'); + }); + + it('includes logging tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('start_sim_log_cap'); + expect(names).toContain('stop_sim_log_cap'); + }); + + it('includes project scaffolding tools', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + expect(names).toContain('scaffold_ios_project'); + expect(names).toContain('scaffold_macos_project'); + }); + + it('tools have annotations where expected', async () => { + const result = await harness.client.listTools(); + + // build_sim should have destructiveHint annotation + const buildSim = result.tools.find((t) => t.name === 'build_sim'); + expect(buildSim).toBeDefined(); + expect(buildSim!.annotations).toBeDefined(); + + // list_sims should have readOnlyHint annotation + const listSims = result.tools.find((t) => t.name === 'list_sims'); + expect(listSims).toBeDefined(); + expect(listSims!.annotations).toBeDefined(); + }); + + it('no duplicate tool names', async () => { + const result = await harness.client.listTools(); + const names = result.tools.map((t) => t.name); + const uniqueNames = new Set(names); + expect(uniqueNames.size).toBe(names.length); + }); + + it('every MCP-available, predicate-free tool in an enabled workflow is registered', async () => { + const result = await harness.client.listTools(); + const registeredNames = new Set(result.tools.map((t) => t.name)); + const manifest = loadManifest(); + + // Collect tool IDs from workflows that are both MCP-available AND predicate-free + // (workflows with predicates may be excluded at runtime) + const toolIdsInEnabledWorkflows = new Set(); + for (const workflow of manifest.workflows.values()) { + if (workflow.availability.mcp && workflow.predicates.length === 0) { + for (const toolId of workflow.tools) { + toolIdsInEnabledWorkflows.add(toolId); + } + } + } + + const missingTools: string[] = []; + for (const [toolId, tool] of manifest.tools) { + if (tool.availability.mcp && tool.predicates.length === 0) { + if (!toolIdsInEnabledWorkflows.has(toolId)) continue; + const mcpName = tool.names.mcp; + if (!registeredNames.has(mcpName)) { + missingTools.push(mcpName); + } + } + } + + expect(missingTools).toEqual([]); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-doctor.test.ts b/src/smoke-tests/__tests__/e2e-mcp-doctor.test.ts new file mode 100644 index 00000000..23eb1359 --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-doctor.test.ts @@ -0,0 +1,35 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { getContent } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + xcrun: { success: true, output: '' }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness?.cleanup(); +}); + +describe('MCP Doctor Tool (e2e)', () => { + it('doctor returns diagnostic content', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'doctor', + arguments: {}, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + + const hasText = content.some( + (c) => typeof c.text === 'string' && c.text.includes('XcodeBuildMCP Doctor'), + ); + expect(hasText).toBe(true); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-error-paths.test.ts b/src/smoke-tests/__tests__/e2e-mcp-error-paths.test.ts new file mode 100644 index 00000000..ce808c92 --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-error-paths.test.ts @@ -0,0 +1,176 @@ +import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { extractText, isErrorResponse } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + 'simctl list devices': { + success: false, + output: 'simctl error: unable to enumerate devices', + }, + xcodebuild: { + success: false, + output: 'xcodebuild: error: The workspace does not exist.', + }, + 'swift build': { + success: false, + output: 'error: build failed', + }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +beforeEach(async () => { + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { all: true }, + }); +}); + +describe('MCP Error Paths (e2e)', () => { + describe('missing session defaults', () => { + it('build_sim errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'build_sim', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + }); + + it('build_device errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'build_device', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + }); + + it('build_macos errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'build_macos', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + }); + + it('clean errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'clean', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + }); + + it('test_sim errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'test_sim', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + }); + + it('tap errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'tap', + arguments: { x: 100, y: 200 }, + }); + expect(isErrorResponse(result)).toBe(true); + }); + + it('boot_sim errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'boot_sim', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + }); + + it('show_build_settings errors without session defaults', async () => { + const result = await harness.client.callTool({ + name: 'show_build_settings', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + }); + }); + + describe('command failure propagation', () => { + it('build_sim propagates xcodebuild failure', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyApp', + projectPath: '/path/to/MyApp.xcodeproj', + simulatorId: 'AAAAAAAA-1111-2222-3333-444444444444', + }, + }); + + const result = await harness.client.callTool({ + name: 'build_sim', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + const text = extractText(result).toLowerCase(); + expect(text).toContain('fail'); + }); + + it('swift_package_build propagates swift build failure', async () => { + const result = await harness.client.callTool({ + name: 'swift_package_build', + arguments: { packagePath: '/path/to/package' }, + }); + + expect(isErrorResponse(result)).toBe(true); + const text = extractText(result).toLowerCase(); + expect(text).toContain('fail'); + }); + + it('list_sims propagates simctl failure', async () => { + const result = await harness.client.callTool({ + name: 'list_sims', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + const text = extractText(result).toLowerCase(); + expect(text).toContain('fail'); + }); + }); + + describe('invalid parameter combinations', () => { + it('session_set_defaults resolves both projectPath and workspacePath by keeping workspacePath', async () => { + const result = await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + projectPath: '/path/to/MyApp.xcodeproj', + workspacePath: '/path/to/MyApp.xcworkspace', + }, + }); + + const text = extractText(result); + expect(text).toContain('keeping workspacePath'); + }); + + it('build_sim still errors when only scheme is set without project or simulator', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { scheme: 'MyApp' }, + }); + + const result = await harness.client.callTool({ + name: 'build_sim', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-invocation.test.ts b/src/smoke-tests/__tests__/e2e-mcp-invocation.test.ts new file mode 100644 index 00000000..2f6f75f8 --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-invocation.test.ts @@ -0,0 +1,274 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { isErrorResponse, expectContent } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + 'simctl list devices': { + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { + name: 'iPhone 16 Pro', + udid: 'AAAAAAAA-1111-2222-3333-444444444444', + state: 'Shutdown', + isAvailable: true, + }, + ], + }, + }), + }, + xcodebuild: { success: true, output: 'Build Succeeded' }, + 'swift build': { success: true, output: 'Build complete!' }, + 'simctl boot': { success: true, output: '' }, + 'xctrace list devices': { success: true, output: 'No devices found.' }, + 'axe tap': { success: true, output: 'Tap performed at (100, 200)' }, + 'simctl io': { success: true, output: '/tmp/screenshot.png' }, + devicectl: { success: true, output: '{}' }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +describe('MCP Tool Invocation (e2e)', () => { + describe('every tool responds to callTool', () => { + it('all registered tools return a response when called with empty args', async () => { + const { tools } = await harness.client.listTools(); + const results: { name: string; ok: boolean; hasContent: boolean }[] = []; + + for (const tool of tools) { + try { + const result = await harness.client.callTool({ + name: tool.name, + arguments: {}, + }); + + const content = 'content' in result ? result.content : undefined; + results.push({ + name: tool.name, + ok: true, + hasContent: Array.isArray(content) && content.length > 0, + }); + } catch { + // MCP protocol errors are acceptable for tools with required params + results.push({ name: tool.name, ok: true, hasContent: false }); + } + } + + expect(results.length).toBe(tools.length); + for (const r of results) { + expect(r.ok).toBe(true); + } + }, 60_000); + }); + + describe('representative tools with valid args', () => { + it('list_sims captures simctl command', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'list_sims', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('simctl') && c.includes('list'))).toBe(true); + }); + + it('build_sim captures xcodebuild command with scheme', async () => { + // Session-aware tools require session defaults to be set first + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyApp', + projectPath: '/path/to/MyApp.xcodeproj', + simulatorId: 'AAAAAAAA-1111-2222-3333-444444444444', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'build_sim', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild') && c.includes('MyApp'))).toBe(true); + }); + + it('clean captures xcodebuild clean command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyApp', + projectPath: '/path/to/MyApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'clean', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild') && c.includes('clean'))).toBe(true); + }); + + it('swift_package_build captures swift build command', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'swift_package_build', + arguments: { + packagePath: '/path/to/package', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('swift') && c.includes('build'))).toBe(true); + }); + + it('boot_sim captures simctl boot command', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + simulatorId: 'AAAAAAAA-1111-2222-3333-444444444444', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'boot_sim', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('simctl') && c.includes('boot'))).toBe(true); + }); + + it('list_schemes responds with content', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + projectPath: '/path/to/MyApp.xcodeproj', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'list_schemes', + arguments: {}, + }); + + expectContent(result); + + expect(harness.capturedCommands.length).toBeGreaterThan(0); + }); + + it('list_devices responds with content', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'list_devices', + arguments: {}, + }); + + expectContent(result); + + expect(harness.capturedCommands.length).toBeGreaterThan(0); + }); + + it('session_set_defaults works without external commands', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'TestScheme', + }, + }); + + expectContent(result); + }); + + it('session_show_defaults returns current defaults', async () => { + const result = await harness.client.callTool({ + name: 'session_show_defaults', + arguments: {}, + }); + + expectContent(result); + }); + + it('show_build_settings responds with content', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + projectPath: '/path/to/MyApp.xcodeproj', + scheme: 'MyApp', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'show_build_settings', + arguments: {}, + }); + + expectContent(result); + }); + }); + + describe('error handling', () => { + it('returns error for missing required args', async () => { + // Clear any session defaults from previous tests + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { all: true }, + }); + + // build_sim requires scheme + projectPath/workspacePath + simulatorId/simulatorName + const result = await harness.client.callTool({ + name: 'build_sim', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + + it('returns error response for non-existent tool', async () => { + // The MCP SDK may either throw or return an error response + // depending on the server implementation + let threw = false; + let errorMessage = ''; + try { + const result = await harness.client.callTool({ + name: 'this_tool_does_not_exist', + arguments: {}, + }); + expect(isErrorResponse(result)).toBe(true); + } catch (err: unknown) { + threw = true; + errorMessage = (err as Error).message; + } + + if (threw) { + expect(errorMessage.toLowerCase()).toMatch(/not found|unknown|error/); + } + }); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-logging.test.ts b/src/smoke-tests/__tests__/e2e-mcp-logging.test.ts new file mode 100644 index 00000000..3f967af6 --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-logging.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { isErrorResponse, expectContent } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + 'simctl spawn': { success: true, output: '' }, + 'log collect': { success: true, output: 'Log captured' }, + devicectl: { success: true, output: '{}' }, + xcrun: { success: true, output: '' }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +describe('MCP Logging Tools (e2e)', () => { + it('start_sim_log_cap requires simulatorId and bundleId via session', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + simulatorId: 'AAAAAAAA-1111-2222-3333-444444444444', + bundleId: 'io.sentry.TestApp', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'start_sim_log_cap', + arguments: {}, + }); + + expectContent(result); + }); + + it('stop_sim_log_cap returns error for unknown session', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'stop_sim_log_cap', + arguments: { + logSessionId: 'nonexistent-session-id', + }, + }); + + expectContent(result); + expect(isErrorResponse(result)).toBe(true); + }); + + it('start_device_log_cap requires deviceId and bundleId via session', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + deviceId: 'BBBBBBBB-1111-2222-3333-444444444444', + bundleId: 'io.sentry.TestApp', + }, + }); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'start_device_log_cap', + arguments: {}, + }); + + expectContent(result); + }); + + it('stop_device_log_cap returns error for unknown session', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'stop_device_log_cap', + arguments: { + logSessionId: 'nonexistent-device-session-id', + }, + }); + + expectContent(result); + expect(isErrorResponse(result)).toBe(true); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-scaffolding.test.ts b/src/smoke-tests/__tests__/e2e-mcp-scaffolding.test.ts new file mode 100644 index 00000000..6e489a71 --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-scaffolding.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { expectContent } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: {}, + }); +}, 30_000); + +afterAll(async () => { + await harness?.cleanup(); +}); + +describe('MCP Project Scaffolding Tools (e2e)', () => { + it('scaffold_ios_project returns content with valid args', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'scaffold_ios_project', + arguments: { + projectName: 'TestApp', + outputPath: '/tmp/test-scaffold-ios', + }, + }); + + expectContent(result); + }); + + it('scaffold_macos_project returns content with valid args', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'scaffold_macos_project', + arguments: { + projectName: 'TestMacApp', + outputPath: '/tmp/test-scaffold-macos', + }, + }); + + expectContent(result); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-sessions.test.ts b/src/smoke-tests/__tests__/e2e-mcp-sessions.test.ts new file mode 100644 index 00000000..bb5323af --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-sessions.test.ts @@ -0,0 +1,219 @@ +import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { extractText, expectContent } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + xcodebuild: { success: true, output: 'Build Succeeded' }, + 'simctl list devices': { + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { + name: 'iPhone 16 Pro', + udid: 'AAAAAAAA-1111-2222-3333-444444444444', + state: 'Shutdown', + isAvailable: true, + }, + ], + }, + }), + }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +beforeEach(async () => { + // Clear defaults before each test + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { all: true }, + }); +}); + +describe('MCP Session Management (e2e)', () => { + it('session_set_defaults stores scheme', async () => { + const result = await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { scheme: 'MyApp' }, + }); + + expect(result).toBeDefined(); + const text = extractText(result); + expect(text).toContain('scheme'); + }); + + it('session_show_defaults returns the set defaults', async () => { + // Set some defaults + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { scheme: 'TestApp', projectPath: '/path/to/project' }, + }); + + // Show defaults + const result = await harness.client.callTool({ + name: 'session_show_defaults', + arguments: {}, + }); + + const text = extractText(result); + expect(text).toContain('TestApp'); + expect(text).toContain('/path/to/project'); + }); + + it('session_clear_defaults clears all defaults', async () => { + // Set defaults + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { scheme: 'ClearMeScheme', projectPath: '/clear-me-proj' }, + }); + + // Clear all + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { all: true }, + }); + + // Show should be empty + const result = await harness.client.callTool({ + name: 'session_show_defaults', + arguments: {}, + }); + + const text = extractText(result); + // Should not contain the previously set values + expect(text).not.toContain('ClearMeScheme'); + expect(text).not.toContain('/clear-me-proj'); + }); + + it('session_clear_defaults clears specific keys', async () => { + // Set multiple defaults + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { scheme: 'KeepThis', projectPath: '/clear/this' }, + }); + + // Clear only projectPath + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { keys: ['projectPath'] }, + }); + + // Show defaults + const result = await harness.client.callTool({ + name: 'session_show_defaults', + arguments: {}, + }); + + const text = extractText(result); + expect(text).toContain('KeepThis'); + expect(text).not.toContain('/clear/this'); + }); + + it('session defaults flow into tool invocations', async () => { + // Set session defaults + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'SessionScheme', + projectPath: '/session/project.xcodeproj', + simulatorId: 'AAAAAAAA-1111-2222-3333-444444444444', + }, + }); + + // Invoke build_sim without explicit scheme/project (should use session defaults) + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'build_sim', + arguments: {}, + }); + + expectContent(result); + + // The captured commands should include the session default scheme + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + const buildCommand = commandStrs.find((c) => c.includes('xcodebuild') && c.includes('-scheme')); + expect(buildCommand).toBeDefined(); + expect(buildCommand).toContain('SessionScheme'); + }); + + it('updating session defaults changes subsequent tool behavior', async () => { + // Set initial defaults + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'FirstScheme', + projectPath: '/first/project.xcodeproj', + simulatorId: 'AAAAAAAA-1111-2222-3333-444444444444', + }, + }); + + // Update scheme via session_set_defaults + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'UpdatedScheme', + }, + }); + + // Invoke build_sim - should use the updated scheme + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'build_sim', + arguments: {}, + }); + + expect(result).toBeDefined(); + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + const buildCommand = commandStrs.find((c) => c.includes('xcodebuild') && c.includes('-scheme')); + expect(buildCommand).toBeDefined(); + expect(buildCommand).toContain('UpdatedScheme'); + }); + + it('supports namespaced defaults by switching active profile', async () => { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + profile: 'ios', + createIfNotExists: true, + scheme: 'IOSScheme', + projectPath: '/ios/project.xcodeproj', + }, + }); + + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + profile: 'watch', + createIfNotExists: true, + scheme: 'WatchScheme', + projectPath: '/watch/project.xcodeproj', + }, + }); + + const activeWatch = await harness.client.callTool({ + name: 'session_show_defaults', + arguments: {}, + }); + expect(extractText(activeWatch)).toContain('WatchScheme'); + expect(extractText(activeWatch)).not.toContain('IOSScheme'); + + await harness.client.callTool({ + name: 'session_use_defaults_profile', + arguments: { profile: 'ios' }, + }); + const activeIos = await harness.client.callTool({ + name: 'session_show_defaults', + arguments: {}, + }); + expect(extractText(activeIos)).toContain('IOSScheme'); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-simulator.test.ts b/src/smoke-tests/__tests__/e2e-mcp-simulator.test.ts new file mode 100644 index 00000000..8d433d6a --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-simulator.test.ts @@ -0,0 +1,328 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { expectContent } from '../test-helpers.ts'; + +const SIM_UUID = 'AAAAAAAA-1111-4222-A333-444444444444'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + 'simctl list devices': { + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { + name: 'iPhone 16 Pro', + udid: SIM_UUID, + state: 'Booted', + isAvailable: true, + }, + ], + }, + }), + }, + 'simctl list': { + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { + name: 'iPhone 16 Pro', + udid: SIM_UUID, + state: 'Booted', + isAvailable: true, + }, + ], + }, + }), + }, + xcodebuild: { + success: true, + output: + 'Build Succeeded\n' + + 'CODESIGNING_FOLDER_PATH = /tmp/Build/Products/Debug-iphonesimulator/MyApp.app\n' + + 'BUILT_PRODUCTS_DIR = /tmp/Build/Products/Debug-iphonesimulator\n' + + 'FULL_PRODUCT_NAME = MyApp.app\n', + }, + 'simctl boot': { success: true, output: '' }, + 'simctl io': { success: true, output: '/tmp/screenshot.png' }, + 'simctl install': { success: true, output: '' }, + 'simctl launch': { success: true, output: 'com.test.MyApp: 12345' }, + 'simctl terminate': { success: true, output: '' }, + 'simctl erase': { success: true, output: '' }, + 'simctl shutdown': { success: true, output: '' }, + 'simctl ui': { success: true, output: '' }, + 'simctl location': { success: true, output: '' }, + 'simctl status_bar': { success: true, output: '' }, + 'simctl get_app_container': { success: true, output: '/path/to/MyApp.app' }, + 'simctl recordVideo': { success: true, output: '' }, + PlistBuddy: { success: true, output: 'com.test.MyApp' }, + 'open -a Simulator': { success: true, output: '' }, + sips: { success: true, output: '' }, + 'swift -e': { success: true, output: '400,800' }, + axe: { success: true, output: '' }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +function setSimulatorSessionDefaults(): Promise { + return harness.client.callTool({ + name: 'session_set_defaults', + arguments: { + scheme: 'MyApp', + projectPath: '/path/to/MyApp.xcodeproj', + simulatorId: SIM_UUID, + bundleId: 'com.test.MyApp', + }, + }); +} + +describe('MCP Simulator Tool Invocation (e2e)', () => { + describe('simulator workflow tools', () => { + it('build_run_sim captures xcodebuild and simctl commands', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'build_run_sim', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild'))).toBe(true); + }); + + it('test_sim captures xcodebuild test command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'test_sim', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('xcodebuild'))).toBe(true); + }); + + it('launch_app_sim captures simctl launch command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'launch_app_sim', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('simctl') && c.includes('launch'))).toBe(true); + }); + + it('stop_app_sim captures simctl terminate command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'stop_app_sim', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('simctl') && c.includes('terminate'))).toBe(true); + }); + + it('install_app_sim responds with content', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'install_app_sim', + arguments: { + appPath: '/tmp/Build/Products/Debug-iphonesimulator/MyApp.app', + }, + }); + + expectContent(result); + }); + + it('open_sim captures open command', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'open_sim', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('open') && c.includes('Simulator'))).toBe(true); + }); + + it('record_sim_video responds with content', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'record_sim_video', + arguments: { + start: true, + }, + }); + + expectContent(result); + }); + + it('get_sim_app_path captures xcodebuild -showBuildSettings command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'get_sim_app_path', + arguments: { + platform: 'iOS Simulator', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some((c) => c.includes('xcodebuild') && c.includes('-showBuildSettings')), + ).toBe(true); + }); + + it('screenshot captures simctl io screenshot command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'screenshot', + arguments: { + returnFormat: 'path', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some( + (c) => c.includes('simctl') && c.includes('io') && c.includes('screenshot'), + ), + ).toBe(true); + }); + }); + + describe('simulator-management workflow tools', () => { + it('erase_sims captures simctl erase command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'erase_sims', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('simctl') && c.includes('erase'))).toBe(true); + }); + + it('set_sim_appearance captures simctl ui command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'set_sim_appearance', + arguments: { + mode: 'dark', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some((c) => c.includes('simctl') && c.includes('ui') && c.includes('dark')), + ).toBe(true); + }); + + it('set_sim_location captures simctl location set command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'set_sim_location', + arguments: { + latitude: 37.7749, + longitude: -122.4194, + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some( + (c) => c.includes('simctl') && c.includes('location') && c.includes('set'), + ), + ).toBe(true); + }); + + it('reset_sim_location captures simctl location clear command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'reset_sim_location', + arguments: {}, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some( + (c) => c.includes('simctl') && c.includes('location') && c.includes('clear'), + ), + ).toBe(true); + }); + + it('sim_statusbar captures simctl status_bar command', async () => { + await setSimulatorSessionDefaults(); + + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'sim_statusbar', + arguments: { + dataNetwork: '5g', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect( + commandStrs.some( + (c) => c.includes('simctl') && c.includes('status_bar') && c.includes('5g'), + ), + ).toBe(true); + }); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-swift-package.test.ts b/src/smoke-tests/__tests__/e2e-mcp-swift-package.test.ts new file mode 100644 index 00000000..5caecc97 --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-swift-package.test.ts @@ -0,0 +1,92 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { expectContent } from '../test-helpers.ts'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + 'swift build': { success: true, output: 'Build complete!' }, + 'swift package': { success: true, output: 'Package cleaned' }, + 'swift test': { success: true, output: 'Test Suite passed' }, + 'swift run': { success: true, output: 'Running...' }, + pgrep: { success: false, output: '' }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +describe('MCP Swift Package Tools (e2e)', () => { + it('swift_package_clean captures swift package clean command', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'swift_package_clean', + arguments: { + packagePath: '/path/to/package', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('swift') && c.includes('clean'))).toBe(true); + }); + + it('swift_package_test captures swift test command', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'swift_package_test', + arguments: { + packagePath: '/path/to/package', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('swift') && c.includes('test'))).toBe(true); + }); + + it('swift_package_run captures swift run command', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'swift_package_run', + arguments: { + packagePath: '/path/to/package', + }, + }); + + expectContent(result); + + const commandStrs = harness.capturedCommands.map((c) => c.command.join(' ')); + expect(commandStrs.some((c) => c.includes('swift') && c.includes('run'))).toBe(true); + }); + + it('swift_package_stop returns content for unknown PID', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'swift_package_stop', + arguments: { + pid: 99999, + }, + }); + + const content1 = expectContent(result); + expect(content1.some((c) => typeof c.text === 'string' && c.text.length > 0)).toBe(true); + }); + + it('swift_package_list returns content listing processes', async () => { + harness.resetCapturedCommands(); + const result = await harness.client.callTool({ + name: 'swift_package_list', + arguments: {}, + }); + + const content2 = expectContent(result); + expect(content2.some((c) => typeof c.text === 'string' && c.text.length > 0)).toBe(true); + }); +}); diff --git a/src/smoke-tests/__tests__/e2e-mcp-ui-automation.test.ts b/src/smoke-tests/__tests__/e2e-mcp-ui-automation.test.ts new file mode 100644 index 00000000..cbf4a3c2 --- /dev/null +++ b/src/smoke-tests/__tests__/e2e-mcp-ui-automation.test.ts @@ -0,0 +1,395 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createMcpTestHarness, type McpTestHarness } from '../mcp-test-harness.ts'; +import { extractText, isErrorResponse, getContent } from '../test-helpers.ts'; + +const SIM_ID = 'AAAAAAAA-1111-2222-3333-444444444444'; + +let harness: McpTestHarness; + +beforeAll(async () => { + harness = await createMcpTestHarness({ + commandResponses: { + 'axe tap': { success: true, output: 'Tap performed' }, + 'axe swipe': { success: true, output: 'Swipe performed' }, + 'axe button': { success: true, output: 'Button pressed' }, + 'axe gesture': { success: true, output: 'Gesture performed' }, + 'axe key': { success: true, output: 'Key pressed' }, + 'axe key-sequence': { success: true, output: 'Key sequence performed' }, + 'axe touch': { success: true, output: 'Touch performed' }, + 'axe type': { success: true, output: 'Type performed' }, + 'axe describe-ui': { + success: true, + output: JSON.stringify({ type: 'application', children: [] }), + }, + 'simctl io': { success: true, output: '/tmp/screenshot.png' }, + 'simctl list devices': { + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { + name: 'iPhone 16 Pro', + udid: SIM_ID, + state: 'Booted', + isAvailable: true, + }, + ], + }, + }), + }, + sips: { success: true, output: '' }, + swift: { success: true, output: '393,852' }, + }, + }); +}, 30_000); + +afterAll(async () => { + await harness.cleanup(); +}); + +async function setSimulatorDefaults(): Promise { + await harness.client.callTool({ + name: 'session_set_defaults', + arguments: { simulatorId: SIM_ID }, + }); +} + +async function clearDefaults(): Promise { + await harness.client.callTool({ + name: 'session_clear_defaults', + arguments: { all: true }, + }); +} + +describe('MCP UI Automation Tools (e2e)', () => { + describe('tap', () => { + it('responds via MCP with coordinate tap', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'tap', + arguments: { x: 100, y: 200 }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + + it('responds via MCP with element id tap', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'tap', + arguments: { id: 'myButton' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + + it('responds via MCP with element label tap', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'tap', + arguments: { label: 'Submit' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('swipe', () => { + it('responds via MCP with swipe coordinates', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'swipe', + arguments: { x1: 100, y1: 200, x2: 100, y2: 600 }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + + it('responds via MCP with optional duration and delta', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'swipe', + arguments: { x1: 50, y1: 100, x2: 50, y2: 500, duration: 0.5, delta: 10 }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('button', () => { + it('responds via MCP with home button press', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'button', + arguments: { buttonType: 'home' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + + it('responds via MCP with lock button press', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'button', + arguments: { buttonType: 'lock' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('gesture', () => { + it('responds via MCP with scroll-down gesture', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'gesture', + arguments: { preset: 'scroll-down' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + + it('responds via MCP with swipe-from-left-edge gesture', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'gesture', + arguments: { preset: 'swipe-from-left-edge' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('key_press', () => { + it('responds via MCP with key press', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'key_press', + arguments: { keyCode: 40 }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('key_sequence', () => { + it('responds via MCP with key sequence', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'key_sequence', + arguments: { keyCodes: [4, 5, 6, 7] }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('long_press', () => { + it('responds via MCP with long press', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'long_press', + arguments: { x: 150, y: 300, duration: 500 }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('screenshot', () => { + it('responds via MCP with screenshot capture', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'screenshot', + arguments: {}, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + + it('responds via MCP with path return format', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'screenshot', + arguments: { returnFormat: 'path' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('snapshot_ui', () => { + it('responds via MCP with UI hierarchy', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'snapshot_ui', + arguments: {}, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('touch', () => { + it('responds via MCP with touch down+up', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'touch', + arguments: { x: 200, y: 400, down: true, up: true }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + + it('responds via MCP with touch down only', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'touch', + arguments: { x: 200, y: 400, down: true }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('type_text', () => { + it('responds via MCP with text typing', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'type_text', + arguments: { text: 'Hello World' }, + }); + + const content = getContent(result); + expect(content.length).toBeGreaterThan(0); + }); + }); + + describe('error paths', () => { + it('returns error when simulatorId session default is missing', async () => { + await clearDefaults(); + + const result = await harness.client.callTool({ + name: 'tap', + arguments: { x: 100, y: 200 }, + }); + + expect(isErrorResponse(result)).toBe(true); + const text = extractText(result); + expect(text.toLowerCase()).toMatch(/simulatorid|required|missing|provide/); + }); + + it('returns error for touch with neither down nor up', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'touch', + arguments: { x: 100, y: 200 }, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + + it('returns error for tap with no target specified', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'tap', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + + it('returns error for type_text with empty text', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'type_text', + arguments: { text: '' }, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + + it('returns error for key_sequence with empty keyCodes array', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'key_sequence', + arguments: { keyCodes: [] }, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + + it('returns error for swipe with missing coordinates', async () => { + await setSimulatorDefaults(); + harness.resetCapturedCommands(); + + const result = await harness.client.callTool({ + name: 'swipe', + arguments: {}, + }); + + expect(isErrorResponse(result)).toBe(true); + }); + }); +}); diff --git a/src/smoke-tests/mcp-test-harness.ts b/src/smoke-tests/mcp-test-harness.ts new file mode 100644 index 00000000..25c1f549 --- /dev/null +++ b/src/smoke-tests/mcp-test-harness.ts @@ -0,0 +1,230 @@ +import { existsSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import type { ChildProcess } from 'node:child_process'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; +import type { CommandExecutor, CommandResponse } from '../utils/CommandExecutor.ts'; +import { + __setTestCommandExecutorOverride, + __setTestFileSystemExecutorOverride, + __clearTestExecutorOverrides, +} from '../utils/command.ts'; +import { + __resetConfigStoreForTests, + initConfigStore, + type RuntimeConfigOverrides, +} from '../utils/config-store.ts'; +import { __resetServerStateForTests } from '../server/server-state.ts'; +import { __resetToolRegistryForTests } from '../utils/tool-registry.ts'; +import { createMockFileSystemExecutor } from '../test-utils/mock-executors.ts'; +import { createServer } from '../server/server.ts'; +import { bootstrapServer } from '../server/bootstrap.ts'; +import { sessionStore } from '../utils/session-store.ts'; +import { + __setTestDebuggerToolContextOverride, + __clearTestDebuggerToolContextOverride, + DebuggerManager, +} from '../utils/debugger/index.ts'; +import { getPackageRoot } from '../core/manifest/load-manifest.ts'; +import { shutdownXcodeToolsBridge } from '../integrations/xcode-tools-bridge/index.ts'; + +export interface CapturedCommand { + command: string[]; + logPrefix?: string; + useShell?: boolean; + opts?: { env?: Record; cwd?: string }; + detached?: boolean; +} + +export interface McpTestHarness { + client: Client; + capturedCommands: CapturedCommand[]; + resetCapturedCommands(): void; + cleanup(): Promise; +} + +export interface McpTestHarnessOptions { + enabledWorkflows?: string[]; + commandResponses?: Record; +} + +const defaultCommandResponse: CommandResponse = { + success: true, + output: '', + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + process: { pid: 99999 } as ChildProcess, + exitCode: 0, +}; + +export async function createMcpTestHarness(opts?: McpTestHarnessOptions): Promise { + const capturedCommands: CapturedCommand[] = []; + + const capturingExecutor: CommandExecutor = async ( + command, + logPrefix, + useShell, + execOpts, + detached, + ) => { + capturedCommands.push({ command, logPrefix, useShell, opts: execOpts, detached }); + + if (opts?.commandResponses) { + const commandStr = command.join(' '); + const sorted = Object.entries(opts.commandResponses).sort(([a], [b]) => b.length - a.length); + for (const [pattern, response] of sorted) { + if (commandStr.includes(pattern)) { + return { + ...defaultCommandResponse, + ...response, + exitCode: response.success ? 0 : 1, + }; + } + } + } + + return defaultCommandResponse; + }; + + // Reset all singletons + __resetConfigStoreForTests(); + __resetServerStateForTests(); + __resetToolRegistryForTests(); + sessionStore.clear(); + + const mockFs = createMockFileSystemExecutor(); + + // Set executor overrides on the vitest-resolved source modules + __setTestCommandExecutorOverride(capturingExecutor); + __setTestFileSystemExecutorOverride(mockFs); + + // Also set overrides on the built module instances (used by dynamically imported tool handlers) + const buildRoot = resolve(getPackageRoot(), 'build'); + if (!existsSync(buildRoot)) { + throw new Error( + `Build directory not found at ${buildRoot}. Run "npm run build" before running smoke tests.`, + ); + } + + // Dynamic imports required: built modules are separate JS instances that must be patched independently of vitest-resolved source modules. + const builtCommandModule = (await import( + pathToFileURL(resolve(buildRoot, 'utils/command.js')).href + )) as { + __setTestCommandExecutorOverride: typeof __setTestCommandExecutorOverride; + __setTestFileSystemExecutorOverride: typeof __setTestFileSystemExecutorOverride; + __clearTestExecutorOverrides: typeof __clearTestExecutorOverrides; + }; + builtCommandModule.__setTestCommandExecutorOverride(capturingExecutor); + builtCommandModule.__setTestFileSystemExecutorOverride(mockFs); + + // Set debugger tool context override (source module) + __setTestDebuggerToolContextOverride({ + executor: capturingExecutor, + debugger: new DebuggerManager(), + }); + + // Set debugger tool context override (built module) + const builtDebuggerModule = (await import( + pathToFileURL(resolve(buildRoot, 'utils/debugger/tool-context.js')).href + )) as { + __setTestDebuggerToolContextOverride: typeof __setTestDebuggerToolContextOverride; + __clearTestDebuggerToolContextOverride: typeof __clearTestDebuggerToolContextOverride; + }; + builtDebuggerModule.__setTestDebuggerToolContextOverride({ + executor: capturingExecutor, + debugger: new DebuggerManager(), + }); + + // Initialize the built config-store module (separate singleton from source module). + // Tool modules loaded from build/ use this config store for schema resolution + // (e.g. session-aware vs legacy schema selection) and requirement validation. + const builtConfigStoreModule = (await import( + pathToFileURL(resolve(buildRoot, 'utils/config-store.js')).href + )) as { + __resetConfigStoreForTests: typeof __resetConfigStoreForTests; + initConfigStore: typeof initConfigStore; + }; + builtConfigStoreModule.__resetConfigStoreForTests(); + await builtConfigStoreModule.initConfigStore({ + cwd: '/test', + fs: mockFs, + overrides: { + debug: true, + disableXcodeAutoSync: true, + } satisfies RuntimeConfigOverrides, + }); + + // Import the built session-store module (separate singleton from source module). + // Session-aware tool handlers in build/ read/write defaults via this store. + const builtSessionStoreModule = (await import( + pathToFileURL(resolve(buildRoot, 'utils/session-store.js')).href + )) as { + sessionStore: typeof sessionStore; + }; + builtSessionStoreModule.sessionStore.clear(); + + // Create server (uses the real createServer + manifest system) + const server = createServer(); + + // Bootstrap with workflows enabled for maximum coverage. + // xcode-ide is excluded: it connects to the real Xcode tools bridge MCP + // server which triggers system permission prompts and requires Xcode. + const allWorkflows = opts?.enabledWorkflows ?? [ + 'simulator', + 'simulator-management', + 'device', + 'macos', + 'project-discovery', + 'project-scaffolding', + 'session-management', + 'swift-package', + 'logging', + 'debugging', + 'ui-automation', + 'utilities', + 'workflow-discovery', + 'doctor', + ]; + + await bootstrapServer(server, { + enabledWorkflows: allWorkflows, + configOverrides: { + debug: true, + disableXcodeAutoSync: true, + }, + fileSystemExecutor: mockFs, + }); + + // Create InMemoryTransport linked pair + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + // Connect server to one end + await server.connect(serverTransport); + + // Create and connect client to the other end + const client = new Client({ name: 'e2e-test-client', version: '1.0.0' }); + await client.connect(clientTransport); + + return { + client, + capturedCommands, + resetCapturedCommands(): void { + capturedCommands.length = 0; + }, + async cleanup(): Promise { + await client.close(); + await server.close(); + await shutdownXcodeToolsBridge(); + __clearTestExecutorOverrides(); + builtCommandModule.__clearTestExecutorOverrides(); + __clearTestDebuggerToolContextOverride(); + builtDebuggerModule.__clearTestDebuggerToolContextOverride(); + __resetConfigStoreForTests(); + builtConfigStoreModule.__resetConfigStoreForTests(); + __resetServerStateForTests(); + __resetToolRegistryForTests(); + sessionStore.clear(); + builtSessionStoreModule.sessionStore.clear(); + }, + }; +} diff --git a/src/smoke-tests/test-helpers.ts b/src/smoke-tests/test-helpers.ts new file mode 100644 index 00000000..db9ac27d --- /dev/null +++ b/src/smoke-tests/test-helpers.ts @@ -0,0 +1,33 @@ +export function extractText(result: unknown): string { + const r = result as { content?: Array<{ text?: string }> }; + if (!r.content || !Array.isArray(r.content)) return ''; + return r.content.map((c) => c.text ?? '').join('\n'); +} + +export function isErrorResponse(result: unknown): boolean { + const r = result as { isError?: boolean; content?: Array<{ text?: string }> }; + if (r.isError) return true; + if (!r.content || !Array.isArray(r.content)) return false; + return r.content.some( + (c) => + typeof c.text === 'string' && + (c.text.toLowerCase().includes('error') || + c.text.toLowerCase().includes('required') || + c.text.toLowerCase().includes('missing') || + c.text.toLowerCase().includes('must provide') || + c.text.toLowerCase().includes('fail')), + ); +} + +export function getContent(result: unknown): Array<{ type?: string; text?: string }> { + const r = result as { content?: Array<{ type?: string; text?: string }> }; + return Array.isArray(r.content) ? r.content : []; +} + +export function expectContent(result: unknown): Array<{ type?: string; text?: string }> { + const content = getContent(result); + if (content.length === 0) { + throw new Error('Expected result to have non-empty content'); + } + return content; +} diff --git a/src/test-utils/mock-executors.ts b/src/test-utils/mock-executors.ts index bbe20112..03b09000 100644 --- a/src/test-utils/mock-executors.ts +++ b/src/test-utils/mock-executors.ts @@ -16,8 +16,28 @@ */ import { ChildProcess } from 'child_process'; -import { CommandExecutor } from '../utils/CommandExecutor.ts'; -import { FileSystemExecutor } from '../utils/FileSystemExecutor.ts'; +import type { WriteStream } from 'fs'; +import { EventEmitter } from 'node:events'; +import { PassThrough } from 'node:stream'; +import type { CommandExecutor, CommandResponse } from '../utils/CommandExecutor.ts'; +import type { FileSystemExecutor } from '../utils/FileSystemExecutor.ts'; +import type { InteractiveProcess, InteractiveSpawner } from '../utils/execution/index.ts'; + +export type { CommandExecutor, FileSystemExecutor }; + +export const mockProcess = { pid: 12345 } as unknown as ChildProcess; + +export function createMockCommandResponse( + overrides: Partial = {}, +): CommandResponse { + return { + success: overrides.success ?? true, + output: overrides.output ?? '', + error: overrides.error, + process: overrides.process ?? mockProcess, + exitCode: overrides.exitCode ?? (overrides.success === false ? 1 : 0), + }; +} /** * Create a mock executor for testing @@ -33,6 +53,13 @@ export function createMockExecutor( process?: unknown; exitCode?: number; shouldThrow?: Error; + onExecute?: ( + command: string[], + logPrefix?: string, + useShell?: boolean, + opts?: { env?: Record; cwd?: string }, + detached?: boolean, + ) => void; } | Error | string, @@ -65,13 +92,20 @@ export function createMockExecutor( spawnfile: 'sh', } as unknown as ChildProcess; - return async () => ({ - success: result.success ?? true, - output: result.output ?? '', - error: result.error, - process: (result.process ?? mockProcess) as ChildProcess, - exitCode: result.exitCode ?? (result.success === false ? 1 : 0), - }); + return async (command, logPrefix, useShell, opts, detached) => { + // Call onExecute callback if provided + if (result.onExecute) { + result.onExecute(command, logPrefix, useShell, opts, detached); + } + + return { + success: result.success ?? true, + output: result.output ?? '', + error: result.error, + process: (result.process ?? mockProcess) as ChildProcess, + exitCode: result.exitCode ?? (result.success === false ? 1 : 0), + }; + }; } /** @@ -159,21 +193,132 @@ export function createCommandMatchingMockExecutor( }; } +export type MockInteractiveSession = { + stdout: PassThrough; + stderr: PassThrough; + stdin: PassThrough; + emitExit: (code?: number | null, signal?: NodeJS.Signals | null) => void; + emitError: (error: Error) => void; +}; + +export type MockInteractiveSpawnerScript = { + onSpawn?: (session: MockInteractiveSession) => void; + onWrite?: (data: string, session: MockInteractiveSession) => void; + onKill?: (signal: NodeJS.Signals | undefined, session: MockInteractiveSession) => void; + onDispose?: (session: MockInteractiveSession) => void; +}; + +export function createMockInteractiveSpawner( + script: MockInteractiveSpawnerScript = {}, +): InteractiveSpawner { + return (): InteractiveProcess => { + const stdout = new PassThrough(); + const stderr = new PassThrough(); + const stdin = new PassThrough(); + const emitter = new EventEmitter(); + const mockProcess = emitter as unknown as ChildProcess; + const mutableProcess = mockProcess as unknown as { + stdout: PassThrough | null; + stderr: PassThrough | null; + stdin: PassThrough | null; + killed: boolean; + exitCode: number | null; + signalCode: NodeJS.Signals | null; + spawnargs: string[]; + spawnfile: string; + pid: number; + }; + + mutableProcess.stdout = stdout; + mutableProcess.stderr = stderr; + mutableProcess.stdin = stdin; + mutableProcess.killed = false; + mutableProcess.exitCode = null; + mutableProcess.signalCode = null; + mutableProcess.spawnargs = []; + mutableProcess.spawnfile = 'mock'; + mutableProcess.pid = 12345; + mockProcess.kill = ((signal?: NodeJS.Signals): boolean => { + mutableProcess.killed = true; + emitter.emit('exit', 0, signal ?? null); + return true; + }) as ChildProcess['kill']; + + const session: MockInteractiveSession = { + stdout, + stderr, + stdin, + emitExit: (code = 0, signal = null) => { + emitter.emit('exit', code, signal); + }, + emitError: (error) => { + emitter.emit('error', error); + }, + }; + + script.onSpawn?.(session); + + let disposed = false; + + return { + process: mockProcess, + write(data: string): void { + if (disposed) { + throw new Error('Mock interactive process disposed'); + } + script.onWrite?.(data, session); + }, + kill(signal?: NodeJS.Signals): void { + if (disposed) return; + mutableProcess.killed = true; + script.onKill?.(signal, session); + emitter.emit('exit', 0, signal ?? null); + }, + dispose(): void { + if (disposed) return; + disposed = true; + script.onDispose?.(session); + stdout.end(); + stderr.end(); + stdin.end(); + emitter.removeAllListeners(); + }, + }; + }; +} + /** * Create a mock file system executor for testing */ export function createMockFileSystemExecutor( overrides?: Partial, ): FileSystemExecutor { + const mockWriteStream = ((): WriteStream => { + const emitter = new EventEmitter(); + const stream = Object.assign(emitter, { + destroyed: false, + write: () => true, + end: () => { + stream.destroyed = true; + emitter.emit('close'); + }, + }) as unknown as WriteStream; + return stream; + })(); + return { mkdir: async (): Promise => {}, readFile: async (): Promise => 'mock file content', writeFile: async (): Promise => {}, + createWriteStream: () => mockWriteStream, cp: async (): Promise => {}, readdir: async (): Promise => [], rm: async (): Promise => {}, existsSync: (): boolean => false, - stat: async (): Promise<{ isDirectory(): boolean }> => ({ isDirectory: (): boolean => true }), + stat: async (): Promise<{ isDirectory(): boolean; mtimeMs: number }> => ({ + isDirectory: (): boolean => true, + mtimeMs: Date.now(), + }), mkdtemp: async (): Promise => '/tmp/mock-temp-123456', tmpdir: (): string => '/tmp', ...overrides, @@ -211,6 +356,14 @@ export function createNoopFileSystemExecutor(): FileSystemExecutor { `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, ); }, + createWriteStream: (): WriteStream => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, cp: async (): Promise => { throw new Error( `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + @@ -243,7 +396,7 @@ export function createNoopFileSystemExecutor(): FileSystemExecutor { `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, ); }, - stat: async (): Promise<{ isDirectory(): boolean }> => { + stat: async (): Promise<{ isDirectory(): boolean; mtimeMs: number }> => { throw new Error( `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + `This executor should never be called in this test context.\n` + diff --git a/src/types/common.ts b/src/types/common.ts index 96ff1914..8d49bccf 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -12,6 +12,34 @@ * - Supporting error handling with standardized error response types */ +/** + * Represents a suggested next step that can be rendered for CLI or MCP. + */ +export interface NextStep { + /** Optional MCP tool name (e.g., "boot_sim") */ + tool?: string; + /** CLI tool name (kebab-case, disambiguated) */ + cliTool?: string; + /** Workflow name for CLI grouping (e.g., "simulator") */ + workflow?: string; + /** Human-readable description of the action (optional when manifest template provides it) */ + label?: string; + /** Optional parameters to pass to the tool */ + params?: Record; + /** Optional ordering hint for merged steps */ + priority?: number; +} + +export type NextStepParams = Record; +export type NextStepParamsMap = Record; + +/** + * Output style controls verbosity of tool responses. + * - 'normal': Full output including next steps + * - 'minimal': Essential result only, no next steps + */ +export type OutputStyle = 'normal' | 'minimal'; + /** * Enum representing Xcode build platforms. */ @@ -35,6 +63,10 @@ export interface ToolResponse { content: ToolResponseContent[]; isError?: boolean; _meta?: Record; + /** Structured next steps that get rendered differently for CLI vs MCP */ + nextSteps?: NextStep[]; + /** Dynamic params for manifest nextSteps keyed by toolId */ + nextStepParams?: NextStepParamsMap; [key: string]: unknown; // Index signature to match CallToolResult } diff --git a/src/utils/CommandExecutor.ts b/src/utils/CommandExecutor.ts index 177c5cad..94e65b0d 100644 --- a/src/utils/CommandExecutor.ts +++ b/src/utils/CommandExecutor.ts @@ -1,5 +1,8 @@ import { ChildProcess } from 'child_process'; +// Runtime marker to prevent empty output in unbundled builds +export const _typeModule = true as const; + export interface CommandExecOptions { env?: Record; cwd?: string; @@ -8,6 +11,10 @@ export interface CommandExecOptions { /** * Command executor function type for dependency injection */ +/** + * NOTE: `detached` only changes when the promise resolves; it does not detach/unref + * the OS process. Callers must still manage lifecycle and open streams. + */ export type CommandExecutor = ( command: string[], logPrefix?: string, diff --git a/src/utils/FileSystemExecutor.ts b/src/utils/FileSystemExecutor.ts index 5c91258c..4baf499c 100644 --- a/src/utils/FileSystemExecutor.ts +++ b/src/utils/FileSystemExecutor.ts @@ -2,15 +2,21 @@ * File system executor interface for dependency injection */ +import type { WriteStream } from 'fs'; + +// Runtime marker to prevent empty output in unbundled builds +export const _typeModule = true as const; + export interface FileSystemExecutor { mkdir(path: string, options?: { recursive?: boolean }): Promise; readFile(path: string, encoding?: BufferEncoding): Promise; writeFile(path: string, content: string, encoding?: BufferEncoding): Promise; + createWriteStream(path: string, options?: { flags?: string }): WriteStream; cp(source: string, destination: string, options?: { recursive?: boolean }): Promise; readdir(path: string, options?: { withFileTypes?: boolean }): Promise; rm(path: string, options?: { recursive?: boolean; force?: boolean }): Promise; existsSync(path: string): boolean; - stat(path: string): Promise<{ isDirectory(): boolean }>; + stat(path: string): Promise<{ isDirectory(): boolean; mtimeMs: number }>; mkdtemp(prefix: string): Promise; tmpdir(): string; } diff --git a/src/utils/__tests__/axe-helpers.test.ts b/src/utils/__tests__/axe-helpers.test.ts new file mode 100644 index 00000000..468a48e4 --- /dev/null +++ b/src/utils/__tests__/axe-helpers.test.ts @@ -0,0 +1,68 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { chmodSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { getBundledAxeEnvironment } from '../axe-helpers.ts'; +import { resetResourceRootCacheForTests } from '../../core/resource-root.ts'; + +describe('axe-helpers', () => { + let originalResourceRoot: string | undefined; + let originalDyldFrameworkPath: string | undefined; + let tempDir: string; + + beforeEach(() => { + originalResourceRoot = process.env.XCODEBUILDMCP_RESOURCE_ROOT; + originalDyldFrameworkPath = process.env.DYLD_FRAMEWORK_PATH; + tempDir = mkdtempSync(join(tmpdir(), 'xbmcp-axe-helpers-')); + resetResourceRootCacheForTests(); + }); + + afterEach(() => { + if (originalResourceRoot === undefined) { + delete process.env.XCODEBUILDMCP_RESOURCE_ROOT; + } else { + process.env.XCODEBUILDMCP_RESOURCE_ROOT = originalResourceRoot; + } + + if (originalDyldFrameworkPath === undefined) { + delete process.env.DYLD_FRAMEWORK_PATH; + } else { + process.env.DYLD_FRAMEWORK_PATH = originalDyldFrameworkPath; + } + + rmSync(tempDir, { recursive: true, force: true }); + resetResourceRootCacheForTests(); + }); + + it('returns DYLD_FRAMEWORK_PATH when bundled axe is resolved from resource root', () => { + const resourceRoot = join(tempDir, 'portable-root'); + const axePath = join(resourceRoot, 'bundled', 'axe'); + const frameworksDir = join(resourceRoot, 'bundled', 'Frameworks'); + mkdirSync(frameworksDir, { recursive: true }); + writeFileSync(axePath, ''); + chmodSync(axePath, 0o755); + process.env.XCODEBUILDMCP_RESOURCE_ROOT = resourceRoot; + delete process.env.DYLD_FRAMEWORK_PATH; + + const env = getBundledAxeEnvironment(); + expect(env).toEqual({ + DYLD_FRAMEWORK_PATH: frameworksDir, + }); + }); + + it('preserves existing DYLD_FRAMEWORK_PATH entries when using bundled axe', () => { + const resourceRoot = join(tempDir, 'portable-root'); + const axePath = join(resourceRoot, 'bundled', 'axe'); + const frameworksDir = join(resourceRoot, 'bundled', 'Frameworks'); + mkdirSync(frameworksDir, { recursive: true }); + writeFileSync(axePath, ''); + chmodSync(axePath, 0o755); + process.env.XCODEBUILDMCP_RESOURCE_ROOT = resourceRoot; + process.env.DYLD_FRAMEWORK_PATH = '/existing/frameworks'; + + const env = getBundledAxeEnvironment(); + expect(env).toEqual({ + DYLD_FRAMEWORK_PATH: `${frameworksDir}:/existing/frameworks`, + }); + }); +}); diff --git a/src/utils/__tests__/build-utils-suppress-warnings.test.ts b/src/utils/__tests__/build-utils-suppress-warnings.test.ts index 61dd69a8..1eac6002 100644 --- a/src/utils/__tests__/build-utils-suppress-warnings.test.ts +++ b/src/utils/__tests__/build-utils-suppress-warnings.test.ts @@ -75,4 +75,96 @@ describe('executeXcodeBuildCommand - suppressWarnings', () => { expect(textContent).not.toContain('⚠️ Warning:'); expect(textContent).toContain('❌ Error:'); }); + + it('should not flag source code lines containing "error" or "warning" as diagnostics', async () => { + sessionStore.setDefaults({ suppressWarnings: false }); + + // Swift source lines echoed during compilation that contain "error"/"warning" as substrings + const buildOutput = [ + ' var authError: Error?', + ' private(set) var error: WalletError?', + ' private(set) var lastError: Error?', + ' var loadError: Error?', + ' private(set) var error: String?', + ' var warningCount: Int = 0', + ' let isWarning: Bool', + ' fatalError("unexpected state")', + ].join('\n'); + + const mockExecutor = createMockExecutor({ + success: true, + output: buildOutput, + error: '', + exitCode: 0, + }); + + const result = await executeXcodeBuildCommand( + { + projectPath: '/test/project.xcodeproj', + scheme: 'TestScheme', + configuration: 'Debug', + }, + { + platform: XcodePlatform.macOS, + logPrefix: 'Test', + }, + false, + 'build', + mockExecutor, + ); + + expect(result.content).toBeDefined(); + const textContent = result.content + ?.filter((c) => c.type === 'text') + .map((c) => (c as { text: string }).text) + .join('\n'); + expect(textContent).not.toContain('❌ Error:'); + expect(textContent).not.toContain('⚠️ Warning:'); + }); + + it('should match real xcodebuild diagnostic lines', async () => { + sessionStore.setDefaults({ suppressWarnings: false }); + + const buildOutput = [ + "/path/to/File.swift:42:10: error: cannot find 'foo' in scope", + "/path/to/File.swift:15:5: warning: unused variable 'bar'", + 'error: build failed', + 'warning: deprecated API usage', + 'ld: warning: directory not found for option', + 'clang: error: linker command failed', + 'xcode-select: error: tool xcodebuild requires Xcode', + 'fatal error: too many errors emitted', + "/path/to/header.h:1:9: fatal error: 'Header.h' file not found", + ].join('\n'); + + const mockExecutor = createMockExecutor({ + success: true, + output: buildOutput, + error: '', + exitCode: 0, + }); + + const result = await executeXcodeBuildCommand( + { + projectPath: '/test/project.xcodeproj', + scheme: 'TestScheme', + configuration: 'Debug', + }, + { + platform: XcodePlatform.macOS, + logPrefix: 'Test', + }, + false, + 'build', + mockExecutor, + ); + + expect(result.content).toBeDefined(); + const textContent = result.content + ?.filter((c) => c.type === 'text') + .map((c) => (c as { text: string }).text) + .join('\n'); + expect(textContent).toContain('❌ Error:'); + expect(textContent).toContain('⚠️ Warning:'); + }); }); diff --git a/src/utils/__tests__/build-utils.test.ts b/src/utils/__tests__/build-utils.test.ts index 9364e76a..a7fd73e9 100644 --- a/src/utils/__tests__/build-utils.test.ts +++ b/src/utils/__tests__/build-utils.test.ts @@ -3,6 +3,7 @@ */ import { describe, it, expect } from 'vitest'; +import path from 'node:path'; import { createMockExecutor } from '../../test-utils/mock-executors.ts'; import { executeXcodeBuildCommand } from '../build-utils.ts'; import { XcodePlatform } from '../xcode.ts'; @@ -260,4 +261,131 @@ describe('build-utils Sentry Classification', () => { expect(result.content[0].text).toContain('❌ [stderr] Some error without exit code'); }); }); + + describe('Working Directory (cwd) Handling', () => { + it('should pass project directory as cwd for workspace builds', async () => { + let capturedOptions: any; + const mockExecutor = createMockExecutor({ + success: true, + output: 'BUILD SUCCEEDED', + exitCode: 0, + onExecute: (_command, _logPrefix, _useShell, opts) => { + capturedOptions = opts; + }, + }); + + await executeXcodeBuildCommand( + { + scheme: 'TestScheme', + configuration: 'Debug', + workspacePath: '/path/to/project/MyProject.xcworkspace', + }, + mockPlatformOptions, + false, + 'build', + mockExecutor, + ); + + expect(capturedOptions).toBeDefined(); + expect(capturedOptions.cwd).toBe('/path/to/project'); + }); + + it('should pass project directory as cwd for project builds', async () => { + let capturedOptions: any; + const mockExecutor = createMockExecutor({ + success: true, + output: 'BUILD SUCCEEDED', + exitCode: 0, + onExecute: (_command, _logPrefix, _useShell, opts) => { + capturedOptions = opts; + }, + }); + + await executeXcodeBuildCommand( + { + scheme: 'TestScheme', + configuration: 'Debug', + projectPath: '/path/to/project/MyProject.xcodeproj', + }, + mockPlatformOptions, + false, + 'build', + mockExecutor, + ); + + expect(capturedOptions).toBeDefined(); + expect(capturedOptions.cwd).toBe('/path/to/project'); + }); + + it('should merge cwd with existing execOpts', async () => { + let capturedOptions: any; + const mockExecutor = createMockExecutor({ + success: true, + output: 'BUILD SUCCEEDED', + exitCode: 0, + onExecute: (_command, _logPrefix, _useShell, opts) => { + capturedOptions = opts; + }, + }); + + await executeXcodeBuildCommand( + { + scheme: 'TestScheme', + configuration: 'Debug', + workspacePath: '/path/to/project/MyProject.xcworkspace', + }, + mockPlatformOptions, + false, + 'build', + mockExecutor, + { env: { CUSTOM_VAR: 'value' } }, + ); + + expect(capturedOptions).toBeDefined(); + expect(capturedOptions.cwd).toBe('/path/to/project'); + expect(capturedOptions.env).toEqual({ CUSTOM_VAR: 'value' }); + }); + + it('should resolve relative project and derived data paths before execution', async () => { + let capturedOptions: unknown; + let capturedCommand: string[] | undefined; + const mockExecutor = createMockExecutor({ + success: true, + output: 'BUILD SUCCEEDED', + exitCode: 0, + onExecute: (command, _logPrefix, _useShell, opts) => { + capturedCommand = command; + capturedOptions = opts; + }, + }); + + const relativeProjectPath = 'example_projects/iOS/MCPTest.xcodeproj'; + const relativeDerivedDataPath = '.derivedData/e2e'; + const expectedProjectPath = path.resolve(relativeProjectPath); + const expectedDerivedDataPath = path.resolve(relativeDerivedDataPath); + + await executeXcodeBuildCommand( + { + scheme: 'TestScheme', + configuration: 'Debug', + projectPath: relativeProjectPath, + derivedDataPath: relativeDerivedDataPath, + }, + { + platform: XcodePlatform.iOSSimulator, + simulatorName: 'iPhone 17 Pro', + useLatestOS: true, + logPrefix: 'iOS Simulator Build', + }, + false, + 'build', + mockExecutor, + ); + + expect(capturedCommand).toBeDefined(); + expect(capturedCommand).toContain(expectedProjectPath); + expect(capturedCommand).toContain(expectedDerivedDataPath); + expect(capturedOptions).toEqual({ cwd: path.dirname(expectedProjectPath) }); + }); + }); }); diff --git a/src/utils/__tests__/config-store.test.ts b/src/utils/__tests__/config-store.test.ts new file mode 100644 index 00000000..90ceac4f --- /dev/null +++ b/src/utils/__tests__/config-store.test.ts @@ -0,0 +1,248 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import path from 'node:path'; +import { createMockFileSystemExecutor } from '../../test-utils/mock-executors.ts'; +import { + __resetConfigStoreForTests, + getConfig, + initConfigStore, + persistActiveSessionDefaultsProfile, + persistSessionDefaultsPatch, +} from '../config-store.ts'; + +const cwd = '/repo'; +const configPath = path.join(cwd, '.xcodebuildmcp', 'config.yaml'); + +describe('config-store', () => { + beforeEach(() => { + __resetConfigStoreForTests(); + }); + + function createFs(readFile?: string) { + return createMockFileSystemExecutor({ + existsSync: (targetPath) => targetPath === configPath && readFile != null, + readFile: async (targetPath) => { + if (targetPath !== configPath) { + throw new Error(`Unexpected readFile path: ${targetPath}`); + } + if (readFile == null) { + throw new Error('readFile called without fixture content'); + } + return readFile; + }, + }); + } + + it('uses defaults when config is missing and overrides are not provided', async () => { + await initConfigStore({ cwd, fs: createFs() }); + + const config = getConfig(); + expect(config.debug).toBe(false); + expect(config.incrementalBuildsEnabled).toBe(false); + expect(config.dapRequestTimeoutMs).toBe(30000); + expect(config.dapLogEvents).toBe(false); + expect(config.launchJsonWaitMs).toBe(8000); + }); + + it('parses env values when provided', async () => { + const env = { + XCODEBUILDMCP_DEBUG: 'true', + INCREMENTAL_BUILDS_ENABLED: '1', + XCODEBUILDMCP_DAP_REQUEST_TIMEOUT_MS: '12345', + XCODEBUILDMCP_DAP_LOG_EVENTS: 'true', + XBMCP_LAUNCH_JSON_WAIT_MS: '9000', + XCODEBUILDMCP_ENABLED_WORKFLOWS: 'simulator,logging', + XCODEBUILDMCP_UI_DEBUGGER_GUARD_MODE: 'warn', + XCODEBUILDMCP_DEBUGGER_BACKEND: 'lldb', + }; + + await initConfigStore({ cwd, fs: createFs(), env }); + + const config = getConfig(); + expect(config.debug).toBe(true); + expect(config.incrementalBuildsEnabled).toBe(true); + expect(config.dapRequestTimeoutMs).toBe(12345); + expect(config.dapLogEvents).toBe(true); + expect(config.launchJsonWaitMs).toBe(9000); + expect(config.enabledWorkflows).toEqual(['simulator', 'logging']); + expect(config.uiDebuggerGuardMode).toBe('warn'); + expect(config.debuggerBackend).toBe('lldb-cli'); + }); + + it('prefers overrides over config file values and config over env', async () => { + const yaml = ['schemaVersion: 1', 'debug: false', 'dapRequestTimeoutMs: 4000', ''].join('\n'); + const env = { + XCODEBUILDMCP_DEBUG: 'true', + XCODEBUILDMCP_DAP_REQUEST_TIMEOUT_MS: '999', + }; + + await initConfigStore({ + cwd, + fs: createFs(yaml), + overrides: { debug: true, dapRequestTimeoutMs: 12345 }, + env, + }); + + const config = getConfig(); + expect(config.debug).toBe(true); + expect(config.dapRequestTimeoutMs).toBe(12345); + }); + + it('resolves enabledWorkflows from overrides, config, then defaults', async () => { + const yamlWithoutWorkflows = ['schemaVersion: 1', 'debug: false', ''].join('\n'); + + await initConfigStore({ cwd, fs: createFs(yamlWithoutWorkflows) }); + + const config = getConfig(); + expect(config.enabledWorkflows).toEqual([]); + + const yamlWithExplicitEmpty = ['schemaVersion: 1', 'enabledWorkflows: []', ''].join('\n'); + + await initConfigStore({ cwd, fs: createFs(yamlWithExplicitEmpty) }); + + const explicitEmpty = getConfig(); + expect(explicitEmpty.enabledWorkflows).toEqual([]); + + await initConfigStore({ + cwd, + fs: createFs(yamlWithExplicitEmpty), + overrides: { enabledWorkflows: ['device'] }, + }); + + const updated = getConfig(); + expect(updated.enabledWorkflows).toEqual(['device']); + }); + + it('merges namespaced session defaults profiles from file and overrides', async () => { + const yaml = [ + 'schemaVersion: 1', + 'activeSessionDefaultsProfile: "ios"', + 'sessionDefaultsProfiles:', + ' ios:', + ' scheme: "FromFile"', + ' workspacePath: "./App.xcworkspace"', + '', + ].join('\n'); + + await initConfigStore({ + cwd, + fs: createFs(yaml), + overrides: { + sessionDefaultsProfiles: { + ios: { simulatorName: 'iPhone 16' }, + watch: { scheme: 'WatchScheme' }, + }, + }, + }); + + const config = getConfig(); + expect(config.activeSessionDefaultsProfile).toBe('ios'); + expect(config.sessionDefaultsProfiles?.ios?.scheme).toBe('FromFile'); + expect(config.sessionDefaultsProfiles?.ios?.simulatorName).toBe('iPhone 16'); + expect(config.sessionDefaultsProfiles?.watch?.scheme).toBe('WatchScheme'); + }); + + it('persists active profile selection and updates resolved config', async () => { + const writes: { path: string; content: string }[] = []; + const fs = createMockFileSystemExecutor({ + existsSync: () => true, + readFile: async () => 'schemaVersion: 1\n', + writeFile: async (targetPath, content) => { + writes.push({ path: targetPath, content }); + }, + }); + + await initConfigStore({ cwd, fs }); + await persistActiveSessionDefaultsProfile('ios'); + + expect(getConfig().activeSessionDefaultsProfile).toBe('ios'); + expect(writes).toHaveLength(1); + }); + + it('normalizes profile names for persisted defaults patch and resolved config', async () => { + const writes: { path: string; content: string }[] = []; + let persistedYaml = 'schemaVersion: 1\n'; + const fs = createMockFileSystemExecutor({ + existsSync: () => true, + readFile: async () => persistedYaml, + writeFile: async (targetPath, content) => { + writes.push({ path: targetPath, content }); + persistedYaml = content; + }, + }); + + await initConfigStore({ cwd, fs }); + await persistSessionDefaultsPatch({ + profile: ' ios ', + patch: { scheme: 'App' }, + }); + + expect(getConfig().sessionDefaultsProfiles?.ios?.scheme).toBe('App'); + expect(getConfig().sessionDefaultsProfiles?.[' ios ']).toBeUndefined(); + expect(writes).toHaveLength(1); + }); + + it('normalizes profile names for active profile persistence', async () => { + const writes: { path: string; content: string }[] = []; + const fs = createMockFileSystemExecutor({ + existsSync: () => true, + readFile: async () => 'schemaVersion: 1\n', + writeFile: async (targetPath, content) => { + writes.push({ path: targetPath, content }); + }, + }); + + await initConfigStore({ cwd, fs }); + await persistActiveSessionDefaultsProfile(' ios '); + + expect(getConfig().activeSessionDefaultsProfile).toBe('ios'); + expect(writes).toHaveLength(1); + }); + + it('keeps non-session config immutable after init when persisting session defaults', async () => { + let persistedYaml = 'schemaVersion: 1\n'; + const fs = createMockFileSystemExecutor({ + existsSync: () => true, + readFile: async () => persistedYaml, + writeFile: async (_targetPath, content) => { + persistedYaml = content; + }, + }); + + await initConfigStore({ + cwd, + fs, + env: { XCODEBUILDMCP_DEBUG: 'true' }, + }); + + expect(getConfig().debug).toBe(true); + + await persistSessionDefaultsPatch({ patch: { scheme: 'App' } }); + + expect(getConfig().debug).toBe(true); + expect(getConfig().sessionDefaults?.scheme).toBe('App'); + }); + + it('keeps non-session config immutable after init when persisting active profile', async () => { + let persistedYaml = 'schemaVersion: 1\n'; + const fs = createMockFileSystemExecutor({ + existsSync: () => true, + readFile: async () => persistedYaml, + writeFile: async (_targetPath, content) => { + persistedYaml = content; + }, + }); + + await initConfigStore({ + cwd, + fs, + env: { XCODEBUILDMCP_DEBUG: 'true' }, + }); + + expect(getConfig().debug).toBe(true); + + await persistActiveSessionDefaultsProfile('ios'); + + expect(getConfig().debug).toBe(true); + expect(getConfig().activeSessionDefaultsProfile).toBe('ios'); + }); +}); diff --git a/src/utils/__tests__/debugger-simctl.test.ts b/src/utils/__tests__/debugger-simctl.test.ts new file mode 100644 index 00000000..615288d5 --- /dev/null +++ b/src/utils/__tests__/debugger-simctl.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from 'vitest'; +import { resolveSimulatorAppPid } from '../debugger/simctl.ts'; +import { createMockExecutor } from '../../test-utils/mock-executors.ts'; + +describe('resolveSimulatorAppPid', () => { + it('returns PID when bundle id is found', async () => { + const mockExecutor = createMockExecutor({ + success: true, + output: '1234 0 io.sentry.MyApp\n', + }); + + const pid = await resolveSimulatorAppPid({ + executor: mockExecutor, + simulatorId: 'SIM-123', + bundleId: 'io.sentry.MyApp', + }); + + expect(pid).toBe(1234); + }); + + it('throws when bundle id is missing', async () => { + const mockExecutor = createMockExecutor({ + success: true, + output: '999 0 other.app\n', + }); + + await expect( + resolveSimulatorAppPid({ + executor: mockExecutor, + simulatorId: 'SIM-123', + bundleId: 'io.sentry.MyApp', + }), + ).rejects.toThrow('No running process found'); + }); + + it('throws when PID is missing', async () => { + const mockExecutor = createMockExecutor({ + success: true, + output: '- 0 io.sentry.MyApp\n', + }); + + await expect( + resolveSimulatorAppPid({ + executor: mockExecutor, + simulatorId: 'SIM-123', + bundleId: 'io.sentry.MyApp', + }), + ).rejects.toThrow('not running'); + }); +}); diff --git a/src/utils/__tests__/infer-platform.test.ts b/src/utils/__tests__/infer-platform.test.ts new file mode 100644 index 00000000..fa8c635f --- /dev/null +++ b/src/utils/__tests__/infer-platform.test.ts @@ -0,0 +1,264 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import { createMockCommandResponse, createMockExecutor } from '../../test-utils/mock-executors.ts'; +import type { CommandExecutor } from '../execution/index.ts'; +import { sessionStore } from '../session-store.ts'; +import { inferPlatform } from '../infer-platform.ts'; +import { XcodePlatform } from '../../types/common.ts'; + +describe('inferPlatform', () => { + beforeEach(() => { + sessionStore.clear(); + }); + + it('uses cached simulatorPlatform when selector matches session defaults', async () => { + sessionStore.setDefaults({ + simulatorId: 'SIM-UUID', + simulatorPlatform: XcodePlatform.tvOSSimulator, + }); + + const executor = createMockExecutor(new Error('Executor should not be called')); + const result = await inferPlatform({ simulatorId: 'SIM-UUID' }, executor); + + expect(result.platform).toBe(XcodePlatform.tvOSSimulator); + expect(result.source).toBe('simulator-platform-cache'); + }); + + it('ignores cached simulatorPlatform when explicit selector differs', async () => { + sessionStore.setDefaults({ + simulatorId: 'OLD-SIM-UUID', + simulatorPlatform: XcodePlatform.watchOSSimulator, + }); + + const mockExecutor: CommandExecutor = async () => + createMockCommandResponse({ + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.tvOS-18-0': [ + { + udid: 'SIM-UUID', + name: 'Apple TV', + isAvailable: true, + }, + ], + }, + }), + }); + + const result = await inferPlatform({ simulatorId: 'SIM-UUID' }, mockExecutor); + + expect(result.platform).toBe(XcodePlatform.tvOSSimulator); + expect(result.source).toBe('simulator-runtime'); + }); + + it('prefers simulator runtime metadata when simulatorName is provided', async () => { + const mockExecutor: CommandExecutor = async () => + createMockCommandResponse({ + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { + udid: 'SIM-UUID', + name: 'iPhone 16 Pro', + isAvailable: true, + }, + ], + }, + }), + }); + + const result = await inferPlatform({ simulatorName: 'iPhone 16 Pro' }, mockExecutor); + + expect(result.platform).toBe(XcodePlatform.iOSSimulator); + expect(result.source).toBe('simulator-runtime'); + }); + + it('reads simulatorName from session defaults and prefers runtime metadata', async () => { + sessionStore.setDefaults({ simulatorName: 'Apple Watch Ultra 2' }); + + const mockExecutor: CommandExecutor = async () => + createMockCommandResponse({ + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.watchOS-11-0': [ + { + udid: 'WATCH-UUID', + name: 'Apple Watch Ultra 2', + isAvailable: true, + }, + ], + }, + }), + }); + + const result = await inferPlatform({}, mockExecutor); + + expect(result.platform).toBe(XcodePlatform.watchOSSimulator); + expect(result.source).toBe('simulator-runtime'); + }); + + it('does not let session simulatorName override an explicit simulatorId', async () => { + sessionStore.setDefaults({ simulatorName: 'Apple Watch Ultra 2' }); + + const mockExecutor: CommandExecutor = async () => + createMockCommandResponse({ + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.watchOS-11-0': [ + { + udid: 'WATCH-UUID', + name: 'Apple Watch Ultra 2', + isAvailable: true, + }, + ], + 'com.apple.CoreSimulator.SimRuntime.tvOS-18-0': [ + { + udid: 'SIM-UUID', + name: 'Apple TV', + isAvailable: true, + }, + ], + }, + }), + }); + + const result = await inferPlatform({ simulatorId: 'SIM-UUID' }, mockExecutor); + + expect(result.platform).toBe(XcodePlatform.tvOSSimulator); + expect(result.source).toBe('simulator-runtime'); + }); + + it('infers platform from simulator runtime when simulatorId is provided', async () => { + const mockExecutor: CommandExecutor = async () => + createMockCommandResponse({ + success: true, + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.tvOS-18-0': [ + { + udid: 'SIM-UUID', + name: 'Apple TV', + isAvailable: true, + }, + ], + }, + }), + }); + + const result = await inferPlatform({ simulatorId: 'SIM-UUID' }, mockExecutor); + + expect(result.platform).toBe(XcodePlatform.tvOSSimulator); + expect(result.source).toBe('simulator-runtime'); + }); + + it('falls back to build settings when simulator runtime cannot be resolved', async () => { + const callHistory: string[][] = []; + const mockExecutor: CommandExecutor = async (command) => { + callHistory.push(command); + + if (command[0] === 'xcrun') { + return createMockCommandResponse({ + success: true, + output: JSON.stringify({ devices: {} }), + }); + } + + return createMockCommandResponse({ + success: true, + output: 'SDKROOT = watchsimulator\nSUPPORTED_PLATFORMS = watchsimulator watchos', + }); + }; + + const result = await inferPlatform( + { + simulatorId: 'SIM-UUID', + projectPath: '/tmp/Test.xcodeproj', + scheme: 'WatchScheme', + }, + mockExecutor, + ); + + expect(result.platform).toBe(XcodePlatform.watchOSSimulator); + expect(result.source).toBe('build-settings'); + expect(callHistory).toHaveLength(2); + expect(callHistory[0]).toEqual(['xcrun', 'simctl', 'list', 'devices', 'available', '--json']); + expect(callHistory[1]).toEqual([ + 'xcodebuild', + '-showBuildSettings', + '-scheme', + 'WatchScheme', + '-project', + '/tmp/Test.xcodeproj', + ]); + }); + + it('prefers workspace defaults when both projectPath and workspacePath are present in session defaults', async () => { + sessionStore.setDefaults({ + projectPath: '/tmp/Test.xcodeproj', + workspacePath: '/tmp/Test.xcworkspace', + scheme: 'WatchScheme', + }); + + const callHistory: string[][] = []; + const mockExecutor: CommandExecutor = async (command) => { + callHistory.push(command); + + if (command[0] === 'xcrun') { + return createMockCommandResponse({ + success: true, + output: JSON.stringify({ devices: {} }), + }); + } + + return createMockCommandResponse({ + success: true, + output: 'SDKROOT = watchsimulator', + }); + }; + + const result = await inferPlatform({ simulatorId: 'SIM-UUID' }, mockExecutor); + + expect(result.platform).toBe(XcodePlatform.watchOSSimulator); + expect(result.source).toBe('build-settings'); + expect(callHistory).toHaveLength(2); + expect(callHistory[1]).toEqual([ + 'xcodebuild', + '-showBuildSettings', + '-scheme', + 'WatchScheme', + '-workspace', + '/tmp/Test.xcworkspace', + ]); + }); + + it('defaults to iOS when simulator and build-settings inference both fail', async () => { + const mockExecutor: CommandExecutor = async (command) => { + if (command[0] === 'xcrun') { + return createMockCommandResponse({ + success: false, + error: 'simctl failed', + }); + } + + return createMockCommandResponse({ + success: false, + error: 'xcodebuild failed', + }); + }; + + const result = await inferPlatform( + { + simulatorId: 'SIM-UUID', + workspacePath: '/tmp/Test.xcworkspace', + scheme: 'UnknownScheme', + }, + mockExecutor, + ); + + expect(result.platform).toBe(XcodePlatform.iOSSimulator); + expect(result.source).toBe('default'); + }); +}); diff --git a/src/utils/__tests__/log_capture.test.ts b/src/utils/__tests__/log_capture.test.ts new file mode 100644 index 00000000..32554997 --- /dev/null +++ b/src/utils/__tests__/log_capture.test.ts @@ -0,0 +1,363 @@ +import { ChildProcess } from 'child_process'; +import { Writable } from 'stream'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { + activeLogSessions, + startLogCapture, + stopLogCapture, + type SubsystemFilter, +} from '../log_capture.ts'; +import type { CommandExecutor } from '../CommandExecutor.ts'; +import type { FileSystemExecutor } from '../FileSystemExecutor.ts'; + +type CallHistoryEntry = { + command: string[]; + logPrefix?: string; + useShell?: boolean; + opts?: { env?: Record; cwd?: string }; + detached?: boolean; +}; + +function createMockExecutorWithCalls(callHistory: CallHistoryEntry[]): CommandExecutor { + const mockProcess: Partial = {}; + Object.assign(mockProcess, { + pid: 12345, + stdout: null, + stderr: null, + killed: false, + exitCode: null, + on: () => mockProcess, + }); + + return async (command, logPrefix, useShell, opts, detached) => { + callHistory.push({ command, logPrefix, useShell, opts, detached }); + return { success: true, output: '', process: mockProcess as ChildProcess }; + }; +} + +function expectPredicate( + call: CallHistoryEntry, + bundleId: string, + subsystemFilter: SubsystemFilter, +): void { + const predicateIndex = call.command.indexOf('--predicate'); + expect(predicateIndex).toBeGreaterThan(-1); + const predicate = call.command[predicateIndex + 1]; + + switch (subsystemFilter) { + case 'app': + expect(predicate).toBe(`subsystem == "${bundleId}"`); + return; + case 'swiftui': + expect(predicate).toBe(`subsystem == "${bundleId}" OR subsystem == "com.apple.SwiftUI"`); + return; + default: { + const subsystems = [bundleId, ...subsystemFilter]; + const expected = subsystems.map((s) => `subsystem == "${s}"`).join(' OR '); + expect(predicate).toBe(expected); + } + } +} + +type InMemoryFileRecord = { content: string; mtimeMs: number }; + +function createInMemoryFileSystemExecutor(): FileSystemExecutor { + const files = new Map(); + const tempDir = '/virtual/tmp'; + + return { + mkdir: async () => {}, + readFile: async (path) => { + const record = files.get(path); + if (!record) { + throw new Error(`Missing file: ${path}`); + } + return record.content; + }, + writeFile: async (path, content) => { + files.set(path, { content, mtimeMs: Date.now() }); + }, + createWriteStream: (path) => { + const chunks: Buffer[] = []; + + const stream = new Writable({ + write(chunk, _encoding, callback) { + chunks.push(Buffer.from(chunk)); + callback(); + }, + final(callback) { + const existing = files.get(path)?.content ?? ''; + files.set(path, { + content: existing + Buffer.concat(chunks).toString('utf8'), + mtimeMs: Date.now(), + }); + callback(); + }, + }); + + return stream as unknown as ReturnType; + }, + cp: async () => {}, + readdir: async (dir) => { + const prefix = `${dir}/`; + return Array.from(files.keys()) + .filter((filePath) => filePath.startsWith(prefix)) + .map((filePath) => filePath.slice(prefix.length)); + }, + stat: async (path) => { + const record = files.get(path); + if (!record) { + throw new Error(`Missing file: ${path}`); + } + return { isDirectory: (): boolean => false, mtimeMs: record.mtimeMs }; + }, + rm: async (path) => { + files.delete(path); + }, + existsSync: (path) => files.has(path), + mkdtemp: async (prefix) => `${tempDir}/${prefix}mock-temp`, + tmpdir: () => tempDir, + }; +} + +beforeEach(() => { + activeLogSessions.clear(); +}); + +afterEach(() => { + activeLogSessions.clear(); +}); + +describe('startLogCapture', () => { + it('creates log stream command with app subsystem by default', async () => { + const callHistory: CallHistoryEntry[] = []; + const executor = createMockExecutorWithCalls(callHistory); + const fileSystem = createInMemoryFileSystemExecutor(); + + const result = await startLogCapture( + { simulatorUuid: 'sim-uuid', bundleId: 'io.sentry.app' }, + executor, + fileSystem, + ); + + expect(result.error).toBeUndefined(); + expect(callHistory).toHaveLength(1); + expect(callHistory[0].command).toEqual([ + 'xcrun', + 'simctl', + 'spawn', + 'sim-uuid', + 'log', + 'stream', + '--level=debug', + '--predicate', + 'subsystem == "io.sentry.app"', + ]); + expect(callHistory[0].logPrefix).toBe('OS Log Capture'); + expect(callHistory[0].useShell).toBe(false); + expect(callHistory[0].detached).toBe(true); + }); + + it('creates log stream command without predicate when filter is all', async () => { + const callHistory: CallHistoryEntry[] = []; + const executor = createMockExecutorWithCalls(callHistory); + const fileSystem = createInMemoryFileSystemExecutor(); + + const result = await startLogCapture( + { + simulatorUuid: 'sim-uuid', + bundleId: 'io.sentry.app', + subsystemFilter: 'all', + }, + executor, + fileSystem, + ); + + expect(result.error).toBeUndefined(); + expect(callHistory).toHaveLength(1); + expect(callHistory[0].command).toEqual([ + 'xcrun', + 'simctl', + 'spawn', + 'sim-uuid', + 'log', + 'stream', + '--level=debug', + ]); + }); + + it('creates log stream command with SwiftUI predicate', async () => { + const callHistory: CallHistoryEntry[] = []; + const executor = createMockExecutorWithCalls(callHistory); + const fileSystem = createInMemoryFileSystemExecutor(); + + const result = await startLogCapture( + { + simulatorUuid: 'sim-uuid', + bundleId: 'io.sentry.app', + subsystemFilter: 'swiftui', + }, + executor, + fileSystem, + ); + + expect(result.error).toBeUndefined(); + expect(callHistory).toHaveLength(1); + expectPredicate(callHistory[0], 'io.sentry.app', 'swiftui'); + }); + + it('creates log stream command with custom subsystem predicate', async () => { + const callHistory: CallHistoryEntry[] = []; + const executor = createMockExecutorWithCalls(callHistory); + const fileSystem = createInMemoryFileSystemExecutor(); + + const result = await startLogCapture( + { + simulatorUuid: 'sim-uuid', + bundleId: 'io.sentry.app', + subsystemFilter: ['com.apple.UIKit', 'com.apple.CoreData'], + }, + executor, + fileSystem, + ); + + expect(result.error).toBeUndefined(); + expect(callHistory).toHaveLength(1); + expectPredicate(callHistory[0], 'io.sentry.app', ['com.apple.UIKit', 'com.apple.CoreData']); + }); + + it('creates console capture and log stream commands when captureConsole is true', async () => { + const callHistory: CallHistoryEntry[] = []; + const executor = createMockExecutorWithCalls(callHistory); + const fileSystem = createInMemoryFileSystemExecutor(); + + const result = await startLogCapture( + { + simulatorUuid: 'sim-uuid', + bundleId: 'io.sentry.app', + captureConsole: true, + args: ['--flag', 'value'], + }, + executor, + fileSystem, + ); + + expect(result.error).toBeUndefined(); + expect(callHistory).toHaveLength(2); + expect(callHistory[0].command).toEqual([ + 'xcrun', + 'simctl', + 'launch', + '--console-pty', + '--terminate-running-process', + 'sim-uuid', + 'io.sentry.app', + '--flag', + 'value', + ]); + expect(callHistory[0].logPrefix).toBe('Console Log Capture'); + expect(callHistory[0].useShell).toBe(false); + expect(callHistory[0].detached).toBe(true); + + expect(callHistory[1].logPrefix).toBe('OS Log Capture'); + expect(callHistory[1].useShell).toBe(false); + expect(callHistory[1].detached).toBe(true); + }); + + it('passes SIMCTL_CHILD_-prefixed env to console launch executor when env is provided', async () => { + const callHistory: CallHistoryEntry[] = []; + const executor = createMockExecutorWithCalls(callHistory); + const fileSystem = createInMemoryFileSystemExecutor(); + + const result = await startLogCapture( + { + simulatorUuid: 'sim-uuid', + bundleId: 'io.sentry.app', + captureConsole: true, + env: { STAGING_ENABLED: '1', DEBUG: 'true' }, + }, + executor, + fileSystem, + ); + + expect(result.error).toBeUndefined(); + expect(callHistory).toHaveLength(2); + // Console launch call should have prefixed env + expect(callHistory[0].opts).toEqual({ + env: { + SIMCTL_CHILD_STAGING_ENABLED: '1', + SIMCTL_CHILD_DEBUG: 'true', + }, + }); + // OS log stream call should not have env + expect(callHistory[1].opts).toBeUndefined(); + }); + + it('does not pass env opts to executor when env is not provided', async () => { + const callHistory: CallHistoryEntry[] = []; + const executor = createMockExecutorWithCalls(callHistory); + const fileSystem = createInMemoryFileSystemExecutor(); + + const result = await startLogCapture( + { + simulatorUuid: 'sim-uuid', + bundleId: 'io.sentry.app', + captureConsole: true, + }, + executor, + fileSystem, + ); + + expect(result.error).toBeUndefined(); + expect(callHistory[0].opts).toBeUndefined(); + }); +}); + +describe('stopLogCapture', () => { + it('returns error when session is missing', async () => { + const fileSystem = createInMemoryFileSystemExecutor(); + const result = await stopLogCapture('missing-session', fileSystem); + + expect(result.logContent).toBe(''); + expect(result.error).toBe('Log capture session not found: missing-session'); + }); + + it('kills active processes and returns log content', async () => { + const fileSystem = createInMemoryFileSystemExecutor(); + const logFilePath = `${fileSystem.tmpdir()}/session.log`; + await fileSystem.writeFile(logFilePath, 'test log content'); + const logStream = fileSystem.createWriteStream(logFilePath, { flags: 'a' }); + + let killCount = 0; + const runningProcess = { + killed: false, + exitCode: null, + kill: () => { + killCount += 1; + }, + } as unknown as ChildProcess; + + const finishedProcess = { + killed: false, + exitCode: 0, + kill: () => { + killCount += 1; + }, + } as unknown as ChildProcess; + + activeLogSessions.set('session-1', { + processes: [runningProcess, finishedProcess], + logFilePath, + simulatorUuid: 'sim-uuid', + bundleId: 'io.sentry.app', + logStream, + }); + + const result = await stopLogCapture('session-1', fileSystem); + + expect(result.error).toBeUndefined(); + expect(result.logContent).toBe('test log content'); + expect(killCount).toBe(1); + expect(activeLogSessions.has('session-1')).toBe(false); + }); +}); diff --git a/src/utils/__tests__/logger.test.ts b/src/utils/__tests__/logger.test.ts new file mode 100644 index 00000000..d728b13a --- /dev/null +++ b/src/utils/__tests__/logger.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest'; +import { __mapLogLevelToSentryForTests, __shouldCaptureToSentryForTests } from '../logger.ts'; + +describe('logger sentry capture policy', () => { + it('does not capture by default', () => { + expect(__shouldCaptureToSentryForTests()).toBe(false); + }); + + it('does not capture when sentry is false', () => { + expect(__shouldCaptureToSentryForTests({ sentry: false })).toBe(false); + }); + + it('captures only when explicitly enabled', () => { + expect(__shouldCaptureToSentryForTests({ sentry: true })).toBe(true); + }); + + it('maps internal levels to Sentry log levels', () => { + expect(__mapLogLevelToSentryForTests('emergency')).toBe('fatal'); + expect(__mapLogLevelToSentryForTests('warning')).toBe('warn'); + expect(__mapLogLevelToSentryForTests('notice')).toBe('info'); + expect(__mapLogLevelToSentryForTests('error')).toBe('error'); + }); +}); diff --git a/src/utils/__tests__/nskeyedarchiver-parser.test.ts b/src/utils/__tests__/nskeyedarchiver-parser.test.ts new file mode 100644 index 00000000..19a74a8f --- /dev/null +++ b/src/utils/__tests__/nskeyedarchiver-parser.test.ts @@ -0,0 +1,195 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { readFileSync, existsSync } from 'fs'; +import { join } from 'path'; +import { + parseXcuserstate, + parseXcuserstateBuffer, + isUID, + findStringIndex, + findDictWithKey, +} from '../nskeyedarchiver-parser.ts'; + +// Path to the example project's xcuserstate (used as test fixture) +const EXAMPLE_PROJECT_XCUSERSTATE = join( + process.cwd(), + 'example_projects/iOS/MCPTest.xcodeproj/project.xcworkspace/xcuserdata/johndoe.xcuserdatad/UserInterfaceState.xcuserstate', +); + +// Expected values for the MCPTest example project +const EXPECTED_MCPTEST = { + scheme: 'MCPTest', + simulatorId: 'B38FE93D-578B-454B-BE9A-C6FA0CE5F096', + simulatorPlatform: 'iphonesimulator', +}; + +describe('NSKeyedArchiver Parser', () => { + describe('parseXcuserstate (file path)', () => { + it.skipIf(!existsSync(EXAMPLE_PROJECT_XCUSERSTATE))( + 'extracts scheme name from example project', + () => { + const result = parseXcuserstate(EXAMPLE_PROJECT_XCUSERSTATE); + expect(result.scheme).toBe(EXPECTED_MCPTEST.scheme); + }, + ); + + it.skipIf(!existsSync(EXAMPLE_PROJECT_XCUSERSTATE))( + 'extracts simulator UUID from example project', + () => { + const result = parseXcuserstate(EXAMPLE_PROJECT_XCUSERSTATE); + expect(result.simulatorId).toBe(EXPECTED_MCPTEST.simulatorId); + }, + ); + + it.skipIf(!existsSync(EXAMPLE_PROJECT_XCUSERSTATE))( + 'extracts simulator platform from example project', + () => { + const result = parseXcuserstate(EXAMPLE_PROJECT_XCUSERSTATE); + expect(result.simulatorPlatform).toBe(EXPECTED_MCPTEST.simulatorPlatform); + }, + ); + + it.skipIf(!existsSync(EXAMPLE_PROJECT_XCUSERSTATE))( + 'extracts device location from example project', + () => { + const result = parseXcuserstate(EXAMPLE_PROJECT_XCUSERSTATE); + expect(result.deviceLocation).toMatch(/^dvtdevice-iphonesimulator:[A-F0-9-]{36}$/); + }, + ); + + it('returns empty result for non-existent file', () => { + const result = parseXcuserstate('/non/existent/file.xcuserstate'); + expect(result).toEqual({}); + }); + }); + + describe('parseXcuserstateBuffer (buffer)', () => { + let fixtureBuffer: Buffer; + + beforeAll(() => { + if (existsSync(EXAMPLE_PROJECT_XCUSERSTATE)) { + fixtureBuffer = readFileSync(EXAMPLE_PROJECT_XCUSERSTATE); + } + }); + + it.skipIf(!existsSync(EXAMPLE_PROJECT_XCUSERSTATE))('extracts scheme name from buffer', () => { + const result = parseXcuserstateBuffer(fixtureBuffer); + expect(result.scheme).toBe(EXPECTED_MCPTEST.scheme); + }); + + it.skipIf(!existsSync(EXAMPLE_PROJECT_XCUSERSTATE))( + 'extracts simulator UUID from buffer', + () => { + const result = parseXcuserstateBuffer(fixtureBuffer); + expect(result.simulatorId).toBe(EXPECTED_MCPTEST.simulatorId); + }, + ); + + it.skipIf(!existsSync(EXAMPLE_PROJECT_XCUSERSTATE))( + 'extracts all fields correctly from buffer', + () => { + const result = parseXcuserstateBuffer(fixtureBuffer); + expect(result).toMatchObject({ + scheme: EXPECTED_MCPTEST.scheme, + simulatorId: EXPECTED_MCPTEST.simulatorId, + simulatorPlatform: EXPECTED_MCPTEST.simulatorPlatform, + }); + expect(result.deviceLocation).toBeDefined(); + }, + ); + + it('returns empty result for empty buffer', () => { + const result = parseXcuserstateBuffer(Buffer.from([])); + expect(result).toEqual({}); + }); + + it('returns empty result for invalid plist data', () => { + const result = parseXcuserstateBuffer(Buffer.from('not a plist')); + expect(result).toEqual({}); + }); + }); + + describe('helper functions', () => { + describe('isUID', () => { + it('returns true for valid UID objects', () => { + expect(isUID({ UID: 0 })).toBe(true); + expect(isUID({ UID: 123 })).toBe(true); + }); + + it('returns false for non-UID values', () => { + expect(isUID(null)).toBe(false); + expect(isUID(undefined)).toBe(false); + expect(isUID(123)).toBe(false); + expect(isUID('string')).toBe(false); + expect(isUID({ notUID: 123 })).toBe(false); + expect(isUID({ UID: 'string' })).toBe(false); + }); + }); + + describe('findStringIndex', () => { + it('finds string at correct index', () => { + const objects = ['$null', 'first', 'second', 'third']; + expect(findStringIndex(objects, 'first')).toBe(1); + expect(findStringIndex(objects, 'third')).toBe(3); + }); + + it('returns -1 for missing string', () => { + const objects = ['$null', 'first', 'second']; + expect(findStringIndex(objects, 'missing')).toBe(-1); + }); + }); + + describe('findDictWithKey', () => { + it('finds dictionary containing key index', () => { + const objects = [ + '$null', + 'KeyName', + { + 'NS.keys': [{ UID: 1 }], + 'NS.objects': [{ UID: 3 }], + }, + 'ValueName', + ]; + + const dict = findDictWithKey(objects, 1); + expect(dict).toBeDefined(); + expect(dict?.['NS.keys']).toHaveLength(1); + }); + + it('returns undefined when key not found', () => { + const objects = [ + '$null', + 'KeyName', + { + 'NS.keys': [{ UID: 1 }], + 'NS.objects': [{ UID: 3 }], + }, + ]; + + const dict = findDictWithKey(objects, 99); + expect(dict).toBeUndefined(); + }); + + it('skips non-dictionary objects', () => { + const objects = ['$null', 'string', 123, null, { noKeys: true }]; + const dict = findDictWithKey(objects, 1); + expect(dict).toBeUndefined(); + }); + }); + }); + + describe('edge cases', () => { + it('handles xcuserstate without ActiveScheme', () => { + // This would require a specially crafted test fixture + // For now, we just verify the function doesn't crash + const result = parseXcuserstateBuffer(Buffer.from('bplist00')); + expect(result).toEqual({}); + }); + + it('handles scheme object without IDENameString', () => { + // The parser should gracefully handle missing nested keys + // and return partial results + const result = parseXcuserstateBuffer(Buffer.from('invalid')); + expect(result.scheme).toBeUndefined(); + }); + }); +}); diff --git a/src/utils/__tests__/platform-detection.test.ts b/src/utils/__tests__/platform-detection.test.ts new file mode 100644 index 00000000..c170f518 --- /dev/null +++ b/src/utils/__tests__/platform-detection.test.ts @@ -0,0 +1,116 @@ +import { describe, expect, it } from 'vitest'; +import { createMockExecutor } from '../../test-utils/mock-executors.ts'; +import { XcodePlatform } from '../../types/common.ts'; +import { detectPlatformFromScheme } from '../platform-detection.ts'; + +describe('detectPlatformFromScheme', () => { + it('detects simulator platform from SDKROOT', async () => { + const executor = createMockExecutor({ + success: true, + output: 'SDKROOT = watchsimulator\nSUPPORTED_PLATFORMS = watchsimulator watchos', + }); + + const result = await detectPlatformFromScheme( + '/tmp/Test.xcodeproj', + undefined, + 'WatchScheme', + executor, + ); + + expect(result.platform).toBe(XcodePlatform.watchOSSimulator); + expect(result.sdkroot).toBe('watchsimulator'); + }); + + it('falls back to SUPPORTED_PLATFORMS when SDKROOT is missing', async () => { + const executor = createMockExecutor({ + success: true, + output: 'SUPPORTED_PLATFORMS = appletvsimulator appletvos', + }); + + const result = await detectPlatformFromScheme( + undefined, + '/tmp/Test.xcworkspace', + 'TVScheme', + executor, + ); + + expect(result.platform).toBe(XcodePlatform.tvOSSimulator); + expect(result.sdkroot).toBeNull(); + }); + + it('returns null platform for non-simulator SDKROOT values', async () => { + const executor = createMockExecutor({ + success: true, + output: 'SDKROOT = macosx\nSUPPORTED_PLATFORMS = macosx', + }); + + const result = await detectPlatformFromScheme( + '/tmp/Test.xcodeproj', + undefined, + 'MacScheme', + executor, + ); + + expect(result.platform).toBeNull(); + expect(result.sdkroot).toBe('macosx'); + }); + + it('prefers simulator SDKROOT when build settings contain multiple blocks', async () => { + const executor = createMockExecutor({ + success: true, + output: ` +Build settings for action build and target DeviceTarget: + SDKROOT = iphoneos + SUPPORTED_PLATFORMS = iphoneos + +Build settings for action build and target SimulatorTarget: + SDKROOT = iphonesimulator18.0 + SUPPORTED_PLATFORMS = iphonesimulator iphoneos +`, + }); + + const result = await detectPlatformFromScheme( + '/tmp/Test.xcodeproj', + undefined, + 'MixedScheme', + executor, + ); + + expect(result.platform).toBe(XcodePlatform.iOSSimulator); + expect(result.sdkroot).toBe('iphonesimulator18.0'); + }); + + it('returns error when both projectPath and workspacePath are provided', async () => { + const executor = createMockExecutor({ + success: true, + output: 'SDKROOT = iphonesimulator', + }); + + const result = await detectPlatformFromScheme( + '/tmp/Test.xcodeproj', + '/tmp/Test.xcworkspace', + 'AmbiguousScheme', + executor, + ); + + expect(result.platform).toBeNull(); + expect(result.error).toContain('mutually exclusive'); + }); + + it('surfaces command failure details', async () => { + const executor = createMockExecutor({ + success: false, + error: 'xcodebuild failed', + }); + + const result = await detectPlatformFromScheme( + '/tmp/Test.xcodeproj', + undefined, + 'BrokenScheme', + executor, + ); + + expect(result.platform).toBeNull(); + expect(result.error).toBe('xcodebuild failed'); + }); +}); diff --git a/src/utils/__tests__/process-tree.test.ts b/src/utils/__tests__/process-tree.test.ts new file mode 100644 index 00000000..7d7dff7a --- /dev/null +++ b/src/utils/__tests__/process-tree.test.ts @@ -0,0 +1,93 @@ +import { describe, it, expect } from 'vitest'; +import { getProcessTree } from '../process-tree.ts'; +import { createCommandMatchingMockExecutor } from '../../test-utils/mock-executors.ts'; + +describe('getProcessTree', () => { + it('parses pid, ppid, name, and command', async () => { + const executor = createCommandMatchingMockExecutor({ + '/bin/ps -o pid=,ppid=,comm=,args= -p 123': { + output: '123 1 Xcode /Applications/Xcode.app/Contents/MacOS/Xcode', + }, + '/bin/ps -o pid=,ppid=,comm=,args= -p 1': { + output: '1 0 launchd /sbin/launchd', + }, + }); + + const result = await getProcessTree(executor, '123'); + expect(result.error).toBeUndefined(); + expect(result.entries).toEqual([ + { + pid: '123', + ppid: '1', + name: 'Xcode', + command: '/Applications/Xcode.app/Contents/MacOS/Xcode', + }, + { + pid: '1', + ppid: '0', + name: 'launchd', + command: '/sbin/launchd', + }, + ]); + }); + + it('handles lines without command args', async () => { + const executor = createCommandMatchingMockExecutor({ + '/bin/ps -o pid=,ppid=,comm=,args= -p 123': { + output: '123 1 Xcode', + }, + '/bin/ps -o pid=,ppid=,comm=,args= -p 1': { + output: '1 0 launchd', + }, + }); + + const result = await getProcessTree(executor, '123'); + expect(result.error).toBeUndefined(); + expect(result.entries).toEqual([ + { + pid: '123', + ppid: '1', + name: 'Xcode', + command: '', + }, + { + pid: '1', + ppid: '0', + name: 'launchd', + command: '', + }, + ]); + }); + + it('returns error when ps output is empty', async () => { + const executor = createCommandMatchingMockExecutor({ + '/bin/ps -o pid=,ppid=,comm=,args= -p 123': { + output: '', + }, + 'ps -o pid=,ppid=,comm=,args= -p 123': { + output: '', + }, + }); + + const result = await getProcessTree(executor, '123'); + expect(result.entries).toEqual([]); + expect(result.error).toContain('ps returned no output for pid 123'); + }); + + it('returns error when ps exits unsuccessfully', async () => { + const executor = createCommandMatchingMockExecutor({ + '/bin/ps -o pid=,ppid=,comm=,args= -p 123': { + success: false, + error: 'ps failed', + }, + 'ps -o pid=,ppid=,comm=,args= -p 123': { + success: false, + error: 'ps failed', + }, + }); + + const result = await getProcessTree(executor, '123'); + expect(result.entries).toEqual([]); + expect(result.error).toContain('ps failed'); + }); +}); diff --git a/src/utils/__tests__/project-config.test.ts b/src/utils/__tests__/project-config.test.ts new file mode 100644 index 00000000..46f9e12e --- /dev/null +++ b/src/utils/__tests__/project-config.test.ts @@ -0,0 +1,355 @@ +import { describe, expect, it } from 'vitest'; +import path from 'node:path'; +import { parse as parseYaml } from 'yaml'; +import { createMockFileSystemExecutor } from '../../test-utils/mock-executors.ts'; +import { + loadProjectConfig, + persistActiveSessionDefaultsProfileToProjectConfig, + persistSessionDefaultsToProjectConfig, +} from '../project-config.ts'; + +const cwd = '/repo'; +const configPath = path.join(cwd, '.xcodebuildmcp', 'config.yaml'); +const configDir = path.join(cwd, '.xcodebuildmcp'); + +type MockWrite = { path: string; content: string }; + +type MockFsFixture = { + fs: ReturnType; + writes: MockWrite[]; + mkdirs: string[]; +}; + +function createFsFixture(options?: { exists?: boolean; readFile?: string }): MockFsFixture { + const writes: MockWrite[] = []; + const mkdirs: string[] = []; + const exists = options?.exists ?? false; + const readFileContent = options?.readFile; + + const fs = createMockFileSystemExecutor({ + existsSync: (targetPath) => (targetPath === configPath ? exists : false), + readFile: async (targetPath) => { + if (targetPath !== configPath) { + throw new Error(`Unexpected readFile path: ${targetPath}`); + } + if (readFileContent == null) { + throw new Error('readFile called but no readFile content was provided'); + } + return readFileContent; + }, + writeFile: async (targetPath, content) => { + writes.push({ path: targetPath, content }); + }, + mkdir: async (targetPath) => { + mkdirs.push(targetPath); + }, + }); + + return { fs, writes, mkdirs }; +} + +describe('project-config', () => { + describe('loadProjectConfig', () => { + it('should return found=false when config does not exist', async () => { + const { fs } = createFsFixture({ exists: false }); + const result = await loadProjectConfig({ fs, cwd }); + expect(result).toEqual({ found: false }); + }); + + it('should normalize mutual exclusivity and resolve relative paths', async () => { + const yaml = [ + 'schemaVersion: 1', + 'enabledWorkflows: simulator,device', + 'debug: true', + 'axePath: "./bin/axe"', + 'sessionDefaults:', + ' projectPath: "./App.xcodeproj"', + ' workspacePath: "./App.xcworkspace"', + ' simulatorName: "iPhone 16"', + ' simulatorId: "SIM-1"', + ' derivedDataPath: "./.derivedData"', + '', + ].join('\n'); + + const { fs } = createFsFixture({ exists: true, readFile: yaml }); + const result = await loadProjectConfig({ fs, cwd }); + + if (!result.found) throw new Error('expected config to be found'); + + const defaults = result.config.sessionDefaults ?? {}; + expect(result.config.enabledWorkflows).toEqual(['simulator', 'device']); + expect(result.config.debug).toBe(true); + expect(result.config.axePath).toBe(path.join(cwd, 'bin', 'axe')); + expect(defaults.workspacePath).toBe(path.join(cwd, 'App.xcworkspace')); + expect(defaults.projectPath).toBeUndefined(); + expect(defaults.simulatorId).toBe('SIM-1'); + expect(defaults.simulatorName).toBe('iPhone 16'); + expect(defaults.derivedDataPath).toBe(path.join(cwd, '.derivedData')); + expect(result.notices.length).toBeGreaterThan(0); + }); + + it('should normalize debuggerBackend and resolve template paths', async () => { + const yaml = [ + 'schemaVersion: 1', + 'debuggerBackend: lldb', + 'iosTemplatePath: "./templates/ios"', + 'macosTemplatePath: "/opt/templates/macos"', + '', + ].join('\n'); + + const { fs } = createFsFixture({ exists: true, readFile: yaml }); + const result = await loadProjectConfig({ fs, cwd }); + + if (!result.found) throw new Error('expected config to be found'); + + expect(result.config.debuggerBackend).toBe('lldb-cli'); + expect(result.config.iosTemplatePath).toBe(path.join(cwd, 'templates', 'ios')); + expect(result.config.macosTemplatePath).toBe('/opt/templates/macos'); + }); + + it('should resolve file URLs in session defaults and top-level paths', async () => { + const yaml = [ + 'schemaVersion: 1', + 'axePath: "file:///repo/bin/axe"', + 'sessionDefaults:', + ' workspacePath: "file:///repo/App.xcworkspace"', + ' derivedDataPath: "file:///repo/.derivedData"', + '', + ].join('\n'); + + const { fs } = createFsFixture({ exists: true, readFile: yaml }); + const result = await loadProjectConfig({ fs, cwd }); + + if (!result.found) throw new Error('expected config to be found'); + + expect(result.config.axePath).toBe('/repo/bin/axe'); + const defaults = result.config.sessionDefaults ?? {}; + expect(defaults.workspacePath).toBe('/repo/App.xcworkspace'); + expect(defaults.derivedDataPath).toBe('/repo/.derivedData'); + }); + + it('normalizes namespaced session defaults profiles and active profile', async () => { + const yaml = [ + 'schemaVersion: 1', + 'activeSessionDefaultsProfile: "ios"', + 'sessionDefaultsProfiles:', + ' ios:', + ' projectPath: "./App.xcodeproj"', + ' workspacePath: "./App.xcworkspace"', + ' simulatorName: "iPhone 16"', + ' watch:', + ' workspacePath: "./Watch.xcworkspace"', + '', + ].join('\n'); + + const { fs } = createFsFixture({ exists: true, readFile: yaml }); + const result = await loadProjectConfig({ fs, cwd }); + if (!result.found) throw new Error('expected config to be found'); + + expect(result.config.activeSessionDefaultsProfile).toBe('ios'); + expect(result.config.sessionDefaultsProfiles?.ios?.workspacePath).toBe( + path.join(cwd, 'App.xcworkspace'), + ); + expect(result.config.sessionDefaultsProfiles?.ios?.projectPath).toBeUndefined(); + expect(result.config.sessionDefaultsProfiles?.watch?.workspacePath).toBe( + path.join(cwd, 'Watch.xcworkspace'), + ); + }); + + it('should return an error result when schemaVersion is unsupported', async () => { + const yaml = ['schemaVersion: 2', 'sessionDefaults:', ' scheme: "App"', ''].join('\n'); + const { fs } = createFsFixture({ exists: true, readFile: yaml }); + + const result = await loadProjectConfig({ fs, cwd }); + expect(result.found).toBe(false); + expect('error' in result).toBe(true); + if ('error' in result) { + expect(result.error).toBeInstanceOf(Error); + } + }); + + it('should return an error result when YAML does not parse to an object', async () => { + const { fs } = createFsFixture({ exists: true, readFile: '- item' }); + + const result = await loadProjectConfig({ fs, cwd }); + expect(result.found).toBe(false); + expect('error' in result).toBe(true); + if ('error' in result) { + expect(result.error.message).toBe('Project config must be an object'); + } + }); + }); + + describe('persistSessionDefaultsToProjectConfig', () => { + it('should merge patches, delete exclusive keys, and preserve unknown sections', async () => { + const yaml = [ + 'schemaVersion: 1', + 'debug: true', + 'enabledWorkflows:', + ' - simulator', + 'sessionDefaults:', + ' scheme: "Old"', + ' simulatorName: "OldSim"', + 'server:', + ' enabledWorkflows:', + ' - simulator', + '', + ].join('\n'); + + const { fs, writes, mkdirs } = createFsFixture({ exists: true, readFile: yaml }); + + await persistSessionDefaultsToProjectConfig({ + fs, + cwd, + patch: { scheme: 'New', simulatorId: 'SIM-1' }, + deleteKeys: ['simulatorName'], + }); + + expect(mkdirs).toContain(configDir); + expect(writes.length).toBe(1); + expect(writes[0].path).toBe(configPath); + + const parsed = parseYaml(writes[0].content) as { + schemaVersion: number; + debug?: boolean; + enabledWorkflows?: string[]; + sessionDefaults?: Record; + server?: { enabledWorkflows?: string[] }; + }; + + expect(parsed.schemaVersion).toBe(1); + expect(parsed.debug).toBe(true); + expect(parsed.enabledWorkflows).toEqual(['simulator']); + expect(parsed.sessionDefaults?.scheme).toBe('New'); + expect(parsed.sessionDefaults?.simulatorId).toBe('SIM-1'); + expect(parsed.sessionDefaults?.simulatorName).toBeUndefined(); + expect(parsed.server?.enabledWorkflows).toEqual(['simulator']); + }); + + it('should overwrite invalid existing config with a minimal valid config', async () => { + const { fs, writes } = createFsFixture({ exists: true, readFile: '- not-an-object' }); + + await persistSessionDefaultsToProjectConfig({ + fs, + cwd, + patch: { scheme: 'App' }, + }); + + expect(writes.length).toBe(1); + const parsed = parseYaml(writes[0].content) as { + schemaVersion: number; + sessionDefaults?: Record; + }; + + expect(parsed.schemaVersion).toBe(1); + expect(parsed.sessionDefaults?.scheme).toBe('App'); + }); + + it('persists session defaults to a named profile', async () => { + const yaml = [ + 'schemaVersion: 1', + 'sessionDefaultsProfiles:', + ' ios:', + ' scheme: "Old"', + '', + ].join('\n'); + const { fs, writes } = createFsFixture({ exists: true, readFile: yaml }); + + await persistSessionDefaultsToProjectConfig({ + fs, + cwd, + profile: 'ios', + patch: { scheme: 'NewIOS', simulatorId: 'SIM-1' }, + }); + + expect(writes.length).toBe(1); + const parsed = parseYaml(writes[0].content) as { + sessionDefaultsProfiles?: Record>; + }; + expect(parsed.sessionDefaultsProfiles?.ios?.scheme).toBe('NewIOS'); + expect(parsed.sessionDefaultsProfiles?.ios?.simulatorId).toBe('SIM-1'); + }); + + it('trims named profile before persisting session defaults', async () => { + const { fs, writes } = createFsFixture({ exists: false }); + + await persistSessionDefaultsToProjectConfig({ + fs, + cwd, + profile: ' ios ', + patch: { scheme: 'NewIOS' }, + }); + + expect(writes.length).toBe(1); + const parsed = parseYaml(writes[0].content) as { + sessionDefaultsProfiles?: Record>; + }; + expect(parsed.sessionDefaultsProfiles?.ios?.scheme).toBe('NewIOS'); + expect(parsed.sessionDefaultsProfiles?.[' ios ']).toBeUndefined(); + }); + + it('throws when named profile is empty after trimming', async () => { + const { fs } = createFsFixture({ exists: false }); + + await expect( + persistSessionDefaultsToProjectConfig({ + fs, + cwd, + profile: ' ', + patch: { scheme: 'NewIOS' }, + }), + ).rejects.toThrow('Profile name cannot be empty.'); + }); + }); + + describe('persistActiveSessionDefaultsProfileToProjectConfig', () => { + it('persists active profile name', async () => { + const { fs, writes } = createFsFixture({ exists: true, readFile: 'schemaVersion: 1\n' }); + + await persistActiveSessionDefaultsProfileToProjectConfig({ + fs, + cwd, + profile: 'ios', + }); + + expect(writes.length).toBe(1); + const parsed = parseYaml(writes[0].content) as { + activeSessionDefaultsProfile?: string; + }; + expect(parsed.activeSessionDefaultsProfile).toBe('ios'); + }); + + it('trims active profile name before persisting', async () => { + const { fs, writes } = createFsFixture({ exists: true, readFile: 'schemaVersion: 1\n' }); + + await persistActiveSessionDefaultsProfileToProjectConfig({ + fs, + cwd, + profile: ' ios ', + }); + + expect(writes.length).toBe(1); + const parsed = parseYaml(writes[0].content) as { + activeSessionDefaultsProfile?: string; + }; + expect(parsed.activeSessionDefaultsProfile).toBe('ios'); + }); + + it('removes active profile when switching to global', async () => { + const yaml = ['schemaVersion: 1', 'activeSessionDefaultsProfile: "watch"', ''].join('\n'); + const { fs, writes } = createFsFixture({ exists: true, readFile: yaml }); + + await persistActiveSessionDefaultsProfileToProjectConfig({ + fs, + cwd, + profile: null, + }); + + expect(writes.length).toBe(1); + const parsed = parseYaml(writes[0].content) as { + activeSessionDefaultsProfile?: string; + }; + expect(parsed.activeSessionDefaultsProfile).toBeUndefined(); + }); + }); +}); diff --git a/src/utils/__tests__/sentry-redaction.test.ts b/src/utils/__tests__/sentry-redaction.test.ts new file mode 100644 index 00000000..c1e45f47 --- /dev/null +++ b/src/utils/__tests__/sentry-redaction.test.ts @@ -0,0 +1,85 @@ +import { describe, expect, it } from 'vitest'; +import type * as Sentry from '@sentry/node'; +import { + __parseXcodeVersionForTests, + __redactEventForTests, + __redactLogForTests, +} from '../sentry.ts'; + +describe('sentry redaction', () => { + it('removes identity/request context and redacts user paths', () => { + const event: Sentry.Event = { + message: 'failed to open /Users/cam/project/App/AppDelegate.swift', + user: { id: '123' }, + request: { url: 'https://example.com' }, + breadcrumbs: [{ category: 'test', message: '/Users/cam/tmp' }], + exception: { + values: [ + { + type: 'Error', + value: 'build failed in /Users/cam/project', + stacktrace: { + frames: [ + { + abs_path: '/Users/cam/project/src/tool.ts', + filename: '/Users/cam/project/src/tool.ts', + }, + ], + }, + }, + ], + }, + extra: { + output: 'log at /Users/cam/project/build.log', + attempts: 1, + nested: { + cwd: '/Users/cam/project', + }, + }, + }; + + const redacted = __redactEventForTests(event); + + expect(redacted.user).toBeUndefined(); + expect(redacted.request).toBeUndefined(); + expect(redacted.breadcrumbs).toBeUndefined(); + expect(redacted.message).toContain('/Users//project/App/AppDelegate.swift'); + expect(redacted.exception?.values?.[0]?.value).toContain('/Users//project'); + expect(redacted.exception?.values?.[0]?.stacktrace?.frames?.[0]?.abs_path).toContain( + '/Users//project/src/tool.ts', + ); + expect(redacted.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toContain( + '/Users//project/src/tool.ts', + ); + expect(redacted.extra?.output).toBe('log at /Users//project/build.log'); + expect(redacted.extra?.attempts).toBe(1); + expect(redacted.extra?.nested).toEqual({ cwd: '/Users//project' }); + }); + + it('parses xcode version metadata safely', () => { + const parsed = __parseXcodeVersionForTests('Xcode 16.3\nBuild version 16E123\n'); + expect(parsed).toEqual({ version: '16.3', buildVersion: '16E123' }); + }); + + it('redacts user paths in log payloads', () => { + const log: Sentry.Log = { + level: 'info', + message: 'tool failed at /Users/cam/project/build.log', + attributes: { + file: '/Users/cam/project/App/AppDelegate.swift', + nested: { cwd: '/Users/cam/project' }, + }, + }; + + const redacted = __redactLogForTests(log); + + expect(redacted).toEqual({ + level: 'info', + message: 'tool failed at /Users//project/build.log', + attributes: { + file: '/Users//project/App/AppDelegate.swift', + nested: { cwd: '/Users//project' }, + }, + }); + }); +}); diff --git a/src/utils/__tests__/session-aware-tool-factory.test.ts b/src/utils/__tests__/session-aware-tool-factory.test.ts index 7543fe2d..feed93e5 100644 --- a/src/utils/__tests__/session-aware-tool-factory.test.ts +++ b/src/utils/__tests__/session-aware-tool-factory.test.ts @@ -2,11 +2,27 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as z from 'zod'; import { createSessionAwareTool } from '../typed-tool-factory.ts'; import { sessionStore } from '../session-store.ts'; -import { createMockExecutor } from '../../test-utils/mock-executors.ts'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../test-utils/mock-executors.ts'; +import { + __resetConfigStoreForTests, + initConfigStore, + type RuntimeConfigOverrides, +} from '../config-store.ts'; + +const cwd = '/repo'; + +async function initConfigStoreForTest(overrides?: RuntimeConfigOverrides): Promise { + __resetConfigStoreForTests(); + await initConfigStore({ cwd, fs: createMockFileSystemExecutor(), overrides }); +} describe('createSessionAwareTool', () => { - beforeEach(() => { + beforeEach(async () => { sessionStore.clear(); + await initConfigStoreForTest({ disableSessionDefaults: false }); }); const internalSchema = z @@ -99,23 +115,14 @@ describe('createSessionAwareTool', () => { }); it('uses opt-out messaging when session defaults schema is disabled', async () => { - const original = process.env.XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS; - process.env.XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS = 'true'; - - try { - const result = await handler({ projectPath: '/p.xcodeproj', simulatorId: 'SIM-1' }); - expect(result.isError).toBe(true); - const text = result.content[0].text; - expect(text).toContain('Missing required parameters'); - expect(text).toContain('scheme is required'); - expect(text).not.toContain('session defaults'); - } finally { - if (original === undefined) { - delete process.env.XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS; - } else { - process.env.XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS = original; - } - } + await initConfigStoreForTest({ disableSessionDefaults: true }); + + const result = await handler({ projectPath: '/p.xcodeproj', simulatorId: 'SIM-1' }); + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Missing required parameters'); + expect(text).toContain('scheme is required'); + expect(text).not.toContain('session defaults'); }); it('should surface Zod validation errors when invalid', async () => { @@ -206,4 +213,49 @@ describe('createSessionAwareTool', () => { expect(msg).toContain('projectPath'); expect(msg).toContain('workspacePath'); }); + + it('prefers first key when both values of exclusive pair come from session defaults', async () => { + // Create handler that echoes which simulator param was used + const echoHandler = createSessionAwareTool({ + internalSchema: z.object({ + scheme: z.string(), + projectPath: z.string().optional(), + simulatorId: z.string().optional(), + simulatorName: z.string().optional(), + }), + logicFunction: async (params) => ({ + content: [ + { + type: 'text', + text: JSON.stringify({ + simulatorId: params.simulatorId, + simulatorName: params.simulatorName, + }), + }, + ], + isError: false, + }), + getExecutor: () => createMockExecutor({ success: true }), + requirements: [{ allOf: ['scheme'] }], + exclusivePairs: [['simulatorId', 'simulatorName']], + }); + + // Set both simulatorId and simulatorName in session defaults + sessionStore.setDefaults({ + scheme: 'App', + projectPath: '/a.xcodeproj', + simulatorId: 'SIM-123', + simulatorName: 'iPhone 16', + }); + + // Call with no args - both come from session defaults + const result = await echoHandler({}); + expect(result.isError).toBe(false); + + const content = result.content[0] as { type: 'text'; text: string }; + const parsed = JSON.parse(content.text); + // simulatorId should be kept (first in pair), simulatorName should be pruned + expect(parsed.simulatorId).toBe('SIM-123'); + expect(parsed.simulatorName).toBeUndefined(); + }); }); diff --git a/src/utils/__tests__/session-store.test.ts b/src/utils/__tests__/session-store.test.ts index 752c8f47..70f25f17 100644 --- a/src/utils/__tests__/session-store.test.ts +++ b/src/utils/__tests__/session-store.test.ts @@ -43,4 +43,55 @@ describe('SessionStore', () => { expect(all.scheme).toBe('App'); expect(all.simulatorId).toBe('SIM-1'); }); + + it('isolates defaults by active profile', () => { + sessionStore.setDefaults({ scheme: 'GlobalApp' }); + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'iOSApp', simulatorName: 'iPhone 16' }); + sessionStore.setActiveProfile('watch'); + sessionStore.setDefaults({ scheme: 'WatchApp' }); + + sessionStore.setActiveProfile('ios'); + expect(sessionStore.getAll()).toMatchObject({ scheme: 'iOSApp', simulatorName: 'iPhone 16' }); + + sessionStore.setActiveProfile('watch'); + expect(sessionStore.getAll()).toMatchObject({ scheme: 'WatchApp' }); + expect(sessionStore.getAll().simulatorName).toBeUndefined(); + + sessionStore.setActiveProfile(null); + expect(sessionStore.getAll()).toMatchObject({ scheme: 'GlobalApp' }); + }); + + it('does not inherit global project/workspace defaults into named profiles', () => { + sessionStore.setDefaults({ workspacePath: '/repo/MyApp.xcworkspace' }); + + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'iOSApp' }); + + expect(sessionStore.getAll().workspacePath).toBeUndefined(); + expect(sessionStore.getAll()).toMatchObject({ scheme: 'iOSApp' }); + + sessionStore.setActiveProfile(null); + expect(sessionStore.getAll()).toMatchObject({ workspacePath: '/repo/MyApp.xcworkspace' }); + }); + + it('clear(keys) only affects active profile while clear() clears all profiles', () => { + sessionStore.setActiveProfile('ios'); + sessionStore.setDefaults({ scheme: 'iOSApp', simulatorId: 'SIM-1' }); + sessionStore.setActiveProfile('watch'); + sessionStore.setDefaults({ scheme: 'WatchApp', simulatorId: 'SIM-2' }); + + sessionStore.setActiveProfile('ios'); + sessionStore.clear(['simulatorId']); + expect(sessionStore.getAll().scheme).toBe('iOSApp'); + expect(sessionStore.getAll().simulatorId).toBeUndefined(); + + sessionStore.setActiveProfile('watch'); + expect(sessionStore.getAll().simulatorId).toBe('SIM-2'); + + sessionStore.clear(); + expect(sessionStore.getAll()).toEqual({}); + expect(sessionStore.getActiveProfile()).toBeNull(); + expect(sessionStore.listProfiles()).toEqual([]); + }); }); diff --git a/src/utils/__tests__/simctl-child-env.test.ts b/src/utils/__tests__/simctl-child-env.test.ts new file mode 100644 index 00000000..aa657a8c --- /dev/null +++ b/src/utils/__tests__/simctl-child-env.test.ts @@ -0,0 +1,34 @@ +import { describe, it, expect } from 'vitest'; +import { normalizeSimctlChildEnv } from '../environment.ts'; + +describe('normalizeSimctlChildEnv', () => { + it('should prefix unprefixed keys with SIMCTL_CHILD_', () => { + const result = normalizeSimctlChildEnv({ FOO: '1', BAR: '2' }); + expect(result).toEqual({ SIMCTL_CHILD_FOO: '1', SIMCTL_CHILD_BAR: '2' }); + }); + + it('should preserve already-prefixed keys', () => { + const result = normalizeSimctlChildEnv({ SIMCTL_CHILD_FOO: '1' }); + expect(result).toEqual({ SIMCTL_CHILD_FOO: '1' }); + }); + + it('should handle a mix of prefixed and unprefixed keys', () => { + const result = normalizeSimctlChildEnv({ FOO: '1', SIMCTL_CHILD_BAR: '2' }); + expect(result).toEqual({ SIMCTL_CHILD_FOO: '1', SIMCTL_CHILD_BAR: '2' }); + }); + + it('should filter null and undefined values', () => { + const input = { FOO: '1', BAR: null, BAZ: undefined } as unknown as Record; + const result = normalizeSimctlChildEnv(input); + expect(result).toEqual({ SIMCTL_CHILD_FOO: '1' }); + }); + + it('should return empty object for empty input', () => { + expect(normalizeSimctlChildEnv({})).toEqual({}); + }); + + it('should return empty object for null/undefined input', () => { + expect(normalizeSimctlChildEnv(null as unknown as Record)).toEqual({}); + expect(normalizeSimctlChildEnv(undefined as unknown as Record)).toEqual({}); + }); +}); diff --git a/src/utils/__tests__/typed-tool-factory.test.ts b/src/utils/__tests__/typed-tool-factory.test.ts index 92326f96..f3930886 100644 --- a/src/utils/__tests__/typed-tool-factory.test.ts +++ b/src/utils/__tests__/typed-tool-factory.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect } from 'vitest'; import * as z from 'zod'; import { createTypedTool } from '../typed-tool-factory.ts'; import { createMockExecutor } from '../../test-utils/mock-executors.ts'; -import { ToolResponse } from '../../types/common.ts'; +import type { ToolResponse } from '../../types/common.ts'; // Test schema and types const testSchema = z.object({ diff --git a/src/utils/__tests__/workflow-selection.test.ts b/src/utils/__tests__/workflow-selection.test.ts index c2a24668..64baa7b1 100644 --- a/src/utils/__tests__/workflow-selection.test.ts +++ b/src/utils/__tests__/workflow-selection.test.ts @@ -1,7 +1,20 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect } from 'vitest'; import { z } from 'zod'; import { resolveSelectedWorkflows } from '../workflow-selection.ts'; import type { WorkflowGroup } from '../../core/plugin-types.ts'; +import { + __resetConfigStoreForTests, + initConfigStore, + type RuntimeConfigOverrides, +} from '../config-store.ts'; +import { createMockFileSystemExecutor } from '../../test-utils/mock-executors.ts'; + +const cwd = '/repo'; + +async function initConfigStoreForTest(overrides: RuntimeConfigOverrides): Promise { + __resetConfigStoreForTests(); + await initConfigStore({ cwd, fs: createMockFileSystemExecutor(), overrides }); +} function makeWorkflow(name: string): WorkflowGroup { return { @@ -32,57 +45,96 @@ function makeWorkflowMap(names: string[]): Map { } describe('resolveSelectedWorkflows', () => { - let originalDebug: string | undefined; + it('adds doctor when debug is enabled and selection list is provided', async () => { + await initConfigStoreForTest({ + debug: true, + experimentalWorkflowDiscovery: true, + }); + const workflows = makeWorkflowMap([ + 'session-management', + 'workflow-discovery', + 'doctor', + 'simulator', + ]); - beforeEach(() => { - originalDebug = process.env.XCODEBUILDMCP_DEBUG; - }); + const result = resolveSelectedWorkflows(['simulator'], workflows); - afterEach(() => { - if (typeof originalDebug === 'undefined') { - delete process.env.XCODEBUILDMCP_DEBUG; - } else { - process.env.XCODEBUILDMCP_DEBUG = originalDebug; - } + expect(result.selectedNames).toEqual([ + 'session-management', + 'workflow-discovery', + 'doctor', + 'simulator', + ]); + expect(result.selectedWorkflows.map((workflow) => workflow.directoryName)).toEqual([ + 'session-management', + 'workflow-discovery', + 'doctor', + 'simulator', + ]); }); - it('adds doctor when debug is enabled and selection list is provided', () => { - process.env.XCODEBUILDMCP_DEBUG = 'true'; - const workflows = makeWorkflowMap(['session-management', 'doctor', 'simulator']); + it('does not add doctor when debug is disabled', async () => { + await initConfigStoreForTest({ + debug: false, + experimentalWorkflowDiscovery: true, + }); + const workflows = makeWorkflowMap([ + 'session-management', + 'workflow-discovery', + 'doctor', + 'simulator', + ]); - const result = resolveSelectedWorkflows(workflows, ['simulator']); + const result = resolveSelectedWorkflows(['simulator'], workflows); - expect(result.selectedNames).toEqual(['session-management', 'doctor', 'simulator']); + expect(result.selectedNames).toEqual(['session-management', 'workflow-discovery', 'simulator']); expect(result.selectedWorkflows.map((workflow) => workflow.directoryName)).toEqual([ 'session-management', - 'doctor', + 'workflow-discovery', 'simulator', ]); }); - it('does not add doctor when debug is disabled', () => { - process.env.XCODEBUILDMCP_DEBUG = 'false'; - const workflows = makeWorkflowMap(['session-management', 'doctor', 'simulator']); + it('defaults to simulator workflow when no selection list is provided', async () => { + await initConfigStoreForTest({ + debug: true, + experimentalWorkflowDiscovery: true, + }); + const workflows = makeWorkflowMap([ + 'session-management', + 'workflow-discovery', + 'doctor', + 'simulator', + ]); - const result = resolveSelectedWorkflows(workflows, ['simulator']); + const result = resolveSelectedWorkflows([], workflows); - expect(result.selectedNames).toEqual(['session-management', 'simulator']); + expect(result.selectedNames).toEqual([ + 'session-management', + 'workflow-discovery', + 'doctor', + 'simulator', + ]); expect(result.selectedWorkflows.map((workflow) => workflow.directoryName)).toEqual([ 'session-management', + 'workflow-discovery', + 'doctor', 'simulator', ]); }); - it('returns all workflows when no selection list is provided', () => { - process.env.XCODEBUILDMCP_DEBUG = 'true'; - const workflows = makeWorkflowMap(['session-management', 'doctor', 'simulator']); + it('excludes workflow-discovery when experimental flag is disabled', async () => { + await initConfigStoreForTest({ + debug: false, + experimentalWorkflowDiscovery: false, + }); + const workflows = makeWorkflowMap(['session-management', 'workflow-discovery', 'simulator']); - const result = resolveSelectedWorkflows(workflows, []); + const result = resolveSelectedWorkflows([], workflows); - expect(result.selectedNames).toBeNull(); + expect(result.selectedNames).toEqual(['session-management', 'simulator']); expect(result.selectedWorkflows.map((workflow) => workflow.directoryName)).toEqual([ 'session-management', - 'doctor', 'simulator', ]); }); diff --git a/src/utils/__tests__/xcode-process.test.ts b/src/utils/__tests__/xcode-process.test.ts new file mode 100644 index 00000000..8f363c22 --- /dev/null +++ b/src/utils/__tests__/xcode-process.test.ts @@ -0,0 +1,94 @@ +import { describe, it, expect } from 'vitest'; +import { detectXcodeRuntime, isRunningUnderXcode } from '../xcode-process.ts'; +import { createCommandMatchingMockExecutor } from '../../test-utils/mock-executors.ts'; + +describe('isRunningUnderXcode', () => { + it('detects Xcode by name', () => { + expect( + isRunningUnderXcode([ + { + pid: '1', + ppid: '0', + name: 'Xcode', + command: '/Applications/Xcode.app/Contents/MacOS/Xcode', + }, + ]), + ).toBe(true); + }); + + it('detects Xcode by command suffix', () => { + expect( + isRunningUnderXcode([ + { + pid: '1', + ppid: '0', + name: 'xcode', + command: '/Volumes/Dev/Xcode-26.3.app/Contents/MacOS/Xcode', + }, + ]), + ).toBe(true); + }); + + it('returns false when no match exists', () => { + expect( + isRunningUnderXcode([ + { + pid: '1', + ppid: '0', + name: 'launchd', + command: '/sbin/launchd', + }, + ]), + ).toBe(false); + }); +}); + +describe('detectXcodeRuntime', () => { + it('returns true when the process tree contains Xcode', async () => { + const executor = createCommandMatchingMockExecutor({ + '/bin/ps -o pid=,ppid=,comm=,args= -p 123': { + output: '123 1 Xcode /Applications/Xcode.app/Contents/MacOS/Xcode', + }, + '/bin/ps -o pid=,ppid=,comm=,args= -p 1': { + output: '1 0 launchd /sbin/launchd', + }, + }); + + const result = await detectXcodeRuntime(executor, '123'); + expect(result.error).toBeUndefined(); + expect(result.runningUnderXcode).toBe(true); + }); + + it('returns false when the process tree has no Xcode match', async () => { + const executor = createCommandMatchingMockExecutor({ + '/bin/ps -o pid=,ppid=,comm=,args= -p 123': { + output: '123 1 node node /tmp/server.js', + }, + '/bin/ps -o pid=,ppid=,comm=,args= -p 1': { + output: '1 0 launchd /sbin/launchd', + }, + }); + + const result = await detectXcodeRuntime(executor, '123'); + expect(result.error).toBeUndefined(); + expect(result.runningUnderXcode).toBe(false); + }); + + it('returns error when process tree collection fails', async () => { + const executor = createCommandMatchingMockExecutor({ + '/bin/ps -o pid=,ppid=,comm=,args= -p 123': { + success: false, + error: 'ps failed', + }, + 'ps -o pid=,ppid=,comm=,args= -p 123': { + success: false, + error: 'ps failed', + }, + }); + + const result = await detectXcodeRuntime(executor, '123'); + expect(result.processTree).toEqual([]); + expect(result.runningUnderXcode).toBe(false); + expect(result.error).toContain('ps failed'); + }); +}); diff --git a/src/utils/__tests__/xcode-state-reader.test.ts b/src/utils/__tests__/xcode-state-reader.test.ts new file mode 100644 index 00000000..61ec6c9a --- /dev/null +++ b/src/utils/__tests__/xcode-state-reader.test.ts @@ -0,0 +1,299 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { existsSync } from 'fs'; +import { join } from 'path'; +import { + findXcodeStateFile, + lookupSimulatorName, + readXcodeIdeState, +} from '../xcode-state-reader.ts'; +import { createCommandMatchingMockExecutor } from '../../test-utils/mock-executors.ts'; + +// Path to the example project's xcuserstate (used as test fixture) +const EXAMPLE_PROJECT_PATH = join(process.cwd(), 'example_projects/iOS/MCPTest.xcodeproj'); +const EXAMPLE_XCUSERSTATE = join( + EXAMPLE_PROJECT_PATH, + 'project.xcworkspace/xcuserdata/johndoe.xcuserdatad/UserInterfaceState.xcuserstate', +); + +describe('findXcodeStateFile', () => { + it('returns undefined when no project/workspace found', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '' }, + }); + + const result = await findXcodeStateFile({ executor, cwd: '/test/project' }); + expect(result).toBeUndefined(); + }); + + it('finds xcuserstate in xcworkspace', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '/test/project/MyApp.xcworkspace\n' }, + stat: { output: '1704067200\n' }, // mtime + }); + + const result = await findXcodeStateFile({ executor, cwd: '/test/project' }); + expect(result).toBe( + '/test/project/MyApp.xcworkspace/xcuserdata/testuser.xcuserdatad/UserInterfaceState.xcuserstate', + ); + }); + + it('finds xcuserstate in xcodeproj when no workspace', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '/test/project/MyApp.xcodeproj\n' }, + stat: { output: '1704067200\n' }, + }); + + const result = await findXcodeStateFile({ executor, cwd: '/test/project' }); + expect(result).toBe( + '/test/project/MyApp.xcodeproj/project.xcworkspace/xcuserdata/testuser.xcuserdatad/UserInterfaceState.xcuserstate', + ); + }); + + it('returns first valid xcuserstate when multiple found', async () => { + // When multiple xcuserstate files exist with same mtime, returns first by sort order + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { + output: '/test/project/App.xcworkspace\n/test/project/Other.xcworkspace\n', + }, + stat: { output: '1704067200\n' }, + }); + + const result = await findXcodeStateFile({ executor, cwd: '/test/project' }); + // Should return one of them (implementation sorts by mtime then takes first) + expect(result).toMatch(/\.xcworkspace\/xcuserdata\/testuser\.xcuserdatad/); + }); + + it('returns undefined when xcuserstate file does not exist', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '/test/project/MyApp.xcworkspace\n' }, + stat: { success: false, error: 'No such file' }, + }); + + const result = await findXcodeStateFile({ executor, cwd: '/test/project' }); + expect(result).toBeUndefined(); + }); + + it('finds project in parent directory when cwd is nested within searchRoot', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + 'find /test/project/subdir -maxdepth 6': { output: '' }, + 'find /test/project -maxdepth 1': { output: '/test/project/MyApp.xcodeproj\n' }, + stat: { output: '1704067200\n' }, + }); + + const result = await findXcodeStateFile({ + executor, + cwd: '/test/project/subdir', + searchRoot: '/test/project', + }); + + expect(result).toBe( + '/test/project/MyApp.xcodeproj/project.xcworkspace/xcuserdata/testuser.xcuserdatad/UserInterfaceState.xcuserstate', + ); + }); + + it('does not search above searchRoot boundary', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + 'find /test/project/subdir -maxdepth 6': { output: '' }, + 'find /test/project -maxdepth 1': { output: '' }, + }); + + const result = await findXcodeStateFile({ + executor, + cwd: '/test/project/subdir', + searchRoot: '/test/project', + }); + + expect(result).toBeUndefined(); + }); + + it('uses configured workspacePath directly', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + 'test -f': { success: true }, + }); + + const result = await findXcodeStateFile({ + executor, + cwd: '/test/project', + workspacePath: '/configured/path/MyApp.xcworkspace', + }); + + expect(result).toBe( + '/configured/path/MyApp.xcworkspace/xcuserdata/testuser.xcuserdatad/UserInterfaceState.xcuserstate', + ); + }); + + it('uses configured projectPath directly', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + 'test -f': { success: true }, + }); + + const result = await findXcodeStateFile({ + executor, + cwd: '/test/project', + projectPath: '/configured/path/MyApp.xcodeproj', + }); + + expect(result).toBe( + '/configured/path/MyApp.xcodeproj/project.xcworkspace/xcuserdata/testuser.xcuserdatad/UserInterfaceState.xcuserstate', + ); + }); +}); + +describe('lookupSimulatorName', () => { + it('returns simulator name for valid UUID', async () => { + const simctlOutput = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ + { udid: '2FCB5689-88F1-4CDF-9E7F-8E310CD41D72', name: 'iPhone 16' }, + { udid: 'OTHER-UUID', name: 'iPhone 15' }, + ], + }, + }); + + const executor = createCommandMatchingMockExecutor({ + 'xcrun simctl': { output: simctlOutput }, + }); + + const result = await lookupSimulatorName( + { executor, cwd: '/test' }, + '2FCB5689-88F1-4CDF-9E7F-8E310CD41D72', + ); + + expect(result).toBe('iPhone 16'); + }); + + it('returns undefined for unknown UUID', async () => { + const simctlOutput = JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [{ udid: 'OTHER-UUID', name: 'iPhone 15' }], + }, + }); + + const executor = createCommandMatchingMockExecutor({ + 'xcrun simctl': { output: simctlOutput }, + }); + + const result = await lookupSimulatorName({ executor, cwd: '/test' }, 'UNKNOWN-UUID'); + + expect(result).toBeUndefined(); + }); + + it('returns undefined when simctl fails', async () => { + const executor = createCommandMatchingMockExecutor({ + 'xcrun simctl': { success: false, error: 'simctl failed' }, + }); + + const result = await lookupSimulatorName( + { executor, cwd: '/test' }, + '2FCB5689-88F1-4CDF-9E7F-8E310CD41D72', + ); + + expect(result).toBeUndefined(); + }); +}); + +describe('readXcodeIdeState', () => { + it('returns error when no project found', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '' }, + }); + + const result = await readXcodeIdeState({ executor, cwd: '/test/project' }); + + expect(result.error).toBeDefined(); + expect(result.scheme).toBeUndefined(); + expect(result.simulatorId).toBeUndefined(); + }); + + it('returns error when xcuserstate not found', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '/test/project/MyApp.xcworkspace\n' }, + stat: { success: false, error: 'No such file' }, + }); + + const result = await readXcodeIdeState({ executor, cwd: '/test/project' }); + + expect(result.error).toBeDefined(); + }); +}); + +describe('readXcodeIdeState integration', () => { + // These tests use the actual example project fixture + + it.skipIf(!existsSync(EXAMPLE_XCUSERSTATE))( + 'reads scheme and simulator from example project', + async () => { + // Mock executor that returns real paths + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'johndoe\n' }, + find: { output: `${EXAMPLE_PROJECT_PATH}\n` }, + stat: { output: '1704067200\n' }, + 'xcrun simctl': { + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.xrOS-2-0': [ + { + udid: 'B38FE93D-578B-454B-BE9A-C6FA0CE5F096', + name: 'Apple Vision Pro', + }, + ], + }, + }), + }, + }); + + const result = await readXcodeIdeState({ + executor, + cwd: join(process.cwd(), 'example_projects/iOS'), + }); + + expect(result.error).toBeUndefined(); + expect(result.scheme).toBe('MCPTest'); + expect(result.simulatorId).toBe('B38FE93D-578B-454B-BE9A-C6FA0CE5F096'); + expect(result.simulatorName).toBe('Apple Vision Pro'); + }, + ); + + it.skipIf(!existsSync(EXAMPLE_XCUSERSTATE))( + 'reads scheme using configured projectPath', + async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'johndoe\n' }, + 'test -f': { success: true }, + 'xcrun simctl': { + output: JSON.stringify({ + devices: { + 'com.apple.CoreSimulator.SimRuntime.xrOS-2-0': [ + { + udid: 'B38FE93D-578B-454B-BE9A-C6FA0CE5F096', + name: 'Apple Vision Pro', + }, + ], + }, + }), + }, + }); + + const result = await readXcodeIdeState({ + executor, + cwd: '/some/other/path', + projectPath: EXAMPLE_PROJECT_PATH, + }); + + expect(result.error).toBeUndefined(); + expect(result.scheme).toBe('MCPTest'); + expect(result.simulatorId).toBe('B38FE93D-578B-454B-BE9A-C6FA0CE5F096'); + }, + ); +}); diff --git a/src/utils/__tests__/xcode-state-watcher.test.ts b/src/utils/__tests__/xcode-state-watcher.test.ts new file mode 100644 index 00000000..14adf67f --- /dev/null +++ b/src/utils/__tests__/xcode-state-watcher.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { + startXcodeStateWatcher, + stopXcodeStateWatcher, + isWatcherRunning, + getWatchedPath, +} from '../xcode-state-watcher.ts'; +import { createCommandMatchingMockExecutor } from '../../test-utils/mock-executors.ts'; + +describe('xcode-state-watcher', () => { + afterEach(async () => { + await stopXcodeStateWatcher(); + }); + + describe('startXcodeStateWatcher', () => { + it('returns false when no xcuserstate file found', async () => { + const executor = createCommandMatchingMockExecutor({ + whoami: { output: 'testuser\n' }, + find: { output: '' }, + }); + + const result = await startXcodeStateWatcher({ + executor, + cwd: '/nonexistent', + }); + + expect(result).toBe(false); + expect(isWatcherRunning()).toBe(false); + }); + }); + + describe('stopXcodeStateWatcher', () => { + it('can be called when no watcher is running', async () => { + expect(isWatcherRunning()).toBe(false); + await stopXcodeStateWatcher(); + expect(isWatcherRunning()).toBe(false); + }); + }); + + describe('isWatcherRunning', () => { + it('returns false initially', () => { + expect(isWatcherRunning()).toBe(false); + }); + }); + + describe('getWatchedPath', () => { + it('returns null when no watcher is running', () => { + expect(getWatchedPath()).toBe(null); + }); + }); +}); diff --git a/src/utils/__tests__/xcodemake.test.ts b/src/utils/__tests__/xcodemake.test.ts new file mode 100644 index 00000000..9ed1bdda --- /dev/null +++ b/src/utils/__tests__/xcodemake.test.ts @@ -0,0 +1,49 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const { executorMock } = vi.hoisted(() => ({ + executorMock: vi.fn(), +})); + +vi.mock('../command.ts', () => ({ + getDefaultCommandExecutor: () => executorMock, +})); + +import { executeXcodemakeCommand } from '../xcodemake.ts'; + +describe('executeXcodemakeCommand', () => { + beforeEach(() => { + executorMock.mockReset(); + }); + + it('runs xcodemake using child-process cwd without mutating process cwd', async () => { + const projectDir = '/tmp/project'; + const originalCwd = process.cwd(); + executorMock.mockResolvedValue({ success: true, output: 'ok' }); + + await executeXcodemakeCommand( + projectDir, + ['-scheme', 'App', '-project', '/tmp/project/App.xcodeproj'], + 'Build', + ); + + expect(executorMock).toHaveBeenCalledWith( + ['xcodemake', '-scheme', 'App', '-project', 'App.xcodeproj'], + 'Build', + false, + { cwd: projectDir }, + ); + expect(process.cwd()).toBe(originalCwd); + }); + + it('does not mutate process cwd when command execution fails', async () => { + const projectDir = '/tmp/project'; + const originalCwd = process.cwd(); + executorMock.mockRejectedValue(new Error('xcodemake failed')); + + await expect(executeXcodemakeCommand(projectDir, ['-scheme', 'App'], 'Build')).rejects.toThrow( + 'xcodemake failed', + ); + + expect(process.cwd()).toBe(originalCwd); + }); +}); diff --git a/src/utils/axe-helpers.ts b/src/utils/axe-helpers.ts index 15117948..79a38756 100644 --- a/src/utils/axe-helpers.ts +++ b/src/utils/axe-helpers.ts @@ -1,64 +1,129 @@ /** * AXe Helper Functions * - * This utility module provides functions to work with the bundled AXe tool. - * Always uses the bundled version to ensure consistency. + * This utility module provides functions to resolve and execute AXe. + * Prefers bundled AXe when present, but allows env and PATH fallback. */ -import { existsSync } from 'fs'; -import { dirname, join } from 'path'; +import { accessSync, constants, existsSync } from 'fs'; +import { delimiter, join, resolve } from 'path'; import { createTextResponse } from './validation.ts'; -import { ToolResponse } from '../types/common.ts'; +import type { ToolResponse } from '../types/common.ts'; import type { CommandExecutor } from './execution/index.ts'; import { getDefaultCommandExecutor } from './execution/index.ts'; +import { getConfig } from './config-store.ts'; +import { getBundledAxePath, getBundledFrameworksDir } from '../core/resource-root.ts'; -function getPackageRoot(): string { - const entry = process.argv[1]; - if (entry) { - const entryDir = dirname(entry); - return dirname(entryDir); +export type AxeBinarySource = 'env' | 'bundled' | 'path'; + +export type AxeBinary = { + path: string; + source: AxeBinarySource; +}; + +function isExecutable(path: string): boolean { + try { + accessSync(path, constants.X_OK); + return true; + } catch { + return false; + } +} + +function resolveAxePathFromConfig(): string | null { + const value = getConfig().axePath; + if (!value) return null; + const resolved = resolve(value); + return isExecutable(resolved) ? resolved : null; +} + +function resolveBundledAxePath(): string | null { + const candidates = new Set(); + candidates.add(getBundledAxePath()); + candidates.add(join(process.cwd(), 'bundled', 'axe')); + + for (const candidate of candidates) { + if (existsSync(candidate) && isExecutable(candidate)) { + return candidate; + } + } + return null; +} + +function resolveAxePathFromPath(): string | null { + const pathValue = process.env.PATH ?? ''; + const entries = pathValue.split(delimiter).filter(Boolean); + for (const entry of entries) { + const candidate = join(entry, 'axe'); + if (isExecutable(candidate)) { + return candidate; + } } - return process.cwd(); + return null; } -// In the npm package, build/index.js is at the same level as bundled/ -// So we go up one level from build/ to get to the package root -const bundledAxePath = join(getPackageRoot(), 'bundled', 'axe'); +export function resolveAxeBinary(): AxeBinary | null { + const configPath = resolveAxePathFromConfig(); + if (configPath) { + return { path: configPath, source: 'env' }; + } + + const bundledPath = resolveBundledAxePath(); + if (bundledPath) { + return { path: bundledPath, source: 'bundled' }; + } + + const pathBinary = resolveAxePathFromPath(); + if (pathBinary) { + return { path: pathBinary, source: 'path' }; + } + + return null; +} /** - * Get the path to the bundled axe binary + * Get the path to the available axe binary */ export function getAxePath(): string | null { - // Always use bundled version for consistency - if (existsSync(bundledAxePath)) { - return bundledAxePath; - } - return null; + return resolveAxeBinary()?.path ?? null; } /** * Get environment variables needed for bundled AXe to run */ export function getBundledAxeEnvironment(): Record { - // No special environment variables needed - bundled AXe binary - // has proper @rpath configuration to find frameworks - return {}; + const resolved = resolveAxeBinary(); + if (resolved?.source !== 'bundled') { + return {}; + } + + const frameworksDir = getBundledFrameworksDir(); + if (!existsSync(frameworksDir)) { + return {}; + } + + const currentFrameworkPath = process.env.DYLD_FRAMEWORK_PATH; + const frameworkPath = currentFrameworkPath + ? `${frameworksDir}${delimiter}${currentFrameworkPath}` + : frameworksDir; + + return { DYLD_FRAMEWORK_PATH: frameworkPath }; } /** - * Check if bundled axe tool is available + * Check if axe tool is available (bundled, env override, or PATH) */ export function areAxeToolsAvailable(): boolean { return getAxePath() !== null; } +export const AXE_NOT_AVAILABLE_MESSAGE = + 'AXe tool not found. UI automation features are not available.\n\n' + + 'Install AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\n' + + 'Ensure bundled artifacts are included or PATH is configured.'; + export function createAxeNotAvailableResponse(): ToolResponse { - return createTextResponse( - 'Bundled axe tool not found. UI automation features are not available.\n\n' + - 'This is likely an installation issue with the npm package.\n' + - 'Please reinstall xcodebuildmcp or report this issue.', - true, - ); + return createTextResponse(AXE_NOT_AVAILABLE_MESSAGE, true); } /** diff --git a/src/utils/axe/index.ts b/src/utils/axe/index.ts index 0ab22ebc..b53b8b66 100644 --- a/src/utils/axe/index.ts +++ b/src/utils/axe/index.ts @@ -4,4 +4,5 @@ export { getBundledAxeEnvironment, areAxeToolsAvailable, isAxeAtLeastVersion, + resolveAxeBinary, } from '../axe-helpers.ts'; diff --git a/src/utils/build-utils.ts b/src/utils/build-utils.ts index 565c4127..255f6207 100644 --- a/src/utils/build-utils.ts +++ b/src/utils/build-utils.ts @@ -19,8 +19,8 @@ import { log } from './logger.ts'; import { XcodePlatform, constructDestinationString } from './xcode.ts'; -import { CommandExecutor, CommandExecOptions } from './command.ts'; -import { ToolResponse, SharedBuildParams, PlatformBuildOptions } from '../types/common.ts'; +import type { CommandExecutor, CommandExecOptions } from './command.ts'; +import type { ToolResponse, SharedBuildParams, PlatformBuildOptions } from '../types/common.ts'; import { createTextResponse, consolidateContentForClaudeCode } from './validation.ts'; import { isXcodemakeEnabled, @@ -33,6 +33,13 @@ import { import { sessionStore } from './session-store.ts'; import path from 'path'; +function resolvePathFromCwd(pathValue: string): string { + if (path.isAbsolute(pathValue)) { + return pathValue; + } + return path.resolve(process.cwd(), pathValue); +} + /** * Common function to execute an Xcode build command across platforms * @param params Common build parameters @@ -53,11 +60,16 @@ export async function executeXcodeBuildCommand( // Collect warnings, errors, and stderr messages from the build output const buildMessages: { type: 'text'; text: string }[] = []; function grepWarningsAndErrors(text: string): { type: 'warning' | 'error'; content: string }[] { + // Require "error:"/"warning:" at line start (with optional tool prefix like "ld: ") + // or after a file:line:col location prefix, to avoid false positives from source + // code like "var authError: Error?" echoed during compilation. return text .split('\n') .map((content) => { - if (/warning:/i.test(content)) return { type: 'warning', content }; - if (/error:/i.test(content)) return { type: 'error', content }; + if (/(?:^(?:[\w-]+:\s+)?|:\d+:\s+)warning:\s/i.test(content)) + return { type: 'warning', content }; + if (/(?:^(?:[\w-]+:\s+)?|:\d+:\s+)(?:fatal )?error:\s/i.test(content)) + return { type: 'error', content }; return null; }) .filter(Boolean) as { type: 'warning' | 'error'; content: string }[]; @@ -98,14 +110,21 @@ export async function executeXcodeBuildCommand( try { const command = ['xcodebuild']; + const workspacePath = params.workspacePath + ? resolvePathFromCwd(params.workspacePath) + : undefined; + const projectPath = params.projectPath ? resolvePathFromCwd(params.projectPath) : undefined; + const derivedDataPath = params.derivedDataPath + ? resolvePathFromCwd(params.derivedDataPath) + : undefined; let projectDir = ''; - if (params.workspacePath) { - projectDir = path.dirname(params.workspacePath); - command.push('-workspace', params.workspacePath); - } else if (params.projectPath) { - projectDir = path.dirname(params.projectPath); - command.push('-project', params.projectPath); + if (workspacePath) { + projectDir = path.dirname(workspacePath); + command.push('-workspace', workspacePath); + } else if (projectPath) { + projectDir = path.dirname(projectPath); + command.push('-project', projectPath); } command.push('-scheme', params.scheme); @@ -179,8 +198,8 @@ export async function executeXcodeBuildCommand( command.push('-destination', destinationString); - if (params.derivedDataPath) { - command.push('-derivedDataPath', params.derivedDataPath); + if (derivedDataPath) { + command.push('-derivedDataPath', derivedDataPath); } if (params.extraArgs && params.extraArgs.length > 0) { @@ -227,7 +246,11 @@ export async function executeXcodeBuildCommand( } } else { // Use standard xcodebuild - result = await executor(command, platformOptions.logPrefix, true, execOpts); + // Pass projectDir as cwd to ensure CocoaPods relative paths resolve correctly + result = await executor(command, platformOptions.logPrefix, false, { + ...execOpts, + cwd: projectDir, + }); } // Grep warnings and errors from stdout (build output) diff --git a/src/utils/command.ts b/src/utils/command.ts index 27924a20..9d85fb3d 100644 --- a/src/utils/command.ts +++ b/src/utils/command.ts @@ -10,15 +10,15 @@ */ import { spawn } from 'child_process'; -import { existsSync } from 'fs'; +import { createWriteStream, existsSync } from 'fs'; import { tmpdir as osTmpdir } from 'os'; import { log } from './logger.ts'; -import { FileSystemExecutor } from './FileSystemExecutor.ts'; -import { CommandExecutor, CommandResponse, CommandExecOptions } from './CommandExecutor.ts'; +import type { FileSystemExecutor } from './FileSystemExecutor.ts'; +import type { CommandExecutor, CommandResponse, CommandExecOptions } from './CommandExecutor.ts'; // Re-export types for backward compatibility -export { CommandExecutor, CommandResponse, CommandExecOptions } from './CommandExecutor.ts'; -export { FileSystemExecutor } from './FileSystemExecutor.ts'; +export type { CommandExecutor, CommandResponse, CommandExecOptions } from './CommandExecutor.ts'; +export type { FileSystemExecutor } from './FileSystemExecutor.ts'; /** * Default executor implementation using spawn (current production behavior) @@ -27,20 +27,20 @@ export { FileSystemExecutor } from './FileSystemExecutor.ts'; * @param logPrefix Prefix for logging * @param useShell Whether to use shell execution (true) or direct execution (false) * @param opts Optional execution options (env: environment variables to merge with process.env, cwd: working directory) - * @param detached Whether to spawn process without waiting for completion (for streaming/background processes) + * @param detached Whether to resolve without waiting for completion (does not detach/unref the process) * @returns Promise resolving to command response with the process */ async function defaultExecutor( command: string[], logPrefix?: string, - useShell: boolean = true, + useShell: boolean = false, opts?: CommandExecOptions, detached: boolean = false, ): Promise { // Properly escape arguments for shell let escapedCommand = command; if (useShell) { - // For shell execution, we need to format as ['sh', '-c', 'full command string'] + // For shell execution, we need to format as ['/bin/sh', '-c', 'full command string'] const commandString = command .map((arg) => { // Shell metacharacters that require quoting: space, quotes, equals, dollar, backticks, semicolons, pipes, etc. @@ -52,17 +52,25 @@ async function defaultExecutor( }) .join(' '); - escapedCommand = ['sh', '-c', commandString]; + escapedCommand = ['/bin/sh', '-c', commandString]; } - // Log the actual command that will be executed - const displayCommand = - useShell && escapedCommand.length === 3 ? escapedCommand[2] : escapedCommand.join(' '); - log('info', `Executing ${logPrefix ?? ''} command: ${displayCommand}`); - return new Promise((resolve, reject) => { - const executable = escapedCommand[0]; - const args = escapedCommand.slice(1); + let executable = escapedCommand[0]; + let args = escapedCommand.slice(1); + + if (!useShell && executable === 'xcodebuild') { + const xcrunPath = '/usr/bin/xcrun'; + if (existsSync(xcrunPath)) { + executable = xcrunPath; + args = ['xcodebuild', ...args]; + } + } + + // Log the actual command that will be executed + const displayCommand = + useShell && escapedCommand.length === 3 ? escapedCommand[2] : [executable, ...args].join(' '); + log('debug', `Executing ${logPrefix ?? ''} command: ${displayCommand}`); const spawnOpts: Parameters[2] = { stdio: ['ignore', 'pipe', 'pipe'], // ignore stdin, pipe stdout/stderr @@ -70,6 +78,21 @@ async function defaultExecutor( cwd: opts?.cwd, }; + log('debug', `defaultExecutor PATH: ${process.env.PATH ?? ''}`); + + const logSpawnError = (err: Error): void => { + const errnoErr = err as NodeJS.ErrnoException & { spawnargs?: string[] }; + const errorDetails = { + code: errnoErr.code, + errno: errnoErr.errno, + syscall: errnoErr.syscall, + path: errnoErr.path, + spawnargs: errnoErr.spawnargs, + stack: errnoErr.stack, + }; + log('error', `Spawn error details: ${JSON.stringify(errorDetails, null, 2)}`); + }; + const childProcess = spawn(executable, args, spawnOpts); let stdout = ''; @@ -91,6 +114,7 @@ async function defaultExecutor( childProcess.on('error', (err) => { if (!resolved) { resolved = true; + logSpawnError(err); reject(err); } }); @@ -131,6 +155,7 @@ async function defaultExecutor( }); childProcess.on('error', (err) => { + logSpawnError(err); reject(err); }); } @@ -158,6 +183,10 @@ const defaultFileSystemExecutor: FileSystemExecutor = { await fs.writeFile(path, content, encoding); }, + createWriteStream(path: string, options?: { flags?: string }) { + return createWriteStream(path, options); + }, + async cp(source: string, destination: string, options?: { recursive?: boolean }): Promise { const fs = await import('fs/promises'); await fs.cp(source, destination, options); @@ -177,7 +206,7 @@ const defaultFileSystemExecutor: FileSystemExecutor = { return existsSync(path); }, - async stat(path: string): Promise<{ isDirectory(): boolean }> { + async stat(path: string): Promise<{ isDirectory(): boolean; mtimeMs: number }> { const fs = await import('fs/promises'); return await fs.stat(path); }, @@ -192,18 +221,35 @@ const defaultFileSystemExecutor: FileSystemExecutor = { }, }; +let _testCommandExecutorOverride: CommandExecutor | null = null; +let _testFileSystemExecutorOverride: FileSystemExecutor | null = null; + +export function __setTestCommandExecutorOverride(executor: CommandExecutor | null): void { + _testCommandExecutorOverride = executor; +} + +export function __setTestFileSystemExecutorOverride(executor: FileSystemExecutor | null): void { + _testFileSystemExecutorOverride = executor; +} + +export function __clearTestExecutorOverrides(): void { + _testCommandExecutorOverride = null; + _testFileSystemExecutorOverride = null; +} + /** * Get default command executor with test safety * Throws error if used in test environment to ensure proper mocking */ export function getDefaultCommandExecutor(): CommandExecutor { if (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test') { + if (_testCommandExecutorOverride) return _testCommandExecutorOverride; throw new Error( `🚨 REAL SYSTEM EXECUTOR DETECTED IN TEST! 🚨\n` + `This test is trying to use the default command executor instead of a mock.\n` + `Fix: Pass createMockExecutor() as the commandExecutor parameter in your test.\n` + `Example: await plugin.handler(args, createMockExecutor({success: true}), mockFileSystem)\n` + - `See docs/TESTING.md for proper testing patterns.`, + `See docs/dev/TESTING.md for proper testing patterns.`, ); } return defaultExecutor; @@ -215,12 +261,13 @@ export function getDefaultCommandExecutor(): CommandExecutor { */ export function getDefaultFileSystemExecutor(): FileSystemExecutor { if (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test') { + if (_testFileSystemExecutorOverride) return _testFileSystemExecutorOverride; throw new Error( `🚨 REAL FILESYSTEM EXECUTOR DETECTED IN TEST! 🚨\n` + `This test is trying to use the default filesystem executor instead of a mock.\n` + `Fix: Pass createMockFileSystemExecutor() as the fileSystemExecutor parameter in your test.\n` + `Example: await plugin.handler(args, mockCmd, createMockFileSystemExecutor())\n` + - `See docs/TESTING.md for proper testing patterns.`, + `See docs/dev/TESTING.md for proper testing patterns.`, ); } return defaultFileSystemExecutor; diff --git a/src/utils/config-store.ts b/src/utils/config-store.ts new file mode 100644 index 00000000..13c7e41d --- /dev/null +++ b/src/utils/config-store.ts @@ -0,0 +1,605 @@ +import type { FileSystemExecutor } from './FileSystemExecutor.ts'; +import type { SessionDefaults } from './session-store.ts'; +import { log } from './logger.ts'; +import { + loadProjectConfig, + persistActiveSessionDefaultsProfileToProjectConfig, + persistSessionDefaultsToProjectConfig, + type ProjectConfig, +} from './project-config.ts'; +import type { DebuggerBackendKind } from './debugger/types.ts'; +import type { UiDebuggerGuardMode } from './runtime-config-types.ts'; +import { normalizeSessionDefaultsProfileName } from './session-defaults-profile.ts'; + +export type RuntimeConfigOverrides = Partial<{ + enabledWorkflows: string[]; + debug: boolean; + experimentalWorkflowDiscovery: boolean; + disableSessionDefaults: boolean; + disableXcodeAutoSync: boolean; + uiDebuggerGuardMode: UiDebuggerGuardMode; + incrementalBuildsEnabled: boolean; + dapRequestTimeoutMs: number; + dapLogEvents: boolean; + launchJsonWaitMs: number; + axePath: string; + iosTemplatePath: string; + iosTemplateVersion: string; + macosTemplatePath: string; + macosTemplateVersion: string; + debuggerBackend: DebuggerBackendKind; + sessionDefaults: Partial; + sessionDefaultsProfiles: Record>; + activeSessionDefaultsProfile: string; +}>; + +export type ResolvedRuntimeConfig = { + enabledWorkflows: string[]; + debug: boolean; + experimentalWorkflowDiscovery: boolean; + disableSessionDefaults: boolean; + disableXcodeAutoSync: boolean; + uiDebuggerGuardMode: UiDebuggerGuardMode; + incrementalBuildsEnabled: boolean; + dapRequestTimeoutMs: number; + dapLogEvents: boolean; + launchJsonWaitMs: number; + axePath?: string; + iosTemplatePath?: string; + iosTemplateVersion?: string; + macosTemplatePath?: string; + macosTemplateVersion?: string; + debuggerBackend: DebuggerBackendKind; + sessionDefaults?: Partial; + sessionDefaultsProfiles?: Record>; + activeSessionDefaultsProfile?: string; +}; + +type ConfigStoreState = { + initialized: boolean; + cwd?: string; + fs?: FileSystemExecutor; + overrides?: RuntimeConfigOverrides; + fileConfig?: ProjectConfig; + resolved: ResolvedRuntimeConfig; +}; + +const DEFAULT_CONFIG: ResolvedRuntimeConfig = { + enabledWorkflows: [], + debug: false, + experimentalWorkflowDiscovery: false, + disableSessionDefaults: false, + disableXcodeAutoSync: false, + uiDebuggerGuardMode: 'error', + incrementalBuildsEnabled: false, + dapRequestTimeoutMs: 30_000, + dapLogEvents: false, + launchJsonWaitMs: 8000, + debuggerBackend: 'dap', +}; + +const storeState: ConfigStoreState = { + initialized: false, + resolved: { ...DEFAULT_CONFIG }, +}; + +function hasOwnProperty( + obj: T | undefined, + key: K, +): obj is T & Record { + if (!obj) return false; + return Object.prototype.hasOwnProperty.call(obj, key); +} + +function parseBoolean(value: string | undefined): boolean | undefined { + if (!value) return undefined; + const normalized = value.trim().toLowerCase(); + if (['1', 'true', 'yes', 'on'].includes(normalized)) return true; + if (['0', 'false', 'no', 'off'].includes(normalized)) return false; + return undefined; +} + +function parsePositiveInt(value: string | undefined): number | undefined { + if (!value) return undefined; + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed <= 0) return undefined; + return Math.floor(parsed); +} + +function parseNonNegativeInt(value: string | undefined): number | undefined { + if (!value) return undefined; + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed < 0) return undefined; + return Math.floor(parsed); +} + +function parseEnabledWorkflows(value: string | undefined): string[] | undefined { + if (value == null) return undefined; + const normalized = value + .split(',') + .map((name) => name.trim().toLowerCase()) + .filter(Boolean); + return normalized; +} + +function parseUiDebuggerGuardMode(value: string | undefined): UiDebuggerGuardMode | undefined { + if (!value) return undefined; + const normalized = value.trim().toLowerCase(); + if (['off', '0', 'false', 'no'].includes(normalized)) return 'off'; + if (['warn', 'warning'].includes(normalized)) return 'warn'; + if (['error', '1', 'true', 'yes', 'on'].includes(normalized)) return 'error'; + return undefined; +} + +function parseDebuggerBackend(value: string | undefined): DebuggerBackendKind | undefined { + if (!value) return undefined; + const normalized = value.trim().toLowerCase(); + if (normalized === 'lldb' || normalized === 'lldb-cli') return 'lldb-cli'; + if (normalized === 'dap') return 'dap'; + log('warning', `Unsupported debugger backend '${value}', falling back to defaults.`); + return undefined; +} + +function getErrorKind(error: unknown): string { + if (error instanceof Error) { + return error.name || 'Error'; + } + return typeof error; +} + +function setIfDefined( + config: RuntimeConfigOverrides, + key: K, + value: RuntimeConfigOverrides[K] | undefined, +): void { + if (value !== undefined) { + config[key] = value; + } +} + +function readEnvConfig(env: NodeJS.ProcessEnv): RuntimeConfigOverrides { + const config: RuntimeConfigOverrides = {}; + + setIfDefined( + config, + 'enabledWorkflows', + parseEnabledWorkflows(env.XCODEBUILDMCP_ENABLED_WORKFLOWS), + ); + + setIfDefined(config, 'debug', parseBoolean(env.XCODEBUILDMCP_DEBUG)); + + setIfDefined( + config, + 'experimentalWorkflowDiscovery', + parseBoolean(env.XCODEBUILDMCP_EXPERIMENTAL_WORKFLOW_DISCOVERY), + ); + + setIfDefined( + config, + 'disableSessionDefaults', + parseBoolean(env.XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS), + ); + + setIfDefined( + config, + 'disableXcodeAutoSync', + parseBoolean(env.XCODEBUILDMCP_DISABLE_XCODE_AUTO_SYNC), + ); + + setIfDefined( + config, + 'uiDebuggerGuardMode', + parseUiDebuggerGuardMode(env.XCODEBUILDMCP_UI_DEBUGGER_GUARD_MODE), + ); + + setIfDefined(config, 'incrementalBuildsEnabled', parseBoolean(env.INCREMENTAL_BUILDS_ENABLED)); + + const axePath = env.XCODEBUILDMCP_AXE_PATH ?? env.AXE_PATH; + if (axePath) config.axePath = axePath; + + const iosTemplatePath = env.XCODEBUILDMCP_IOS_TEMPLATE_PATH; + if (iosTemplatePath) config.iosTemplatePath = iosTemplatePath; + + const macosTemplatePath = env.XCODEBUILDMCP_MACOS_TEMPLATE_PATH; + if (macosTemplatePath) config.macosTemplatePath = macosTemplatePath; + + const iosTemplateVersion = + env.XCODEBUILD_MCP_IOS_TEMPLATE_VERSION ?? env.XCODEBUILD_MCP_TEMPLATE_VERSION; + if (iosTemplateVersion) config.iosTemplateVersion = iosTemplateVersion; + + const macosTemplateVersion = + env.XCODEBUILD_MCP_MACOS_TEMPLATE_VERSION ?? env.XCODEBUILD_MCP_TEMPLATE_VERSION; + if (macosTemplateVersion) config.macosTemplateVersion = macosTemplateVersion; + + setIfDefined(config, 'debuggerBackend', parseDebuggerBackend(env.XCODEBUILDMCP_DEBUGGER_BACKEND)); + + setIfDefined( + config, + 'dapRequestTimeoutMs', + parsePositiveInt(env.XCODEBUILDMCP_DAP_REQUEST_TIMEOUT_MS), + ); + + setIfDefined(config, 'dapLogEvents', parseBoolean(env.XCODEBUILDMCP_DAP_LOG_EVENTS)); + + setIfDefined(config, 'launchJsonWaitMs', parseNonNegativeInt(env.XBMCP_LAUNCH_JSON_WAIT_MS)); + + return config; +} + +function resolveFromLayers(opts: { + key: keyof RuntimeConfigOverrides; + overrides?: RuntimeConfigOverrides; + fileConfig?: ProjectConfig; + envConfig: RuntimeConfigOverrides; + fallback: T; +}): T; +function resolveFromLayers(opts: { + key: keyof RuntimeConfigOverrides; + overrides?: RuntimeConfigOverrides; + fileConfig?: ProjectConfig; + envConfig: RuntimeConfigOverrides; + fallback?: undefined; +}): T | undefined; +function resolveFromLayers(opts: { + key: keyof RuntimeConfigOverrides; + overrides?: RuntimeConfigOverrides; + fileConfig?: ProjectConfig; + envConfig: RuntimeConfigOverrides; + fallback?: T; +}): T | undefined { + const { key, overrides, fileConfig, envConfig, fallback } = opts; + if (hasOwnProperty(overrides, key)) { + return overrides[key] as T | undefined; + } + if (hasOwnProperty(fileConfig, key)) { + return fileConfig[key] as T | undefined; + } + if (hasOwnProperty(envConfig, key)) { + return envConfig[key] as T | undefined; + } + return fallback; +} + +function resolveSessionDefaults(opts: { + overrides?: RuntimeConfigOverrides; + fileConfig?: ProjectConfig; +}): Partial | undefined { + const overrideDefaults = opts.overrides?.sessionDefaults; + const fileDefaults = opts.fileConfig?.sessionDefaults; + if (!overrideDefaults && !fileDefaults) return undefined; + return { ...(fileDefaults ?? {}), ...(overrideDefaults ?? {}) }; +} + +function resolveSessionDefaultsProfiles(opts: { + overrides?: RuntimeConfigOverrides; + fileConfig?: ProjectConfig; +}): Record> | undefined { + const overrideProfiles = opts.overrides?.sessionDefaultsProfiles; + const fileProfiles = opts.fileConfig?.sessionDefaultsProfiles; + if (!overrideProfiles && !fileProfiles) return undefined; + + const merged: Record> = {}; + for (const [name, defaults] of Object.entries(fileProfiles ?? {})) { + merged[name] = { ...defaults }; + } + for (const [name, defaults] of Object.entries(overrideProfiles ?? {})) { + merged[name] = { ...(merged[name] ?? {}), ...defaults }; + } + return merged; +} + +function resolveActiveSessionDefaultsProfile(opts: { + overrides?: RuntimeConfigOverrides; + fileConfig?: ProjectConfig; +}): string | undefined { + if (hasOwnProperty(opts.overrides, 'activeSessionDefaultsProfile')) { + return opts.overrides.activeSessionDefaultsProfile; + } + if (hasOwnProperty(opts.fileConfig, 'activeSessionDefaultsProfile')) { + return opts.fileConfig.activeSessionDefaultsProfile; + } + return undefined; +} + +function refreshResolvedSessionFields(): void { + storeState.resolved.sessionDefaults = resolveSessionDefaults({ + overrides: storeState.overrides, + fileConfig: storeState.fileConfig, + }); + storeState.resolved.sessionDefaultsProfiles = resolveSessionDefaultsProfiles({ + overrides: storeState.overrides, + fileConfig: storeState.fileConfig, + }); + storeState.resolved.activeSessionDefaultsProfile = resolveActiveSessionDefaultsProfile({ + overrides: storeState.overrides, + fileConfig: storeState.fileConfig, + }); +} + +function getCurrentFileConfig(): ProjectConfig { + return storeState.fileConfig ?? { schemaVersion: 1 }; +} + +function applySessionDefaultsPatchToFileConfig(opts: { + fileConfig: ProjectConfig; + profile: string | null; + patch: Partial; + deleteKeys?: (keyof SessionDefaults)[]; +}): ProjectConfig { + const nextFileConfig: ProjectConfig = { ...opts.fileConfig }; + const baseDefaults = + opts.profile === null + ? (nextFileConfig.sessionDefaults ?? {}) + : (nextFileConfig.sessionDefaultsProfiles?.[opts.profile] ?? {}); + + const nextSessionDefaults: Partial = { ...baseDefaults, ...opts.patch }; + for (const key of opts.deleteKeys ?? []) { + delete nextSessionDefaults[key]; + } + + if (opts.profile === null) { + nextFileConfig.sessionDefaults = nextSessionDefaults; + return nextFileConfig; + } + + nextFileConfig.sessionDefaultsProfiles = { + ...(nextFileConfig.sessionDefaultsProfiles ?? {}), + [opts.profile]: nextSessionDefaults, + }; + return nextFileConfig; +} + +function applyActiveProfileToFileConfig(opts: { + fileConfig: ProjectConfig; + profile: string | null; +}): ProjectConfig { + const nextFileConfig: ProjectConfig = { ...opts.fileConfig }; + if (opts.profile === null) { + delete nextFileConfig.activeSessionDefaultsProfile; + } else { + nextFileConfig.activeSessionDefaultsProfile = opts.profile; + } + return nextFileConfig; +} + +function resolveConfig(opts: { + fileConfig?: ProjectConfig; + overrides?: RuntimeConfigOverrides; + env?: NodeJS.ProcessEnv; +}): ResolvedRuntimeConfig { + const envConfig = readEnvConfig(opts.env ?? process.env); + + return { + enabledWorkflows: resolveFromLayers({ + key: 'enabledWorkflows', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.enabledWorkflows, + }), + debug: resolveFromLayers({ + key: 'debug', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.debug, + }), + experimentalWorkflowDiscovery: resolveFromLayers({ + key: 'experimentalWorkflowDiscovery', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.experimentalWorkflowDiscovery, + }), + disableSessionDefaults: resolveFromLayers({ + key: 'disableSessionDefaults', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.disableSessionDefaults, + }), + disableXcodeAutoSync: resolveFromLayers({ + key: 'disableXcodeAutoSync', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.disableXcodeAutoSync, + }), + uiDebuggerGuardMode: resolveFromLayers({ + key: 'uiDebuggerGuardMode', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.uiDebuggerGuardMode, + }), + incrementalBuildsEnabled: resolveFromLayers({ + key: 'incrementalBuildsEnabled', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.incrementalBuildsEnabled, + }), + dapRequestTimeoutMs: resolveFromLayers({ + key: 'dapRequestTimeoutMs', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.dapRequestTimeoutMs, + }), + dapLogEvents: resolveFromLayers({ + key: 'dapLogEvents', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.dapLogEvents, + }), + launchJsonWaitMs: resolveFromLayers({ + key: 'launchJsonWaitMs', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.launchJsonWaitMs, + }), + axePath: resolveFromLayers({ + key: 'axePath', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + }), + iosTemplatePath: resolveFromLayers({ + key: 'iosTemplatePath', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + }), + iosTemplateVersion: resolveFromLayers({ + key: 'iosTemplateVersion', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + }), + macosTemplatePath: resolveFromLayers({ + key: 'macosTemplatePath', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + }), + macosTemplateVersion: resolveFromLayers({ + key: 'macosTemplateVersion', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + }), + debuggerBackend: resolveFromLayers({ + key: 'debuggerBackend', + overrides: opts.overrides, + fileConfig: opts.fileConfig, + envConfig, + fallback: DEFAULT_CONFIG.debuggerBackend, + }), + sessionDefaults: resolveSessionDefaults({ + overrides: opts.overrides, + fileConfig: opts.fileConfig, + }), + sessionDefaultsProfiles: resolveSessionDefaultsProfiles({ + overrides: opts.overrides, + fileConfig: opts.fileConfig, + }), + activeSessionDefaultsProfile: resolveActiveSessionDefaultsProfile({ + overrides: opts.overrides, + fileConfig: opts.fileConfig, + }), + }; +} + +export async function initConfigStore(opts: { + cwd: string; + fs: FileSystemExecutor; + overrides?: RuntimeConfigOverrides; + env?: NodeJS.ProcessEnv; +}): Promise<{ found: boolean; path?: string; notices: string[] }> { + storeState.cwd = opts.cwd; + storeState.fs = opts.fs; + storeState.overrides = opts.overrides; + + let fileConfig: ProjectConfig | undefined; + let found = false; + let path: string | undefined; + let notices: string[] = []; + + try { + const result = await loadProjectConfig({ fs: opts.fs, cwd: opts.cwd }); + if (result.found) { + fileConfig = result.config; + found = true; + path = result.path; + notices = result.notices; + } else if ('error' in result) { + const errorMessage = + result.error instanceof Error ? result.error.message : String(result.error); + log('warning', `Failed to read or parse project config at ${result.path}. ${errorMessage}`); + log('warning', '[infra/config-store] project config read/parse failed', { sentry: true }); + } + } catch (error) { + log('warning', `Failed to load project config from ${opts.cwd}. ${error}`); + log('warning', `[infra/config-store] project config load threw (${getErrorKind(error)})`, { + sentry: true, + }); + } + + storeState.fileConfig = fileConfig; + storeState.resolved = resolveConfig({ fileConfig, overrides: opts.overrides, env: opts.env }); + storeState.initialized = true; + return { found, path, notices }; +} + +export function getConfig(): ResolvedRuntimeConfig { + if (!storeState.initialized) { + return resolveConfig({}); + } + + return storeState.resolved; +} + +export async function persistSessionDefaultsPatch(opts: { + patch: Partial; + deleteKeys?: (keyof SessionDefaults)[]; + profile?: string | null; +}): Promise<{ path: string }> { + if (!storeState.initialized || !storeState.fs || !storeState.cwd) { + throw new Error('Config store has not been initialized.'); + } + + const normalizedProfile = normalizeSessionDefaultsProfileName(opts.profile); + + const result = await persistSessionDefaultsToProjectConfig({ + fs: storeState.fs, + cwd: storeState.cwd, + patch: opts.patch, + deleteKeys: opts.deleteKeys, + profile: normalizedProfile, + }); + + storeState.fileConfig = applySessionDefaultsPatchToFileConfig({ + fileConfig: getCurrentFileConfig(), + profile: normalizedProfile, + patch: opts.patch, + deleteKeys: opts.deleteKeys, + }); + refreshResolvedSessionFields(); + + return result; +} + +export async function persistActiveSessionDefaultsProfile( + profile: string | null, +): Promise<{ path: string }> { + if (!storeState.initialized || !storeState.fs || !storeState.cwd) { + throw new Error('Config store has not been initialized.'); + } + + const normalizedProfile = normalizeSessionDefaultsProfileName(profile); + + const result = await persistActiveSessionDefaultsProfileToProjectConfig({ + fs: storeState.fs, + cwd: storeState.cwd, + profile: normalizedProfile, + }); + + storeState.fileConfig = applyActiveProfileToFileConfig({ + fileConfig: getCurrentFileConfig(), + profile: normalizedProfile, + }); + refreshResolvedSessionFields(); + + return result; +} + +export function __resetConfigStoreForTests(): void { + storeState.initialized = false; + storeState.cwd = undefined; + storeState.fs = undefined; + storeState.overrides = undefined; + storeState.fileConfig = undefined; + storeState.resolved = { ...DEFAULT_CONFIG }; +} diff --git a/src/utils/debugger/__tests__/debugger-manager-dap.test.ts b/src/utils/debugger/__tests__/debugger-manager-dap.test.ts new file mode 100644 index 00000000..b809dd42 --- /dev/null +++ b/src/utils/debugger/__tests__/debugger-manager-dap.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it } from 'vitest'; + +import type { BreakpointInfo, BreakpointSpec } from '../types.ts'; +import type { DebuggerBackend } from '../backends/DebuggerBackend.ts'; +import { DebuggerManager } from '../debugger-manager.ts'; +import { + __resetConfigStoreForTests, + initConfigStore, + type RuntimeConfigOverrides, +} from '../../config-store.ts'; +import { createMockFileSystemExecutor } from '../../../test-utils/mock-executors.ts'; + +const cwd = '/repo'; + +async function initConfigStoreForTest(overrides?: RuntimeConfigOverrides): Promise { + __resetConfigStoreForTests(); + await initConfigStore({ cwd, fs: createMockFileSystemExecutor(), overrides }); +} + +function createBackend(overrides: Partial = {}): DebuggerBackend { + const base: DebuggerBackend = { + kind: 'dap', + attach: async () => {}, + detach: async () => {}, + runCommand: async () => '', + resume: async () => {}, + addBreakpoint: async (spec: BreakpointSpec): Promise => ({ + id: 1, + spec, + rawOutput: '', + }), + removeBreakpoint: async () => '', + getStack: async () => '', + getVariables: async () => '', + getExecutionState: async () => ({ status: 'unknown' }), + dispose: async () => {}, + }; + + return { ...base, ...overrides }; +} + +describe('DebuggerManager DAP selection', () => { + it('selects dap backend when config override is set', async () => { + await initConfigStoreForTest({ debuggerBackend: 'dap' }); + + let selected: string | null = null; + const backend = createBackend({ kind: 'dap' }); + const manager = new DebuggerManager({ + backendFactory: async (kind) => { + selected = kind; + return backend; + }, + }); + + await manager.createSession({ simulatorId: 'sim-1', pid: 1000 }); + + expect(selected).toBe('dap'); + }); + + it('disposes backend when attach fails without masking error', async () => { + const error = new Error('attach failed'); + let disposeCalled = false; + + const backend = createBackend({ + attach: async () => { + throw error; + }, + dispose: async () => { + disposeCalled = true; + throw new Error('dispose failed'); + }, + }); + + const manager = new DebuggerManager({ + backendFactory: async () => backend, + }); + + await expect( + manager.createSession({ simulatorId: 'sim-1', pid: 2000, backend: 'dap' }), + ).rejects.toThrow('attach failed'); + expect(disposeCalled).toBe(true); + }); +}); diff --git a/src/utils/debugger/backends/DebuggerBackend.ts b/src/utils/debugger/backends/DebuggerBackend.ts new file mode 100644 index 00000000..5e817daa --- /dev/null +++ b/src/utils/debugger/backends/DebuggerBackend.ts @@ -0,0 +1,20 @@ +import type { BreakpointInfo, BreakpointSpec, DebugExecutionState } from '../types.ts'; + +export interface DebuggerBackend { + readonly kind: 'lldb-cli' | 'dap'; + + attach(opts: { pid: number; simulatorId: string; waitFor?: boolean }): Promise; + detach(): Promise; + + runCommand(command: string, opts?: { timeoutMs?: number }): Promise; + resume(opts?: { threadId?: number }): Promise; + + addBreakpoint(spec: BreakpointSpec, opts?: { condition?: string }): Promise; + removeBreakpoint(id: number): Promise; + + getStack(opts?: { threadIndex?: number; maxFrames?: number }): Promise; + getVariables(opts?: { frameIndex?: number }): Promise; + getExecutionState(opts?: { timeoutMs?: number }): Promise; + + dispose(): Promise; +} diff --git a/src/utils/debugger/backends/__tests__/dap-backend.test.ts b/src/utils/debugger/backends/__tests__/dap-backend.test.ts new file mode 100644 index 00000000..8aa01bf5 --- /dev/null +++ b/src/utils/debugger/backends/__tests__/dap-backend.test.ts @@ -0,0 +1,171 @@ +import { describe, expect, it } from 'vitest'; + +import type { DapEvent, DapRequest, DapResponse } from '../../dap/types.ts'; +import { createDapBackend } from '../dap-backend.ts'; +import { + createMockExecutor, + createMockInteractiveSpawner, + type MockInteractiveSession, +} from '../../../../test-utils/mock-executors.ts'; +import type { BreakpointSpec } from '../../types.ts'; + +type ResponsePlan = { + body?: Record; + events?: DapEvent[]; +}; + +function encodeMessage(message: Record): string { + const payload = JSON.stringify(message); + return `Content-Length: ${Buffer.byteLength(payload, 'utf8')}\r\n\r\n${payload}`; +} + +function createDapSpawner(handlers: Record ResponsePlan>) { + let buffer = Buffer.alloc(0); + let responseSeq = 1000; + + return createMockInteractiveSpawner({ + onWrite: (data: string, session: MockInteractiveSession) => { + buffer = Buffer.concat([buffer, Buffer.from(data, 'utf8')]); + while (true) { + const headerEnd = buffer.indexOf('\r\n\r\n'); + if (headerEnd === -1) return; + const header = buffer.slice(0, headerEnd).toString('utf8'); + const match = header.match(/Content-Length:\s*(\d+)/i); + if (!match) { + buffer = buffer.slice(headerEnd + 4); + continue; + } + const length = Number(match[1]); + const bodyStart = headerEnd + 4; + const bodyEnd = bodyStart + length; + if (buffer.length < bodyEnd) return; + + const body = buffer.slice(bodyStart, bodyEnd).toString('utf8'); + buffer = buffer.slice(bodyEnd); + const request = JSON.parse(body) as DapRequest; + const handler = handlers[request.command]; + if (!handler) { + throw new Error(`Unexpected DAP request: ${request.command}`); + } + const plan = handler(request); + if (plan.events) { + for (const event of plan.events) { + session.stdout.write(encodeMessage(event)); + } + } + const response: DapResponse = { + seq: responseSeq++, + type: 'response', + request_seq: request.seq, + success: true, + command: request.command, + body: plan.body, + }; + session.stdout.write(encodeMessage(response)); + } + }, + }); +} + +function createDefaultHandlers() { + return { + initialize: () => ({ body: { supportsConfigurationDoneRequest: true } }), + attach: () => ({ body: {} }), + configurationDone: () => ({ body: {} }), + threads: () => ({ body: { threads: [{ id: 1, name: 'main' }] } }), + stackTrace: () => ({ + body: { + stackFrames: [ + { + id: 11, + name: 'main', + source: { path: '/tmp/main.swift' }, + line: 42, + }, + ], + }, + }), + scopes: () => ({ + body: { + scopes: [{ name: 'Locals', variablesReference: 100 }], + }, + }), + variables: () => ({ + body: { + variables: [{ name: 'answer', value: '42', type: 'Int' }], + }, + }), + evaluate: () => ({ + body: { + result: 'ok', + output: 'evaluated', + }, + }), + setBreakpoints: (request: DapRequest) => { + const args = request.arguments as { breakpoints: Array<{ line: number }> }; + const breakpoints = (args?.breakpoints ?? []).map((bp, index) => ({ + id: 100 + index, + line: bp.line, + verified: true, + })); + return { body: { breakpoints } }; + }, + setFunctionBreakpoints: (request: DapRequest) => { + const args = request.arguments as { breakpoints: Array<{ name: string }> }; + const breakpoints = (args?.breakpoints ?? []).map((bp, index) => ({ + id: 200 + index, + verified: true, + })); + return { body: { breakpoints } }; + }, + disconnect: () => ({ body: {} }), + } satisfies Record ResponsePlan>; +} + +describe('DapBackend', () => { + it('maps stack, variables, and evaluate', async () => { + const handlers = createDefaultHandlers(); + const spawner = createDapSpawner(handlers); + const executor = createMockExecutor({ success: true, output: '/usr/bin/lldb-dap' }); + + const backend = await createDapBackend({ executor, spawner, requestTimeoutMs: 1_000 }); + await backend.attach({ pid: 4242, simulatorId: 'sim-1' }); + + const stack = await backend.getStack(); + expect(stack).toContain('frame #0: main at /tmp/main.swift:42'); + + const vars = await backend.getVariables(); + expect(vars).toContain('Locals'); + expect(vars).toContain('answer (Int) = 42'); + + const output = await backend.runCommand('frame variable'); + expect(output).toContain('evaluated'); + + await backend.detach(); + await backend.dispose(); + }); + + it('adds and removes breakpoints', async () => { + const handlers = createDefaultHandlers(); + const spawner = createDapSpawner(handlers); + const executor = createMockExecutor({ success: true, output: '/usr/bin/lldb-dap' }); + + const backend = await createDapBackend({ executor, spawner, requestTimeoutMs: 1_000 }); + await backend.attach({ pid: 4242, simulatorId: 'sim-1' }); + + const fileSpec: BreakpointSpec = { kind: 'file-line', file: '/tmp/main.swift', line: 12 }; + const fileBreakpoint = await backend.addBreakpoint(fileSpec, { condition: 'answer == 42' }); + expect(fileBreakpoint.id).toBe(100); + + await backend.removeBreakpoint(fileBreakpoint.id); + + const fnSpec: BreakpointSpec = { kind: 'function', name: 'doWork' }; + const fnBreakpoint = await backend.addBreakpoint(fnSpec); + expect(fnBreakpoint.id).toBe(200); + + await backend.removeBreakpoint(fnBreakpoint.id); + + await backend.detach(); + await backend.dispose(); + }); +}); diff --git a/src/utils/debugger/backends/dap-backend.ts b/src/utils/debugger/backends/dap-backend.ts new file mode 100644 index 00000000..ae1566e8 --- /dev/null +++ b/src/utils/debugger/backends/dap-backend.ts @@ -0,0 +1,598 @@ +import type { DebuggerBackend } from './DebuggerBackend.ts'; +import type { BreakpointInfo, BreakpointSpec, DebugExecutionState } from '../types.ts'; +import type { CommandExecutor, InteractiveSpawner } from '../../execution/index.ts'; +import { getDefaultCommandExecutor, getDefaultInteractiveSpawner } from '../../execution/index.ts'; +import { log } from '../../logging/index.ts'; +import { getConfig } from '../../config-store.ts'; +import type { + DapEvent, + EvaluateResponseBody, + ScopesResponseBody, + SetBreakpointsResponseBody, + StackTraceResponseBody, + StoppedEventBody, + ThreadsResponseBody, + VariablesResponseBody, +} from '../dap/types.ts'; +import { DapTransport } from '../dap/transport.ts'; +import { resolveLldbDapCommand } from '../dap/adapter-discovery.ts'; + +const LOG_PREFIX = '[DAP Backend]'; + +type FileLineBreakpointRecord = { line: number; condition?: string; id?: number }; +type FunctionBreakpointRecord = { name: string; condition?: string; id?: number }; + +type BreakpointRecord = { + spec: BreakpointSpec; + condition?: string; +}; + +class DapBackend implements DebuggerBackend { + readonly kind = 'dap' as const; + + private readonly executor: CommandExecutor; + private readonly spawner: InteractiveSpawner; + private readonly requestTimeoutMs: number; + private readonly logEvents: boolean; + + private transport: DapTransport | null = null; + private unsubscribeEvents: (() => void) | null = null; + private attached = false; + private disposed = false; + private queue: Promise = Promise.resolve(); + + private lastStoppedThreadId: number | null = null; + private executionState: DebugExecutionState = { status: 'unknown' }; + private breakpointsById = new Map(); + private fileLineBreakpointsByFile = new Map(); + private functionBreakpoints: FunctionBreakpointRecord[] = []; + private nextSyntheticId = -1; + + constructor(opts: { + executor: CommandExecutor; + spawner: InteractiveSpawner; + requestTimeoutMs: number; + logEvents: boolean; + }) { + this.executor = opts.executor; + this.spawner = opts.spawner; + this.requestTimeoutMs = opts.requestTimeoutMs; + this.logEvents = opts.logEvents; + } + + async attach(opts: { pid: number; simulatorId: string; waitFor?: boolean }): Promise { + void opts.simulatorId; + return this.enqueue(async () => { + if (this.disposed) { + throw new Error('DAP backend disposed'); + } + if (this.attached) { + throw new Error('DAP backend already attached'); + } + + const adapterCommand = await resolveLldbDapCommand({ executor: this.executor }); + const transport = new DapTransport({ + spawner: this.spawner, + adapterCommand, + logPrefix: LOG_PREFIX, + }); + this.transport = transport; + this.unsubscribeEvents = transport.onEvent((event) => this.handleEvent(event)); + + try { + const init = await this.request< + { + clientID: string; + clientName: string; + adapterID: string; + linesStartAt1: boolean; + columnsStartAt1: boolean; + pathFormat: string; + supportsVariableType: boolean; + supportsVariablePaging: boolean; + }, + { supportsConfigurationDoneRequest?: boolean } + >('initialize', { + clientID: 'xcodebuildmcp', + clientName: 'XcodeBuildMCP', + adapterID: 'lldb-dap', + linesStartAt1: true, + columnsStartAt1: true, + pathFormat: 'path', + supportsVariableType: true, + supportsVariablePaging: false, + }); + + await this.request('attach', { + pid: opts.pid, + waitFor: opts.waitFor ?? false, + }); + + if (init.supportsConfigurationDoneRequest !== false) { + await this.request('configurationDone', {}); + } + + this.attached = true; + log('info', `${LOG_PREFIX} attached to pid ${opts.pid}`); + } catch (error) { + this.cleanupTransport(); + throw error; + } + }); + } + + async detach(): Promise { + return this.enqueue(async () => { + if (!this.transport) return; + try { + await this.request('disconnect', { terminateDebuggee: false }); + } finally { + this.cleanupTransport(); + } + }); + } + + async runCommand(command: string, opts?: { timeoutMs?: number }): Promise { + this.ensureAttached(); + + try { + const body = await this.request< + { expression: string; context: string }, + EvaluateResponseBody + >('evaluate', { expression: command, context: 'repl' }, opts); + return formatEvaluateResult(body); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (/evaluate|repl|not supported/i.test(message)) { + throw new Error( + 'DAP backend does not support LLDB command evaluation. Set XCODEBUILDMCP_DEBUGGER_BACKEND=lldb-cli to use the CLI backend.', + ); + } + throw error; + } + } + + async resume(opts?: { threadId?: number }): Promise { + return this.enqueue(async () => { + this.ensureAttached(); + + let threadId = opts?.threadId; + if (!threadId) { + const thread = await this.resolveThread(); + threadId = thread.id; + } + + await this.request('continue', { threadId }); + this.executionState = { status: 'running' }; + this.lastStoppedThreadId = null; + }); + } + + async addBreakpoint( + spec: BreakpointSpec, + opts?: { condition?: string }, + ): Promise { + return this.enqueue(async () => { + this.ensureAttached(); + + if (spec.kind === 'file-line') { + const current = this.fileLineBreakpointsByFile.get(spec.file) ?? []; + const nextBreakpoints = [...current, { line: spec.line, condition: opts?.condition }]; + const updated = await this.setFileBreakpoints(spec.file, nextBreakpoints); + const added = updated[nextBreakpoints.length - 1]; + if (!added?.id) { + throw new Error('DAP breakpoint id missing for file breakpoint.'); + } + return { + id: added.id, + spec, + rawOutput: `Set breakpoint ${added.id} at ${spec.file}:${spec.line}`, + }; + } + + const nextBreakpoints = [ + ...this.functionBreakpoints, + { name: spec.name, condition: opts?.condition }, + ]; + const updated = await this.setFunctionBreakpoints(nextBreakpoints); + const added = updated[nextBreakpoints.length - 1]; + if (!added?.id) { + throw new Error('DAP breakpoint id missing for function breakpoint.'); + } + return { + id: added.id, + spec, + rawOutput: `Set breakpoint ${added.id} on ${spec.name}`, + }; + }); + } + + async removeBreakpoint(id: number): Promise { + return this.enqueue(async () => { + this.ensureAttached(); + + const record = this.breakpointsById.get(id); + if (!record) { + throw new Error(`Breakpoint not found: ${id}`); + } + + if (record.spec.kind === 'file-line') { + const current = this.fileLineBreakpointsByFile.get(record.spec.file) ?? []; + const nextBreakpoints = current.filter((breakpoint) => breakpoint.id !== id); + await this.setFileBreakpoints(record.spec.file, nextBreakpoints); + } else { + const nextBreakpoints = this.functionBreakpoints.filter( + (breakpoint) => breakpoint.id !== id, + ); + await this.setFunctionBreakpoints(nextBreakpoints); + } + + return `Removed breakpoint ${id}.`; + }); + } + + async getStack(opts?: { threadIndex?: number; maxFrames?: number }): Promise { + this.ensureAttached(); + + try { + const thread = await this.resolveThread(opts?.threadIndex); + const stack = await this.request< + { threadId: number; startFrame?: number; levels?: number }, + StackTraceResponseBody + >('stackTrace', { + threadId: thread.id, + startFrame: 0, + levels: opts?.maxFrames, + }); + + if (!stack.stackFrames.length) { + return `Thread ${thread.id}: no stack frames.`; + } + + const threadLabel = thread.name + ? `Thread ${thread.id} (${thread.name})` + : `Thread ${thread.id}`; + const formatted = stack.stackFrames.map((frame, index) => { + const location = frame.source?.path ?? frame.source?.name ?? 'unknown'; + const line = frame.line ?? 0; + return `frame #${index}: ${frame.name} at ${location}:${line}`; + }); + + return [threadLabel, ...formatted].join('\n'); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (/running|not stopped|no thread|no frames/i.test(message)) { + throw new Error('Process is running; pause or hit a breakpoint to fetch stack.'); + } + throw error; + } + } + + async getVariables(opts?: { frameIndex?: number }): Promise { + this.ensureAttached(); + + try { + const thread = await this.resolveThread(); + const frameIndex = opts?.frameIndex ?? 0; + const stack = await this.request< + { threadId: number; startFrame?: number; levels?: number }, + StackTraceResponseBody + >('stackTrace', { + threadId: thread.id, + startFrame: 0, + levels: frameIndex + 1, + }); + + if (stack.stackFrames.length <= frameIndex) { + throw new Error(`Frame index ${frameIndex} is out of range.`); + } + + const frame = stack.stackFrames[frameIndex]; + const scopes = await this.request<{ frameId: number }, ScopesResponseBody>('scopes', { + frameId: frame.id, + }); + + if (!scopes.scopes.length) { + return 'No scopes available.'; + } + + const sections: string[] = []; + for (const scope of scopes.scopes) { + if (!scope.variablesReference) { + sections.push(`${scope.name}:\n (no variables)`); + continue; + } + + const vars = await this.request<{ variablesReference: number }, VariablesResponseBody>( + 'variables', + { + variablesReference: scope.variablesReference, + }, + ); + + if (!vars.variables.length) { + sections.push(`${scope.name}:\n (no variables)`); + continue; + } + + const lines = vars.variables.map((variable) => ` ${formatVariable(variable)}`); + sections.push(`${scope.name}:\n${lines.join('\n')}`); + } + + return sections.join('\n\n'); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (/running|not stopped|no thread/i.test(message)) { + throw new Error('Process is running; pause or hit a breakpoint to fetch variables.'); + } + throw error; + } + } + + async getExecutionState(opts?: { timeoutMs?: number }): Promise { + return this.enqueue(async () => { + this.ensureAttached(); + + if (this.executionState.status !== 'unknown') { + return this.executionState; + } + + try { + const body = await this.request('threads', undefined, opts); + const threads = body.threads ?? []; + if (!threads.length) { + return { status: 'unknown' }; + } + + const threadId = threads[0].id; + try { + await this.request< + { threadId: number; startFrame?: number; levels?: number }, + StackTraceResponseBody + >( + 'stackTrace', + { threadId, startFrame: 0, levels: 1 }, + { timeoutMs: opts?.timeoutMs ?? this.requestTimeoutMs }, + ); + const state: DebugExecutionState = { status: 'stopped', threadId }; + this.executionState = state; + return state; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (/running|not stopped/i.test(message)) { + const state: DebugExecutionState = { status: 'running', description: message }; + this.executionState = state; + return state; + } + return { status: 'unknown', description: message }; + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (/running|not stopped/i.test(message)) { + return { status: 'running', description: message }; + } + return { status: 'unknown', description: message }; + } + }); + } + + async dispose(): Promise { + if (this.disposed) return; + this.disposed = true; + try { + this.cleanupTransport(); + } catch (error) { + log('debug', `${LOG_PREFIX} dispose failed: ${String(error)}`); + } + } + + private ensureAttached(): void { + if (!this.transport || !this.attached) { + throw new Error('No active DAP session. Attach first.'); + } + } + + private async request( + command: string, + args?: A, + opts?: { timeoutMs?: number }, + ): Promise { + const transport = this.transport; + if (!transport) { + throw new Error('DAP transport not initialized.'); + } + + return transport.sendRequest(command, args, { + timeoutMs: opts?.timeoutMs ?? this.requestTimeoutMs, + }); + } + + private async resolveThread(threadIndex?: number): Promise<{ id: number; name?: string }> { + const body = await this.request('threads'); + const threads = body.threads ?? []; + if (!threads.length) { + throw new Error('No threads available.'); + } + + if (typeof threadIndex === 'number') { + if (threadIndex < 0 || threadIndex >= threads.length) { + throw new Error(`Thread index ${threadIndex} is out of range.`); + } + return threads[threadIndex]; + } + + if (this.lastStoppedThreadId) { + const stopped = threads.find((thread) => thread.id === this.lastStoppedThreadId); + if (stopped) { + return stopped; + } + } + + return threads[0]; + } + + private handleEvent(event: DapEvent): void { + if (this.logEvents) { + log('debug', `${LOG_PREFIX} event: ${JSON.stringify(event)}`); + } + + if (event.event === 'stopped') { + const body = event.body as StoppedEventBody | undefined; + this.executionState = { + status: 'stopped', + reason: body?.reason, + description: body?.description, + threadId: body?.threadId, + }; + if (body?.threadId) { + this.lastStoppedThreadId = body.threadId; + } + return; + } + + if (event.event === 'continued') { + this.executionState = { status: 'running' }; + this.lastStoppedThreadId = null; + return; + } + + if (event.event === 'exited' || event.event === 'terminated') { + this.executionState = { status: 'terminated' }; + this.lastStoppedThreadId = null; + } + } + + private cleanupTransport(): void { + this.attached = false; + this.lastStoppedThreadId = null; + this.executionState = { status: 'unknown' }; + this.unsubscribeEvents?.(); + this.unsubscribeEvents = null; + + if (this.transport) { + this.transport.dispose(); + this.transport = null; + } + } + + private async setFileBreakpoints( + file: string, + breakpoints: FileLineBreakpointRecord[], + ): Promise { + const response = await this.request< + { source: { path: string }; breakpoints: Array<{ line: number; condition?: string }> }, + SetBreakpointsResponseBody + >('setBreakpoints', { + source: { path: file }, + breakpoints: breakpoints.map((bp) => ({ line: bp.line, condition: bp.condition })), + }); + + const updated = breakpoints.map((bp, index) => ({ + ...bp, + id: resolveBreakpointId(response.breakpoints?.[index]?.id, () => this.nextSyntheticId--), + })); + + this.replaceFileBreakpoints(file, updated); + return updated; + } + + private replaceFileBreakpoints(file: string, breakpoints: FileLineBreakpointRecord[]): void { + const existing = this.fileLineBreakpointsByFile.get(file) ?? []; + for (const breakpoint of existing) { + if (breakpoint.id != null) { + this.breakpointsById.delete(breakpoint.id); + } + } + + this.fileLineBreakpointsByFile.set(file, breakpoints); + for (const breakpoint of breakpoints) { + if (breakpoint.id != null) { + this.breakpointsById.set(breakpoint.id, { + spec: { kind: 'file-line', file, line: breakpoint.line }, + condition: breakpoint.condition, + }); + } + } + } + + private async setFunctionBreakpoints( + breakpoints: FunctionBreakpointRecord[], + ): Promise { + const response = await this.request< + { breakpoints: Array<{ name: string; condition?: string }> }, + SetBreakpointsResponseBody + >('setFunctionBreakpoints', { + breakpoints: breakpoints.map((bp) => ({ name: bp.name, condition: bp.condition })), + }); + + const updated = breakpoints.map((bp, index) => ({ + ...bp, + id: resolveBreakpointId(response.breakpoints?.[index]?.id, () => this.nextSyntheticId--), + })); + + this.replaceFunctionBreakpoints(updated); + return updated; + } + + private replaceFunctionBreakpoints(breakpoints: FunctionBreakpointRecord[]): void { + for (const breakpoint of this.functionBreakpoints) { + if (breakpoint.id != null) { + this.breakpointsById.delete(breakpoint.id); + } + } + + this.functionBreakpoints = breakpoints; + for (const breakpoint of breakpoints) { + if (breakpoint.id != null) { + this.breakpointsById.set(breakpoint.id, { + spec: { kind: 'function', name: breakpoint.name }, + condition: breakpoint.condition, + }); + } + } + } + + private enqueue(work: () => Promise): Promise { + const next = this.queue.then(work, work) as Promise; + this.queue = next.then( + () => undefined, + () => undefined, + ); + return next; + } +} + +function resolveBreakpointId(id: number | undefined, fallback: () => number): number { + if (typeof id === 'number' && Number.isFinite(id)) { + return id; + } + return fallback(); +} + +function formatEvaluateResult(body: EvaluateResponseBody): string { + const parts = [body.output, body.result].filter((value) => value && value.trim().length > 0); + return parts.join('\n'); +} + +function formatVariable(variable: { name: string; value: string; type?: string }): string { + const typeSuffix = variable.type ? ` (${variable.type})` : ''; + return `${variable.name}${typeSuffix} = ${variable.value}`; +} + +export async function createDapBackend(opts?: { + executor?: CommandExecutor; + spawner?: InteractiveSpawner; + requestTimeoutMs?: number; +}): Promise { + const config = getConfig(); + const executor = opts?.executor ?? getDefaultCommandExecutor(); + const spawner = opts?.spawner ?? getDefaultInteractiveSpawner(); + const requestTimeoutMs = opts?.requestTimeoutMs ?? config.dapRequestTimeoutMs; + const backend = new DapBackend({ + executor, + spawner, + requestTimeoutMs, + logEvents: config.dapLogEvents, + }); + return backend; +} diff --git a/src/utils/debugger/backends/lldb-cli-backend.ts b/src/utils/debugger/backends/lldb-cli-backend.ts new file mode 100644 index 00000000..c297054a --- /dev/null +++ b/src/utils/debugger/backends/lldb-cli-backend.ts @@ -0,0 +1,297 @@ +import type { InteractiveProcess, InteractiveSpawner } from '../../execution/index.ts'; +import { getDefaultInteractiveSpawner } from '../../execution/index.ts'; +import type { DebuggerBackend } from './DebuggerBackend.ts'; +import type { BreakpointInfo, BreakpointSpec, DebugExecutionState } from '../types.ts'; + +const DEFAULT_COMMAND_TIMEOUT_MS = 30_000; +const DEFAULT_STARTUP_TIMEOUT_MS = 10_000; +const LLDB_PROMPT = 'XCODEBUILDMCP_LLDB> '; +const COMMAND_SENTINEL = '__XCODEBUILDMCP_DONE__'; +const COMMAND_SENTINEL_REGEX = new RegExp(`(^|\\r?\\n)${COMMAND_SENTINEL}(\\r?\\n)`); + +class LldbCliBackend implements DebuggerBackend { + readonly kind = 'lldb-cli' as const; + + private readonly spawner: InteractiveSpawner; + private readonly prompt = LLDB_PROMPT; + private readonly process: InteractiveProcess; + private buffer = ''; + private pending: { + resolve: (output: string) => void; + reject: (error: Error) => void; + timeout: NodeJS.Timeout; + } | null = null; + private queue: Promise = Promise.resolve(); + private ready: Promise; + private disposed = false; + + constructor(spawner: InteractiveSpawner) { + this.spawner = spawner; + const lldbCommand = [ + 'xcrun', + 'lldb', + '--no-lldbinit', + '-o', + `settings set prompt "${this.prompt}"`, + ]; + + this.process = this.spawner(lldbCommand); + + this.process.process.stdout?.on('data', (data: Buffer) => this.handleData(data)); + this.process.process.stderr?.on('data', (data: Buffer) => this.handleData(data)); + this.process.process.on('exit', (code, signal) => { + const detail = signal ? `signal ${signal}` : `code ${code ?? 'unknown'}`; + this.failPending(new Error(`LLDB process exited (${detail})`)); + }); + + this.ready = this.initialize(); + } + + private async initialize(): Promise { + // Prime the prompt by running a sentinel command we can parse reliably. + this.process.write(`script print("${COMMAND_SENTINEL}")\n`); + await this.waitForSentinel(DEFAULT_STARTUP_TIMEOUT_MS); + } + + async waitUntilReady(): Promise { + await this.ready; + } + + async attach(opts: { pid: number; simulatorId: string; waitFor?: boolean }): Promise { + const command = opts.waitFor + ? `process attach --pid ${opts.pid} --waitfor` + : `process attach --pid ${opts.pid}`; + const output = await this.runCommand(command); + assertNoLldbError('attach', output); + } + + async detach(): Promise { + const output = await this.runCommand('process detach'); + assertNoLldbError('detach', output); + } + + async runCommand(command: string, opts?: { timeoutMs?: number }): Promise { + return this.enqueue(async () => { + if (this.disposed) { + throw new Error('LLDB backend disposed'); + } + await this.ready; + this.process.write(`${command}\n`); + this.process.write(`script print("${COMMAND_SENTINEL}")\n`); + const output = await this.waitForSentinel(opts?.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS); + return sanitizeOutput(output, this.prompt).trimEnd(); + }); + } + + async resume(): Promise { + return this.enqueue(async () => { + if (this.disposed) { + throw new Error('LLDB backend disposed'); + } + await this.ready; + this.process.write('process continue\n'); + }); + } + + async addBreakpoint( + spec: BreakpointSpec, + opts?: { condition?: string }, + ): Promise { + const command = + spec.kind === 'file-line' + ? `breakpoint set --file "${spec.file}" --line ${spec.line}` + : `breakpoint set --name "${spec.name}"`; + const output = await this.runCommand(command); + assertNoLldbError('breakpoint', output); + + const match = output.match(/Breakpoint\s+(\d+):/); + if (!match) { + throw new Error(`Unable to parse breakpoint id from output: ${output}`); + } + + const id = Number(match[1]); + + if (opts?.condition) { + const condition = formatConditionForLldb(opts.condition); + const modifyOutput = await this.runCommand(`breakpoint modify -c ${condition} ${id}`); + assertNoLldbError('breakpoint modify', modifyOutput); + } + + return { + id, + spec, + rawOutput: output, + }; + } + + async removeBreakpoint(id: number): Promise { + const output = await this.runCommand(`breakpoint delete ${id}`); + assertNoLldbError('breakpoint delete', output); + return output; + } + + async getStack(opts?: { threadIndex?: number; maxFrames?: number }): Promise { + let command = 'thread backtrace'; + if (typeof opts?.maxFrames === 'number') { + command += ` -c ${opts.maxFrames}`; + } + if (typeof opts?.threadIndex === 'number') { + command += ` ${opts.threadIndex}`; + } + return this.runCommand(command); + } + + async getVariables(opts?: { frameIndex?: number }): Promise { + if (typeof opts?.frameIndex === 'number') { + await this.runCommand(`frame select ${opts.frameIndex}`); + } + return this.runCommand('frame variable'); + } + + async getExecutionState(opts?: { timeoutMs?: number }): Promise { + try { + const output = await this.runCommand('process status', { + timeoutMs: opts?.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS, + }); + const normalized = output.toLowerCase(); + + if (/no process|exited|terminated/.test(normalized)) { + return { status: 'terminated', description: output.trim() }; + } + if (/\bstopped\b/.test(normalized)) { + return { + status: 'stopped', + reason: parseStopReason(output), + description: output.trim(), + }; + } + if (/\brunning\b/.test(normalized)) { + return { status: 'running', description: output.trim() }; + } + if (/error:/.test(normalized)) { + return { status: 'unknown', description: output.trim() }; + } + + return { status: 'unknown', description: output.trim() }; + } catch (error) { + return { + status: 'unknown', + description: error instanceof Error ? error.message : String(error), + }; + } + } + + async dispose(): Promise { + if (this.disposed) return; + this.disposed = true; + this.failPending(new Error('LLDB backend disposed')); + this.process.dispose(); + } + + private enqueue(work: () => Promise): Promise { + const next = this.queue.then(work, work) as Promise; + this.queue = next.then( + () => undefined, + () => undefined, + ); + return next; + } + + private handleData(data: Buffer): void { + this.buffer += data.toString('utf8'); + this.checkPending(); + } + + private waitForSentinel(timeoutMs: number): Promise { + if (this.pending) { + return Promise.reject(new Error('LLDB command already pending')); + } + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + this.pending = null; + reject(new Error(`LLDB command timed out after ${timeoutMs}ms`)); + }, timeoutMs); + + this.pending = { resolve, reject, timeout }; + this.checkPending(); + }); + } + + private checkPending(): void { + if (!this.pending) return; + const sentinelMatch = this.buffer.match(COMMAND_SENTINEL_REGEX); + const sentinelIndex = sentinelMatch?.index; + const sentinelLength = sentinelMatch?.[0].length; + if (sentinelIndex == null || sentinelLength == null) return; + + const output = this.buffer.slice(0, sentinelIndex); + const remainderStart = sentinelIndex + sentinelLength; + + const promptIndex = this.buffer.indexOf(this.prompt, remainderStart); + if (promptIndex !== -1) { + this.buffer = this.buffer.slice(promptIndex + this.prompt.length); + } else { + this.buffer = this.buffer.slice(remainderStart); + } + + const { resolve, timeout } = this.pending; + this.pending = null; + clearTimeout(timeout); + resolve(output); + } + + private failPending(error: Error): void { + if (!this.pending) return; + const { reject, timeout } = this.pending; + this.pending = null; + clearTimeout(timeout); + reject(error); + } +} + +function assertNoLldbError(context: string, output: string): void { + if (/error:/i.test(output)) { + throw new Error(`LLDB ${context} failed: ${output.trim()}`); + } +} + +function sanitizeOutput(output: string, prompt: string): string { + const lines = output.split(/\r?\n/); + const filtered = lines.filter((line) => { + if (!line) return false; + if (line.startsWith(prompt)) return false; + if (line.includes(`script print("${COMMAND_SENTINEL}")`)) return false; + if (line.includes(COMMAND_SENTINEL)) return false; + return true; + }); + return filtered.join('\n'); +} + +function formatConditionForLldb(condition: string): string { + const escaped = condition.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return `"${escaped}"`; +} + +function parseStopReason(output: string): string | undefined { + const match = output.match(/stop reason\s*=\s*(.+)/i); + if (!match) return undefined; + return match[1]?.trim() || undefined; +} + +export async function createLldbCliBackend( + spawner: InteractiveSpawner = getDefaultInteractiveSpawner(), +): Promise { + const backend = new LldbCliBackend(spawner); + try { + await backend.waitUntilReady(); + } catch (error) { + try { + await backend.dispose(); + } catch { + // Best-effort cleanup; keep original error. + } + throw error; + } + return backend; +} diff --git a/src/utils/debugger/dap/__tests__/transport-framing.test.ts b/src/utils/debugger/dap/__tests__/transport-framing.test.ts new file mode 100644 index 00000000..c779cbb9 --- /dev/null +++ b/src/utils/debugger/dap/__tests__/transport-framing.test.ts @@ -0,0 +1,168 @@ +import type { ChildProcess } from 'node:child_process'; +import { EventEmitter } from 'node:events'; +import { PassThrough } from 'node:stream'; +import type { InteractiveProcess, InteractiveSpawner } from '../../../execution/index.ts'; +import { describe, expect, it } from 'vitest'; + +import { DapTransport } from '../transport.ts'; +import type { DapEvent, DapResponse } from '../types.ts'; +type TestSession = { + stdout: PassThrough; + stderr: PassThrough; + stdin: PassThrough; + emitExit: (code?: number | null, signal?: NodeJS.Signals | null) => void; + emitError: (error: Error) => void; +}; + +function encodeMessage(message: Record): string { + const payload = JSON.stringify(message); + return `Content-Length: ${Buffer.byteLength(payload, 'utf8')}\r\n\r\n${payload}`; +} + +function buildResponse( + requestSeq: number, + command: string, + body?: Record, +): DapResponse { + return { + seq: requestSeq + 100, + type: 'response', + request_seq: requestSeq, + success: true, + command, + body, + }; +} + +function createTestSpawner(): { spawner: InteractiveSpawner; session: TestSession } { + const stdout = new PassThrough(); + const stderr = new PassThrough(); + const stdin = new PassThrough(); + const emitter = new EventEmitter(); + const mockProcess = emitter as unknown as ChildProcess; + const mutableProcess = mockProcess as unknown as { + stdout: PassThrough | null; + stderr: PassThrough | null; + stdin: PassThrough | null; + killed: boolean; + exitCode: number | null; + signalCode: NodeJS.Signals | null; + spawnargs: string[]; + spawnfile: string; + pid: number; + }; + + mutableProcess.stdout = stdout; + mutableProcess.stderr = stderr; + mutableProcess.stdin = stdin; + mutableProcess.killed = false; + mutableProcess.exitCode = null; + mutableProcess.signalCode = null; + mutableProcess.spawnargs = []; + mutableProcess.spawnfile = 'mock'; + mutableProcess.pid = 12345; + mockProcess.kill = ((signal?: NodeJS.Signals): boolean => { + mutableProcess.killed = true; + emitter.emit('exit', 0, signal ?? null); + return true; + }) as ChildProcess['kill']; + + const session: TestSession = { + stdout, + stderr, + stdin, + emitExit: (code = 0, signal = null) => { + emitter.emit('exit', code, signal); + }, + emitError: (error) => { + emitter.emit('error', error); + }, + }; + + const spawner: InteractiveSpawner = (): InteractiveProcess => ({ + process: mockProcess, + write(data: string): void { + stdin.write(data); + }, + kill(signal?: NodeJS.Signals): void { + mockProcess.kill?.(signal); + }, + dispose(): void { + stdout.end(); + stderr.end(); + stdin.end(); + emitter.removeAllListeners(); + }, + }); + + return { spawner, session }; +} + +describe('DapTransport framing', () => { + it('parses responses across chunk boundaries', async () => { + const { spawner, session } = createTestSpawner(); + + const transport = new DapTransport({ spawner, adapterCommand: ['lldb-dap'] }); + + const responsePromise = transport.sendRequest( + 'initialize', + undefined, + { timeoutMs: 1_000 }, + ); + + const response = encodeMessage(buildResponse(1, 'initialize', { ok: true })); + session.stdout.write(response.slice(0, 12)); + session.stdout.write(response.slice(12)); + + await expect(responsePromise).resolves.toEqual({ ok: true }); + transport.dispose(); + }); + + it('handles multiple messages in a single chunk', async () => { + const { spawner, session } = createTestSpawner(); + + const transport = new DapTransport({ spawner, adapterCommand: ['lldb-dap'] }); + const events: DapEvent[] = []; + transport.onEvent((event) => events.push(event)); + + const responsePromise = transport.sendRequest( + 'threads', + undefined, + { timeoutMs: 1_000 }, + ); + + const eventMessage = encodeMessage({ + seq: 55, + type: 'event', + event: 'output', + body: { output: 'hello' }, + }); + const responseMessage = encodeMessage(buildResponse(1, 'threads', { ok: true })); + + session.stdout.write(`${eventMessage}${responseMessage}`); + + await expect(responsePromise).resolves.toEqual({ ok: true }); + expect(events).toHaveLength(1); + expect(events[0]?.event).toBe('output'); + transport.dispose(); + }); + + it('continues after invalid headers', async () => { + const { spawner, session } = createTestSpawner(); + + const transport = new DapTransport({ spawner, adapterCommand: ['lldb-dap'] }); + + const responsePromise = transport.sendRequest( + 'stackTrace', + undefined, + { timeoutMs: 1_000 }, + ); + + session.stdout.write('Content-Length: nope\r\n\r\n'); + const responseMessage = encodeMessage(buildResponse(1, 'stackTrace', { ok: true })); + session.stdout.write(responseMessage); + + await expect(responsePromise).resolves.toEqual({ ok: true }); + transport.dispose(); + }); +}); diff --git a/src/utils/debugger/dap/adapter-discovery.ts b/src/utils/debugger/dap/adapter-discovery.ts new file mode 100644 index 00000000..5325d374 --- /dev/null +++ b/src/utils/debugger/dap/adapter-discovery.ts @@ -0,0 +1,33 @@ +import type { CommandExecutor } from '../../execution/index.ts'; +import { log } from '../../logging/index.ts'; +import { DependencyError } from '../../errors.ts'; + +const LOG_PREFIX = '[DAP Adapter]'; + +export async function resolveLldbDapCommand(opts: { + executor: CommandExecutor; +}): Promise { + try { + const result = await opts.executor(['xcrun', '--find', 'lldb-dap'], LOG_PREFIX); + if (!result.success) { + throw new DependencyError('xcrun returned a non-zero exit code for lldb-dap discovery.'); + } + + const resolved = result.output.trim(); + if (!resolved) { + throw new DependencyError('xcrun did not return a path for lldb-dap.'); + } + + log('debug', `${LOG_PREFIX} resolved lldb-dap: ${resolved}`); + return [resolved]; + } catch (error) { + if (error instanceof DependencyError) { + throw error; + } + const message = error instanceof Error ? error.message : String(error); + throw new DependencyError( + 'DAP backend selected but lldb-dap not found. Ensure Xcode is installed and xcrun can locate lldb-dap, or set XCODEBUILDMCP_DEBUGGER_BACKEND=lldb-cli.', + message, + ); + } +} diff --git a/src/utils/debugger/dap/transport.ts b/src/utils/debugger/dap/transport.ts new file mode 100644 index 00000000..636668cf --- /dev/null +++ b/src/utils/debugger/dap/transport.ts @@ -0,0 +1,212 @@ +import { EventEmitter } from 'node:events'; +import type { InteractiveProcess, InteractiveSpawner } from '../../execution/index.ts'; +import { log } from '../../logging/index.ts'; +import type { DapEvent, DapRequest, DapResponse } from './types.ts'; + +const DEFAULT_LOG_PREFIX = '[DAP Transport]'; + +export type DapTransportOptions = { + spawner: InteractiveSpawner; + adapterCommand: string[]; + env?: Record; + cwd?: string; + logPrefix?: string; +}; + +type PendingRequest = { + command: string; + resolve: (body: unknown) => void; + reject: (error: Error) => void; + timeout: NodeJS.Timeout; +}; + +export class DapTransport { + private readonly process: InteractiveProcess; + private readonly logPrefix: string; + private readonly pending = new Map(); + private readonly events = new EventEmitter(); + private nextSeq = 1; + private buffer = Buffer.alloc(0); + private disposed = false; + private exited = false; + + constructor(options: DapTransportOptions) { + this.logPrefix = options.logPrefix ?? DEFAULT_LOG_PREFIX; + this.process = options.spawner(options.adapterCommand, { + env: options.env, + cwd: options.cwd, + }); + + this.process.process.stdout?.on('data', (data: Buffer) => this.handleStdout(data)); + this.process.process.stderr?.on('data', (data: Buffer) => this.handleStderr(data)); + this.process.process.on('exit', (code, signal) => this.handleExit(code, signal)); + this.process.process.on('error', (error) => this.handleError(error)); + } + + sendRequest(command: string, args?: A, opts?: { timeoutMs?: number }): Promise { + if (this.disposed || this.exited) { + return Promise.reject(new Error('DAP transport is not available')); + } + + const seq = this.nextSeq++; + const request: DapRequest = { + seq, + type: 'request', + command, + arguments: args, + }; + + const payload = JSON.stringify(request); + const message = `Content-Length: ${Buffer.byteLength(payload, 'utf8')}\r\n\r\n${payload}`; + + return new Promise((resolve, reject) => { + const timeoutMs = opts?.timeoutMs ?? 30_000; + const timeout = setTimeout(() => { + this.pending.delete(seq); + reject(new Error(`DAP request timed out after ${timeoutMs}ms (${command})`)); + }, timeoutMs); + + this.pending.set(seq, { + command, + resolve: (body) => resolve(body as B), + reject, + timeout, + }); + + try { + this.process.write(message); + } catch (error) { + clearTimeout(timeout); + this.pending.delete(seq); + reject(error instanceof Error ? error : new Error(String(error))); + } + }); + } + + onEvent(handler: (event: DapEvent) => void): () => void { + this.events.on('event', handler); + return () => { + this.events.off('event', handler); + }; + } + + dispose(): void { + if (this.disposed) return; + this.disposed = true; + this.failAllPending(new Error('DAP transport disposed')); + try { + this.process.dispose(); + } catch (error) { + log('debug', `${this.logPrefix} dispose error: ${String(error)}`); + } + } + + private handleStdout(data: Buffer): void { + if (this.disposed) return; + this.buffer = Buffer.concat([this.buffer, data]); + this.processBuffer(); + } + + private handleStderr(data: Buffer): void { + if (this.disposed) return; + const message = data.toString('utf8').trim(); + if (!message) return; + log('debug', `${this.logPrefix} stderr: ${message}`); + } + + private handleExit(code: number | null, signal: NodeJS.Signals | null): void { + this.exited = true; + const detail = signal ? `signal ${signal}` : `code ${code ?? 'unknown'}`; + this.failAllPending(new Error(`DAP adapter exited (${detail})`)); + } + + private handleError(error: Error): void { + this.exited = true; + this.failAllPending(new Error(`DAP adapter error: ${error.message}`)); + } + + private processBuffer(): void { + while (true) { + const headerEnd = this.buffer.indexOf('\r\n\r\n'); + if (headerEnd === -1) { + return; + } + + const header = this.buffer.slice(0, headerEnd).toString('utf8'); + const contentLength = this.parseContentLength(header); + if (contentLength == null) { + log('error', `${this.logPrefix} invalid DAP header: ${header}`); + this.buffer = this.buffer.slice(headerEnd + 4); + continue; + } + + const messageStart = headerEnd + 4; + const messageEnd = messageStart + contentLength; + if (this.buffer.length < messageEnd) { + return; + } + + const bodyBuffer = this.buffer.slice(messageStart, messageEnd); + this.buffer = this.buffer.slice(messageEnd); + + try { + const message = JSON.parse(bodyBuffer.toString('utf8')) as + | DapResponse + | DapEvent + | DapRequest; + this.handleMessage(message); + } catch (error) { + log('error', `${this.logPrefix} failed to parse DAP message: ${String(error)}`); + } + } + } + + private handleMessage(message: DapResponse | DapEvent | DapRequest): void { + if (message.type === 'response') { + const pending = this.pending.get(message.request_seq); + if (!pending) { + log('debug', `${this.logPrefix} received response without pending request`); + return; + } + + this.pending.delete(message.request_seq); + clearTimeout(pending.timeout); + + if (!message.success) { + const detail = message.message ?? 'DAP request failed'; + pending.reject(new Error(`${pending.command} failed: ${detail}`)); + return; + } + + pending.resolve(message.body ?? {}); + return; + } + + if (message.type === 'event') { + this.events.emit('event', message); + return; + } + + log('debug', `${this.logPrefix} ignoring DAP request: ${message.command ?? 'unknown'}`); + } + + private parseContentLength(header: string): number | null { + const lines = header.split(/\r?\n/); + for (const line of lines) { + const match = line.match(/^Content-Length:\s*(\d+)/i); + if (match) { + const length = Number(match[1]); + return Number.isFinite(length) ? length : null; + } + } + return null; + } + + private failAllPending(error: Error): void { + for (const [seq, pending] of this.pending.entries()) { + clearTimeout(pending.timeout); + pending.reject(error); + this.pending.delete(seq); + } + } +} diff --git a/src/utils/debugger/dap/types.ts b/src/utils/debugger/dap/types.ts new file mode 100644 index 00000000..07386bd7 --- /dev/null +++ b/src/utils/debugger/dap/types.ts @@ -0,0 +1,91 @@ +export type DapRequest = { + seq: number; + type: 'request'; + command: string; + arguments?: C; +}; + +export type DapResponse = { + seq: number; + type: 'response'; + request_seq: number; + success: boolean; + command: string; + message?: string; + body?: B; +}; + +export type DapEvent = { + seq: number; + type: 'event'; + event: string; + body?: B; +}; + +export type InitializeResponseBody = { + supportsConfigurationDoneRequest?: boolean; + supportsFunctionBreakpoints?: boolean; + supportsConditionalBreakpoints?: boolean; + supportsEvaluateForHovers?: boolean; + supportsTerminateRequest?: boolean; +}; + +export type ThreadsResponseBody = { + threads: Array<{ id: number; name?: string }>; +}; + +export type StackTraceResponseBody = { + stackFrames: Array<{ + id: number; + name: string; + source?: { path?: string; name?: string }; + line?: number; + column?: number; + }>; + totalFrames?: number; +}; + +export type ScopesResponseBody = { + scopes: Array<{ name: string; variablesReference: number; expensive?: boolean }>; +}; + +export type VariablesResponseBody = { + variables: Array<{ + name: string; + value: string; + type?: string; + variablesReference?: number; + }>; +}; + +export type SetBreakpointsResponseBody = { + breakpoints: Array<{ + id?: number; + verified?: boolean; + message?: string; + source?: { path?: string; name?: string }; + line?: number; + }>; +}; + +export type EvaluateResponseBody = { + result?: string; + output?: string; + type?: string; + variablesReference?: number; +}; + +export type StoppedEventBody = { + reason: string; + threadId?: number; + allThreadsStopped?: boolean; + description?: string; +}; + +export type OutputEventBody = { + category?: string; + output: string; + data?: unknown; +}; + +export type TerminatedEventBody = Record; diff --git a/src/utils/debugger/debugger-manager.ts b/src/utils/debugger/debugger-manager.ts new file mode 100644 index 00000000..a93092e9 --- /dev/null +++ b/src/utils/debugger/debugger-manager.ts @@ -0,0 +1,231 @@ +import { v4 as uuidv4 } from 'uuid'; +import type { DebuggerBackend } from './backends/DebuggerBackend.ts'; +import { createDapBackend } from './backends/dap-backend.ts'; +import { createLldbCliBackend } from './backends/lldb-cli-backend.ts'; +import type { + BreakpointInfo, + BreakpointSpec, + DebugExecutionState, + DebugSessionInfo, + DebuggerBackendKind, +} from './types.ts'; +import { getConfig } from '../config-store.ts'; +import { acquireDaemonActivity } from '../../daemon/activity-registry.ts'; + +export type DebuggerBackendFactory = (kind: DebuggerBackendKind) => Promise; + +type DebugSessionEntry = { + info: DebugSessionInfo; + backend: DebuggerBackend; + releaseActivity: () => void; +}; + +export class DebuggerManager { + private readonly backendFactory: DebuggerBackendFactory; + private readonly sessions = new Map(); + private currentSessionId: string | null = null; + + constructor(options: { backendFactory?: DebuggerBackendFactory } = {}) { + this.backendFactory = options.backendFactory ?? defaultBackendFactory; + } + + async createSession(opts: { + simulatorId: string; + pid: number; + backend?: DebuggerBackendKind; + waitFor?: boolean; + }): Promise { + const backendKind = resolveBackendKind(opts.backend); + const backend = await this.backendFactory(backendKind); + + try { + await backend.attach({ pid: opts.pid, simulatorId: opts.simulatorId, waitFor: opts.waitFor }); + } catch (error) { + try { + await backend.dispose(); + } catch { + // Best-effort cleanup; keep original attach error. + } + throw error; + } + + const now = Date.now(); + const info: DebugSessionInfo = { + id: uuidv4(), + backend: backendKind, + simulatorId: opts.simulatorId, + pid: opts.pid, + createdAt: now, + lastUsedAt: now, + }; + + this.sessions.set(info.id, { + info, + backend, + releaseActivity: acquireDaemonActivity('debug.session'), + }); + return info; + } + + getSession(id?: string): DebugSessionEntry | null { + const resolvedId = id ?? this.currentSessionId; + if (!resolvedId) return null; + return this.sessions.get(resolvedId) ?? null; + } + + setCurrentSession(id: string): void { + if (!this.sessions.has(id)) { + throw new Error(`Debug session not found: ${id}`); + } + this.currentSessionId = id; + } + + getCurrentSessionId(): string | null { + return this.currentSessionId; + } + + listSessions(): DebugSessionInfo[] { + return Array.from(this.sessions.values()).map((session) => ({ ...session.info })); + } + + findSessionForSimulator(simulatorId: string): DebugSessionInfo | null { + if (!simulatorId) return null; + if (this.currentSessionId) { + const current = this.sessions.get(this.currentSessionId); + if (current?.info.simulatorId === simulatorId) { + return current.info; + } + } + + for (const session of this.sessions.values()) { + if (session.info.simulatorId === simulatorId) { + return session.info; + } + } + + return null; + } + + async detachSession(id?: string): Promise { + const session = this.requireSession(id); + try { + await session.backend.detach(); + } finally { + await session.backend.dispose(); + session.releaseActivity(); + this.sessions.delete(session.info.id); + if (this.currentSessionId === session.info.id) { + this.currentSessionId = null; + } + } + } + + async disposeAll(): Promise { + await Promise.allSettled( + Array.from(this.sessions.values()).map(async (session) => { + try { + await session.backend.detach(); + } catch { + // Best-effort cleanup; detach can fail if the process exited. + } finally { + await session.backend.dispose(); + session.releaseActivity(); + } + }), + ); + this.sessions.clear(); + this.currentSessionId = null; + } + + async addBreakpoint( + id: string | undefined, + spec: BreakpointSpec, + opts?: { condition?: string }, + ): Promise { + const session = this.requireSession(id); + const result = await session.backend.addBreakpoint(spec, opts); + this.touch(session.info.id); + return result; + } + + async removeBreakpoint(id: string | undefined, breakpointId: number): Promise { + const session = this.requireSession(id); + const result = await session.backend.removeBreakpoint(breakpointId); + this.touch(session.info.id); + return result; + } + + async getStack( + id: string | undefined, + opts?: { threadIndex?: number; maxFrames?: number }, + ): Promise { + const session = this.requireSession(id); + const result = await session.backend.getStack(opts); + this.touch(session.info.id); + return result; + } + + async getVariables(id: string | undefined, opts?: { frameIndex?: number }): Promise { + const session = this.requireSession(id); + const result = await session.backend.getVariables(opts); + this.touch(session.info.id); + return result; + } + + async getExecutionState( + id: string | undefined, + opts?: { timeoutMs?: number }, + ): Promise { + const session = this.requireSession(id); + const result = await session.backend.getExecutionState(opts); + this.touch(session.info.id); + return result; + } + + async runCommand( + id: string | undefined, + command: string, + opts?: { timeoutMs?: number }, + ): Promise { + const session = this.requireSession(id); + const result = await session.backend.runCommand(command, opts); + this.touch(session.info.id); + return result; + } + + async resumeSession(id?: string, opts?: { threadId?: number }): Promise { + const session = this.requireSession(id); + await session.backend.resume(opts); + this.touch(session.info.id); + } + + private requireSession(id?: string): DebugSessionEntry { + const session = this.getSession(id); + if (!session) { + throw new Error('No active debug session. Provide debugSessionId or attach first.'); + } + return session; + } + + private touch(id: string): void { + const session = this.sessions.get(id); + if (!session) return; + session.info.lastUsedAt = Date.now(); + } +} + +function resolveBackendKind(explicit?: DebuggerBackendKind): DebuggerBackendKind { + if (explicit) return explicit; + return getConfig().debuggerBackend; +} + +const defaultBackendFactory: DebuggerBackendFactory = async (kind) => { + switch (kind) { + case 'lldb-cli': + return createLldbCliBackend(); + case 'dap': + return createDapBackend(); + default: + throw new Error(`Unsupported debugger backend: ${kind}`); + } +}; diff --git a/src/utils/debugger/index.ts b/src/utils/debugger/index.ts new file mode 100644 index 00000000..ea399d82 --- /dev/null +++ b/src/utils/debugger/index.ts @@ -0,0 +1,26 @@ +import { DebuggerManager } from './debugger-manager.ts'; + +let defaultDebuggerManager: DebuggerManager | null = null; + +export function getDefaultDebuggerManager(): DebuggerManager { + defaultDebuggerManager ??= new DebuggerManager(); + return defaultDebuggerManager; +} + +export { DebuggerManager } from './debugger-manager.ts'; +export { + getDefaultDebuggerToolContext, + __setTestDebuggerToolContextOverride, + __clearTestDebuggerToolContextOverride, +} from './tool-context.ts'; +export { resolveSimulatorAppPid } from './simctl.ts'; +export { guardUiAutomationAgainstStoppedDebugger } from './ui-automation-guard.ts'; +export type { + BreakpointInfo, + BreakpointSpec, + DebugExecutionState, + DebugExecutionStatus, + DebugSessionInfo, + DebuggerBackendKind, +} from './types.ts'; +export type { DebuggerToolContext } from './tool-context.ts'; diff --git a/src/utils/debugger/simctl.ts b/src/utils/debugger/simctl.ts new file mode 100644 index 00000000..a987f94b --- /dev/null +++ b/src/utils/debugger/simctl.ts @@ -0,0 +1,38 @@ +import type { CommandExecutor } from '../execution/index.ts'; + +export async function resolveSimulatorAppPid(opts: { + executor: CommandExecutor; + simulatorId: string; + bundleId: string; +}): Promise { + const result = await opts.executor( + ['xcrun', 'simctl', 'spawn', opts.simulatorId, 'launchctl', 'list'], + 'Resolve simulator app PID', + true, + ); + + if (!result.success) { + throw new Error(result.error ?? 'Failed to read simulator process list'); + } + + const lines = result.output.split('\n'); + for (const line of lines) { + if (!line.includes(opts.bundleId)) continue; + + const columns = line.trim().split(/\s+/); + const pidToken = columns[0]; + + if (!pidToken || pidToken === '-') { + throw new Error(`App ${opts.bundleId} is not running on simulator ${opts.simulatorId}`); + } + + const pid = Number(pidToken); + if (Number.isNaN(pid) || pid <= 0) { + throw new Error(`Unable to parse PID for ${opts.bundleId} from: ${line}`); + } + + return pid; + } + + throw new Error(`No running process found for ${opts.bundleId} on simulator ${opts.simulatorId}`); +} diff --git a/src/utils/debugger/tool-context.ts b/src/utils/debugger/tool-context.ts new file mode 100644 index 00000000..c4e5087a --- /dev/null +++ b/src/utils/debugger/tool-context.ts @@ -0,0 +1,29 @@ +import type { CommandExecutor } from '../execution/index.ts'; +import { getDefaultCommandExecutor } from '../execution/index.ts'; +import type { DebuggerManager } from './debugger-manager.ts'; +import { getDefaultDebuggerManager } from './index.ts'; + +export type DebuggerToolContext = { + executor: CommandExecutor; + debugger: DebuggerManager; +}; + +let _testContextOverride: DebuggerToolContext | null = null; + +export function __setTestDebuggerToolContextOverride(ctx: DebuggerToolContext | null): void { + _testContextOverride = ctx; +} + +export function __clearTestDebuggerToolContextOverride(): void { + _testContextOverride = null; +} + +export function getDefaultDebuggerToolContext(): DebuggerToolContext { + if ((process.env.VITEST === 'true' || process.env.NODE_ENV === 'test') && _testContextOverride) { + return _testContextOverride; + } + return { + executor: getDefaultCommandExecutor(), + debugger: getDefaultDebuggerManager(), + }; +} diff --git a/src/utils/debugger/types.ts b/src/utils/debugger/types.ts new file mode 100644 index 00000000..3dbb7728 --- /dev/null +++ b/src/utils/debugger/types.ts @@ -0,0 +1,29 @@ +export type DebuggerBackendKind = 'lldb-cli' | 'dap'; + +export interface DebugSessionInfo { + id: string; + backend: DebuggerBackendKind; + simulatorId: string; + pid: number; + createdAt: number; + lastUsedAt: number; +} + +export type BreakpointSpec = + | { kind: 'file-line'; file: string; line: number } + | { kind: 'function'; name: string }; + +export interface BreakpointInfo { + id: number; + spec: BreakpointSpec; + rawOutput: string; +} + +export type DebugExecutionStatus = 'running' | 'stopped' | 'unknown' | 'terminated'; + +export type DebugExecutionState = { + status: DebugExecutionStatus; + reason?: string; + description?: string; + threadId?: number; +}; diff --git a/src/utils/debugger/ui-automation-guard.ts b/src/utils/debugger/ui-automation-guard.ts new file mode 100644 index 00000000..f6f469af --- /dev/null +++ b/src/utils/debugger/ui-automation-guard.ts @@ -0,0 +1,101 @@ +import type { ToolResponse } from '../../types/common.ts'; +import { createErrorResponse } from '../responses/index.ts'; +import { log } from '../logging/index.ts'; +import { getUiDebuggerGuardMode } from '../environment.ts'; +import type { DebugExecutionState } from './types.ts'; +import type { DebuggerManager } from './debugger-manager.ts'; + +type GuardResult = { + blockedResponse?: ToolResponse; + warningText?: string; +}; + +const LOG_PREFIX = '[UI Automation Guard]'; + +export async function guardUiAutomationAgainstStoppedDebugger(opts: { + debugger: DebuggerManager; + simulatorId: string; + toolName: string; + mode?: 'error' | 'warn' | 'off'; +}): Promise { + const mode = opts.mode ?? getUiDebuggerGuardMode(); + if (mode === 'off') return {}; + + const session = opts.debugger.findSessionForSimulator(opts.simulatorId); + if (!session) return {}; + + let state: DebugExecutionState; + try { + state = await opts.debugger.getExecutionState(session.id); + } catch (error) { + log( + 'warn', + `${LOG_PREFIX} ${opts.toolName}: unable to read execution state for ${session.id}: ${String(error)}`, + ); + return {}; + } + + if (state.status !== 'stopped') return {}; + + const details = buildGuardDetails({ + toolName: opts.toolName, + simulatorId: opts.simulatorId, + sessionId: session.id, + backend: session.backend, + pid: session.pid, + state, + }); + + if (mode === 'warn') { + return { warningText: buildGuardWarning(details) }; + } + + return { + blockedResponse: createErrorResponse( + 'UI automation blocked: app is paused in debugger', + details, + ), + }; +} + +function buildGuardDetails(params: { + toolName: string; + simulatorId: string; + sessionId: string; + backend: string; + pid: number; + state: DebugExecutionState; +}): string { + const stateLabel = formatStateLabel(params.state); + const lines = [ + `tool=${params.toolName}`, + `simulatorId=${params.simulatorId}`, + `debugSessionId=${params.sessionId}`, + `backend=${params.backend}`, + `pid=${params.pid}`, + `state=${stateLabel}`, + ]; + + if (params.state.description) { + lines.push(`stateDetails=${params.state.description}`); + } + + lines.push( + '', + 'Resume execution via debug_continue, remove breakpoints, or detach via debug_detach before using UI tools.', + ); + + return lines.join('\n'); +} + +function formatStateLabel(state: DebugExecutionState): string { + if (!state.reason) return state.status; + return `${state.status} (${state.reason})`; +} + +function buildGuardWarning(details: string): string { + return [ + 'Warning: debugger reports the app is paused; UI automation may return empty results.', + details, + ].join('\n'); +} diff --git a/src/utils/environment.ts b/src/utils/environment.ts index fac49e9a..82aef678 100644 --- a/src/utils/environment.ts +++ b/src/utils/environment.ts @@ -7,6 +7,8 @@ import { execSync } from 'child_process'; import { log } from './logger.ts'; +import { getConfig } from './config-store.ts'; +import type { UiDebuggerGuardMode } from './runtime-config-types.ts'; /** * Interface for environment detection abstraction @@ -71,12 +73,12 @@ export function getDefaultEnvironmentDetector(): EnvironmentDetector { * Global opt-out for session defaults in MCP tool schemas. * When enabled, tools re-expose all parameters instead of hiding session-managed fields. */ -export function isSessionDefaultsSchemaOptOutEnabled(): boolean { - const raw = process.env.XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS; - if (!raw) return false; +export function isSessionDefaultsOptOutEnabled(): boolean { + return getConfig().disableSessionDefaults; +} - const normalized = raw.trim().toLowerCase(); - return ['1', 'true', 'yes', 'on'].includes(normalized); +export function getUiDebuggerGuardMode(): UiDebuggerGuardMode { + return getConfig().uiDebuggerGuardMode; } /** @@ -96,3 +98,21 @@ export function normalizeTestRunnerEnv(vars: Record): Record { SIMCTL_CHILD_FOO: '1', SIMCTL_CHILD_BAR: '2' } + */ +export function normalizeSimctlChildEnv(vars: Record): Record { + const normalized: Record = {}; + for (const [key, value] of Object.entries(vars ?? {})) { + if (value == null) continue; + const prefixedKey = key.startsWith('SIMCTL_CHILD_') ? key : `SIMCTL_CHILD_${key}`; + normalized[prefixedKey] = value; + } + return normalized; +} diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 359399cb..70997b59 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,4 +1,4 @@ -import { ToolResponse } from '../types/common.ts'; +import type { ToolResponse } from '../types/common.ts'; /** * Error Utilities - Type-safe error hierarchy for the application diff --git a/src/utils/execution/index.ts b/src/utils/execution/index.ts index efe9ef4f..4ca96841 100644 --- a/src/utils/execution/index.ts +++ b/src/utils/execution/index.ts @@ -2,8 +2,20 @@ * Focused execution facade. * Prefer importing from 'utils/execution/index.js' instead of the legacy utils barrel. */ -export { getDefaultCommandExecutor, getDefaultFileSystemExecutor } from '../command.ts'; +export { + getDefaultCommandExecutor, + getDefaultFileSystemExecutor, + __setTestCommandExecutorOverride, + __setTestFileSystemExecutorOverride, + __clearTestExecutorOverrides, +} from '../command.ts'; +export { getDefaultInteractiveSpawner } from './interactive-process.ts'; // Types export type { CommandExecutor, CommandResponse, CommandExecOptions } from '../CommandExecutor.ts'; export type { FileSystemExecutor } from '../FileSystemExecutor.ts'; +export type { + InteractiveProcess, + InteractiveSpawner, + SpawnInteractiveOptions, +} from './interactive-process.ts'; diff --git a/src/utils/execution/interactive-process.ts b/src/utils/execution/interactive-process.ts new file mode 100644 index 00000000..888d551b --- /dev/null +++ b/src/utils/execution/interactive-process.ts @@ -0,0 +1,81 @@ +import { spawn, type ChildProcess } from 'node:child_process'; + +export interface InteractiveProcess { + readonly process: ChildProcess; + write(data: string): void; + kill(signal?: NodeJS.Signals): void; + dispose(): void; +} + +export interface SpawnInteractiveOptions { + cwd?: string; + env?: Record; +} + +export type InteractiveSpawner = ( + command: string[], + opts?: SpawnInteractiveOptions, +) => InteractiveProcess; + +class DefaultInteractiveProcess implements InteractiveProcess { + readonly process: ChildProcess; + private disposed = false; + + constructor(process: ChildProcess) { + this.process = process; + } + + write(data: string): void { + if (this.disposed) { + throw new Error('Interactive process is disposed'); + } + if (!this.process.stdin) { + throw new Error('Interactive process stdin is not available'); + } + this.process.stdin.write(data); + } + + kill(signal?: NodeJS.Signals): void { + if (this.disposed) return; + this.process.kill(signal); + } + + dispose(): void { + if (this.disposed) return; + this.disposed = true; + this.process.stdin?.end(); + this.process.stdout?.removeAllListeners(); + this.process.stderr?.removeAllListeners(); + this.process.removeAllListeners(); + if (!this.process.killed) { + this.process.kill(); + } + } +} + +function createInteractiveProcess( + command: string[], + opts?: SpawnInteractiveOptions, +): InteractiveProcess { + if (command.length === 0) { + throw new Error('Command array must not be empty'); + } + const [executable, ...args] = command; + const childProcess = spawn(executable, args, { + stdio: ['pipe', 'pipe', 'pipe'], + env: { ...process.env, ...(opts?.env ?? {}) }, + cwd: opts?.cwd, + }); + + return new DefaultInteractiveProcess(childProcess); +} + +export function getDefaultInteractiveSpawner(): InteractiveSpawner { + if (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test') { + throw new Error( + 'Interactive process spawn blocked in tests. Inject a mock InteractiveSpawner.', + ); + } + + return createInteractiveProcess; +} diff --git a/src/utils/infer-platform.ts b/src/utils/infer-platform.ts new file mode 100644 index 00000000..c3d3cba5 --- /dev/null +++ b/src/utils/infer-platform.ts @@ -0,0 +1,276 @@ +import { XcodePlatform } from '../types/common.ts'; +import type { CommandExecutor } from './execution/index.ts'; +import { getDefaultCommandExecutor } from './execution/index.ts'; +import { log } from './logging/index.ts'; +import { detectPlatformFromScheme, type SimulatorPlatform } from './platform-detection.ts'; +import { sessionStore, type SessionDefaults } from './session-store.ts'; + +type PlatformInferenceSource = + | 'simulator-platform-cache' + | 'simulator-name' + | 'simulator-runtime' + | 'build-settings' + | 'default'; + +export interface InferPlatformParams { + projectPath?: string; + workspacePath?: string; + scheme?: string; + simulatorId?: string; + simulatorName?: string; + sessionDefaults?: Partial; +} + +export interface InferPlatformResult { + platform: SimulatorPlatform; + source: PlatformInferenceSource; +} + +const SIMULATOR_PLATFORMS: readonly SimulatorPlatform[] = [ + XcodePlatform.iOSSimulator, + XcodePlatform.watchOSSimulator, + XcodePlatform.tvOSSimulator, + XcodePlatform.visionOSSimulator, +] as const; + +const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +function inferPlatformFromSimulatorName(simulatorName: string): SimulatorPlatform | null { + const name = simulatorName.toLowerCase(); + + if (name.includes('watch')) return XcodePlatform.watchOSSimulator; + if (name.includes('apple tv') || name.includes('tvos')) return XcodePlatform.tvOSSimulator; + if ( + name.includes('apple vision') || + name.includes('vision pro') || + name.includes('visionos') || + name.includes('xros') + ) { + return XcodePlatform.visionOSSimulator; + } + if (name.includes('iphone') || name.includes('ipad') || name.includes('ipod')) { + return XcodePlatform.iOSSimulator; + } + + return null; +} + +function inferPlatformFromRuntime(runtime: string): SimulatorPlatform | null { + const value = runtime.toLowerCase(); + + if (value.includes('simruntime.watchos') || value.startsWith('watchos')) { + return XcodePlatform.watchOSSimulator; + } + if ( + value.includes('simruntime.tvos') || + value.includes('simruntime.appletv') || + value.startsWith('tvos') + ) { + return XcodePlatform.tvOSSimulator; + } + if ( + value.includes('simruntime.xros') || + value.includes('simruntime.visionos') || + value.startsWith('xros') || + value.startsWith('visionos') + ) { + return XcodePlatform.visionOSSimulator; + } + if (value.includes('simruntime.ios') || value.startsWith('ios')) { + return XcodePlatform.iOSSimulator; + } + + return null; +} + +function isSimulatorPlatform(value: unknown): value is SimulatorPlatform { + return SIMULATOR_PLATFORMS.includes(value as SimulatorPlatform); +} + +function inferSimulatorSelectorForTool(params: { + simulatorId?: string; + simulatorName?: string; + sessionDefaults?: Partial; +}): { simulatorId?: string; simulatorName?: string } { + const defaults = params.sessionDefaults ?? sessionStore.getAll(); + + if (params.simulatorId) { + return { simulatorId: params.simulatorId }; + } + if (params.simulatorName) { + return { simulatorName: params.simulatorName }; + } + if (defaults.simulatorId) { + return { simulatorId: defaults.simulatorId }; + } + if (defaults.simulatorName) { + return { simulatorName: defaults.simulatorName }; + } + + return {}; +} + +function resolveCachedPlatform(params: InferPlatformParams): SimulatorPlatform | null { + const defaults = params.sessionDefaults ?? sessionStore.getAll(); + if (!isSimulatorPlatform(defaults.simulatorPlatform)) { + return null; + } + + const hasExplicitId = Boolean(params.simulatorId); + const hasExplicitName = Boolean(params.simulatorName); + + if (!hasExplicitId && !hasExplicitName) { + return defaults.simulatorPlatform; + } + + if (hasExplicitId && defaults.simulatorId && params.simulatorId === defaults.simulatorId) { + return defaults.simulatorPlatform; + } + + if ( + hasExplicitName && + defaults.simulatorName && + params.simulatorName === defaults.simulatorName + ) { + return defaults.simulatorPlatform; + } + + return null; +} + +function resolveProjectFromSession(params: InferPlatformParams): { + projectPath?: string; + workspacePath?: string; + scheme?: string; +} { + const defaults = params.sessionDefaults ?? sessionStore.getAll(); + const hasExplicitProjectPath = params.projectPath !== undefined; + const hasExplicitWorkspacePath = params.workspacePath !== undefined; + const projectPath = + params.projectPath ?? (params.workspacePath ? undefined : defaults.projectPath); + const workspacePath = + params.workspacePath ?? (params.projectPath ? undefined : defaults.workspacePath); + + if (projectPath && workspacePath && !hasExplicitProjectPath && !hasExplicitWorkspacePath) { + return { + projectPath: undefined, + workspacePath, + scheme: params.scheme ?? defaults.scheme, + }; + } + + return { + projectPath, + workspacePath, + scheme: params.scheme ?? defaults.scheme, + }; +} + +async function inferPlatformFromSimctl( + simulatorId: string | undefined, + simulatorName: string | undefined, + executor: CommandExecutor, +): Promise { + if (!simulatorId && !simulatorName) return null; + + const result = await executor( + ['xcrun', 'simctl', 'list', 'devices', 'available', '--json'], + 'Infer Simulator Platform', + true, + ); + + if (!result.success) { + log('warning', `[Platform Inference] simctl failed: ${result.error ?? 'Unknown error'}`); + return null; + } + + let parsed: unknown; + try { + parsed = JSON.parse(result.output); + } catch { + log('warning', `[Platform Inference] Failed to parse simctl JSON output`); + return null; + } + + if (!parsed || typeof parsed !== 'object' || !('devices' in parsed)) { + log('warning', `[Platform Inference] simctl JSON missing devices`); + return null; + } + + const devices = (parsed as { devices: Record }).devices; + for (const runtime of Object.keys(devices)) { + const list = devices[runtime]; + if (!Array.isArray(list)) continue; + + for (const device of list) { + if (!device || typeof device !== 'object') continue; + const current = device as { + udid?: unknown; + name?: unknown; + isAvailable?: unknown; + }; + + if (simulatorId) { + const matchesId = typeof current.udid === 'string' && current.udid === simulatorId; + if (!matchesId) continue; + } else { + const matchesName = typeof current.name === 'string' && current.name === simulatorName; + if (!matchesName) continue; + } + if (typeof current.isAvailable === 'boolean' && !current.isAvailable) continue; + + return inferPlatformFromRuntime(runtime); + } + } + + return null; +} + +export async function inferPlatform( + params: InferPlatformParams, + executor: CommandExecutor = getDefaultCommandExecutor(), +): Promise { + const cachedPlatform = resolveCachedPlatform(params); + if (cachedPlatform) { + return { platform: cachedPlatform, source: 'simulator-platform-cache' }; + } + + const { simulatorId, simulatorName } = inferSimulatorSelectorForTool({ + simulatorId: params.simulatorId, + simulatorName: params.simulatorName, + sessionDefaults: params.sessionDefaults, + }); + + let simulatorIdForLookup = simulatorId; + let simulatorNameForLookup = simulatorName; + if (!simulatorIdForLookup && simulatorName && UUID_REGEX.test(simulatorName)) { + simulatorIdForLookup = simulatorName; + simulatorNameForLookup = undefined; + } + + const inferredFromRuntime = await inferPlatformFromSimctl( + simulatorIdForLookup, + simulatorNameForLookup, + executor, + ); + if (inferredFromRuntime) { + return { platform: inferredFromRuntime, source: 'simulator-runtime' }; + } + + if (simulatorNameForLookup) { + const inferredFromName = inferPlatformFromSimulatorName(simulatorNameForLookup); + if (inferredFromName) { + return { platform: inferredFromName, source: 'simulator-name' }; + } + } + + const { projectPath, workspacePath, scheme } = resolveProjectFromSession(params); + if (scheme && (projectPath || workspacePath)) { + const detection = await detectPlatformFromScheme(projectPath, workspacePath, scheme, executor); + if (detection.platform) { + return { platform: detection.platform, source: 'build-settings' }; + } + } + + return { platform: XcodePlatform.iOSSimulator, source: 'default' }; +} diff --git a/src/utils/log-capture/device-log-sessions.ts b/src/utils/log-capture/device-log-sessions.ts new file mode 100644 index 00000000..938df930 --- /dev/null +++ b/src/utils/log-capture/device-log-sessions.ts @@ -0,0 +1,14 @@ +import type { ChildProcess } from 'child_process'; +import type * as fs from 'fs'; + +export interface DeviceLogSession { + process: ChildProcess; + logFilePath: string; + deviceUuid: string; + bundleId: string; + logStream?: fs.WriteStream; + hasEnded: boolean; + releaseActivity?: () => void; +} + +export const activeDeviceLogSessions = new Map(); diff --git a/src/utils/log-capture/index.ts b/src/utils/log-capture/index.ts index 986c525a..2a25b5c3 100644 --- a/src/utils/log-capture/index.ts +++ b/src/utils/log-capture/index.ts @@ -1 +1,9 @@ -export { startLogCapture, stopLogCapture } from '../log_capture.ts'; +import { activeLogSessions, startLogCapture, stopLogCapture } from '../log_capture.ts'; + +export type { SubsystemFilter } from '../log_capture.ts'; + +export function listActiveSimulatorLogSessionIds(): string[] { + return Array.from(activeLogSessions.keys()).sort(); +} + +export { startLogCapture, stopLogCapture }; diff --git a/src/utils/log_capture.ts b/src/utils/log_capture.ts index 6588aa42..3e32c58d 100644 --- a/src/utils/log_capture.ts +++ b/src/utils/log_capture.ts @@ -1,10 +1,14 @@ -import * as fs from 'fs'; import * as path from 'path'; -import * as os from 'os'; import type { ChildProcess } from 'child_process'; +import type { Writable } from 'stream'; +import { finished } from 'stream/promises'; import { v4 as uuidv4 } from 'uuid'; import { log } from '../utils/logger.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from './command.ts'; +import type { CommandExecutor } from './command.ts'; +import { getDefaultCommandExecutor, getDefaultFileSystemExecutor } from './command.ts'; +import { normalizeSimctlChildEnv } from './environment.ts'; +import type { FileSystemExecutor } from './FileSystemExecutor.ts'; +import { acquireDaemonActivity } from '../daemon/activity-registry.ts'; /** * Log file retention policy: @@ -19,6 +23,41 @@ export interface LogSession { logFilePath: string; simulatorUuid: string; bundleId: string; + logStream: Writable; + releaseActivity?: () => void; +} + +/** + * Subsystem filter options for log capture. + * - 'app': Only capture logs from the app's bundle ID subsystem (default) + * - 'all': Capture all logs (no subsystem filtering) + * - 'swiftui': Capture logs from app + SwiftUI subsystem (useful for Self._printChanges()) + * - string[]: Custom array of subsystems to capture (always includes the app's bundle ID) + */ +export type SubsystemFilter = 'app' | 'all' | 'swiftui' | string[]; + +/** + * Build the predicate string for log filtering based on subsystem filter option. + */ +function buildLogPredicate(bundleId: string, subsystemFilter: SubsystemFilter): string | null { + if (subsystemFilter === 'all') { + // No filtering - capture everything from this process + return null; + } + + if (subsystemFilter === 'app') { + return `subsystem == "${bundleId}"`; + } + + if (subsystemFilter === 'swiftui') { + // Include both app logs and SwiftUI logs (for Self._printChanges()) + return `subsystem == "${bundleId}" OR subsystem == "com.apple.SwiftUI"`; + } + + // Custom array of subsystems - always include the app's bundle ID + const subsystems = new Set([bundleId, ...subsystemFilter]); + const predicates = Array.from(subsystems).map((s) => `subsystem == "${s}"`); + return predicates.join(' OR '); } export const activeLogSessions: Map = new Map(); @@ -33,22 +72,64 @@ export async function startLogCapture( bundleId: string; captureConsole?: boolean; args?: string[]; + env?: Record; + subsystemFilter?: SubsystemFilter; }, executor: CommandExecutor = getDefaultCommandExecutor(), + fileSystem: FileSystemExecutor = getDefaultFileSystemExecutor(), ): Promise<{ sessionId: string; logFilePath: string; processes: ChildProcess[]; error?: string }> { // Clean up old logs before starting a new session - await cleanOldLogs(); + await cleanOldLogs(fileSystem); - const { simulatorUuid, bundleId, captureConsole = false, args = [] } = params; + const { + simulatorUuid, + bundleId, + captureConsole = false, + args = [], + env, + subsystemFilter = 'app', + } = params; const logSessionId = uuidv4(); const logFileName = `${LOG_FILE_PREFIX}${logSessionId}.log`; - const logFilePath = path.join(os.tmpdir(), logFileName); + const logFilePath = path.join(fileSystem.tmpdir(), logFileName); + + let logStream: Writable | null = null; + const processes: ChildProcess[] = []; + const closeFailedCapture = async (): Promise => { + for (const process of processes) { + try { + if (!process.killed && process.exitCode === null) { + process.kill('SIGTERM'); + } + } catch (error) { + log( + 'warn', + `Failed to stop log capture process during cleanup: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } + } + + if (logStream) { + logStream.end(); + try { + await finished(logStream); + } catch (error) { + log( + 'warn', + `Failed to flush log stream during cleanup: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } + } + }; try { - await fs.promises.mkdir(os.tmpdir(), { recursive: true }); - await fs.promises.writeFile(logFilePath, ''); - const logStream = fs.createWriteStream(logFilePath, { flags: 'a' }); - const processes: ChildProcess[] = []; + await fileSystem.mkdir(fileSystem.tmpdir(), { recursive: true }); + await fileSystem.writeFile(logFilePath, ''); + logStream = fileSystem.createWriteStream(logFilePath, { flags: 'a' }); logStream.write('\n--- Log capture for bundle ID: ' + bundleId + ' ---\n'); if (captureConsole) { @@ -65,15 +146,17 @@ export async function startLogCapture( launchCommand.push(...args); } + const launchOpts = env ? { env: normalizeSimctlChildEnv(env) } : undefined; const stdoutLogResult = await executor( launchCommand, 'Console Log Capture', - true, // useShell - undefined, // env + false, // useShell + launchOpts, // env true, // detached - don't wait for this streaming process to complete ); if (!stdoutLogResult.success) { + await closeFailedCapture(); return { sessionId: '', logFilePath: '', @@ -82,30 +165,38 @@ export async function startLogCapture( }; } - stdoutLogResult.process.stdout?.pipe(logStream); - stdoutLogResult.process.stderr?.pipe(logStream); + stdoutLogResult.process.stdout?.pipe(logStream, { end: false }); + stdoutLogResult.process.stderr?.pipe(logStream, { end: false }); processes.push(stdoutLogResult.process); } + // Build the log stream command based on subsystem filter + const logPredicate = buildLogPredicate(bundleId, subsystemFilter); + const osLogCommand = [ + 'xcrun', + 'simctl', + 'spawn', + simulatorUuid, + 'log', + 'stream', + '--level=debug', + ]; + + // Only add predicate if filtering is needed + if (logPredicate) { + osLogCommand.push('--predicate', logPredicate); + } + const osLogResult = await executor( - [ - 'xcrun', - 'simctl', - 'spawn', - simulatorUuid, - 'log', - 'stream', - '--level=debug', - '--predicate', - `subsystem == "${bundleId}"`, - ], + osLogCommand, 'OS Log Capture', - true, // useShell + false, // useShell undefined, // env true, // detached - don't wait for this streaming process to complete ); if (!osLogResult.success) { + await closeFailedCapture(); return { sessionId: '', logFilePath: '', @@ -114,8 +205,8 @@ export async function startLogCapture( }; } - osLogResult.process.stdout?.pipe(logStream); - osLogResult.process.stderr?.pipe(logStream); + osLogResult.process.stdout?.pipe(logStream, { end: false }); + osLogResult.process.stderr?.pipe(logStream, { end: false }); processes.push(osLogResult.process); for (const process of processes) { @@ -124,16 +215,20 @@ export async function startLogCapture( }); } + const releaseActivity = acquireDaemonActivity('logging.simulator'); activeLogSessions.set(logSessionId, { processes, logFilePath, simulatorUuid, bundleId, + logStream, + releaseActivity, }); log('info', `Log capture started with session ID: ${logSessionId}`); return { sessionId: logSessionId, logFilePath, processes }; } catch (error) { + await closeFailedCapture(); const message = error instanceof Error ? error.message : String(error); log('error', `Failed to start log capture: ${message}`); return { sessionId: '', logFilePath: '', processes: [], error: message }; @@ -145,6 +240,7 @@ export async function startLogCapture( */ export async function stopLogCapture( logSessionId: string, + fileSystem: FileSystemExecutor = getDefaultFileSystemExecutor(), ): Promise<{ logContent: string; error?: string }> { const session = activeLogSessions.get(logSessionId); if (!session) { @@ -154,19 +250,24 @@ export async function stopLogCapture( try { log('info', `Attempting to stop log capture session: ${logSessionId}`); - const logFilePath = session.logFilePath; + const { logFilePath, logStream } = session; for (const process of session.processes) { if (!process.killed && process.exitCode === null) { process.kill('SIGTERM'); } } + logStream.end(); + await finished(logStream); + session.releaseActivity?.(); activeLogSessions.delete(logSessionId); log( 'info', `Log capture session ${logSessionId} stopped. Log file retained at: ${logFilePath}`, ); - await fs.promises.access(logFilePath, fs.constants.R_OK); - const fileContent = await fs.promises.readFile(logFilePath, 'utf-8'); + if (!fileSystem.existsSync(logFilePath)) { + throw new Error(`Log file not found: ${logFilePath}`); + } + const fileContent = await fileSystem.readFile(logFilePath, 'utf-8'); log('info', `Successfully read log content from ${logFilePath}`); return { logContent: fileContent }; } catch (error) { @@ -180,11 +281,11 @@ export async function stopLogCapture( * Deletes log files older than LOG_RETENTION_DAYS from the temp directory. * Runs quietly; errors are logged but do not throw. */ -async function cleanOldLogs(): Promise { - const tempDir = os.tmpdir(); - let files: string[]; +async function cleanOldLogs(fileSystem: FileSystemExecutor): Promise { + const tempDir = fileSystem.tmpdir(); + let files: unknown[]; try { - files = await fs.promises.readdir(tempDir); + files = await fileSystem.readdir(tempDir); } catch (err) { log( 'warn', @@ -194,15 +295,17 @@ async function cleanOldLogs(): Promise { } const now = Date.now(); const retentionMs = LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000; + const fileNames = files.filter((file): file is string => typeof file === 'string'); + await Promise.all( - files + fileNames .filter((f) => f.startsWith(LOG_FILE_PREFIX) && f.endsWith('.log')) .map(async (f) => { const filePath = path.join(tempDir, f); try { - const stat = await fs.promises.stat(filePath); + const stat = await fileSystem.stat(filePath); if (now - stat.mtimeMs > retentionMs) { - await fs.promises.unlink(filePath); + await fileSystem.rm(filePath, { force: true }); log('info', `Deleted old log file: ${filePath}`); } } catch (err) { diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 05635fb2..87afaa75 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -17,15 +17,21 @@ * It's used by virtually all other modules for status reporting and error logging. */ +import { createWriteStream, type WriteStream } from 'node:fs'; import { createRequire } from 'node:module'; import { resolve } from 'node:path'; -// Note: Removed "import * as Sentry from '@sentry/node'" to prevent native module loading at import time -const SENTRY_ENABLED = - process.env.SENTRY_DISABLED !== 'true' && process.env.XCODEBUILDMCP_SENTRY_DISABLED !== 'true'; +function isSentryDisabledFromEnv(): boolean { + return ( + process.env.SENTRY_DISABLED === 'true' || process.env.XCODEBUILDMCP_SENTRY_DISABLED === 'true' + ); +} + +const sentryEnabled = !isSentryDisabledFromEnv(); // Log levels in order of severity (lower number = more severe) const LOG_LEVELS = { + none: -1, emergency: 0, alert: 1, critical: 2, @@ -45,8 +51,15 @@ export interface LogContext { sentry?: boolean; } -// Client-requested log level (null means no filtering) -let clientLogLevel: LogLevel | null = null; +export function __shouldCaptureToSentryForTests(context?: LogContext): boolean { + return context?.sentry === true; +} + +// Client-requested log level ("none" means no output unless explicitly enabled) +let clientLogLevel: LogLevel = 'none'; + +let logFileStream: WriteStream | null = null; +let logFilePath: string | null = null; function isTestEnv(): boolean { return ( @@ -57,6 +70,7 @@ function isTestEnv(): boolean { } type SentryModule = typeof import('@sentry/node'); +type SentryLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'; const require = createRequire( typeof __filename === 'string' ? __filename : resolve(process.cwd(), 'package.json'), @@ -64,35 +78,56 @@ const require = createRequire( let cachedSentry: SentryModule | null = null; function loadSentrySync(): SentryModule | null { - if (!SENTRY_ENABLED || isTestEnv()) return null; - if (cachedSentry) return cachedSentry; + if (!sentryEnabled || isTestEnv()) { + return null; + } + if (cachedSentry) { + return cachedSentry; + } try { cachedSentry = require('@sentry/node') as SentryModule; return cachedSentry; } catch { - // If @sentry/node is not installed in some environments, fail silently. return null; } } function withSentry(cb: (s: SentryModule) => void): void { const s = loadSentrySync(); - if (!s) return; + if (!s) { + return; + } try { cb(s); } catch { - // no-op: avoid throwing inside logger + // Avoid throwing inside logger } } -if (!SENTRY_ENABLED) { - if (process.env.SENTRY_DISABLED === 'true') { - log('info', 'Sentry disabled due to SENTRY_DISABLED environment variable'); - } else if (process.env.XCODEBUILDMCP_SENTRY_DISABLED === 'true') { - log('info', 'Sentry disabled due to XCODEBUILDMCP_SENTRY_DISABLED environment variable'); +function mapLogLevelToSentry(level: string): SentryLogLevel { + switch (level.toLowerCase()) { + case 'emergency': + case 'alert': + return 'fatal'; + case 'critical': + case 'error': + return 'error'; + case 'warning': + return 'warn'; + case 'debug': + return 'debug'; + case 'notice': + case 'info': + return 'info'; + default: + return 'info'; } } +export function __mapLogLevelToSentryForTests(level: string): SentryLogLevel { + return mapLogLevelToSentry(level); +} + /** * Set the minimum log level for client-requested filtering * @param level The minimum log level to output @@ -102,11 +137,57 @@ export function setLogLevel(level: LogLevel): void { log('debug', `Log level set to: ${level}`); } +export function setLogFile(path: string | null): void { + if (!path) { + if (logFileStream) { + try { + logFileStream.end(); + } catch { + // ignore + } + } + logFileStream = null; + logFilePath = null; + return; + } + + if (logFilePath === path && logFileStream) { + return; + } + + if (logFileStream) { + try { + logFileStream.end(); + } catch { + // ignore + } + } + + try { + const stream = createWriteStream(path, { flags: 'a' }); + stream.on('error', (error) => { + if (stream !== logFileStream) return; + logFileStream = null; + logFilePath = null; + const message = error instanceof Error ? error.message : String(error); + const timestamp = new Date().toISOString(); + console.error(`[${timestamp}] [ERROR] Log file disabled after error: ${message}`); + }); + logFileStream = stream; + logFilePath = path; + const timestamp = new Date().toISOString(); + logFileStream.write(`[${timestamp}] [INFO] Log file initialized\n`); + } catch { + logFileStream = null; + logFilePath = null; + } +} + /** * Get the current client-requested log level - * @returns The current log level or null if no filtering is active + * @returns The current log level */ -export function getLogLevel(): LogLevel | null { +export function getLogLevel(): LogLevel { return clientLogLevel; } @@ -116,23 +197,19 @@ export function getLogLevel(): LogLevel | null { * @returns true if the message should be logged */ function shouldLog(level: string): boolean { - // Suppress logging during tests to keep test output clean - if (isTestEnv()) { + if (isTestEnv() && !logFileStream) { return false; } - // If no client level set, log everything - if (clientLogLevel === null) { - return true; + if (clientLogLevel === 'none') { + return false; } - // Check if the level is valid const levelKey = level.toLowerCase() as LogLevel; if (!(levelKey in LOG_LEVELS)) { - return true; // Log unknown levels + return true; } - // Only log if the message level is at or above the client's requested level return LOG_LEVELS[levelKey] <= LOG_LEVELS[clientLogLevel]; } @@ -143,23 +220,36 @@ function shouldLog(level: string): boolean { * @param context Optional context to control Sentry capture and other behavior */ export function log(level: string, message: string, context?: LogContext): void { - // Check if we should log this level - if (!shouldLog(level)) { - return; - } - const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`; - // Default: error level goes to Sentry - // But respect explicit override from context - const captureToSentry = SENTRY_ENABLED && (context?.sentry ?? level === 'error'); + const captureToSentry = sentryEnabled && __shouldCaptureToSentryForTests(context); if (captureToSentry) { - withSentry((s) => s.captureMessage(logMessage)); + withSentry((s) => { + const sentryLevel = mapLogLevelToSentry(level); + const loggerMethod = s.logger?.[sentryLevel]; + if (typeof loggerMethod === 'function') { + loggerMethod(message); + return; + } + s.captureMessage(logMessage); + }); + } + + if (logFileStream && clientLogLevel !== 'none') { + try { + logFileStream.write(`${logMessage}\n`); + } catch { + // ignore file logging failures + } + } + + if (!shouldLog(level)) { + return; } - // It's important to use console.error here to ensure logs don't interfere with MCP protocol communication - // see https://modelcontextprotocol.io/docs/tools/debugging#server-side-logging + // Uses stderr to avoid interfering with MCP protocol on stdout + // https://modelcontextprotocol.io/docs/tools/debugging#server-side-logging console.error(logMessage); } diff --git a/src/utils/nskeyedarchiver-parser.ts b/src/utils/nskeyedarchiver-parser.ts new file mode 100644 index 00000000..918c0d9f --- /dev/null +++ b/src/utils/nskeyedarchiver-parser.ts @@ -0,0 +1,241 @@ +/** + * NSKeyedArchiver Parser for Xcode xcuserstate files + * + * Parses binary plist files encoded with NSKeyedArchiver format + * to extract ActiveScheme and ActiveRunDestination values. + * + * Uses bplist-parser for robust binary plist parsing instead of + * relying on plutil output format which can change between macOS versions. + */ + +import { readFileSync } from 'fs'; +import { parseBuffer as bplistParseBuffer } from 'bplist-parser'; + +interface BplistUID { + UID: number; +} + +interface BplistResult { + $archiver?: string; + $objects?: unknown[]; + $top?: Record; + $version?: number; +} + +/** Parsed xcuserstate result */ +export interface XcodeStateResult { + scheme?: string; + simulatorId?: string; + simulatorPlatform?: string; + deviceLocation?: string; +} + +/** Represents a dictionary in the NSKeyedArchiver format */ +interface ArchivedDict { + 'NS.keys'?: BplistUID[]; + 'NS.objects'?: BplistUID[]; + [key: string]: unknown; +} + +/** + * Checks if a value is a bplist UID reference + */ +function isUID(value: unknown): value is BplistUID { + return ( + typeof value === 'object' && + value !== null && + 'UID' in value && + typeof (value as BplistUID).UID === 'number' + ); +} + +/** + * Resolves a UID to its value in the $objects array + */ +function resolveUID(objects: unknown[], uid: BplistUID | unknown): unknown { + if (!isUID(uid)) return uid; + const index = uid.UID; + if (index < 0 || index >= objects.length) return undefined; + return objects[index]; +} + +/** + * Finds the index of a string in the $objects array + */ +function findStringIndex(objects: unknown[], value: string): number { + return objects.findIndex((obj) => obj === value); +} + +/** + * Finds a dictionary that has the given key index in its NS.keys array + */ +function findDictWithKey(objects: unknown[], keyIndex: number): ArchivedDict | undefined { + for (const obj of objects) { + if (typeof obj !== 'object' || obj === null) continue; + const dict = obj as ArchivedDict; + const keys = dict['NS.keys']; + if (!Array.isArray(keys)) continue; + + const hasKey = keys.some((k) => isUID(k) && k.UID === keyIndex); + if (hasKey) return dict; + } + return undefined; +} + +/** + * Gets the value for a key in an NS.keys/NS.objects dictionary + */ +function getValueForKey(objects: unknown[], dict: ArchivedDict, keyIndex: number): unknown { + const keys = dict['NS.keys']; + const values = dict['NS.objects']; + + if (!Array.isArray(keys) || !Array.isArray(values)) return undefined; + + const keyPosition = keys.findIndex((k) => isUID(k) && k.UID === keyIndex); + if (keyPosition === -1 || keyPosition >= values.length) return undefined; + + return resolveUID(objects, values[keyPosition]); +} + +/** + * Main entry point: parses xcuserstate file and extracts Xcode state + * + * @param xcuserstatePath - Path to UserInterfaceState.xcuserstate file + * @returns Extracted scheme and simulator information + */ +export function parseXcuserstate(xcuserstatePath: string): XcodeStateResult { + const result: XcodeStateResult = {}; + + try { + const buffer = readFileSync(xcuserstatePath); + const [root] = bplistParseBuffer(buffer) as [BplistResult]; + + if (!root || root.$archiver !== 'NSKeyedArchiver' || !Array.isArray(root.$objects)) { + return result; + } + + const objects = root.$objects; + + // Find key indices + const activeSchemeIdx = findStringIndex(objects, 'ActiveScheme'); + const activeRunDestIdx = findStringIndex(objects, 'ActiveRunDestination'); + const ideNameStringIdx = findStringIndex(objects, 'IDENameString'); + const targetDeviceLocationIdx = findStringIndex(objects, 'targetDeviceLocation'); + + if (activeSchemeIdx === -1 && activeRunDestIdx === -1) { + return result; + } + + // Find the dictionary containing ActiveScheme key + const parentDict = findDictWithKey(objects, activeSchemeIdx); + if (!parentDict) { + return result; + } + + // Extract scheme name: ActiveScheme -> { IDENameString -> "SchemeName" } + if (activeSchemeIdx !== -1 && ideNameStringIdx !== -1) { + const schemeObj = getValueForKey(objects, parentDict, activeSchemeIdx); + if (typeof schemeObj === 'object' && schemeObj !== null) { + const schemeDict = schemeObj as ArchivedDict; + const schemeName = getValueForKey(objects, schemeDict, ideNameStringIdx); + if (typeof schemeName === 'string') { + result.scheme = schemeName; + } + } + } + + // Extract run destination: ActiveRunDestination -> { targetDeviceLocation -> "dvtdevice-..." } + if (activeRunDestIdx !== -1 && targetDeviceLocationIdx !== -1) { + const destObj = getValueForKey(objects, parentDict, activeRunDestIdx); + if (typeof destObj === 'object' && destObj !== null) { + const destDict = destObj as ArchivedDict; + const location = getValueForKey(objects, destDict, targetDeviceLocationIdx); + if (typeof location === 'string') { + result.deviceLocation = location; + + // Extract UUID from location string: "dvtdevice-iphonesimulator:UUID" + const match = location.match(/dvtdevice-([a-z]+):([A-F0-9-]{36})/i); + if (match) { + result.simulatorPlatform = match[1]; + result.simulatorId = match[2]; + } + } + } + } + } catch (error) { + // Return empty result on error - this is best-effort parsing + // that should never crash the server + console.error('Failed to parse xcuserstate:', error); + } + + return result; +} + +/** + * Parses xcuserstate from a Buffer (useful for testing with fixtures) + * + * @param buffer - Buffer containing the xcuserstate binary plist + * @returns Extracted scheme and simulator information + */ +export function parseXcuserstateBuffer(buffer: Buffer): XcodeStateResult { + const result: XcodeStateResult = {}; + + try { + const [root] = bplistParseBuffer(buffer) as [BplistResult]; + + if (!root || root.$archiver !== 'NSKeyedArchiver' || !Array.isArray(root.$objects)) { + return result; + } + + const objects = root.$objects; + + const activeSchemeIdx = findStringIndex(objects, 'ActiveScheme'); + const activeRunDestIdx = findStringIndex(objects, 'ActiveRunDestination'); + const ideNameStringIdx = findStringIndex(objects, 'IDENameString'); + const targetDeviceLocationIdx = findStringIndex(objects, 'targetDeviceLocation'); + + if (activeSchemeIdx === -1 && activeRunDestIdx === -1) { + return result; + } + + const parentDict = findDictWithKey(objects, activeSchemeIdx); + if (!parentDict) { + return result; + } + + if (activeSchemeIdx !== -1 && ideNameStringIdx !== -1) { + const schemeObj = getValueForKey(objects, parentDict, activeSchemeIdx); + if (typeof schemeObj === 'object' && schemeObj !== null) { + const schemeDict = schemeObj as ArchivedDict; + const schemeName = getValueForKey(objects, schemeDict, ideNameStringIdx); + if (typeof schemeName === 'string') { + result.scheme = schemeName; + } + } + } + + if (activeRunDestIdx !== -1 && targetDeviceLocationIdx !== -1) { + const destObj = getValueForKey(objects, parentDict, activeRunDestIdx); + if (typeof destObj === 'object' && destObj !== null) { + const destDict = destObj as ArchivedDict; + const location = getValueForKey(objects, destDict, targetDeviceLocationIdx); + if (typeof location === 'string') { + result.deviceLocation = location; + + const match = location.match(/dvtdevice-([a-z]+):([A-F0-9-]{36})/i); + if (match) { + result.simulatorPlatform = match[1]; + result.simulatorId = match[2]; + } + } + } + } + } catch (error) { + console.error('Failed to parse xcuserstate buffer:', error); + } + + return result; +} + +// Export helpers for testing +export { isUID, resolveUID, findStringIndex, findDictWithKey, getValueForKey }; diff --git a/src/utils/platform-detection.ts b/src/utils/platform-detection.ts new file mode 100644 index 00000000..61a4918f --- /dev/null +++ b/src/utils/platform-detection.ts @@ -0,0 +1,129 @@ +import { XcodePlatform } from '../types/common.ts'; +import type { CommandExecutor } from './execution/index.ts'; +import { getDefaultCommandExecutor } from './execution/index.ts'; +import { log } from './logging/index.ts'; + +export type SimulatorPlatform = + | XcodePlatform.iOSSimulator + | XcodePlatform.watchOSSimulator + | XcodePlatform.tvOSSimulator + | XcodePlatform.visionOSSimulator; + +export interface PlatformDetectionResult { + platform: SimulatorPlatform | null; + sdkroot: string | null; + supportedPlatforms: string[]; + error?: string; +} + +function sdkrootToSimulatorPlatform(sdkroot: string): SimulatorPlatform | null { + const sdkLower = sdkroot.toLowerCase(); + + if (sdkLower.startsWith('watchsimulator')) return XcodePlatform.watchOSSimulator; + if (sdkLower.startsWith('appletvsimulator')) return XcodePlatform.tvOSSimulator; + if (sdkLower.startsWith('xrsimulator')) return XcodePlatform.visionOSSimulator; + if (sdkLower.startsWith('iphonesimulator')) return XcodePlatform.iOSSimulator; + + return null; +} + +function supportedPlatformsToSimulatorPlatform(platforms: string[]): SimulatorPlatform | null { + const normalized = new Set(platforms.map((platform) => platform.toLowerCase())); + + if (normalized.has('watchsimulator')) return XcodePlatform.watchOSSimulator; + if (normalized.has('appletvsimulator')) return XcodePlatform.tvOSSimulator; + if (normalized.has('xrsimulator')) return XcodePlatform.visionOSSimulator; + if (normalized.has('iphonesimulator')) return XcodePlatform.iOSSimulator; + + return null; +} + +function extractBuildSettingValues(output: string, settingName: string): string[] { + const regex = new RegExp(`^\\s*${settingName}\\s*=\\s*(.+)$`, 'gm'); + const values: string[] = []; + + for (const match of output.matchAll(regex)) { + const value = match[1]?.trim(); + if (value) values.push(value); + } + + return values; +} + +export async function detectPlatformFromScheme( + projectPath: string | undefined, + workspacePath: string | undefined, + scheme: string, + executor: CommandExecutor = getDefaultCommandExecutor(), +): Promise { + const command = ['xcodebuild', '-showBuildSettings', '-scheme', scheme]; + + if (projectPath && workspacePath) { + return { + platform: null, + sdkroot: null, + supportedPlatforms: [], + error: 'projectPath and workspacePath are mutually exclusive for platform detection', + }; + } + + if (projectPath) { + command.push('-project', projectPath); + } else if (workspacePath) { + command.push('-workspace', workspacePath); + } else { + return { + platform: null, + sdkroot: null, + supportedPlatforms: [], + error: 'Either projectPath or workspacePath is required for platform detection', + }; + } + + try { + const result = await executor(command, 'Platform Detection', true); + if (!result.success) { + return { + platform: null, + sdkroot: null, + supportedPlatforms: [], + error: result.error ?? 'xcodebuild -showBuildSettings failed', + }; + } + + const output = result.output ?? ''; + const sdkroots = extractBuildSettingValues(output, 'SDKROOT'); + const supportedPlatforms = extractBuildSettingValues(output, 'SUPPORTED_PLATFORMS').flatMap( + (value) => value.split(/\s+/), + ); + + let sdkroot: string | null = null; + let platform: SimulatorPlatform | null = null; + + for (const sdkrootValue of sdkroots) { + const detected = sdkrootToSimulatorPlatform(sdkrootValue); + if (detected) { + platform = detected; + sdkroot = sdkrootValue; + break; + } + } + + if (!sdkroot && sdkroots.length > 0) sdkroot = sdkroots[0]; + + if (!platform && supportedPlatforms.length > 0) { + platform = supportedPlatformsToSimulatorPlatform(supportedPlatforms); + } + + return { platform, sdkroot, supportedPlatforms }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + log('warning', `[Platform Detection] ${errorMessage}`); + return { + platform: null, + sdkroot: null, + supportedPlatforms: [], + error: errorMessage, + }; + } +} diff --git a/src/utils/plugin-registry/index.ts b/src/utils/plugin-registry/index.ts deleted file mode 100644 index 3ab0f191..00000000 --- a/src/utils/plugin-registry/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { loadWorkflowGroups, loadPlugins } from '../../core/plugin-registry.ts'; diff --git a/src/utils/process-tree.ts b/src/utils/process-tree.ts new file mode 100644 index 00000000..c53f812a --- /dev/null +++ b/src/utils/process-tree.ts @@ -0,0 +1,90 @@ +import type { CommandExecutor } from './execution/index.ts'; + +export type ProcessTreeEntry = { + pid: string; + ppid: string; + name: string; + command: string; +}; + +export type ProcessTreeResult = { + entries: ProcessTreeEntry[]; + error?: string; +}; + +export async function getProcessTree( + executor: CommandExecutor, + startPid = process.pid.toString(), +): Promise { + const results: ProcessTreeEntry[] = []; + const seen = new Set(); + let currentPid = startPid; + let lastError: string | undefined; + + const parseLine = (line: string): ProcessTreeEntry | null => { + const tokens = line.trim().split(/\s+/); + if (tokens.length < 3) { + return null; + } + const pid = tokens[0]; + const ppid = tokens[1]; + const name = tokens[2]; + if (!pid || !ppid || !name) { + return null; + } + const command = tokens.slice(3).join(' '); + return { pid, ppid, name, command }; + }; + + const fetchProcessInfo = async (pid: string): Promise => { + const command = ['-o', 'pid=,ppid=,comm=,args=', '-p', pid]; + const attempts = [ + { bin: '/bin/ps', label: 'Get process info (ps)' }, + { bin: 'ps', label: 'Get process info (ps fallback)' }, + ]; + + for (const attempt of attempts) { + try { + const res = await executor([attempt.bin, ...command], attempt.label); + if (!res.success) { + lastError = res.error ?? `ps returned non-zero exit code for pid ${pid}`; + continue; + } + const line = res.output.trim().split('\n')[0]?.trim(); + if (!line) { + lastError = `ps returned no output for pid ${pid}`; + continue; + } + const parsed = parseLine(line); + if (!parsed) { + lastError = `ps output was not parseable for pid ${pid}`; + continue; + } + return parsed; + } catch (error) { + lastError = error instanceof Error ? error.message : String(error); + } + } + + return null; + }; + + while (currentPid && currentPid !== '0' && !seen.has(currentPid)) { + seen.add(currentPid); + const entry = await fetchProcessInfo(currentPid); + if (!entry) { + break; + } + + results.push(entry); + if (entry.ppid === entry.pid || entry.ppid === '0') { + break; + } + currentPid = entry.ppid; + } + + return { + entries: results, + error: results.length === 0 ? lastError : undefined, + }; +} diff --git a/src/utils/project-config.ts b/src/utils/project-config.ts new file mode 100644 index 00000000..64115078 --- /dev/null +++ b/src/utils/project-config.ts @@ -0,0 +1,344 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'; +import type { FileSystemExecutor } from './FileSystemExecutor.ts'; +import type { SessionDefaults } from './session-store.ts'; +import { log } from './logger.ts'; +import { removeUndefined } from './remove-undefined.ts'; +import { runtimeConfigFileSchema, type RuntimeConfigFile } from './runtime-config-schema.ts'; +import { normalizeSessionDefaultsProfileName } from './session-defaults-profile.ts'; + +const CONFIG_DIR = '.xcodebuildmcp'; +const CONFIG_FILE = 'config.yaml'; + +export type ProjectConfig = RuntimeConfigFile & { + schemaVersion: 1; + sessionDefaults?: Partial; + sessionDefaultsProfiles?: Record>; + activeSessionDefaultsProfile?: string; + enabledWorkflows?: string[]; + debuggerBackend?: 'dap' | 'lldb-cli'; + [key: string]: unknown; +}; + +export type LoadProjectConfigOptions = { + fs: FileSystemExecutor; + cwd: string; +}; + +export type LoadProjectConfigResult = + | { found: false } + | { found: false; path: string; error: Error } + | { found: true; path: string; config: ProjectConfig; notices: string[] }; + +export type PersistSessionDefaultsOptions = { + fs: FileSystemExecutor; + cwd: string; + patch: Partial; + deleteKeys?: (keyof SessionDefaults)[]; + profile?: string | null; +}; + +export type PersistActiveSessionDefaultsProfileOptions = { + fs: FileSystemExecutor; + cwd: string; + profile?: string | null; +}; + +type PersistenceTargetOptions = { + fs: FileSystemExecutor; + configPath: string; +}; + +function getConfigDir(cwd: string): string { + return path.join(cwd, CONFIG_DIR); +} + +function getConfigPath(cwd: string): string { + return path.join(getConfigDir(cwd), CONFIG_FILE); +} + +function isPlainObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function toError(value: unknown): Error { + return value instanceof Error ? value : new Error(String(value)); +} + +function hasValue>(defaults: T, key: keyof T): boolean { + return Object.prototype.hasOwnProperty.call(defaults, key) && defaults[key] !== undefined; +} + +function normalizeMutualExclusivity(defaults: Partial): { + normalized: Partial; + notices: string[]; +} { + const normalized: Partial = { ...defaults }; + const notices: string[] = []; + + if (hasValue(normalized, 'projectPath') && hasValue(normalized, 'workspacePath')) { + delete normalized.projectPath; + notices.push('Both projectPath and workspacePath were provided; keeping workspacePath.'); + } + + if (hasValue(normalized, 'simulatorId') && hasValue(normalized, 'simulatorName')) { + notices.push( + 'Both simulatorId and simulatorName were provided; storing both and preferring simulatorId when disambiguating.', + ); + } + + return { normalized, notices }; +} + +function tryFileUrlToPath(value: string): string | null { + if (!value.startsWith('file:')) { + return null; + } + + try { + return fileURLToPath(value); + } catch (error) { + log('warning', `Failed to parse file URL path: ${value}. ${String(error)}`); + return null; + } +} + +function normalizePathValue(value: string, cwd: string): string { + const fileUrlPath = tryFileUrlToPath(value); + if (fileUrlPath) { + return fileUrlPath; + } + + if (path.isAbsolute(value)) { + return value; + } + + return path.resolve(cwd, value); +} + +function resolveRelativeSessionPaths( + defaults: Partial, + cwd: string, +): Partial { + const resolved: Partial = { ...defaults }; + const pathKeys = ['projectPath', 'workspacePath', 'derivedDataPath'] as const; + + for (const key of pathKeys) { + const value = resolved[key]; + if (typeof value === 'string' && value.length > 0) { + resolved[key] = normalizePathValue(value, cwd); + } + } + + return resolved; +} + +function normalizeEnabledWorkflows(value: unknown): string[] { + if (value == null) return []; + if (Array.isArray(value)) { + const normalized = value + .filter((name): name is string => typeof name === 'string') + .map((name) => name.trim().toLowerCase()) + .filter(Boolean); + return normalized; + } + if (typeof value === 'string') { + const normalized = value + .split(',') + .map((name) => name.trim().toLowerCase()) + .filter(Boolean); + return normalized; + } + return []; +} + +function resolveRelativeTopLevelPaths(config: ProjectConfig, cwd: string): ProjectConfig { + const resolved: ProjectConfig = { ...config }; + const pathKeys = ['axePath', 'iosTemplatePath', 'macosTemplatePath'] as const; + + for (const key of pathKeys) { + const value = resolved[key]; + if (typeof value === 'string' && value.length > 0) { + resolved[key] = normalizePathValue(value, cwd); + } + } + + return resolved; +} + +function normalizeSessionDefaultsProfiles( + profiles: Record>, + cwd: string, +): { profiles: Record>; notices: string[] } { + const normalizedProfiles: Record> = {}; + const notices: string[] = []; + + for (const [profileName, defaults] of Object.entries(profiles)) { + const trimmedName = profileName.trim(); + if (trimmedName.length === 0) { + notices.push('Ignored sessionDefaultsProfiles entry with an empty profile name.'); + continue; + } + const normalized = normalizeMutualExclusivity(defaults); + notices.push(...normalized.notices.map((notice) => `[profile:${trimmedName}] ${notice}`)); + normalizedProfiles[trimmedName] = resolveRelativeSessionPaths(normalized.normalized, cwd); + } + + return { profiles: normalizedProfiles, notices }; +} + +function normalizeDebuggerBackend(config: RuntimeConfigFile): ProjectConfig { + if (config.debuggerBackend === 'lldb') { + const normalized: RuntimeConfigFile = { ...config, debuggerBackend: 'lldb-cli' }; + return toProjectConfig(normalized); + } + return toProjectConfig(config); +} + +function normalizeConfigForPersistence(config: RuntimeConfigFile): ProjectConfig { + const base = normalizeDebuggerBackend(config); + if (config.enabledWorkflows === undefined) { + return base; + } + const normalizedWorkflows = normalizeEnabledWorkflows(config.enabledWorkflows); + return { ...base, enabledWorkflows: normalizedWorkflows }; +} + +function toProjectConfig(config: RuntimeConfigFile): ProjectConfig { + return config as ProjectConfig; +} + +function parseProjectConfig(rawText: string): RuntimeConfigFile { + const parsed: unknown = parseYaml(rawText); + if (!isPlainObject(parsed)) { + throw new Error('Project config must be an object'); + } + return runtimeConfigFileSchema.parse(parsed) as RuntimeConfigFile; +} + +async function readBaseConfigForPersistence( + options: PersistenceTargetOptions, +): Promise { + if (!options.fs.existsSync(options.configPath)) { + return { schemaVersion: 1 }; + } + + try { + const rawText = await options.fs.readFile(options.configPath, 'utf8'); + const parsed = parseProjectConfig(rawText); + return { ...normalizeConfigForPersistence(parsed), schemaVersion: 1 }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + log( + 'warning', + `Failed to read or parse project config at ${options.configPath}. Overwriting with new config. ${errorMessage}`, + ); + return { schemaVersion: 1 }; + } +} + +export async function loadProjectConfig( + options: LoadProjectConfigOptions, +): Promise { + const configPath = getConfigPath(options.cwd); + + if (!options.fs.existsSync(configPath)) { + return { found: false }; + } + + try { + const rawText = await options.fs.readFile(configPath, 'utf8'); + const parsed = parseProjectConfig(rawText); + const notices: string[] = []; + + let config = normalizeDebuggerBackend(parsed); + + if (parsed.enabledWorkflows !== undefined) { + const normalizedWorkflows = normalizeEnabledWorkflows(parsed.enabledWorkflows); + config = { ...config, enabledWorkflows: normalizedWorkflows }; + } + + if (config.sessionDefaults) { + const normalized = normalizeMutualExclusivity(config.sessionDefaults); + notices.push(...normalized.notices); + const resolved = resolveRelativeSessionPaths(normalized.normalized, options.cwd); + config = { ...config, sessionDefaults: resolved }; + } + + if (config.sessionDefaultsProfiles) { + const normalizedProfiles = normalizeSessionDefaultsProfiles( + config.sessionDefaultsProfiles, + options.cwd, + ); + notices.push(...normalizedProfiles.notices); + config = { ...config, sessionDefaultsProfiles: normalizedProfiles.profiles }; + } + + config = resolveRelativeTopLevelPaths(config, options.cwd); + + return { found: true, path: configPath, config, notices }; + } catch (error) { + return { found: false, path: configPath, error: toError(error) }; + } +} + +export async function persistSessionDefaultsToProjectConfig( + options: PersistSessionDefaultsOptions, +): Promise<{ path: string }> { + const configDir = getConfigDir(options.cwd); + const configPath = getConfigPath(options.cwd); + + await options.fs.mkdir(configDir, { recursive: true }); + const baseConfig = await readBaseConfigForPersistence({ fs: options.fs, configPath }); + + const patch = removeUndefined(options.patch as Record); + const targetProfile = normalizeSessionDefaultsProfileName(options.profile); + const isGlobalProfile = targetProfile === null; + const baseDefaults = isGlobalProfile + ? (baseConfig.sessionDefaults ?? {}) + : (baseConfig.sessionDefaultsProfiles?.[targetProfile] ?? {}); + const nextSessionDefaults: Partial = { ...baseDefaults, ...patch }; + + const nextConfig: ProjectConfig = { + ...baseConfig, + schemaVersion: 1, + }; + for (const key of options.deleteKeys ?? []) { + delete nextSessionDefaults[key]; + } + if (isGlobalProfile) { + nextConfig.sessionDefaults = nextSessionDefaults; + } else { + nextConfig.sessionDefaultsProfiles = { + ...(nextConfig.sessionDefaultsProfiles ?? {}), + [targetProfile]: nextSessionDefaults, + }; + } + + await options.fs.writeFile(configPath, stringifyYaml(nextConfig), 'utf8'); + + return { path: configPath }; +} + +export async function persistActiveSessionDefaultsProfileToProjectConfig( + options: PersistActiveSessionDefaultsProfileOptions, +): Promise<{ path: string }> { + const configDir = getConfigDir(options.cwd); + const configPath = getConfigPath(options.cwd); + + await options.fs.mkdir(configDir, { recursive: true }); + const baseConfig = await readBaseConfigForPersistence({ fs: options.fs, configPath }); + + const nextConfig: ProjectConfig = { ...baseConfig, schemaVersion: 1 }; + const activeProfile = normalizeSessionDefaultsProfileName(options.profile); + if (activeProfile === null) { + delete nextConfig.activeSessionDefaultsProfile; + } else { + nextConfig.activeSessionDefaultsProfile = activeProfile; + } + + await options.fs.writeFile(configPath, stringifyYaml(nextConfig), 'utf8'); + + return { path: configPath }; +} diff --git a/src/utils/remove-undefined.ts b/src/utils/remove-undefined.ts new file mode 100644 index 00000000..9a63bed0 --- /dev/null +++ b/src/utils/remove-undefined.ts @@ -0,0 +1,9 @@ +export function removeUndefined>(input: T): Partial { + const result: Partial = {}; + for (const [key, value] of Object.entries(input)) { + if (value !== undefined) { + result[key as keyof T] = value as T[keyof T]; + } + } + return result; +} diff --git a/src/utils/responses/__tests__/next-steps-renderer.test.ts b/src/utils/responses/__tests__/next-steps-renderer.test.ts new file mode 100644 index 00000000..c0ff6080 --- /dev/null +++ b/src/utils/responses/__tests__/next-steps-renderer.test.ts @@ -0,0 +1,333 @@ +import { describe, it, expect } from 'vitest'; +import { + renderNextStep, + renderNextStepsSection, + processToolResponse, +} from '../next-steps-renderer.ts'; +import type { NextStep, ToolResponse } from '../../../types/common.ts'; + +describe('next-steps-renderer', () => { + describe('renderNextStep', () => { + it('should format step for CLI with workflow and no params', () => { + const step: NextStep = { + tool: 'open_sim', + cliTool: 'open-sim', + workflow: 'simulator', + label: 'Open the Simulator app', + params: {}, + }; + + const result = renderNextStep(step, 'cli'); + expect(result).toBe('Open the Simulator app: xcodebuildmcp simulator open-sim'); + }); + + it('should format step for CLI with workflow and params', () => { + const step: NextStep = { + tool: 'install_app_sim', + cliTool: 'install-app-sim', + workflow: 'simulator', + label: 'Install an app', + params: { simulatorId: 'ABC123', appPath: '/path/to/app' }, + }; + + const result = renderNextStep(step, 'cli'); + expect(result).toBe( + 'Install an app: xcodebuildmcp simulator install-app-sim --simulator-id "ABC123" --app-path "/path/to/app"', + ); + }); + + it('should use cliTool for CLI rendering', () => { + const step: NextStep = { + tool: 'install_app_sim', + cliTool: 'install-app', + workflow: 'simulator', + label: 'Install an app', + params: { simulatorId: 'ABC123' }, + }; + + const result = renderNextStep(step, 'cli'); + expect(result).toBe( + 'Install an app: xcodebuildmcp simulator install-app --simulator-id "ABC123"', + ); + }); + + it('should fallback to kebab-case tool name for CLI without cliTool', () => { + const step: NextStep = { + tool: 'open_sim', + label: 'Open the Simulator app', + }; + + expect(renderNextStep(step, 'cli')).toBe('Open the Simulator app: xcodebuildmcp open-sim'); + }); + + it('should format step for CLI without workflow', () => { + const step: NextStep = { + tool: 'open_sim', + cliTool: 'open-sim', + label: 'Open the Simulator app', + params: {}, + }; + + const result = renderNextStep(step, 'cli'); + expect(result).toBe('Open the Simulator app: xcodebuildmcp open-sim'); + }); + + it('should format step for CLI with boolean param (true)', () => { + const step: NextStep = { + tool: 'some_tool', + cliTool: 'some-tool', + label: 'Do something', + params: { verbose: true }, + }; + + const result = renderNextStep(step, 'cli'); + expect(result).toBe('Do something: xcodebuildmcp some-tool --verbose'); + }); + + it('should format step for CLI with boolean param (false)', () => { + const step: NextStep = { + tool: 'some_tool', + cliTool: 'some-tool', + label: 'Do something', + params: { verbose: false }, + }; + + const result = renderNextStep(step, 'cli'); + expect(result).toBe('Do something: xcodebuildmcp some-tool'); + }); + + it('should format step for MCP with no params', () => { + const step: NextStep = { + tool: 'open_sim', + label: 'Open the Simulator app', + }; + + const result = renderNextStep(step, 'mcp'); + expect(result).toBe('Open the Simulator app: open_sim()'); + }); + + it('should format step for MCP with params', () => { + const step: NextStep = { + tool: 'install_app_sim', + label: 'Install an app', + params: { simulatorId: 'ABC123', appPath: '/path/to/app' }, + }; + + const result = renderNextStep(step, 'mcp'); + expect(result).toBe( + 'Install an app: install_app_sim({ simulatorId: "ABC123", appPath: "/path/to/app" })', + ); + }); + + it('should format step for MCP with numeric param', () => { + const step: NextStep = { + tool: 'some_tool', + label: 'Do something', + params: { count: 42 }, + }; + + const result = renderNextStep(step, 'mcp'); + expect(result).toBe('Do something: some_tool({ count: 42 })'); + }); + + it('should format step for MCP with boolean param', () => { + const step: NextStep = { + tool: 'some_tool', + label: 'Do something', + params: { verbose: true }, + }; + + const result = renderNextStep(step, 'mcp'); + expect(result).toBe('Do something: some_tool({ verbose: true })'); + }); + + it('should handle daemon runtime same as MCP', () => { + const step: NextStep = { + tool: 'open_sim', + label: 'Open the Simulator app', + }; + + const result = renderNextStep(step, 'daemon'); + expect(result).toBe('Open the Simulator app: open_sim()'); + }); + + it('should render label-only step as plain text', () => { + const step: NextStep = { + label: 'Verify layout visually before continuing', + }; + + expect(renderNextStep(step, 'cli')).toBe('Verify layout visually before continuing'); + expect(renderNextStep(step, 'mcp')).toBe('Verify layout visually before continuing'); + }); + }); + + describe('renderNextStepsSection', () => { + it('should return empty string for empty steps', () => { + const result = renderNextStepsSection([], 'cli'); + expect(result).toBe(''); + }); + + it('should render numbered list for CLI', () => { + const steps: NextStep[] = [ + { tool: 'open_sim', cliTool: 'open-sim', label: 'Open Simulator', params: {} }, + { + tool: 'install_app_sim', + cliTool: 'install-app-sim', + label: 'Install app', + params: { simulatorId: 'X' }, + }, + ]; + + const result = renderNextStepsSection(steps, 'cli'); + expect(result).toBe( + '\n\nNext steps:\n' + + '1. Open Simulator: xcodebuildmcp open-sim\n' + + '2. Install app: xcodebuildmcp install-app-sim --simulator-id "X"', + ); + }); + + it('should render numbered list for MCP', () => { + const steps: NextStep[] = [ + { tool: 'open_sim', label: 'Open Simulator', params: {} }, + { tool: 'install_app_sim', label: 'Install app', params: { simulatorId: 'X' } }, + ]; + + const result = renderNextStepsSection(steps, 'mcp'); + expect(result).toBe( + '\n\nNext steps:\n' + + '1. Open Simulator: open_sim()\n' + + '2. Install app: install_app_sim({ simulatorId: "X" })', + ); + }); + + it('should keep declared order', () => { + const steps: NextStep[] = [ + { tool: 'third', label: 'Third', params: {} }, + { tool: 'first', label: 'First', params: {} }, + { tool: 'second', label: 'Second', params: {} }, + ]; + + const result = renderNextStepsSection(steps, 'mcp'); + expect(result).toContain('1. Third: third()'); + expect(result).toContain('2. First: first()'); + expect(result).toContain('3. Second: second()'); + }); + + it('should render label-only next step without command', () => { + const steps: NextStep[] = [ + { label: 'Take a look at the screenshot' }, + { tool: 'open_sim', label: 'Open simulator', params: {} }, + ]; + + const result = renderNextStepsSection(steps, 'cli'); + expect(result).toContain('1. Take a look at the screenshot'); + expect(result).toContain('2. Open simulator: xcodebuildmcp open-sim'); + }); + }); + + describe('processToolResponse', () => { + it('should pass through response with no nextSteps', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Success!' }], + }; + + const result = processToolResponse(response, 'cli', 'normal'); + expect(result).toEqual({ + content: [{ type: 'text', text: 'Success!' }], + }); + }); + + it('should strip nextSteps in minimal style', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Success!' }], + nextSteps: [{ tool: 'foo', cliTool: 'foo', label: 'Do foo', params: {} }], + }; + + const result = processToolResponse(response, 'cli', 'minimal'); + expect(result).toEqual({ + content: [{ type: 'text', text: 'Success!' }], + }); + expect(result.nextSteps).toBeUndefined(); + }); + + it('should append next steps to last text content in normal style', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Simulator booted.' }], + nextSteps: [ + { + tool: 'open_sim', + cliTool: 'open-sim', + label: 'Open Simulator', + params: {}, + priority: 1, + }, + ], + }; + + const result = processToolResponse(response, 'cli', 'normal'); + expect(result.content[0].text).toBe( + 'Simulator booted.\n\nNext steps:\n1. Open Simulator: xcodebuildmcp open-sim', + ); + expect(result.nextSteps).toBeUndefined(); + }); + + it('should render MCP-style for MCP runtime', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Simulator booted.' }], + nextSteps: [{ tool: 'open_sim', label: 'Open Simulator', params: {}, priority: 1 }], + }; + + const result = processToolResponse(response, 'mcp', 'normal'); + expect(result.content[0].text).toBe( + 'Simulator booted.\n\nNext steps:\n1. Open Simulator: open_sim()', + ); + }); + + it('should handle response with empty nextSteps array', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Done.' }], + nextSteps: [], + }; + + const result = processToolResponse(response, 'cli', 'normal'); + expect(result).toEqual({ + content: [{ type: 'text', text: 'Done.' }], + }); + }); + + it('should preserve other response properties', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Error!' }], + isError: true, + _meta: { foo: 'bar' }, + nextSteps: [{ tool: 'retry', cliTool: 'retry', label: 'Retry', params: {} }], + }; + + const result = processToolResponse(response, 'cli', 'minimal'); + expect(result.isError).toBe(true); + expect(result._meta).toEqual({ foo: 'bar' }); + }); + + it('should not mutate original response', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Original' }], + nextSteps: [{ tool: 'foo', cliTool: 'foo', label: 'Foo', params: {} }], + }; + + processToolResponse(response, 'cli', 'normal'); + + expect(response.content[0].text).toBe('Original'); + expect(response.nextSteps).toHaveLength(1); + }); + + it('should default to normal style when not specified', () => { + const response: ToolResponse = { + content: [{ type: 'text', text: 'Success!' }], + nextSteps: [{ tool: 'foo', cliTool: 'foo', label: 'Do foo', params: {} }], + }; + + const result = processToolResponse(response, 'cli'); + expect(result.content[0].text).toContain('Next steps:'); + }); + }); +}); diff --git a/src/utils/responses/index.ts b/src/utils/responses/index.ts index ef740dcc..1707ea06 100644 --- a/src/utils/responses/index.ts +++ b/src/utils/responses/index.ts @@ -10,6 +10,11 @@ export { SystemError, ValidationError, } from '../errors.ts'; +export { + processToolResponse, + renderNextStep, + renderNextStepsSection, +} from './next-steps-renderer.ts'; // Types -export type { ToolResponse } from '../../types/common.ts'; +export type { ToolResponse, NextStep, OutputStyle } from '../../types/common.ts'; diff --git a/src/utils/responses/next-steps-renderer.ts b/src/utils/responses/next-steps-renderer.ts new file mode 100644 index 00000000..4ad71e46 --- /dev/null +++ b/src/utils/responses/next-steps-renderer.ts @@ -0,0 +1,149 @@ +import type { RuntimeKind } from '../../runtime/types.ts'; +import type { NextStep, OutputStyle, ToolResponse } from '../../types/common.ts'; + +/** + * Convert a string to kebab-case for CLI flag names. + */ +function toKebabCase(name: string): string { + return name + .replace(/_/g, '-') + .replace(/([a-z])([A-Z])/g, '$1-$2') + .toLowerCase(); +} + +function resolveLabel(step: NextStep): string { + if (step.label?.trim()) return step.label; + if (step.tool) return step.tool; + if (step.cliTool) return step.cliTool; + return 'Next action'; +} + +/** + * Format a single next step for CLI output. + * Example: xcodebuildmcp simulator open-sim + * Example: xcodebuildmcp simulator install-app-sim --simulator-id "ABC123" --app-path "PATH" + */ +function formatNextStepForCli(step: NextStep): string { + if (!step.tool) { + return resolveLabel(step); + } + const parts = ['xcodebuildmcp']; + const cliTool = step.cliTool ?? toKebabCase(step.tool); + const params = step.params ?? {}; + + // Include workflow as subcommand if provided + if (step.workflow) { + parts.push(step.workflow); + } + + parts.push(cliTool); + + for (const [key, value] of Object.entries(params)) { + const flagName = toKebabCase(key); + if (typeof value === 'boolean') { + if (value) { + parts.push(`--${flagName}`); + } + } else { + parts.push(`--${flagName} "${String(value)}"`); + } + } + + return parts.join(' '); +} + +/** + * Format a single next step for MCP output. + * Example: open_sim() + * Example: install_app_sim({ simulatorId: "ABC123", appPath: "PATH" }) + */ +function formatNextStepForMcp(step: NextStep): string { + if (!step.tool) { + return resolveLabel(step); + } + + const paramEntries = Object.entries(step.params ?? {}); + if (paramEntries.length === 0) { + return `${step.tool}()`; + } + + const paramsStr = paramEntries + .map(([key, value]) => { + if (typeof value === 'string') { + return `${key}: "${value}"`; + } + return `${key}: ${String(value)}`; + }) + .join(', '); + + return `${step.tool}({ ${paramsStr} })`; +} + +/** + * Render a single next step based on runtime. + */ +export function renderNextStep(step: NextStep, runtime: RuntimeKind): string { + if (!step.tool) { + return resolveLabel(step); + } + const formatted = runtime === 'cli' ? formatNextStepForCli(step) : formatNextStepForMcp(step); + if (!step.label) { + return formatted; + } + return `${step.label}: ${formatted}`; +} + +/** + * Render the full next steps section. + * Returns empty string if no steps. + */ +export function renderNextStepsSection(steps: NextStep[], runtime: RuntimeKind): string { + if (steps.length === 0) { + return ''; + } + + const sorted = [...steps].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0)); + const lines = sorted.map((step, index) => `${index + 1}. ${renderNextStep(step, runtime)}`); + + return `\n\nNext steps:\n${lines.join('\n')}`; +} + +/** + * Process a tool response, applying next steps rendering based on runtime and style. + * + * - In 'minimal' style, nextSteps are stripped entirely + * - In 'normal' style, nextSteps are rendered and appended to text content + * + * Returns a new response object (does not mutate the original). + */ +export function processToolResponse( + response: ToolResponse, + runtime: RuntimeKind, + style: OutputStyle = 'normal', +): ToolResponse { + const { nextSteps, ...rest } = response; + + // If no nextSteps or minimal style, strip nextSteps and return + if (!nextSteps || nextSteps.length === 0 || style === 'minimal') { + return { ...rest }; + } + + // Render next steps section + const nextStepsSection = renderNextStepsSection(nextSteps, runtime); + + // Append to the last text content item + const processedContent = response.content.map((item, index) => { + if (item.type === 'text' && index === response.content.length - 1) { + return { ...item, text: item.text + nextStepsSection }; + } + return item; + }); + + // If no text content existed, add one with just the next steps + const hasTextContent = response.content.some((item) => item.type === 'text'); + if (!hasTextContent && nextStepsSection) { + processedContent.push({ type: 'text', text: nextStepsSection.trim() }); + } + + return { ...rest, content: processedContent }; +} diff --git a/src/utils/runtime-config-schema.ts b/src/utils/runtime-config-schema.ts new file mode 100644 index 00000000..8900ac19 --- /dev/null +++ b/src/utils/runtime-config-schema.ts @@ -0,0 +1,31 @@ +import * as z from 'zod'; +import { sessionDefaultsSchema } from './session-defaults-schema.ts'; + +export const runtimeConfigFileSchema = z + .object({ + schemaVersion: z.literal(1).optional().default(1), + enabledWorkflows: z.union([z.array(z.string()), z.string()]).optional(), + debug: z.boolean().optional(), + experimentalWorkflowDiscovery: z.boolean().optional(), + disableSessionDefaults: z.boolean().optional(), + disableXcodeAutoSync: z.boolean().optional(), + uiDebuggerGuardMode: z.enum(['error', 'warn', 'off']).optional(), + incrementalBuildsEnabled: z.boolean().optional(), + dapRequestTimeoutMs: z.number().int().positive().optional(), + dapLogEvents: z.boolean().optional(), + launchJsonWaitMs: z.number().int().nonnegative().optional(), + axePath: z.string().optional(), + iosTemplatePath: z.string().optional(), + iosTemplateVersion: z.string().optional(), + macosTemplatePath: z.string().optional(), + macosTemplateVersion: z.string().optional(), + debuggerBackend: z.enum(['dap', 'lldb-cli', 'lldb']).optional(), + sessionDefaults: sessionDefaultsSchema.optional(), + sessionDefaultsProfiles: z.record(z.string(), sessionDefaultsSchema).optional(), + activeSessionDefaultsProfile: z.string().optional(), + }) + .passthrough(); + +export type RuntimeConfigFile = z.infer & { + [key: string]: unknown; +}; diff --git a/src/utils/runtime-config-types.ts b/src/utils/runtime-config-types.ts new file mode 100644 index 00000000..3d953113 --- /dev/null +++ b/src/utils/runtime-config-types.ts @@ -0,0 +1 @@ +export type UiDebuggerGuardMode = 'error' | 'warn' | 'off'; diff --git a/src/utils/runtime-registry.ts b/src/utils/runtime-registry.ts deleted file mode 100644 index 54c80559..00000000 --- a/src/utils/runtime-registry.ts +++ /dev/null @@ -1,35 +0,0 @@ -export type RuntimeToolInfo = - | { - mode: 'runtime'; - enabledWorkflows: string[]; - enabledTools: string[]; - totalRegistered: number; - } - | { - mode: 'static'; - enabledWorkflows: string[]; - enabledTools: string[]; - totalRegistered: number; - note: string; - }; - -let runtimeToolInfo: RuntimeToolInfo | null = null; - -export function recordRuntimeRegistration(info: { - enabledWorkflows: string[]; - enabledTools: string[]; -}): void { - const enabledWorkflows = [...new Set(info.enabledWorkflows)]; - const enabledTools = [...new Set(info.enabledTools)]; - - runtimeToolInfo = { - mode: 'runtime', - enabledWorkflows, - enabledTools, - totalRegistered: enabledTools.length, - }; -} - -export function getRuntimeRegistration(): RuntimeToolInfo | null { - return runtimeToolInfo; -} diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts index 8ecba4f2..4a9e48c4 100644 --- a/src/utils/sentry.ts +++ b/src/utils/sentry.ts @@ -2,90 +2,161 @@ * Sentry instrumentation for XcodeBuildMCP * * This file initializes Sentry when explicitly called to avoid side effects - * during module import (needed for Smithery's module-based entry). + * during module import. */ import * as Sentry from '@sentry/node'; -import { execSync } from 'child_process'; import { version } from '../version.ts'; -// Inlined system info functions to avoid circular dependencies -function getXcodeInfo(): { version: string; path: string; selectedXcode: string; error?: string } { - try { - const xcodebuildOutput = execSync('xcodebuild -version', { encoding: 'utf8' }).trim(); - const version = xcodebuildOutput.split('\n').slice(0, 2).join(' - '); - const path = execSync('xcode-select -p', { encoding: 'utf8' }).trim(); - const selectedXcode = execSync('xcrun --find xcodebuild', { encoding: 'utf8' }).trim(); - - return { version, path, selectedXcode }; - } catch (error) { - return { - version: 'Not available', - path: 'Not available', - selectedXcode: 'Not available', - error: error instanceof Error ? error.message : String(error), - }; - } -} - -function getEnvironmentVariables(): Record { - const relevantVars = [ - 'INCREMENTAL_BUILDS_ENABLED', - 'PATH', - 'DEVELOPER_DIR', - 'HOME', - 'USER', - 'TMPDIR', - 'NODE_ENV', - 'SENTRY_DISABLED', - ]; - - const envVars: Record = {}; - relevantVars.forEach((varName) => { - envVars[varName] = process.env[varName] ?? ''; - }); +const USER_HOME_PATH_PATTERN = /\/Users\/[^/\s]+/g; +const XCODE_VERSION_PATTERN = /^Xcode\s+(.+)$/m; +const XCODE_BUILD_PATTERN = /^Build version\s+(.+)$/m; +const SENTRY_SELF_TEST_ENV_VAR = 'XCODEBUILDMCP_SENTRY_SELFTEST'; + +export type SentryRuntimeMode = 'mcp' | 'cli-daemon' | 'cli'; +export type SentryToolRuntime = 'cli' | 'daemon' | 'mcp'; +export type SentryToolTransport = 'direct' | 'daemon' | 'xcode-ide-daemon'; +export type SentryToolInvocationOutcome = 'completed' | 'infra_error'; +export type SentryDaemonLifecycleEvent = 'start' | 'shutdown' | 'crash'; + +export interface SentryRuntimeContext { + mode: SentryRuntimeMode; + xcodeAvailable?: boolean; + enabledWorkflows?: string[]; + disableSessionDefaults?: boolean; + disableXcodeAutoSync?: boolean; + incrementalBuildsEnabled?: boolean; + debugEnabled?: boolean; + uiDebuggerGuardMode?: string; + xcodeIdeWorkflowEnabled?: boolean; + axeAvailable?: boolean; + axeSource?: 'env' | 'bundled' | 'path' | 'unavailable'; + axeVersion?: string; + xcodeDeveloperDir?: string; + xcodebuildPath?: string; + xcodemakeAvailable?: boolean; + xcodemakeEnabled?: boolean; + xcodeVersion?: string; + xcodeBuildVersion?: string; +} + +function redactPathLikeData(value: string): string { + return value.replace(USER_HOME_PATH_PATTERN, '/Users/'); +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function redactUnknown(value: unknown): unknown { + if (typeof value === 'string') { + return redactPathLikeData(value); + } - Object.keys(process.env).forEach((key) => { - if (key.startsWith('XCODEBUILDMCP_')) { - envVars[key] = process.env[key] ?? ''; + if (Array.isArray(value)) { + return value.map((item) => redactUnknown(item)); + } + + if (isRecord(value)) { + const output: Record = {}; + for (const [key, nested] of Object.entries(value)) { + output[key] = redactUnknown(nested); } - }); + return output; + } - return envVars; + return value; } -function checkBinaryAvailability(binary: string): { available: boolean; version?: string } { - try { - execSync(`which ${binary}`, { stdio: 'ignore' }); - } catch { - return { available: false }; +function redactEvent(event: Sentry.ErrorEvent): Sentry.ErrorEvent { + // Remove default identity/request surfaces entirely. + delete event.user; + delete event.request; + delete event.breadcrumbs; + + if (typeof event.message === 'string') { + event.message = redactPathLikeData(event.message); } - let version: string | undefined; - const versionCommands: Record = { - axe: 'axe --version', - mise: 'mise --version', - }; + const exceptionValues = event.exception?.values ?? []; + for (const exceptionValue of exceptionValues) { + if (typeof exceptionValue.value === 'string') { + exceptionValue.value = redactPathLikeData(exceptionValue.value); + } - if (binary in versionCommands) { - try { - version = execSync(versionCommands[binary], { - encoding: 'utf8', - stdio: ['ignore', 'pipe', 'ignore'], - }).trim(); - } catch { - // Version command failed, but binary exists + const frames = exceptionValue.stacktrace?.frames ?? []; + for (const frame of frames) { + if (typeof frame.abs_path === 'string') { + frame.abs_path = redactPathLikeData(frame.abs_path); + } + if (typeof frame.filename === 'string') { + frame.filename = redactPathLikeData(frame.filename); + } } } - return { available: true, version }; + if (event.extra) { + for (const [key, value] of Object.entries(event.extra)) { + event.extra[key] = redactUnknown(value); + } + } + + return event; +} + +function redactLog(log: Sentry.Log): Sentry.Log | null { + if (!log) { + return null; + } + + if (typeof log.message === 'string') { + log.message = redactPathLikeData(log.message); + } + + if (log.attributes !== undefined) { + log.attributes = redactUnknown(log.attributes) as Record; + } + + return log; +} + +export function __redactEventForTests(event: Sentry.Event): Sentry.Event { + const clone = structuredClone(event); + return redactEvent(clone as Sentry.ErrorEvent) as Sentry.Event; +} + +export function __redactLogForTests(log: Sentry.Log): Sentry.Log | null { + const clone = structuredClone(log); + return redactLog(clone); +} + +function parseXcodeVersionOutput(output: string): { + version?: string; + buildVersion?: string; +} { + const versionMatch = output.match(XCODE_VERSION_PATTERN); + const buildMatch = output.match(XCODE_BUILD_PATTERN); + return { + version: versionMatch?.[1]?.trim(), + buildVersion: buildMatch?.[1]?.trim(), + }; +} + +export function __parseXcodeVersionForTests(output: string): { + version?: string; + buildVersion?: string; +} { + return parseXcodeVersionOutput(output); } let initialized = false; +let enriched = false; +let selfTestEmitted = false; +let pendingRuntimeContext: SentryRuntimeContext | null = null; function isSentryDisabled(): boolean { return ( - process.env.SENTRY_DISABLED === 'true' || process.env.XCODEBUILDMCP_SENTRY_DISABLED === 'true' + process.env.XCODEBUILDMCP_SENTRY_DISABLED === 'true' || process.env.SENTRY_DISABLED === 'true' ); } @@ -93,7 +164,152 @@ function isTestEnv(): boolean { return process.env.VITEST === 'true' || process.env.NODE_ENV === 'test'; } -export function initSentry(): void { +function isSentrySelfTestEnabled(): boolean { + const raw = process.env[SENTRY_SELF_TEST_ENV_VAR]?.trim().toLowerCase(); + return raw === '1' || raw === 'true' || raw === 'yes'; +} + +function emitSentrySelfTest(mode: SentryRuntimeMode | undefined): void { + if (!isSentrySelfTestEnabled() || selfTestEmitted) { + return; + } + + const marker = new Date().toISOString(); + const attributes: Record = { + source: 'xcodebuildmcp.sentry_selftest', + marker, + runtime_mode: mode ?? 'unknown', + pid: process.pid, + }; + + Sentry.logger.info('XcodeBuildMCP Sentry self-test log', attributes); + Sentry.startSpan( + { + name: 'XcodeBuildMCP Sentry self-test transaction', + op: 'xcodebuildmcp.sentry_selftest', + forceTransaction: true, + attributes, + }, + () => undefined, + ); + + selfTestEmitted = true; +} + +function boolToTag(value: boolean | undefined): string | undefined { + if (value === undefined) { + return undefined; + } + return String(value); +} + +function setTagIfDefined(key: string, value: string | undefined): void { + if (!value) { + return; + } + Sentry.setTag(key, value); +} + +function applyRuntimeContext(context: SentryRuntimeContext): void { + setTagIfDefined('runtime.mode', context.mode); + setTagIfDefined('xcode.available', boolToTag(context.xcodeAvailable)); + setTagIfDefined('config.disable_session_defaults', boolToTag(context.disableSessionDefaults)); + setTagIfDefined('config.disable_xcode_auto_sync', boolToTag(context.disableXcodeAutoSync)); + setTagIfDefined('config.incremental_builds_enabled', boolToTag(context.incrementalBuildsEnabled)); + setTagIfDefined('config.debug_enabled', boolToTag(context.debugEnabled)); + setTagIfDefined('config.ui_debugger_guard_mode', context.uiDebuggerGuardMode); + setTagIfDefined('config.xcode_ide_workflow_enabled', boolToTag(context.xcodeIdeWorkflowEnabled)); + setTagIfDefined('axe.available', boolToTag(context.axeAvailable)); + setTagIfDefined('axe.source', context.axeSource); + setTagIfDefined('axe.version', context.axeVersion); + setTagIfDefined('xcodemake.available', boolToTag(context.xcodemakeAvailable)); + setTagIfDefined('xcodemake.enabled', boolToTag(context.xcodemakeEnabled)); + setTagIfDefined('xcode.version', context.xcodeVersion); + setTagIfDefined('xcode.build_version', context.xcodeBuildVersion); + + if (context.enabledWorkflows) { + Sentry.setTag('config.workflow_count', String(context.enabledWorkflows.length)); + Sentry.setContext('xcodebuildmcp.runtime', { + enabledWorkflows: context.enabledWorkflows.join(','), + }); + } +} + +export function setSentryRuntimeContext(context: SentryRuntimeContext): void { + pendingRuntimeContext = context; + + if (!initialized || isSentryDisabled() || isTestEnv()) { + return; + } + + applyRuntimeContext(context); +} + +interface XcodeVersionMetadata { + version?: string; + buildVersion?: string; + developerDir?: string; + xcodebuildPath?: string; +} + +export async function getXcodeVersionMetadata( + runCommand: (command: string[]) => Promise<{ success: boolean; output: string }>, +): Promise { + const metadata: XcodeVersionMetadata = {}; + + try { + const result = await runCommand(['xcodebuild', '-version']); + if (result.success) { + const parsed = parseXcodeVersionOutput(result.output); + metadata.version = parsed.version; + metadata.buildVersion = parsed.buildVersion; + } + } catch { + // ignore + } + + try { + const result = await runCommand(['xcode-select', '-p']); + if (result.success) { + metadata.developerDir = result.output.trim(); + } + } catch { + // ignore + } + + try { + const result = await runCommand(['xcrun', '--find', 'xcodebuild']); + if (result.success) { + metadata.xcodebuildPath = result.output.trim(); + } + } catch { + // ignore + } + + return metadata; +} + +export async function getAxeVersionMetadata( + runCommand: (command: string[]) => Promise<{ success: boolean; output: string }>, + axePath: string | undefined, +): Promise { + if (!axePath) { + return undefined; + } + + try { + const result = await runCommand([axePath, '--version']); + if (!result.success) { + return undefined; + } + const versionLine = result.output.trim().split('\n')[0]?.trim(); + return versionLine || undefined; + } catch { + return undefined; + } +} + +export function initSentry(context?: Pick): void { if (initialized || isSentryDisabled() || isTestEnv()) { return; } @@ -103,41 +319,177 @@ export function initSentry(): void { Sentry.init({ dsn: process.env.SENTRY_DSN ?? - 'https://798607831167c7b9fe2f2912f5d3c665@o4509258288332800.ingest.de.sentry.io/4509258293837904', + 'https://326e2c19ee84f3b2c892207b5726cde0@o1.ingest.us.sentry.io/4510869777416192', - // Setting this option to true will send default PII data to Sentry - // For example, automatic IP address collection on events - sendDefaultPii: true, - - // Set release version to match application version + // Keep Sentry defaults as lean as possible for privacy: internal failures only. + sendDefaultPii: false, + tracesSampleRate: 1.0, + enableLogs: true, + _experiments: { + enableMetrics: true, + }, + maxBreadcrumbs: 0, + beforeBreadcrumb: () => null, + beforeSend: redactEvent, + beforeSendLog: redactLog, + serverName: '', release: `xcodebuildmcp@${version}`, - - // Always report under production environment environment: 'production', - - // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring - // We recommend adjusting this value in production - tracesSampleRate: 1.0, }); - const axeAvailable = checkBinaryAvailability('axe'); - const miseAvailable = checkBinaryAvailability('mise'); - const envVars = getEnvironmentVariables(); - const xcodeInfo = getXcodeInfo(); - - // Add additional context that might be helpful for debugging - const tags: Record = { - nodeVersion: process.version, - platform: process.platform, - arch: process.arch, - axeAvailable: axeAvailable.available ? 'true' : 'false', - axeVersion: axeAvailable.version ?? 'Unknown', - miseAvailable: miseAvailable.available ? 'true' : 'false', - miseVersion: miseAvailable.version ?? 'Unknown', - ...Object.fromEntries(Object.entries(envVars).map(([k, v]) => [`env_${k}`, v ?? ''])), - xcodeVersion: xcodeInfo.version ?? 'Unknown', - xcodePath: xcodeInfo.path ?? 'Unknown', + if (context?.mode) { + Sentry.setTag('runtime.mode', context.mode); + } + + emitSentrySelfTest(context?.mode); +} + +export function enrichSentryContext(): void { + if (!initialized || enriched || isSentryDisabled() || isTestEnv()) { + return; + } + + enriched = true; + + if (pendingRuntimeContext) { + applyRuntimeContext(pendingRuntimeContext); + emitSentrySelfTest(pendingRuntimeContext.mode); + return; + } + + emitSentrySelfTest(undefined); +} + +export async function flushAndCloseSentry(timeoutMs = 2000): Promise { + if (!initialized || isSentryDisabled() || isTestEnv()) { + return; + } + + try { + await Sentry.close(timeoutMs); + } catch { + // Best effort during shutdown. + } +} + +interface ToolInvocationMetric { + toolName: string; + runtime: SentryToolRuntime; + transport: SentryToolTransport; + outcome: SentryToolInvocationOutcome; + durationMs: number; +} + +interface InternalErrorMetric { + component: string; + runtime: SentryToolRuntime; + errorKind: string; +} + +type DaemonGaugeMetricName = 'inflight_requests' | 'active_sessions' | 'idle_timeout_ms'; + +function sanitizeTagValue(value: string): string { + const trimmed = value.trim().toLowerCase(); + if (!trimmed) { + return 'unknown'; + } + return trimmed.replace(/[^a-z0-9._-]/g, '_').slice(0, 64); +} + +function shouldEmitMetrics(): boolean { + return initialized && !isSentryDisabled() && !isTestEnv(); +} + +export function recordToolInvocationMetric(metric: ToolInvocationMetric): void { + if (!shouldEmitMetrics()) { + return; + } + + const tags = { + tool_name: sanitizeTagValue(metric.toolName), + runtime: metric.runtime, + transport: metric.transport, + outcome: metric.outcome, }; - Sentry.setTags(tags); + try { + Sentry.metrics.count('xcodebuildmcp.tool.invocation.count', 1, { attributes: tags }); + Sentry.metrics.distribution( + 'xcodebuildmcp.tool.invocation.duration_ms', + Math.max(0, metric.durationMs), + { attributes: tags }, + ); + } catch { + // Metrics are best effort and must never affect tool execution. + } +} + +export function recordInternalErrorMetric(metric: InternalErrorMetric): void { + if (!shouldEmitMetrics()) { + return; + } + + try { + Sentry.metrics.count('xcodebuildmcp.internal_error.count', 1, { + attributes: { + component: sanitizeTagValue(metric.component), + runtime: metric.runtime, + error_kind: sanitizeTagValue(metric.errorKind), + }, + }); + } catch { + // Metrics are best effort and must never affect runtime behavior. + } +} + +export function recordDaemonLifecycleMetric(event: SentryDaemonLifecycleEvent): void { + if (!shouldEmitMetrics()) { + return; + } + + try { + Sentry.metrics.count(`xcodebuildmcp.daemon.${event}.count`, 1, { + attributes: { + runtime: 'daemon', + }, + }); + } catch { + // Metrics are best effort and must never affect runtime behavior. + } +} + +export function recordBootstrapDurationMetric( + runtime: SentryRuntimeMode, + durationMs: number, +): void { + if (!shouldEmitMetrics()) { + return; + } + + try { + Sentry.metrics.distribution('xcodebuildmcp.bootstrap.duration_ms', Math.max(0, durationMs), { + attributes: { + runtime, + }, + }); + } catch { + // Metrics are best effort and must never affect runtime behavior. + } +} + +export function recordDaemonGaugeMetric(metricName: DaemonGaugeMetricName, value: number): void { + if (!shouldEmitMetrics()) { + return; + } + + const normalizedValue = Number.isFinite(value) ? Math.max(0, value) : 0; + try { + Sentry.metrics.gauge(`xcodebuildmcp.daemon.${metricName}`, normalizedValue, { + attributes: { + runtime: 'daemon', + }, + }); + } catch { + // Metrics are best effort and must never affect runtime behavior. + } } diff --git a/src/utils/session-defaults-profile.ts b/src/utils/session-defaults-profile.ts new file mode 100644 index 00000000..15e4117a --- /dev/null +++ b/src/utils/session-defaults-profile.ts @@ -0,0 +1,12 @@ +export function normalizeSessionDefaultsProfileName(profile?: string | null): string | null { + if (profile == null) { + return null; + } + + const trimmed = profile.trim(); + if (trimmed.length === 0) { + throw new Error('Profile name cannot be empty.'); + } + + return trimmed; +} diff --git a/src/utils/session-defaults-schema.ts b/src/utils/session-defaults-schema.ts new file mode 100644 index 00000000..0383e86e --- /dev/null +++ b/src/utils/session-defaults-schema.ts @@ -0,0 +1,57 @@ +import * as z from 'zod'; + +export const sessionDefaultKeys = [ + 'projectPath', + 'workspacePath', + 'scheme', + 'configuration', + 'simulatorName', + 'simulatorId', + 'simulatorPlatform', + 'deviceId', + 'useLatestOS', + 'arch', + 'suppressWarnings', + 'derivedDataPath', + 'preferXcodebuild', + 'platform', + 'bundleId', +] as const; + +export type SessionDefaultKey = (typeof sessionDefaultKeys)[number]; + +export const sessionDefaultsSchema = z.object({ + projectPath: z.string().optional().describe('xcodeproj path (xor workspacePath)'), + workspacePath: z.string().optional().describe('xcworkspace path (xor projectPath)'), + scheme: z.string().optional(), + configuration: z + .string() + .optional() + .describe("Build configuration for Xcode and SwiftPM tools (e.g. 'Debug' or 'Release')."), + simulatorName: z.string().optional(), + simulatorId: z.string().optional(), + simulatorPlatform: z + .enum(['iOS Simulator', 'watchOS Simulator', 'tvOS Simulator', 'visionOS Simulator']) + .optional() + .describe('Cached inferred simulator platform.'), + deviceId: z.string().optional(), + useLatestOS: z.boolean().optional(), + arch: z.enum(['arm64', 'x86_64']).optional(), + suppressWarnings: z.boolean().optional(), + derivedDataPath: z + .string() + .optional() + .describe('Default DerivedData path for Xcode build/test/clean tools.'), + preferXcodebuild: z + .boolean() + .optional() + .describe('Prefer xcodebuild over incremental builds for Xcode build/test/clean tools.'), + platform: z + .string() + .optional() + .describe('Default device platform for device tools (e.g. iOS, watchOS).'), + bundleId: z + .string() + .optional() + .describe('Default bundle ID for launch/stop/log tools when working on a single app.'), +}); diff --git a/src/utils/session-status.ts b/src/utils/session-status.ts new file mode 100644 index 00000000..55bd2ab4 --- /dev/null +++ b/src/utils/session-status.ts @@ -0,0 +1,37 @@ +import { getDefaultDebuggerManager } from './debugger/index.ts'; +import { listActiveSimulatorLogSessionIds } from './log-capture/index.ts'; +import { activeDeviceLogSessions } from './log-capture/device-log-sessions.ts'; + +export type SessionRuntimeStatusSnapshot = { + logging: { + simulator: { activeSessionIds: string[] }; + device: { activeSessionIds: string[] }; + }; + debug: { + currentSessionId: string | null; + sessionIds: string[]; + }; +}; + +export function getSessionRuntimeStatusSnapshot(): SessionRuntimeStatusSnapshot { + const debuggerManager = getDefaultDebuggerManager(); + const sessionIds = debuggerManager + .listSessions() + .map((session) => session.id) + .sort(); + + return { + logging: { + simulator: { + activeSessionIds: listActiveSimulatorLogSessionIds(), + }, + device: { + activeSessionIds: Array.from(activeDeviceLogSessions.keys()).sort(), + }, + }, + debug: { + currentSessionId: debuggerManager.getCurrentSessionId(), + sessionIds, + }, + }; +} diff --git a/src/utils/session-store.ts b/src/utils/session-store.ts index e61c691b..1185bd6a 100644 --- a/src/utils/session-store.ts +++ b/src/utils/session-store.ts @@ -7,41 +7,153 @@ export type SessionDefaults = { configuration?: string; simulatorName?: string; simulatorId?: string; + simulatorPlatform?: + | 'iOS Simulator' + | 'watchOS Simulator' + | 'tvOS Simulator' + | 'visionOS Simulator'; deviceId?: string; useLatestOS?: boolean; arch?: 'arm64' | 'x86_64'; suppressWarnings?: boolean; + derivedDataPath?: string; + preferXcodebuild?: boolean; + platform?: string; + bundleId?: string; }; class SessionStore { - private defaults: SessionDefaults = {}; + private globalDefaults: SessionDefaults = {}; + private profiles: Record = {}; + private activeProfile: string | null = null; + private revision = 0; + + private getProfileLabel(profile: string | null): string { + return profile ?? 'global'; + } + + private clearAll(): void { + this.globalDefaults = {}; + this.profiles = {}; + this.activeProfile = null; + this.revision += 1; + log('info', '[Session] All defaults cleared'); + } + + private clearAllForProfile(profile: string | null): void { + if (profile === null) { + this.globalDefaults = {}; + this.revision += 1; + log('info', '[Session] All defaults cleared (global)'); + return; + } + + delete this.profiles[profile]; + this.revision += 1; + log('info', `[Session] All defaults cleared (${profile})`); + } + + private setDefaultsForResolvedProfile(profile: string | null, defaults: SessionDefaults): void { + if (profile === null) { + this.globalDefaults = defaults; + return; + } + this.profiles[profile] = defaults; + } setDefaults(partial: Partial): void { - this.defaults = { ...this.defaults, ...partial }; - log('info', `[Session] Defaults updated: ${Object.keys(partial).join(', ')}`); + this.setDefaultsForProfile(this.activeProfile, partial); + } + + setDefaultsForProfile(profile: string | null, partial: Partial): void { + const previous = this.getAllForProfile(profile); + const next = { ...previous, ...partial }; + this.setDefaultsForResolvedProfile(profile, next); + this.revision += 1; + const profileLabel = this.getProfileLabel(profile); + log('info', `[Session] Defaults updated (${profileLabel}): ${Object.keys(partial).join(', ')}`); } clear(keys?: (keyof SessionDefaults)[]): void { if (keys == null) { - this.defaults = {}; - log('info', '[Session] All defaults cleared'); + this.clearAll(); + return; + } + + this.clearForProfile(this.activeProfile, keys); + } + + clearForProfile(profile: string | null, keys?: (keyof SessionDefaults)[]): void { + if (keys == null) { + this.clearAllForProfile(profile); return; } + if (keys.length === 0) { // No-op when an empty array is provided (e.g., empty UI selection) log('info', '[Session] No keys provided to clear; no changes made'); return; } - for (const k of keys) delete this.defaults[k]; - log('info', `[Session] Defaults cleared: ${keys.join(', ')}`); + + const next = this.getAllForProfile(profile); + for (const k of keys) delete next[k]; + + this.setDefaultsForResolvedProfile(profile, next); + this.revision += 1; + const profileLabel = this.getProfileLabel(profile); + log('info', `[Session] Defaults cleared (${profileLabel}): ${keys.join(', ')}`); } get(key: K): SessionDefaults[K] { - return this.defaults[key]; + return this.getAll()[key]; } getAll(): SessionDefaults { - return { ...this.defaults }; + return this.getAllForProfile(this.activeProfile); + } + + getAllForProfile(profile: string | null): SessionDefaults { + if (profile === null) { + return { ...this.globalDefaults }; + } + return { ...(this.profiles[profile] ?? {}) }; + } + + listProfiles(): string[] { + return Object.keys(this.profiles).sort((a, b) => a.localeCompare(b)); + } + + getActiveProfile(): string | null { + return this.activeProfile; + } + + setActiveProfile(profile: string | null): void { + this.activeProfile = profile; + this.revision += 1; + if (profile != null && this.profiles[profile] == null) { + this.profiles[profile] = {}; + } + log('info', `[Session] Active defaults profile: ${profile ?? 'global'}`); + } + + getRevision(): number { + return this.revision; + } + + setDefaultsIfRevision(partial: Partial, expectedRevision: number): boolean { + return this.setDefaultsIfRevisionForProfile(this.activeProfile, partial, expectedRevision); + } + + setDefaultsIfRevisionForProfile( + profile: string | null, + partial: Partial, + expectedRevision: number, + ): boolean { + if (this.revision !== expectedRevision) { + return false; + } + this.setDefaultsForProfile(profile, partial); + return true; } } diff --git a/src/utils/simulator-defaults-refresh.ts b/src/utils/simulator-defaults-refresh.ts new file mode 100644 index 00000000..1aa8b02c --- /dev/null +++ b/src/utils/simulator-defaults-refresh.ts @@ -0,0 +1,117 @@ +import { persistSessionDefaultsPatch } from './config-store.ts'; +import { getDefaultCommandExecutor, type CommandExecutor } from './execution/index.ts'; +import { inferPlatform } from './infer-platform.ts'; +import { log } from './logger.ts'; +import { resolveSimulatorIdToName, resolveSimulatorNameToId } from './simulator-resolver.ts'; +import { sessionStore, type SessionDefaults } from './session-store.ts'; + +type RefreshReason = 'startup-hydration' | 'session-set-defaults'; + +export interface ScheduleSimulatorDefaultsRefreshOptions { + executor?: CommandExecutor; + expectedRevision: number; + reason: RefreshReason; + profile: string | null; + persist?: boolean; + simulatorId?: string; + simulatorName?: string; + recomputePlatform?: boolean; +} + +function shouldSkipBackgroundRefresh(): boolean { + return process.env.NODE_ENV === 'test' || process.env.VITEST === 'true'; +} + +export function scheduleSimulatorDefaultsRefresh( + options: ScheduleSimulatorDefaultsRefreshOptions, +): boolean { + const hasSelector = options.simulatorId != null || options.simulatorName != null; + if (!hasSelector) { + return false; + } + + if (shouldSkipBackgroundRefresh()) { + return false; + } + + setTimeout(() => { + void refreshSimulatorDefaults(options); + }, 0); + + return true; +} + +async function refreshSimulatorDefaults( + options: ScheduleSimulatorDefaultsRefreshOptions, +): Promise { + let simulatorId = options.simulatorId; + let simulatorName = options.simulatorName; + const patch: Partial = {}; + const executor = options.executor ?? getDefaultCommandExecutor(); + + try { + if (!simulatorId && simulatorName) { + const resolution = await resolveSimulatorNameToId(executor, simulatorName); + if (resolution.success) { + simulatorId = resolution.simulatorId; + patch.simulatorId = resolution.simulatorId; + } + } + + if (!simulatorName && simulatorId) { + const resolution = await resolveSimulatorIdToName(executor, simulatorId); + if (resolution.success) { + simulatorName = resolution.simulatorName; + patch.simulatorName = resolution.simulatorName; + } + } + + const shouldRecomputePlatform = options.recomputePlatform ?? true; + if (shouldRecomputePlatform && (simulatorId || simulatorName)) { + const inferred = await inferPlatform( + { + simulatorId, + simulatorName, + sessionDefaults: { + ...sessionStore.getAllForProfile(options.profile), + ...patch, + simulatorId, + simulatorName, + simulatorPlatform: undefined, + }, + }, + executor, + ); + + if (inferred.source !== 'default') { + patch.simulatorPlatform = inferred.platform; + } + } + + if (Object.keys(patch).length === 0) { + return; + } + + const applied = sessionStore.setDefaultsIfRevisionForProfile( + options.profile, + patch, + options.expectedRevision, + ); + if (!applied) { + log( + 'info', + `[Session] Skipped background simulator defaults refresh (${options.reason}) because defaults changed during refresh.`, + ); + return; + } + + if (options.persist) { + await persistSessionDefaultsPatch({ patch, profile: options.profile }); + } + } catch (error) { + log( + 'warning', + `[Session] Background simulator defaults refresh failed (${options.reason}): ${String(error)}`, + ); + } +} diff --git a/src/utils/simulator-resolver.ts b/src/utils/simulator-resolver.ts new file mode 100644 index 00000000..e0e87f57 --- /dev/null +++ b/src/utils/simulator-resolver.ts @@ -0,0 +1,154 @@ +/** + * Shared utility for resolving simulator names to UUIDs. + * Centralizes the lookup logic used across multiple tools. + */ + +import type { CommandExecutor } from './execution/index.ts'; +import { log } from './logger.ts'; + +export type SimulatorResolutionResult = + | { success: true; simulatorId: string; simulatorName: string } + | { success: false; error: string }; + +/** + * Resolves a simulator name to its UUID by querying simctl. + * + * @param executor - Command executor for running simctl + * @param simulatorName - The human-readable simulator name (e.g., "iPhone 16") + * @returns Resolution result with simulatorId on success, or error message on failure + */ +export async function resolveSimulatorNameToId( + executor: CommandExecutor, + simulatorName: string, +): Promise { + log('info', `Looking up simulator by name: ${simulatorName}`); + + const result = await executor( + ['xcrun', 'simctl', 'list', 'devices', 'available', '--json'], + 'List Simulators', + false, + ); + + if (!result.success) { + return { + success: false, + error: `Failed to list simulators: ${result.error}`, + }; + } + + let simulatorsData: { devices: Record> }; + try { + simulatorsData = JSON.parse(result.output) as typeof simulatorsData; + } catch (parseError) { + return { + success: false, + error: `Failed to parse simulator list: ${parseError}`, + }; + } + + for (const runtime in simulatorsData.devices) { + const devices = simulatorsData.devices[runtime]; + const simulator = devices.find((device) => device.name === simulatorName); + if (simulator) { + log('info', `Resolved simulator "${simulatorName}" to UUID: ${simulator.udid}`); + return { + success: true, + simulatorId: simulator.udid, + simulatorName: simulator.name, + }; + } + } + + return { + success: false, + error: `Simulator named "${simulatorName}" not found. Use list_sims to see available simulators.`, + }; +} + +/** + * Resolves a simulator UUID to its name by querying simctl. + * + * @param executor - Command executor for running simctl + * @param simulatorId - The simulator UUID + * @returns Resolution result with simulatorName on success, or error message on failure + */ +export async function resolveSimulatorIdToName( + executor: CommandExecutor, + simulatorId: string, +): Promise { + log('info', `Looking up simulator by UUID: ${simulatorId}`); + + const result = await executor( + ['xcrun', 'simctl', 'list', 'devices', 'available', '--json'], + 'List Simulators', + false, + ); + + if (!result.success) { + return { + success: false, + error: `Failed to list simulators: ${result.error}`, + }; + } + + let simulatorsData: { devices: Record> }; + try { + simulatorsData = JSON.parse(result.output) as typeof simulatorsData; + } catch (parseError) { + return { + success: false, + error: `Failed to parse simulator list: ${parseError}`, + }; + } + + for (const runtime in simulatorsData.devices) { + const devices = simulatorsData.devices[runtime]; + const simulator = devices.find((device) => device.udid === simulatorId); + if (simulator) { + log('info', `Resolved simulator UUID "${simulatorId}" to name: ${simulator.name}`); + return { + success: true, + simulatorId: simulator.udid, + simulatorName: simulator.name, + }; + } + } + + return { + success: false, + error: `Simulator UUID "${simulatorId}" not found. Use list_sims to see available simulators.`, + }; +} + +/** + * Helper to resolve simulatorId from either simulatorId or simulatorName. + * If simulatorId is provided, returns it directly. + * If only simulatorName is provided, resolves it to simulatorId. + * + * @param executor - Command executor for running simctl + * @param simulatorId - Optional simulator UUID + * @param simulatorName - Optional simulator name + * @returns Resolution result with simulatorId, or error if neither provided or lookup fails + */ +export async function resolveSimulatorIdOrName( + executor: CommandExecutor, + simulatorId: string | undefined, + simulatorName: string | undefined, +): Promise { + if (simulatorId) { + return { + success: true, + simulatorId, + simulatorName: simulatorName ?? simulatorId, + }; + } + + if (simulatorName) { + return resolveSimulatorNameToId(executor, simulatorName); + } + + return { + success: false, + error: 'Either simulatorId or simulatorName must be provided.', + }; +} diff --git a/src/utils/simulator-utils.ts b/src/utils/simulator-utils.ts index b07d7a9a..547cebf2 100644 --- a/src/utils/simulator-utils.ts +++ b/src/utils/simulator-utils.ts @@ -3,7 +3,7 @@ */ import type { CommandExecutor } from './execution/index.ts'; -import { ToolResponse } from '../types/common.ts'; +import type { ToolResponse } from '../types/common.ts'; import { log } from './logging/index.ts'; import { createErrorResponse } from './responses/index.ts'; diff --git a/src/utils/template-manager.ts b/src/utils/template-manager.ts index f5e3e128..55302ea8 100644 --- a/src/utils/template-manager.ts +++ b/src/utils/template-manager.ts @@ -3,14 +3,15 @@ import { tmpdir } from 'os'; import { randomUUID } from 'crypto'; import { log } from './logger.ts'; import { iOSTemplateVersion, macOSTemplateVersion } from '../version.ts'; -import { CommandExecutor } from './command.ts'; -import { FileSystemExecutor } from './FileSystemExecutor.ts'; +import type { CommandExecutor } from './command.ts'; +import type { FileSystemExecutor } from './FileSystemExecutor.ts'; +import { getConfig } from './config-store.ts'; /** * Template manager for downloading and managing project templates */ export class TemplateManager { - private static readonly GITHUB_ORG = 'cameroncooke'; + private static readonly GITHUB_ORG = 'getsentry'; private static readonly IOS_TEMPLATE_REPO = 'XcodeBuildMCP-iOS-Template'; private static readonly MACOS_TEMPLATE_REPO = 'XcodeBuildMCP-macOS-Template'; @@ -23,16 +24,19 @@ export class TemplateManager { commandExecutor: CommandExecutor, fileSystemExecutor: FileSystemExecutor, ): Promise { - // Check for local override - const envVar = - platform === 'iOS' ? 'XCODEBUILDMCP_IOS_TEMPLATE_PATH' : 'XCODEBUILDMCP_MACOS_TEMPLATE_PATH'; - - const localPath = process.env[envVar]; - log('debug', `[TemplateManager] Checking env var '${envVar}'. Value: '${localPath}'`); + const config = getConfig(); + const localPath = platform === 'iOS' ? config.iosTemplatePath : config.macosTemplatePath; + log( + 'debug', + `[TemplateManager] Checking config override for ${platform} template. Value: '${localPath}'`, + ); if (localPath) { const pathExists = fileSystemExecutor.existsSync(localPath); - log('debug', `[TemplateManager] Env var set. Path '${localPath}' exists? ${pathExists}`); + log( + 'debug', + `[TemplateManager] Config override set. Path '${localPath}' exists? ${pathExists}`, + ); if (pathExists) { const templateSubdir = join(localPath, 'template'); const subdirExists = fileSystemExecutor.existsSync(templateSubdir); @@ -49,7 +53,7 @@ export class TemplateManager { } } - log('debug', '[TemplateManager] Env var not set or path invalid, proceeding to download.'); + log('debug', '[TemplateManager] No valid config override, proceeding to download.'); // Download from GitHub release return await this.downloadTemplate(platform, commandExecutor, fileSystemExecutor); } @@ -63,13 +67,13 @@ export class TemplateManager { fileSystemExecutor: FileSystemExecutor, ): Promise { const repo = platform === 'iOS' ? this.IOS_TEMPLATE_REPO : this.MACOS_TEMPLATE_REPO; - const defaultVersion = platform === 'iOS' ? iOSTemplateVersion : macOSTemplateVersion; - const envVarName = - platform === 'iOS' - ? 'XCODEBUILD_MCP_IOS_TEMPLATE_VERSION' - : 'XCODEBUILD_MCP_MACOS_TEMPLATE_VERSION'; + const defaultVersion = + platform === 'iOS' ? String(iOSTemplateVersion) : String(macOSTemplateVersion); + const config = getConfig(); const version = String( - process.env[envVarName] ?? process.env.XCODEBUILD_MCP_TEMPLATE_VERSION ?? defaultVersion, + platform === 'iOS' + ? (config.iosTemplateVersion ?? defaultVersion) + : (config.macosTemplateVersion ?? defaultVersion), ); // Create temp directory for download @@ -83,7 +87,6 @@ export class TemplateManager { log('info', `Downloading ${platform} template ${version} from GitHub...`); log('info', `Download URL: ${downloadUrl}`); - // Download the release artifact const curlResult = await commandExecutor( ['curl', '-L', '-f', '-o', zipPath, downloadUrl], 'Download Template', diff --git a/src/utils/test-common.ts b/src/utils/test-common.ts index cc9333e6..8703f134 100644 --- a/src/utils/test-common.ts +++ b/src/utils/test-common.ts @@ -18,12 +18,13 @@ import { mkdtemp, rm } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; import { log } from './logger.ts'; -import { XcodePlatform } from './xcode.ts'; +import type { XcodePlatform } from './xcode.ts'; import { executeXcodeBuildCommand } from './build/index.ts'; import { createTextResponse, consolidateContentForClaudeCode } from './validation.ts'; import { normalizeTestRunnerEnv } from './environment.ts'; -import { ToolResponse } from '../types/common.ts'; -import { CommandExecutor, CommandExecOptions, getDefaultCommandExecutor } from './command.ts'; +import type { ToolResponse } from '../types/common.ts'; +import type { CommandExecutor, CommandExecOptions } from './command.ts'; +import { getDefaultCommandExecutor } from './command.ts'; /** * Type definition for test summary structure from xcresulttool diff --git a/src/utils/tool-registry.ts b/src/utils/tool-registry.ts index 188d70a7..c5cdcffa 100644 --- a/src/utils/tool-registry.ts +++ b/src/utils/tool-registry.ts @@ -1,54 +1,240 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { loadWorkflowGroups } from '../core/plugin-registry.ts'; -import { ToolResponse } from '../types/common.ts'; +import { type RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { server } from '../server/server-state.ts'; +import type { ToolResponse } from '../types/common.ts'; +import type { ToolCatalog, ToolDefinition } from '../runtime/types.ts'; import { log } from './logger.ts'; -import { recordRuntimeRegistration } from './runtime-registry.ts'; -import { resolveSelectedWorkflows } from './workflow-selection.ts'; +import { processToolResponse } from './responses/index.ts'; +import { loadManifest } from '../core/manifest/load-manifest.ts'; +import { importToolModule } from '../core/manifest/import-tool-module.ts'; +import { getEffectiveCliName } from '../core/manifest/schema.ts'; +import { createToolCatalog } from '../runtime/tool-catalog.ts'; +import { postProcessToolResponse } from '../runtime/tool-invoker.ts'; +import type { PredicateContext } from '../visibility/predicate-types.ts'; +import { selectWorkflowsForMcp, isToolExposedForRuntime } from '../visibility/exposure.ts'; +import { getConfig } from './config-store.ts'; +import { recordInternalErrorMetric, recordToolInvocationMetric } from './sentry.ts'; + +export interface RuntimeToolInfo { + enabledWorkflows: string[]; + registeredToolCount: number; +} + +const registryState: { + tools: Map; + enabledWorkflows: Set; + /** Current MCP predicate context (stored for use by manage_workflows) */ + currentContext: PredicateContext | null; + /** Catalog of currently registered MCP tools for next-step template resolution */ + catalog: ToolCatalog | null; +} = { + tools: new Map(), + enabledWorkflows: new Set(), + currentContext: null, + catalog: null, +}; + +export function getRuntimeRegistration(): RuntimeToolInfo | null { + if (registryState.tools.size === 0 && registryState.enabledWorkflows.size === 0) { + return null; + } + return { + enabledWorkflows: [...registryState.enabledWorkflows], + registeredToolCount: registryState.tools.size, + }; +} + +export function getRegisteredWorkflows(): string[] { + return [...registryState.enabledWorkflows]; +} + +function defaultPredicateContext(): PredicateContext { + return { + runtime: 'mcp', + config: getConfig(), + runningUnderXcode: false, + }; +} /** - * Register workflows (selected list or all when omitted) + * Get the current MCP predicate context. + * Returns the context used for the most recent workflow registration, + * or a default context if not yet initialized. */ -export async function registerWorkflows( - server: McpServer, - workflowNames: string[] = [], -): Promise { - const workflowGroups = await loadWorkflowGroups(); - const selection = resolveSelectedWorkflows(workflowGroups, workflowNames); - let registeredCount = 0; - const registeredTools = new Set(); - const registeredWorkflows = new Set(); - - for (const workflow of selection.selectedWorkflows) { - registeredWorkflows.add(workflow.directoryName); - for (const tool of workflow.tools) { - if (registeredTools.has(tool.name)) { +export function getMcpPredicateContext(): PredicateContext { + return registryState.currentContext ?? defaultPredicateContext(); +} + +/** + * Apply workflow selection using the manifest system. + */ +export async function applyWorkflowSelectionFromManifest( + requestedWorkflows: string[] | undefined, + ctx: PredicateContext, +): Promise { + if (!server) { + throw new Error('Tool registry has not been initialized.'); + } + + // Store the context for later use (e.g., by manage_workflows) + registryState.currentContext = ctx; + + const manifest = loadManifest(); + const allWorkflows = Array.from(manifest.workflows.values()); + + // Select workflows using manifest-driven rules + const selectedWorkflows = selectWorkflowsForMcp(allWorkflows, requestedWorkflows, ctx); + + const desiredToolNames = new Set(); + const desiredWorkflows = new Set(); + const catalogTools: ToolDefinition[] = []; + const moduleCache = new Map>>(); + + for (const workflow of selectedWorkflows) { + desiredWorkflows.add(workflow.id); + + for (const toolId of workflow.tools) { + const toolManifest = manifest.tools.get(toolId); + if (!toolManifest) continue; + + // Check tool visibility using predicates + if (!isToolExposedForRuntime(toolManifest, ctx)) { continue; } - server.registerTool( - tool.name, - { - description: tool.description ?? '', - inputSchema: tool.schema, - annotations: tool.annotations, - }, - (args: unknown): Promise => tool.handler(args as Record), - ); - registeredTools.add(tool.name); - registeredCount += 1; + + const toolName = toolManifest.names.mcp; + desiredToolNames.add(toolName); + + let toolModule = moduleCache.get(toolId); + if (!toolModule) { + try { + toolModule = await importToolModule(toolManifest.module); + moduleCache.set(toolId, toolModule); + } catch (err) { + log('warning', `Failed to import tool module ${toolManifest.module}: ${err}`); + continue; + } + } + + catalogTools.push({ + id: toolManifest.id, + cliName: getEffectiveCliName(toolManifest), + mcpName: toolName, + workflow: workflow.id, + description: toolManifest.description, + annotations: toolManifest.annotations, + nextStepTemplates: toolManifest.nextSteps, + mcpSchema: toolModule.schema, + cliSchema: toolModule.schema, + stateful: toolManifest.routing?.stateful ?? false, + handler: toolModule.handler as ToolDefinition['handler'], + }); + + if (!registryState.tools.has(toolName)) { + const registeredTool = server.registerTool( + toolName, + { + description: toolManifest.description ?? '', + inputSchema: toolModule.schema, + annotations: toolManifest.annotations, + }, + async (args: unknown): Promise => { + const startedAt = Date.now(); + try { + const response = await toolModule.handler(args as Record); + const catalog = registryState.catalog; + const catalogTool = catalog?.getByMcpName(toolName); + const postProcessedResponse = + catalog && catalogTool + ? postProcessToolResponse({ + tool: catalogTool, + response: response as ToolResponse, + catalog, + runtime: 'mcp', + }) + : (response as ToolResponse); + + recordToolInvocationMetric({ + toolName, + runtime: 'mcp', + transport: 'direct', + outcome: 'completed', + durationMs: Date.now() - startedAt, + }); + + return processToolResponse(postProcessedResponse, 'mcp', 'normal'); + } catch (error) { + recordInternalErrorMetric({ + component: 'mcp-tool-registry', + runtime: 'mcp', + errorKind: error instanceof Error ? error.name || 'Error' : typeof error, + }); + recordToolInvocationMetric({ + toolName, + runtime: 'mcp', + transport: 'direct', + outcome: 'infra_error', + durationMs: Date.now() - startedAt, + }); + throw error; + } + }, + ); + registryState.tools.set(toolName, registeredTool); + } } } - recordRuntimeRegistration({ - enabledWorkflows: [...registeredWorkflows], - enabledTools: [...registeredTools], - }); - - if (selection.selectedNames) { - log( - 'info', - `✅ Registered ${registeredCount} tools from workflows: ${selection.selectedNames.join(', ')}`, - ); - } else { - log('info', `✅ Registered ${registeredCount} tools in static mode.`); + registryState.catalog = createToolCatalog(catalogTools); + + // Unregister tools no longer in selection + for (const [toolName, registeredTool] of registryState.tools.entries()) { + if (!desiredToolNames.has(toolName)) { + registeredTool.remove(); + registryState.tools.delete(toolName); + } + } + + registryState.enabledWorkflows = desiredWorkflows; + + const workflowLabel = selectedWorkflows.map((w) => w.id).join(', '); + log('info', `Registered ${desiredToolNames.size} tools from workflows: ${workflowLabel}`); + + return { + enabledWorkflows: [...registryState.enabledWorkflows], + registeredToolCount: registryState.tools.size, + }; +} + +/** + * Register workflows using manifest system. + */ +export async function registerWorkflowsFromManifest( + workflowNames?: string[], + ctx?: PredicateContext, +): Promise { + await applyWorkflowSelectionFromManifest(workflowNames, ctx ?? defaultPredicateContext()); +} + +/** + * Update workflows using manifest system. + */ +export async function updateWorkflowsFromManifest( + workflowNames?: string[], + ctx?: PredicateContext, +): Promise { + await registerWorkflowsFromManifest(workflowNames, ctx); +} + +export function __resetToolRegistryForTests(): void { + for (const tool of registryState.tools.values()) { + try { + tool.remove(); + } catch { + // Safe to ignore: server may already be closed during cleanup + } } + registryState.tools.clear(); + registryState.enabledWorkflows.clear(); + registryState.currentContext = null; + registryState.catalog = null; } diff --git a/src/utils/typed-tool-factory.ts b/src/utils/typed-tool-factory.ts index 37a72f44..f6ddfe95 100644 --- a/src/utils/typed-tool-factory.ts +++ b/src/utils/typed-tool-factory.ts @@ -10,37 +10,22 @@ */ import * as z from 'zod'; -import { ToolResponse } from '../types/common.ts'; +import type { ToolResponse } from '../types/common.ts'; import type { CommandExecutor } from './execution/index.ts'; import { createErrorResponse } from './responses/index.ts'; import { sessionStore, type SessionDefaults } from './session-store.ts'; -import { isSessionDefaultsSchemaOptOutEnabled } from './environment.ts'; +import { isSessionDefaultsOptOutEnabled } from './environment.ts'; -/** - * Creates a type-safe tool handler that validates parameters at runtime - * before passing them to the typed logic function. - * - * This is the ONLY safe way to cross the type boundary from the generic - * MCP handler signature to our typed domain logic. - * - * @param schema - Zod schema for parameter validation - * @param logicFunction - The typed logic function to execute - * @param getExecutor - Function to get the command executor (must be provided) - * @returns A handler function compatible with MCP SDK requirements - */ -export function createTypedTool( +function createValidatedHandler( schema: z.ZodType, - logicFunction: (params: TParams, executor: CommandExecutor) => Promise, - getExecutor: () => CommandExecutor, -) { + logicFunction: (params: TParams, context: TContext) => Promise, + getContext: () => TContext, +): (args: Record) => Promise { return async (args: Record): Promise => { try { - // Runtime validation - the ONLY safe way to cross the type boundary - // This provides both compile-time and runtime type safety const validatedParams = schema.parse(args); - // Now we have guaranteed type safety - no assertions needed! - return await logicFunction(validatedParams, getExecutor()); + return await logicFunction(validatedParams, getContext()); } catch (error) { if (error instanceof z.ZodError) { const details = `Invalid parameters:\n${formatZodIssues(error)}`; @@ -53,6 +38,31 @@ export function createTypedTool( }; } +/** + * Creates a type-safe tool handler that validates parameters at runtime + * before passing them to the typed logic function. + * + * @param schema - Zod schema for parameter validation + * @param logicFunction - The typed logic function to execute + * @param getExecutor - Function to get the command executor (must be provided) + * @returns A handler function compatible with MCP SDK requirements + */ +export function createTypedTool( + schema: z.ZodType, + logicFunction: (params: TParams, executor: CommandExecutor) => Promise, + getExecutor: () => CommandExecutor, +): (args: Record) => Promise { + return createValidatedHandler(schema, logicFunction, getExecutor); +} + +export function createTypedToolWithContext( + schema: z.ZodType, + logicFunction: (params: TParams, context: TContext) => Promise, + getContext: () => TContext, +): (args: Record) => Promise { + return createValidatedHandler(schema, logicFunction, getContext); +} + export type SessionRequirement = | { allOf: (keyof SessionDefaults)[]; message?: string } | { oneOf: (keyof SessionDefaults)[]; message?: string }; @@ -84,7 +94,7 @@ export function getSessionAwareToolSchemaShape(opts: { sessionAware: z.ZodObject; legacy: z.ZodObject; }): ToolSchemaShape { - return isSessionDefaultsSchemaOptOutEnabled() ? opts.legacy.shape : opts.sessionAware.shape; + return isSessionDefaultsOptOutEnabled() ? opts.legacy.shape : opts.sessionAware.shape; } export function createSessionAwareTool(opts: { @@ -93,11 +103,37 @@ export function createSessionAwareTool(opts: { getExecutor: () => CommandExecutor; requirements?: SessionRequirement[]; exclusivePairs?: (keyof SessionDefaults)[][]; // when args provide one side, drop conflicting session-default side(s) -}) { +}): (rawArgs: Record) => Promise { + return createSessionAwareHandler({ + internalSchema: opts.internalSchema, + logicFunction: opts.logicFunction, + getContext: opts.getExecutor, + requirements: opts.requirements, + exclusivePairs: opts.exclusivePairs, + }); +} + +export function createSessionAwareToolWithContext(opts: { + internalSchema: z.ZodType; + logicFunction: (params: TParams, context: TContext) => Promise; + getContext: () => TContext; + requirements?: SessionRequirement[]; + exclusivePairs?: (keyof SessionDefaults)[][]; +}): (rawArgs: Record) => Promise { + return createSessionAwareHandler(opts); +} + +function createSessionAwareHandler(opts: { + internalSchema: z.ZodType; + logicFunction: (params: TParams, context: TContext) => Promise; + getContext: () => TContext; + requirements?: SessionRequirement[]; + exclusivePairs?: (keyof SessionDefaults)[][]; +}): (rawArgs: Record) => Promise { const { internalSchema, logicFunction, - getExecutor, + getContext, requirements = [], exclusivePairs = [], } = opts; @@ -144,6 +180,24 @@ export function createSessionAwareTool(opts: { } } + // When both values of an exclusive pair come from session defaults (not user args), + // prefer the first key in the pair. This ensures simulatorId is preferred over simulatorName. + for (const pair of exclusivePairs) { + const allFromDefaults = pair.every( + (k) => !Object.prototype.hasOwnProperty.call(sanitizedArgs, k), + ); + if (!allFromDefaults) continue; + + const presentKeys = pair.filter((k) => merged[k] != null); + if (presentKeys.length > 1) { + // Keep first key (preferred), remove others + for (let i = 1; i < presentKeys.length; i++) { + delete merged[presentKeys[i]]; + } + } + } + + // Check requirements first (before expensive simulator resolution) for (const req of requirements) { if ('allOf' in req) { const missing = missingFromMerged(req.allOf, merged); @@ -154,7 +208,7 @@ export function createSessionAwareTool(opts: { const { title, body } = formatRequirementError({ message: req.message ?? `Required: ${req.allOf.join(', ')}`, setHint, - optOutEnabled: isSessionDefaultsSchemaOptOutEnabled(), + optOutEnabled: isSessionDefaultsOptOutEnabled(), }); return createErrorResponse(title, body); } @@ -168,7 +222,7 @@ export function createSessionAwareTool(opts: { const { title, body } = formatRequirementError({ message: req.message ?? `Provide one of: ${options}`, setHint: `Set with: ${setHints}`, - optOutEnabled: isSessionDefaultsSchemaOptOutEnabled(), + optOutEnabled: isSessionDefaultsOptOutEnabled(), }); return createErrorResponse(title, body); } @@ -176,7 +230,7 @@ export function createSessionAwareTool(opts: { } const validated = internalSchema.parse(merged); - return await logicFunction(validated, getExecutor()); + return await logicFunction(validated, getContext()); } catch (error) { if (error instanceof z.ZodError) { const details = `Invalid parameters:\n${formatZodIssues(error)}`; diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 4c2a7f84..33a3682f 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -22,8 +22,8 @@ import * as fs from 'fs'; import { log } from './logger.ts'; -import { ToolResponse, ValidationResult } from '../types/common.ts'; -import { FileSystemExecutor } from './FileSystemExecutor.ts'; +import type { ToolResponse, ValidationResult } from '../types/common.ts'; +import type { FileSystemExecutor } from './FileSystemExecutor.ts'; import { getDefaultEnvironmentDetector } from './environment.ts'; /** @@ -259,4 +259,4 @@ export function consolidateContentForClaudeCode(response: ToolResponse): ToolRes } // Export the ToolResponse type for use in other files -export { ToolResponse, ValidationResult }; +export type { ToolResponse, ValidationResult }; diff --git a/src/utils/video_capture.ts b/src/utils/video_capture.ts index ba6063b4..b3c83771 100644 --- a/src/utils/video_capture.ts +++ b/src/utils/video_capture.ts @@ -9,6 +9,7 @@ import type { ChildProcess } from 'child_process'; import { log } from './logging/index.ts'; import { getAxePath, getBundledAxeEnvironment } from './axe-helpers.ts'; import type { CommandExecutor } from './execution/index.ts'; +import { acquireDaemonActivity } from '../daemon/activity-registry.ts'; type Session = { process: unknown; @@ -16,6 +17,7 @@ type Session = { startedAt: number; buffer: string; ended: boolean; + releaseActivity?: () => void; }; const sessions = new Map(); @@ -38,6 +40,7 @@ function ensureSignalHandlersAttached(): void { } catch { // ignore } finally { + sess.releaseActivity?.(); sessions.delete(simulatorUuid); } } @@ -64,6 +67,10 @@ function createSessionId(simulatorUuid: string): string { return `${simulatorUuid}:${Date.now()}`; } +export function listActiveVideoCaptureSessionIds(): string[] { + return Array.from(sessions.keys()).sort(); +} + /** * Start recording video for a simulator using AXe. */ @@ -100,7 +107,7 @@ export async function startSimulatorVideoCapture( log('info', `Starting AXe video recording for simulator ${simulatorUuid} at ${fps} fps`); - const result = await executor(command, 'Start Simulator Video Capture', true, { env }, true); + const result = await executor(command, 'Start Simulator Video Capture', false, { env }, true); if (!result.success || !result.process) { return { @@ -116,6 +123,7 @@ export async function startSimulatorVideoCapture( startedAt: Date.now(), buffer: '', ended: false, + releaseActivity: acquireDaemonActivity('video.capture'), }; try { @@ -230,6 +238,7 @@ export async function stopSimulatorVideoCapture( const combinedOutput = session.buffer; const parsedPath = parseLastAbsoluteMp4Path(combinedOutput) ?? undefined; + session.releaseActivity?.(); sessions.delete(simulatorUuid); log( diff --git a/src/utils/workflow-selection.ts b/src/utils/workflow-selection.ts index e3b01e36..dc1de3da 100644 --- a/src/utils/workflow-selection.ts +++ b/src/utils/workflow-selection.ts @@ -1,9 +1,14 @@ import type { WorkflowGroup } from '../core/plugin-types.ts'; +import { getConfig } from './config-store.ts'; -const REQUIRED_WORKFLOW = 'session-management'; -const DEBUG_WORKFLOW = 'doctor'; +export const REQUIRED_WORKFLOW = 'session-management'; +export const WORKFLOW_DISCOVERY_WORKFLOW = 'workflow-discovery'; +export const DEBUG_WORKFLOW = 'doctor'; +export const DEFAULT_WORKFLOW = 'simulator'; -function normalizeWorkflowNames(workflowNames: string[]): string[] { +type WorkflowName = string; + +function normalizeWorkflowNames(workflowNames: WorkflowName[]): WorkflowName[] { return workflowNames.map((name) => name.trim().toLowerCase()).filter(Boolean); } @@ -11,28 +16,75 @@ function isWorkflowGroup(value: WorkflowGroup | undefined): value is WorkflowGro return Boolean(value); } -function isDebugEnabled(): boolean { - const value = process.env.XCODEBUILDMCP_DEBUG ?? ''; - return value.toLowerCase() === 'true' || value === '1'; +export function isDebugEnabled(): boolean { + return getConfig().debug; +} + +export function isWorkflowDiscoveryEnabled(): boolean { + return getConfig().experimentalWorkflowDiscovery; +} + +/** + * Resolve selected workflow names to only include workflows that + * match real workflows, ensuring the mandatory workflows are always included. + * + * @param workflowNames - The list of selected workflow names + * @returns The list of workflows to register. + */ +export function resolveSelectedWorkflowNames( + workflowNames: WorkflowName[] = [], + availableWorkflowNames: WorkflowName[] = [], +): { + selectedWorkflowNames: WorkflowName[]; + selectedNames: WorkflowName[]; +} { + const normalizedNames = normalizeWorkflowNames(workflowNames); + const baseAutoSelected = [REQUIRED_WORKFLOW]; + + if (isWorkflowDiscoveryEnabled()) { + baseAutoSelected.push(WORKFLOW_DISCOVERY_WORKFLOW); + } + + if (isDebugEnabled()) { + baseAutoSelected.push(DEBUG_WORKFLOW); + } + + // When no workflows specified, default to simulator workflow + const effectiveNames = normalizedNames.length > 0 ? normalizedNames : [DEFAULT_WORKFLOW]; + const selectedNames = [...new Set([...baseAutoSelected, ...effectiveNames])]; + + // Filter selected names to only include workflows that match real workflows. + const selectedWorkflowNames = selectedNames.filter((workflowName) => + availableWorkflowNames.includes(workflowName), + ); + + return { selectedWorkflowNames, selectedNames }; } +/** + * Resolve selected workflow groups to only include workflow groups that + * match real workflow groups, ensuring the mandatory workflow groups are always included. + * + * @param workflowNames - The list of selected workflow names + * @param workflowGroups - The map of workflow groups + * @returns The list of workflow groups to register. + */ export function resolveSelectedWorkflows( - workflowGroups: Map, - workflowNames: string[] = [], + workflowNames: WorkflowName[] = [], + workflowGroupsParam?: Map, ): { selectedWorkflows: WorkflowGroup[]; - selectedNames: string[] | null; + selectedNames: WorkflowName[]; } { - const normalizedNames = normalizeWorkflowNames(workflowNames); - const autoSelected = isDebugEnabled() ? [REQUIRED_WORKFLOW, DEBUG_WORKFLOW] : [REQUIRED_WORKFLOW]; - const selectedNames = - normalizedNames.length > 0 ? [...new Set([...autoSelected, ...normalizedNames])] : null; + const resolvedWorkflowGroups = workflowGroupsParam ?? new Map(); + const availableWorkflowNames = [...resolvedWorkflowGroups.keys()]; + const selection = resolveSelectedWorkflowNames(workflowNames, availableWorkflowNames); - const selectedWorkflows = selectedNames - ? selectedNames.map((workflowName) => workflowGroups.get(workflowName)).filter(isWorkflowGroup) - : [...workflowGroups.values()]; + const selectedWorkflows = selection.selectedWorkflowNames + .map((workflowName) => resolvedWorkflowGroups.get(workflowName)) + .filter(isWorkflowGroup); - return { selectedWorkflows, selectedNames }; + return { selectedWorkflows, selectedNames: selection.selectedNames }; } export function collectToolNames(workflows: WorkflowGroup[]): string[] { diff --git a/src/utils/xcode-process.ts b/src/utils/xcode-process.ts new file mode 100644 index 00000000..496f9b9d --- /dev/null +++ b/src/utils/xcode-process.ts @@ -0,0 +1,31 @@ +import type { CommandExecutor } from './execution/index.ts'; +import { getProcessTree, type ProcessTreeEntry } from './process-tree.ts'; + +export type { ProcessTreeEntry }; + +export type XcodeRuntimeDetection = { + runningUnderXcode: boolean; + processTree: ProcessTreeEntry[]; + error?: string; +}; + +export function isRunningUnderXcode(entries: ProcessTreeEntry[]): boolean { + return entries.some( + (entry) => + entry.name === 'Xcode' || + entry.command.includes('Contents/MacOS/Xcode') || + entry.command.includes('com.apple.dt.Xcode'), + ); +} + +export async function detectXcodeRuntime( + executor: CommandExecutor, + startPid?: string, +): Promise { + const processTreeResult = await getProcessTree(executor, startPid); + return { + runningUnderXcode: isRunningUnderXcode(processTreeResult.entries), + processTree: processTreeResult.entries, + error: processTreeResult.error, + }; +} diff --git a/src/utils/xcode-state-reader.ts b/src/utils/xcode-state-reader.ts new file mode 100644 index 00000000..d9c4994a --- /dev/null +++ b/src/utils/xcode-state-reader.ts @@ -0,0 +1,331 @@ +/** + * Xcode IDE State Reader + * + * Reads Xcode's UserInterfaceState.xcuserstate file to extract the currently + * selected scheme and run destination (simulator/device). + * + * This enables XcodeBuildMCP to auto-sync with Xcode's IDE selection when + * running under Xcode's coding agent. + */ + +import { dirname, resolve, sep } from 'node:path'; +import { log } from './logger.ts'; +import { parseXcuserstate } from './nskeyedarchiver-parser.ts'; +import type { CommandExecutor } from './execution/index.ts'; + +export interface XcodeStateResult { + scheme?: string; + simulatorId?: string; + simulatorName?: string; + error?: string; +} + +export interface XcodeStateReaderContext { + executor: CommandExecutor; + cwd: string; + /** Optional boundary for parent-directory fallback search (typically workspace root) */ + searchRoot?: string; + /** Optional pre-configured workspace path to use directly */ + workspacePath?: string; + /** Optional pre-configured project path to use directly */ + projectPath?: string; +} + +/** + * Finds the UserInterfaceState.xcuserstate file for the workspace/project. + * + * Search order: + * 1. Use configured workspacePath/projectPath if provided + * 2. Search for .xcworkspace/.xcodeproj under cwd + * 3. If none (or to broaden candidates), search direct children of parent directories + * up to searchRoot (workspace boundary) + * + * For each found project: + * - .xcworkspace: /xcuserdata/.xcuserdatad/UserInterfaceState.xcuserstate + * - .xcodeproj: /project.xcworkspace/xcuserdata/.xcuserdatad/UserInterfaceState.xcuserstate + */ +function buildFindProjectsCommand(root: string, maxDepth: number): string[] { + return [ + 'find', + root, + '-maxdepth', + String(maxDepth), + '(', + '-name', + '*.xcworkspace', + '-o', + '-name', + '*.xcodeproj', + ')', + '-type', + 'd', + ]; +} + +function isPathWithinBoundary(path: string, boundary: string): boolean { + return path === boundary || path.startsWith(`${boundary}${sep}`); +} + +function listParentDirectories(startPath: string, boundaryPath: string): string[] { + const parents: string[] = []; + const start = resolve(startPath); + const boundary = resolve(boundaryPath); + + if (!isPathWithinBoundary(start, boundary)) { + return parents; + } + + let current = start; + while (true) { + const parent = dirname(current); + if (parent === current) { + break; + } + + if (!isPathWithinBoundary(parent, boundary)) { + break; + } + + parents.push(parent); + if (parent === boundary) { + break; + } + + current = parent; + } + + return parents; +} + +function collectFindPaths(output: string): string[] { + return output + .trim() + .split('\n') + .map((path) => path.trim()) + .filter(Boolean); +} + +export async function findXcodeStateFile( + ctx: XcodeStateReaderContext, +): Promise { + const { executor, cwd, searchRoot, workspacePath, projectPath } = ctx; + + // Get current username + const userResult = await executor(['whoami'], 'Get username', false); + if (!userResult.success) { + log('warning', `[xcode-state] Failed to get username: ${userResult.error}`); + return undefined; + } + const username = userResult.output.trim(); + + // If workspacePath or projectPath is configured, use it directly + if (workspacePath || projectPath) { + const basePath = workspacePath ?? projectPath; + const xcuserstatePath = buildXcuserstatePath(basePath!, username); + const testResult = await executor( + ['test', '-f', xcuserstatePath], + 'Check xcuserstate exists', + false, + ); + if (testResult.success) { + log('debug', `[xcode-state] Found xcuserstate from config: ${xcuserstatePath}`); + return xcuserstatePath; + } + log('debug', `[xcode-state] Configured path xcuserstate not found: ${xcuserstatePath}`); + } + + const discoveredPaths = new Set(); + + // Search descendants from cwd with increased depth (projects can be nested deeper). + const descendantsResult = await executor( + buildFindProjectsCommand(cwd, 6), + 'Find Xcode project/workspace in cwd descendants', + false, + ); + if (descendantsResult.success && descendantsResult.output.trim()) { + for (const path of collectFindPaths(descendantsResult.output)) { + discoveredPaths.add(path); + } + } + + // Also search direct children of parent directories to support nested cwd usage. + // Example: cwd=/repo/feature/subdir, project=/repo/App.xcodeproj + // Parent traversal stops at searchRoot (workspace boundary). + const parentSearchBoundary = searchRoot ?? cwd; + for (const parentDir of listParentDirectories(cwd, parentSearchBoundary)) { + const parentResult = await executor( + buildFindProjectsCommand(parentDir, 1), + 'Find Xcode project/workspace in parent directory', + false, + ); + if (!parentResult.success || !parentResult.output.trim()) { + continue; + } + for (const path of collectFindPaths(parentResult.output)) { + discoveredPaths.add(path); + } + } + + if (discoveredPaths.size === 0) { + log( + 'debug', + `[xcode-state] No Xcode project/workspace found in ${cwd} (boundary: ${parentSearchBoundary})`, + ); + return undefined; + } + + const paths = [...discoveredPaths]; + + // Filter out nested workspaces inside xcodeproj and sort + const filteredPaths = paths + .filter((p) => !p.includes('.xcodeproj/project.xcworkspace')) + .sort((a, b) => { + // Prefer .xcworkspace over .xcodeproj + const aIsWorkspace = a.endsWith('.xcworkspace'); + const bIsWorkspace = b.endsWith('.xcworkspace'); + if (aIsWorkspace && !bIsWorkspace) return -1; + if (!aIsWorkspace && bIsWorkspace) return 1; + return 0; + }); + + // Collect all candidate xcuserstate files with their mtimes + const candidates: Array<{ path: string; mtime: number }> = []; + + for (const projectPath of filteredPaths) { + const xcuserstatePath = buildXcuserstatePath(projectPath, username); + + // Check if file exists and get mtime + const statResult = await executor( + ['stat', '-f', '%m', xcuserstatePath], + 'Get xcuserstate mtime', + false, + ); + + if (statResult.success) { + const mtime = parseInt(statResult.output.trim(), 10); + candidates.push({ path: xcuserstatePath, mtime }); + } + } + + if (candidates.length === 0) { + log('debug', `[xcode-state] No xcuserstate file found for user ${username}`); + return undefined; + } + + // If multiple candidates, pick the one with the newest mtime (most recently active) + if (candidates.length > 1) { + candidates.sort((a, b) => b.mtime - a.mtime); + log( + 'debug', + `[xcode-state] Found ${candidates.length} xcuserstate files, using newest: ${candidates[0].path}`, + ); + } + + log('debug', `[xcode-state] Found xcuserstate: ${candidates[0].path}`); + return candidates[0].path; +} + +/** + * Builds the path to the xcuserstate file for a given project/workspace path. + */ +function buildXcuserstatePath(projectPath: string, username: string): string { + if (projectPath.endsWith('.xcworkspace')) { + return `${projectPath}/xcuserdata/${username}.xcuserdatad/UserInterfaceState.xcuserstate`; + } else { + // .xcodeproj - look in embedded workspace + return `${projectPath}/project.xcworkspace/xcuserdata/${username}.xcuserdatad/UserInterfaceState.xcuserstate`; + } +} + +/** + * Looks up a simulator name by its UUID. + */ +export async function lookupSimulatorName( + ctx: XcodeStateReaderContext, + simulatorId: string, +): Promise { + const { executor } = ctx; + + const result = await executor( + ['xcrun', 'simctl', 'list', 'devices', 'available', '--json'], + 'List simulators', + false, + ); + + if (!result.success) { + log('warning', `[xcode-state] Failed to list simulators: ${result.error}`); + return undefined; + } + + try { + const data = JSON.parse(result.output) as { + devices: Record>; + }; + + for (const runtime of Object.values(data.devices)) { + for (const device of runtime) { + if (device.udid === simulatorId) { + return device.name; + } + } + } + } catch (e) { + log('warning', `[xcode-state] Failed to parse simulator list: ${e}`); + } + + return undefined; +} + +/** + * Reads Xcode's IDE state and extracts the active scheme and simulator. + * + * Uses bplist-parser for robust binary plist parsing of the xcuserstate file, + * navigating the NSKeyedArchiver object graph to extract: + * - ActiveScheme -> IDENameString (scheme name) + * - ActiveRunDestination -> targetDeviceLocation (simulator/device UUID) + * + * @param ctx Context with command executor and working directory + * @returns The extracted Xcode state or an error + */ +export async function readXcodeIdeState(ctx: XcodeStateReaderContext): Promise { + try { + // Find the xcuserstate file + const xcuserstatePath = await findXcodeStateFile(ctx); + if (!xcuserstatePath) { + return { error: 'No Xcode project/workspace found in working directory' }; + } + + // Parse the state file using bplist-parser + const state = parseXcuserstate(xcuserstatePath); + + const result: XcodeStateResult = {}; + + if (state.scheme) { + result.scheme = state.scheme; + log('info', `[xcode-state] Detected active scheme: ${state.scheme}`); + } + + if (state.simulatorId) { + result.simulatorId = state.simulatorId; + + // Look up the simulator name + const name = await lookupSimulatorName(ctx, state.simulatorId); + if (name) { + result.simulatorName = name; + log('info', `[xcode-state] Detected active simulator: ${name} (${state.simulatorId})`); + } else { + log('info', `[xcode-state] Detected active destination: ${state.simulatorId}`); + } + } + + if (!result.scheme && !result.simulatorId) { + return { error: 'Could not extract active scheme or destination from Xcode state' }; + } + + return result; + } catch (e) { + const message = e instanceof Error ? e.message : String(e); + log('warning', `[xcode-state] Failed to read Xcode IDE state: ${message}`); + return { error: message }; + } +} diff --git a/src/utils/xcode-state-watcher.ts b/src/utils/xcode-state-watcher.ts new file mode 100644 index 00000000..cf94017c --- /dev/null +++ b/src/utils/xcode-state-watcher.ts @@ -0,0 +1,282 @@ +/** + * Xcode IDE State Watcher + * + * Watches Xcode's UserInterfaceState.xcuserstate file for changes and + * automatically syncs scheme/simulator selection to session defaults. + * + * Uses chokidar for reliable FSEvents-based file watching on macOS. + */ + +import { watch, type FSWatcher } from 'chokidar'; +import { log } from './logger.ts'; +import { parseXcuserstate } from './nskeyedarchiver-parser.ts'; +import { sessionStore } from './session-store.ts'; +import { findXcodeStateFile, lookupSimulatorName } from './xcode-state-reader.ts'; +import type { CommandExecutor } from './execution/index.ts'; +import { getDefaultCommandExecutor } from './execution/index.ts'; + +interface WatcherState { + watcher: FSWatcher | null; + watchedPath: string | null; + cachedScheme: string | null; + cachedSimulatorId: string | null; + debounceTimer: ReturnType | null; + executor: CommandExecutor | null; + cwd: string | null; + projectPath: string | null; + workspacePath: string | null; +} + +const state: WatcherState = { + watcher: null, + watchedPath: null, + cachedScheme: null, + cachedSimulatorId: null, + debounceTimer: null, + executor: null, + cwd: null, + projectPath: null, + workspacePath: null, +}; + +const DEBOUNCE_MS = 300; + +/** + * Look up bundle ID for a scheme using xcodebuild -showBuildSettings + */ +export async function lookupBundleId( + executor: CommandExecutor, + scheme: string, + projectPath?: string | null, + workspacePath?: string | null, +): Promise { + const args = ['xcodebuild', '-showBuildSettings', '-scheme', scheme, '-skipPackageUpdates']; + + if (workspacePath) { + args.push('-workspace', workspacePath); + } else if (projectPath) { + args.push('-project', projectPath); + } else { + // No project/workspace specified, let xcodebuild find it + } + + const result = await executor(args, 'Get bundle ID from build settings', false); + + if (!result.success) { + log('debug', `[xcode-watcher] Failed to get build settings: ${result.error}`); + return undefined; + } + + // Parse PRODUCT_BUNDLE_IDENTIFIER from output + const match = result.output.match(/PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(.+)/); + if (match) { + return match[1].trim(); + } + + return undefined; +} + +/** + * Extract scheme and simulator ID from xcuserstate file + */ +function extractState(filePath: string): { scheme: string | null; simulatorId: string | null } { + try { + const result = parseXcuserstate(filePath); + return { + scheme: result.scheme ?? null, + simulatorId: result.simulatorId ?? null, + }; + } catch (e) { + log('warning', `[xcode-watcher] Failed to parse xcuserstate: ${e}`); + return { scheme: null, simulatorId: null }; + } +} + +/** + * Handle file change event (debounced) + */ +function handleFileChange(): void { + if (state.debounceTimer) { + clearTimeout(state.debounceTimer); + } + + state.debounceTimer = setTimeout(() => { + state.debounceTimer = null; + processFileChange().catch((e) => { + log('warning', `[xcode-watcher] Error processing file change: ${e}`); + }); + }, DEBOUNCE_MS); +} + +/** + * Process the file change and update session defaults + */ +async function processFileChange(): Promise { + if (!state.watchedPath) return; + + const newState = extractState(state.watchedPath); + + const schemeChanged = newState.scheme !== state.cachedScheme; + const simulatorChanged = newState.simulatorId !== state.cachedSimulatorId; + + if (!schemeChanged && !simulatorChanged) { + log('debug', '[xcode-watcher] File changed but scheme/simulator unchanged'); + return; + } + + const updates: Record = {}; + + if (schemeChanged && newState.scheme) { + updates.scheme = newState.scheme; + log('info', `[xcode-watcher] Scheme changed: "${state.cachedScheme}" -> "${newState.scheme}"`); + state.cachedScheme = newState.scheme; + } + + if (simulatorChanged && newState.simulatorId) { + updates.simulatorId = newState.simulatorId; + log( + 'info', + `[xcode-watcher] Simulator changed: "${state.cachedSimulatorId}" -> "${newState.simulatorId}"`, + ); + state.cachedSimulatorId = newState.simulatorId; + } + + // Update session defaults immediately with scheme/simulatorId + if (Object.keys(updates).length > 0) { + sessionStore.setDefaults(updates); + log('info', `[xcode-watcher] Session defaults updated: ${JSON.stringify(updates)}`); + } + + // Look up simulator name asynchronously (non-blocking) + if (simulatorChanged && newState.simulatorId && state.executor && state.cwd) { + lookupSimulatorName({ executor: state.executor, cwd: state.cwd }, newState.simulatorId) + .then((name) => { + if (name) { + sessionStore.setDefaults({ simulatorName: name }); + log('info', `[xcode-watcher] Simulator name resolved: "${name}"`); + } + }) + .catch((e) => { + log('debug', `[xcode-watcher] Failed to lookup simulator name: ${e}`); + }); + } + + // Look up bundle ID asynchronously when scheme changes (non-blocking) + if (schemeChanged && newState.scheme && state.executor) { + lookupBundleId(state.executor, newState.scheme, state.projectPath, state.workspacePath) + .then((bundleId) => { + if (bundleId) { + sessionStore.setDefaults({ bundleId }); + log('info', `[xcode-watcher] Bundle ID resolved: "${bundleId}"`); + } + }) + .catch((e) => { + log('debug', `[xcode-watcher] Failed to lookup bundle ID: ${e}`); + }); + } +} + +export interface StartWatcherOptions { + executor?: CommandExecutor; + cwd?: string; + searchRoot?: string; + workspacePath?: string; + projectPath?: string; +} + +/** + * Start watching the xcuserstate file for changes + */ +export async function startXcodeStateWatcher(options: StartWatcherOptions = {}): Promise { + if (state.watcher) { + log('debug', '[xcode-watcher] Watcher already running'); + return true; + } + + const executor = options.executor ?? getDefaultCommandExecutor(); + const cwd = options.cwd ?? process.cwd(); + + const xcuserstatePath = await findXcodeStateFile({ + executor, + cwd, + searchRoot: options.searchRoot, + workspacePath: options.workspacePath, + projectPath: options.projectPath, + }); + + if (!xcuserstatePath) { + log('debug', '[xcode-watcher] No xcuserstate file found, watcher not started'); + return false; + } + + // Initialize cached state + const initialState = extractState(xcuserstatePath); + state.cachedScheme = initialState.scheme; + state.cachedSimulatorId = initialState.simulatorId; + state.watchedPath = xcuserstatePath; + state.executor = executor; + state.cwd = cwd; + state.projectPath = options.projectPath ?? null; + state.workspacePath = options.workspacePath ?? null; + + log( + 'info', + `[xcode-watcher] Starting watcher for ${xcuserstatePath} (scheme="${initialState.scheme}", sim="${initialState.simulatorId}")`, + ); + + state.watcher = watch(xcuserstatePath, { + persistent: true, + ignoreInitial: true, + awaitWriteFinish: { + stabilityThreshold: 100, + pollInterval: 50, + }, + }); + + state.watcher.on('change', () => { + log('debug', '[xcode-watcher] File change detected'); + handleFileChange(); + }); + + state.watcher.on('error', (error: unknown) => { + const message = error instanceof Error ? error.message : String(error); + log('warning', `[xcode-watcher] Watcher error: ${message}`); + }); + + return true; +} + +/** + * Stop the xcuserstate watcher + */ +export async function stopXcodeStateWatcher(): Promise { + if (state.debounceTimer) { + clearTimeout(state.debounceTimer); + state.debounceTimer = null; + } + + if (state.watcher) { + await state.watcher.close(); + state.watcher = null; + state.watchedPath = null; + state.executor = null; + state.cwd = null; + state.projectPath = null; + state.workspacePath = null; + log('info', '[xcode-watcher] Watcher stopped'); + } +} + +/** + * Check if the watcher is currently running + */ +export function isWatcherRunning(): boolean { + return state.watcher !== null; +} + +/** + * Get the currently watched path + */ +export function getWatchedPath(): string | null { + return state.watchedPath; +} diff --git a/src/utils/xcodemake.ts b/src/utils/xcodemake.ts index 00a46f39..3d9f8954 100644 --- a/src/utils/xcodemake.ts +++ b/src/utils/xcodemake.ts @@ -14,11 +14,13 @@ */ import { log } from './logger.ts'; -import { CommandResponse, getDefaultCommandExecutor } from './command.ts'; -import { existsSync, readdirSync } from 'fs'; +import type { CommandResponse } from './command.ts'; +import { getDefaultCommandExecutor } from './command.ts'; +import { existsSync, readdirSync, statSync } from 'fs'; import * as path from 'path'; import * as os from 'os'; import * as fs from 'fs/promises'; +import { getConfig } from './config-store.ts'; // Environment variable to control xcodemake usage export const XCODEMAKE_ENV_VAR = 'INCREMENTAL_BUILDS_ENABLED'; @@ -31,8 +33,7 @@ let overriddenXcodemakePath: string | null = null; * @returns boolean indicating if xcodemake should be used */ export function isXcodemakeEnabled(): boolean { - const envValue = process.env[XCODEMAKE_ENV_VAR]; - return envValue === '1' || envValue === 'true' || envValue === 'yes'; + return getConfig().incrementalBuildsEnabled; } /** @@ -43,6 +44,36 @@ function getXcodemakeCommand(): string { return overriddenXcodemakePath ?? 'xcodemake'; } +function isExecutable(path: string): boolean { + try { + const stat = statSync(path); + return stat.isFile() && (stat.mode & 0o111) !== 0; + } catch { + return false; + } +} + +/** + * Check whether xcodemake binary is currently resolvable without side effects. + * This does not attempt download or installation. + */ +export function isXcodemakeBinaryAvailable(): boolean { + if (overriddenXcodemakePath && isExecutable(overriddenXcodemakePath)) { + return true; + } + + const pathValue = process.env.PATH ?? ''; + const entries = pathValue.split(path.delimiter).filter(Boolean); + for (const entry of entries) { + const candidate = path.join(entry, 'xcodemake'); + if (isExecutable(candidate)) { + return true; + } + } + + return false; +} + /** * Override the xcodemake command path * @param path Path to the xcodemake executable @@ -122,16 +153,15 @@ export async function isXcodemakeAvailable(): Promise { return true; } - // If not found, download and install it log('info', 'xcodemake not found in PATH, attempting to download...'); const installed = await installXcodemake(); - if (installed) { - log('info', 'xcodemake installed successfully'); - return true; - } else { - log('warn', 'xcodemake installation failed'); + if (!installed) { + log('warning', 'xcodemake installation failed'); return false; } + + log('info', 'xcodemake installed successfully'); + return true; } catch (error) { log( 'error', @@ -205,15 +235,18 @@ export async function executeXcodemakeCommand( buildArgs: string[], logPrefix: string, ): Promise { - // Change directory to project directory, this is needed for xcodemake to work - process.chdir(projectDir); - const xcodemakeCommand = [getXcodemakeCommand(), ...buildArgs]; - // Remove projectDir from arguments - const command = xcodemakeCommand.map((arg) => arg.replace(projectDir + '/', '')); + // Remove projectDir from arguments if present at the start + const prefix = projectDir + '/'; + const command = xcodemakeCommand.map((arg) => { + if (arg.startsWith(prefix)) { + return arg.substring(prefix.length); + } + return arg; + }); - return getDefaultCommandExecutor()(command, logPrefix); + return getDefaultCommandExecutor()(command, logPrefix, false, { cwd: projectDir }); } /** @@ -226,6 +259,6 @@ export async function executeMakeCommand( projectDir: string, logPrefix: string, ): Promise { - const command = ['cd', projectDir, '&&', 'make']; - return getDefaultCommandExecutor()(command, logPrefix); + const command = ['make']; + return getDefaultCommandExecutor()(command, logPrefix, false, { cwd: projectDir }); } diff --git a/src/utils/xcodemake/index.ts b/src/utils/xcodemake/index.ts index cb24f210..c115b16f 100644 --- a/src/utils/xcodemake/index.ts +++ b/src/utils/xcodemake/index.ts @@ -1 +1,6 @@ -export { isXcodemakeEnabled, isXcodemakeAvailable, doesMakefileExist } from '../xcodemake.ts'; +export { + isXcodemakeEnabled, + isXcodemakeAvailable, + isXcodemakeBinaryAvailable, + doesMakefileExist, +} from '../xcodemake.ts'; diff --git a/src/version.ts b/src/version.ts deleted file mode 100644 index 640d2d73..00000000 --- a/src/version.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const version = '1.15.1'; -export const iOSTemplateVersion = 'v1.0.8'; -export const macOSTemplateVersion = 'v1.0.5'; diff --git a/src/visibility/__tests__/exposure.test.ts b/src/visibility/__tests__/exposure.test.ts new file mode 100644 index 00000000..95c36135 --- /dev/null +++ b/src/visibility/__tests__/exposure.test.ts @@ -0,0 +1,348 @@ +import { describe, it, expect } from 'vitest'; +import { + isWorkflowAvailableForRuntime, + isWorkflowEnabledForRuntime, + isToolAvailableForRuntime, + isToolExposedForRuntime, + isToolInWorkflowExposed, + filterExposedTools, + filterEnabledWorkflows, + getDefaultEnabledWorkflows, + getAutoIncludeWorkflows, + selectWorkflowsForMcp, +} from '../exposure.ts'; +import type { ToolManifestEntry, WorkflowManifestEntry } from '../../core/manifest/schema.ts'; +import type { PredicateContext } from '../predicate-types.ts'; +import type { ResolvedRuntimeConfig } from '../../utils/config-store.ts'; + +function createDefaultConfig( + overrides: Partial = {}, +): ResolvedRuntimeConfig { + return { + debug: false, + enabledWorkflows: [], + experimentalWorkflowDiscovery: false, + disableSessionDefaults: false, + disableXcodeAutoSync: false, + uiDebuggerGuardMode: 'error', + incrementalBuildsEnabled: false, + dapRequestTimeoutMs: 30000, + dapLogEvents: false, + launchJsonWaitMs: 8000, + debuggerBackend: 'dap', + ...overrides, + }; +} + +function createContext(overrides: Partial = {}): PredicateContext { + return { + runtime: 'mcp', + config: createDefaultConfig(), + runningUnderXcode: false, + ...overrides, + }; +} + +function createTool(overrides: Partial = {}): ToolManifestEntry { + return { + id: 'test_tool', + module: 'mcp/tools/test/test_tool', + names: { mcp: 'test_tool' }, + availability: { mcp: true, cli: true }, + predicates: [], + nextSteps: [], + ...overrides, + }; +} + +function createWorkflow(overrides: Partial = {}): WorkflowManifestEntry { + return { + id: 'test-workflow', + title: 'Test Workflow', + description: 'A test workflow', + availability: { mcp: true, cli: true }, + predicates: [], + tools: ['test_tool'], + ...overrides, + }; +} + +describe('exposure', () => { + describe('isWorkflowAvailableForRuntime', () => { + it('should return true when workflow is available for runtime', () => { + const workflow = createWorkflow({ availability: { mcp: true, cli: false } }); + expect(isWorkflowAvailableForRuntime(workflow, 'mcp')).toBe(true); + }); + + it('should return false when workflow is not available for runtime', () => { + const workflow = createWorkflow({ availability: { mcp: true, cli: false } }); + expect(isWorkflowAvailableForRuntime(workflow, 'cli')).toBe(false); + }); + + it('should ignore manifest availability in daemon runtime', () => { + const workflow = createWorkflow({ availability: { mcp: false, cli: false } }); + expect(isWorkflowAvailableForRuntime(workflow, 'daemon')).toBe(true); + }); + }); + + describe('isWorkflowEnabledForRuntime', () => { + it('should return true when available and predicates pass', () => { + const workflow = createWorkflow(); + const ctx = createContext({ runtime: 'mcp' }); + expect(isWorkflowEnabledForRuntime(workflow, ctx)).toBe(true); + }); + + it('should return false when not available', () => { + const workflow = createWorkflow({ availability: { mcp: false, cli: true } }); + const ctx = createContext({ runtime: 'mcp' }); + expect(isWorkflowEnabledForRuntime(workflow, ctx)).toBe(false); + }); + + it('should return false when predicate fails', () => { + const workflow = createWorkflow({ predicates: ['debugEnabled'] }); + const ctx = createContext({ + runtime: 'mcp', + config: createDefaultConfig({ debug: false }), + }); + expect(isWorkflowEnabledForRuntime(workflow, ctx)).toBe(false); + }); + }); + + describe('isToolAvailableForRuntime', () => { + it('should return true when tool is available for runtime', () => { + const tool = createTool({ availability: { mcp: true, cli: false } }); + expect(isToolAvailableForRuntime(tool, 'mcp')).toBe(true); + }); + + it('should return false when tool is not available for runtime', () => { + const tool = createTool({ availability: { mcp: false, cli: true } }); + expect(isToolAvailableForRuntime(tool, 'mcp')).toBe(false); + }); + + it('should ignore manifest availability in daemon runtime', () => { + const tool = createTool({ availability: { mcp: false, cli: false } }); + expect(isToolAvailableForRuntime(tool, 'daemon')).toBe(true); + }); + }); + + describe('isToolExposedForRuntime', () => { + it('should return true when available and predicates pass', () => { + const tool = createTool(); + const ctx = createContext({ runtime: 'mcp' }); + expect(isToolExposedForRuntime(tool, ctx)).toBe(true); + }); + + it('should return false when not available', () => { + const tool = createTool({ availability: { mcp: false, cli: true } }); + const ctx = createContext({ runtime: 'mcp' }); + expect(isToolExposedForRuntime(tool, ctx)).toBe(false); + }); + + it('should return false when hideWhenXcodeAgentMode predicate fails (running under Xcode)', () => { + const tool = createTool({ predicates: ['hideWhenXcodeAgentMode'] }); + const ctx = createContext({ + runtime: 'mcp', + runningUnderXcode: true, + }); + expect(isToolExposedForRuntime(tool, ctx)).toBe(false); + }); + + it('should return true when hideWhenXcodeAgentMode predicate passes (not under Xcode)', () => { + const tool = createTool({ predicates: ['hideWhenXcodeAgentMode'] }); + const ctx = createContext({ + runtime: 'mcp', + runningUnderXcode: false, + }); + expect(isToolExposedForRuntime(tool, ctx)).toBe(true); + }); + }); + + describe('isToolInWorkflowExposed', () => { + it('should return true when both workflow and tool are enabled', () => { + const workflow = createWorkflow(); + const tool = createTool(); + const ctx = createContext({ runtime: 'mcp' }); + expect(isToolInWorkflowExposed(tool, workflow, ctx)).toBe(true); + }); + + it('should return false when workflow is not enabled', () => { + const workflow = createWorkflow({ + availability: { mcp: false, cli: true }, + }); + const tool = createTool(); + const ctx = createContext({ runtime: 'mcp' }); + expect(isToolInWorkflowExposed(tool, workflow, ctx)).toBe(false); + }); + + it('should return false when tool is not exposed', () => { + const workflow = createWorkflow(); + const tool = createTool({ availability: { mcp: false, cli: true } }); + const ctx = createContext({ runtime: 'mcp' }); + expect(isToolInWorkflowExposed(tool, workflow, ctx)).toBe(false); + }); + }); + + describe('filterExposedTools', () => { + it('should filter out tools that are not exposed', () => { + const tools = [ + createTool({ id: 'tool1' }), + createTool({ id: 'tool2', availability: { mcp: false, cli: true } }), + createTool({ id: 'tool3' }), + ]; + const ctx = createContext({ runtime: 'mcp' }); + + const filtered = filterExposedTools(tools, ctx); + expect(filtered).toHaveLength(2); + expect(filtered.map((t) => t.id)).toEqual(['tool1', 'tool3']); + }); + }); + + describe('filterEnabledWorkflows', () => { + it('should filter out workflows that are not enabled', () => { + const workflows = [ + createWorkflow({ id: 'wf1' }), + createWorkflow({ id: 'wf2', availability: { mcp: false, cli: true } }), + createWorkflow({ id: 'wf3' }), + ]; + const ctx = createContext({ runtime: 'mcp' }); + + const filtered = filterEnabledWorkflows(workflows, ctx); + expect(filtered).toHaveLength(2); + expect(filtered.map((w) => w.id)).toEqual(['wf1', 'wf3']); + }); + }); + + describe('getDefaultEnabledWorkflows', () => { + it('should return only default-enabled workflows', () => { + const workflows = [ + createWorkflow({ + id: 'wf1', + selection: { mcp: { defaultEnabled: true, autoInclude: false } }, + }), + createWorkflow({ + id: 'wf2', + selection: { mcp: { defaultEnabled: false, autoInclude: false } }, + }), + createWorkflow({ + id: 'wf3', + selection: { mcp: { defaultEnabled: true, autoInclude: false } }, + }), + ]; + + const defaultEnabled = getDefaultEnabledWorkflows(workflows); + expect(defaultEnabled).toHaveLength(2); + expect(defaultEnabled.map((w) => w.id)).toEqual(['wf1', 'wf3']); + }); + }); + + describe('getAutoIncludeWorkflows', () => { + it('should return auto-include workflows whose predicates pass', () => { + const workflows = [ + createWorkflow({ + id: 'wf1', + selection: { mcp: { defaultEnabled: false, autoInclude: true } }, + predicates: [], + }), + createWorkflow({ + id: 'wf2', + selection: { mcp: { defaultEnabled: false, autoInclude: true } }, + predicates: ['debugEnabled'], + }), + createWorkflow({ + id: 'wf3', + selection: { mcp: { defaultEnabled: false, autoInclude: false } }, + }), + ]; + + const ctx = createContext({ + config: createDefaultConfig({ debug: false }), + }); + + const autoInclude = getAutoIncludeWorkflows(workflows, ctx); + expect(autoInclude).toHaveLength(1); + expect(autoInclude[0].id).toBe('wf1'); + }); + + it('should include auto-include workflows when their predicates pass', () => { + const workflows = [ + createWorkflow({ + id: 'doctor', + selection: { mcp: { defaultEnabled: false, autoInclude: true } }, + predicates: ['debugEnabled'], + }), + ]; + + const ctx = createContext({ + config: createDefaultConfig({ debug: true }), + }); + + const autoInclude = getAutoIncludeWorkflows(workflows, ctx); + expect(autoInclude).toHaveLength(1); + expect(autoInclude[0].id).toBe('doctor'); + }); + }); + + describe('selectWorkflowsForMcp', () => { + const allWorkflows = [ + createWorkflow({ + id: 'session-management', + selection: { mcp: { defaultEnabled: true, autoInclude: true } }, + }), + createWorkflow({ + id: 'simulator', + selection: { mcp: { defaultEnabled: true, autoInclude: false } }, + }), + createWorkflow({ + id: 'device', + selection: { mcp: { defaultEnabled: false, autoInclude: false } }, + }), + createWorkflow({ + id: 'doctor', + selection: { mcp: { defaultEnabled: false, autoInclude: true } }, + predicates: ['debugEnabled'], + }), + ]; + + it('should include auto-include workflows', () => { + const ctx = createContext(); + const selected = selectWorkflowsForMcp(allWorkflows, undefined, ctx); + expect(selected.map((w) => w.id)).toContain('session-management'); + }); + + it('should include default-enabled workflows when no workflows requested', () => { + const ctx = createContext(); + const selected = selectWorkflowsForMcp(allWorkflows, undefined, ctx); + expect(selected.map((w) => w.id)).toContain('simulator'); + }); + + it('should include requested workflows', () => { + const ctx = createContext(); + const selected = selectWorkflowsForMcp(allWorkflows, ['device'], ctx); + expect(selected.map((w) => w.id)).toContain('device'); + expect(selected.map((w) => w.id)).toContain('session-management'); // autoInclude + }); + + it('should not include default-enabled when workflows are requested', () => { + const ctx = createContext(); + const selected = selectWorkflowsForMcp(allWorkflows, ['device'], ctx); + // simulator is default-enabled but not requested + expect(selected.map((w) => w.id)).not.toContain('simulator'); + }); + + it('should include auto-include workflows when predicates pass', () => { + const ctx = createContext({ + config: createDefaultConfig({ debug: true }), + }); + const selected = selectWorkflowsForMcp(allWorkflows, ['device'], ctx); + expect(selected.map((w) => w.id)).toContain('doctor'); + }); + + it('should not include auto-include workflows when predicates fail', () => { + const ctx = createContext({ + config: createDefaultConfig({ debug: false }), + }); + const selected = selectWorkflowsForMcp(allWorkflows, ['device'], ctx); + expect(selected.map((w) => w.id)).not.toContain('doctor'); + }); + }); +}); diff --git a/src/visibility/__tests__/predicate-registry.test.ts b/src/visibility/__tests__/predicate-registry.test.ts new file mode 100644 index 00000000..a082738c --- /dev/null +++ b/src/visibility/__tests__/predicate-registry.test.ts @@ -0,0 +1,220 @@ +import { describe, it, expect } from 'vitest'; +import { + PREDICATES, + evalPredicates, + getPredicateNames, + isValidPredicate, +} from '../predicate-registry.ts'; +import type { PredicateContext } from '../predicate-types.ts'; +import type { ResolvedRuntimeConfig } from '../../utils/config-store.ts'; + +function createDefaultConfig( + overrides: Partial = {}, +): ResolvedRuntimeConfig { + return { + debug: false, + enabledWorkflows: [], + experimentalWorkflowDiscovery: false, + disableSessionDefaults: false, + disableXcodeAutoSync: false, + uiDebuggerGuardMode: 'error', + incrementalBuildsEnabled: false, + dapRequestTimeoutMs: 30000, + dapLogEvents: false, + launchJsonWaitMs: 8000, + debuggerBackend: 'dap', + ...overrides, + }; +} + +function createContext(overrides: Partial = {}): PredicateContext { + return { + runtime: 'mcp', + config: createDefaultConfig(), + runningUnderXcode: false, + ...overrides, + }; +} + +describe('predicate-registry', () => { + describe('PREDICATES', () => { + describe('debugEnabled', () => { + it('should return true when debug is enabled', () => { + const ctx = createContext({ + config: createDefaultConfig({ debug: true }), + }); + expect(PREDICATES.debugEnabled(ctx)).toBe(true); + }); + + it('should return false when debug is disabled', () => { + const ctx = createContext({ + config: createDefaultConfig({ debug: false }), + }); + expect(PREDICATES.debugEnabled(ctx)).toBe(false); + }); + }); + + describe('experimentalWorkflowDiscoveryEnabled', () => { + it('should return true when experimental workflow discovery is enabled', () => { + const ctx = createContext({ + config: createDefaultConfig({ experimentalWorkflowDiscovery: true }), + }); + expect(PREDICATES.experimentalWorkflowDiscoveryEnabled(ctx)).toBe(true); + }); + + it('should return false when experimental workflow discovery is disabled', () => { + const ctx = createContext({ + config: createDefaultConfig({ debug: false }), + }); + expect(PREDICATES.experimentalWorkflowDiscoveryEnabled(ctx)).toBe(false); + }); + }); + + describe('runningUnderXcodeAgent', () => { + it('should return true when running under Xcode', () => { + const ctx = createContext({ runningUnderXcode: true }); + expect(PREDICATES.runningUnderXcodeAgent(ctx)).toBe(true); + }); + + it('should return false when not running under Xcode', () => { + const ctx = createContext({ runningUnderXcode: false }); + expect(PREDICATES.runningUnderXcodeAgent(ctx)).toBe(false); + }); + }); + + describe('mcpRuntimeOnly', () => { + it('should return true for MCP runtime', () => { + const ctx = createContext({ runtime: 'mcp' }); + expect(PREDICATES.mcpRuntimeOnly(ctx)).toBe(true); + }); + + it('should return false for CLI runtime', () => { + const ctx = createContext({ runtime: 'cli' }); + expect(PREDICATES.mcpRuntimeOnly(ctx)).toBe(false); + }); + }); + + describe('hideWhenXcodeAgentMode', () => { + it('should return true when not running under Xcode', () => { + const ctx = createContext({ runningUnderXcode: false }); + expect(PREDICATES.hideWhenXcodeAgentMode(ctx)).toBe(true); + }); + + it('should return false when running under Xcode', () => { + const ctx = createContext({ runningUnderXcode: true }); + expect(PREDICATES.hideWhenXcodeAgentMode(ctx)).toBe(false); + }); + }); + + describe('xcodeAutoSyncDisabled', () => { + it('should return true when running under Xcode AND auto-sync is disabled', () => { + const ctx = createContext({ + runningUnderXcode: true, + config: createDefaultConfig({ disableXcodeAutoSync: true }), + }); + expect(PREDICATES.xcodeAutoSyncDisabled(ctx)).toBe(true); + }); + + it('should return false when running under Xcode but auto-sync is enabled', () => { + const ctx = createContext({ + runningUnderXcode: true, + config: createDefaultConfig({ disableXcodeAutoSync: false }), + }); + expect(PREDICATES.xcodeAutoSyncDisabled(ctx)).toBe(false); + }); + + it('should return false when not running under Xcode even if auto-sync is disabled', () => { + const ctx = createContext({ + runningUnderXcode: false, + config: createDefaultConfig({ disableXcodeAutoSync: true }), + }); + expect(PREDICATES.xcodeAutoSyncDisabled(ctx)).toBe(false); + }); + + it('should return false when not running under Xcode and auto-sync is enabled', () => { + const ctx = createContext({ + runningUnderXcode: false, + config: createDefaultConfig({ disableXcodeAutoSync: false }), + }); + expect(PREDICATES.xcodeAutoSyncDisabled(ctx)).toBe(false); + }); + }); + + describe('always', () => { + it('should always return true', () => { + const ctx = createContext(); + expect(PREDICATES.always(ctx)).toBe(true); + }); + }); + + describe('never', () => { + it('should always return false', () => { + const ctx = createContext(); + expect(PREDICATES.never(ctx)).toBe(false); + }); + }); + }); + + describe('evalPredicates', () => { + it('should return true for empty predicate list', () => { + const ctx = createContext(); + expect(evalPredicates([], ctx)).toBe(true); + }); + + it('should return true for undefined predicate list', () => { + const ctx = createContext(); + expect(evalPredicates(undefined, ctx)).toBe(true); + }); + + it('should return true when all predicates pass', () => { + const ctx = createContext({ + config: createDefaultConfig({ debug: true, experimentalWorkflowDiscovery: true }), + }); + expect(evalPredicates(['debugEnabled', 'experimentalWorkflowDiscoveryEnabled'], ctx)).toBe( + true, + ); + }); + + it('should return false when any predicate fails', () => { + const ctx = createContext({ + config: createDefaultConfig({ debug: true }), + }); + expect(evalPredicates(['debugEnabled', 'experimentalWorkflowDiscoveryEnabled'], ctx)).toBe( + false, + ); + }); + + it('should throw for unknown predicate', () => { + const ctx = createContext(); + expect(() => evalPredicates(['unknownPredicate'], ctx)).toThrow( + "Unknown predicate 'unknownPredicate'", + ); + }); + }); + + describe('getPredicateNames', () => { + it('should return all predicate names', () => { + const names = getPredicateNames(); + expect(names).toContain('debugEnabled'); + expect(names).toContain('experimentalWorkflowDiscoveryEnabled'); + expect(names).toContain('runningUnderXcodeAgent'); + expect(names).toContain('mcpRuntimeOnly'); + expect(names).toContain('hideWhenXcodeAgentMode'); + expect(names).toContain('xcodeAutoSyncDisabled'); + expect(names).toContain('always'); + expect(names).toContain('never'); + }); + }); + + describe('isValidPredicate', () => { + it('should return true for valid predicates', () => { + expect(isValidPredicate('debugEnabled')).toBe(true); + expect(isValidPredicate('hideWhenXcodeAgentMode')).toBe(true); + }); + + it('should return false for invalid predicates', () => { + expect(isValidPredicate('unknownPredicate')).toBe(false); + expect(isValidPredicate('')).toBe(false); + }); + }); +}); diff --git a/src/visibility/exposure.ts b/src/visibility/exposure.ts new file mode 100644 index 00000000..89fc6cea --- /dev/null +++ b/src/visibility/exposure.ts @@ -0,0 +1,163 @@ +/** + * Exposure evaluation for tools and workflows. + * Determines whether tools/workflows should be visible based on + * availability flags and predicate evaluation. + */ + +import type { ToolManifestEntry, WorkflowManifestEntry } from '../core/manifest/schema.ts'; +import type { PredicateContext, RuntimeKind } from './predicate-types.ts'; +import { evalPredicates } from './predicate-registry.ts'; + +/** + * Check if a workflow is available for the current runtime. + * This checks the availability flag only, not predicates. + */ +export function isWorkflowAvailableForRuntime( + workflow: WorkflowManifestEntry, + runtime: RuntimeKind, +): boolean { + if (runtime === 'daemon') { + return true; + } + return workflow.availability[runtime]; +} + +/** + * Check if a workflow is enabled (visible) for the current runtime context. + * Checks both availability flag and all predicates. + */ +export function isWorkflowEnabledForRuntime( + workflow: WorkflowManifestEntry, + ctx: PredicateContext, +): boolean { + // Check availability flag first + if (!isWorkflowAvailableForRuntime(workflow, ctx.runtime)) { + return false; + } + + // Then check predicates + return evalPredicates(workflow.predicates, ctx); +} + +/** + * Check if a tool is available for the current runtime. + * This checks the availability flag only, not predicates. + */ +export function isToolAvailableForRuntime(tool: ToolManifestEntry, runtime: RuntimeKind): boolean { + if (runtime === 'daemon') { + return true; + } + return tool.availability[runtime]; +} + +/** + * Check if a tool is exposed (visible) for the current runtime context. + * Checks both availability flag and all predicates. + */ +export function isToolExposedForRuntime(tool: ToolManifestEntry, ctx: PredicateContext): boolean { + // Check availability flag first + if (!isToolAvailableForRuntime(tool, ctx.runtime)) { + return false; + } + + // Then check predicates + return evalPredicates(tool.predicates, ctx); +} + +/** + * Check if a tool within a workflow is exposed. + * Both the workflow and tool must be enabled for the tool to be exposed. + */ +export function isToolInWorkflowExposed( + tool: ToolManifestEntry, + workflow: WorkflowManifestEntry, + ctx: PredicateContext, +): boolean { + // Workflow must be enabled + if (!isWorkflowEnabledForRuntime(workflow, ctx)) { + return false; + } + + // Tool must be exposed + return isToolExposedForRuntime(tool, ctx); +} + +/** + * Filter tools based on exposure rules. + */ +export function filterExposedTools( + tools: ToolManifestEntry[], + ctx: PredicateContext, +): ToolManifestEntry[] { + return tools.filter((tool) => isToolExposedForRuntime(tool, ctx)); +} + +/** + * Filter workflows based on exposure rules. + */ +export function filterEnabledWorkflows( + workflows: WorkflowManifestEntry[], + ctx: PredicateContext, +): WorkflowManifestEntry[] { + return workflows.filter((workflow) => isWorkflowEnabledForRuntime(workflow, ctx)); +} + +/** + * Get default-enabled workflows (used when no workflows are explicitly selected). + */ +export function getDefaultEnabledWorkflows( + workflows: WorkflowManifestEntry[], +): WorkflowManifestEntry[] { + return workflows.filter((wf) => wf.selection?.mcp?.defaultEnabled === true); +} + +/** + * Get auto-include workflows (included when their predicates pass). + */ +export function getAutoIncludeWorkflows( + workflows: WorkflowManifestEntry[], + ctx: PredicateContext, +): WorkflowManifestEntry[] { + return workflows.filter( + (wf) => wf.selection?.mcp?.autoInclude === true && isWorkflowEnabledForRuntime(wf, ctx), + ); +} + +/** + * Select workflows for MCP runtime according to the manifest-driven selection rules. + * + * Selection logic: + * 1. Include auto-include workflows whose predicates pass + * 2. If user specified workflows, include those + * 3. If no workflows specified, include default-enabled workflows + * 4. Filter all by availability + predicates + */ +export function selectWorkflowsForMcp( + allWorkflows: WorkflowManifestEntry[], + requestedWorkflowIds: string[] | undefined, + ctx: PredicateContext, +): WorkflowManifestEntry[] { + const selectedIds = new Set(); + + // 1. Include auto-include workflows whose predicates pass + for (const wf of getAutoIncludeWorkflows(allWorkflows, ctx)) { + selectedIds.add(wf.id); + } + + // 2/3. Include requested or default-enabled workflows + if (requestedWorkflowIds && requestedWorkflowIds.length > 0) { + for (const id of requestedWorkflowIds) { + selectedIds.add(id); + } + } else { + for (const wf of getDefaultEnabledWorkflows(allWorkflows)) { + selectedIds.add(wf.id); + } + } + + // Build final list from selected IDs + const selected = allWorkflows.filter((wf) => selectedIds.has(wf.id)); + + // 4. Filter by availability + predicates + return filterEnabledWorkflows(selected, ctx); +} diff --git a/src/visibility/index.ts b/src/visibility/index.ts new file mode 100644 index 00000000..3df35b66 --- /dev/null +++ b/src/visibility/index.ts @@ -0,0 +1,7 @@ +/** + * Visibility system exports. + */ + +export * from './predicate-types.ts'; +export * from './predicate-registry.ts'; +export * from './exposure.ts'; diff --git a/src/visibility/predicate-registry.ts b/src/visibility/predicate-registry.ts new file mode 100644 index 00000000..f9ba366f --- /dev/null +++ b/src/visibility/predicate-registry.ts @@ -0,0 +1,100 @@ +/** + * Predicate registry for tool/workflow visibility filtering. + * YAML manifests reference predicate names; this registry provides the implementations. + */ + +import type { PredicateFn, PredicateContext } from './predicate-types.ts'; + +/** + * Registry of named predicate functions. + * All predicates return true to show the tool/workflow, false to hide. + */ +export const PREDICATES: Record = { + /** + * Show only when debug mode is enabled in config. + */ + debugEnabled: (ctx: PredicateContext): boolean => ctx.config.debug, + + /** + * Show only when experimental workflow discovery is enabled. + */ + experimentalWorkflowDiscoveryEnabled: (ctx: PredicateContext): boolean => + ctx.config.experimentalWorkflowDiscovery, + + /** + * Show only when running under Xcode's coding agent. + * Use for tools/workflows that require the Xcode environment. + */ + runningUnderXcodeAgent: (ctx: PredicateContext): boolean => ctx.runningUnderXcode === true, + + /** + * Show only for MCP runtime. + * Use for MCP-only gateway tools that should not appear in CLI workflows. + */ + mcpRuntimeOnly: (ctx: PredicateContext): boolean => ctx.runtime === 'mcp', + + /** + * Hide when running inside Xcode's coding agent. + * Use for XcodeBuildMCP tools that conflict with Xcode's native equivalents. + */ + hideWhenXcodeAgentMode: (ctx: PredicateContext): boolean => !ctx.runningUnderXcode, + + /** + * Show only when Xcode auto-sync is disabled AND running under Xcode. + * Use for the manual sync tool that should appear when automatic sync is turned off. + */ + xcodeAutoSyncDisabled: (ctx: PredicateContext): boolean => + ctx.runningUnderXcode === true && ctx.config.disableXcodeAutoSync === true, + + /** + * Always visible - useful for explicit documentation in YAML. + */ + always: (): boolean => true, + + /** + * Never visible - useful for temporarily disabling tools. + */ + never: (): boolean => false, +}; + +/** + * Evaluate a list of predicate names against a context. + * All predicates must pass (AND logic) for the result to be true. + * + * @param names - Array of predicate names to evaluate + * @param ctx - Predicate context + * @returns true if all predicates pass, false if any fails + * @throws Error if an unknown predicate name is referenced + */ +export function evalPredicates(names: string[] | undefined, ctx: PredicateContext): boolean { + if (!names || names.length === 0) { + return true; + } + + for (const name of names) { + const fn = PREDICATES[name]; + if (!fn) { + throw new Error( + `Unknown predicate '${name}'. Available predicates: ${Object.keys(PREDICATES).join(', ')}`, + ); + } + if (!fn(ctx)) { + return false; + } + } + return true; +} + +/** + * Get all available predicate names. + */ +export function getPredicateNames(): string[] { + return Object.keys(PREDICATES); +} + +/** + * Check if a predicate name is valid. + */ +export function isValidPredicate(name: string): boolean { + return name in PREDICATES; +} diff --git a/src/visibility/predicate-types.ts b/src/visibility/predicate-types.ts new file mode 100644 index 00000000..323a4143 --- /dev/null +++ b/src/visibility/predicate-types.ts @@ -0,0 +1,32 @@ +/** + * Predicate context and type definitions for visibility filtering. + * Predicates are named functions that determine tool/workflow visibility + * based on runtime context. + */ + +import type { ResolvedRuntimeConfig } from '../utils/config-store.ts'; + +/** + * Runtime kind for predicate evaluation. + */ +export type RuntimeKind = 'cli' | 'mcp' | 'daemon'; + +/** + * Context passed to predicate functions for visibility evaluation. + */ +export interface PredicateContext { + /** Current runtime mode */ + runtime: RuntimeKind; + + /** Resolved runtime configuration */ + config: ResolvedRuntimeConfig; + + /** Whether running under Xcode agent environment */ + runningUnderXcode: boolean; +} + +/** + * Predicate function type. + * Returns true if the tool/workflow should be visible, false to hide. + */ +export type PredicateFn = (ctx: PredicateContext) => boolean; diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..360df4d2 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "allowImportingTsExtensions": false, + "declaration": true, + "declarationMap": true, + "outDir": "./build", + "rootDir": "./src" + }, + "include": [ + "src/**/*", + "src/core/generated-plugins.ts", + "src/core/generated-resources.ts" + ], + "exclude": [ + "node_modules", + "**/*.test.ts", + "**/__tests__/**", + "tests-vitest/**/*", + "plugins/**/*" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 8727b3a5..08cc02f8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "forceConsistentCasingInFileNames": true, "sourceMap": true, "inlineSources": true, + "verbatimModuleSyntax": true, // Set `sourceRoot` to "/" to strip the build path prefix // from generated source code references. diff --git a/tsconfig.tests.json b/tsconfig.tests.json new file mode 100644 index 00000000..322f2bdf --- /dev/null +++ b/tsconfig.tests.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "types": ["vitest/globals", "node"] + }, + "include": ["src/**/*.test.ts", "tests-vitest/**/*.ts"], + "exclude": [ + "node_modules", + "plugins/**/*", + "src/core/generated-plugins.ts", + "src/core/generated-resources.ts" + ] +} diff --git a/tsup.config.ts b/tsup.config.ts index 304b59e3..600fec47 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,37 +1,61 @@ import { defineConfig } from 'tsup'; -import { chmodSync, existsSync } from 'fs'; -import { createPluginDiscoveryPlugin } from './build-plugins/plugin-discovery.js'; +import { chmodSync, existsSync, readdirSync, readFileSync, writeFileSync } from 'fs'; +import { glob } from 'glob'; +import { join } from 'path'; + +/** + * Recursively rewrites .ts imports to .js in all JavaScript files. + * Required because Node.js cannot resolve .ts extensions at runtime. + */ +function rewriteTsImportsInDir(dir: string): void { + const entries = readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + rewriteTsImportsInDir(fullPath); + } else if (entry.name.endsWith('.js')) { + let content = readFileSync(fullPath, 'utf-8'); + // Rewrite: import ... from "./path.ts" or import ... from "../path.ts" + // Also handles: export ... from "./path.ts" + const rewritten = content.replace( + /((?:import|export)[^'"]*['"])([^'"]+)(\.ts)(['"])/g, + '$1$2.js$4', + ); + if (rewritten !== content) { + writeFileSync(fullPath, rewritten, 'utf-8'); + } + } + } +} export default defineConfig({ - entry: { - index: 'src/index.ts', - 'doctor-cli': 'src/doctor-cli.ts', - }, + // Include all TypeScript files for unbundled output + entry: await glob('src/**/*.ts', { + ignore: ['**/*.test.ts', '**/__tests__/**'], + }), format: ['esm'], target: 'node18', platform: 'node', outDir: 'build', clean: true, - sourcemap: true, // Enable source maps for debugging - dts: { - entry: { - index: 'src/index.ts', - }, - }, + sourcemap: true, + dts: false, // Skip declaration files for speed + bundle: false, // UNBUNDLED: Output individual files splitting: false, shims: false, - treeshake: true, + treeshake: false, // Disable treeshake for unbundled minify: false, - esbuildPlugins: [createPluginDiscoveryPlugin()], onSuccess: async () => { + // Rewrite .ts imports to .js in all output files + rewriteTsImportsInDir('build'); console.log('✅ Build complete!'); // Set executable permissions for built files - if (existsSync('build/index.js')) { - chmodSync('build/index.js', '755'); - } - if (existsSync('build/doctor-cli.js')) { - chmodSync('build/doctor-cli.js', '755'); + const executables = ['build/cli.js', 'build/doctor-cli.js', 'build/daemon.js']; + for (const file of executables) { + if (existsSync(file)) { + chmodSync(file, '755'); + } } }, -}); \ No newline at end of file +}); diff --git a/vitest.config.ts b/vitest.config.ts index 7736bd83..87ac5a2e 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ environment: 'node', globals: true, include: [ - 'src/**/__tests__/**/*.test.ts' // Only __tests__ directories + 'src/**/__tests__/**/*.test.ts', // Only __tests__ directories ], exclude: [ 'node_modules/**', @@ -19,16 +19,17 @@ export default defineConfig({ '**/full-output.txt', '**/experiments/**', '**/__pycache__/**', - '**/dist/**' + '**/dist/**', + 'src/smoke-tests/**', ], pool: 'threads', poolOptions: { threads: { - maxThreads: 4 - } + maxThreads: 4, + }, }, env: { - NODE_OPTIONS: '--max-old-space-size=4096' + NODE_OPTIONS: '--max-old-space-size=4096', }, testTimeout: 30000, hookTimeout: 10000, @@ -42,14 +43,14 @@ export default defineConfig({ 'tests/**', 'example_projects/**', '**/*.config.*', - '**/*.d.ts' - ] - } + '**/*.d.ts', + ], + }, }, resolve: { alias: { // Handle .js imports in TypeScript files - '^(\\.{1,2}/.*)\\.js$': '$1' - } - } -}); \ No newline at end of file + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + }, +}); diff --git a/vitest.smoke.config.ts b/vitest.smoke.config.ts new file mode 100644 index 00000000..2a881182 --- /dev/null +++ b/vitest.smoke.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + globals: true, + include: ['src/smoke-tests/__tests__/**/*.test.ts'], + pool: 'forks', + poolOptions: { + forks: { + maxForks: 1, + }, + }, + env: { + NODE_OPTIONS: '--max-old-space-size=4096', + }, + testTimeout: 60000, + hookTimeout: 30000, + teardownTimeout: 10000, + }, + resolve: { + alias: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + }, +});