[
  {
    "path": ".eslintignore",
    "content": "build\ncli/assets\ndist\ntypes\nandroid/capacitor/src/main/assets/native-bridge.js\nios/Capacitor/Capacitor/assets/native-bridge.js\nios/Frameworks/Capacitor.xcframework/ios-arm64_x86_64-simulator/Capacitor.framework/native-bridge.js\nios/Frameworks/Capacitor.xcframework/ios-arm64/Capacitor.framework/native-bridge.js\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: Bug Report\ndescription: File a bug report\ntitle: \"[Bug]: \"\nlabels: [\"triage\"]\ntype: \"bug\"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ## Thanks for taking the time to fill out this bug report!\n  - type: textarea\n    id: capacitor-version\n    attributes:\n      label: Capacitor Version\n      description: \"Paste the output from the `npx cap doctor` command into the box below\"\n      placeholder: \"Paste the output from the `npx cap doctor` here\"\n    validations:\n      required: true\n  - type: textarea\n    id: other-tech-details\n    attributes:\n      label: Other API Details\n      description: \"Please provide the following information with your request and any other relevant technical details (versions of IDEs, local environment info, plugin information or links, etc).\"\n      placeholder: |\n          npm --version output:\n          node --version output:\n          pod --version output (iOS Cocoapods issues only):\n      render: Shell\n  - type: checkboxes\n    id: platforms\n    attributes:\n      label: Platforms Affected\n      description: \"Check the platforms that this bug affects.\"\n      options:\n        - label: iOS\n          required: false\n        - label: Android\n          required: false\n        - label: Web\n          required: false\n  - type: textarea\n    id: current-behavior\n    attributes:\n      label: Current Behavior\n      description: \"Describe the bug in detail. The more detail the better.\"\n      placeholder: \"Tell us what you see!\"\n    validations:\n      required: true\n  - type: textarea\n    id: expected-behavior\n    attributes:\n      label: Expected Behavior\n      description: \"Describe what you expect to happen\"\n      placeholder: \"Tell us what you expect!\"\n    validations:\n      required: true\n  - type: input\n    id: reproduction\n    attributes:\n      label: Project Reproduction\n      description: \"Include a link to a repository we can access that reproduces the problem. Try to make it as simple as possible.\"\n    validations:\n      required: true\n  - type: textarea\n    id: additional-info\n    attributes:\n      label: Additional Information\n      description: \"Anything else we need to know to help? Include it here.\"\n      placeholder: \"Tell us anything!\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: ⚙️ Request a new plugin\n    url: https://github.com/capacitor-community/proposals\n    about: Propose a new plugin or platform to the Capacitor community\n  - name: Discuss issues on our forums\n    url: https://forum.ionicframework.com/\n    about: Ask questions and discuss with other community members\n  - name: Chat on our Discord\n    url: https://ionic.link/discord\n    about: Ask questions and chat with other community members\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "content": "name: Feature Request\ndescription: Request a new feature\ntitle: \"[Feature]: \"\nlabels: [\"triage\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ## Thanks for your input on enhancements to Capacitor!\n  - type: input\n    id: Description\n    attributes:\n      label: Description\n      description: \"A short description of your requested feature\"\n    validations:\n      required: true\n  - type: checkboxes\n    id: platforms\n    attributes:\n      label: Platforms\n      description: \"Check the platforms that will need this feature\"\n      options:\n        - label: iOS\n          required: false\n        - label: Android\n          required: false\n        - label: Web\n          required: false\n  - type: textarea\n    id: requested-feature\n    attributes:\n      label: Request or proposed solution\n      description: \"Describe the feature you are looking for in detail and any Preferred Solution you have\"\n      placeholder: \"This new thing would be great!\"\n    validations:\n      required: true\n  - type: textarea\n    id: alternatives\n    attributes:\n      label: Alternatives\n      description: \"Describe some alternatives, if any\"\n      placeholder: \"Some other ideas!\"\n    validations:\n      required: false\n  - type: textarea\n    id: additional-info\n    attributes:\n      label: Additional Information\n      description: \"Anything else we need to know to help? Include it here.\"\n      placeholder: \"Tell us anything!\"\n"
  },
  {
    "path": ".github/bot.yml",
    "content": "tasks:\n  - name: add-label\n    on:\n      issues:\n        types: [labeled]\n    condition: 'payload.label.name == \"needs reproduction\"'\n    config:\n      label: 'needs reply'\n  - name: remove-label\n    on:\n      issue_comment:\n        types: [created]\n    config:\n      label: 'needs reply'\n      exclude-labeler: true\n"
  },
  {
    "path": ".github/ionic-issue-bot.yml",
    "content": "triage:\n  label: triage\n  dryRun: false\n\nlockClosed:\n  days: 30\n  maxIssuesPerRun: 100\n  message: >\n    Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue.\n    If this is still an issue with the latest version of Capacitor, please create a new issue and ensure the template is fully filled out.\n  dryRun: false\n\ncomment:\n  labels:\n    - label: \"type: bug\"\n      message: >\n        This issue has been labeled as `type: bug`. This label is added to issues\n        that that have been reproduced and are being tracked in our internal issue tracker.\n    - label: \"needs reproduction\"\n      message: >\n        This issue needs more information before it can be addressed.\n        In particular, the reporter needs to provide a minimal sample app that demonstrates the issue.\n        If no sample app is provided within 7 days, the issue will be closed.\n\n        Please see the Contributing Guide for [how to create a Sample App](https://github.com/ionic-team/capacitor/blob/HEAD/CONTRIBUTING.md#creating-a-code-reproduction).\n\n        Thanks!\n        Ionitron 💙\n  dryRun: false\n\ncloseAndLock:\n  labels:\n    - label: \"support request\"\n      message: >\n        Thanks for the issue! This issue appears to be a support request. We use this issue tracker exclusively for\n        bug reports and feature requests. Please use our [forum](https://forum.ionicframework.com) or our community [Discord](https://ionic.link/discord) for questions about Capacitor.\n\n\n        Thank you for using Capacitor!\n  close: true\n  lock: true\n  dryRun: false\n\n"
  },
  {
    "path": ".github/workflows/capacitor-bot.yml",
    "content": "name: Capacitor Bot\n\non:\n  push:\n    branches: [main]\n  issues:\n    types: [opened, edited, labeled]\n  pull_request:\n    types: [opened]\n  issue_comment:\n    types: [created]\n\njobs:\n  bot:\n    name: ${{ github.event_name }}/${{ github.event.action }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - uses: ionic-team/bot@main\n        with:\n          repo-token: ${{ secrets.BOT_TOKEN }}\n        env:\n          GIT_AUTHOR_NAME: Ionitron\n          GIT_AUTHOR_EMAIL: hi@ionicframework.com\n          GIT_COMMITTER_NAME: Ionitron\n          GIT_COMMITTER_EMAIL: hi@ionicframework.com\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    types:\n      - 'synchronize'\n      - 'opened'\n    branches:\n      - '**'\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}\n\njobs:\n  setup:\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Get Latest\n        uses: actions/setup-node@v6\n        with:\n          node-version: 22.x\n      - uses: actions/checkout@v5\n      - name: Restore Dependency Cache\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.OS }}-dependencies-cache-${{ hashFiles('**/package.json') }}\n  lint:\n    runs-on: macos-15\n    timeout-minutes: 60\n    steps:\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22.x\n      - uses: actions/checkout@v5\n      - name: Restore Dependency Cache\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.OS }}-dependencies-cache-${{ hashFiles('**/package.json') }}\n      - run: npm install\n      - run: brew install swiftlint\n      - run: npm run lint\n  test-cli:\n    runs-on: macos-15\n    timeout-minutes: 60\n    needs:\n      - setup\n      - lint\n    steps:\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22.x\n      - uses: actions/checkout@v5\n      - name: Restore Dependency Cache\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.OS }}-dependencies-cache-${{ hashFiles('**/package.json') }}\n      - run: npm install\n      - run: npm run build\n        working-directory: ./cli\n      - run: npm test\n        working-directory: ./cli\n  test-core:\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    needs:\n      - setup\n      - lint\n    steps:\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22.x\n      - uses: actions/checkout@v5\n      - name: Restore Dependency Cache\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.OS }}-dependencies-cache-${{ hashFiles('**/package.json') }}\n      - run: npm install\n      - run: npm run build\n        working-directory: ./core\n      - run: npm test\n        working-directory: ./core\n  test-ios:\n    runs-on: macos-15\n    timeout-minutes: 60\n    needs:\n      - setup\n      - lint\n    strategy:\n      matrix:\n        xcode:\n          - /Applications/Xcode_26.0.app\n    steps:\n      - run: sudo xcode-select --switch ${{ matrix.xcode }}\n      - run: xcrun simctl list > /dev/null\n      - run: xcodebuild -downloadPlatform iOS\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22.x\n      - uses: actions/checkout@v5\n      - name: Restore Dependency Cache\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.OS }}-dependencies-cache-${{ hashFiles('**/package.json') }}\n      - run: npm install\n      - run: npm run verify\n        working-directory: ./ios\n      - name: Validate native podspec\n        run: sh ./scripts/native-podspec.sh lint\n  test-android:\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    needs:\n      - setup\n      - lint\n    steps:\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22.x\n      - name: set up JDK 21\n        uses: actions/setup-java@v5\n        with:\n          java-version: '21'\n          distribution: 'zulu'\n      - uses: actions/checkout@v5\n      - name: Restore Dependency Cache\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.OS }}-dependencies-cache-${{ hashFiles('**/package.json') }}\n      - run: npm install\n      - run: npm run verify\n        working-directory: ./android\n"
  },
  {
    "path": ".github/workflows/needs-reply.yml",
    "content": "name: Close old issues that need reply\n\non:\n  schedule:\n    - cron: \"0 0 * * *\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Close old issues that need reply\n        uses: imhoffd/needs-reply@v2\n        with:\n          repo-token: ${{ secrets.BOT_TOKEN }}\n          issue-label: 'needs reply'\n          days-before-close: 7\n          close-message: |\n            It looks like this issue didn't get the information it needed, so I'll close it for now. If I made a mistake, sorry! I am just a bot.\n\n            Have a great day!\n            Ionitron 💙\n"
  },
  {
    "path": ".github/workflows/publish-android.yml",
    "content": "name: Publish Native Android Library\n\non: \n  workflow_call:\n    secrets:\n      ANDROID_OSSRH_USERNAME:\n        required: true\n      ANDROID_OSSRH_PASSWORD:\n        required: true\n      ANDROID_SIGNING_KEY_ID:\n        required: true\n      ANDROID_SIGNING_PASSWORD:\n        required: true\n      ANDROID_SIGNING_KEY:\n        required: true\n      ANDROID_SONATYPE_STAGING_PROFILE_ID:\n        required: true\n      CAP_GH_RELEASE_TOKEN:\n        required: true\n  workflow_dispatch:\n\njobs:\n  publish-android:\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n    permissions:\n      contents: read\n      packages: write\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          ref: 'main'\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - name: set up JDK 21\n        uses: actions/setup-java@v5\n        with:\n          java-version: '21'\n          distribution: 'zulu'\n      - name: Grant execute permission for gradlew\n        run: chmod +x ./android/gradlew\n      - name: Grant execute permission for publishing script\n        run: chmod +x ./scripts/publish-android.sh\n      - name: Run publish script\n        working-directory: ./scripts\n        env:\n          ANDROID_OSSRH_USERNAME: ${{ secrets.ANDROID_OSSRH_USERNAME }}\n          ANDROID_OSSRH_PASSWORD: ${{ secrets.ANDROID_OSSRH_PASSWORD }}\n          ANDROID_SIGNING_KEY_ID: ${{ secrets.ANDROID_SIGNING_KEY_ID }}\n          ANDROID_SIGNING_PASSWORD: ${{ secrets.ANDROID_SIGNING_PASSWORD }}\n          ANDROID_SIGNING_KEY: ${{ secrets.ANDROID_SIGNING_KEY }}\n          ANDROID_SONATYPE_STAGING_PROFILE_ID: ${{ secrets.ANDROID_SONATYPE_STAGING_PROFILE_ID }}\n        run: ./publish-android.sh\n"
  },
  {
    "path": ".github/workflows/publish-ios.yml",
    "content": "name: Publish Native iOS Library\n\non:\n  workflow_call:\n    secrets:\n      COCOAPODS_TRUNK_TOKEN:\n        required: true\n  workflow_dispatch:\n\njobs:\n  publish-ios:\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - run: sudo xcode-select --switch /Applications/Xcode_26.0.app\n      - run: xcrun simctl list > /dev/null\n      - run: xcodebuild -downloadPlatform iOS\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22.x\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          ref: 'main'\n      - name: Install Cocoapods\n        run: gem install cocoapods\n      - name: Deploy to Cocoapods\n        run: sh ./scripts/native-podspec.sh publish\n        env:\n          COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/publish-latest.yml",
    "content": "name: Publish Latest\n\non: workflow_dispatch\n\npermissions:\n  contents: write\n  id-token: write\n  packages: write\n\njobs:\n  publish-npm-latest:\n    uses: ./.github/workflows/publish-npm-latest.yml\n    secrets:\n      CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n  publish-android:\n    needs: publish-npm-latest\n    uses: ./.github/workflows/publish-android.yml\n    secrets:\n      ANDROID_OSSRH_USERNAME: ${{ secrets.ANDROID_OSSRH_USERNAME }}\n      ANDROID_OSSRH_PASSWORD: ${{ secrets.ANDROID_OSSRH_PASSWORD }}\n      ANDROID_SIGNING_KEY_ID: ${{ secrets.ANDROID_SIGNING_KEY_ID }}\n      ANDROID_SIGNING_PASSWORD: ${{ secrets.ANDROID_SIGNING_PASSWORD }}\n      ANDROID_SIGNING_KEY: ${{ secrets.ANDROID_SIGNING_KEY }}\n      ANDROID_SONATYPE_STAGING_PROFILE_ID: ${{ secrets.ANDROID_SONATYPE_STAGING_PROFILE_ID }}\n      CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n  publish-ios:\n    needs: publish-npm-latest\n    uses: ./.github/workflows/publish-ios.yml\n    secrets:\n      COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/publish-npm-alpha.yml",
    "content": "name: Publish NPM Alpha\n\non: workflow_dispatch\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  deploy-npm-alpha:\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n          cache-dependency-path: '**/package.json'\n      - run: |\n          npm install --force\n      - name: Version & Publish\n        env:\n          GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n        run: |\n          git config user.name \"Github Workflow (on behalf of ${{ github.actor }})\"\n          git config user.email \"users.noreply.github.com\"\n          npm whoami\n          npm run ci:publish:alpha\n"
  },
  {
    "path": ".github/workflows/publish-npm-beta.yml",
    "content": "name: Publish NPM Beta\n\non: workflow_dispatch\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  deploy-npm-beta:\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n          cache-dependency-path: '**/package.json'\n      - run: |\n          npm install\n      - name: Version & Publish\n        env:\n          GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n        run: |\n          git config user.name \"Github Workflow (on behalf of ${{ github.actor }})\"\n          git config user.email \"users.noreply.github.com\"\n          npm whoami\n          npm run ci:publish:beta\n"
  },
  {
    "path": ".github/workflows/publish-npm-dev.yml",
    "content": "name: Publish NPM Dev\n\non: workflow_dispatch\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  deploy-npm-dev:\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n          cache-dependency-path: '**/package.json'\n      - name: NPM Install\n        run: |\n          npm install\n      - name: Version & Publish\n        env:\n          GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n        run: |\n          git config user.name \"Github Workflow (on behalf of ${{ github.actor }})\"\n          git config user.email \"users.noreply.github.com\"\n          echo $GITHUB_REF_NAME\n          npm whoami\n          npx lerna version prerelease --conventional-commits --conventional-prerelease --preid dev-$(date +\"%Y%m%dT%H%M%S\") --allow-branch $GITHUB_REF_NAME --force-publish --no-changelog --no-git-tag-version --no-push --yes\n          npx lerna exec -- npm publish --tag dev --provenance\n"
  },
  {
    "path": ".github/workflows/publish-npm-latest-from-pre.yml",
    "content": "name: Publish NPM Latest From Prerelease\n\non:\n  workflow_call:\n    secrets:\n      CAP_GH_RELEASE_TOKEN:\n        required: true\n      NPM_TOKEN:\n        required: true\n  workflow_dispatch:\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  deploy-npm-latest:\n    if: github.ref == 'refs/heads/main'\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n          cache-dependency-path: '**/package.json'\n      - run: |\n          npm install\n      - name: Version & Publish\n        env:\n          GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n        run: |\n          git config user.name \"Github Workflow (on behalf of ${{ github.actor }})\"\n          git config user.email \"users.noreply.github.com\"\n          npm whoami\n          npm run ci:publish:latest-from-pre\n"
  },
  {
    "path": ".github/workflows/publish-npm-latest.yml",
    "content": "name: Publish NPM Latest\n\non:\n  workflow_call:\n    secrets:\n      CAP_GH_RELEASE_TOKEN:\n        required: true\n      NPM_TOKEN:\n        required: true\n  workflow_dispatch:\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  deploy-npm-latest:\n    if: github.ref == 'refs/heads/main'\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n          cache-dependency-path: '**/package.json'\n      - run: |\n          npm install\n      - name: Version & Publish\n        env:\n          GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n        run: |\n          git config user.name \"Github Workflow (on behalf of ${{ github.actor }})\"\n          git config user.email \"users.noreply.github.com\"\n          npm whoami\n          npm run ci:publish:latest\n"
  },
  {
    "path": ".github/workflows/publish-npm-nightly.yml",
    "content": "name: Publish NPM Nightly\n\non:\n schedule:\n   - cron: '0 15 * * 1-5'\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  deploy-npm-nightly:\n    if: github.ref == 'refs/heads/main'\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n          cache-dependency-path: '**/package.json'\n      - run: |\n          npm install\n      - name: Version & Publish\n        env:\n          GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n        run: |\n          git config user.name \"Github Workflow (on behalf of ${{ github.actor }})\"\n          git config user.email \"users.noreply.github.com\"\n          npm whoami\n          npm run ci:publish:nightly\n"
  },
  {
    "path": ".github/workflows/publish-npm-rc.yml",
    "content": "name: Publish NPM RC\n\non: workflow_dispatch\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  deploy-npm-rc:\n    runs-on: macos-15\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n          cache-dependency-path: '**/package.json'\n      - run: |\n          npm install\n      - name: Version & Publish\n        env:\n          GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n        run: |\n          git config user.name \"Github Workflow (on behalf of ${{ github.actor }})\"\n          git config user.email \"users.noreply.github.com\"\n          npm whoami\n          npm run ci:publish:rc\n"
  },
  {
    "path": ".github/workflows/publish-spm-release.yaml",
    "content": "name: Publish SPM Release\n\non:\n  push:\n    tags:\n      - \"*\"\n\njobs:\n  publish-spm:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Assign Tag Number to RELEASE_VERSION environment variable\n        run: echo \"RELEASE_VERSION=${GITHUB_REF#refs/*/}\" >> $GITHUB_ENV\n      - name: Dispatch Release Workflow on ionic-team/capacitor-swift-pm\n        run: gh workflow run dispatch-release.yaml -f release-version=${{ env.RELEASE_VERSION }} --repo ionic-team/capacitor-swift-pm\n        env:\n          GITHUB_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\ndist/\ntypes/\n.sourcemaps\nxcuserdata/\nnode_modules/\nPods/\n*.map\n.DS_Store\nPodfile.lock\nBuild/*\nbuild/\nIndex/\n.*.sw*\nandroid-template.iml\n!/build/.npmkeep\nlerna-debug.log\nlocal.properties\ncontents.xcworkspacedata\nandroid-template/.gradle/\nandroid-template/app/app.iml\n.gradle/\n.settings/\n.project\n.env\ncapacitor-build.log\n\n# Carthage and Carthage-built binaries\nCarthage\n*.xcframework.zip\n"
  },
  {
    "path": ".npmrc",
    "content": "package-lock=false"
  },
  {
    "path": ".prettierignore",
    "content": "build\ncore/types\ncli/assets\ndist\nandroid/capacitor/src/main/assets/native-bridge.js\nios/Capacitor/Capacitor/assets/native-bridge.js\nios/Frameworks/Capacitor.xcframework/ios-arm64_x86_64-simulator/Capacitor.framework/native-bridge.js\nios/Frameworks/Capacitor.xcframework/ios-arm64/Capacitor.framework/native-bridge.js\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Debug CLI cli/dist/**.js (only works with js files in the cli/dist folder)\",\n      \"skipFiles\": [\n        \"<node_internals>/**\"\n      ],\n      \"program\": \"${workspaceFolder}/cli/bin/capacitor\",\n      \"args\": [\"${input:capacitorCommand}\"],\n      \"outFiles\": [\n        \"${workspaceFolder}/cli/dist/**/*.js\"\n      ],\n      \"sourceMaps\": true,\n      \"console\": \"integratedTerminal\",\n      \"internalConsoleOptions\": \"neverOpen\",\n      \"cwd\": \"${input:projectPath}\",\n      \"env\": {\n        \"CLI_PATH\": \"${workspaceFolder}/cli\",\n        \"CAPACITOR_CLI_PATH\": \"${workspaceFolder}\",\n      }\n    }\n  ],\n  \"inputs\": [\n    {\n      \"id\": \"capacitorCommand\",\n      \"type\": \"promptString\",\n      \"description\": \"Capacitor CLI command to run (e.g. 'doctor')\",\n      \"default\": \"doctor\"\n    },\n    {\n      \"id\": \"projectPath\",\n      \"type\": \"promptString\",\n      \"description\": \"Path to the project where you want to run the command\",\n      \"default\": \"${env:HOME}/repos/capacitor-testapp\"\n    }\n  ]\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n# [8.2.0](https://github.com/ionic-team/capacitor/compare/8.1.0...8.2.0) (2026-03-06)\n\n### Bug Fixes\n\n- **android:** Add missing null checks in BridgeActivity ([#8185](https://github.com/ionic-team/capacitor/issues/8185)) ([bd29b99](https://github.com/ionic-team/capacitor/commit/bd29b9913a9279de26fc21c6cb0b93b8f5e5433a))\n- **android:** Concurrent Range Requests for assets ([#8357](https://github.com/ionic-team/capacitor/issues/8357)) ([5e82c89](https://github.com/ionic-team/capacitor/commit/5e82c89f1bff6d0e9ccea2554007aacb920d4c58))\n- **android:** handle lowercase range header ([#8368](https://github.com/ionic-team/capacitor/issues/8368)) ([ae0e2dd](https://github.com/ionic-team/capacitor/commit/ae0e2ddccb2904ee4b3d47d4be1f7556ac7000a1))\n- **android:** invalid http range seeking ([#8369](https://github.com/ionic-team/capacitor/issues/8369)) ([3109d22](https://github.com/ionic-team/capacitor/commit/3109d22547253ed44293777c60652f14cf83e416))\n- **cli:** Allow to run update on non macOS ([#8344](https://github.com/ionic-team/capacitor/issues/8344)) ([a441280](https://github.com/ionic-team/capacitor/commit/a441280d7c6b310ca516d6fb2736c09525987774))\n- **cli:** Don't overwrite config.server section with `--live-reload` ([#7528](https://github.com/ionic-team/capacitor/issues/7528)) ([782b9d9](https://github.com/ionic-team/capacitor/commit/782b9d9c26dcf1282b918996becb0224c0baca1d))\n- **cli:** use 8.0.0 as default Capacitor SPM dependency version ([#8341](https://github.com/ionic-team/capacitor/issues/8341)) ([a55dc5e](https://github.com/ionic-team/capacitor/commit/a55dc5ee4dfeab861cde1e11c9063aefea91006b))\n- **docs:** fix typo in CapApp-SPM README ([#8348](https://github.com/ionic-team/capacitor/issues/8348)) ([7d001ac](https://github.com/ionic-team/capacitor/commit/7d001ac4c58757fba922ea50f5bf5233ce217490))\n- **ios:** remove tmpWindow usages on presentVC/dismissVC ([#8338](https://github.com/ionic-team/capacitor/issues/8338)) ([fc9647f](https://github.com/ionic-team/capacitor/commit/fc9647f26f08ff64f53b32c79fb19f153e3b0a24))\n\n### Features\n\n- **cli:** Add --https option for --live-reload ([#8194](https://github.com/ionic-team/capacitor/issues/8194)) ([5db81e6](https://github.com/ionic-team/capacitor/commit/5db81e68c67652e9d2b29d7ad30629b423d2ad30))\n\n# [8.1.0](https://github.com/ionic-team/capacitor/compare/8.0.2...8.1.0) (2026-02-11)\n\n### Bug Fixes\n\n- **cookies:** only send expires param on web if a date is set ([b10cd7f](https://github.com/ionic-team/capacitor/commit/b10cd7ff15b010a76802374214f4e0cbd04abdab))\n\n### Features\n\n- **cli:** Add packageManager to iOS config ([#8321](https://github.com/ionic-team/capacitor/issues/8321)) ([a125498](https://github.com/ionic-team/capacitor/commit/a1254983bbb9dcb273d93f3c5f639b792e516406))\n\n### Reverts\n\n- revert version bump from [#8319](https://github.com/ionic-team/capacitor/issues/8319) and [#8320](https://github.com/ionic-team/capacitor/issues/8320) ([a48ebb6](https://github.com/ionic-team/capacitor/commit/a48ebb622ea4ebe92927bf1756a4d8ac6012884b))\n\n## [8.0.2](https://github.com/ionic-team/capacitor/compare/8.0.1...8.0.2) (2026-01-27)\n\n### Bug Fixes\n\n- **android:** AGP 9.0 no longer supports `proguard-android.txt` ([#8315](https://github.com/ionic-team/capacitor/issues/8315)) ([dcc76c3](https://github.com/ionic-team/capacitor/commit/dcc76c37508a9b71fb36d95707748c2dd115cf52))\n- **cli:** Update tar package ([#8311](https://github.com/ionic-team/capacitor/issues/8311)) ([0969c5c](https://github.com/ionic-team/capacitor/commit/0969c5cd0b16cf23d2489a85a3b8fa1bee2ebf3b))\n- **core:** make SystemBars hide and show options optional ([#8305](https://github.com/ionic-team/capacitor/issues/8305)) ([95dc7d8](https://github.com/ionic-team/capacitor/commit/95dc7d8ace3aabdda7e325c4a8ef7d1432ad37e9))\n- **SystemBars:** get correct style on handleOnConfigurationChanged ([#8295](https://github.com/ionic-team/capacitor/issues/8295)) ([2a66b44](https://github.com/ionic-team/capacitor/commit/2a66b44915895f971e4a26a8612798aa2f95ea11))\n- **SystemBars:** Set window background color according to theme ([#8306](https://github.com/ionic-team/capacitor/issues/8306)) ([6037e38](https://github.com/ionic-team/capacitor/commit/6037e3836ec24c9ddf26e74e5fbec20ab506adfd))\n- **SystemBars:** Skipping margin manipulation when on a fixed WebView ([#8309](https://github.com/ionic-team/capacitor/issues/8309)) ([53c33b6](https://github.com/ionic-team/capacitor/commit/53c33b614218bf635322fbdf9a38038a7964e3d4))\n\n## [8.0.1](https://github.com/ionic-team/capacitor/compare/8.0.0...8.0.1) (2026-01-13)\n\n### Bug Fixes\n\n- **android:** Remove calculated bottom inset if keyboard is visible ([#8280](https://github.com/ionic-team/capacitor/issues/8280)) ([196b642](https://github.com/ionic-team/capacitor/commit/196b642236d293a5012e3c307fcd942766e56cce))\n- **cli:** Support wireless iOS devices in `cap run` ([#8301](https://github.com/ionic-team/capacitor/issues/8301)) ([dcb368c](https://github.com/ionic-team/capacitor/commit/dcb368c33555487f1b6f46a6d8e30736bdd78955))\n- **cli:** use latest native-run ([#8296](https://github.com/ionic-team/capacitor/issues/8296)) ([121d830](https://github.com/ionic-team/capacitor/commit/121d83013f39e25009121533a2c3ad86e428d6b2))\n\n# [8.0.0](https://github.com/ionic-team/capacitor/compare/8.0.0-beta.0...8.0.0) (2025-12-08)\n\n### Bug Fixes\n\n- **cli:** Android apk name multi flavor dimensions parsing ([#7598](https://github.com/ionic-team/capacitor/issues/7598)) ([2dc20ee](https://github.com/ionic-team/capacitor/commit/2dc20ee894c2f5761431caa8269e9f418fc222b6))\n- **cli:** make migrate update to 8.0.0 ([#8250](https://github.com/ionic-team/capacitor/issues/8250)) ([ee8ba7b](https://github.com/ionic-team/capacitor/commit/ee8ba7bbeeeef6db0461f7a59c91095fd7cbd78b))\n- **ios:** move PrivacyInfo.xcprivacy to resource_bundles to fix build… ([#8264](https://github.com/ionic-team/capacitor/issues/8264)) ([e6f50b8](https://github.com/ionic-team/capacitor/commit/e6f50b8c0c41acaaa21af373e195751508c37e9d))\n\n### Features\n\n- **android:** Improving SystemBars inset handling ([#8268](https://github.com/ionic-team/capacitor/issues/8268)) ([81ae30a](https://github.com/ionic-team/capacitor/commit/81ae30a503797e417dd125b06262dabc4696c88a))\n\n# [8.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.3...8.0.0-beta.0) (2025-11-14)\n\n### Bug Fixes\n\n- **android-template:** replace deprecated Gradle property name syntax ([#8204](https://github.com/ionic-team/capacitor/issues/8204)) ([e48694b](https://github.com/ionic-team/capacitor/commit/e48694b9e1ff1d3f2de2f8b140cbbeb280d6a21d))\n- **android:** add command not changing namespace ([#8224](https://github.com/ionic-team/capacitor/issues/8224)) ([60cf666](https://github.com/ionic-team/capacitor/commit/60cf66675159fb09cd41c94ce657202d01e05c74))\n- **android:** remove kotlin-bom dependency ([#8237](https://github.com/ionic-team/capacitor/issues/8237)) ([a894749](https://github.com/ionic-team/capacitor/commit/a89474920569d404e3b19e22e782e656d5b59bdd))\n- **android:** replace deprecated Gradle property name syntax ([b68ac9e](https://github.com/ionic-team/capacitor/commit/b68ac9e2dfc3ed292b7879bd7aeaab3d05ac84d5))\n- **cli:** fix cap run command for yarn pnp mode ([#7754](https://github.com/ionic-team/capacitor/issues/7754)) ([79ace5c](https://github.com/ionic-team/capacitor/commit/79ace5c7054d86d5b33ce325751ff738c63d4b0c))\n- **cli:** replace deprecated Gradle property name syntax ([#8207](https://github.com/ionic-team/capacitor/issues/8207)) ([3912030](https://github.com/ionic-team/capacitor/commit/3912030491a4b252bfd344eb708409dc1a3737e6))\n- **ios:** use ReturnPromise for SystemBars returnType ([#8239](https://github.com/ionic-team/capacitor/issues/8239)) ([eb51288](https://github.com/ionic-team/capacitor/commit/eb5128866953281dc7bed88bd734fc3804b3a702))\n- make Plugin.resolve act consistently ([#8225](https://github.com/ionic-team/capacitor/issues/8225)) ([06aeb9e](https://github.com/ionic-team/capacitor/commit/06aeb9e85d162d6be9d96820edcb2008cd74da84))\n\n### Features\n\n- **android-template:** Update com.google.gms:google-service version ([#8195](https://github.com/ionic-team/capacitor/issues/8195)) ([ba05dd3](https://github.com/ionic-team/capacitor/commit/ba05dd3da26dbe807e7405f98f4160ba6aea73ce))\n- **cli:** Select a cap run target by target name ([#8199](https://github.com/ionic-team/capacitor/issues/8199)) ([b30c472](https://github.com/ionic-team/capacitor/commit/b30c472490117a5dbb14a9556ef1b6ca6e39b45b))\n- **iOS:** Allow plugins to hook into handling WebView URL authentication challenges ([#8216](https://github.com/ionic-team/capacitor/issues/8216)) ([e8507cf](https://github.com/ionic-team/capacitor/commit/e8507cfe4d93337ddee8ab6597aab5209ebb4858))\n- System Bars Plugin ([#8180](https://github.com/ionic-team/capacitor/issues/8180)) ([a32216a](https://github.com/ionic-team/capacitor/commit/a32216ac0607172a3a9c7ae5cdbfc598769294a6))\n\n# [8.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.2...8.0.0-alpha.3) (2025-10-22)\n\n### Bug Fixes\n\n- **cli:** change SPM incompatible plugin message ([#8129](https://github.com/ionic-team/capacitor/issues/8129)) ([28c2a96](https://github.com/ionic-team/capacitor/commit/28c2a96d898b07a05ae763f798e8903601cc3f67))\n- **cli:** Don't exclude Cordova plugins from SPM management ([#8140](https://github.com/ionic-team/capacitor/issues/8140)) ([69bbd3d](https://github.com/ionic-team/capacitor/commit/69bbd3dc59cd6f56252c06f23b351952af8661be))\n- **cli:** prefer studio executable over studio.sh on linux if available ([#8110](https://github.com/ionic-team/capacitor/issues/8110)) ([62bd16f](https://github.com/ionic-team/capacitor/commit/62bd16f675cb492b52c0b73483fddfd63c608189))\n- **ios:** also call bridge.reset() on webViewWebContentProcessDidTerminate ([#8143](https://github.com/ionic-team/capacitor/issues/8143)) ([1de1f15](https://github.com/ionic-team/capacitor/commit/1de1f157169bc0e23060ffabf044c6d391b98efe))\n- **ios:** Remove Cordova UIView extension ([#8189](https://github.com/ionic-team/capacitor/issues/8189)) ([0c7bcd3](https://github.com/ionic-team/capacitor/commit/0c7bcd3d10f3e3d5a1259f5096879771f8e26436))\n- **ios:** replace deprecation warnings ([#8156](https://github.com/ionic-team/capacitor/issues/8156)) ([e76b29b](https://github.com/ionic-team/capacitor/commit/e76b29b77a7d71ef2341dd3aa530c5d9f291a941))\n- **ios:** Silence WKProcessPool warning ([#8184](https://github.com/ionic-team/capacitor/issues/8184)) ([b6abcb7](https://github.com/ionic-team/capacitor/commit/b6abcb7d656ee24e48a5d0dc7b68888b0318fe5d))\n\n# [8.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.1...8.0.0-alpha.2) (2025-08-20)\n\n**Note:** Version bump only for package capacitor\n\n# [8.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/7.4.2...8.0.0-alpha.1) (2025-08-20)\n\n### Bug Fixes\n\n- http content headers not sent when using axios ([#8039](https://github.com/ionic-team/capacitor/issues/8039)) ([67cac40](https://github.com/ionic-team/capacitor/commit/67cac40660b3e8cc78d1d228b7c6915450948ef1))\n\n### Features\n\n- **android-template:** Bump minSdkVersion to 24 ([#8080](https://github.com/ionic-team/capacitor/issues/8080)) ([c5f9ea9](https://github.com/ionic-team/capacitor/commit/c5f9ea9db85bcf0eaf5d3c67060f10092c434e23))\n- **android:** Bump default minSdkVersion to 24 ([#8083](https://github.com/ionic-team/capacitor/issues/8083)) ([c022ddf](https://github.com/ionic-team/capacitor/commit/c022ddf21452379bb183ff7c846f73bd07a15dbf))\n- **ios-template:** bump deployment target to 15 ([#8088](https://github.com/ionic-team/capacitor/issues/8088)) ([7a0c09a](https://github.com/ionic-team/capacitor/commit/7a0c09a55428aef3f5af4a59eede70be0b741aa8))\n\n## [7.4.2](https://github.com/ionic-team/capacitor/compare/7.4.1...7.4.2) (2025-07-10)\n\n### Bug Fixes\n\n- **android:** consider display cutout area for insets ([#8042](https://github.com/ionic-team/capacitor/issues/8042)) ([b478211](https://github.com/ionic-team/capacitor/commit/b4782116856c35e3fb567393f10a36ce4632b44c))\n- **http:** Properly URL-encode key and values during `x-www-form-urlencoded` POSTs ([#8037](https://github.com/ionic-team/capacitor/issues/8037)) ([87b4641](https://github.com/ionic-team/capacitor/commit/87b4641d1fa32b78e6fc2e87ee7b2c49b625b213))\n\n## [7.4.1](https://github.com/ionic-team/capacitor/compare/7.4.0...7.4.1) (2025-07-03)\n\n**Note:** Version bump only for package capacitor\n\n# [7.4.0](https://github.com/ionic-team/capacitor/compare/7.3.0...7.4.0) (2025-06-18)\n\n### Features\n\n- **cli:** add spm-migration-assistant command ([#7963](https://github.com/ionic-team/capacitor/issues/7963)) ([ef42722](https://github.com/ionic-team/capacitor/commit/ef427225f63056b1731b3cd8941aea2311d3bc4d))\n- **cli:** fail cap update on uninstalled cordova dependencies ([#8032](https://github.com/ionic-team/capacitor/issues/8032)) ([b393de8](https://github.com/ionic-team/capacitor/commit/b393de8bc9e68aff2cb70428a7c86c98367feade))\n\n# [7.3.0](https://github.com/ionic-team/capacitor/compare/7.2.0...7.3.0) (2025-06-05)\n\n### Bug Fixes\n\n- add error code checking to `runPlatformHook` ([#7994](https://github.com/ionic-team/capacitor/issues/7994)) ([8717680](https://github.com/ionic-team/capacitor/commit/871768049187afe9f5d358d3fe08b6f18f70533e))\n- **cli:** Don't downgrade deployment target on migrate ([#7953](https://github.com/ionic-team/capacitor/issues/7953)) ([46e8792](https://github.com/ionic-team/capacitor/commit/46e87925b675933b413737854b1376eb1c71a23e))\n- **cli:** ensures the execution order of CLI hooks ([#7947](https://github.com/ionic-team/capacitor/issues/7947)) ([c3ea809](https://github.com/ionic-team/capacitor/commit/c3ea8094cdd385ac02a5aae89f8b44b6c53d493a))\n- **cli:** unexpected error on target device list ([#8015](https://github.com/ionic-team/capacitor/issues/8015)) ([f827c48](https://github.com/ionic-team/capacitor/commit/f827c48f08b13a265e8671291c24f3506e14a094))\n- **cli:** use proper build params ([#8016](https://github.com/ionic-team/capacitor/issues/8016)) ([12ce2a4](https://github.com/ionic-team/capacitor/commit/12ce2a486542011c8d6f4cd89a67464f3f27e28d))\n- **core:** Prevent error when hasListeners is empty ([#7975](https://github.com/ionic-team/capacitor/issues/7975)) ([a4a0942](https://github.com/ionic-team/capacitor/commit/a4a0942eddba068e078bd782bb093ed1ecff9e00))\n- Remove all plugin listeners in bridge reset ([#7962](https://github.com/ionic-team/capacitor/issues/7962)) ([06aeea9](https://github.com/ionic-team/capacitor/commit/06aeea973aa56ac2fb791f893ebd899253ee65f9))\n\n### Features\n\n- **cli:** Initial Cordova plugin SPM support ([#7999](https://github.com/ionic-team/capacitor/issues/7999)) ([c030354](https://github.com/ionic-team/capacitor/commit/c03035426343ddc4e303ca3420e476bb7f1e2201))\n- expose `appStartPath` on cap config server configuration ([#8019](https://github.com/ionic-team/capacitor/issues/8019)) ([a274fef](https://github.com/ionic-team/capacitor/commit/a274fef069176a8e528a22f4734d1e29a539709c))\n- **ios:** Alternative debug config for SPM ([#7982](https://github.com/ionic-team/capacitor/issues/7982)) ([a054aa5](https://github.com/ionic-team/capacitor/commit/a054aa5bb4d5eabe5da5d42a380bd51b8785958c))\n\n# [7.2.0](https://github.com/ionic-team/capacitor/compare/7.1.0...7.2.0) (2025-03-31)\n\n### Bug Fixes\n\n- **android:** rename bridge layout to avoid collision ([#7919](https://github.com/ionic-team/capacitor/issues/7919)) ([a629f16](https://github.com/ionic-team/capacitor/commit/a629f167b345cda1c44f37131235d14353eff504))\n- **cli:** Don't migrate Podfile on SPM projects ([#7939](https://github.com/ionic-team/capacitor/issues/7939)) ([ab4e700](https://github.com/ionic-team/capacitor/commit/ab4e700035289f445668dfd2f86278ffc71ab269))\n- **ios:** respect shouldEncodeUrlParams value ([#7931](https://github.com/ionic-team/capacitor/issues/7931)) ([4fb12a0](https://github.com/ionic-team/capacitor/commit/4fb12a03a92c0f38b0d48b114252342f4bdc17bf))\n\n### Features\n\n- **http:** Apply overrideUserAgent to requests ([#7906](https://github.com/ionic-team/capacitor/issues/7906)) ([52482c9](https://github.com/ionic-team/capacitor/commit/52482c9d3c575b737054b41f9d1730c70cc5f471))\n- **ios:** Enable Fullscreen API on WebView ([#7909](https://github.com/ionic-team/capacitor/issues/7909)) ([172638e](https://github.com/ionic-team/capacitor/commit/172638ec7b6eb67bf25f8dac2818122ba31c2c91))\n\n# [7.1.0](https://github.com/ionic-team/capacitor/compare/7.0.1...7.1.0) (2025-03-12)\n\n### Bug Fixes\n\n- **android:** add EdgeToEdge compatibility ([#7871](https://github.com/ionic-team/capacitor/issues/7871)) ([64a8bc4](https://github.com/ionic-team/capacitor/commit/64a8bc40de2522c75a94a40cba6c8ccd82481cb8))\n- **android:** sanitize portable file name ([#7894](https://github.com/ionic-team/capacitor/issues/7894)) ([5f09297](https://github.com/ionic-team/capacitor/commit/5f092970e33da1ec896efc4e2a5ae3fb77fca658))\n- **cli:** don't run bundle if not installed ([#7896](https://github.com/ionic-team/capacitor/issues/7896)) ([ee55f6c](https://github.com/ionic-team/capacitor/commit/ee55f6c54639c1a61fa4f9eed1f366ba2d968b86))\n- **core:** use getPlatform instead of platform in cordova.js ([#7902](https://github.com/ionic-team/capacitor/issues/7902)) ([277db7b](https://github.com/ionic-team/capacitor/commit/277db7b48caaf870eefdf701ea99332c4338d7ed))\n- **http:** boundary not added for Request objects ([#7897](https://github.com/ionic-team/capacitor/issues/7897)) ([bdaa6f3](https://github.com/ionic-team/capacitor/commit/bdaa6f3c38c33f3a021ac61f2de89101a5b66cff))\n- **ios:** don't check isMediaExtension on range requests ([#7868](https://github.com/ionic-team/capacitor/issues/7868)) ([028caa5](https://github.com/ionic-team/capacitor/commit/028caa5378d359fb1004098aa93a24ad0f49a4ae))\n- **ios:** listen for CapacitorViewDidAppear ([#7850](https://github.com/ionic-team/capacitor/issues/7850)) ([e24ffb7](https://github.com/ionic-team/capacitor/commit/e24ffb7d4de0bf3d53e92537f21c864f121c1fad))\n- **ios:** Reset plugin listeners when WebView process is terminated ([#7905](https://github.com/ionic-team/capacitor/issues/7905)) ([d039157](https://github.com/ionic-team/capacitor/commit/d0391576726955b2c1b484f1ca9a03465b9ef67e))\n\n### Features\n\n- Add function to inject external JS into WebView before document load ([#7864](https://github.com/ionic-team/capacitor/issues/7864)) ([ec0954c](https://github.com/ionic-team/capacitor/commit/ec0954c197543e913939f3ab9c4bcb172bfa3530))\n- **android:** add adjustMarginsForEdgeToEdge configuration option ([#7885](https://github.com/ionic-team/capacitor/issues/7885)) ([1ea86d1](https://github.com/ionic-team/capacitor/commit/1ea86d166afd315e72847c5e734a8c175fb90e04))\n- **cli:** add more configurations to build command ([#7769](https://github.com/ionic-team/capacitor/issues/7769)) ([90f95d1](https://github.com/ionic-team/capacitor/commit/90f95d1a829f3d87cb46af827b5bfaac319a9694))\n\n## [7.0.1](https://github.com/ionic-team/capacitor/compare/7.0.0...7.0.1) (2025-01-21)\n\n### Bug Fixes\n\n- make migrate use 7.0.0 ([#7837](https://github.com/ionic-team/capacitor/issues/7837)) ([5dc309e](https://github.com/ionic-team/capacitor/commit/5dc309ea8dd7905e9e6236744f29162df80b5ed8))\n- use Capacitor 7 for SPM dependency ([#7835](https://github.com/ionic-team/capacitor/issues/7835)) ([640c3cb](https://github.com/ionic-team/capacitor/commit/640c3cb22a62e4b8fb13cd36031d39307622c60a))\n\n# [7.0.0](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.1...7.0.0) (2025-01-20)\n\n**Note:** Version bump only for package capacitor\n\n# [7.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.0...7.0.0-rc.1) (2025-01-20)\n\n**Note:** Version bump only for package capacitor\n\n# [7.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/7.0.0-beta.0...7.0.0-rc.0) (2024-12-20)\n\n**Note:** Version bump only for package capacitor\n\n# [7.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.2...7.0.0-beta.0) (2024-12-20)\n\n### Bug Fixes\n\n- **cli:** correct rimraf import ([#7811](https://github.com/ionic-team/capacitor/issues/7811)) ([0891624](https://github.com/ionic-team/capacitor/commit/0891624c46b78a0fd39f617f834d5cdd1f54f5e6))\n- **cli:** update link to telemetry information ([e922e2b](https://github.com/ionic-team/capacitor/commit/e922e2b718f5c6f2e4062cdccdeb92da3321e67d))\n- **ios:** Make Bridge webView first responder ([#7753](https://github.com/ionic-team/capacitor/issues/7753)) ([77e4668](https://github.com/ionic-team/capacitor/commit/77e4668fa8dbb24b4561387e101547f74e37538e))\n\n### Features\n\n- Add global initialFocus configuration ([#7775](https://github.com/ionic-team/capacitor/issues/7775)) ([61d0165](https://github.com/ionic-team/capacitor/commit/61d01653685d8e3594d2d8a6bd870fa9643ba95c))\n\n# [7.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.1...7.0.0-alpha.2) (2024-11-19)\n\n### Bug Fixes\n\n- **cli:** make Cordova plugins use same default kotlin version as Capacitor ([#7752](https://github.com/ionic-team/capacitor/issues/7752)) ([a4aeb55](https://github.com/ionic-team/capacitor/commit/a4aeb55720ecc83b3870bf32bf9825d6aee4644f))\n\n# [7.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/6.1.2...7.0.0-alpha.1) (2024-10-14)\n\n### Bug Fixes\n\n- **android-template:** prevent app restart on bluetooh keyboard changes ([#7652](https://github.com/ionic-team/capacitor/issues/7652)) ([200c7cb](https://github.com/ionic-team/capacitor/commit/200c7cb911898fbfb12b0b25e8cfd1982e0a6bf0))\n- **cli:** replace app-store deprecated method on build ([#7637](https://github.com/ionic-team/capacitor/issues/7637)) ([942b108](https://github.com/ionic-team/capacitor/commit/942b108c1d506539c0c53276ed4ec35eed36634e))\n- **ios:** fix retain cycle caused by CDVPluginManager ([#7692](https://github.com/ionic-team/capacitor/issues/7692)) ([02bdb3d](https://github.com/ionic-team/capacitor/commit/02bdb3d1d15907dcc577f16b7f2e22050e54ffef))\n\n### Features\n\n- **core:** expose `methodName` via `CAPPluginCall` ([#7641](https://github.com/ionic-team/capacitor/issues/7641)) ([df109aa](https://github.com/ionic-team/capacitor/commit/df109aa1cfa1ea824e22feecbd2b7183a57fc693))\n- **ios:** JSValueEncoder/Decoder feature parity with JSONEncoder/Decoder ([#7647](https://github.com/ionic-team/capacitor/issues/7647)) ([410249b](https://github.com/ionic-team/capacitor/commit/410249b6c626e67235f25b466ed4969d52148bd1)), closes [#7576](https://github.com/ionic-team/capacitor/issues/7576)\n\n## [6.1.2](https://github.com/ionic-team/capacitor/compare/6.1.1...6.1.2) (2024-08-07)\n\n### Bug Fixes\n\n- **android:** better cleaning of allowedOrigin url ([#7607](https://github.com/ionic-team/capacitor/issues/7607)) ([d123260](https://github.com/ionic-team/capacitor/commit/d123260c70f26b0e786515ced9c95447f9ee81a0))\n- **http:** pass original url as query param on the proxy url ([#7527](https://github.com/ionic-team/capacitor/issues/7527)) ([1da06e6](https://github.com/ionic-team/capacitor/commit/1da06e66cb9cfbf5a5cc48ba6c23cdbe18bc8fc0))\n- support dependencies with \".swift\" at the end of name ([#7583](https://github.com/ionic-team/capacitor/issues/7583)) ([ceee68a](https://github.com/ionic-team/capacitor/commit/ceee68a2db363e9d9a638aa4ed8569fd82d1013a))\n\n## [6.1.1](https://github.com/ionic-team/capacitor/compare/6.1.0...6.1.1) (2024-07-17)\n\n### Bug Fixes\n\n- **android:** Allow WebView to load blob urls ([#7551](https://github.com/ionic-team/capacitor/issues/7551)) ([0dca917](https://github.com/ionic-team/capacitor/commit/0dca917da54b01c6cf4fcd063f8191a8457d7c93))\n- **android:** javascript injection not working on urls with query ([#7545](https://github.com/ionic-team/capacitor/issues/7545)) ([57ce5c6](https://github.com/ionic-team/capacitor/commit/57ce5c65560c34d106e8b88cffe26ad22947bba6))\n- **android:** send FormData on older devices ([#7519](https://github.com/ionic-team/capacitor/issues/7519)) ([de8b50d](https://github.com/ionic-team/capacitor/commit/de8b50dc1d87510c5f19d04f30b6be12b51d8482))\n- **android:** UTF-8 encode form data value ([#7525](https://github.com/ionic-team/capacitor/issues/7525)) ([a73ed31](https://github.com/ionic-team/capacitor/commit/a73ed318cdb819863425f6b1b7ec23ea53454931))\n- **cli:** enable cleartext for live reload ([#7563](https://github.com/ionic-team/capacitor/issues/7563)) ([e06648f](https://github.com/ionic-team/capacitor/commit/e06648f4566c0b80ab420f977e02f1b4cd73fc1f))\n- **http:** handle UInt8Array on body ([#7546](https://github.com/ionic-team/capacitor/issues/7546)) ([cfb9ce1](https://github.com/ionic-team/capacitor/commit/cfb9ce175615f69fe86b61af6d51ec2044d147cd))\n- **ios:** make removeAllListeners accessible from javascript ([#7566](https://github.com/ionic-team/capacitor/issues/7566)) ([388a822](https://github.com/ionic-team/capacitor/commit/388a8223c60e8eac8437ca4cb26a93ab359f53ad))\n- **ios:** Match CapApp-SPM iOS version with project version ([#7556](https://github.com/ionic-team/capacitor/issues/7556)) ([df4dc9a](https://github.com/ionic-team/capacitor/commit/df4dc9a17f1b999a68093091d201ba15d234c5f8))\n\n# [6.1.0](https://github.com/ionic-team/capacitor/compare/6.0.0...6.1.0) (2024-06-11)\n\n### Bug Fixes\n\n- **android:** avoid crash if server url ends in / ([#7426](https://github.com/ionic-team/capacitor/issues/7426)) ([f8264cc](https://github.com/ionic-team/capacitor/commit/f8264ccae1f2fec553521bc62d767c4909ea6d79))\n- **cli:** Avoid duplicate entries in packageClassList ([#7470](https://github.com/ionic-team/capacitor/issues/7470)) ([cca0b80](https://github.com/ionic-team/capacitor/commit/cca0b805291bd002c515be0a1c15de84ee23c5d3))\n- **cli:** Run sync before updating gradle ([#7497](https://github.com/ionic-team/capacitor/issues/7497)) ([f27786e](https://github.com/ionic-team/capacitor/commit/f27786ea1367bc0ec53697eeb6d654a7e12a1507))\n- **http:** don't override readyState for non POST requests ([#7488](https://github.com/ionic-team/capacitor/issues/7488)) ([30c13a8](https://github.com/ionic-team/capacitor/commit/30c13a865e7710e6dc5f0ee014e951d52d030795))\n- **ios:** check if urlSchemeTask is stopped before calling its methods ([#7482](https://github.com/ionic-team/capacitor/issues/7482)) ([b32b5b1](https://github.com/ionic-team/capacitor/commit/b32b5b17ed14bd43c846b3fcb930bfd88e245e40))\n\n### Features\n\n- **cli:** run plugin hooks ([#7499](https://github.com/ionic-team/capacitor/issues/7499)) ([3b847ea](https://github.com/ionic-team/capacitor/commit/3b847eac42673c812a4538c319b81c8f8afc9955))\n- **ios:** CAPPluginMethod selector-based initializer ([#7412](https://github.com/ionic-team/capacitor/issues/7412)) ([44c5b55](https://github.com/ionic-team/capacitor/commit/44c5b55e36b85471a85ee5a1af47bdb1a5d83a8b))\n\n# [6.0.0](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.2...6.0.0) (2024-04-15)\n\n### Bug Fixes\n\n- **cli:** Change Capacitor plugin files check ([#7375](https://github.com/ionic-team/capacitor/issues/7375)) ([fbfb778](https://github.com/ionic-team/capacitor/commit/fbfb77825fdd3343e85868ea28b8e14a482dcf4a))\n- **cli:** Don't downgrade gradle version on migrate ([#7385](https://github.com/ionic-team/capacitor/issues/7385)) ([c79211e](https://github.com/ionic-team/capacitor/commit/c79211ec9dde81a57aca5cee8ab09cdef0c9ad86))\n- **cli:** Removing extra dash in android apk name flavor parsing ([#7382](https://github.com/ionic-team/capacitor/issues/7382)) ([3c411fd](https://github.com/ionic-team/capacitor/commit/3c411fd0a400b18c2d032e3b8be3fc4b5449bcbd))\n- **http:** Add URLSearchParams support ([#7374](https://github.com/ionic-team/capacitor/issues/7374)) ([9367ecc](https://github.com/ionic-team/capacitor/commit/9367ecc56a0c78249dccdf95dca5006422144289))\n- **http:** prevent POST request from being proxied ([#7395](https://github.com/ionic-team/capacitor/issues/7395)) ([7b8c352](https://github.com/ionic-team/capacitor/commit/7b8c3523decd5610dcf09e926640bf35b382d61d))\n- secure cookies often are not set by the cookie plugin ([#7261](https://github.com/ionic-team/capacitor/issues/7261)) ([cda1886](https://github.com/ionic-team/capacitor/commit/cda18861aea3ced7835f959cba612cea98761c58))\n\n### Features\n\n- **cli:** Support bun when running cap migrate ([#7386](https://github.com/ionic-team/capacitor/issues/7386)) ([3f96ff9](https://github.com/ionic-team/capacitor/commit/3f96ff911588c517dc2d924f55ef28a25c30bd33))\n\n# [6.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.1...6.0.0-rc.2) (2024-03-25)\n\n### Bug Fixes\n\n- **cli:** also generate packageClassList on copy ([#7358](https://github.com/ionic-team/capacitor/issues/7358)) ([865cceb](https://github.com/ionic-team/capacitor/commit/865cceb1bf7e1b973e8f83f3c77040add71b403c))\n- **cli:** use correct pass signature for apksigner signing ([#7365](https://github.com/ionic-team/capacitor/issues/7365)) ([e4f8610](https://github.com/ionic-team/capacitor/commit/e4f86103c2806df4ca2729600361e6338c30f083))\n- **http:** change proxy url generation ([#7354](https://github.com/ionic-team/capacitor/issues/7354)) ([318c316](https://github.com/ionic-team/capacitor/commit/318c316847c5b059fb88b46d4acd31e1ced477e5))\n- inject cordova files if a cordova plugin is present ([#7363](https://github.com/ionic-team/capacitor/issues/7363)) ([ce9d505](https://github.com/ionic-team/capacitor/commit/ce9d50585b1cab183245197878bf625cf0289275))\n- vue 3 log warning causes error on iOS ([#6993](https://github.com/ionic-team/capacitor/issues/6993)) ([87271e2](https://github.com/ionic-team/capacitor/commit/87271e2671013ad35d13b22f2e96d4fe8f4eeaf0))\n\n# [6.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.0...6.0.0-rc.1) (2024-03-15)\n\n### Bug Fixes\n\n- **android:** handle http errors on the proxy ([#7280](https://github.com/ionic-team/capacitor/issues/7280)) ([cca3c80](https://github.com/ionic-team/capacitor/commit/cca3c80298b768a5eaf1da4d95f659e303ed7042))\n- **android:** incorrect http url params encoding ([#6586](https://github.com/ionic-team/capacitor/issues/6586)) ([e9ddb0c](https://github.com/ionic-team/capacitor/commit/e9ddb0cfdb1ed320b650e1d01a04d37e644f6dd3))\n- **android:** let WebView handle errors ([#7286](https://github.com/ionic-team/capacitor/issues/7286)) ([286b694](https://github.com/ionic-team/capacitor/commit/286b69460f1227a75a9865f91fb51c455fd4370e))\n- **android:** make JSInjector replace first <head> only ([#6895](https://github.com/ionic-team/capacitor/issues/6895)) ([93c8a8d](https://github.com/ionic-team/capacitor/commit/93c8a8ddbc547a6a9501e02a73719268228ebdca))\n- **android:** prevent crash on script injection if the script is too long ([#7308](https://github.com/ionic-team/capacitor/issues/7308)) ([c9895e8](https://github.com/ionic-team/capacitor/commit/c9895e89aeb69613906fc8a2270f7bf3f4913fda))\n- **android:** Remove path from allowedOriginRules ([#7293](https://github.com/ionic-team/capacitor/issues/7293)) ([9eb565c](https://github.com/ionic-team/capacitor/commit/9eb565cddd30888125d1503d1c0a5aa5aa5e48cb))\n- **cli:** adjust SPM parameters for build and run commands ([#7342](https://github.com/ionic-team/capacitor/issues/7342)) ([02f8983](https://github.com/ionic-team/capacitor/commit/02f8983300c102125db6b41b0bb174fe41e1785b))\n- **cli:** apkName for multi-dimensional flavors ([#6704](https://github.com/ionic-team/capacitor/issues/6704)) ([d7b23f2](https://github.com/ionic-team/capacitor/commit/d7b23f21c554c94029b24d6e0c9ad4635f055759))\n- **cli:** Attempt to verify non-cjs modules exist if cjs resolution fails ([#7310](https://github.com/ionic-team/capacitor/issues/7310)) ([663e7b3](https://github.com/ionic-team/capacitor/commit/663e7b383be0ceee1d23b929ceb6374d00faf011))\n- **cli:** correct build path for non flavor builds ([#7281](https://github.com/ionic-team/capacitor/issues/7281)) ([0f9651d](https://github.com/ionic-team/capacitor/commit/0f9651d99cdd9cb463e494ed016838cd6d4a34c4))\n- **cli:** Migrate variables to newer dependency versions ([#7235](https://github.com/ionic-team/capacitor/issues/7235)) ([aaf01ab](https://github.com/ionic-team/capacitor/commit/aaf01ab938e02220d7e6b5cd9329baa3edd02b68))\n- **cli:** remove certificate file extension check on copy ([#7240](https://github.com/ionic-team/capacitor/issues/7240)) ([93a0044](https://github.com/ionic-team/capacitor/commit/93a004473d7c4008bf02e8077460dfbdc3042312))\n- **core:** make 'content-type' header count for XMLHttpRequest patch ([#7161](https://github.com/ionic-team/capacitor/issues/7161)) ([26d7f68](https://github.com/ionic-team/capacitor/commit/26d7f688284914c6ef795564ba424119efc32a1c))\n- **http:** better handling of active requests and shutting down gracefully ([5d294c9](https://github.com/ionic-team/capacitor/commit/5d294c9b84f09bab649a7112c86959a41a9a4f5e))\n- **http:** handle proxy urls with port ([#7273](https://github.com/ionic-team/capacitor/issues/7273)) ([514409a](https://github.com/ionic-team/capacitor/commit/514409aeb93ad65be105bbe2da8d2cd86ff159b0))\n- **http:** keep original URL properties on proxy ([#7329](https://github.com/ionic-team/capacitor/issues/7329)) ([cbb6407](https://github.com/ionic-team/capacitor/commit/cbb6407225b42a0d9db4f335a9766f119501021d))\n- **http:** Make proxy work with Request objects ([#7348](https://github.com/ionic-team/capacitor/issues/7348)) ([7e68725](https://github.com/ionic-team/capacitor/commit/7e6872573df03ab5cdc10a1a27db3e9fe81a141d))\n- **http:** route get requests through custom handler ([#6818](https://github.com/ionic-team/capacitor/issues/6818)) ([b853d06](https://github.com/ionic-team/capacitor/commit/b853d065055b5a819949551be58b62d40b52e37c))\n- **http:** set port for proxy url ([#7341](https://github.com/ionic-team/capacitor/issues/7341)) ([a3059dc](https://github.com/ionic-team/capacitor/commit/a3059dca4a7746d9fb7102a7d41f4da80e2f48b4))\n- **ios:** Moves `updateBinaryVersion` call to the end of `loadView`. ([#7226](https://github.com/ionic-team/capacitor/issues/7226)) ([7724760](https://github.com/ionic-team/capacitor/commit/77247602ac150797d2375118f702cf9ba39ea957))\n- **ios:** overwrite CORS headers on livereload ([#7339](https://github.com/ionic-team/capacitor/issues/7339)) ([0c8d69b](https://github.com/ionic-team/capacitor/commit/0c8d69b7f9c163730e7d74bb0147b61742c61676))\n- **spm:** add product lines to Package.swift ([#7278](https://github.com/ionic-team/capacitor/issues/7278)) ([e45d60d](https://github.com/ionic-team/capacitor/commit/e45d60d15445fa55320eb6274de67d4b4191f30a))\n\n### Features\n\n- **webview:** add setServerAssetPath method ([881235b](https://github.com/ionic-team/capacitor/commit/881235b14de23ef988746bfb89a5a0fc3c8d8466))\n\n# [6.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.2...6.0.0-rc.0) (2024-01-23)\n\n### Bug Fixes\n\n- **android:** don't proxy requests if no jsInjector ([#7199](https://github.com/ionic-team/capacitor/issues/7199)) ([7dc5585](https://github.com/ionic-team/capacitor/commit/7dc5585996c17eedd154b5d9118eb880fef616fe))\n- **android:** handle deprecation warnings ([#7190](https://github.com/ionic-team/capacitor/issues/7190)) ([06636d7](https://github.com/ionic-team/capacitor/commit/06636d73a1b12c8f932642ee7caf91432690fea5))\n- **cli:** correctly build and sign Android apps using Flavors ([#7082](https://github.com/ionic-team/capacitor/issues/7082)) ([7d3a99d](https://github.com/ionic-team/capacitor/commit/7d3a99d15ba024b4bde9e698e57937002da38bd4))\n- **cli:** Specify keystore alias and alias password build options for apksigner ([#7073](https://github.com/ionic-team/capacitor/issues/7073)) ([6612c49](https://github.com/ionic-team/capacitor/commit/6612c49f5fde5ef2fee555206018391fc00e2be0))\n- **cli:** type only import in config ([#6765](https://github.com/ionic-team/capacitor/issues/6765)) ([2b493ae](https://github.com/ionic-team/capacitor/commit/2b493ae77b9ee6e5fbcb2fafb27bc04693d7e02b))\n- **cli:** Update plugins with breaking changes ([#7148](https://github.com/ionic-team/capacitor/issues/7148)) ([b8c2a92](https://github.com/ionic-team/capacitor/commit/b8c2a92390f21fe653563d7bad73a3f2b5628acb))\n- remove duplicates from auto registration array using set ([#7192](https://github.com/ionic-team/capacitor/issues/7192)) ([c4984ae](https://github.com/ionic-team/capacitor/commit/c4984ae4433602dbe028f72736cb6e56e8f92bf9))\n- stop crashing from `objc_getClassList` ([#7187](https://github.com/ionic-team/capacitor/issues/7187)) ([e148db7](https://github.com/ionic-team/capacitor/commit/e148db7758e4186ad45fd74185a10fb757ff9b29))\n- **web:** Implement `retainUntilConsumed` on notifyListeners ([#7127](https://github.com/ionic-team/capacitor/issues/7127)) ([526292e](https://github.com/ionic-team/capacitor/commit/526292eb273ee7d8fa9a9912ce3b59e9a104c19e))\n\n### Features\n\n- **android:** Load alternative layout when no WebView ([#7141](https://github.com/ionic-team/capacitor/issues/7141)) ([87c399a](https://github.com/ionic-team/capacitor/commit/87c399ab6b41f217b4d8a58bfb916a5d78641fd0))\n- **cli:** allow async in capacitor config file ([#4299](https://github.com/ionic-team/capacitor/issues/4299)) ([5cecc3e](https://github.com/ionic-team/capacitor/commit/5cecc3ee22868f9397792ec80a4b753a07b68f30))\n- **ios:** Replace usage of UserDefaults with KeyValueStore. ([#7191](https://github.com/ionic-team/capacitor/issues/7191)) ([cd58ba2](https://github.com/ionic-team/capacitor/commit/cd58ba2a654f40bf72616f430d7b9604b283e23d))\n\n# [6.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.1...6.0.0-beta.2) (2023-12-14)\n\n**Note:** Version bump only for package capacitor\n\n# [6.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.0...6.0.0-beta.1) (2023-12-14)\n\n### Bug Fixes\n\n- **ios:** Add Codable folder to podspec source_files ([#7131](https://github.com/ionic-team/capacitor/issues/7131)) ([04d1d55](https://github.com/ionic-team/capacitor/commit/04d1d557b51fcac31281a3f547300f06c6dacfb2))\n\n# [6.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.2...6.0.0-beta.0) (2023-12-13)\n\n### Bug Fixes\n\n- **http:** parse readablestream data on fetch request objects ([#6919](https://github.com/ionic-team/capacitor/issues/6919)) ([80ec3b7](https://github.com/ionic-team/capacitor/commit/80ec3b73db18b7b6841bf90ed50a67389946ab87))\n- **http:** properly write form-urlencoded data on android request body ([#7001](https://github.com/ionic-team/capacitor/issues/7001)) ([a986ee5](https://github.com/ionic-team/capacitor/commit/a986ee541f54a1d3ac637b514fe547b224b36903))\n- **http:** set formdata boundary and body when content-type not explicitly set ([0c2ccd9](https://github.com/ionic-team/capacitor/commit/0c2ccd910a92ce3deaa67eb1819a4faa39c6af6e))\n- **ios:** add some new cordova-ios classes used by Cordova plugins ([#7096](https://github.com/ionic-team/capacitor/issues/7096)) ([3db9051](https://github.com/ionic-team/capacitor/commit/3db9051eb015cf5f402f81b4cbaa7b27a5c9477a))\n\n### Features\n\n- **ios:** Add Codable support for CAPPluginCall and JSValueContainer ([#7119](https://github.com/ionic-team/capacitor/issues/7119)) ([af417e0](https://github.com/ionic-team/capacitor/commit/af417e0cbbb1a3a7b3b62756eebb8d1dc0952cc4))\n- support for Amazon Fire WebView ([#6603](https://github.com/ionic-team/capacitor/issues/6603)) ([3cb4eb8](https://github.com/ionic-team/capacitor/commit/3cb4eb89632bce8dc872418fdb130bfd4de40b68))\n\n# [6.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.1...6.0.0-alpha.2) (2023-11-15)\n\n### Bug Fixes\n\n- **ios:** Remove CocoaPods Xcode 15 workaround that causes issues ([#7059](https://github.com/ionic-team/capacitor/issues/7059)) ([043a8db](https://github.com/ionic-team/capacitor/commit/043a8dba4059e33c7445696c186110bef1130e16))\n\n# [6.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/5.2.3...6.0.0-alpha.1) (2023-11-08)\n\n### Bug Fixes\n\n- allow double quotes in Gemfile ([#6903](https://github.com/ionic-team/capacitor/issues/6903)) ([3abdbed](https://github.com/ionic-team/capacitor/commit/3abdbed38844d5d59d244f6f0dfc2647f29ce446))\n- **android:** handle webview version for developer builds ([#6907](https://github.com/ionic-team/capacitor/issues/6907)) ([88498e6](https://github.com/ionic-team/capacitor/commit/88498e6228492a9ae917d3a7b37c242881f9fe52))\n- **android:** make local urls use unpatched fetch ([#6953](https://github.com/ionic-team/capacitor/issues/6953)) ([e50e56c](https://github.com/ionic-team/capacitor/commit/e50e56c5231f230497d1bd420e02e2e065c38f86))\n- **android:** Use Logger class instead of Log in CapacitorCookieManager ([#6923](https://github.com/ionic-team/capacitor/issues/6923)) ([8aaa356](https://github.com/ionic-team/capacitor/commit/8aaa356ab1f14b56df821e8ac0bb7e43bfa094fa))\n- **cli:** force latest native-run version for iOS 17 support ([#6926](https://github.com/ionic-team/capacitor/issues/6926)) ([7e7c8b9](https://github.com/ionic-team/capacitor/commit/7e7c8b9113f541d530c5883dea1f52b2957c0859))\n- **cli:** Pin @ionic/utils-subprocess version ([#7057](https://github.com/ionic-team/capacitor/issues/7057)) ([0ac019a](https://github.com/ionic-team/capacitor/commit/0ac019a36070b4cb9917a82e406453169c7d5559))\n- **cli:** use helper in Podfile with correct path ([#6878](https://github.com/ionic-team/capacitor/issues/6878)) ([8e95be9](https://github.com/ionic-team/capacitor/commit/8e95be9f91169e258ab5cdd8fd673106392b8429))\n- **cli:** Use latest native-run ([#7023](https://github.com/ionic-team/capacitor/issues/7023)) ([4125160](https://github.com/ionic-team/capacitor/commit/412516069e15fbdbc17ad130c2f3a67891b6bc45))\n- **cookies:** remove session cookies when initializing the cookie manager ([037863b](https://github.com/ionic-team/capacitor/commit/037863bea6f3a00978125dc2f8ecba1e896c0740))\n- **http:** add support for Request objects in fetch ([24b3cc1](https://github.com/ionic-team/capacitor/commit/24b3cc113e3d8aae5d85dbf2d25bec0c35136477))\n- **http:** disconnect active connections if call or bridge is destroyed ([a1ed6cc](https://github.com/ionic-team/capacitor/commit/a1ed6cc6f07465d683b95e3796d944f863a7b857))\n- **http:** inherit object properties on window.XMLHttpRequest ([91c11d0](https://github.com/ionic-team/capacitor/commit/91c11d06f773c45a10f6f2d52f672ae6f189b162))\n- **http:** return numbers and booleans as-is when application/json is the content type ([03dd3f9](https://github.com/ionic-team/capacitor/commit/03dd3f96c7ee75b6fff2b7c40d0c9a58fb04fce5))\n- **ios-template:** added workaround for Cocoapods bug in XC15 ([#6847](https://github.com/ionic-team/capacitor/issues/6847)) ([10ccc76](https://github.com/ionic-team/capacitor/commit/10ccc769b67eda12a2899c447949a4865d3e9954))\n- Update migrate to Capacitor 6 ([#6872](https://github.com/ionic-team/capacitor/issues/6872)) ([98eec8f](https://github.com/ionic-team/capacitor/commit/98eec8fe9fd332d6669965fa5a21412233b3e06e))\n\n### Features\n\n- add livereload to run command ([#6831](https://github.com/ionic-team/capacitor/issues/6831)) ([4099969](https://github.com/ionic-team/capacitor/commit/4099969f70e9b995182bacecc16e160d89bbc746))\n- Add the spm root project to the template ([#6877](https://github.com/ionic-team/capacitor/issues/6877)) ([02c44c2](https://github.com/ionic-team/capacitor/commit/02c44c2d9ed1b76a72b0f8a2c338b556133c9582))\n- Add XCFrameworks ([#7020](https://github.com/ionic-team/capacitor/issues/7020)) ([5306095](https://github.com/ionic-team/capacitor/commit/53060955dc83cdbfda66bed60c2efcba395a9ca8))\n- **android:** allow developers to provide logic for onRenderProcessGone in WebViewListener ([#6966](https://github.com/ionic-team/capacitor/issues/6966)) ([79e17bb](https://github.com/ionic-team/capacitor/commit/79e17bb5e6ccd813bddc626703152d3983f6d93b))\n- better support monorepos ([#6811](https://github.com/ionic-team/capacitor/issues/6811)) ([ae35e29](https://github.com/ionic-team/capacitor/commit/ae35e29fb8c886dea867683a23a558d2d344073b))\n- **ios:** Makes CapacitorBridge, WebViewAssetHandler, and WebViewDelegationHandler open classes, along with several of their methods ([#7009](https://github.com/ionic-team/capacitor/issues/7009)) ([40d62cb](https://github.com/ionic-team/capacitor/commit/40d62cbce950c2f3972764fe134cc37f2343f33d))\n- modify package.swift on update and sync ([#7042](https://github.com/ionic-team/capacitor/issues/7042)) ([24573fb](https://github.com/ionic-team/capacitor/commit/24573fb864c43551e2ce42721b45ff901155627d))\n\n# [5.6.0](https://github.com/ionic-team/capacitor/compare/5.5.1...5.6.0) (2023-12-14)\n\n### Bug Fixes\n\n- **cli:** Use latest native-run ([#7030](https://github.com/ionic-team/capacitor/issues/7030)) ([1d948d4](https://github.com/ionic-team/capacitor/commit/1d948d4df6b6b6f8cfdc02e72d84ae8be963f4a0))\n- **http:** properly write form-urlencoded data on android request body ([#7130](https://github.com/ionic-team/capacitor/issues/7130)) ([a745a89](https://github.com/ionic-team/capacitor/commit/a745a89e18a5082ae4e737d78aa20929f6952382))\n- **http:** set formdata boundary and body when content-type not explicitly set ([#7133](https://github.com/ionic-team/capacitor/issues/7133)) ([3862d6e](https://github.com/ionic-team/capacitor/commit/3862d6e6721793d78add9acf5b14fd9a8f7a5b60))\n- **ios:** add some new cordova-ios classes used by Cordova plugins ([#7115](https://github.com/ionic-team/capacitor/issues/7115)) ([5fb902b](https://github.com/ionic-team/capacitor/commit/5fb902b232d9afded2edc865c8d3c0c0e7efe5e7))\n\n### Features\n\n- support for Amazon Fire WebView ([#6603](https://github.com/ionic-team/capacitor/issues/6603)) ([#7129](https://github.com/ionic-team/capacitor/issues/7129)) ([421d2c0](https://github.com/ionic-team/capacitor/commit/421d2c02e4d1954d16d573facae9c235fee60f02))\n\n## [5.5.1](https://github.com/ionic-team/capacitor/compare/5.5.0...5.5.1) (2023-10-25)\n\n### Bug Fixes\n\n- **ios:** CAPWebView config update ([#7004](https://github.com/ionic-team/capacitor/issues/7004)) ([f3e8be0](https://github.com/ionic-team/capacitor/commit/f3e8be0453c31f74a2fdf4c9a6d8d7967a6b5c20))\n\n# [5.5.0](https://github.com/ionic-team/capacitor/compare/5.4.2...5.5.0) (2023-10-11)\n\n### Features\n\n- **android:** allow developers to provide logic for onRenderProcessGone in WebViewListener ([#6946](https://github.com/ionic-team/capacitor/issues/6946)) ([34b724a](https://github.com/ionic-team/capacitor/commit/34b724a4cf406c23b2a9952ef81e0327b78a3b3a))\n\n## [5.4.2](https://github.com/ionic-team/capacitor/compare/5.4.1...5.4.2) (2023-10-04)\n\n### Bug Fixes\n\n- **android:** make local urls use unpatched fetch ([#6954](https://github.com/ionic-team/capacitor/issues/6954)) ([56fb853](https://github.com/ionic-team/capacitor/commit/56fb8536af53f4f4ee49b9394fd966ad514b9458))\n\n## [5.4.1](https://github.com/ionic-team/capacitor/compare/5.4.0...5.4.1) (2023-09-21)\n\n### Bug Fixes\n\n- **android:** handle webview version for developer builds ([#6911](https://github.com/ionic-team/capacitor/issues/6911)) ([b5b0398](https://github.com/ionic-team/capacitor/commit/b5b0398a7fe117a824f97125f5feabe81073daf3))\n- **android:** Use Logger class instead of Log in CapacitorCookieManager ([#6925](https://github.com/ionic-team/capacitor/issues/6925)) ([b6901e0](https://github.com/ionic-team/capacitor/commit/b6901e01e05cd22a71841d2f5821fbe2a6939ead))\n- **cli:** force latest native-run version for iOS 17 support ([#6928](https://github.com/ionic-team/capacitor/issues/6928)) ([f9be9f5](https://github.com/ionic-team/capacitor/commit/f9be9f5791e6f0881be2c73bb8fbe7a8c1b10848))\n- **cookies:** retrieve cookies when using a custom android scheme ([6b5ddad](https://github.com/ionic-team/capacitor/commit/6b5ddad8b36e33ef4171f6da5cc311ed3f634ac6))\n- **http:** parse readablestream data on fetch request objects ([3fe0642](https://github.com/ionic-team/capacitor/commit/3fe06426bd20713e2322780b70bc5d97ad371fae))\n- **http:** return xhr response headers case insensitive ([687b6b1](https://github.com/ionic-team/capacitor/commit/687b6b1780506c17fb73ed1d9cbf50c1d1e40ef1))\n- **ios:** Add workaround for CocoaPods problem on Xcode 15 ([#6921](https://github.com/ionic-team/capacitor/issues/6921)) ([1ffa244](https://github.com/ionic-team/capacitor/commit/1ffa2441fc8a04e4bf1712d0afb868a83e7f1951))\n\n# [5.4.0](https://github.com/ionic-team/capacitor/compare/5.3.0...5.4.0) (2023-09-14)\n\n### Bug Fixes\n\n- **cli:** use helper in Podfile with correct path ([#6888](https://github.com/ionic-team/capacitor/issues/6888)) ([9048432](https://github.com/ionic-team/capacitor/commit/9048432755095ce3dcca9d3bab39894f2b6c3967))\n- **http:** add support for defining xhr and angular http response types ([09bd040](https://github.com/ionic-team/capacitor/commit/09bd040dfe4b8808d7499b6ee592005420406cac))\n- **http:** add support for Request objects in fetch ([2fe4535](https://github.com/ionic-team/capacitor/commit/2fe4535e781b1a5cfa0f3359c1afa5c360073b6a))\n- **http:** inherit object properties on window.XMLHttpRequest ([5cd3b2f](https://github.com/ionic-team/capacitor/commit/5cd3b2fa6d6936864e1aab2e98963df2d4da3b95))\n\n### Features\n\n- add livereload to run command ([#6831](https://github.com/ionic-team/capacitor/issues/6831)) ([54a63ae](https://github.com/ionic-team/capacitor/commit/54a63ae0a5f0845d5ef2c0d10bd0c27682866940))\n\n# [5.3.0](https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0) (2023-08-23)\n\n### Bug Fixes\n\n- **cookies:** remove session cookies when initializing the cookie manager ([037863b](https://github.com/ionic-team/capacitor/commit/037863bea6f3a00978125dc2f8ecba1e896c0740))\n- **http:** disconnect active connections if call or bridge is destroyed ([a1ed6cc](https://github.com/ionic-team/capacitor/commit/a1ed6cc6f07465d683b95e3796d944f863a7b857))\n- **http:** return numbers and booleans as-is when application/json is the content type ([03dd3f9](https://github.com/ionic-team/capacitor/commit/03dd3f96c7ee75b6fff2b7c40d0c9a58fb04fce5))\n\n### Features\n\n- better support monorepos ([#6811](https://github.com/ionic-team/capacitor/issues/6811)) ([ae35e29](https://github.com/ionic-team/capacitor/commit/ae35e29fb8c886dea867683a23a558d2d344073b))\n\n## [5.2.3](https://github.com/ionic-team/capacitor/compare/5.2.2...5.2.3) (2023-08-10)\n\n### Bug Fixes\n\n- **android:** allow single input file selection from samsumg gallery ([#6778](https://github.com/ionic-team/capacitor/issues/6778)) ([3d57ecd](https://github.com/ionic-team/capacitor/commit/3d57ecdf7631d1581047bd5d9f86ea657ecad845))\n- **android:** avoid R8 optimizations remove plugin classes ([#6783](https://github.com/ionic-team/capacitor/issues/6783)) ([cc85df5](https://github.com/ionic-team/capacitor/commit/cc85df5f3a6999883623054573bafc30665e41e7))\n- **cli:** remove package related checks on doctor command ([#6773](https://github.com/ionic-team/capacitor/issues/6773)) ([4499b6b](https://github.com/ionic-team/capacitor/commit/4499b6bb6c52e9bc7fdfdb35ee2519881996eedf))\n- **cli:** signing type option issue ([#6716](https://github.com/ionic-team/capacitor/issues/6716)) ([ee0f745](https://github.com/ionic-team/capacitor/commit/ee0f7457e458ca4bb4eb74f67552ac2ace76016b))\n- **cookies:** hide httpOnly cookies from client ([0cc927e](https://github.com/ionic-team/capacitor/commit/0cc927ef5f0f7076a6d486d666d78483f1d71c54))\n- **http:** return valid response for relative url xhr requests ([bde6569](https://github.com/ionic-team/capacitor/commit/bde65696218f97a8328041f137457f46e5eb766a))\n\n## [5.2.2](https://github.com/ionic-team/capacitor/compare/5.2.1...5.2.2) (2023-07-19)\n\n### Bug Fixes\n\n- add http method to prototype.open ([#6740](https://github.com/ionic-team/capacitor/issues/6740)) ([1fd2d87](https://github.com/ionic-team/capacitor/commit/1fd2d8762ff2341a8fe20eec9e774c6a29576e88))\n\n## [5.2.1](https://github.com/ionic-team/capacitor/compare/5.2.0...5.2.1) (2023-07-13)\n\n### Bug Fixes\n\n- allow single parameter on setRequestBody ([#6728](https://github.com/ionic-team/capacitor/issues/6728)) ([5343bdb](https://github.com/ionic-team/capacitor/commit/5343bdb60d26849cd8f9c8ff28ba7d9ddbd05b26))\n\n# [5.2.0](https://github.com/ionic-team/capacitor/compare/5.1.1...5.2.0) (2023-07-12)\n\n### Bug Fixes\n\n- **cli:** make migrate not error if there are no dependencies ([#6707](https://github.com/ionic-team/capacitor/issues/6707)) ([25ca83a](https://github.com/ionic-team/capacitor/commit/25ca83a8a76fe0eaf73c0db24fd950b33fd16063))\n- **cookies:** sanitize url before retrieving/setting cookies ([ca40634](https://github.com/ionic-team/capacitor/commit/ca4063471f215d3f7525e51592d9c72138a52855))\n- **http:** fire events in correct order when using xhr ([5ed3617](https://github.com/ionic-team/capacitor/commit/5ed361787596bb5949f6ae5e366495f296352bf3))\n\n### Features\n\n- **http:** support for FormData requests ([#6708](https://github.com/ionic-team/capacitor/issues/6708)) ([849c564](https://github.com/ionic-team/capacitor/commit/849c56458205bea3b078b1ee19807d7fd84c47b1))\n\n## [5.1.1](https://github.com/ionic-team/capacitor/compare/5.1.0...5.1.1) (2023-07-05)\n\n### Bug Fixes\n\n- **ios:** Revert server url addition for CAPWebView. ([#6705](https://github.com/ionic-team/capacitor/issues/6705)) ([1b8352d](https://github.com/ionic-team/capacitor/commit/1b8352dc5124dc3f57d7881d619537cbf8c3674b))\n\n# [5.1.0](https://github.com/ionic-team/capacitor/compare/5.0.5...5.1.0) (2023-06-29)\n\n### Bug Fixes\n\n- **android:** Move bridge localUrl initialization to initWebView ([#6685](https://github.com/ionic-team/capacitor/issues/6685)) ([7f5f0ca](https://github.com/ionic-team/capacitor/commit/7f5f0ca4220d40d6a19c778c18f9534ef3b65899))\n- **android:** revert cookie manager initialization to plugin load ([53a2d47](https://github.com/ionic-team/capacitor/commit/53a2d4792e026a89723a672a01fc34990add71f0))\n- **ios:** Return proper MIME Type for local WASM files ([#6675](https://github.com/ionic-team/capacitor/issues/6675)) ([d7856de](https://github.com/ionic-team/capacitor/commit/d7856de62a4c058ac474ae91a5fd221dabf99c0a))\n- **ios:** set cors headers in asset handler for live reload ([e5a1c81](https://github.com/ionic-team/capacitor/commit/e5a1c81fe81904dfd7e3f5100a04088173effc1c))\n\n### Features\n\n- **android:** add check for excluded domains before ssl request ([7906d36](https://github.com/ionic-team/capacitor/commit/7906d3616e8bfb2e2c1c81ee123424c06fc4e5ab))\n- **cli:** add apksigner as a build option ([#6442](https://github.com/ionic-team/capacitor/issues/6442)) ([9818a76](https://github.com/ionic-team/capacitor/commit/9818a76ab4ea6660b444354f239344d37c77d3b3))\n- export buildRequestInit function so we can use for downloadFile ([95b0575](https://github.com/ionic-team/capacitor/commit/95b0575e3fbc1b1408aa69b61c58e18bf8882cea)\n\n  )\n\n## [5.0.5](https://github.com/ionic-team/capacitor/compare/5.0.4...5.0.5) (2023-06-09)\n\n### Bug Fixes\n\n- **http:** don't throw errors when content-type is null on response ([#6627](https://github.com/ionic-team/capacitor/issues/6627)) ([538821f](https://github.com/ionic-team/capacitor/commit/538821f267aa3b79548fed6aaea8880ff949ffdd))\n\n## [5.0.4](https://github.com/ionic-team/capacitor/compare/5.0.3...5.0.4) (2023-05-23)\n\n### Bug Fixes\n\n- **cicd:** removed `set -eo pipefail` to allow job to continue ([#6596](https://github.com/ionic-team/capacitor/issues/6596)) ([caeeb09](https://github.com/ionic-team/capacitor/commit/caeeb090922a5f7e56b1629209cb4227ae60da07))\n- **cli:** correct migration of package from AndroidManifest.xml to build.gradle ([#6607](https://github.com/ionic-team/capacitor/issues/6607)) ([1c26a3e](https://github.com/ionic-team/capacitor/commit/1c26a3e57f356a0972bd43854ca86770a49f2d63))\n- **cli:** Don't succeed migration if npm install failed ([#6595](https://github.com/ionic-team/capacitor/issues/6595)) ([6843d96](https://github.com/ionic-team/capacitor/commit/6843d9642fad9a322579cbe5f01563929a83dbf5))\n- **cli:** proper plugin module patch in monorepos ([#6589](https://github.com/ionic-team/capacitor/issues/6589)) ([d49e632](https://github.com/ionic-team/capacitor/commit/d49e6324ab5e0bea58ff6ca32feb7ea39d33a772))\n\n## [5.0.3](https://github.com/ionic-team/capacitor/compare/5.0.2...5.0.3) (2023-05-10)\n\n**Note:** Version bump only for package capacitor\n\n## [5.0.2](https://github.com/ionic-team/capacitor/compare/5.0.1...5.0.2) (2023-05-09)\n\n### Bug Fixes\n\n- **cli:** handle unrecognized java --version ([#6577](https://github.com/ionic-team/capacitor/issues/6577)) ([56b0037](https://github.com/ionic-team/capacitor/commit/56b0037a70d64019563b6e55e53de423f471fe2f))\n- **cli:** Move package to build.gradle in Capacitor plugins ([#6569](https://github.com/ionic-team/capacitor/issues/6569)) ([8cb26cd](https://github.com/ionic-team/capacitor/commit/8cb26cd97a4f9cf59abb6b3828a07555a6af0b15))\n- fallback to plain `pod` if `Gemfile` does not contain CocoaPods ([#6581](https://github.com/ionic-team/capacitor/issues/6581)) ([3a41b4c](https://github.com/ionic-team/capacitor/commit/3a41b4c1b70af7a45201fb11b04dc5558893aa7e))\n\n## [5.0.1](https://github.com/ionic-team/capacitor/compare/5.0.0...5.0.1) (2023-05-05)\n\n### Bug Fixes\n\n- **cli:** install minor Capacitor 5 version ([#6562](https://github.com/ionic-team/capacitor/issues/6562)) ([f4af0a2](https://github.com/ionic-team/capacitor/commit/f4af0a298fb5a5f8257f175327058341a230ae4f))\n- **cli:** Update migration link ([#6560](https://github.com/ionic-team/capacitor/issues/6560)) ([e03062e](https://github.com/ionic-team/capacitor/commit/e03062e6025fea0edfabbff2081b3f91017aece4))\n\n# [5.0.0](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.3...5.0.0) (2023-05-03)\n\n**Note:** Version bump only for package capacitor\n\n# [5.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.2...5.0.0-rc.3) (2023-05-03)\n\n**Note:** Version bump only for package capacitor\n\n# [5.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.1...5.0.0-rc.2) (2023-05-03)\n\n### Bug Fixes\n\n- check for android and JDK ([#6554](https://github.com/ionic-team/capacitor/issues/6554)) ([ddcc818](https://github.com/ionic-team/capacitor/commit/ddcc818e828b290459d3ddffe9102fc312139823))\n\n# [5.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.0...5.0.0-rc.1) (2023-05-02)\n\n### Bug Fixes\n\n- **cli:** Avoid infinite loop in namespace migration ([#6551](https://github.com/ionic-team/capacitor/issues/6551)) ([d3aacde](https://github.com/ionic-team/capacitor/commit/d3aacdeb0c86d3941464954e7d1f582e405be489))\n- **cli:** Migrate more plugin variables ([#6552](https://github.com/ionic-team/capacitor/issues/6552)) ([b7da5b9](https://github.com/ionic-team/capacitor/commit/b7da5b988ce7da5ea3991eaec46b9e52ff3635f1))\n\n# [5.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.3...5.0.0-rc.0) (2023-05-01)\n\n**Note:** Version bump only for package capacitor\n\n# [5.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.2...5.0.0-beta.3) (2023-04-21)\n\n### Bug Fixes\n\n- **cookies:** init cookie manager after server url is set ([0ee772f](https://github.com/ionic-team/capacitor/commit/0ee772ff6456ad0948a0dd025dfcf2658a5563a0))\n\n### Features\n\n- **android:** update gradle to 8.0.2 and gradle plugin to 8.0.0 ([#6497](https://github.com/ionic-team/capacitor/issues/6497)) ([01b5b39](https://github.com/ionic-team/capacitor/commit/01b5b399324ae5d0896989478a6910fb946542d7))\n- **cli:** android manifest to build.gradle migration ([#6533](https://github.com/ionic-team/capacitor/issues/6533)) ([245b6ab](https://github.com/ionic-team/capacitor/commit/245b6ab85b0f481f08c21e25f2b2a7eb6da9996c))\n- **cli:** Migrate update to gradle 8 ([#6530](https://github.com/ionic-team/capacitor/issues/6530)) ([da3ac0e](https://github.com/ionic-team/capacitor/commit/da3ac0e72c0559223e9b91f31830810d39638734))\n\n# [5.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.1...5.0.0-beta.2) (2023-04-13)\n\n### Bug Fixes\n\n- **android:** launching intents without host ([#6489](https://github.com/ionic-team/capacitor/issues/6489)) ([95f7474](https://github.com/ionic-team/capacitor/commit/95f747401ac5a666de4338a18666060e9c1ff39e))\n- **android:** unify kotlin dependency version ([#6501](https://github.com/ionic-team/capacitor/issues/6501)) ([0a40477](https://github.com/ionic-team/capacitor/commit/0a4047768cbde9bc17d92955e64ab11d2e3b3335))\n- **cookies:** check isEnabled before setting cookieHandler ([bb04f24](https://github.com/ionic-team/capacitor/commit/bb04f24f0b4a99e46ed5ca047d3d3df81804d516))\n- **ios/android:** copy url from nativeResponse to response ([#6482](https://github.com/ionic-team/capacitor/issues/6482)) ([828fb71](https://github.com/ionic-team/capacitor/commit/828fb71ebb52c0655d5879ad0edaac7368ab2b96))\n- remove accept-charset ([#6386](https://github.com/ionic-team/capacitor/issues/6386)) ([bbf6f7e](https://github.com/ionic-team/capacitor/commit/bbf6f7e1af0c49c0bc917942b6715c613be3f557))\n\n### Features\n\n- **ios:** Add Bundler support ([#5205](https://github.com/ionic-team/capacitor/issues/5205)) ([f21c6d0](https://github.com/ionic-team/capacitor/commit/f21c6d01fc30e46c151afc93da9727dbf6c9ddcf))\n- **ios:** add webContentsDebuggingEnabled configuration ([#6495](https://github.com/ionic-team/capacitor/issues/6495)) ([c691e4a](https://github.com/ionic-team/capacitor/commit/c691e4aecbfb7a45ce0465d1fe9020ab715815d3))\n\n# [5.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.0...5.0.0-beta.1) (2023-04-03)\n\n### Bug Fixes\n\n- copy url from nativeResponse to response in native-bridge.ts ([#6397](https://github.com/ionic-team/capacitor/issues/6397)) ([e81a2ff](https://github.com/ionic-team/capacitor/commit/e81a2ff42ddd446f3004ab5af6e74209f7ff076a))\n\n### Features\n\n- **cli:** add npm update step to migrate ([#6462](https://github.com/ionic-team/capacitor/issues/6462)) ([65520c3](https://github.com/ionic-team/capacitor/commit/65520c36cdb4ac6f8811eb72624c447f2a0d884a))\n- **cli:** non interactive migrate ([#6461](https://github.com/ionic-team/capacitor/issues/6461)) ([53dfeaf](https://github.com/ionic-team/capacitor/commit/53dfeaf77ace5b165260b68351eae8e5bf72ea0a))\n\n# [5.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/5.0.0-alpha.1...5.0.0-beta.0) (2023-03-31)\n\n### Bug Fixes\n\n- 204 http response ([#6266](https://github.com/ionic-team/capacitor/issues/6266)) ([771f6ce](https://github.com/ionic-team/capacitor/commit/771f6ce1f35159848db218a42dc4f56b5106f750))\n- **android:** Allow WebView to load data urls ([#6418](https://github.com/ionic-team/capacitor/issues/6418)) ([daf2ec6](https://github.com/ionic-team/capacitor/commit/daf2ec64df0c567c6a42560488e5d2515eff8a33))\n- **android:** proper app url check for launching intents ([#6450](https://github.com/ionic-team/capacitor/issues/6450)) ([302ba35](https://github.com/ionic-team/capacitor/commit/302ba353acbd6d67e96e2b28870bc9c5bb4e9af0))\n- **android:** remove stored references to bridge that holds it in memory ([#6448](https://github.com/ionic-team/capacitor/issues/6448)) ([4737d2b](https://github.com/ionic-team/capacitor/commit/4737d2b46b480c7c0246ac6414494cbbdac7811b))\n- **cli:** Fix cordova plugin config checker over checking 5.x ([#6444](https://github.com/ionic-team/capacitor/issues/6444)) ([9d21a0e](https://github.com/ionic-team/capacitor/commit/9d21a0e363141fa0792c12d977b13692be67cf6d))\n- **ios:** Event listeners were unexpectedly nil ([#6445](https://github.com/ionic-team/capacitor/issues/6445)) ([209d4ed](https://github.com/ionic-team/capacitor/commit/209d4edace610b00e689440a5c08e72f5da60cc2))\n\n### Features\n\n- **android:** Fix for [#6258](https://github.com/ionic-team/capacitor/issues/6258), Add support for modern Huawei devices ([#6402](https://github.com/ionic-team/capacitor/issues/6402)) ([17f2f4a](https://github.com/ionic-team/capacitor/commit/17f2f4ac744a038c1dae3cbd74a670d5ede92ef3))\n- **cli:** Add missing Cap 4 to Cap 5 migration tasks ([#6453](https://github.com/ionic-team/capacitor/issues/6453)) ([7dff363](https://github.com/ionic-team/capacitor/commit/7dff36376d6efa6ea8793b81550979ee9254991b))\n- **cli:** Add support for Android build `--flavor` ([#6437](https://github.com/ionic-team/capacitor/issues/6437)) ([e91a8e7](https://github.com/ionic-team/capacitor/commit/e91a8e795604042684ec9e20b620eee36e0dfda2))\n- **cli:** update migrate command for cap4 -> cap5 ([#6447](https://github.com/ionic-team/capacitor/issues/6447)) ([b1f0a37](https://github.com/ionic-team/capacitor/commit/b1f0a3744d3158dab083ea31ea3e747b937729d2))\n- retain multiple calls per event until consumed ([#6419](https://github.com/ionic-team/capacitor/issues/6419)) ([5aba2cb](https://github.com/ionic-team/capacitor/commit/5aba2cbe29bdbab2a7af861c65d8323acf9c54a6))\n- Upgrade to Typescript 5.x ([#6433](https://github.com/ionic-team/capacitor/issues/6433)) ([88d0ded](https://github.com/ionic-team/capacitor/commit/88d0ded9e7356531ffc4563b9b81a0f3f069484b))\n\n# [5.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/4.7.0...5.0.0-alpha.1) (2023-03-16)\n\n### Bug Fixes\n\n- **android:** handle empty permission list ([#6375](https://github.com/ionic-team/capacitor/issues/6375)) ([b11a9df](https://github.com/ionic-team/capacitor/commit/b11a9df070f18a25364a9109e295556fc75ea7f9))\n- **android:** handle null http headers and params ([#6370](https://github.com/ionic-team/capacitor/issues/6370)) ([e486672](https://github.com/ionic-team/capacitor/commit/e486672731818d5c64c50956562aa4766f169d41))\n- **android:** solve and/or silence lint errors ([#6358](https://github.com/ionic-team/capacitor/issues/6358)) ([c627415](https://github.com/ionic-team/capacitor/commit/c627415743bec92dcb65ab8b8840003d8c0a5286))\n- **cli:** point build to proper workspace name ([#6371](https://github.com/ionic-team/capacitor/issues/6371)) ([78b7a59](https://github.com/ionic-team/capacitor/commit/78b7a591429b5fc1dc7bf87e06e9a1afd3599408))\n- **iOS:** Separate cookies by `; ` rather than `;` when accessing through `document.cookie` ([#6313](https://github.com/ionic-team/capacitor/issues/6313)) ([beade60](https://github.com/ionic-team/capacitor/commit/beade6020e25dc405e796e1b06bf6dd90b217693))\n\n### Features\n\n- **android-template:** Removing enableJetifier ([#6339](https://github.com/ionic-team/capacitor/issues/6339)) ([e118175](https://github.com/ionic-team/capacitor/commit/e11817544fcdc7fb6ecc02907d9a002f4f61c277))\n- **android:** Removing enableJetifier ([#6333](https://github.com/ionic-team/capacitor/issues/6333)) ([fc0b403](https://github.com/ionic-team/capacitor/commit/fc0b403265f63eab35cdb2f262fb1e047db4b6bd))\n- **cli:** Add --forwardPorts option to Capacitor CLI ([#5645](https://github.com/ionic-team/capacitor/issues/5645)) ([2f04d29](https://github.com/ionic-team/capacitor/commit/2f04d29a17eb46f9bd140b93e821e11380699367))\n- **cli:** update init to set androidScheme to https ([#6342](https://github.com/ionic-team/capacitor/issues/6342)) ([158b27e](https://github.com/ionic-team/capacitor/commit/158b27e53fd803c87de32e8da9f0c5117f81a3a5))\n\n# [4.7.0](https://github.com/ionic-team/capacitor/compare/4.6.3...4.7.0) (2023-02-22)\n\n### Bug Fixes\n\n- handle fetch headers that are Headers objects ([#6320](https://github.com/ionic-team/capacitor/issues/6320)) ([cb00e49](https://github.com/ionic-team/capacitor/commit/cb00e4952acca8e877555f30b2190f6685d25934))\n- **cli:** prevent error on manifest element without children ([#6278](https://github.com/ionic-team/capacitor/issues/6278)) ([a7e374f](https://github.com/ionic-team/capacitor/commit/a7e374fc4d834ded437edb4c8a0be98b6691be4c))\n- **cli:** Remove buildOptions from platform capacitor.config.json ([#6292](https://github.com/ionic-team/capacitor/issues/6292)) ([acddcd9](https://github.com/ionic-team/capacitor/commit/acddcd95b40a7d4cc6c7682d2d1019f96dacf68d))\n- **ios:** Avoid double encoding on http urls ([#6288](https://github.com/ionic-team/capacitor/issues/6288)) ([4768085](https://github.com/ionic-team/capacitor/commit/4768085414768bb2c013afcc6c645664893cd297))\n- **ios:** Correctly Attach Headers to Request ([#6303](https://github.com/ionic-team/capacitor/issues/6303)) ([a3f875c](https://github.com/ionic-team/capacitor/commit/a3f875cf42e111fde07d6e87643264b19ed77573))\n\n### Features\n\n- **android:** add ability to create config from a custom file path ([#6264](https://github.com/ionic-team/capacitor/issues/6264)) ([42b4f0f](https://github.com/ionic-team/capacitor/commit/42b4f0f416c8038ae368860007910bb09c8ec84e))\n- **android:** Add SSL Pinning logic ([#6314](https://github.com/ionic-team/capacitor/issues/6314)) ([07f113e](https://github.com/ionic-team/capacitor/commit/07f113e6933e15c45d772f69f7128cbb3706f7b9))\n- **android:** enable loading of assets outside of the content web asset directory ([#6301](https://github.com/ionic-team/capacitor/issues/6301)) ([364497d](https://github.com/ionic-team/capacitor/commit/364497d4aca93fc716a0673ef9103479aed791ec))\n- **cli:** add ssl pinning copy logic ([#6312](https://github.com/ionic-team/capacitor/issues/6312)) ([cce66c1](https://github.com/ionic-team/capacitor/commit/cce66c1d59370ba35db879f4d7a3620d22175ab0))\n\n## [4.6.3](https://github.com/ionic-team/capacitor/compare/4.6.2...4.6.3) (2023-02-03)\n\n### Bug Fixes\n\n- **cli/cordova:** Exclude private framework Headers ([#6229](https://github.com/ionic-team/capacitor/issues/6229)) ([6c2726d](https://github.com/ionic-team/capacitor/commit/6c2726dc0817c9e57c8b7909e4a69a9b376c425d))\n- **ios:** crash when http headers contain numbers ([#6251](https://github.com/ionic-team/capacitor/issues/6251)) ([028c556](https://github.com/ionic-team/capacitor/commit/028c556a50b41ee99fe8f4f1aa2f42d3fd57f92d))\n\n## [4.6.2](https://github.com/ionic-team/capacitor/compare/4.6.1...4.6.2) (2023-01-17)\n\n### Bug Fixes\n\n- **android:** get application/x-www-form-urlencoded as string ([#6165](https://github.com/ionic-team/capacitor/issues/6165)) ([0735e89](https://github.com/ionic-team/capacitor/commit/0735e89d48e77a1ddca97a48e3851f4a0a3ea2c1))\n- **cli:** config file android release type not overridden ([#6205](https://github.com/ionic-team/capacitor/issues/6205)) ([1441c55](https://github.com/ionic-team/capacitor/commit/1441c551737ce42dd2b82fc1a9da1e8311e27f1a))\n- **cli:** flavor flag not using correct apk ([#6151](https://github.com/ionic-team/capacitor/issues/6151)) ([f4e7f19](https://github.com/ionic-team/capacitor/commit/f4e7f19f186e334404b2cd0decc3205e57bf4469))\n- **ios:** CapacitorHttp cannot use delete method ([#6220](https://github.com/ionic-team/capacitor/issues/6220)) ([4d238a9](https://github.com/ionic-team/capacitor/commit/4d238a9e0dcf1e3e8c105c3aa4c7361abf16398e))\n- **ios:** encode whitespace in http plugin urls ([#6169](https://github.com/ionic-team/capacitor/issues/6169)) ([dccb0a9](https://github.com/ionic-team/capacitor/commit/dccb0a99850c7c878906156a509ecd675836ef1a))\n- **ios/android:** better http error handling ([#6208](https://github.com/ionic-team/capacitor/issues/6208)) ([7d4d70a](https://github.com/ionic-team/capacitor/commit/7d4d70a0500b7996c710c0762907f44bdf27c92b))\n\n## [4.6.1](https://github.com/ionic-team/capacitor/compare/4.6.0...4.6.1) (2022-12-05)\n\n### Bug Fixes\n\n- **cli:** support variables in config warn checks ([#6136](https://github.com/ionic-team/capacitor/issues/6136)) ([b460add](https://github.com/ionic-team/capacitor/commit/b460add5e22139f234ca8fae98f174bb7c447292))\n\n# [4.6.0](https://github.com/ionic-team/capacitor/compare/4.5.0...4.6.0) (2022-12-01)\n\n### Bug Fixes\n\n- **android:** Don't run Cordova plugins on ui thread ([#6108](https://github.com/ionic-team/capacitor/issues/6108)) ([592ee86](https://github.com/ionic-team/capacitor/commit/592ee862a58f5cb0737620a0246fe8ae295d27cf))\n- **cli:** useLegacyBridge should be optional ([#6095](https://github.com/ionic-team/capacitor/issues/6095)) ([20f68fe](https://github.com/ionic-team/capacitor/commit/20f68feab2cb88cf8a79a987533839aa49255607))\n- **cli:** warns about config that is present ([#6060](https://github.com/ionic-team/capacitor/issues/6060)) ([7ac43e7](https://github.com/ionic-team/capacitor/commit/7ac43e722139a61129cfecf98da373659b1aeac8))\n- **cookies:** Use Set-Cookie headers to persist cookies ([57f8b39](https://github.com/ionic-team/capacitor/commit/57f8b39d7f4c5ee0e5e5cb316913e9450a81d22b))\n\n### Features\n\n- **android:** Plugin Instance Support ([#6073](https://github.com/ionic-team/capacitor/issues/6073)) ([3d5b7c2](https://github.com/ionic-team/capacitor/commit/3d5b7c2d372cf764c625f46d1e8761e05b8959da))\n- **ios:** Plugin Registration and Plugin Instance Support ([#6072](https://github.com/ionic-team/capacitor/issues/6072)) ([9f1d863](https://github.com/ionic-team/capacitor/commit/9f1d863c1222096334a0dd05f39ce7f984a2763a))\n\n# [4.5.0](https://github.com/ionic-team/capacitor/compare/4.4.0...4.5.0) (2022-11-16)\n\n### Bug Fixes\n\n- **android:** Silence deprecation warning on handlePermissionResult ([#6092](https://github.com/ionic-team/capacitor/issues/6092)) ([888b13e](https://github.com/ionic-team/capacitor/commit/888b13e89c48dab949b38135a3ec443ac4fd852e))\n- **cli:** add vite config to framework detection ([#6039](https://github.com/ionic-team/capacitor/issues/6039)) ([3796d42](https://github.com/ionic-team/capacitor/commit/3796d42665f3150f99c761aa561a9e34d03cae28))\n- **cli:** calculate padding of super.onCreate(savedInstanceState); line ([#6016](https://github.com/ionic-team/capacitor/issues/6016)) ([5729ac1](https://github.com/ionic-team/capacitor/commit/5729ac19e7880713ec52bac431a2756da5aa3109))\n- **cli:** Update gradle versions only if they are older ([#6015](https://github.com/ionic-team/capacitor/issues/6015)) ([ae94101](https://github.com/ionic-team/capacitor/commit/ae941017fff3bcfa75e0788535f356a56ce6fa05))\n- **cli/ios:** Read handleApplicationNotifications configuration option ([#6030](https://github.com/ionic-team/capacitor/issues/6030)) ([99ccf18](https://github.com/ionic-team/capacitor/commit/99ccf181f6ee8a00ed97bdbf9076e2b2ea27cd57))\n\n### Features\n\n- **android/cli:** Allow to use the old addJavascriptInterface bridge ([#6043](https://github.com/ionic-team/capacitor/issues/6043)) ([a6e7c54](https://github.com/ionic-team/capacitor/commit/a6e7c5422687b703492a5fcc49369eacc376143d))\n- **cookies:** add get cookies plugin method ([ba1e770](https://github.com/ionic-team/capacitor/commit/ba1e7702a3338714aee24388c0afea39706c9341))\n\n# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21)\n\n### Bug Fixes\n\n- **android:** added ServerPath object and building options for setting initial load from portals ([#6008](https://github.com/ionic-team/capacitor/issues/6008)) ([205b6e6](https://github.com/ionic-team/capacitor/commit/205b6e61806158244846608b1e6c0c7b26ee4ab7))\n- **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc))\n- **http:** fix exception thrown on 204 responses ([1f6e8be](https://github.com/ionic-team/capacitor/commit/1f6e8be9d8813c4397e2c54ac4c06beb55f97b5f))\n- **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f))\n\n### Features\n\n- **cli:** add build command for android ([#5891](https://github.com/ionic-team/capacitor/issues/5891)) ([6d4e620](https://github.com/ionic-team/capacitor/commit/6d4e620308b6dd97376e3af7de1dd1a530083f1c))\n- **cli:** add build command for ios ([#5925](https://github.com/ionic-team/capacitor/issues/5925)) ([8e8414f](https://github.com/ionic-team/capacitor/commit/8e8414fa6f4ccb245576cc113eb969937613bbf7))\n- **cli:** supports secure live updates in Portals for Capacitor config ([#5955](https://github.com/ionic-team/capacitor/issues/5955)) ([a309b45](https://github.com/ionic-team/capacitor/commit/a309b455fdd190613353bdf0eb04469cf4aa6ccd))\n\n# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21)\n\n### Bug Fixes\n\n- **android:** open external links in browser ([#5913](https://github.com/ionic-team/capacitor/issues/5913)) ([7553ede](https://github.com/ionic-team/capacitor/commit/7553ede93170971e21ab3dec1798443d084ead2a))\n- **android:** set all cookies on proxied requests ([#5781](https://github.com/ionic-team/capacitor/issues/5781)) ([5ef6a38](https://github.com/ionic-team/capacitor/commit/5ef6a3889121dd39a9159ff80250df18854bc557))\n- **android:** set WebViewClient on the WebView ([#5919](https://github.com/ionic-team/capacitor/issues/5919)) ([020ed8e](https://github.com/ionic-team/capacitor/commit/020ed8eaeb7864399d4b93f54ab7601c607d8e0d))\n- **cli:** Find the Info.plist when using scheme ([#5914](https://github.com/ionic-team/capacitor/issues/5914)) ([f7029ac](https://github.com/ionic-team/capacitor/commit/f7029acb885ec60f85a434b6f71e4f2a633c7651))\n- **cli:** Make migrator update gradle wrapper files ([#5910](https://github.com/ionic-team/capacitor/issues/5910)) ([b8b9b1f](https://github.com/ionic-team/capacitor/commit/b8b9b1f96249908435017eea6c427221f1971836))\n- **cli:** Make update from windows use proper paths on Podfile ([#5906](https://github.com/ionic-team/capacitor/issues/5906)) ([c41d28f](https://github.com/ionic-team/capacitor/commit/c41d28f8cc829c6bf69d776280c9f1fdba9f866f))\n- **cli:** show error if npm install on migration failed ([#5904](https://github.com/ionic-team/capacitor/issues/5904)) ([aa60a75](https://github.com/ionic-team/capacitor/commit/aa60a75d9c2c784e127a4d89e4079b412fbe7262))\n- **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e))\n\n### Features\n\n- Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45))\n\n# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08)\n\n### Features\n\n- **cli:** add inline option to copy command ([#5901](https://github.com/ionic-team/capacitor/issues/5901)) ([17fbabb](https://github.com/ionic-team/capacitor/commit/17fbabb2a77d1b356d24048efc5883bd4d049104))\n- **cli:** add scheme and flavor options to run command ([#5873](https://github.com/ionic-team/capacitor/issues/5873)) ([e4c143d](https://github.com/ionic-team/capacitor/commit/e4c143d4da653533570215964808c2f32f5469d3))\n- **cli:** copy signature when using secure live updates ([#5896](https://github.com/ionic-team/capacitor/issues/5896)) ([0f17177](https://github.com/ionic-team/capacitor/commit/0f17177b1c64c0f69f86e990e4e150b820da497b))\n\n# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18)\n\n### Bug Fixes\n\n- **cli:** Also update preferences plugin if present ([#5831](https://github.com/ionic-team/capacitor/issues/5831)) ([b9d5954](https://github.com/ionic-team/capacitor/commit/b9d5954ca0b333f2caa20179b96b049379860ea5))\n- **cli:** Don't add google-services plugin if missing ([#5825](https://github.com/ionic-team/capacitor/issues/5825)) ([48ff9e6](https://github.com/ionic-team/capacitor/commit/48ff9e6461e8037a5c6da87c90efc6bc872d7f08))\n- **cli:** make migrator also update plugin variables ([#5871](https://github.com/ionic-team/capacitor/issues/5871)) ([478d48c](https://github.com/ionic-team/capacitor/commit/478d48c3e322cffc6f0ff7ce590b635de4b41279))\n- **cli:** Migrator put registerPlugin before super.onCreate ([#5828](https://github.com/ionic-team/capacitor/issues/5828)) ([8cd3373](https://github.com/ionic-team/capacitor/commit/8cd3373133903f97a836fd6ac6b7ce4e1ba9317e))\n- **cli:** prevent error on migrate when devDependencies is missing ([#5863](https://github.com/ionic-team/capacitor/issues/5863)) ([474ad1f](https://github.com/ionic-team/capacitor/commit/474ad1f4d4a9ea0636a457836c938dac9f6534e8))\n- **cli:** remove double space in cap 2 variables file ([#5826](https://github.com/ionic-team/capacitor/issues/5826)) ([7184097](https://github.com/ionic-team/capacitor/commit/7184097da88ed34f3e754119f967d262aa5e2add))\n- **cli:** Support of BoM dependencies on cordova plugins ([#5827](https://github.com/ionic-team/capacitor/issues/5827)) ([ea2d95b](https://github.com/ionic-team/capacitor/commit/ea2d95ba43467cd2d4c4637aacab6bf655d9c596))\n- **ios:** Prevent Xcode 14 warning on CAPWebView ([#5821](https://github.com/ionic-team/capacitor/issues/5821)) ([66954ef](https://github.com/ionic-team/capacitor/commit/66954ef6bc93f2038d85a386ef2f8b582af11bc3))\n- **ios:** return proper mimeType on M1 x86_64 simulators ([#5853](https://github.com/ionic-team/capacitor/issues/5853)) ([325b6fe](https://github.com/ionic-team/capacitor/commit/325b6fe83939efaaef44c7e8624e33de742a57e2)), closes [#5793](https://github.com/ionic-team/capacitor/issues/5793)\n- update @types/tar to prevent core build failure ([#5822](https://github.com/ionic-team/capacitor/issues/5822)) ([59e64b7](https://github.com/ionic-team/capacitor/commit/59e64b7c548341c27a8477ddc867290592c43815))\n\n### Features\n\n- **cli:** Option to inline JS source maps during sync ([#5843](https://github.com/ionic-team/capacitor/issues/5843)) ([7ce6dd4](https://github.com/ionic-team/capacitor/commit/7ce6dd4b6fb5cdc395add6f656fbedc785178ae3))\n- **ios:** Add `setServerBasePath(_:)` to CAPBridgeProtocol ([#5860](https://github.com/ionic-team/capacitor/issues/5860)) ([76f28e7](https://github.com/ionic-team/capacitor/commit/76f28e70a5c0a03e4c6b9a93a0c068666a2c38ff))\n\n## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28)\n\n### Bug Fixes\n\n- **cli:** Correct Splash theme update ([#5805](https://github.com/ionic-team/capacitor/issues/5805)) ([25b82a8](https://github.com/ionic-team/capacitor/commit/25b82a84425bf09b2be45b213788b0e13982b9b3))\n- **cli:** Revert some splash migration errors ([#5806](https://github.com/ionic-team/capacitor/issues/5806)) ([471feed](https://github.com/ionic-team/capacitor/commit/471feedc07bef357ac798fcba664bd373e9f8ebf))\n- **ios:** publish Podfile script ([#5799](https://github.com/ionic-team/capacitor/issues/5799)) ([604f03a](https://github.com/ionic-team/capacitor/commit/604f03a29bc500d2841987d0a0f1b20d34fba7d6))\n\n# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27)\n\n### Bug Fixes\n\n- **android:** Publish proguard-rules.pro on npm ([#5761](https://github.com/ionic-team/capacitor/issues/5761)) ([df77103](https://github.com/ionic-team/capacitor/commit/df77103ca411fa452239099769289eeeea2404d2))\n- **ios:** error data is optional ([#5782](https://github.com/ionic-team/capacitor/issues/5782)) ([da48d79](https://github.com/ionic-team/capacitor/commit/da48d798c3463de9de188ae6a6475fd6afba6091))\n\n### Features\n\n- **android:** Add android.minWebviewVersion configuration option ([#5768](https://github.com/ionic-team/capacitor/issues/5768)) ([ad83827](https://github.com/ionic-team/capacitor/commit/ad838279e9cd190ce6f1a020a0ac9e3916786324))\n- **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625))\n- **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264))\n- **android-template:** Use Android 12 splash API ([#5777](https://github.com/ionic-team/capacitor/issues/5777)) ([f3ab951](https://github.com/ionic-team/capacitor/commit/f3ab9519e1f08d5dfeb2db61b6939725be92b4f3))\n- **cli:** add migrator for cap3 to cap4 ([#5762](https://github.com/ionic-team/capacitor/issues/5762)) ([7cb660a](https://github.com/ionic-team/capacitor/commit/7cb660a34d9a87274761d4492d0d77c9ef44ace8))\n- **ios:** Add overrideable router var for CAPWebView. ([#5743](https://github.com/ionic-team/capacitor/issues/5743)) ([c1de1c0](https://github.com/ionic-team/capacitor/commit/c1de1c0138aad188a760118e35983d10d257f8e7))\n- **iOS:** post install script for deployment target ([#5783](https://github.com/ionic-team/capacitor/issues/5783)) ([f5afa94](https://github.com/ionic-team/capacitor/commit/f5afa94b3b9c246d87b2af03359840f503bace90))\n- Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3))\n\n# [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08)\n\n### Bug Fixes\n\n- **ios:** Add check for both serverURL and localURL in navigation ([#5736](https://github.com/ionic-team/capacitor/issues/5736)) ([8e824f3](https://github.com/ionic-team/capacitor/commit/8e824f33ad4df898fb8c0936a8f5e9041832a5c5))\n- **ios:** properly deliver retained events after listener re-add [#5732](https://github.com/ionic-team/capacitor/issues/5732) ([c5d6328](https://github.com/ionic-team/capacitor/commit/c5d632831924a1bcc868bc46b42f7ff619408752))\n\n### Features\n\n- **ios:** Add `setServerBasePath(path:)` to CAPWebView ([#5742](https://github.com/ionic-team/capacitor/issues/5742)) ([1afbf8a](https://github.com/ionic-team/capacitor/commit/1afbf8a9dd0b8f7b1ac439d24e5d8ba26f786318))\n- Add CapWebView ([#5715](https://github.com/ionic-team/capacitor/issues/5715)) ([143d266](https://github.com/ionic-team/capacitor/commit/143d266ef0a818bac59dbbdaeda3b5c382ebfa1d))\n\n# [4.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.0...4.0.0-beta.1) (2022-06-27)\n\n### Bug Fixes\n\n- **ios:** Remove Cordova as an embedded framework ([#5709](https://github.com/ionic-team/capacitor/issues/5709)) ([bbf6d24](https://github.com/ionic-team/capacitor/commit/bbf6d248bf9217a5c5c6c15c7bcfeda209aba5b1))\n\n### Features\n\n- **ios:** Allow to configure popover size ([#5717](https://github.com/ionic-team/capacitor/issues/5717)) ([ca1a125](https://github.com/ionic-team/capacitor/commit/ca1a125e5ab05d6066dd303bc75e99dfe21f210a))\n\n# [4.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.6.0...4.0.0-beta.0) (2022-06-17)\n\n### Bug Fixes\n\n- **android:** make removeAllListeners return a promise ([#5527](https://github.com/ionic-team/capacitor/issues/5527)) ([6f4d858](https://github.com/ionic-team/capacitor/commit/6f4d858ea879d97109c0c7da2d664d04806adc2a))\n- **android:** prevent app from loading if server.url is invalid ([d4a0dea](https://github.com/ionic-team/capacitor/commit/d4a0deaa37eda4476f0be030e266c2c1260fc6e8))\n- **cli:** Use CURRENT_PROJECT_VERSION variable on ios template ([#5418](https://github.com/ionic-team/capacitor/issues/5418)) ([0a07648](https://github.com/ionic-team/capacitor/commit/0a07648b4d1c5fb1fd7de3c72ac1bbcb30f48203))\n- **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc))\n\n### Features\n\n- **android:** don't allow server.androidScheme to be set to schemes handled by WebView ([01285ba](https://github.com/ionic-team/capacitor/commit/01285ba253d602b08a41240ad2ccf370730d51a3))\n- **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3))\n- **android:** set default targetSDK to 32 ([#5611](https://github.com/ionic-team/capacitor/issues/5611)) ([416b966](https://github.com/ionic-team/capacitor/commit/416b9662fbf6233d23216c0c0441862603c3a723))\n- **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad))\n- **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672))\n- **android-template:** use DayNight theme as default ([#5643](https://github.com/ionic-team/capacitor/issues/5643)) ([9d76869](https://github.com/ionic-team/capacitor/commit/9d76869129cec6ea67c083a850447b4bfcf48947))\n- **cli:** export android-template activity for SDK 31 support ([#5351](https://github.com/ionic-team/capacitor/issues/5351)) ([79b4a3c](https://github.com/ionic-team/capacitor/commit/79b4a3c56ce9704bc8f5b0b8ce6d5e60f86d4d2c))\n- **cli:** set targetSDK to 31 in android-template ([#5443](https://github.com/ionic-team/capacitor/issues/5443)) ([8793c58](https://github.com/ionic-team/capacitor/commit/8793c58f24611d7780aff80d547b367f4114f7c5))\n- **cli:** set targetSDK to 32 in android-template ([#5612](https://github.com/ionic-team/capacitor/issues/5612)) ([8b8be47](https://github.com/ionic-team/capacitor/commit/8b8be4706b7534e346738479865e5f66a25005bf))\n- **cli:** Upgrade Gradle to 7.4 in the template ([#5446](https://github.com/ionic-team/capacitor/issues/5446)) ([11b648d](https://github.com/ionic-team/capacitor/commit/11b648d21567c5ab8e7e195fdefec1e1254ce62a))\n- **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))\n- **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))\n- **ios:** Support of range requests on WebViewAssetHandler ([#5659](https://github.com/ionic-team/capacitor/issues/5659)) ([348c08d](https://github.com/ionic-team/capacitor/commit/348c08d511e9d57a1b2ecedc3290c65fa9ba3924))\n\n# [3.6.0](https://github.com/ionic-team/capacitor/compare/3.5.1...3.6.0) (2022-06-17)\n\n### Bug Fixes\n\n- **ios:** Use `URL(fileURLWithPath:)` instead of `URL(string:)` ([#5603](https://github.com/ionic-team/capacitor/issues/5603)) ([5fac1b2](https://github.com/ionic-team/capacitor/commit/5fac1b2da5aa5882087716cb2aa862d89173f4a1))\n\n### Features\n\n- **android:** update support for Portals for Capacitor to include Live Updates ([#5660](https://github.com/ionic-team/capacitor/issues/5660)) ([62f0a5e](https://github.com/ionic-team/capacitor/commit/62f0a5eaa40776aad79dbf8f8c0900037d3cc97e))\n- **iOS, Android:** add AppUUID Lib for plugins ([#5690](https://github.com/ionic-team/capacitor/issues/5690)) ([05e76cf](https://github.com/ionic-team/capacitor/commit/05e76cf526a44e07fa75f9482fa2223a13918638))\n\n# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12)\n\n### Bug Fixes\n\n- **android:** make removeAllListeners return a promise ([#5527](https://github.com/ionic-team/capacitor/issues/5527)) ([6f4d858](https://github.com/ionic-team/capacitor/commit/6f4d858ea879d97109c0c7da2d664d04806adc2a))\n- **android:** prevent app from loading if server.url is invalid ([d4a0dea](https://github.com/ionic-team/capacitor/commit/d4a0deaa37eda4476f0be030e266c2c1260fc6e8))\n- **cli:** Use CURRENT_PROJECT_VERSION variable on ios template ([#5418](https://github.com/ionic-team/capacitor/issues/5418)) ([0a07648](https://github.com/ionic-team/capacitor/commit/0a07648b4d1c5fb1fd7de3c72ac1bbcb30f48203))\n- **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc))\n\n### Features\n\n- **android:** don't allow server.androidScheme to be set to schemes handled by WebView ([01285ba](https://github.com/ionic-team/capacitor/commit/01285ba253d602b08a41240ad2ccf370730d51a3))\n- **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3))\n- **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad))\n- **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672))\n- **cli:** export android-template activity for SDK 31 support ([#5351](https://github.com/ionic-team/capacitor/issues/5351)) ([79b4a3c](https://github.com/ionic-team/capacitor/commit/79b4a3c56ce9704bc8f5b0b8ce6d5e60f86d4d2c))\n- **cli:** set targetSDK to 31 in android-template ([#5443](https://github.com/ionic-team/capacitor/issues/5443)) ([8793c58](https://github.com/ionic-team/capacitor/commit/8793c58f24611d7780aff80d547b367f4114f7c5))\n- **cli:** Upgrade Gradle to 7.4 in the template ([#5446](https://github.com/ionic-team/capacitor/issues/5446)) ([11b648d](https://github.com/ionic-team/capacitor/commit/11b648d21567c5ab8e7e195fdefec1e1254ce62a))\n- **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))\n- **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))\n\n## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04)\n\n### Bug Fixes\n\n- **android:** move initialFocus on webview into config ([#5579](https://github.com/ionic-team/capacitor/issues/5579)) ([8b4e861](https://github.com/ionic-team/capacitor/commit/8b4e861514b0fbe08e9296f49c280234f54742e1))\n- **core:** Make cordova bridge use Promise instead of setTimeout ([#5586](https://github.com/ionic-team/capacitor/issues/5586)) ([f35d96b](https://github.com/ionic-team/capacitor/commit/f35d96b185f5890600a64b78e6bf939c336cbb2d))\n\n# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22)\n\n### Features\n\n- **android:** Add overridable routing for WebViewLocalServer ([#5553](https://github.com/ionic-team/capacitor/issues/5553)) ([3bb288e](https://github.com/ionic-team/capacitor/commit/3bb288e848c5c0e49c1e58c0782e0b1ffd7b1f31))\n- **cli:** support capacitor portals plugin changes needed ([#5558](https://github.com/ionic-team/capacitor/issues/5558)) ([6810a19](https://github.com/ionic-team/capacitor/commit/6810a19ae2bbda1f4b2afad61d37ca822ca157f5))\n- **ios:** Add overrideable routing for CAPBridgeViewController subclasses ([#5546](https://github.com/ionic-team/capacitor/issues/5546)) ([8875d5e](https://github.com/ionic-team/capacitor/commit/8875d5e2721e8a8ee763ce70cb672db383f36efa))\n\n# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25)\n\n### Bug Fixes\n\n- **cli:** Use CURRENT_PROJECT_VERSION variable on ios template ([#5418](https://github.com/ionic-team/capacitor/issues/5418)) ([0a07648](https://github.com/ionic-team/capacitor/commit/0a07648b4d1c5fb1fd7de3c72ac1bbcb30f48203))\n\n### Features\n\n- **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3))\n- **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad))\n- **cli:** export android-template activity for SDK 31 support ([#5351](https://github.com/ionic-team/capacitor/issues/5351)) ([79b4a3c](https://github.com/ionic-team/capacitor/commit/79b4a3c56ce9704bc8f5b0b8ce6d5e60f86d4d2c))\n- **cli:** set targetSDK to 31 in android-template ([#5443](https://github.com/ionic-team/capacitor/issues/5443)) ([8793c58](https://github.com/ionic-team/capacitor/commit/8793c58f24611d7780aff80d547b367f4114f7c5))\n- **cli:** Upgrade Gradle to 7.4 in the template ([#5446](https://github.com/ionic-team/capacitor/issues/5446)) ([11b648d](https://github.com/ionic-team/capacitor/commit/11b648d21567c5ab8e7e195fdefec1e1254ce62a))\n- **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))\n\n## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04)\n\n### Bug Fixes\n\n- **cli:** avoid srcFiles is not iterable on kotlinNeededCheck ([#5481](https://github.com/ionic-team/capacitor/issues/5481)) ([3c2b579](https://github.com/ionic-team/capacitor/commit/3c2b579c6edb1fc69d85689b268eb92067b7821b))\n\n## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03)\n\n### Bug Fixes\n\n- **android:** compatibility with cordova kotlin plugins ([#5438](https://github.com/ionic-team/capacitor/issues/5438)) ([55bf004](https://github.com/ionic-team/capacitor/commit/55bf004897b3feb280ab6b6575d2a2c1a0a183e2))\n\n## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09)\n\n### Bug Fixes\n\n- **ci:** add token to ghr call in xcframework build action ([#5422](https://github.com/ionic-team/capacitor/issues/5422)) ([5573a77](https://github.com/ionic-team/capacitor/commit/5573a77b13539a5c36e4fbc7a1cd6548689d83d3))\n- **ci:** Fix tags/releases for Attach .xcframework CI action ([#5423](https://github.com/ionic-team/capacitor/issues/5423)) ([57b3df3](https://github.com/ionic-team/capacitor/commit/57b3df3724a2bda8e59af83f99c127b1503b5c37))\n- **cli:** Better error on gradlew permission problems ([#5405](https://github.com/ionic-team/capacitor/issues/5405)) ([9420f08](https://github.com/ionic-team/capacitor/commit/9420f08dedad78cfaa5500cccf8bdbf1a9140684))\n- **ios:** Reload webView on webViewWebContentProcessDidTerminate ([#5391](https://github.com/ionic-team/capacitor/issues/5391)) ([beebff4](https://github.com/ionic-team/capacitor/commit/beebff4550575c28c233937a11a8eacf5a76411c))\n\n# [3.4.0](https://github.com/ionic-team/capacitor/compare/3.3.4...3.4.0) (2022-01-19)\n\n### Bug Fixes\n\n- **android:** Add appcompat to capacitor-cordova-android-plugins module ([#5373](https://github.com/ionic-team/capacitor/issues/5373)) ([1c756b7](https://github.com/ionic-team/capacitor/commit/1c756b77bf334036a4b30ddac86ac926fa6c0b3d))\n- **android:** prevent input file crash if accept has . ([#5363](https://github.com/ionic-team/capacitor/issues/5363)) ([bdacb30](https://github.com/ionic-team/capacitor/commit/bdacb300bb6391dc4b84bb2bab075df993a15cba))\n\n### Features\n\n- **android:** Add getLong helper on PluginCall ([#5235](https://github.com/ionic-team/capacitor/issues/5235)) ([26261fb](https://github.com/ionic-team/capacitor/commit/26261fb49211330c4db72c259359565da7d7bc4b))\n- **ios:** Add new iOS 15 Motion permission delegate ([#5317](https://github.com/ionic-team/capacitor/issues/5317)) ([c05a3cb](https://github.com/ionic-team/capacitor/commit/c05a3cbbf02217e3972d5e067970cae18bff3faa))\n- **ios:** Add new iOS15 media capture permission delegate ([#5196](https://github.com/ionic-team/capacitor/issues/5196)) ([d8b54ac](https://github.com/ionic-team/capacitor/commit/d8b54ac23414bfe56e50e1254066630a6f87eb0e))\n\n## [3.3.4](https://github.com/ionic-team/capacitor/compare/3.3.3...3.3.4) (2022-01-05)\n\n### Bug Fixes\n\n- **android:** Prevent crash if activity killed on input file ([#5328](https://github.com/ionic-team/capacitor/issues/5328)) ([a206841](https://github.com/ionic-team/capacitor/commit/a20684180a9b6fd50547ae578f21531faa116da5))\n- **cli:** Escape appName from invalid characters on add ([#5325](https://github.com/ionic-team/capacitor/issues/5325)) ([033f4ee](https://github.com/ionic-team/capacitor/commit/033f4eef59fdb7cc32018b162114511448bc46a6))\n- **cli:** sync failing if Info.plist is localized ([#5333](https://github.com/ionic-team/capacitor/issues/5333)) ([df7a104](https://github.com/ionic-team/capacitor/commit/df7a1041c4e2d9a5a1ceef247ed00f9f8467df76))\n\n## [3.3.3](https://github.com/ionic-team/capacitor/compare/3.3.2...3.3.3) (2021-12-08)\n\n### Bug Fixes\n\n- **android:** change logging level of google services message ([#5189](https://github.com/ionic-team/capacitor/issues/5189)) ([6b1dd43](https://github.com/ionic-team/capacitor/commit/6b1dd430dc91eb9e2b36edaa173ccc294c9fb4ff))\n- **android:** Prevent crash in restoreInstanceState if bundleData is null ([#5289](https://github.com/ionic-team/capacitor/issues/5289)) ([622d62f](https://github.com/ionic-team/capacitor/commit/622d62fc0d7cd79558bf6f11331bd7d6690aa4f9))\n- **android:** restrict android run command to configured flavour ([#5256](https://github.com/ionic-team/capacitor/issues/5256)) ([ba84443](https://github.com/ionic-team/capacitor/commit/ba84443dce9c81e09140def57a60018b527b5bb5))\n- **cli:** Add onesignal-cordova-plugin to the static list again ([#5262](https://github.com/ionic-team/capacitor/issues/5262)) ([e67ca99](https://github.com/ionic-team/capacitor/commit/e67ca9964c5a923d35f5cf41eb802c665563726f))\n- **ios:** Present js alert on top of the presented VC ([#5282](https://github.com/ionic-team/capacitor/issues/5282)) ([a53d236](https://github.com/ionic-team/capacitor/commit/a53d236452e99d1e6151e19e313b3d1545957419))\n\n## [3.3.2](https://github.com/ionic-team/capacitor/compare/3.3.1...3.3.2) (2021-11-17)\n\n### Bug Fixes\n\n- **android:** Allow web geolocation if only COARSE_LOCATION is granted ([#5236](https://github.com/ionic-team/capacitor/issues/5236)) ([bc7b24e](https://github.com/ionic-team/capacitor/commit/bc7b24e9b58b194b32b750c5816c8d8ef180834a))\n- **cli:** add cordova-plugin-google-analytics to static list ([#5220](https://github.com/ionic-team/capacitor/issues/5220)) ([67a996c](https://github.com/ionic-team/capacitor/commit/67a996c0a6896e32c41ea01822d6435fdd706b84))\n- **cli:** Add plugin to static list if pod has use-frameworks ([#5232](https://github.com/ionic-team/capacitor/issues/5232)) ([8a0518b](https://github.com/ionic-team/capacitor/commit/8a0518be9f6f6a4be4a9f1366cb8dcb191225b9d))\n- **cli:** sync gradle from android folder ([#5233](https://github.com/ionic-team/capacitor/issues/5233)) ([cd779c4](https://github.com/ionic-team/capacitor/commit/cd779c4b6ed4ffc96777be7c94a0af4baca6d6d5))\n\n## [3.3.1](https://github.com/ionic-team/capacitor/compare/3.3.0...3.3.1) (2021-11-05)\n\n### Bug Fixes\n\n- **cli:** Make config don't error if iOS is missing ([#5212](https://github.com/ionic-team/capacitor/issues/5212)) ([db9f12b](https://github.com/ionic-team/capacitor/commit/db9f12b545994b2ed88098c0168bb051f8191771))\n\n# [3.3.0](https://github.com/ionic-team/capacitor/compare/3.2.5...3.3.0) (2021-11-03)\n\n### Bug Fixes\n\n- **cli:** Add Batch plugin to static list ([#5138](https://github.com/ionic-team/capacitor/issues/5138)) ([9470633](https://github.com/ionic-team/capacitor/commit/94706338c096b30390fa288c9b107e253923a644))\n- **cli:** Add onesignal-cordova-plugin to static pod list ([#5143](https://github.com/ionic-team/capacitor/issues/5143)) ([937e240](https://github.com/ionic-team/capacitor/commit/937e2408f9bb60691e653b70d8b7cb02f540b251))\n- **cli:** detect and register multiple plugins from same package ([#5098](https://github.com/ionic-team/capacitor/issues/5098)) ([25e770c](https://github.com/ionic-team/capacitor/commit/25e770c3f598bf3a1e05e21d607ab3ad70268674))\n- **core:** avoid crash on logging circular objects ([#5186](https://github.com/ionic-team/capacitor/issues/5186)) ([1451ec8](https://github.com/ionic-team/capacitor/commit/1451ec850a9ef73267a032638e73f1fc440647b9))\n- **ios:** Avoid CDVScreenOrientationDelegate umbrella header warning ([#5156](https://github.com/ionic-team/capacitor/issues/5156)) ([31ec30d](https://github.com/ionic-team/capacitor/commit/31ec30de193aa3117dbb7eda928ef3a365d5667c))\n\n### Features\n\n- **android:** ability to reload the webview ([#5184](https://github.com/ionic-team/capacitor/issues/5184)) ([c495bed](https://github.com/ionic-team/capacitor/commit/c495bed216ddf05450f185d2d3f09b4052b281a8))\n- **cli:** add support for 'pod install' in VM based environments ([#5144](https://github.com/ionic-team/capacitor/issues/5144)) ([32ecf22](https://github.com/ionic-team/capacitor/commit/32ecf22de0a550756dbfa68b3b17c2333c89a430))\n- **cli:** Allow to configure access origin tags on cordova config.xml ([#5134](https://github.com/ionic-team/capacitor/issues/5134)) ([0841a09](https://github.com/ionic-team/capacitor/commit/0841a093bf73ed4acac9a90be44a8e8a3aedbcdb))\n- **cli:** Allow users to include Cordova plugins to the static list ([#5175](https://github.com/ionic-team/capacitor/issues/5175)) ([664149a](https://github.com/ionic-team/capacitor/commit/664149aadbe80e66dd757315a826ec1ab305edb9))\n\n## [3.2.5](https://github.com/ionic-team/capacitor/compare/3.2.4...3.2.5) (2021-10-13)\n\n### Bug Fixes\n\n- **android:** Avoid ConcurrentModificationException on notifyListeners ([#5125](https://github.com/ionic-team/capacitor/issues/5125)) ([b82bfe0](https://github.com/ionic-team/capacitor/commit/b82bfe0db2e38fa286eb18391b1d5e2f86a1b35c))\n- **android:** Support cordova-android 10 ([#5103](https://github.com/ionic-team/capacitor/issues/5103)) ([e238233](https://github.com/ionic-team/capacitor/commit/e238233dcf34a183af4861176789d1feb1eb51fa))\n- **cli:** create only static pod if needed ([#5099](https://github.com/ionic-team/capacitor/issues/5099)) ([8304744](https://github.com/ionic-team/capacitor/commit/83047445562a52cc927c7c77d55b48288cfc1fcc))\n- **ios:** proper handling of allowNavigation with multiple wildcard ([#5096](https://github.com/ionic-team/capacitor/issues/5096)) ([cda17a6](https://github.com/ionic-team/capacitor/commit/cda17a6c1504235c1c1e4826830f1d0e2ef2d35c))\n\n## [3.2.4](https://github.com/ionic-team/capacitor/compare/3.2.3...3.2.4) (2021-09-27)\n\n### Bug Fixes\n\n- **cli:** await sync on add to avoid telemetry hang ([833bc20](https://github.com/ionic-team/capacitor/commit/833bc20525a2558e03cd0e56c6765ce6828cdfac))\n- **ios:** Add CDVScreenOrientationDelegate protocol on CAPBridgeViewController ([#5070](https://github.com/ionic-team/capacitor/issues/5070)) ([530477d](https://github.com/ionic-team/capacitor/commit/530477d05e1364931f83a30d61d4f9b5cb687b19))\n- **ios:** show correct line number on console logs ([#5073](https://github.com/ionic-team/capacitor/issues/5073)) ([ec41e74](https://github.com/ionic-team/capacitor/commit/ec41e743aa4ba81e791ad446fac461b7f43b46ed))\n\n## [3.2.3](https://github.com/ionic-team/capacitor/compare/3.2.2...3.2.3) (2021-09-15)\n\n### Bug Fixes\n\n- **android:** proguard rules ([#5048](https://github.com/ionic-team/capacitor/issues/5048)) ([cf15c0f](https://github.com/ionic-team/capacitor/commit/cf15c0fb3bd67315011865fedb4157d5076965fd))\n- Add SalesforceMobileSDK-CordovaPlugin to iOS incompatible list ([#5031](https://github.com/ionic-team/capacitor/issues/5031)) ([6f3f79f](https://github.com/ionic-team/capacitor/commit/6f3f79f412b77b0c90988226ec5ade5d0198c706))\n- Define cordovaConfig gradle variable ([#5024](https://github.com/ionic-team/capacitor/issues/5024)) ([55c217e](https://github.com/ionic-team/capacitor/commit/55c217e6898d0270c23c3a7158a5102e9b84ff40))\n- **android:** save activity result launcher calls ([#5004](https://github.com/ionic-team/capacitor/issues/5004)) ([2c1eb60](https://github.com/ionic-team/capacitor/commit/2c1eb603c79b94f6fcc74f0cbef523590b656a1e))\n\n## [3.2.2](https://github.com/ionic-team/capacitor/compare/3.2.1...3.2.2) (2021-09-02)\n\n### Bug Fixes\n\n- **ios:** fixing podspec source paths ([#5002](https://github.com/ionic-team/capacitor/issues/5002)) ([6004a43](https://github.com/ionic-team/capacitor/commit/6004a43c608a4c967e3444c83954ad2095c3dcfd))\n\n## [3.2.1](https://github.com/ionic-team/capacitor/compare/3.2.0...3.2.1) (2021-09-01)\n\n**Note:** Version bump only for package capacitor\n\n# [3.2.0](https://github.com/ionic-team/capacitor/compare/3.1.2...3.2.0) (2021-08-18)\n\n### Bug Fixes\n\n- **android:** Don't inject map files into capacitor script ([#4893](https://github.com/ionic-team/capacitor/issues/4893)) ([992bebc](https://github.com/ionic-team/capacitor/commit/992bebce5a54128ec09b4905c4424fbe392719be))\n- **cli:** Put cordova git pod dependencies in Podfile ([#4940](https://github.com/ionic-team/capacitor/issues/4940)) ([642dbf4](https://github.com/ionic-team/capacitor/commit/642dbf433e22bb695e5f782bd685de42eb2afada))\n- **cli:** Separate Swift plugins from ObjC plugins ([#4925](https://github.com/ionic-team/capacitor/issues/4925)) ([43ce803](https://github.com/ionic-team/capacitor/commit/43ce803975ccd66823aab1e8c0d44d0ca81c6b2f))\n\n### Features\n\n- **core:** implement CapacitorCustomPlatform for 3rd party platforms ([#4771](https://github.com/ionic-team/capacitor/issues/4771)) ([12c6294](https://github.com/ionic-team/capacitor/commit/12c6294b9eb82976b1322f00da9ba5a6004f7977))\n\n## [3.1.2](https://github.com/ionic-team/capacitor/compare/3.1.1...3.1.2) (2021-07-21)\n\n### Bug Fixes\n\n- **android:** add missing android webview lifecycle events ([6a7c4e3](https://github.com/ionic-team/capacitor/commit/6a7c4e3b3a250270ac5c4b0f09da2a613ef2cf17))\n- **android:** Set theme an content view on onCreate ([#4841](https://github.com/ionic-team/capacitor/issues/4841)) ([8950c60](https://github.com/ionic-team/capacitor/commit/8950c600bb6e3804b79c62e83fef2253c2cc2389))\n- **cli:** Don't warn about hideLogs on some commands ([#4813](https://github.com/ionic-team/capacitor/issues/4813)) ([dc279cc](https://github.com/ionic-team/capacitor/commit/dc279cc0a4ba8332296c65ca00647829f43ed1d9))\n- **core:** handle toJSON() in plugin objects ([#4823](https://github.com/ionic-team/capacitor/issues/4823)) ([0479822](https://github.com/ionic-team/capacitor/commit/04798221666437408f22947253a18ccb4f9e409e))\n- **core:** Modify safeStringify to allow multiple null values ([#4853](https://github.com/ionic-team/capacitor/issues/4853)) ([854539b](https://github.com/ionic-team/capacitor/commit/854539b62a658e484954edbe38b25eea1b0b6f10))\n\n## [3.1.1](https://github.com/ionic-team/capacitor/compare/3.1.0...3.1.1) (2021-07-07)\n\n### Bug Fixes\n\n- fixing peer deps issues in android and ios libs ([310d9f4](https://github.com/ionic-team/capacitor/commit/310d9f486db976cb258fcda5ac893f019667617f))\n\n# [3.1.0](https://github.com/ionic-team/capacitor/compare/3.0.2...3.1.0) (2021-07-07)\n\n### Bug Fixes\n\n- **cli:** Don't error if there are no scripts ([#4763](https://github.com/ionic-team/capacitor/issues/4763)) ([dec3fb2](https://github.com/ionic-team/capacitor/commit/dec3fb285239912980f2abea1cf48c583da6a163))\n- **ios:** isNewBinary is true if defaults have no stored versions ([#4779](https://github.com/ionic-team/capacitor/issues/4779)) ([bd86dbe](https://github.com/ionic-team/capacitor/commit/bd86dbeb74771ed201d0100773babf49e6764818))\n\n### Features\n\n- **cli:** Add hooks to call into npm scripts when capacitor commands run ([#4739](https://github.com/ionic-team/capacitor/issues/4739)) ([515230c](https://github.com/ionic-team/capacitor/commit/515230ccefec76d4b7ed03ef1122709d1b63b58a))\n- **cli:** allow run command to use flavors ([#4782](https://github.com/ionic-team/capacitor/issues/4782)) ([05cb853](https://github.com/ionic-team/capacitor/commit/05cb8533d4479efd3dc823b18f48699302f462ba))\n- **ios:** Add limitsNavigationsToAppBoundDomains configuration option ([#4789](https://github.com/ionic-team/capacitor/issues/4789)) ([2b7016f](https://github.com/ionic-team/capacitor/commit/2b7016f3b4d62fd8c9d03fde2745b3d515bf08b2))\n\n## [3.0.2](https://github.com/ionic-team/capacitor/compare/3.0.1...3.0.2) (2021-06-23)\n\n### Bug Fixes\n\n- **android:** Set WEBVIEW_SERVER_URL before injecting native-bridge ([#4748](https://github.com/ionic-team/capacitor/issues/4748)) ([5d6b179](https://github.com/ionic-team/capacitor/commit/5d6b17994abc7ad770b95e3a9fc29aecf5d9fc05))\n- **cli:** correctly show EACCES error on run ([#4742](https://github.com/ionic-team/capacitor/issues/4742)) ([2ab8778](https://github.com/ionic-team/capacitor/commit/2ab877881a292bba0aed946b20c3c6bb58808e58))\n- **cli:** Don't error on ios sync on non macOS ([#4723](https://github.com/ionic-team/capacitor/issues/4723)) ([368ffad](https://github.com/ionic-team/capacitor/commit/368ffad03612841a8f228c6a174c141659f5293d))\n- **core:** cordova events not firing ([#4712](https://github.com/ionic-team/capacitor/issues/4712)) ([ca4e3b6](https://github.com/ionic-team/capacitor/commit/ca4e3b62dba6a40e593a1404ba2fe2b416a4ac14))\n- **ios:** Use proper native events for cordova events ([#4720](https://github.com/ionic-team/capacitor/issues/4720)) ([99c21dc](https://github.com/ionic-team/capacitor/commit/99c21dcf98f1418d992e845492c730160611783a))\n\n## [3.0.1](https://github.com/ionic-team/capacitor/compare/3.0.0...3.0.1) (2021-06-09)\n\n### Bug Fixes\n\n- **android:** Avoid crash on input file ([#4707](https://github.com/ionic-team/capacitor/issues/4707)) ([883c0fe](https://github.com/ionic-team/capacitor/commit/883c0fe4a8a33d2e14894d9b307f4d7ce6d13bad))\n- **android:** Make proxy handle user info in server url ([#4699](https://github.com/ionic-team/capacitor/issues/4699)) ([baeed45](https://github.com/ionic-team/capacitor/commit/baeed45038134d446aef7747e5ad5ce4ac07c438))\n- **android:** Reset bridge on onPageStarted only ([#4634](https://github.com/ionic-team/capacitor/issues/4634)) ([96e4830](https://github.com/ionic-team/capacitor/commit/96e483046c9128dbcaec21efb0f5d619c6b1185f))\n- **cli:** add priority to framework detection ([#4617](https://github.com/ionic-team/capacitor/issues/4617)) ([6a22f03](https://github.com/ionic-team/capacitor/commit/6a22f0375921fc7c015bc72e036dee014b1baed9))\n- **cli:** Better native-run error ([#4676](https://github.com/ionic-team/capacitor/issues/4676)) ([39eebd0](https://github.com/ionic-team/capacitor/commit/39eebd0a2dc45fd5d07f79fb9ad5b919556c5fc5))\n- **cli:** Don't prompt cordova preferences if non interactive ([#4680](https://github.com/ionic-team/capacitor/issues/4680)) ([293527c](https://github.com/ionic-team/capacitor/commit/293527c85296a9b79ce6dbd12e0f3e3e43fbce0b))\n- **cli:** Remove v3 prefix from docs urls ([#4596](https://github.com/ionic-team/capacitor/issues/4596)) ([f99f11a](https://github.com/ionic-team/capacitor/commit/f99f11a6ee65020a8b2e58665e3427de814fba3a))\n- **cli:** Throw error if native-run didn't find targets ([#4681](https://github.com/ionic-team/capacitor/issues/4681)) ([bfbf2b5](https://github.com/ionic-team/capacitor/commit/bfbf2b5ffb48bf1617404385f7407baefcfe3282))\n- Make isPluginAvailable available on bridge ([#4589](https://github.com/ionic-team/capacitor/issues/4589)) ([151e7a8](https://github.com/ionic-team/capacitor/commit/151e7a899d9646dbd5625a2539fd3f2297349bc5))\n\n# [3.0.0](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.4...3.0.0) (2021-05-18)\n\n### Bug Fixes\n\n- **cli:** create-react-app framework detection should not look for react-dev-utils ([#4585](https://github.com/ionic-team/capacitor/issues/4585)) ([9f7910e](https://github.com/ionic-team/capacitor/commit/9f7910ee39ea7721d01428ec65b3d101b8ba963e))\n\n# [3.0.0-rc.4](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.3...3.0.0-rc.4) (2021-05-18)\n\n### Features\n\n- **cli:** auto detect framework's webDir ([#4550](https://github.com/ionic-team/capacitor/issues/4550)) ([329448a](https://github.com/ionic-team/capacitor/commit/329448a26846b5167b16f9169d62a9ff61eef87d))\n\n# [3.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.2...3.0.0-rc.3) (2021-05-11)\n\n**Note:** Version bump only for package capacitor\n\n# [3.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.1...3.0.0-rc.2) (2021-05-07)\n\n### Bug Fixes\n\n- **bridge:** Fix type errors with new Platforms API ([#4524](https://github.com/ionic-team/capacitor/issues/4524)) ([7bbaea8](https://github.com/ionic-team/capacitor/commit/7bbaea85494c53a950abab40bb77f37087e22abe))\n- **bridge:** Safely JSON.Stringify circular json on log ([#4507](https://github.com/ionic-team/capacitor/issues/4507)) ([e4c8fe4](https://github.com/ionic-team/capacitor/commit/e4c8fe41ec3992df5c20e4d0d3b69240ce672e44)), closes [#4506](https://github.com/ionic-team/capacitor/issues/4506)\n- **ios:** Don't auto release saved calls ([#4535](https://github.com/ionic-team/capacitor/issues/4535)) ([4f76933](https://github.com/ionic-team/capacitor/commit/4f76933b98d0461564d3dca9b36d4ea1eba8ed49))\n\n### Features\n\n- **core:** platforms api ([#4255](https://github.com/ionic-team/capacitor/issues/4255)) ([7d62713](https://github.com/ionic-team/capacitor/commit/7d6271369cb15eeab07c0bc7f606de6447a17cd4))\n\n# [3.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.0...3.0.0-rc.1) (2021-04-29)\n\n### Bug Fixes\n\n- generate Capacitor.Plugins object ([#4496](https://github.com/ionic-team/capacitor/issues/4496)) ([1c71b7a](https://github.com/ionic-team/capacitor/commit/1c71b7adb2c325e34d980dbf578dc22afb2c332b))\n- **android:** Release the call after reject/resolve ([#4318](https://github.com/ionic-team/capacitor/issues/4318)) ([a9f30a8](https://github.com/ionic-team/capacitor/commit/a9f30a88bf3cf239a59c4e901e2a9a2a141a9044))\n- **android:** resolve issue with activity result API registration for fragments ([#4402](https://github.com/ionic-team/capacitor/issues/4402)) ([ac6c6bc](https://github.com/ionic-team/capacitor/commit/ac6c6bc031e0c8236004dfb9e1b04f1f849c1519))\n- **cli:** Allow prereleases on node version check ([#4469](https://github.com/ionic-team/capacitor/issues/4469)) ([dd26a98](https://github.com/ionic-team/capacitor/commit/dd26a98bde9c4487178bc4ee45587f86ec53df2a))\n- **cli:** filter targets without id from run device list ([#4397](https://github.com/ionic-team/capacitor/issues/4397)) ([9ec444f](https://github.com/ionic-team/capacitor/commit/9ec444f034d435c7c945e9a20e3ca99a3b1f54d6))\n- **cordova:** use proper targetSdkVersion ([#4498](https://github.com/ionic-team/capacitor/issues/4498)) ([7d48be8](https://github.com/ionic-team/capacitor/commit/7d48be8ce77e1f19e5bb267abdc61eb98b4d0f3c))\n- **core:** Call native implementation before web implementation ([#4493](https://github.com/ionic-team/capacitor/issues/4493)) ([febd606](https://github.com/ionic-team/capacitor/commit/febd60617ab60a3b34132f68f212e9a867d1b434))\n- **core:** Use web listener if there is no native implementation ([#4488](https://github.com/ionic-team/capacitor/issues/4488)) ([196d843](https://github.com/ionic-team/capacitor/commit/196d843a3c9442c5dc6cf61bfe3494fa399dec4f))\n- **ios:** cordova-plugin-screen-orientation compatibility ([#4367](https://github.com/ionic-team/capacitor/issues/4367)) ([b893a57](https://github.com/ionic-team/capacitor/commit/b893a57aaaf3a16e13db9c33037a12f1a5ac92e0))\n- **ios:** fire resume/pause events if no cordova plugins installed ([#4467](https://github.com/ionic-team/capacitor/issues/4467)) ([0105f7a](https://github.com/ionic-team/capacitor/commit/0105f7a2c68f2e7bec16ca23384b6acbb2f3057b))\n- **ios:** put cancel button of confirm/prompt on the left ([#4464](https://github.com/ionic-team/capacitor/issues/4464)) ([e5b53aa](https://github.com/ionic-team/capacitor/commit/e5b53aa687a70938994802c7b1367cfcbb1e3811))\n\n### Features\n\n- Unify logging behavior across environments ([#4416](https://github.com/ionic-team/capacitor/issues/4416)) ([bae0f3d](https://github.com/ionic-team/capacitor/commit/bae0f3d2cee84978636d0f589bc7e2f745671baf))\n- **android:** ability to add listeners to the Capacitor WebView ([#4405](https://github.com/ionic-team/capacitor/issues/4405)) ([7bdcc15](https://github.com/ionic-team/capacitor/commit/7bdcc15a20248fc17b5867b215bba0c43e29b2c0))\n- **cli:** Add signup prompt on first init ([#4440](https://github.com/ionic-team/capacitor/issues/4440)) ([b3faa97](https://github.com/ionic-team/capacitor/commit/b3faa97d9b0ee542a8d90f433545f7e83402c485))\n- **iOS:** Include native-bridge.js as a resource in both Cocoapods and direct build ([#4505](https://github.com/ionic-team/capacitor/issues/4505)) ([c16ccc0](https://github.com/ionic-team/capacitor/commit/c16ccc0118aec57dc23649894bc3bcd83827f89f))\n\n# [3.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.6...3.0.0-rc.0) (2021-03-10)\n\n### Bug Fixes\n\n- **android:** calls re-saved during permission/activity result callbacks were being released ([502a2d1](https://github.com/ionic-team/capacitor/commit/502a2d18ddce870f4caf810b405f7363f2340067))\n- **android:** live reload not working when using adb reverse ([362f221](https://github.com/ionic-team/capacitor/commit/362f2219767a5f28e3ce1f6857a0e20024adc6b6))\n- **cli:** Make tests work on npm 7 ([#4297](https://github.com/ionic-team/capacitor/issues/4297)) ([676c907](https://github.com/ionic-team/capacitor/commit/676c907c7ce84fdc57d00152024144a74d24b137))\n\n### Features\n\n- **android:** add configurable app path for embedded capacitor ([#4264](https://github.com/ionic-team/capacitor/issues/4264)) ([e433691](https://github.com/ionic-team/capacitor/commit/e43369144f7f378edc4f5d4f8dbbafe6cff6a70d))\n- **android:** Unifying saving plugin calls ([#4254](https://github.com/ionic-team/capacitor/issues/4254)) ([a648c51](https://github.com/ionic-team/capacitor/commit/a648c51588627404b5ad30c35943fed18af4a546))\n- **iOS:** Obj-C convenience accessors on CAPPluginCall ([#4309](https://github.com/ionic-team/capacitor/issues/4309)) ([e3657d7](https://github.com/ionic-team/capacitor/commit/e3657d77647187946ffcd4c4791f4a47c768db7f))\n- **iOS:** Unifying saving plugin calls ([#4253](https://github.com/ionic-team/capacitor/issues/4253)) ([de71da5](https://github.com/ionic-team/capacitor/commit/de71da52b80ff52d0234a5301fc6cae675640a33))\n\n# [3.0.0-beta.6](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.5...3.0.0-beta.6) (2021-02-27)\n\n**Note:** Version bump only for package capacitor\n\n# [3.0.0-beta.5](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.4...3.0.0-beta.5) (2021-02-27)\n\n**Note:** Version bump only for package capacitor\n\n# [3.0.0-beta.4](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.3...3.0.0-beta.4) (2021-02-26)\n\n### Bug Fixes\n\n- **cli:** init failure if config.xml is present ([#4227](https://github.com/ionic-team/capacitor/issues/4227)) ([f1703dc](https://github.com/ionic-team/capacitor/commit/f1703dcb3ebaa83df9f2b72ca00eb6721e96dfd9))\n\n# [3.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.2...3.0.0-beta.3) (2021-02-18)\n\n### Bug Fixes\n\n- **cli:** do not error if webDir is missing when adding platforms ([#4215](https://github.com/ionic-team/capacitor/issues/4215)) ([4583add](https://github.com/ionic-team/capacitor/commit/4583add61f5f7ac60de8722664811d96d4095459))\n- **core:** do not add window.cordova on web apps ([#4214](https://github.com/ionic-team/capacitor/issues/4214)) ([6d673ef](https://github.com/ionic-team/capacitor/commit/6d673ef7076f00c37eac0f801c4c487415df6d4d))\n\n### Features\n\n- **cli:** do not require webDir when server.url is set ([#4200](https://github.com/ionic-team/capacitor/issues/4200)) ([91ddfbd](https://github.com/ionic-team/capacitor/commit/91ddfbd3cd1f598906b2ddc5cab8904420f231f6))\n- **iOS:** Add automatic Date serialization to bridge communication ([#4177](https://github.com/ionic-team/capacitor/issues/4177)) ([3dabc69](https://github.com/ionic-team/capacitor/commit/3dabc69eab1c8ce0b7734acb641b67d349ec3093))\n\n# [3.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.1...3.0.0-beta.2) (2021-02-08)\n\n### Bug Fixes\n\n- **core:** handle js.error messages to fix window error handler ([#4124](https://github.com/ionic-team/capacitor/issues/4124)) ([c0deb1d](https://github.com/ionic-team/capacitor/commit/c0deb1de349f5631af08eecbffc0ea4dea97c60d))\n- address bug in `isPluginAvailable()` for web and native ([#4114](https://github.com/ionic-team/capacitor/issues/4114)) ([2fbd954](https://github.com/ionic-team/capacitor/commit/2fbd95465a321b8f4c50d4daf22a63d8043cee9b))\n- **android:** get PermissionState enum by state value ([#4100](https://github.com/ionic-team/capacitor/issues/4100)) ([194ae86](https://github.com/ionic-team/capacitor/commit/194ae8699944bf016132fb64fe48010679a6d64e))\n- **android:** requestPermission call rejects if permission missing in manifest ([55ef5ff](https://github.com/ionic-team/capacitor/commit/55ef5ff38e87729412c44bfa4b2f29e53044cecc))\n- **core:** fix another $$typeof issue ([#4113](https://github.com/ionic-team/capacitor/issues/4113)) ([4cbae41](https://github.com/ionic-team/capacitor/commit/4cbae41908670ab843bea5850da7a2cf1082afdb))\n- **iOS:** preserve null values in bridged types ([#4072](https://github.com/ionic-team/capacitor/issues/4072)) ([6dc691e](https://github.com/ionic-team/capacitor/commit/6dc691e66a07a421d5d4b08028ea05a65b3ddd84))\n- remove USE_PUSH flag and code from iOS template ([#4070](https://github.com/ionic-team/capacitor/issues/4070)) ([6d54243](https://github.com/ionic-team/capacitor/commit/6d54243108883e0b07d725dcc7a1cb8700f1b35e))\n\n### Features\n\n- **android:** activity result use new API and update permission result callbacks to match ([#4127](https://github.com/ionic-team/capacitor/issues/4127)) ([002f1e5](https://github.com/ionic-team/capacitor/commit/002f1e55173a50b9fe918b4eda73b5113b713282))\n- **android:** androidxActivityVersion & androidxFragmentVersion gradle variables ([#4103](https://github.com/ionic-team/capacitor/issues/4103)) ([4f77b96](https://github.com/ionic-team/capacitor/commit/4f77b962be85fc6bfc555a106c5b3e6707526626))\n- **cli:** configurable iOS build scheme ([#4073](https://github.com/ionic-team/capacitor/issues/4073)) ([e6374dc](https://github.com/ionic-team/capacitor/commit/e6374dc88c388a30186e8bfea22ce129ca1a9e02))\n- **cli:** send config, rootDir, and webDir to custom platform hooks ([#4084](https://github.com/ionic-team/capacitor/issues/4084)) ([13e9860](https://github.com/ionic-team/capacitor/commit/13e9860468126ba3c37b25d9093ab5f6cce2df2c))\n\n# [3.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.0...3.0.0-beta.1) (2021-01-14)\n\n**Note:** Version bump only for package capacitor\n\n# [3.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.14...3.0.0-beta.0) (2021-01-13)\n\n### Features\n\n- **core:** add commonjs output format ([#4064](https://github.com/ionic-team/capacitor/issues/4064)) ([74b7be8](https://github.com/ionic-team/capacitor/commit/74b7be89ef1bbf13ccd103410037cfe81c8fc124))\n\n# [3.0.0-alpha.14](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.13...3.0.0-alpha.14) (2021-01-13)\n\n### Bug Fixes\n\n- **android:** append missing new lines on injected cordova files ([#4058](https://github.com/ionic-team/capacitor/issues/4058)) ([dbdc78d](https://github.com/ionic-team/capacitor/commit/dbdc78dc08e016dfbc2454d4f53a49f16f744b3e))\n- **cli:** bump minimum node version to 12.4.0 ([#4059](https://github.com/ionic-team/capacitor/issues/4059)) ([61e3be0](https://github.com/ionic-team/capacitor/commit/61e3be0c865a3591a0d6bcfc27d0bbb72ee98395))\n- **cli:** default to new directory instead of crashing ([70fdf0b](https://github.com/ionic-team/capacitor/commit/70fdf0be0e0f06b4f20e20a3ae4bfef4de2374e9))\n\n### Features\n\n- **android:** method to check permission for an alias ([#4062](https://github.com/ionic-team/capacitor/issues/4062)) ([c88c4b4](https://github.com/ionic-team/capacitor/commit/c88c4b46b949a87c1b89476b75273adef725242b))\n\n# [3.0.0-alpha.13](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.12...3.0.0-alpha.13) (2021-01-13)\n\n### Bug Fixes\n\n- **cli:** use stderr for logs when using --json option ([#4044](https://github.com/ionic-team/capacitor/issues/4044)) ([452a101](https://github.com/ionic-team/capacitor/commit/452a101648fe6da4232d18985c5d814920505920))\n- **iOS:** properly handle date types during JSValue coercion ([#4043](https://github.com/ionic-team/capacitor/issues/4043)) ([1affae7](https://github.com/ionic-team/capacitor/commit/1affae7cf8d2f49681bf25be48633ab985bbd12f))\n- **iOS:** skip Swift type coercion on Cordova plugin calls ([#4048](https://github.com/ionic-team/capacitor/issues/4048)) ([7bb9e0f](https://github.com/ionic-team/capacitor/commit/7bb9e0f22fdea369a6522c2d63a5b56baab9f5ca))\n\n### Features\n\n- **cli:** create TS configuration files in `init` ([#3999](https://github.com/ionic-team/capacitor/issues/3999)) ([fa7003e](https://github.com/ionic-team/capacitor/commit/fa7003e4ef1d988633abb85b1b109c51b94fda42))\n\n# [3.0.0-alpha.12](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.11...3.0.0-alpha.12) (2021-01-08)\n\n### Bug Fixes\n\n- move `public` into iOS target directory ([#4002](https://github.com/ionic-team/capacitor/issues/4002)) ([4f41296](https://github.com/ionic-team/capacitor/commit/4f41296a109cf73fdf8e695849e95f292a543f85))\n- **cli:** run an actual debug build for iOS ([#4014](https://github.com/ionic-team/capacitor/issues/4014)) ([dc6399c](https://github.com/ionic-team/capacitor/commit/dc6399cf0b5eb6afb50274a84dc71486cd3e4173))\n- **core:** fix $$typeof() not implemented error ([#4013](https://github.com/ionic-team/capacitor/issues/4013)) ([c7f80b5](https://github.com/ionic-team/capacitor/commit/c7f80b577c1de60cd0a105f3aaf0d1c314f3150d))\n\n### Features\n\n- **android:** switch to new callback-style permission requests ([#4033](https://github.com/ionic-team/capacitor/issues/4033)) ([cc459de](https://github.com/ionic-team/capacitor/commit/cc459de7fc070c0227e066f3e8b92062728ab45d))\n- **cli:** allow 'export default' style TS config files ([#4031](https://github.com/ionic-team/capacitor/issues/4031)) ([9393667](https://github.com/ionic-team/capacitor/commit/9393667bbe629d6c18a22b16fe3f3c6fe83e11f6))\n- **cli:** opt-in anonymous usage data ([#4022](https://github.com/ionic-team/capacitor/issues/4022)) ([3facfb7](https://github.com/ionic-team/capacitor/commit/3facfb790bff79b00ba1ab6dd8cb331989937da7))\n\n# [3.0.0-alpha.11](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.10...3.0.0-alpha.11) (2020-12-26)\n\n### Features\n\n- **android:** expose CapConfig.loadDefault(), deprecate v2 constructor ([#3964](https://github.com/ionic-team/capacitor/issues/3964)) ([94ae977](https://github.com/ionic-team/capacitor/commit/94ae9774d2467fa7ba0336e7183f6d28cae45908))\n- **iOS:** Open CAPBridgeViewController for subclassing ([#3973](https://github.com/ionic-team/capacitor/issues/3973)) ([a601705](https://github.com/ionic-team/capacitor/commit/a601705f8116ac10d1a0b5942511952c07cf474e))\n\n# [3.0.0-alpha.10](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.9...3.0.0-alpha.10) (2020-12-15)\n\n### Bug Fixes\n\n- **android:** include lint.xml for downstream lint tasks ([efa72f3](https://github.com/ionic-team/capacitor/commit/efa72f38c5f64d3b91cc4c4c7d4d87ab38219893))\n\n# [3.0.0-alpha.9](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.8...3.0.0-alpha.9) (2020-12-15)\n\n### Bug Fixes\n\n- **android:** include lint-baseline.xml for downstream lint tasks ([20ccaa0](https://github.com/ionic-team/capacitor/commit/20ccaa0311dcf8468019325ad976156d92ed0202))\n\n# [3.0.0-alpha.8](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.7...3.0.0-alpha.8) (2020-12-15)\n\n### Bug Fixes\n\n- **Android:** Use plugin's getPermissionStates() to support overriding ([#3939](https://github.com/ionic-team/capacitor/issues/3939)) ([855a607](https://github.com/ionic-team/capacitor/commit/855a60711bcf6cff3215a36fac7e5314a2c4d159))\n- **ios:** expose lastURL getter ([#3898](https://github.com/ionic-team/capacitor/issues/3898)) ([90b7fe3](https://github.com/ionic-team/capacitor/commit/90b7fe39f5a7cb9d584618a6fba66338f2bbf5fe))\n\n### Features\n\n- **android:** add onConfigurationChanged() activity lifecycle hook ([#3936](https://github.com/ionic-team/capacitor/issues/3936)) ([29e9e2c](https://github.com/ionic-team/capacitor/commit/29e9e2c5c30f23eb3ea2e88b1427eed0636e8125))\n- **android:** Add WebColor utility for parsing color ([#3947](https://github.com/ionic-team/capacitor/issues/3947)) ([3746404](https://github.com/ionic-team/capacitor/commit/3746404240459ca9ea8175f2bb241d80746e8328))\n- **Android:** Refactoring configuration ([#3778](https://github.com/ionic-team/capacitor/issues/3778)) ([9820a30](https://github.com/ionic-team/capacitor/commit/9820a30688f0a774eced1676f1927cacde53301f))\n\n# [3.0.0-alpha.7](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.6...3.0.0-alpha.7) (2020-12-02)\n\n### Bug Fixes\n\n- **android:** dont release calls that are manually saved, eg listeners/watchers ([#3857](https://github.com/ionic-team/capacitor/issues/3857)) ([f1c8fe9](https://github.com/ionic-team/capacitor/commit/f1c8fe9e039d25eff2122fe915f17e84477427eb))\n- **android:** fixed breaking change to `handleOnActivityResult` ([#3888](https://github.com/ionic-team/capacitor/issues/3888)) ([5fd60e6](https://github.com/ionic-team/capacitor/commit/5fd60e607b79b46cec08c6af1674305b1199d0a4))\n- **android:** resolve undefined for both checkPermissions and requestPermissions by default ([#3855](https://github.com/ionic-team/capacitor/issues/3855)) ([383f62b](https://github.com/ionic-team/capacitor/commit/383f62b2b6531c579aac469e29b7c1c0c1f7540f))\n- **cli:** Properly detect cocoapods on multiple platforms ([#3810](https://github.com/ionic-team/capacitor/issues/3810)) ([8753694](https://github.com/ionic-team/capacitor/commit/8753694b12033feb01c82bd5985dce2584bae80c))\n- **cli:** run sync instead of copy during run ([#3816](https://github.com/ionic-team/capacitor/issues/3816)) ([ff45340](https://github.com/ionic-team/capacitor/commit/ff4534064c7f47331721d086889a42a97cf30945))\n- **cli:** use correct path for native-run ([02cf1ba](https://github.com/ionic-team/capacitor/commit/02cf1ba4b7d1c419551b6494f08cb90553fd93be))\n- **core:** export PermissionState ([#3775](https://github.com/ionic-team/capacitor/issues/3775)) ([2d5ac96](https://github.com/ionic-team/capacitor/commit/2d5ac963d131a704628f8a421be8429b9f63cf61))\n- **ios:** share message handler between webview and bridge ([#3875](https://github.com/ionic-team/capacitor/issues/3875)) ([f7dff2e](https://github.com/ionic-team/capacitor/commit/f7dff2e661a54bee770940ee1ebd9eab6456ba2e))\n\n### Features\n\n- automatically import Android plugins ([#3788](https://github.com/ionic-team/capacitor/issues/3788)) ([aa1e1c6](https://github.com/ionic-team/capacitor/commit/aa1e1c604e260cc8babb0e7f5230f692bdcf6f09))\n- **android:** Add handlePermissions function for plugins to call ([#3768](https://github.com/ionic-team/capacitor/issues/3768)) ([3a7e282](https://github.com/ionic-team/capacitor/commit/3a7e282a7515784dd343bbf1e3d52e0299bac887))\n- **android:** modified plugin annotation format for multi-permissions and empty (auto-grant) ([#3822](https://github.com/ionic-team/capacitor/issues/3822)) ([1b5a3bd](https://github.com/ionic-team/capacitor/commit/1b5a3bdeb1b35612cf04e58bdf2fca68a0832a14))\n- **cli:** add --no-sync option to run ([#3819](https://github.com/ionic-team/capacitor/issues/3819)) ([8def829](https://github.com/ionic-team/capacitor/commit/8def8290bb182436204380fc711d84fa36c17004))\n- **cli:** add config command ([#3817](https://github.com/ionic-team/capacitor/issues/3817)) ([d3d7f89](https://github.com/ionic-team/capacitor/commit/d3d7f893179a170017a36dd76e9ed7bd374a22b3))\n- **cli:** Add configurable pod path ([#3811](https://github.com/ionic-team/capacitor/issues/3811)) ([88f9187](https://github.com/ionic-team/capacitor/commit/88f9187f41f95ef62f6bf854c63f0b1c91dbc2f7))\n- **cli:** extendable plugin configuration types ([#3858](https://github.com/ionic-team/capacitor/issues/3858)) ([f789526](https://github.com/ionic-team/capacitor/commit/f789526e42283c2a166ff32a2b16b70e65b94ba4))\n- **cli:** locate plugins by allowlist ([#3762](https://github.com/ionic-team/capacitor/issues/3762)) ([81963b6](https://github.com/ionic-team/capacitor/commit/81963b615ccbdc8993d0befbefe87173db4ba108))\n- **cli:** STUDIO_PATH environment variable ([#3755](https://github.com/ionic-team/capacitor/issues/3755)) ([65cef53](https://github.com/ionic-team/capacitor/commit/65cef53277444c173ff928dc4ef196f0dae4c8a7))\n- **cli:** support TS/JS config files ([#3756](https://github.com/ionic-team/capacitor/issues/3756)) ([a52775f](https://github.com/ionic-team/capacitor/commit/a52775fb635a5cbcccf2dcaa955ad12804ad5986))\n- **ios:** add local and remote notification router ([#3796](https://github.com/ionic-team/capacitor/issues/3796)) ([f3edaf9](https://github.com/ionic-team/capacitor/commit/f3edaf93d4328ea3ee90df573bf14ef0efc7553b))\n- **ios:** add path utilities to bridge ([#3842](https://github.com/ionic-team/capacitor/issues/3842)) ([c31eb35](https://github.com/ionic-team/capacitor/commit/c31eb35f83a33626a9d88731c0fff18966c71b0b))\n- **iOS:** Add base implementation of permissions calls ([#3856](https://github.com/ionic-team/capacitor/issues/3856)) ([d733236](https://github.com/ionic-team/capacitor/commit/d7332364212794a5005229defd05c129921d9c5d))\n- **iOS:** Refactoring configuration ([#3759](https://github.com/ionic-team/capacitor/issues/3759)) ([e2e64c2](https://github.com/ionic-team/capacitor/commit/e2e64c23b88d93a1c594df51dddd0c55d5f37770))\n\n# [3.0.0-alpha.6](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.5...3.0.0-alpha.6) (2020-10-30)\n\n### Bug Fixes\n\n- **android:** avoid crash on input file capture ([#3715](https://github.com/ionic-team/capacitor/issues/3715)) ([f502a99](https://github.com/ionic-team/capacitor/commit/f502a9964e28012980d636014043e86e918031d7))\n\n### Features\n\n- improve permissions ([eec61a6](https://github.com/ionic-team/capacitor/commit/eec61a6d8d8edfe94aea1a361787d1e6c736e20d))\n- unified errors and error codes ([#3673](https://github.com/ionic-team/capacitor/issues/3673)) ([f9e0803](https://github.com/ionic-team/capacitor/commit/f9e08038aa88f7453e8235f380d2767a12a7a073))\n- **cli:** add `capacitor run` command ([#3599](https://github.com/ionic-team/capacitor/issues/3599)) ([8576b0f](https://github.com/ionic-team/capacitor/commit/8576b0ff6a048981a07cab91135a0071b724e043))\n\n# [3.0.0-alpha.5](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.4...3.0.0-alpha.5) (2020-10-06)\n\n**Note:** Version bump only\n\n# [3.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.3...3.0.0-alpha.4) (2020-09-23)\n\n### Features\n\n- **cli:** ability to specify custom platform directories ([#3565](https://github.com/ionic-team/capacitor/issues/3565)) ([c6eda55](https://github.com/ionic-team/capacitor/commit/c6eda55482ef56abdfe9a33444e828b771af9386))\n\n# [3.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.2...3.0.0-alpha.3) (2020-09-15)\n\n### Bug Fixes\n\n- **android:** allow directories beginning with underscore in assets dir to be packaged ([c23d993](https://github.com/ionic-team/capacitor/commit/c23d99315acea2f0894e5ff8a08dd42a867b2982))\n\n# [3.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.1...3.0.0-alpha.2) (2020-08-31)\n\n### Features\n\n- Add extension for creating data from data url ([#3474](https://github.com/ionic-team/capacitor/issues/3474)) ([2909fd0](https://github.com/ionic-team/capacitor/commit/2909fd0ac0d9fdb2cdb7fd25e38742451aa05fb1))\n\n# [3.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.0...3.0.0-alpha.1) (2020-08-21)\n\n### Bug Fixes\n\n- **cli:** update plugin template to compile using java 8 ([#3350](https://github.com/ionic-team/capacitor/issues/3350)) ([676917e](https://github.com/ionic-team/capacitor/commit/676917eb81580ea5327d496bd80986e68df6ad04))\n- **core:** provide mock implementation for unimplemented platforms ([#3352](https://github.com/ionic-team/capacitor/issues/3352)) ([befe230](https://github.com/ionic-team/capacitor/commit/befe2300435dbd54b22882fb6586c722f5ef466d))\n- **core:** use more explicit result for Browser plugin events ([#3349](https://github.com/ionic-team/capacitor/issues/3349)) ([75f99d4](https://github.com/ionic-team/capacitor/commit/75f99d4de62a6afb2da0ff876ed3b0d351040184))\n- **core:** use own type for backButton event result ([#3348](https://github.com/ionic-team/capacitor/issues/3348)) ([05d0e45](https://github.com/ionic-team/capacitor/commit/05d0e457eb69d5d39c8bb1d0117bc3d31afdca93))\n- **ios:** config bug from swiftlint refactor ([ace879f](https://github.com/ionic-team/capacitor/commit/ace879f42b19aa064efa80142c3783f736745344))\n\n### Features\n\n- **cli:** add fmt script to plugin template ([#3354](https://github.com/ionic-team/capacitor/issues/3354)) ([9ca1e72](https://github.com/ionic-team/capacitor/commit/9ca1e723334f5d21706a8586c11d73162b47a13a))\n- **core:** add unsupported browser exception ([#3389](https://github.com/ionic-team/capacitor/issues/3389)) ([c51e8f8](https://github.com/ionic-team/capacitor/commit/c51e8f8960c795421b35ad1fdd1cd6afbd7a7dfc))\n\n# [3.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/2.4.6...3.0.0-alpha.0) (2020-07-23)\n\n### Features\n\n- **android:** add custom plugins to BridgeFragment ([#3280](https://github.com/ionic-team/capacitor/issues/3280)) ([d131a5f](https://github.com/ionic-team/capacitor/commit/d131a5fed2b9ae29b6952397ec2f81104545b749))\n- **core:** add `registerPlugin` for importing from plugin packages ([#3305](https://github.com/ionic-team/capacitor/issues/3305)) ([95475cc](https://github.com/ionic-team/capacitor/commit/95475cceb4cbd5be2cc7e18f2cf3045eb6c6f7fd))\n\n### Chores\n\n- refactor(android): remove unused interaction listener on BridgeFragment (#3552) ([fae50b6](https://github.com/ionic-team/capacitor/commit/fae50b6)), closes [#3552](https://github.com/ionic-team/capacitor/issues/3552)\n\n## [2.4.6](https://github.com/ionic-team/capacitor/compare/2.4.5...2.4.6) (2021-01-13)\n\n### Bug Fixes\n\n- fix(ios): correctly initialize cordova plugins with webViewEngine (#4039) ([273fab5](https://github.com/ionic-team/capacitor/commit/273fab5)), closes [#4039](https://github.com/ionic-team/capacitor/issues/4039)\n\n## [2.4.5](https://github.com/ionic-team/capacitor/compare/2.4.4...2.4.5) (2020-12-14)\n\n### Bug Fixes\n\n- fix(ios): avoid crash on portrait apps after taking a photo (#3926) ([f182868](https://github.com/ionic-team/capacitor/commit/f182868)), closes [#3926](https://github.com/ionic-team/capacitor/issues/3926)\n\n## [2.4.4](https://github.com/ionic-team/capacitor/compare/2.4.3...2.4.4) (2020-12-01)\n\n### Bug Fixes\n\n- fix: wildcard in allowNavigation (#3833) ([de1eac8](https://github.com/ionic-team/capacitor/commit/de1eac8)), closes [#3833](https://github.com/ionic-team/capacitor/issues/3833)\n- fix(android): load local assets when using wildcard on allowNavigation (#3834) ([66f2efb](https://github.com/ionic-team/capacitor/commit/66f2efb)), closes [#3834](https://github.com/ionic-team/capacitor/issues/3834)\n- fix(cli): replace AndroidManifest.xml Cordova variables with default value (#3863) ([9965d58](https://github.com/ionic-team/capacitor/commit/9965d58)), closes [#3863](https://github.com/ionic-team/capacitor/issues/3863)\n\n## [2.4.3](https://github.com/ionic-team/capacitor/compare/2.4.2...2.4.3) (2020-11-18)\n\n### Bug Fixes\n\n- fix(ios): Don't get location if permission is not determined (#3802) ([4fb9d348](https://github.com/ionic-team/capacitor/commit/4fb9d348)), closes [#3789](https://github.com/ionic-team/capacitor/issues/3789)\n- fix(Filesystem): avoid directory already exists on append (#3629) ([249073d6](https://github.com/ionic-team/capacitor/commit/249073d6)), closes [#3620](https://github.com/ionic-team/capacitor/issues/3620)\n- fix(android): Avoid SecurityError on Android 10 file share (#3655) ([1c47e15f](https://github.com/ionic-team/capacitor/commit/1c47e15f)), closes [#3638](https://github.com/ionic-team/capacitor/issues/3638)\n\n## [2.4.2](https://github.com/ionic-team/capacitor/compare/2.4.1...2.4.2) (2020-09-24)\n\n### Bug Fixes\n\n- fix(android): BridgeFragment NullPointerException (#3553) ([5133e2e](https://github.com/ionic-team/capacitor/commit/5133e2e)), closes [#3553](https://github.com/ionic-team/capacitor/issues/3553)\n- fix(android): move splash screen trigger before the webview render to prevent flicker (2.x) (#3608) ([cbab54c](https://github.com/ionic-team/capacitor/commit/cbab54c)), closes [#3608](https://github.com/ionic-team/capacitor/issues/3608)\n- fix(cli): halt update upon failure (#3595) ([ec086b0](https://github.com/ionic-team/capacitor/commit/ec086b0)), closes [#3595](https://github.com/ionic-team/capacitor/issues/3595)\n- fix(ios): iterate listeners to avoid mutated while being enumerated (#3572) ([fbaab54](https://github.com/ionic-team/capacitor/commit/fbaab54)), closes [#3572](https://github.com/ionic-team/capacitor/issues/3572)\n\n### Chores\n\n- refactor(android): remove unused interaction listener on BridgeFragment (#3552) ([fae50b6](https://github.com/ionic-team/capacitor/commit/fae50b6)), closes [#3552](https://github.com/ionic-team/capacitor/issues/3552)\n\n## [2.4.1](https://github.com/ionic-team/capacitor/compare/2.4.0...2.4.1) (2020-09-09)\n\n### Bug Fixes\n\n- fix(cli): replace SDK variables with default values on Cordova plugins (#3525) ([090427a](https://github.com/ionic-team/capacitor/commit/090427a)), closes [#3525](https://github.com/ionic-team/capacitor/issues/3525)\n- fix(cordova): add CDVPlugin+Resources category for better plugin support (#3380) ([8d89b91](https://github.com/ionic-team/capacitor/commit/8d89b91)), closes [#3380](https://github.com/ionic-team/capacitor/issues/3380)\n\n### Chores\n\n- chore(cli): remove cordova-plugin-googlemaps from skip list (#3436) ([cfb74af](https://github.com/ionic-team/capacitor/commit/cfb74af)), closes [#3436](https://github.com/ionic-team/capacitor/issues/3436)\n\n## [2.4.0](https://github.com/ionic-team/capacitor/compare/2.3.0...2.4.0) (2020-07-27)\n\n### Bug Fixes\n\n- fix(android): don't return 404 on empty files (#3323) ([cfbd1e3](https://github.com/ionic-team/capacitor/commit/cfbd1e3)), closes [#3323](https://github.com/ionic-team/capacitor/issues/3323)\n- fix(android): fix LocalNotification `on` functionality (#3307) ([15af432](https://github.com/ionic-team/capacitor/commit/15af432)), closes [#3307](https://github.com/ionic-team/capacitor/issues/3307)\n- fix(android): Provide a file name when an image is saved to the gallery to prevent crash (#3331) ([a7b9320](https://github.com/ionic-team/capacitor/commit/a7b9320)), closes [#3331](https://github.com/ionic-team/capacitor/issues/3331)\n- fix(cli): avoid npm gitignore rename on new plugins (#3292) ([2c9b5e1](https://github.com/ionic-team/capacitor/commit/2c9b5e1)), closes [#3292](https://github.com/ionic-team/capacitor/issues/3292)\n- fix(ios): only open a URL when the application is active (#3328) ([8d7c58b](https://github.com/ionic-team/capacitor/commit/8d7c58b)), closes [#3328](https://github.com/ionic-team/capacitor/issues/3328)\n\n### Features\n\n- feat(camera): add `preserveAspectRatio` resizing option (#3309) ([27a8bcb](https://github.com/ionic-team/capacitor/commit/27a8bcb)), closes [#3309](https://github.com/ionic-team/capacitor/issues/3309)\n\n### Chores\n\n- chore: bump peerDependencies for 2.4.0 (#3342) ([2ff7bc4](https://github.com/ionic-team/capacitor/commit/2ff7bc4)), closes [#3342](https://github.com/ionic-team/capacitor/issues/3342)\n- chore: make deploy script push to 2.x branch (#3337) ([d0d30d6](https://github.com/ionic-team/capacitor/commit/d0d30d6)), closes [#3337](https://github.com/ionic-team/capacitor/issues/3337)\n- chore(ci): hook up workflows for 2.x ([89f8ae8](https://github.com/ionic-team/capacitor/commit/89f8ae8))\n- chore(readme): add contributors manually ([519ead8](https://github.com/ionic-team/capacitor/commit/519ead8))\n\n## [2.3.0](https://github.com/ionic-team/capacitor/compare/2.2.1...2.3.0) (2020-07-16)\n\n### Bug Fixes\n\n- fix(android): restore local notifications after device reboot (#3027) ([2a39a7d](https://github.com/ionic-team/capacitor/commit/2a39a7d)), closes [#3027](https://github.com/ionic-team/capacitor/issues/3027)\n- fix(android): Splash.show not resolving if splash is visible (#3262) ([bfd9884](https://github.com/ionic-team/capacitor/commit/bfd9884)), closes [#3262](https://github.com/ionic-team/capacitor/issues/3262)\n- fix(android): use notification sound for notifications, not alarm (#2743) ([b2d50f9](https://github.com/ionic-team/capacitor/commit/b2d50f9)), closes [#2743](https://github.com/ionic-team/capacitor/issues/2743)\n- fix(cli): Avoid duplicate usesCleartextTraffic attribute (#3245) ([861874f](https://github.com/ionic-team/capacitor/commit/861874f)), closes [#3245](https://github.com/ionic-team/capacitor/issues/3245)\n- fix(cordova): patch usages of webView superview (#3177) ([8241c81](https://github.com/ionic-team/capacitor/commit/8241c81)), closes [#3177](https://github.com/ionic-team/capacitor/issues/3177)\n\n### Features\n\n- feat: add appId and appName to device info (#3244) ([0d5e132](https://github.com/ionic-team/capacitor/commit/0d5e132)), closes [#3244](https://github.com/ionic-team/capacitor/issues/3244)\n- feat(android): add ability to share both text and file (#3233) ([4e8b59e](https://github.com/ionic-team/capacitor/commit/4e8b59e)), closes [#3233](https://github.com/ionic-team/capacitor/issues/3233)\n- feat(android): add option to make a notification ongoing (#3165) ([1ee51cd](https://github.com/ionic-team/capacitor/commit/1ee51cd)), closes [#3165](https://github.com/ionic-team/capacitor/issues/3165)\n- feat(android): start animatable layers when splash drawable is layered (#2733) ([606b59f](https://github.com/ionic-team/capacitor/commit/606b59f)), closes [#2733](https://github.com/ionic-team/capacitor/issues/2733)\n- feat(android): update to use androidx.exifinterface.media.ExifInterface ([6196907](https://github.com/ionic-team/capacitor/commit/6196907))\n- feat(cli): add hooks to capacitor commands for custom platforms (#3091) ([c2133c5](https://github.com/ionic-team/capacitor/commit/c2133c5)), closes [#3091](https://github.com/ionic-team/capacitor/issues/3091)\n- feat(ios): improve initial webview loading appearance (#2933) ([49720a5](https://github.com/ionic-team/capacitor/commit/49720a5)), closes [#2933](https://github.com/ionic-team/capacitor/issues/2933)\n- feat(web): add file input method for camera (#1856) ([25505d2](https://github.com/ionic-team/capacitor/commit/25505d2)), closes [#1856](https://github.com/ionic-team/capacitor/issues/1856)\n\n### Chores\n\n- chore: update ios and android peerDependencies (#3274) ([d8ee77b](https://github.com/ionic-team/capacitor/commit/d8ee77b)), closes [#3274](https://github.com/ionic-team/capacitor/issues/3274)\n- chore(android): improve error message when Filesystem.copy fails (#3148) ([598d7dc](https://github.com/ionic-team/capacitor/commit/598d7dc)), closes [#3148](https://github.com/ionic-team/capacitor/issues/3148)\n- chore(android): make androidx.exifinterface version configurable (#3236) ([3c64162](https://github.com/ionic-team/capacitor/commit/3c64162)), closes [#3236](https://github.com/ionic-team/capacitor/issues/3236)\n- chore(cli): add deprecation notice for all electron commands (#3268) ([5e84ce9](https://github.com/ionic-team/capacitor/commit/5e84ce9)), closes [#3268](https://github.com/ionic-team/capacitor/issues/3268)\n- chore(cli): add deprecation notice for electron (#3263) ([b7d5639](https://github.com/ionic-team/capacitor/commit/b7d5639)), closes [#3263](https://github.com/ionic-team/capacitor/issues/3263)\n- chore(cli): pin cli version instead of latest on plugin generation (#3201) ([8651ef1](https://github.com/ionic-team/capacitor/commit/8651ef1)), closes [#3201](https://github.com/ionic-team/capacitor/issues/3201)\n- chore(cli): update plugin generation and plugin template (#3241) ([97a5b9a](https://github.com/ionic-team/capacitor/commit/97a5b9a)), closes [#3241](https://github.com/ionic-team/capacitor/issues/3241)\n- chore(cli): use real path to Capacitor iOS Pods (#3249) ([618f9cf](https://github.com/ionic-team/capacitor/commit/618f9cf)), closes [#3249](https://github.com/ionic-team/capacitor/issues/3249)\n\n## [2.2.1](https://github.com/ionic-team/capacitor/compare/2.2.0...2.2.1) (2020-07-01)\n\n### Bug Fixes\n\n- fix: revert static Config class (#3126) ([d104e9a](https://github.com/ionic-team/capacitor/commit/d104e9a)), closes [#3126](https://github.com/ionic-team/capacitor/issues/3126)\n- fix(android): LocalNotification action not dismissing notification (#3112) ([6f5504b](https://github.com/ionic-team/capacitor/commit/6f5504b)), closes [#3112](https://github.com/ionic-team/capacitor/issues/3112)\n- fix(iOS): Making permissions switch statements exhaustive & supporting new iOS 14 cases (#3160) ([f1d8c8c](https://github.com/ionic-team/capacitor/commit/f1d8c8c)), closes [#3160](https://github.com/ionic-team/capacitor/issues/3160)\n- fix(SplashScreen): show method not resolving if autoHide is false (#3144) ([28a0e42](https://github.com/ionic-team/capacitor/commit/28a0e42)), closes [#3144](https://github.com/ionic-team/capacitor/issues/3144)\n\n### Docs\n\n- docs(cli): clarify \"missing web assets directory\" error message (#3131) ([da8b8a0](https://github.com/ionic-team/capacitor/commit/da8b8a0)), closes [#3131](https://github.com/ionic-team/capacitor/issues/3131)\n- docs(contributing): outline difference between issues & discussions (#3083) ([4617908](https://github.com/ionic-team/capacitor/commit/4617908)), closes [#3083](https://github.com/ionic-team/capacitor/issues/3083)\n\n### Chores\n\n- chore: remove package-lock.json files (#3093) ([9cb36bb](https://github.com/ionic-team/capacitor/commit/9cb36bb)), closes [#3093](https://github.com/ionic-team/capacitor/issues/3093)\n- chore: update capacitor twitter url (#3099) ([782e57a](https://github.com/ionic-team/capacitor/commit/782e57a)), closes [#3099](https://github.com/ionic-team/capacitor/issues/3099)\n- chore: Update changelog for 2.2.0 ([dfe4239](https://github.com/ionic-team/capacitor/commit/dfe4239))\n- chore: update ios and android peerDependencies ([12daa6a](https://github.com/ionic-team/capacitor/commit/12daa6a))\n- chore: update site links (#3152) ([cdd0337](https://github.com/ionic-team/capacitor/commit/cdd0337)), closes [#3152](https://github.com/ionic-team/capacitor/issues/3152)\n- chore: Update the location of capacitor pods for IonicRunner (#3120) ([937e3a2](https://github.com/ionic-team/capacitor/commit/937e3a2)), closes [#3120](https://github.com/ionic-team/capacitor/issues/3120)\n- chore(android): update gradle wrapper to 5.6.4 (#3004) ([3a2244c](https://github.com/ionic-team/capacitor/commit/3a2244c)), closes [#3004](https://github.com/ionic-team/capacitor/issues/3004)\n- chore(ci): add core to CI tests (#3094) ([26e5e74](https://github.com/ionic-team/capacitor/commit/26e5e74)), closes [#3094](https://github.com/ionic-team/capacitor/issues/3094)\n- chore(ci): test multiple xcode versions (#3154) ([7b9ac92](https://github.com/ionic-team/capacitor/commit/7b9ac92)), closes [#3154](https://github.com/ionic-team/capacitor/issues/3154)\n- chore(cli): cleanup doctor output (#3096) ([c2adf4c](https://github.com/ionic-team/capacitor/commit/c2adf4c)), closes [#3096](https://github.com/ionic-team/capacitor/issues/3096)\n- chore(cli): read plugin podspec values from package.json (#3092) ([2e61a9a](https://github.com/ionic-team/capacitor/commit/2e61a9a)), closes [#3092](https://github.com/ionic-team/capacitor/issues/3092)\n- chore(e2e): remove for now (#3086) ([0e33ca4](https://github.com/ionic-team/capacitor/commit/0e33ca4)), closes [#3086](https://github.com/ionic-team/capacitor/issues/3086)\n- chore(github): add link to community proposals ([048b07e](https://github.com/ionic-team/capacitor/commit/048b07e))\n- chore(github): add needs-reply workflows ([e97fd48](https://github.com/ionic-team/capacitor/commit/e97fd48))\n- chore(github): add push handler for capacitor bot ([d2b6513](https://github.com/ionic-team/capacitor/commit/d2b6513))\n- chore(github): allow blank issues ([53c0c15](https://github.com/ionic-team/capacitor/commit/53c0c15))\n- chore(github): change new contributor commit message ([7e36434](https://github.com/ionic-team/capacitor/commit/7e36434))\n- chore(github): checkout for capacitor bot ([d4b8457](https://github.com/ionic-team/capacitor/commit/d4b8457))\n- chore(github): exclude Ionitron from contributors script ([fffa487](https://github.com/ionic-team/capacitor/commit/fffa487))\n- chore(github): hook up capacitor-bot ([c5878a8](https://github.com/ionic-team/capacitor/commit/c5878a8))\n- chore(github): lock in bot for now ([9534707](https://github.com/ionic-team/capacitor/commit/9534707))\n- chore(github): remove duplicate section ([615225a](https://github.com/ionic-team/capacitor/commit/615225a))\n- chore(github): update needs-reply label name ([9bd353c](https://github.com/ionic-team/capacitor/commit/9bd353c))\n- chore(github): use config-based bot ([673344d](https://github.com/ionic-team/capacitor/commit/673344d))\n- chore(readme): add new contributor (#3110) ([c04d8c3](https://github.com/ionic-team/capacitor/commit/c04d8c3)), closes [#3110](https://github.com/ionic-team/capacitor/issues/3110)\n- chore(readme): add new contributor (#3150) ([0bc0152](https://github.com/ionic-team/capacitor/commit/0bc0152)), closes [#3150](https://github.com/ionic-team/capacitor/issues/3150)\n- chore(readme): add new contributor (#3173) ([fe3f552](https://github.com/ionic-team/capacitor/commit/fe3f552)), closes [#3173](https://github.com/ionic-team/capacitor/issues/3173)\n- chore(readme): add new contributor (#3185) ([84ca1ed](https://github.com/ionic-team/capacitor/commit/84ca1ed)), closes [#3185](https://github.com/ionic-team/capacitor/issues/3185)\n- chore(readme): add new contributor (#3190) ([ce258f2](https://github.com/ionic-team/capacitor/commit/ce258f2)), closes [#3190](https://github.com/ionic-team/capacitor/issues/3190)\n- chore(readme): show contributors in readme (#3097) ([c4d749f](https://github.com/ionic-team/capacitor/commit/c4d749f)), closes [#3097](https://github.com/ionic-team/capacitor/issues/3097)\n- chore(site): remove from repo (#3138) ([7c82ad3](https://github.com/ionic-team/capacitor/commit/7c82ad3)), closes [#3138](https://github.com/ionic-team/capacitor/issues/3138)\n- chore(test): increase timeout ([c501208](https://github.com/ionic-team/capacitor/commit/c501208))\n- test(android): add tests for PluginMethodHandler (#3153) ([dd7077e](https://github.com/ionic-team/capacitor/commit/dd7077e)), closes [#3153](https://github.com/ionic-team/capacitor/issues/3153)\n- test(android): setup tests, test JSObject (#2508) ([5a37496](https://github.com/ionic-team/capacitor/commit/5a37496)), closes [#2508](https://github.com/ionic-team/capacitor/issues/2508)\n- refactor(android): MessageHandler formatting and simplifying (#2510) ([befe798](https://github.com/ionic-team/capacitor/commit/befe798)), closes [#2510](https://github.com/ionic-team/capacitor/issues/2510)\n\n## [2.2.0](https://github.com/ionic-team/capacitor/compare/2.1.2...2.2.0) (2020-06-10)\n\n### Bug Fixes\n\n- fix(cli/cordova): replace $PACKAGE_NAME with ${applicationId} (#3030) ([7a2a45f](https://github.com/ionic-team/capacitor/commit/7a2a45f)), closes [#3030](https://github.com/ionic-team/capacitor/issues/3030)\n- fix(android): incorrect keyboard height (#2924) ([035f74e](https://github.com/ionic-team/capacitor/commit/035f74e)), closes [#2924](https://github.com/ionic-team/capacitor/issues/2924)\n- fix(android): set cookie on proxied request (#3076) ([9b96edc](https://github.com/ionic-team/capacitor/commit/9b96edc)), closes [#3076](https://github.com/ionic-team/capacitor/issues/3076)\n- fix(android): set cookie on proxied request only if exists (#3077) ([766a61d](https://github.com/ionic-team/capacitor/commit/766a61d)), closes [#3077](https://github.com/ionic-team/capacitor/issues/3077)\n- fix(ios): update frame immediately when keyboard hides (#3038) ([e538bad](https://github.com/ionic-team/capacitor/commit/e538bad)), closes [#3038](https://github.com/ionic-team/capacitor/issues/3038)\n\n### Features\n\n- feat(core/web): add areEnabled implementation for LocalNotifications (#2900) ([179104c](https://github.com/ionic-team/capacitor/commit/179104c)), closes [#2900](https://github.com/ionic-team/capacitor/issues/2900)\n- feat(android): move Config to be per-instance rather than a singleton (#3055) ([b4815a5](https://github.com/ionic-team/capacitor/commit/b4815a5)), closes [#3055](https://github.com/ionic-team/capacitor/issues/3055)\n- feat(ios): show toast when loading url in debug mode (#2871) ([171870b](https://github.com/ionic-team/capacitor/commit/171870b)), closes [#2871](https://github.com/ionic-team/capacitor/issues/2871)\n- feat(Permissions): allow microphone check (#3068) ([a2f2e4f](https://github.com/ionic-team/capacitor/commit/a2f2e4f)), closes [#3068](https://github.com/ionic-team/capacitor/issues/3068)\n\n### Docs\n\n- docs(push): Add descriptions to push notification methods (#3036) ([a2ea9ce](https://github.com/ionic-team/capacitor/commit/a2ea9ce)), closes [#3036](https://github.com/ionic-team/capacitor/issues/3036)\n- docs(share): remove wrong sentence (#3051) ([d7b09c0](https://github.com/ionic-team/capacitor/commit/d7b09c0)), closes [#3051](https://github.com/ionic-team/capacitor/issues/3051)\n\n### Chores\n\n- chore: fix circleci tests (#3025) ([2c4e72f](https://github.com/ionic-team/capacitor/commit/2c4e72f)), closes [#3025](https://github.com/ionic-team/capacitor/issues/3025)\n- chore: fix receive spelling (#3035) ([319bd8a](https://github.com/ionic-team/capacitor/commit/319bd8a)), closes [#3035](https://github.com/ionic-team/capacitor/issues/3035)\n- chore: missing changes on ios publish (#3034) ([5b1f3fb](https://github.com/ionic-team/capacitor/commit/5b1f3fb)), closes [#3034](https://github.com/ionic-team/capacitor/issues/3034)\n- chore: simplify ios publishing (#3028) ([38791a8](https://github.com/ionic-team/capacitor/commit/38791a8)), closes [#3028](https://github.com/ionic-team/capacitor/issues/3028)\n- chore: Update changelog for 2.1.2 ([ee919f7](https://github.com/ionic-team/capacitor/commit/ee919f7))\n- chore(android): avoid connection on proxy to check the content type (#3078) ([0d2894c](https://github.com/ionic-team/capacitor/commit/0d2894c)), closes [#3078](https://github.com/ionic-team/capacitor/issues/3078)\n- chore(android): Fix receive spelling in Javadoc (#3029) ([28c8792](https://github.com/ionic-team/capacitor/commit/28c8792)), closes [#3029](https://github.com/ionic-team/capacitor/issues/3029)\n- chore(ci): switch to Github Actions (#3057) ([8aefce0](https://github.com/ionic-team/capacitor/commit/8aefce0)), closes [#3057](https://github.com/ionic-team/capacitor/issues/3057)\n- chore(github): important emoji fix (#3081) ([1d7fd2c](https://github.com/ionic-team/capacitor/commit/1d7fd2c)), closes [#3081](https://github.com/ionic-team/capacitor/issues/3081)\n- chore(github): issue template cleanup (#3056) ([939ce8b](https://github.com/ionic-team/capacitor/commit/939ce8b)), closes [#3056](https://github.com/ionic-team/capacitor/issues/3056)\n- chore(ios): correct plugin descriptions (#3037) ([a8ad95d](https://github.com/ionic-team/capacitor/commit/a8ad95d)), closes [#3037](https://github.com/ionic-team/capacitor/issues/3037)\n- chore(readme): revamp (#3058) ([7adde78](https://github.com/ionic-team/capacitor/commit/7adde78)), closes [#3058](https://github.com/ionic-team/capacitor/issues/3058)\n- chore(test): increase timeout so integration tests pass (#3079) ([70eff96](https://github.com/ionic-team/capacitor/commit/70eff96)), closes [#3079](https://github.com/ionic-team/capacitor/issues/3079)\n\n## [2.1.2](https://github.com/ionic-team/capacitor/compare/2.1.1...2.1.2) (2020-05-29)\n\n### Bug Fixes\n\n- fix: send error on photos picker dismiss on ios 13 (#3010) ([47f2dd8](https://github.com/ionic-team/capacitor/commit/47f2dd8)), closes [#3010](https://github.com/ionic-team/capacitor/issues/3010)\n- fix(android): remove bintray publishing plugin to fix Gradle 6+ issues (#3016) ([b584b00](https://github.com/ionic-team/capacitor/commit/b584b00)), closes [#3016](https://github.com/ionic-team/capacitor/issues/3016)\n\n### Docs\n\n- docs: add information about disabling logs (#3001) ([e9b2c9d](https://github.com/ionic-team/capacitor/commit/e9b2c9d)), closes [#3001](https://github.com/ionic-team/capacitor/issues/3001)\n- docs: make links to other pages absolute (#3002) ([22d9a09](https://github.com/ionic-team/capacitor/commit/22d9a09)), closes [#3002](https://github.com/ionic-team/capacitor/issues/3002)\n\n### Chores\n\n- chore: remove redundant checks for android_home and bintray (#3019) ([c407d74](https://github.com/ionic-team/capacitor/commit/c407d74)), closes [#3019](https://github.com/ionic-team/capacitor/issues/3019)\n- chore: Update changelog for 2.1.1 ([555f4af](https://github.com/ionic-team/capacitor/commit/555f4af))\n- chore(cli): run tests with max workers, not in band (#3018) ([2e6a9c4](https://github.com/ionic-team/capacitor/commit/2e6a9c4)), closes [#3018](https://github.com/ionic-team/capacitor/issues/3018)\n- chore(ios): remove pod deployment (#3017) ([8b8c051](https://github.com/ionic-team/capacitor/commit/8b8c051)), closes [#3017](https://github.com/ionic-team/capacitor/issues/3017)\n\n## [2.1.1](https://github.com/ionic-team/capacitor/compare/2.1.0...2.1.1) (2020-05-27)\n\n### Bug Fixes\n\n- fix(core/web): Filesystem.appendFile creating wrong parent folder (#2985) ([3951f6b](https://github.com/ionic-team/capacitor/commit/3951f6b)), closes [#2985](https://github.com/ionic-team/capacitor/issues/2985)\n- fix(android): Filesystem.requestPermissions() not working (#2936) ([2a9a95d](https://github.com/ionic-team/capacitor/commit/2a9a95d)), closes [#2936](https://github.com/ionic-team/capacitor/issues/2936)\n- fix(android): Prevent Android 10 crash on Filesystem.readdir (#2950) ([0914c23](https://github.com/ionic-team/capacitor/commit/0914c23)), closes [#2950](https://github.com/ionic-team/capacitor/issues/2950)\n- fix(android): remember camera prompt selection after permission result (#2903) ([cdd317f](https://github.com/ionic-team/capacitor/commit/cdd317f)), closes [#2903](https://github.com/ionic-team/capacitor/issues/2903)\n- fix(cli): Improve plugin.xml framework detection (#2956) ([8736d90](https://github.com/ionic-team/capacitor/commit/8736d90)), closes [#2956](https://github.com/ionic-team/capacitor/issues/2956)\n- fix(cordova): Add cordova-support-google-services to incompatible list (#2912) ([58d0768](https://github.com/ionic-team/capacitor/commit/58d0768)), closes [#2912](https://github.com/ionic-team/capacitor/issues/2912)\n- fix(cordova): Exclude framework headers (#2972) ([53a6371](https://github.com/ionic-team/capacitor/commit/53a6371)), closes [#2972](https://github.com/ionic-team/capacitor/issues/2972)\n- fix(ios): Create tmpWindow when is needed and destroy when not needed (#2995) ([9475129](https://github.com/ionic-team/capacitor/commit/9475129)), closes [#2995](https://github.com/ionic-team/capacitor/issues/2995)\n- fix(web): improve Proxy check to avoid SSR problems (#2851) ([7afc9eb](https://github.com/ionic-team/capacitor/commit/7afc9eb)), closes [#2851](https://github.com/ionic-team/capacitor/issues/2851)\n\n### Docs\n\n- docs(ce-plugins) add @byrds/capacitor-contacts plugin (#2939) ([56d4159](https://github.com/ionic-team/capacitor/commit/56d4159)), closes [#2939](https://github.com/ionic-team/capacitor/issues/2939)\n- docs: Appflow is now available (#2970) ([516386e](https://github.com/ionic-team/capacitor/commit/516386e)), closes [#2970](https://github.com/ionic-team/capacitor/issues/2970)\n- docs(android): Add information about accessing public folders in Android 10 (#2951) ([6d1778b](https://github.com/ionic-team/capacitor/commit/6d1778b)), closes [#2951](https://github.com/ionic-team/capacitor/issues/2951)\n- docs(ce-plugins): add capacitor-blob-writer (#2885) ([cacecb4](https://github.com/ionic-team/capacitor/commit/cacecb4)), closes [#2885](https://github.com/ionic-team/capacitor/issues/2885)\n- docs(ce-plugins): add capacitor-firebase-crashlytics (#2938) ([cb1e672](https://github.com/ionic-team/capacitor/commit/cb1e672)), closes [#2938](https://github.com/ionic-team/capacitor/issues/2938)\n- docs(cordova-migration): Add cordova plugin uninstall details (#2935) ([17bf3ab](https://github.com/ionic-team/capacitor/commit/17bf3ab)), closes [#2935](https://github.com/ionic-team/capacitor/issues/2935)\n- docs(deep-links): clarify apple-app-site-association file (#2963) ([ac9c3de](https://github.com/ionic-team/capacitor/commit/ac9c3de)), closes [#2963](https://github.com/ionic-team/capacitor/issues/2963)\n- docs(Plugins): add information about how to override navigation (#2923) ([63c8542](https://github.com/ionic-team/capacitor/commit/63c8542)), closes [#2923](https://github.com/ionic-team/capacitor/issues/2923)\n- docs(splash): Full Screen & Immersive are android only (#2945) ([b8e7279](https://github.com/ionic-team/capacitor/commit/b8e7279)), closes [#2945](https://github.com/ionic-team/capacitor/issues/2945)\n- docs(workflow): change update instructions to get latest version (#2937) ([0c151fc](https://github.com/ionic-team/capacitor/commit/0c151fc)), closes [#2937](https://github.com/ionic-team/capacitor/issues/2937)\n\n### Chores\n\n- chore(dependencies): Update package dependencies to 2.1.0 ([a5fb2bc](https://github.com/ionic-team/capacitor/commit/a5fb2bc))\n- chore(site): Update dependencies (#2928) ([a9d4698](https://github.com/ionic-team/capacitor/commit/a9d4698)), closes [#2928](https://github.com/ionic-team/capacitor/issues/2928)\n- chore(site): update firebase-tools (#2915) ([bdce166](https://github.com/ionic-team/capacitor/commit/bdce166)), closes [#2915](https://github.com/ionic-team/capacitor/issues/2915)\n\n## (2020-05-07)\n\n## [2.1.0](https://github.com/ionic-team/capacitor/compare/2.0.2...2.1.0) (2020-05-07)\n\n### Bug Fixes\n\n- fix: set launchShowDuration to 0 on new projects only (#2876) ([8de0414](https://github.com/ionic-team/capacitor/commit/8de0414)), closes [#2876](https://github.com/ionic-team/capacitor/issues/2876)\n- fix(android): call error on prompt cancel (#2855) ([c86cfb1](https://github.com/ionic-team/capacitor/commit/c86cfb1)), closes [#2855](https://github.com/ionic-team/capacitor/issues/2855)\n- fix(android): check if NETWORK_PROVIDER is enabled (#2859) ([f4d5c84](https://github.com/ionic-team/capacitor/commit/f4d5c84)), closes [#2859](https://github.com/ionic-team/capacitor/issues/2859)\n- fix(android): make readFile not add newlines on base64 strings (#2857) ([31d65c9](https://github.com/ionic-team/capacitor/commit/31d65c9)), closes [#2857](https://github.com/ionic-team/capacitor/issues/2857)\n- fix(cli): avoid infinite loop on scoped dependencies (#2868) ([69d62f7](https://github.com/ionic-team/capacitor/commit/69d62f7)), closes [#2868](https://github.com/ionic-team/capacitor/issues/2868)\n- fix(ios): remove thread warning on Haptics.selectionEnd() (#2860) ([471ed0c](https://github.com/ionic-team/capacitor/commit/471ed0c)), closes [#2860](https://github.com/ionic-team/capacitor/issues/2860)\n- fix(splash): Make splash launch delay timeout zero to speed up capacitor boot ([b29346b](https://github.com/ionic-team/capacitor/commit/b29346b))\n\n### Features\n\n- feat: Add common hideLogs option (#2865) ([1b3f0ec](https://github.com/ionic-team/capacitor/commit/1b3f0ec)), closes [#2865](https://github.com/ionic-team/capacitor/issues/2865)\n- feat: Allow plugins to override navigation (#2872) ([41f9834](https://github.com/ionic-team/capacitor/commit/41f9834)), closes [#2872](https://github.com/ionic-team/capacitor/issues/2872)\n- feat(android): add vibration option to notifications channel (#2787) ([2f6f0ba](https://github.com/ionic-team/capacitor/commit/2f6f0ba)), closes [#2787](https://github.com/ionic-team/capacitor/issues/2787)\n- feat(android): expose JSON string constructor for JSArray (#2879) ([040bfc8](https://github.com/ionic-team/capacitor/commit/040bfc8)), closes [#2879](https://github.com/ionic-team/capacitor/issues/2879)\n- feat(android): hideLogs feature (#2839) ([d60757a](https://github.com/ionic-team/capacitor/commit/d60757a)), closes [#2839](https://github.com/ionic-team/capacitor/issues/2839)\n- feat(android): implement selection haptic feedback (#2704) ([34dd280](https://github.com/ionic-team/capacitor/commit/34dd280)), closes [#2704](https://github.com/ionic-team/capacitor/issues/2704)\n- feat(camera): make prompt strings localizable (#2631) ([0c09fc8](https://github.com/ionic-team/capacitor/commit/0c09fc8)), closes [#2631](https://github.com/ionic-team/capacitor/issues/2631)\n- feat(cordova): Add WK_WEB_VIEW_ONLY=1 preprocessor macro (#2880) ([603b2e3](https://github.com/ionic-team/capacitor/commit/603b2e3)), closes [#2880](https://github.com/ionic-team/capacitor/issues/2880)\n\n### Docs\n\n- docs(browser): Update information for close method (#2796) ([89c64af](https://github.com/ionic-team/capacitor/commit/89c64af)), closes [#2796](https://github.com/ionic-team/capacitor/issues/2796)\n\n## [2.0.2](https://github.com/ionic-team/capacitor/compare/2.0.1...2.0.2) (2020-04-29)\n\n### Bug Fixes\n\n- fix(android) : App can crash on clipboard.read if empty (#2815) ([fc33265](https://github.com/ionic-team/capacitor/commit/fc33265)), closes [#2815](https://github.com/ionic-team/capacitor/issues/2815)\n- fix(android): avoid camera crash on photo edit cancel (#2776) ([4b8820d](https://github.com/ionic-team/capacitor/commit/4b8820d)), closes [#2776](https://github.com/ionic-team/capacitor/issues/2776)\n- fix(android): don't remove LocalNotification from pending on dismiss (#2809) ([822b140](https://github.com/ionic-team/capacitor/commit/822b140)), closes [#2809](https://github.com/ionic-team/capacitor/issues/2809)\n- fix(ios): allow Browser popover presentation if supported (#2784) ([4b40494](https://github.com/ionic-team/capacitor/commit/4b40494)), closes [#2784](https://github.com/ionic-team/capacitor/issues/2784)\n- fix(ios): remove applicationState check on keyboard plugin (#2820) ([dbc1da1](https://github.com/ionic-team/capacitor/commit/dbc1da1)), closes [#2820](https://github.com/ionic-team/capacitor/issues/2820)\n- fix(web): Gracefully degrade Proxy usage to fix IE11 (#2759) ([b61f909](https://github.com/ionic-team/capacitor/commit/b61f909)), closes [#2759](https://github.com/ionic-team/capacitor/issues/2759)\n\n### Docs\n\n- docs(ios/configuration): Add information on how to rename your app (#2768) ([55c3d52](https://github.com/ionic-team/capacitor/commit/55c3d52)), closes [#2768](https://github.com/ionic-team/capacitor/issues/2768)\n- docs: improve visibility of jetifier command (#2844) ([fd28a3a](https://github.com/ionic-team/capacitor/commit/fd28a3a)), closes [#2844](https://github.com/ionic-team/capacitor/issues/2844)\n- docs(android): explain where to apply the variables change (#2791) ([e5bd2eb](https://github.com/ionic-team/capacitor/commit/e5bd2eb)), closes [#2791](https://github.com/ionic-team/capacitor/issues/2791)\n- docs(ce-plugins): Add capacitor-admob-advanced (#2780) ([9b1593d](https://github.com/ionic-team/capacitor/commit/9b1593d)), closes [#2780](https://github.com/ionic-team/capacitor/issues/2780)\n- docs(deep-links): Rename applinks.json to assetlinks.json (#2842) ([53883ce](https://github.com/ionic-team/capacitor/commit/53883ce)), closes [#2842](https://github.com/ionic-team/capacitor/issues/2842)\n- docs(firebase pn): Update Push Notifications with Firebase Guide (#2698) ([ee5e283](https://github.com/ionic-team/capacitor/commit/ee5e283)), closes [#2698](https://github.com/ionic-team/capacitor/issues/2698)\n- docs(keyboard): Add missing import in example (#2749) ([04fb275](https://github.com/ionic-team/capacitor/commit/04fb275)), closes [#2749](https://github.com/ionic-team/capacitor/issues/2749)\n- docs(troubleshooting): Add AndroidX information and workaround (#2832) ([d9cd399](https://github.com/ionic-team/capacitor/commit/d9cd399)), closes [#2832](https://github.com/ionic-team/capacitor/issues/2832)\n- docs(updating): Provide a full path to variables.gradle file (#2769) ([3638a89](https://github.com/ionic-team/capacitor/commit/3638a89)), closes [#2769](https://github.com/ionic-team/capacitor/issues/2769)\n- docs(updating): Remove duplicate gradle sentence (#2798) ([347029c](https://github.com/ionic-team/capacitor/commit/347029c)), closes [#2798](https://github.com/ionic-team/capacitor/issues/2798)\n\n### Chores\n\n- chore: remove blog (#2813) ([e219e69](https://github.com/ionic-team/capacitor/commit/e219e69)), closes [#2813](https://github.com/ionic-team/capacitor/issues/2813)\n- chore: remove electron mentions (#2812) ([4dad4a1](https://github.com/ionic-team/capacitor/commit/4dad4a1)), closes [#2812](https://github.com/ionic-team/capacitor/issues/2812)\n- chore(core): update jest (#2843) ([b525c17](https://github.com/ionic-team/capacitor/commit/b525c17)), closes [#2843](https://github.com/ionic-team/capacitor/issues/2843)\n\n## [2.0.1](https://github.com/ionic-team/capacitor/compare/2.0.0...2.0.1) (2020-04-10)\n\n### Bug Fixes\n\n- fix(android): Avoid crash on schedule if LocalNotifications are disabled (#2718) ([aac51fe](https://github.com/ionic-team/capacitor/commit/aac51fe)), closes [#2718](https://github.com/ionic-team/capacitor/issues/2718)\n- fix(android): display title on Modals.showActions (#2730) ([c2e0358](https://github.com/ionic-team/capacitor/commit/c2e0358)), closes [#2730](https://github.com/ionic-team/capacitor/issues/2730)\n- fix(android): input autofocus and javascript focus not working (#2719) ([e010a28](https://github.com/ionic-team/capacitor/commit/e010a28)), closes [#2719](https://github.com/ionic-team/capacitor/issues/2719)\n- fix(android): use proper targetSdkVersion (#2706) ([3cd02e4](https://github.com/ionic-team/capacitor/commit/3cd02e4)), closes [#2706](https://github.com/ionic-team/capacitor/issues/2706)\n- fix(cli): avoid error on create command (#2727) ([fefa4b9](https://github.com/ionic-team/capacitor/commit/fefa4b9)), closes [#2727](https://github.com/ionic-team/capacitor/issues/2727)\n- fix(cli): match platform version with cli version on add command (#2724) ([6172932](https://github.com/ionic-team/capacitor/commit/6172932)), closes [#2724](https://github.com/ionic-team/capacitor/issues/2724)\n- fix(cli): use appName as package.json name on electron project (#2741) ([d6fc2d8](https://github.com/ionic-team/capacitor/commit/d6fc2d8)), closes [#2741](https://github.com/ionic-team/capacitor/issues/2741)\n- fix(cli): Warn if core version doesn't match platform version (#2736) ([29a9acf](https://github.com/ionic-team/capacitor/commit/29a9acf)), closes [#2736](https://github.com/ionic-team/capacitor/issues/2736)\n- fix(cordova): Don't add as system library if it's a vendored library (#2729) ([404574d](https://github.com/ionic-team/capacitor/commit/404574d)), closes [#2729](https://github.com/ionic-team/capacitor/issues/2729)\n- fix(cordova): handle plugin.xml asset tag (#2728) ([8e1abfe](https://github.com/ionic-team/capacitor/commit/8e1abfe)), closes [#2728](https://github.com/ionic-team/capacitor/issues/2728)\n- fix(electron): Update Modals Plugin to use new dialog async syntax (#2742) ([4c13fe0](https://github.com/ionic-team/capacitor/commit/4c13fe0)), closes [#2742](https://github.com/ionic-team/capacitor/issues/2742)\n- fix(ios): allow access to extension-less files (#2726) ([3baf81b](https://github.com/ionic-team/capacitor/commit/3baf81b)), closes [#2726](https://github.com/ionic-team/capacitor/issues/2726)\n- fix(ios): don't use the tmpWindow on popover presentation (#2714) ([327ffc5](https://github.com/ionic-team/capacitor/commit/327ffc5)), closes [#2714](https://github.com/ionic-team/capacitor/issues/2714)\n- fix(plugin-template): remove incorrect = from android gradle file (#2689) ([39c8d4a](https://github.com/ionic-team/capacitor/commit/39c8d4a)), closes [#2689](https://github.com/ionic-team/capacitor/issues/2689)\n\n### Docs\n\n- docs(ce-plugins): Remove capacitor-apple-login (#2734) ([b532179](https://github.com/ionic-team/capacitor/commit/b532179)), closes [#2734](https://github.com/ionic-team/capacitor/issues/2734)\n- docs(updating): Add 2.0.0 changes for electron (#2708) ([6a03960](https://github.com/ionic-team/capacitor/commit/6a03960)), closes [#2708](https://github.com/ionic-team/capacitor/issues/2708)\n- docs(updating): Add some 2.0.0 missing information (#2707) ([46ca030](https://github.com/ionic-team/capacitor/commit/46ca030)), closes [#2707](https://github.com/ionic-team/capacitor/issues/2707)\n- docs(updating): modify the guides for 2.0.0 final ([00f6196](https://github.com/ionic-team/capacitor/commit/00f6196))\n\n### Chores\n\n- chore(android): fix release script to use Android X (#2687) ([a63e203](https://github.com/ionic-team/capacitor/commit/a63e203)), closes [#2687](https://github.com/ionic-team/capacitor/issues/2687)\n- chore(android): Improve handling of splashImmersive and splashFullScreen preferences (#2705) ([1c633c5](https://github.com/ionic-team/capacitor/commit/1c633c5)), closes [#2705](https://github.com/ionic-team/capacitor/issues/2705)\n\n## [2.0.0](https://github.com/ionic-team/capacitor/compare/1.5.0...2.0.0) (2020-04-03)\n\n### Bug Fixes\n\n- fix(android): allow Share plugin to provide text or url only (#2436) ([b6328f0](https://github.com/ionic-team/capacitor/commit/b6328f0)), closes [#2436](https://github.com/ionic-team/capacitor/issues/2436)\n- fix(android): Avoid Accessibility.speak crash (#2554) ([77b59f8](https://github.com/ionic-team/capacitor/commit/77b59f8)), closes [#2554](https://github.com/ionic-team/capacitor/issues/2554)\n- fix(android): don't return NO_CAMERA_ERROR if any camera is present (#2558) ([4f6ca98](https://github.com/ionic-team/capacitor/commit/4f6ca98)), closes [#2558](https://github.com/ionic-team/capacitor/issues/2558)\n- fix(android): maintain status bar color during splash (#2603) ([59fcf9e](https://github.com/ionic-team/capacitor/commit/59fcf9e)), closes [#2603](https://github.com/ionic-team/capacitor/issues/2603)\n- fix(android): make camera work on Android 10 (#2559) ([4a1a7b8](https://github.com/ionic-team/capacitor/commit/4a1a7b8)), closes [#2559](https://github.com/ionic-team/capacitor/issues/2559)\n- fix(android): make LocalNotification not crash on showing when (#2677) ([63ecd1c](https://github.com/ionic-team/capacitor/commit/63ecd1c)), closes [#2677](https://github.com/ionic-team/capacitor/issues/2677)\n- fix(android): Make Modals.showActions non cancelable (#2504) ([ffdd78c](https://github.com/ionic-team/capacitor/commit/ffdd78c)), closes [#2504](https://github.com/ionic-team/capacitor/issues/2504)\n- fix(android): make sure scheduled time is shown in LocalNotifications (#2553) ([448e7b7](https://github.com/ionic-team/capacitor/commit/448e7b7)), closes [#2553](https://github.com/ionic-team/capacitor/issues/2553)\n- fix(android): missing AndroidX changes (#2454) ([10acf5c](https://github.com/ionic-team/capacitor/commit/10acf5c)), closes [#2454](https://github.com/ionic-team/capacitor/issues/2454)\n- fix(android): plugin retained events not being retained if listeners were empty (#2408) ([b817e83](https://github.com/ionic-team/capacitor/commit/b817e83)), closes [#2408](https://github.com/ionic-team/capacitor/issues/2408)\n- fix(android): put google() on top of jcenter() in gradle files (#2461) ([3263dbc](https://github.com/ionic-team/capacitor/commit/3263dbc)), closes [#2461](https://github.com/ionic-team/capacitor/issues/2461)\n- fix(android): return original camera image if edition was canceled (#2358) ([ce93ed3](https://github.com/ionic-team/capacitor/commit/ce93ed3)), closes [#2358](https://github.com/ionic-team/capacitor/issues/2358)\n- fix(android): support for multi-line text in LocalNotifications (#2552) ([59d02ab](https://github.com/ionic-team/capacitor/commit/59d02ab)), closes [#2552](https://github.com/ionic-team/capacitor/issues/2552)\n- fix(android): Use NotificationCompat constant for setting visibility (#2586) ([62b11fd](https://github.com/ionic-team/capacitor/commit/62b11fd)), closes [#2586](https://github.com/ionic-team/capacitor/issues/2586)\n- fix(cli): Avoid AndroidManifest.xml not found error on add (#2400) ([120969c](https://github.com/ionic-team/capacitor/commit/120969c)), closes [#2400](https://github.com/ionic-team/capacitor/issues/2400)\n- fix(cli): avoid error when config.xml has no preferences (#2627) ([6c0dc4b](https://github.com/ionic-team/capacitor/commit/6c0dc4b)), closes [#2627](https://github.com/ionic-team/capacitor/issues/2627)\n- fix(cli): prevent cordova dependency loop if plugin contains @ (#2622) ([9dcb2ff](https://github.com/ionic-team/capacitor/commit/9dcb2ff)), closes [#2622](https://github.com/ionic-team/capacitor/issues/2622)\n- fix(cli): properly merge non application config-file (#2478) ([9c589a3](https://github.com/ionic-team/capacitor/commit/9c589a3)), closes [#2478](https://github.com/ionic-team/capacitor/issues/2478)\n- fix(cordova): Add lib prefix to .a library names (#2636) ([2be4a92](https://github.com/ionic-team/capacitor/commit/2be4a92)), closes [#2636](https://github.com/ionic-team/capacitor/issues/2636)\n- fix(cordova): handle source-file with framework attribute (#2507) ([f7cd4c0](https://github.com/ionic-team/capacitor/commit/f7cd4c0)), closes [#2507](https://github.com/ionic-team/capacitor/issues/2507)\n- fix(doctor): add electron checks (#2434) ([d5efb05](https://github.com/ionic-team/capacitor/commit/d5efb05)), closes [#2434](https://github.com/ionic-team/capacitor/issues/2434)\n- fix(electron): correct implementation of Filesystem.appendFile (#2567) ([c6a3b3b](https://github.com/ionic-team/capacitor/commit/c6a3b3b)), closes [#2567](https://github.com/ionic-team/capacitor/issues/2567)\n- fix(electron): Provide app version in Device.getInfo() (#2521) ([0998ae8](https://github.com/ionic-team/capacitor/commit/0998ae8)), closes [#2521](https://github.com/ionic-team/capacitor/issues/2521)\n- fix(electron): various clipboard fixes (#2566) ([2c809ab](https://github.com/ionic-team/capacitor/commit/2c809ab)), closes [#2566](https://github.com/ionic-team/capacitor/issues/2566)\n- fix(ios): avoid crash on registerPlugins on Xcode 11.4 (#2414) ([ca8fa9e](https://github.com/ionic-team/capacitor/commit/ca8fa9e)), closes [#2414](https://github.com/ionic-team/capacitor/issues/2414)\n- fix(ios): implement statusTap for iOS 13 (#2376) ([7cb77c8](https://github.com/ionic-team/capacitor/commit/7cb77c8)), closes [#2376](https://github.com/ionic-team/capacitor/issues/2376)\n- fix(ios): make ActionSheetOptionStyle.Cancel show cancel button (#2496) ([d120021](https://github.com/ionic-team/capacitor/commit/d120021)), closes [#2496](https://github.com/ionic-team/capacitor/issues/2496)\n- fix(ios): Make Camera.getPhoto return exif from gallery photos (#2595) ([18f9d81](https://github.com/ionic-team/capacitor/commit/18f9d81)), closes [#2595](https://github.com/ionic-team/capacitor/issues/2595)\n- fix(ios): Make Clipboard plugin return errors (#2430) ([6a2ee92](https://github.com/ionic-team/capacitor/commit/6a2ee92)), closes [#2430](https://github.com/ionic-team/capacitor/issues/2430)\n- fix(ios): make Clipboard.read return text/plain (#2565) ([078284d](https://github.com/ionic-team/capacitor/commit/078284d)), closes [#2565](https://github.com/ionic-team/capacitor/issues/2565)\n- fix(ios): return error if Cancel is selected from Camera.getPhoto() prompt (#2550) ([a015f8f](https://github.com/ionic-team/capacitor/commit/a015f8f)), closes [#2550](https://github.com/ionic-team/capacitor/issues/2550)\n- fix(ios): writeFile failing on root folders (#2670) ([f7a800c](https://github.com/ionic-team/capacitor/commit/f7a800c)), closes [#2670](https://github.com/ionic-team/capacitor/issues/2670)\n- fix(LocalNotifications): return proper LocalNotificationScheduleResult on schedule (#2490) ([b89fb15](https://github.com/ionic-team/capacitor/commit/b89fb15)), closes [#2490](https://github.com/ionic-team/capacitor/issues/2490)\n- fix(modals): make inputPlaceholder set a placeholder and not text (#2474) ([8002791](https://github.com/ionic-team/capacitor/commit/8002791)), closes [#2474](https://github.com/ionic-team/capacitor/issues/2474)\n- fix(modals): make showActions work on web and electron (#2501) ([f1204b8](https://github.com/ionic-team/capacitor/commit/f1204b8)), closes [#2501](https://github.com/ionic-team/capacitor/issues/2501)\n- fix(toast): unify duration across platforms (#2340) ([717dd0a](https://github.com/ionic-team/capacitor/commit/717dd0a)), closes [#2340](https://github.com/ionic-team/capacitor/issues/2340)\n\n### Features\n\n- feat: add requestPermission to PushNotifications and LocalNotifications (#2516) ([82e38a4](https://github.com/ionic-team/capacitor/commit/82e38a4)), closes [#2516](https://github.com/ionic-team/capacitor/issues/2516)\n- feat: Allow plugins to reject with a string code (#2533) ([f93c354](https://github.com/ionic-team/capacitor/commit/f93c354)), closes [#2533](https://github.com/ionic-team/capacitor/issues/2533)\n- feat: implement removeAllListeners (#2609) ([ac55d63](https://github.com/ionic-team/capacitor/commit/ac55d63)), closes [#2609](https://github.com/ionic-team/capacitor/issues/2609)\n- feat(android): add configurable icon color for local notifications (#2548) ([0bfa0bf](https://github.com/ionic-team/capacitor/commit/0bfa0bf)), closes [#2548](https://github.com/ionic-team/capacitor/issues/2548)\n- feat(android): Add immersive configuration to Splash (#2425) ([2605ad6](https://github.com/ionic-team/capacitor/commit/2605ad6)), closes [#2425](https://github.com/ionic-team/capacitor/issues/2425)\n- feat(android): Add lights and lightColor to PushNotificationChannel (#2618) ([4c0170c](https://github.com/ionic-team/capacitor/commit/4c0170c)), closes [#2618](https://github.com/ionic-team/capacitor/issues/2618)\n- feat(android): Add Statusbar.setOverlaysWebView method (#2597) ([d035939](https://github.com/ionic-team/capacitor/commit/d035939)), closes [#2597](https://github.com/ionic-team/capacitor/issues/2597)\n- feat(android): Allow plugin methods to crash (#2512) ([253cdc9](https://github.com/ionic-team/capacitor/commit/253cdc9)), closes [#2512](https://github.com/ionic-team/capacitor/issues/2512)\n- feat(android): Allow to configure a default notification sound (#2682) ([93eb9aa](https://github.com/ionic-team/capacitor/commit/93eb9aa)), closes [#2682](https://github.com/ionic-team/capacitor/issues/2682)\n- feat(android): avoid app restart on activity resize or uiMode change (#2584) ([4a29ff8](https://github.com/ionic-team/capacitor/commit/4a29ff8)), closes [#2584](https://github.com/ionic-team/capacitor/issues/2584)\n- feat(android): Enable AndroidX and use AndroidX dependencies (#2045) ([8b606e9](https://github.com/ionic-team/capacitor/commit/8b606e9)), closes [#2045](https://github.com/ionic-team/capacitor/issues/2045)\n- feat(android): Handle onDestroy lifecycle event in plugins (#2421) ([6fe6d25](https://github.com/ionic-team/capacitor/commit/6fe6d25)), closes [#2421](https://github.com/ionic-team/capacitor/issues/2421)\n- feat(android): implement BridgeFragment for easier embedding using Fragments (#2666) ([a8d9591](https://github.com/ionic-team/capacitor/commit/a8d9591)), closes [#2666](https://github.com/ionic-team/capacitor/issues/2666)\n- feat(android): make AppRestoredResult also returns error info and success boolean (#2497) ([b650880](https://github.com/ionic-team/capacitor/commit/b650880)), closes [#2497](https://github.com/ionic-team/capacitor/issues/2497)\n- feat(android): Make Bridge.restoreInstanceState() public (#2538) ([7020f1f](https://github.com/ionic-team/capacitor/commit/7020f1f)), closes [#2538](https://github.com/ionic-team/capacitor/issues/2538)\n- feat(android): make JSObject.getString return null instead of 'null' string (#2602) ([52069b7](https://github.com/ionic-team/capacitor/commit/52069b7)), closes [#2602](https://github.com/ionic-team/capacitor/issues/2602)\n- feat(android): Make variables.gradle file not mandatory (#2600) ([4fc5039](https://github.com/ionic-team/capacitor/commit/4fc5039)), closes [#2600](https://github.com/ionic-team/capacitor/issues/2600)\n- feat(android): provide WebViewClient accessor (#2477) ([dd3875b](https://github.com/ionic-team/capacitor/commit/dd3875b)), closes [#2477](https://github.com/ionic-team/capacitor/issues/2477)\n- feat(android): update gradle and dependencies (#2431) ([6598752](https://github.com/ionic-team/capacitor/commit/6598752)), closes [#2431](https://github.com/ionic-team/capacitor/issues/2431)\n- feat(android): use common variables for config and dependencies (#2534) ([d1009bb](https://github.com/ionic-team/capacitor/commit/d1009bb)), closes [#2534](https://github.com/ionic-team/capacitor/issues/2534)\n- feat(android): use Fused Location Provider on Geolocation plugin (#2409) ([7faec79](https://github.com/ionic-team/capacitor/commit/7faec79)), closes [#2409](https://github.com/ionic-team/capacitor/issues/2409)\n- feat(App): Add getState method to check current app state (#2611) ([f20bf29](https://github.com/ionic-team/capacitor/commit/f20bf29)), closes [#2611](https://github.com/ionic-team/capacitor/issues/2611)\n- feat(camera): unify saveToGallery behavior (#2671) ([2185833](https://github.com/ionic-team/capacitor/commit/2185833)), closes [#2671](https://github.com/ionic-team/capacitor/issues/2671)\n- feat(camera): Unify saveToGallery default value to false (#2557) ([d222226](https://github.com/ionic-team/capacitor/commit/d222226)), closes [#2557](https://github.com/ionic-team/capacitor/issues/2557)\n- feat(cli): make init use values from capacitor.config.json as defaults (#2620) ([9157e1f](https://github.com/ionic-team/capacitor/commit/9157e1f)), closes [#2620](https://github.com/ionic-team/capacitor/issues/2620)\n- feat(cli): use name from package.json as default name (#2621) ([e9bec42](https://github.com/ionic-team/capacitor/commit/e9bec42)), closes [#2621](https://github.com/ionic-team/capacitor/issues/2621)\n- feat(clipboard): allow to write images on web plugin (#2523) ([5ba2a20](https://github.com/ionic-team/capacitor/commit/5ba2a20)), closes [#2523](https://github.com/ionic-team/capacitor/issues/2523)\n- feat(clipboard): remove Clipboard.read() options (#2527) ([2209113](https://github.com/ionic-team/capacitor/commit/2209113)), closes [#2527](https://github.com/ionic-team/capacitor/issues/2527)\n- feat(Device): Add getBatteryInfo function (#2435) ([0deca04](https://github.com/ionic-team/capacitor/commit/0deca04)), closes [#2435](https://github.com/ionic-team/capacitor/issues/2435)\n- feat(electron): Remove injectCapacitor function (#2415) ([d17f0be](https://github.com/ionic-team/capacitor/commit/d17f0be)), closes [#2415](https://github.com/ionic-team/capacitor/issues/2415)\n- feat(Filesystem): add recursive option to writeFile (#2487) ([53211a3](https://github.com/ionic-team/capacitor/commit/53211a3)), closes [#2487](https://github.com/ionic-team/capacitor/issues/2487)\n- feat(Filesystem): make writeFile return the file uri (#2484) ([e1a00bd](https://github.com/ionic-team/capacitor/commit/e1a00bd)), closes [#2484](https://github.com/ionic-team/capacitor/issues/2484)\n- feat(Filesystem): Remove createIntermediateDirectories from MkdirOptions (#2410) ([dae3510](https://github.com/ionic-team/capacitor/commit/dae3510)), closes [#2410](https://github.com/ionic-team/capacitor/issues/2410)\n- feat(Filesystem): remove FilesystemDirectory.Application (#2514) ([cd395d2](https://github.com/ionic-team/capacitor/commit/cd395d2)), closes [#2514](https://github.com/ionic-team/capacitor/issues/2514)\n- feat(ios): add device name to Device.getInfo() (#2491) ([4fb244d](https://github.com/ionic-team/capacitor/commit/4fb244d)), closes [#2491](https://github.com/ionic-team/capacitor/issues/2491)\n- feat(ios): add presentVC and dismissVC methods to bridge (#2678) ([a6c91da](https://github.com/ionic-team/capacitor/commit/a6c91da)), closes [#2678](https://github.com/ionic-team/capacitor/issues/2678)\n- feat(ios): allow to set status bar animation style on show and hide (#2587) ([fa6bb3e](https://github.com/ionic-team/capacitor/commit/fa6bb3e)), closes [#2587](https://github.com/ionic-team/capacitor/issues/2587)\n- feat(ios): change native location accuracy values (#2420) ([16c3ea1](https://github.com/ionic-team/capacitor/commit/16c3ea1)), closes [#2420](https://github.com/ionic-team/capacitor/issues/2420)\n- feat(ios): Update Capacitor project to Swift 5.0 (#2465) ([c895fc4](https://github.com/ionic-team/capacitor/commit/c895fc4)), closes [#2465](https://github.com/ionic-team/capacitor/issues/2465)\n- feat(LocalNotifications): add createChannel, deleteChannel and listChannels methods (#2676) ([d72e25d](https://github.com/ionic-team/capacitor/commit/d72e25d)), closes [#2676](https://github.com/ionic-team/capacitor/issues/2676)\n- feat(LocalNotifications): Allow to create notifications without activity (#2648) ([a4e5918](https://github.com/ionic-team/capacitor/commit/a4e5918)), closes [#2648](https://github.com/ionic-team/capacitor/issues/2648)\n- feat(modals): add inputText property to prompt for prefilled text (#2475) ([a05311d](https://github.com/ionic-team/capacitor/commit/a05311d)), closes [#2475](https://github.com/ionic-team/capacitor/issues/2475)\n- feat(PushNotifications): Make register method return if permission was granted (#2324) ([a0bcf5c](https://github.com/ionic-team/capacitor/commit/a0bcf5c)), closes [#2324](https://github.com/ionic-team/capacitor/issues/2324)\n\n### Docs\n\n- updated local notification config docs (#2601) ([9997b3e](https://github.com/ionic-team/capacitor/commit/9997b3e)), closes [#2601](https://github.com/ionic-team/capacitor/issues/2601)\n- docs: fix Local Notifications url (#2542) ([a3a19b3](https://github.com/ionic-team/capacitor/commit/a3a19b3)), closes [#2542](https://github.com/ionic-team/capacitor/issues/2542)\n- docs: update default cordovaSwiftVersion (#2466) ([4908bbb](https://github.com/ionic-team/capacitor/commit/4908bbb)), closes [#2466](https://github.com/ionic-team/capacitor/issues/2466)\n- docs: update Filesystem.writeFile sample (#2568) ([5122527](https://github.com/ionic-team/capacitor/commit/5122527)), closes [#2568](https://github.com/ionic-team/capacitor/issues/2568)\n- docs(android): Add update guide for Capacitor 2.0 beta (#2572) ([0f0ed25](https://github.com/ionic-team/capacitor/commit/0f0ed25)), closes [#2572](https://github.com/ionic-team/capacitor/issues/2572)\n- docs(camera): add missing usage descriptions needed in iOS (#2633) ([7692ee4](https://github.com/ionic-team/capacitor/commit/7692ee4)), closes [#2633](https://github.com/ionic-team/capacitor/issues/2633)\n- docs(ce-guides): Remove dead link (#2418) ([a1b6403](https://github.com/ionic-team/capacitor/commit/a1b6403)), closes [#2418](https://github.com/ionic-team/capacitor/issues/2418)\n- docs(ce-plugins): Add capacitor-dark-mode plugin (#2681) ([4fcb725](https://github.com/ionic-team/capacitor/commit/4fcb725)), closes [#2681](https://github.com/ionic-team/capacitor/issues/2681)\n- docs(ce-plugins): add capacitor-healthkit (#2489) ([9e356db](https://github.com/ionic-team/capacitor/commit/9e356db)), closes [#2489](https://github.com/ionic-team/capacitor/issues/2489)\n- docs(ce-plugins): Add SQLite, SQLite Storage and Video Player plugins (#2589) ([78a28da](https://github.com/ionic-team/capacitor/commit/78a28da)), closes [#2589](https://github.com/ionic-team/capacitor/issues/2589)\n- docs(ce-plugins): Remove or replace deprecated plugins (#2419) ([dfc1ed6](https://github.com/ionic-team/capacitor/commit/dfc1ed6)), closes [#2419](https://github.com/ionic-team/capacitor/issues/2419)\n- docs(clipboard): update read example removing options (#2564) ([49e9f8d](https://github.com/ionic-team/capacitor/commit/49e9f8d)), closes [#2564](https://github.com/ionic-team/capacitor/issues/2564)\n- docs(contributing): Update docs contributing readme (#2592) ([0799d52](https://github.com/ionic-team/capacitor/commit/0799d52)), closes [#2592](https://github.com/ionic-team/capacitor/issues/2592)\n- docs(dependencies): update to latest Capacitor (#2599) ([0154f51](https://github.com/ionic-team/capacitor/commit/0154f51)), closes [#2599](https://github.com/ionic-team/capacitor/issues/2599)\n- docs(Device): fix getInfo response and add getBatteryInfo example (#2569) ([057512a](https://github.com/ionic-team/capacitor/commit/057512a)), closes [#2569](https://github.com/ionic-team/capacitor/issues/2569)\n- docs(firebase-guide): update guide with Capacitor 2.0 changes (#2598) ([4f0e749](https://github.com/ionic-team/capacitor/commit/4f0e749)), closes [#2598](https://github.com/ionic-team/capacitor/issues/2598)\n- docs(guides): Add new Deep Links guide (#2581) ([b9e25f3](https://github.com/ionic-team/capacitor/commit/b9e25f3)), closes [#2581](https://github.com/ionic-team/capacitor/issues/2581)\n- docs(ios): Add update guide for Capacitor 2.0 beta (#2571) ([ca0baf7](https://github.com/ionic-team/capacitor/commit/ca0baf7)), closes [#2571](https://github.com/ionic-team/capacitor/issues/2571)\n- docs(ios): Document hideLogs config option (#2619) ([23b2173](https://github.com/ionic-team/capacitor/commit/23b2173)), closes [#2619](https://github.com/ionic-team/capacitor/issues/2619)\n- docs(LocalNotifications): Update schedule sample (#2570) ([00d313f](https://github.com/ionic-team/capacitor/commit/00d313f)), closes [#2570](https://github.com/ionic-team/capacitor/issues/2570)\n- docs(network): Remove example guide because of dead link (#2417) ([2364505](https://github.com/ionic-team/capacitor/commit/2364505)), closes [#2417](https://github.com/ionic-team/capacitor/issues/2417)\n- docs(permissions): Display permissions page on side menu (#2684) ([1ccc3c0](https://github.com/ionic-team/capacitor/commit/1ccc3c0)), closes [#2684](https://github.com/ionic-team/capacitor/issues/2684)\n- docs(site): Add target blank to external links in site header (#2543) ([f6c2288](https://github.com/ionic-team/capacitor/commit/f6c2288)), closes [#2543](https://github.com/ionic-team/capacitor/issues/2543)\n- docs(Splash): document splashFullScreen and splashImmersive config options (#2613) ([c381202](https://github.com/ionic-team/capacitor/commit/c381202)), closes [#2613](https://github.com/ionic-team/capacitor/issues/2613)\n- docs(StatusBar): Fix typo in setOverlaysWebView usage (#2673) ([05f23fb](https://github.com/ionic-team/capacitor/commit/05f23fb)), closes [#2673](https://github.com/ionic-team/capacitor/issues/2673)\n- docs(types): explain FilesystemDirectory types (#2663) ([6a6cd8b](https://github.com/ionic-team/capacitor/commit/6a6cd8b)), closes [#2663](https://github.com/ionic-team/capacitor/issues/2663)\n- docs(update guide): fix targetSdkVersion instructions (#2585) ([0b4ade8](https://github.com/ionic-team/capacitor/commit/0b4ade8)), closes [#2585](https://github.com/ionic-team/capacitor/issues/2585)\n- docs(updating): Include beta 1 updating steps (#2629) ([ece7c47](https://github.com/ionic-team/capacitor/commit/ece7c47)), closes [#2629](https://github.com/ionic-team/capacitor/issues/2629)\n\n### Chores\n\n- chore: make deploy script publish Android before iOS (#2520) ([08a2ebc](https://github.com/ionic-team/capacitor/commit/08a2ebc)), closes [#2520](https://github.com/ionic-team/capacitor/issues/2520)\n- chore: update electron core dependency ([a37d1bf](https://github.com/ionic-team/capacitor/commit/a37d1bf))\n- chore(android): remove unused launch_splash.xml (#2411) ([8c9fe93](https://github.com/ionic-team/capacitor/commit/8c9fe93)), closes [#2411](https://github.com/ionic-team/capacitor/issues/2411)\n- chore(android): target SDK version 29 (#2433) ([4ff1943](https://github.com/ionic-team/capacitor/commit/4ff1943)), closes [#2433](https://github.com/ionic-team/capacitor/issues/2433)\n- chore(android): Update to latest Gradle plugin and wrapper (#2573) ([221ce96](https://github.com/ionic-team/capacitor/commit/221ce96)), closes [#2573](https://github.com/ionic-team/capacitor/issues/2573)\n- chore(android): use AndroidX to build ([1480a6f](https://github.com/ionic-team/capacitor/commit/1480a6f))\n- chore(circleci): update Xcode and remove install-cocoapods job (#2402) ([599c5c4](https://github.com/ionic-team/capacitor/commit/599c5c4)), closes [#2402](https://github.com/ionic-team/capacitor/issues/2402)\n- chore(cli): fix lint errors (#2479) ([f2ff5ab](https://github.com/ionic-team/capacitor/commit/f2ff5ab)), closes [#2479](https://github.com/ionic-team/capacitor/issues/2479)\n- chore(cli): fix tests for newer node versions (#2403) ([c40d993](https://github.com/ionic-team/capacitor/commit/c40d993)), closes [#2403](https://github.com/ionic-team/capacitor/issues/2403)\n- chore(dependencies): Update package dependencies to 2.0.0 (#2686) ([d708cfd](https://github.com/ionic-team/capacitor/commit/d708cfd)), closes [#2686](https://github.com/ionic-team/capacitor/issues/2686)\n- chore(electron): update template to use latest electron (#2492) ([178eb65](https://github.com/ionic-team/capacitor/commit/178eb65)), closes [#2492](https://github.com/ionic-team/capacitor/issues/2492)\n- chore(example): Fix Clipboard.read() (#2546) ([6b88ba8](https://github.com/ionic-team/capacitor/commit/6b88ba8)), closes [#2546](https://github.com/ionic-team/capacitor/issues/2546)\n- chore(example): update electron project to work with latest capacitor (#2485) ([09fff9b](https://github.com/ionic-team/capacitor/commit/09fff9b)), closes [#2485](https://github.com/ionic-team/capacitor/issues/2485)\n- chore(ios): add platform to Podfile (#2463) ([209e649](https://github.com/ionic-team/capacitor/commit/209e649)), closes [#2463](https://github.com/ionic-team/capacitor/issues/2463)\n- chore(ios): drop Xcode 10 support (#2472) ([255a046](https://github.com/ionic-team/capacitor/commit/255a046)), closes [#2472](https://github.com/ionic-team/capacitor/issues/2472)\n- chore(ios): remove deprecated .swift_version file (#2464) ([63e942e](https://github.com/ionic-team/capacitor/commit/63e942e)), closes [#2464](https://github.com/ionic-team/capacitor/issues/2464)\n- chore(ios): Update app template to use iOS 5 (#2467) ([f2facf6](https://github.com/ionic-team/capacitor/commit/f2facf6)), closes [#2467](https://github.com/ionic-team/capacitor/issues/2467)\n- chore(ios): update example app to use Swift 5 (#2471) ([afd8554](https://github.com/ionic-team/capacitor/commit/afd8554)), closes [#2471](https://github.com/ionic-team/capacitor/issues/2471)\n- chore(ios): Update plugin template to Swift 5 (#2468) ([2f9c8e6](https://github.com/ionic-team/capacitor/commit/2f9c8e6)), closes [#2468](https://github.com/ionic-team/capacitor/issues/2468)\n- chore(tests): run lint on circleci (#2480) ([2ec6cf5](https://github.com/ionic-team/capacitor/commit/2ec6cf5)), closes [#2480](https://github.com/ionic-team/capacitor/issues/2480)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Code of Conduct\n\nAs contributors and maintainers of the Capacitor project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.\n\nCommunication through any of Capacitor's channels (GitHub, Slack, Forum, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.\n\nWe promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Capacitor project to do the same.\n\nIf any member of the community violates this code of conduct, the maintainers of the Capacitor project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.\n\nIf you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [hi@ionicframework.com](mailto:hi@ionicframework.com).\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Capacitor\n\nThis guide provides instructions for contributing to Capacitor through [issues & discussions](#issues--discussions) and [code](#developing-capacitor).\n\n## Issues & Discussions\n\nThe Capacitor repo uses GitHub [issues](https://github.com/ionic-team/capacitor/issues) and [discussions](https://github.com/ionic-team/capacitor/discussions) to track bugs and feature requests, as well as to provide a place for community questions, ideas, and discussions.\n\n* **When to use [issues](https://github.com/ionic-team/capacitor/issues)**:\n    * To report specific, reproducible bugs (see [Creating a Code Reproduction](#creating-a-code-reproduction)).\n    * To propose detailed feature requests.\n* **When to use [discussions](https://github.com/ionic-team/capacitor/discussions)**:\n    * To ask for help.\n    * To ask general questions.\n    * To show off cool stuff.\n    * To propose ideas for improvement.\n    * If you think you found a bug, but may need help to further uncover it.\n    * Anything else! :rainbow:\n\n### Creating a Code Reproduction\n\nWhen reporting bugs, we ask you to provide a minimal sample application that demonstrates the issue. Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.\n\nTo create a code reproduction:\n\n* Create a new application using `npm init @capacitor/app` (or `ionic start --capacitor`).\n* Add the minimum amount of code necessary to recreate the issue you're experiencing.\n* Push the code reproduction to a public GitHub repository and include a link when you create a bug report.\n* Be sure to include steps to reproduce the issue.\n\n## Developing Capacitor\n\n### Repositories\n\n* [Capacitor](https://github.com/ionic-team/capacitor) (this repo): Core Capacitor platforms, CLI, and APIs\n* [Capacitor Plugins](https://github.com/ionic-team/capacitor-plugins): Official Capacitor plugins\n* [Capacitor Community](https://github.com/capacitor-community/): GitHub org for Capacitor Community plugins and platforms\n* [Capacitor Docs](https://github.com/ionic-team/capacitor-docs): Capacitor online documentation\n* [Capacitor TestApp](https://github.com/ionic-team/capacitor-testapp): Test app used by the core team for developing Capacitor\n\n### Design Philosophy\n\nBefore working on Capacitor, it's important to understand the philosophy behind the project to avoid investing time in things that won't fit into the goals of the project.\n\nPlease read Max Lynch's essay [How Capacitor Works](https://ionic.io/blog/how-capacitor-works-2) for a deep dive into the project and its goals.\n\n### Consult with the team\n\nFor any large changes, make sure you've consulted with the team first. You can [open a discussion](https://github.com/ionic-team/capacitor/discussions) to bring up your idea.\n\n### About Third Party Libraries\n\nTo achieve Capacitor's goal of being stable and easy to upgrade, we would like to avoid unnecessary third party libraries as much as possible. Before embarking on Capacitor contributions, make sure you aren't planning on introducing third party libraries without consulting with the team first.\n\nOn native, that means avoid adding any new Cocoapod or Gradle dependencies without explicit approval. If you just need a small bit of functionality from that library, consider adding an implementation to the codebase directly.\n\nOn web, this means do not add any third party libraries such as Firebase or Lodash. Strive for implementations that use pure Web APIs even if it means more work.\n\n### Local Setup\n\n1. Fork and clone the repo.\n1. Install the dependencies.\n\n    ```shell\n    npm install\n    ```\n\n1. Install SwiftLint if you're on macOS. Contributions to iOS code will be linted in CI if you don't have macOS.\n\n    ```shell\n    brew install swiftlint\n    ```\n\n### Branches\n\n* [`main`](https://github.com/ionic-team/capacitor/tree/main): Latest Capacitor development branch\n* [`6.x`](https://github.com/ionic-team/capacitor/tree/6.x): Capacitor 6\n* [`5.x`](https://github.com/ionic-team/capacitor/tree/5.x): Capacitor 5 (not maintained)\n* [`4.x`](https://github.com/ionic-team/capacitor/tree/4.x): Capacitor 4 (not maintained)\n* [`3.x`](https://github.com/ionic-team/capacitor/tree/3.x): Capacitor 3 (not maintained)\n* [`2.x`](https://github.com/ionic-team/capacitor/tree/2.x): Capacitor 2 (not maintained)\n* [`1.x`](https://github.com/ionic-team/capacitor/tree/1.x): Capacitor 1 (not maintained)\n\n### Directory Structure\n\nThis monorepo contains core Capacitor components. The current directory structure looks like this:\n\n* `android`: Capacitor Android Runtime\n* `android-template`: Default Android App installed by the CLI\n* `cli`: Capacitor CLI/Build scripts\n* `core`: Capacitor Core JS library\n* `ios`: Capacitor iOS Runtime\n* `ios-pods-template`: Default iOS CocoaPods App installed by the CLI\n* `ios-spm-template`: Default iOS SPM App installed by the CLI\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017-present Drifty Co.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<br />\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/236501/105104854-e5e42e80-5a67-11eb-8cb8-46fccb079062.png\" width=\"560\" />\n</div>\n<div align=\"center\">\n  ⚡️ Cross-platform apps with JavaScript and the Web ⚡️\n</div>\n<br />\n<p align=\"center\">\n  <a href=\"https://github.com/ionic-team/capacitor/actions?query=workflow%3ACI\"><img src=\"https://img.shields.io/github/actions/workflow/status/ionic-team/capacitor/ci.yml?style=flat-square\" /></a>\n  <a href=\"https://www.npmjs.com/package/@capacitor/core\"><img src=\"https://img.shields.io/npm/dw/@capacitor/core?style=flat-square\" /></a>\n  <a href=\"https://www.npmjs.com/package/@capacitor/core\"><img src=\"https://img.shields.io/npm/v/@capacitor/core?style=flat-square\" /></a>\n  <a href=\"https://www.npmjs.com/package/@capacitor/core\"><img src=\"https://img.shields.io/npm/l/@capacitor/core?style=flat-square\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://capacitorjs.com/docs\"><img src=\"https://img.shields.io/static/v1?label=docs&message=capacitorjs.com&color=blue&style=flat-square\" /></a>\n  <a href=\"https://twitter.com/capacitorjs\"><img src=\"https://img.shields.io/twitter/follow/capacitorjs\" /></a>\n</p>\n\n---\n\nCapacitor lets you run web apps natively on iOS, Android, Web, and more with a single codebase and cross-platform APIs.\n\nCapacitor provides a cross-platform API and code execution layer that makes it easy to call Native SDKs from web code and to write custom native plugins that your app may need. Additionally, Capacitor provides first-class Progressive Web App support so you can write one app and deploy it to the app stores _and_ the mobile web.\n\nCapacitor comes with a Plugin API for building native plugins. Plugins can be written inside Capacitor apps or packaged into an npm dependency for community use. Plugin authors are encouraged to use Swift to develop plugins in iOS and Kotlin (or Java) in Android.\n\n## Getting Started\n\nCapacitor was designed to drop-in to any existing modern web app. Run the following commands to initialize Capacitor in your app:\n\n```\nnpm install @capacitor/core @capacitor/cli\nnpx cap init\n```\n\nNext, install any of the desired native platforms:\n\n```\nnpm install @capacitor/android\nnpx cap add android\nnpm install @capacitor/ios\nnpx cap add ios\n```\n\n### New App?\n\nFor new apps, we recommend trying the [Ionic Framework](https://ionicframework.com/) with Capacitor.\n\nTo begin, install the [Ionic CLI](https://ionicframework.com/docs/cli/) (`npm install -g @ionic/cli`) and start a new app:\n\n```\nionic start --capacitor\n```\n\n## FAQ\n\n#### What are the differences between Capacitor and Cordova?\n\nIn spirit, Capacitor and Cordova are very similar. Capacitor offers backward compatibility with a vast majority of Cordova plugins.\n\nCapacitor and Cordova differ in that Capacitor:\n\n- takes a more modern approach to tooling and plugin development\n- treats native projects as source artifacts as opposed to build artifacts\n- is maintained by the Ionic Team 💙😊\n\nSee [the docs](https://capacitorjs.com/docs/cordova#differences-between-capacitor-and-cordova) for more details.\n\n#### Do I need to use Ionic Framework with Capacitor?\n\nNo, you do not need to use Ionic Framework with Capacitor. Without the Ionic Framework, you may need to implement Native UI yourself. Without the Ionic CLI, you may need to configure tooling yourself to enable features such as [livereload](https://ionicframework.com/docs/cli/livereload). See [the docs](https://capacitorjs.com/docs/getting-started/with-ionic) for more details.\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](./CONTRIBUTING.md).\n\n## Contributors\n\nMade possible by the [Capacitor community](https://github.com/ionic-team/capacitor/graphs/contributors). 💖\n"
  },
  {
    "path": "android/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": "android/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n# [8.2.0](https://github.com/ionic-team/capacitor/compare/8.1.0...8.2.0) (2026-03-06)\n\n### Bug Fixes\n\n- **android:** Add missing null checks in BridgeActivity ([#8185](https://github.com/ionic-team/capacitor/issues/8185)) ([bd29b99](https://github.com/ionic-team/capacitor/commit/bd29b9913a9279de26fc21c6cb0b93b8f5e5433a))\n- **android:** Concurrent Range Requests for assets ([#8357](https://github.com/ionic-team/capacitor/issues/8357)) ([5e82c89](https://github.com/ionic-team/capacitor/commit/5e82c89f1bff6d0e9ccea2554007aacb920d4c58))\n- **android:** handle lowercase range header ([#8368](https://github.com/ionic-team/capacitor/issues/8368)) ([ae0e2dd](https://github.com/ionic-team/capacitor/commit/ae0e2ddccb2904ee4b3d47d4be1f7556ac7000a1))\n- **android:** invalid http range seeking ([#8369](https://github.com/ionic-team/capacitor/issues/8369)) ([3109d22](https://github.com/ionic-team/capacitor/commit/3109d22547253ed44293777c60652f14cf83e416))\n\n# [8.1.0](https://github.com/ionic-team/capacitor/compare/8.0.2...8.1.0) (2026-02-11)\n\n### Reverts\n\n- revert version bump from [#8319](https://github.com/ionic-team/capacitor/issues/8319) and [#8320](https://github.com/ionic-team/capacitor/issues/8320) ([a48ebb6](https://github.com/ionic-team/capacitor/commit/a48ebb622ea4ebe92927bf1756a4d8ac6012884b))\n\n## [8.0.2](https://github.com/ionic-team/capacitor/compare/8.0.1...8.0.2) (2026-01-27)\n\n### Bug Fixes\n\n- **android:** AGP 9.0 no longer supports `proguard-android.txt` ([#8315](https://github.com/ionic-team/capacitor/issues/8315)) ([dcc76c3](https://github.com/ionic-team/capacitor/commit/dcc76c37508a9b71fb36d95707748c2dd115cf52))\n- **SystemBars:** get correct style on handleOnConfigurationChanged ([#8295](https://github.com/ionic-team/capacitor/issues/8295)) ([2a66b44](https://github.com/ionic-team/capacitor/commit/2a66b44915895f971e4a26a8612798aa2f95ea11))\n- **SystemBars:** Set window background color according to theme ([#8306](https://github.com/ionic-team/capacitor/issues/8306)) ([6037e38](https://github.com/ionic-team/capacitor/commit/6037e3836ec24c9ddf26e74e5fbec20ab506adfd))\n- **SystemBars:** Skipping margin manipulation when on a fixed WebView ([#8309](https://github.com/ionic-team/capacitor/issues/8309)) ([53c33b6](https://github.com/ionic-team/capacitor/commit/53c33b614218bf635322fbdf9a38038a7964e3d4))\n\n## [8.0.1](https://github.com/ionic-team/capacitor/compare/8.0.0...8.0.1) (2026-01-13)\n\n### Bug Fixes\n\n- **android:** Remove calculated bottom inset if keyboard is visible ([#8280](https://github.com/ionic-team/capacitor/issues/8280)) ([196b642](https://github.com/ionic-team/capacitor/commit/196b642236d293a5012e3c307fcd942766e56cce))\n\n# [8.0.0](https://github.com/ionic-team/capacitor/compare/8.0.0-beta.0...8.0.0) (2025-12-08)\n\n### Features\n\n- **android:** Improving SystemBars inset handling ([#8268](https://github.com/ionic-team/capacitor/issues/8268)) ([81ae30a](https://github.com/ionic-team/capacitor/commit/81ae30a503797e417dd125b06262dabc4696c88a))\n\n# [8.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.3...8.0.0-beta.0) (2025-11-14)\n\n### Bug Fixes\n\n- **android:** remove kotlin-bom dependency ([#8237](https://github.com/ionic-team/capacitor/issues/8237)) ([a894749](https://github.com/ionic-team/capacitor/commit/a89474920569d404e3b19e22e782e656d5b59bdd))\n- **android:** replace deprecated Gradle property name syntax ([b68ac9e](https://github.com/ionic-team/capacitor/commit/b68ac9e2dfc3ed292b7879bd7aeaab3d05ac84d5))\n- make Plugin.resolve act consistently ([#8225](https://github.com/ionic-team/capacitor/issues/8225)) ([06aeb9e](https://github.com/ionic-team/capacitor/commit/06aeb9e85d162d6be9d96820edcb2008cd74da84))\n\n### Features\n\n- System Bars Plugin ([#8180](https://github.com/ionic-team/capacitor/issues/8180)) ([a32216a](https://github.com/ionic-team/capacitor/commit/a32216ac0607172a3a9c7ae5cdbfc598769294a6))\n\n# [8.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.2...8.0.0-alpha.3) (2025-10-22)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [8.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.1...8.0.0-alpha.2) (2025-08-20)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [8.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/7.4.2...8.0.0-alpha.1) (2025-08-20)\n\n### Bug Fixes\n\n- http content headers not sent when using axios ([#8039](https://github.com/ionic-team/capacitor/issues/8039)) ([67cac40](https://github.com/ionic-team/capacitor/commit/67cac40660b3e8cc78d1d228b7c6915450948ef1))\n\n### Features\n\n- **android:** Bump default minSdkVersion to 24 ([#8083](https://github.com/ionic-team/capacitor/issues/8083)) ([c022ddf](https://github.com/ionic-team/capacitor/commit/c022ddf21452379bb183ff7c846f73bd07a15dbf))\n\n## [7.4.2](https://github.com/ionic-team/capacitor/compare/7.4.1...7.4.2) (2025-07-10)\n\n### Bug Fixes\n\n- **android:** consider display cutout area for insets ([#8042](https://github.com/ionic-team/capacitor/issues/8042)) ([b478211](https://github.com/ionic-team/capacitor/commit/b4782116856c35e3fb567393f10a36ce4632b44c))\n- **http:** Properly URL-encode key and values during `x-www-form-urlencoded` POSTs ([#8037](https://github.com/ionic-team/capacitor/issues/8037)) ([87b4641](https://github.com/ionic-team/capacitor/commit/87b4641d1fa32b78e6fc2e87ee7b2c49b625b213))\n\n## [7.4.1](https://github.com/ionic-team/capacitor/compare/7.4.0...7.4.1) (2025-07-03)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [7.4.0](https://github.com/ionic-team/capacitor/compare/7.3.0...7.4.0) (2025-06-18)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [7.3.0](https://github.com/ionic-team/capacitor/compare/7.2.0...7.3.0) (2025-06-05)\n\n### Bug Fixes\n\n- Remove all plugin listeners in bridge reset ([#7962](https://github.com/ionic-team/capacitor/issues/7962)) ([06aeea9](https://github.com/ionic-team/capacitor/commit/06aeea973aa56ac2fb791f893ebd899253ee65f9))\n\n### Features\n\n- expose `appStartPath` on cap config server configuration ([#8019](https://github.com/ionic-team/capacitor/issues/8019)) ([a274fef](https://github.com/ionic-team/capacitor/commit/a274fef069176a8e528a22f4734d1e29a539709c))\n\n# [7.2.0](https://github.com/ionic-team/capacitor/compare/7.1.0...7.2.0) (2025-03-31)\n\n### Bug Fixes\n\n- **android:** rename bridge layout to avoid collision ([#7919](https://github.com/ionic-team/capacitor/issues/7919)) ([a629f16](https://github.com/ionic-team/capacitor/commit/a629f167b345cda1c44f37131235d14353eff504))\n\n### Features\n\n- **http:** Apply overrideUserAgent to requests ([#7906](https://github.com/ionic-team/capacitor/issues/7906)) ([52482c9](https://github.com/ionic-team/capacitor/commit/52482c9d3c575b737054b41f9d1730c70cc5f471))\n\n# [7.1.0](https://github.com/ionic-team/capacitor/compare/7.0.1...7.1.0) (2025-03-12)\n\n### Bug Fixes\n\n- **android:** add EdgeToEdge compatibility ([#7871](https://github.com/ionic-team/capacitor/issues/7871)) ([64a8bc4](https://github.com/ionic-team/capacitor/commit/64a8bc40de2522c75a94a40cba6c8ccd82481cb8))\n- **android:** sanitize portable file name ([#7894](https://github.com/ionic-team/capacitor/issues/7894)) ([5f09297](https://github.com/ionic-team/capacitor/commit/5f092970e33da1ec896efc4e2a5ae3fb77fca658))\n- **http:** boundary not added for Request objects ([#7897](https://github.com/ionic-team/capacitor/issues/7897)) ([bdaa6f3](https://github.com/ionic-team/capacitor/commit/bdaa6f3c38c33f3a021ac61f2de89101a5b66cff))\n\n### Features\n\n- Add function to inject external JS into WebView before document load ([#7864](https://github.com/ionic-team/capacitor/issues/7864)) ([ec0954c](https://github.com/ionic-team/capacitor/commit/ec0954c197543e913939f3ab9c4bcb172bfa3530))\n- **android:** add adjustMarginsForEdgeToEdge configuration option ([#7885](https://github.com/ionic-team/capacitor/issues/7885)) ([1ea86d1](https://github.com/ionic-team/capacitor/commit/1ea86d166afd315e72847c5e734a8c175fb90e04))\n\n## [7.0.1](https://github.com/ionic-team/capacitor/compare/7.0.0...7.0.1) (2025-01-21)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [7.0.0](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.1...7.0.0) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [7.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.0...7.0.0-rc.1) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [7.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/7.0.0-beta.0...7.0.0-rc.0) (2024-12-20)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [7.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.2...7.0.0-beta.0) (2024-12-20)\n\n### Features\n\n- Add global initialFocus configuration ([#7775](https://github.com/ionic-team/capacitor/issues/7775)) ([61d0165](https://github.com/ionic-team/capacitor/commit/61d01653685d8e3594d2d8a6bd870fa9643ba95c))\n\n# [7.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.1...7.0.0-alpha.2) (2024-11-19)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [7.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/6.1.2...7.0.0-alpha.1) (2024-10-14)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [6.1.2](https://github.com/ionic-team/capacitor/compare/6.1.1...6.1.2) (2024-08-07)\n\n### Bug Fixes\n\n- **android:** better cleaning of allowedOrigin url ([#7607](https://github.com/ionic-team/capacitor/issues/7607)) ([d123260](https://github.com/ionic-team/capacitor/commit/d123260c70f26b0e786515ced9c95447f9ee81a0))\n- **http:** pass original url as query param on the proxy url ([#7527](https://github.com/ionic-team/capacitor/issues/7527)) ([1da06e6](https://github.com/ionic-team/capacitor/commit/1da06e66cb9cfbf5a5cc48ba6c23cdbe18bc8fc0))\n\n## [6.1.1](https://github.com/ionic-team/capacitor/compare/6.1.0...6.1.1) (2024-07-17)\n\n### Bug Fixes\n\n- **android:** Allow WebView to load blob urls ([#7551](https://github.com/ionic-team/capacitor/issues/7551)) ([0dca917](https://github.com/ionic-team/capacitor/commit/0dca917da54b01c6cf4fcd063f8191a8457d7c93))\n- **android:** javascript injection not working on urls with query ([#7545](https://github.com/ionic-team/capacitor/issues/7545)) ([57ce5c6](https://github.com/ionic-team/capacitor/commit/57ce5c65560c34d106e8b88cffe26ad22947bba6))\n- **android:** send FormData on older devices ([#7519](https://github.com/ionic-team/capacitor/issues/7519)) ([de8b50d](https://github.com/ionic-team/capacitor/commit/de8b50dc1d87510c5f19d04f30b6be12b51d8482))\n- **android:** UTF-8 encode form data value ([#7525](https://github.com/ionic-team/capacitor/issues/7525)) ([a73ed31](https://github.com/ionic-team/capacitor/commit/a73ed318cdb819863425f6b1b7ec23ea53454931))\n- **http:** handle UInt8Array on body ([#7546](https://github.com/ionic-team/capacitor/issues/7546)) ([cfb9ce1](https://github.com/ionic-team/capacitor/commit/cfb9ce175615f69fe86b61af6d51ec2044d147cd))\n\n# [6.1.0](https://github.com/ionic-team/capacitor/compare/6.0.0...6.1.0) (2024-06-11)\n\n### Bug Fixes\n\n- **android:** avoid crash if server url ends in / ([#7426](https://github.com/ionic-team/capacitor/issues/7426)) ([f8264cc](https://github.com/ionic-team/capacitor/commit/f8264ccae1f2fec553521bc62d767c4909ea6d79))\n- **http:** don't override readyState for non POST requests ([#7488](https://github.com/ionic-team/capacitor/issues/7488)) ([30c13a8](https://github.com/ionic-team/capacitor/commit/30c13a865e7710e6dc5f0ee014e951d52d030795))\n\n# [6.0.0](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.2...6.0.0) (2024-04-15)\n\n### Bug Fixes\n\n- **http:** Add URLSearchParams support ([#7374](https://github.com/ionic-team/capacitor/issues/7374)) ([9367ecc](https://github.com/ionic-team/capacitor/commit/9367ecc56a0c78249dccdf95dca5006422144289))\n- **http:** prevent POST request from being proxied ([#7395](https://github.com/ionic-team/capacitor/issues/7395)) ([7b8c352](https://github.com/ionic-team/capacitor/commit/7b8c3523decd5610dcf09e926640bf35b382d61d))\n- secure cookies often are not set by the cookie plugin ([#7261](https://github.com/ionic-team/capacitor/issues/7261)) ([cda1886](https://github.com/ionic-team/capacitor/commit/cda18861aea3ced7835f959cba612cea98761c58))\n\n# [6.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.1...6.0.0-rc.2) (2024-03-25)\n\n### Bug Fixes\n\n- **http:** change proxy url generation ([#7354](https://github.com/ionic-team/capacitor/issues/7354)) ([318c316](https://github.com/ionic-team/capacitor/commit/318c316847c5b059fb88b46d4acd31e1ced477e5))\n- vue 3 log warning causes error on iOS ([#6993](https://github.com/ionic-team/capacitor/issues/6993)) ([87271e2](https://github.com/ionic-team/capacitor/commit/87271e2671013ad35d13b22f2e96d4fe8f4eeaf0))\n\n# [6.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.0...6.0.0-rc.1) (2024-03-15)\n\n### Bug Fixes\n\n- **android:** handle http errors on the proxy ([#7280](https://github.com/ionic-team/capacitor/issues/7280)) ([cca3c80](https://github.com/ionic-team/capacitor/commit/cca3c80298b768a5eaf1da4d95f659e303ed7042))\n- **android:** incorrect http url params encoding ([#6586](https://github.com/ionic-team/capacitor/issues/6586)) ([e9ddb0c](https://github.com/ionic-team/capacitor/commit/e9ddb0cfdb1ed320b650e1d01a04d37e644f6dd3))\n- **android:** let WebView handle errors ([#7286](https://github.com/ionic-team/capacitor/issues/7286)) ([286b694](https://github.com/ionic-team/capacitor/commit/286b69460f1227a75a9865f91fb51c455fd4370e))\n- **android:** make JSInjector replace first <head> only ([#6895](https://github.com/ionic-team/capacitor/issues/6895)) ([93c8a8d](https://github.com/ionic-team/capacitor/commit/93c8a8ddbc547a6a9501e02a73719268228ebdca))\n- **android:** prevent crash on script injection if the script is too long ([#7308](https://github.com/ionic-team/capacitor/issues/7308)) ([c9895e8](https://github.com/ionic-team/capacitor/commit/c9895e89aeb69613906fc8a2270f7bf3f4913fda))\n- **android:** Remove path from allowedOriginRules ([#7293](https://github.com/ionic-team/capacitor/issues/7293)) ([9eb565c](https://github.com/ionic-team/capacitor/commit/9eb565cddd30888125d1503d1c0a5aa5aa5e48cb))\n- **core:** make 'content-type' header count for XMLHttpRequest patch ([#7161](https://github.com/ionic-team/capacitor/issues/7161)) ([26d7f68](https://github.com/ionic-team/capacitor/commit/26d7f688284914c6ef795564ba424119efc32a1c))\n- **http:** better handling of active requests and shutting down gracefully ([5d294c9](https://github.com/ionic-team/capacitor/commit/5d294c9b84f09bab649a7112c86959a41a9a4f5e))\n- **http:** handle proxy urls with port ([#7273](https://github.com/ionic-team/capacitor/issues/7273)) ([514409a](https://github.com/ionic-team/capacitor/commit/514409aeb93ad65be105bbe2da8d2cd86ff159b0))\n- **http:** keep original URL properties on proxy ([#7329](https://github.com/ionic-team/capacitor/issues/7329)) ([cbb6407](https://github.com/ionic-team/capacitor/commit/cbb6407225b42a0d9db4f335a9766f119501021d))\n- **http:** Make proxy work with Request objects ([#7348](https://github.com/ionic-team/capacitor/issues/7348)) ([7e68725](https://github.com/ionic-team/capacitor/commit/7e6872573df03ab5cdc10a1a27db3e9fe81a141d))\n- **http:** route get requests through custom handler ([#6818](https://github.com/ionic-team/capacitor/issues/6818)) ([b853d06](https://github.com/ionic-team/capacitor/commit/b853d065055b5a819949551be58b62d40b52e37c))\n- **http:** set port for proxy url ([#7341](https://github.com/ionic-team/capacitor/issues/7341)) ([a3059dc](https://github.com/ionic-team/capacitor/commit/a3059dca4a7746d9fb7102a7d41f4da80e2f48b4))\n\n### Features\n\n- **webview:** add setServerAssetPath method ([881235b](https://github.com/ionic-team/capacitor/commit/881235b14de23ef988746bfb89a5a0fc3c8d8466))\n\n# [6.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.2...6.0.0-rc.0) (2024-01-23)\n\n### Bug Fixes\n\n- **android:** don't proxy requests if no jsInjector ([#7199](https://github.com/ionic-team/capacitor/issues/7199)) ([7dc5585](https://github.com/ionic-team/capacitor/commit/7dc5585996c17eedd154b5d9118eb880fef616fe))\n- **android:** handle deprecation warnings ([#7190](https://github.com/ionic-team/capacitor/issues/7190)) ([06636d7](https://github.com/ionic-team/capacitor/commit/06636d73a1b12c8f932642ee7caf91432690fea5))\n\n### Features\n\n- **android:** Load alternative layout when no WebView ([#7141](https://github.com/ionic-team/capacitor/issues/7141)) ([87c399a](https://github.com/ionic-team/capacitor/commit/87c399ab6b41f217b4d8a58bfb916a5d78641fd0))\n\n# [6.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.1...6.0.0-beta.2) (2023-12-14)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [6.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.0...6.0.0-beta.1) (2023-12-14)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [6.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.2...6.0.0-beta.0) (2023-12-13)\n\n### Bug Fixes\n\n- **http:** parse readablestream data on fetch request objects ([#6919](https://github.com/ionic-team/capacitor/issues/6919)) ([80ec3b7](https://github.com/ionic-team/capacitor/commit/80ec3b73db18b7b6841bf90ed50a67389946ab87))\n- **http:** properly write form-urlencoded data on android request body ([#7001](https://github.com/ionic-team/capacitor/issues/7001)) ([a986ee5](https://github.com/ionic-team/capacitor/commit/a986ee541f54a1d3ac637b514fe547b224b36903))\n\n### Features\n\n- support for Amazon Fire WebView ([#6603](https://github.com/ionic-team/capacitor/issues/6603)) ([3cb4eb8](https://github.com/ionic-team/capacitor/commit/3cb4eb89632bce8dc872418fdb130bfd4de40b68))\n\n# [6.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.1...6.0.0-alpha.2) (2023-11-15)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [6.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/5.2.3...6.0.0-alpha.1) (2023-11-08)\n\n### Bug Fixes\n\n- **android:** handle webview version for developer builds ([#6907](https://github.com/ionic-team/capacitor/issues/6907)) ([88498e6](https://github.com/ionic-team/capacitor/commit/88498e6228492a9ae917d3a7b37c242881f9fe52))\n- **android:** make local urls use unpatched fetch ([#6953](https://github.com/ionic-team/capacitor/issues/6953)) ([e50e56c](https://github.com/ionic-team/capacitor/commit/e50e56c5231f230497d1bd420e02e2e065c38f86))\n- **android:** Use Logger class instead of Log in CapacitorCookieManager ([#6923](https://github.com/ionic-team/capacitor/issues/6923)) ([8aaa356](https://github.com/ionic-team/capacitor/commit/8aaa356ab1f14b56df821e8ac0bb7e43bfa094fa))\n- **cookies:** remove session cookies when initializing the cookie manager ([037863b](https://github.com/ionic-team/capacitor/commit/037863bea6f3a00978125dc2f8ecba1e896c0740))\n- **http:** add support for Request objects in fetch ([24b3cc1](https://github.com/ionic-team/capacitor/commit/24b3cc113e3d8aae5d85dbf2d25bec0c35136477))\n- **http:** disconnect active connections if call or bridge is destroyed ([a1ed6cc](https://github.com/ionic-team/capacitor/commit/a1ed6cc6f07465d683b95e3796d944f863a7b857))\n- **http:** inherit object properties on window.XMLHttpRequest ([91c11d0](https://github.com/ionic-team/capacitor/commit/91c11d06f773c45a10f6f2d52f672ae6f189b162))\n- **http:** return numbers and booleans as-is when application/json is the content type ([03dd3f9](https://github.com/ionic-team/capacitor/commit/03dd3f96c7ee75b6fff2b7c40d0c9a58fb04fce5))\n\n### Features\n\n- **android:** allow developers to provide logic for onRenderProcessGone in WebViewListener ([#6966](https://github.com/ionic-team/capacitor/issues/6966)) ([79e17bb](https://github.com/ionic-team/capacitor/commit/79e17bb5e6ccd813bddc626703152d3983f6d93b))\n\n# [5.6.0](https://github.com/ionic-team/capacitor/compare/5.5.1...5.6.0) (2023-12-14)\n\n### Bug Fixes\n\n- **http:** properly write form-urlencoded data on android request body ([#7130](https://github.com/ionic-team/capacitor/issues/7130)) ([a745a89](https://github.com/ionic-team/capacitor/commit/a745a89e18a5082ae4e737d78aa20929f6952382))\n\n### Features\n\n- support for Amazon Fire WebView ([#6603](https://github.com/ionic-team/capacitor/issues/6603)) ([#7129](https://github.com/ionic-team/capacitor/issues/7129)) ([421d2c0](https://github.com/ionic-team/capacitor/commit/421d2c02e4d1954d16d573facae9c235fee60f02))\n\n## [5.5.1](https://github.com/ionic-team/capacitor/compare/5.5.0...5.5.1) (2023-10-25)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.5.0](https://github.com/ionic-team/capacitor/compare/5.4.2...5.5.0) (2023-10-11)\n\n### Features\n\n- **android:** allow developers to provide logic for onRenderProcessGone in WebViewListener ([#6946](https://github.com/ionic-team/capacitor/issues/6946)) ([34b724a](https://github.com/ionic-team/capacitor/commit/34b724a4cf406c23b2a9952ef81e0327b78a3b3a))\n\n## [5.4.2](https://github.com/ionic-team/capacitor/compare/5.4.1...5.4.2) (2023-10-04)\n\n### Bug Fixes\n\n- **android:** make local urls use unpatched fetch ([#6954](https://github.com/ionic-team/capacitor/issues/6954)) ([56fb853](https://github.com/ionic-team/capacitor/commit/56fb8536af53f4f4ee49b9394fd966ad514b9458))\n\n## [5.4.1](https://github.com/ionic-team/capacitor/compare/5.4.0...5.4.1) (2023-09-21)\n\n### Bug Fixes\n\n- **android:** handle webview version for developer builds ([#6911](https://github.com/ionic-team/capacitor/issues/6911)) ([b5b0398](https://github.com/ionic-team/capacitor/commit/b5b0398a7fe117a824f97125f5feabe81073daf3))\n- **android:** Use Logger class instead of Log in CapacitorCookieManager ([#6925](https://github.com/ionic-team/capacitor/issues/6925)) ([b6901e0](https://github.com/ionic-team/capacitor/commit/b6901e01e05cd22a71841d2f5821fbe2a6939ead))\n- **cookies:** retrieve cookies when using a custom android scheme ([6b5ddad](https://github.com/ionic-team/capacitor/commit/6b5ddad8b36e33ef4171f6da5cc311ed3f634ac6))\n- **http:** parse readablestream data on fetch request objects ([3fe0642](https://github.com/ionic-team/capacitor/commit/3fe06426bd20713e2322780b70bc5d97ad371fae))\n- **http:** return xhr response headers case insensitive ([687b6b1](https://github.com/ionic-team/capacitor/commit/687b6b1780506c17fb73ed1d9cbf50c1d1e40ef1))\n\n# [5.4.0](https://github.com/ionic-team/capacitor/compare/5.3.0...5.4.0) (2023-09-14)\n\n### Bug Fixes\n\n- **http:** add support for defining xhr and angular http response types ([09bd040](https://github.com/ionic-team/capacitor/commit/09bd040dfe4b8808d7499b6ee592005420406cac))\n- **http:** add support for Request objects in fetch ([2fe4535](https://github.com/ionic-team/capacitor/commit/2fe4535e781b1a5cfa0f3359c1afa5c360073b6a))\n- **http:** inherit object properties on window.XMLHttpRequest ([5cd3b2f](https://github.com/ionic-team/capacitor/commit/5cd3b2fa6d6936864e1aab2e98963df2d4da3b95))\n\n# [5.3.0](https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0) (2023-08-23)\n\n### Bug Fixes\n\n- **cookies:** remove session cookies when initializing the cookie manager ([037863b](https://github.com/ionic-team/capacitor/commit/037863bea6f3a00978125dc2f8ecba1e896c0740))\n- **http:** disconnect active connections if call or bridge is destroyed ([a1ed6cc](https://github.com/ionic-team/capacitor/commit/a1ed6cc6f07465d683b95e3796d944f863a7b857))\n- **http:** return numbers and booleans as-is when application/json is the content type ([03dd3f9](https://github.com/ionic-team/capacitor/commit/03dd3f96c7ee75b6fff2b7c40d0c9a58fb04fce5))\n\n## [5.2.3](https://github.com/ionic-team/capacitor/compare/5.2.2...5.2.3) (2023-08-10)\n\n### Bug Fixes\n\n- **android:** allow single input file selection from samsumg gallery ([#6778](https://github.com/ionic-team/capacitor/issues/6778)) ([3d57ecd](https://github.com/ionic-team/capacitor/commit/3d57ecdf7631d1581047bd5d9f86ea657ecad845))\n- **android:** avoid R8 optimizations remove plugin classes ([#6783](https://github.com/ionic-team/capacitor/issues/6783)) ([cc85df5](https://github.com/ionic-team/capacitor/commit/cc85df5f3a6999883623054573bafc30665e41e7))\n- **cookies:** hide httpOnly cookies from client ([0cc927e](https://github.com/ionic-team/capacitor/commit/0cc927ef5f0f7076a6d486d666d78483f1d71c54))\n- **http:** return valid response for relative url xhr requests ([bde6569](https://github.com/ionic-team/capacitor/commit/bde65696218f97a8328041f137457f46e5eb766a))\n\n## [5.2.2](https://github.com/ionic-team/capacitor/compare/5.2.1...5.2.2) (2023-07-19)\n\n### Bug Fixes\n\n- add http method to prototype.open ([#6740](https://github.com/ionic-team/capacitor/issues/6740)) ([1fd2d87](https://github.com/ionic-team/capacitor/commit/1fd2d8762ff2341a8fe20eec9e774c6a29576e88))\n\n## [5.2.1](https://github.com/ionic-team/capacitor/compare/5.2.0...5.2.1) (2023-07-13)\n\n### Bug Fixes\n\n- allow single parameter on setRequestBody ([#6728](https://github.com/ionic-team/capacitor/issues/6728)) ([5343bdb](https://github.com/ionic-team/capacitor/commit/5343bdb60d26849cd8f9c8ff28ba7d9ddbd05b26))\n\n# [5.2.0](https://github.com/ionic-team/capacitor/compare/5.1.1...5.2.0) (2023-07-12)\n\n### Bug Fixes\n\n- **cookies:** sanitize url before retrieving/setting cookies ([ca40634](https://github.com/ionic-team/capacitor/commit/ca4063471f215d3f7525e51592d9c72138a52855))\n- **http:** fire events in correct order when using xhr ([5ed3617](https://github.com/ionic-team/capacitor/commit/5ed361787596bb5949f6ae5e366495f296352bf3))\n\n### Features\n\n- **http:** support for FormData requests ([#6708](https://github.com/ionic-team/capacitor/issues/6708)) ([849c564](https://github.com/ionic-team/capacitor/commit/849c56458205bea3b078b1ee19807d7fd84c47b1))\n\n## [5.1.1](https://github.com/ionic-team/capacitor/compare/5.1.0...5.1.1) (2023-07-05)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.1.0](https://github.com/ionic-team/capacitor/compare/5.0.5...5.1.0) (2023-06-29)\n\n### Bug Fixes\n\n- **android:** Move bridge localUrl initialization to initWebView ([#6685](https://github.com/ionic-team/capacitor/issues/6685)) ([7f5f0ca](https://github.com/ionic-team/capacitor/commit/7f5f0ca4220d40d6a19c778c18f9534ef3b65899))\n- **android:** revert cookie manager initialization to plugin load ([53a2d47](https://github.com/ionic-team/capacitor/commit/53a2d4792e026a89723a672a01fc34990add71f0))\n\n### Features\n\n- **android:** add check for excluded domains before ssl request ([7906d36](https://github.com/ionic-team/capacitor/commit/7906d3616e8bfb2e2c1c81ee123424c06fc4e5ab))\n\n## [5.0.5](https://github.com/ionic-team/capacitor/compare/5.0.4...5.0.5) (2023-06-09)\n\n### Bug Fixes\n\n- **http:** don't throw errors when content-type is null on response ([#6627](https://github.com/ionic-team/capacitor/issues/6627)) ([538821f](https://github.com/ionic-team/capacitor/commit/538821f267aa3b79548fed6aaea8880ff949ffdd))\n\n## [5.0.4](https://github.com/ionic-team/capacitor/compare/5.0.3...5.0.4) (2023-05-23)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [5.0.3](https://github.com/ionic-team/capacitor/compare/5.0.2...5.0.3) (2023-05-10)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [5.0.2](https://github.com/ionic-team/capacitor/compare/5.0.1...5.0.2) (2023-05-09)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [5.0.1](https://github.com/ionic-team/capacitor/compare/5.0.0...5.0.1) (2023-05-05)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.0.0](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.3...5.0.0) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.2...5.0.0-rc.3) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.1...5.0.0-rc.2) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.0...5.0.0-rc.1) (2023-05-02)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.3...5.0.0-rc.0) (2023-05-01)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.2...5.0.0-beta.3) (2023-04-21)\n\n### Bug Fixes\n\n- **cookies:** init cookie manager after server url is set ([0ee772f](https://github.com/ionic-team/capacitor/commit/0ee772ff6456ad0948a0dd025dfcf2658a5563a0))\n\n### Features\n\n- **android:** update gradle to 8.0.2 and gradle plugin to 8.0.0 ([#6497](https://github.com/ionic-team/capacitor/issues/6497)) ([01b5b39](https://github.com/ionic-team/capacitor/commit/01b5b399324ae5d0896989478a6910fb946542d7))\n\n# [5.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.1...5.0.0-beta.2) (2023-04-13)\n\n### Bug Fixes\n\n- **android:** launching intents without host ([#6489](https://github.com/ionic-team/capacitor/issues/6489)) ([95f7474](https://github.com/ionic-team/capacitor/commit/95f747401ac5a666de4338a18666060e9c1ff39e))\n- **android:** unify kotlin dependency version ([#6501](https://github.com/ionic-team/capacitor/issues/6501)) ([0a40477](https://github.com/ionic-team/capacitor/commit/0a4047768cbde9bc17d92955e64ab11d2e3b3335))\n- **cookies:** check isEnabled before setting cookieHandler ([bb04f24](https://github.com/ionic-team/capacitor/commit/bb04f24f0b4a99e46ed5ca047d3d3df81804d516))\n- **ios/android:** copy url from nativeResponse to response ([#6482](https://github.com/ionic-team/capacitor/issues/6482)) ([828fb71](https://github.com/ionic-team/capacitor/commit/828fb71ebb52c0655d5879ad0edaac7368ab2b96))\n- remove accept-charset ([#6386](https://github.com/ionic-team/capacitor/issues/6386)) ([bbf6f7e](https://github.com/ionic-team/capacitor/commit/bbf6f7e1af0c49c0bc917942b6715c613be3f557))\n\n# [5.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.0...5.0.0-beta.1) (2023-04-03)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [5.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/5.0.0-alpha.1...5.0.0-beta.0) (2023-03-31)\n\n### Bug Fixes\n\n- 204 http response ([#6266](https://github.com/ionic-team/capacitor/issues/6266)) ([771f6ce](https://github.com/ionic-team/capacitor/commit/771f6ce1f35159848db218a42dc4f56b5106f750))\n- **android:** Allow WebView to load data urls ([#6418](https://github.com/ionic-team/capacitor/issues/6418)) ([daf2ec6](https://github.com/ionic-team/capacitor/commit/daf2ec64df0c567c6a42560488e5d2515eff8a33))\n- **android:** proper app url check for launching intents ([#6450](https://github.com/ionic-team/capacitor/issues/6450)) ([302ba35](https://github.com/ionic-team/capacitor/commit/302ba353acbd6d67e96e2b28870bc9c5bb4e9af0))\n- **android:** remove stored references to bridge that holds it in memory ([#6448](https://github.com/ionic-team/capacitor/issues/6448)) ([4737d2b](https://github.com/ionic-team/capacitor/commit/4737d2b46b480c7c0246ac6414494cbbdac7811b))\n\n### Features\n\n- **android:** Fix for [#6258](https://github.com/ionic-team/capacitor/issues/6258), Add support for modern Huawei devices ([#6402](https://github.com/ionic-team/capacitor/issues/6402)) ([17f2f4a](https://github.com/ionic-team/capacitor/commit/17f2f4ac744a038c1dae3cbd74a670d5ede92ef3))\n- retain multiple calls per event until consumed ([#6419](https://github.com/ionic-team/capacitor/issues/6419)) ([5aba2cb](https://github.com/ionic-team/capacitor/commit/5aba2cbe29bdbab2a7af861c65d8323acf9c54a6))\n\n# [5.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/4.7.0...5.0.0-alpha.1) (2023-03-16)\n\n### Bug Fixes\n\n- **android:** handle empty permission list ([#6375](https://github.com/ionic-team/capacitor/issues/6375)) ([b11a9df](https://github.com/ionic-team/capacitor/commit/b11a9df070f18a25364a9109e295556fc75ea7f9))\n- **android:** handle null http headers and params ([#6370](https://github.com/ionic-team/capacitor/issues/6370)) ([e486672](https://github.com/ionic-team/capacitor/commit/e486672731818d5c64c50956562aa4766f169d41))\n- **android:** solve and/or silence lint errors ([#6358](https://github.com/ionic-team/capacitor/issues/6358)) ([c627415](https://github.com/ionic-team/capacitor/commit/c627415743bec92dcb65ab8b8840003d8c0a5286))\n\n### Features\n\n- **android:** Removing enableJetifier ([#6333](https://github.com/ionic-team/capacitor/issues/6333)) ([fc0b403](https://github.com/ionic-team/capacitor/commit/fc0b403265f63eab35cdb2f262fb1e047db4b6bd))\n\n# [4.7.0](https://github.com/ionic-team/capacitor/compare/4.6.3...4.7.0) (2023-02-22)\n\n### Bug Fixes\n\n- handle fetch headers that are Headers objects ([#6320](https://github.com/ionic-team/capacitor/issues/6320)) ([cb00e49](https://github.com/ionic-team/capacitor/commit/cb00e4952acca8e877555f30b2190f6685d25934))\n- **ios:** Correctly Attach Headers to Request ([#6303](https://github.com/ionic-team/capacitor/issues/6303)) ([a3f875c](https://github.com/ionic-team/capacitor/commit/a3f875cf42e111fde07d6e87643264b19ed77573))\n\n### Features\n\n- **android:** add ability to create config from a custom file path ([#6264](https://github.com/ionic-team/capacitor/issues/6264)) ([42b4f0f](https://github.com/ionic-team/capacitor/commit/42b4f0f416c8038ae368860007910bb09c8ec84e))\n- **android:** Add SSL Pinning logic ([#6314](https://github.com/ionic-team/capacitor/issues/6314)) ([07f113e](https://github.com/ionic-team/capacitor/commit/07f113e6933e15c45d772f69f7128cbb3706f7b9))\n- **android:** enable loading of assets outside of the content web asset directory ([#6301](https://github.com/ionic-team/capacitor/issues/6301)) ([364497d](https://github.com/ionic-team/capacitor/commit/364497d4aca93fc716a0673ef9103479aed791ec))\n\n## [4.6.3](https://github.com/ionic-team/capacitor/compare/4.6.2...4.6.3) (2023-02-03)\n\n### Bug Fixes\n\n- **ios:** crash when http headers contain numbers ([#6251](https://github.com/ionic-team/capacitor/issues/6251)) ([028c556](https://github.com/ionic-team/capacitor/commit/028c556a50b41ee99fe8f4f1aa2f42d3fd57f92d))\n\n## [4.6.2](https://github.com/ionic-team/capacitor/compare/4.6.1...4.6.2) (2023-01-17)\n\n### Bug Fixes\n\n- **android:** get application/x-www-form-urlencoded as string ([#6165](https://github.com/ionic-team/capacitor/issues/6165)) ([0735e89](https://github.com/ionic-team/capacitor/commit/0735e89d48e77a1ddca97a48e3851f4a0a3ea2c1))\n- **ios/android:** better http error handling ([#6208](https://github.com/ionic-team/capacitor/issues/6208)) ([7d4d70a](https://github.com/ionic-team/capacitor/commit/7d4d70a0500b7996c710c0762907f44bdf27c92b))\n\n## [4.6.1](https://github.com/ionic-team/capacitor/compare/4.6.0...4.6.1) (2022-12-05)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [4.6.0](https://github.com/ionic-team/capacitor/compare/4.5.0...4.6.0) (2022-12-01)\n\n### Bug Fixes\n\n- **android:** Don't run Cordova plugins on ui thread ([#6108](https://github.com/ionic-team/capacitor/issues/6108)) ([592ee86](https://github.com/ionic-team/capacitor/commit/592ee862a58f5cb0737620a0246fe8ae295d27cf))\n- **cookies:** Use Set-Cookie headers to persist cookies ([57f8b39](https://github.com/ionic-team/capacitor/commit/57f8b39d7f4c5ee0e5e5cb316913e9450a81d22b))\n\n### Features\n\n- **android:** Plugin Instance Support ([#6073](https://github.com/ionic-team/capacitor/issues/6073)) ([3d5b7c2](https://github.com/ionic-team/capacitor/commit/3d5b7c2d372cf764c625f46d1e8761e05b8959da))\n\n# [4.5.0](https://github.com/ionic-team/capacitor/compare/4.4.0...4.5.0) (2022-11-16)\n\n### Bug Fixes\n\n- **android:** Silence deprecation warning on handlePermissionResult ([#6092](https://github.com/ionic-team/capacitor/issues/6092)) ([888b13e](https://github.com/ionic-team/capacitor/commit/888b13e89c48dab949b38135a3ec443ac4fd852e))\n\n### Features\n\n- **android/cli:** Allow to use the old addJavascriptInterface bridge ([#6043](https://github.com/ionic-team/capacitor/issues/6043)) ([a6e7c54](https://github.com/ionic-team/capacitor/commit/a6e7c5422687b703492a5fcc49369eacc376143d))\n- **cookies:** add get cookies plugin method ([ba1e770](https://github.com/ionic-team/capacitor/commit/ba1e7702a3338714aee24388c0afea39706c9341))\n\n# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21)\n\n### Bug Fixes\n\n- **android:** added ServerPath object and building options for setting initial load from portals ([#6008](https://github.com/ionic-team/capacitor/issues/6008)) ([205b6e6](https://github.com/ionic-team/capacitor/commit/205b6e61806158244846608b1e6c0c7b26ee4ab7))\n- **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc))\n- **http:** fix exception thrown on 204 responses ([1f6e8be](https://github.com/ionic-team/capacitor/commit/1f6e8be9d8813c4397e2c54ac4c06beb55f97b5f))\n- **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f))\n\n# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21)\n\n### Bug Fixes\n\n- **android:** open external links in browser ([#5913](https://github.com/ionic-team/capacitor/issues/5913)) ([7553ede](https://github.com/ionic-team/capacitor/commit/7553ede93170971e21ab3dec1798443d084ead2a))\n- **android:** set all cookies on proxied requests ([#5781](https://github.com/ionic-team/capacitor/issues/5781)) ([5ef6a38](https://github.com/ionic-team/capacitor/commit/5ef6a3889121dd39a9159ff80250df18854bc557))\n- **android:** set WebViewClient on the WebView ([#5919](https://github.com/ionic-team/capacitor/issues/5919)) ([020ed8e](https://github.com/ionic-team/capacitor/commit/020ed8eaeb7864399d4b93f54ab7601c607d8e0d))\n- **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e))\n\n### Features\n\n- Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45))\n\n# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27)\n\n### Bug Fixes\n\n- **android:** Publish proguard-rules.pro on npm ([#5761](https://github.com/ionic-team/capacitor/issues/5761)) ([df77103](https://github.com/ionic-team/capacitor/commit/df77103ca411fa452239099769289eeeea2404d2))\n\n### Features\n\n- **android:** Add android.minWebviewVersion configuration option ([#5768](https://github.com/ionic-team/capacitor/issues/5768)) ([ad83827](https://github.com/ionic-team/capacitor/commit/ad838279e9cd190ce6f1a020a0ac9e3916786324))\n- **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625))\n- **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264))\n- Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3))\n\n# [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [4.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.0...4.0.0-beta.1) (2022-06-27)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [4.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.6.0...4.0.0-beta.0) (2022-06-17)\n\n### Bug Fixes\n\n- **android:** make removeAllListeners return a promise ([#5527](https://github.com/ionic-team/capacitor/issues/5527)) ([6f4d858](https://github.com/ionic-team/capacitor/commit/6f4d858ea879d97109c0c7da2d664d04806adc2a))\n- **android:** prevent app from loading if server.url is invalid ([d4a0dea](https://github.com/ionic-team/capacitor/commit/d4a0deaa37eda4476f0be030e266c2c1260fc6e8))\n\n### Features\n\n- **android:** don't allow server.androidScheme to be set to schemes handled by WebView ([01285ba](https://github.com/ionic-team/capacitor/commit/01285ba253d602b08a41240ad2ccf370730d51a3))\n- **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3))\n- **android:** set default targetSDK to 32 ([#5611](https://github.com/ionic-team/capacitor/issues/5611)) ([416b966](https://github.com/ionic-team/capacitor/commit/416b9662fbf6233d23216c0c0441862603c3a723))\n- **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad))\n- **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672))\n\n# [3.6.0](https://github.com/ionic-team/capacitor/compare/3.5.1...3.6.0) (2022-06-17)\n\n### Features\n\n- **android:** update support for Portals for Capacitor to include Live Updates ([#5660](https://github.com/ionic-team/capacitor/issues/5660)) ([62f0a5e](https://github.com/ionic-team/capacitor/commit/62f0a5eaa40776aad79dbf8f8c0900037d3cc97e))\n- **iOS, Android:** add AppUUID Lib for plugins ([#5690](https://github.com/ionic-team/capacitor/issues/5690)) ([05e76cf](https://github.com/ionic-team/capacitor/commit/05e76cf526a44e07fa75f9482fa2223a13918638))\n\n# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12)\n\n### Bug Fixes\n\n- **android:** make removeAllListeners return a promise ([#5527](https://github.com/ionic-team/capacitor/issues/5527)) ([6f4d858](https://github.com/ionic-team/capacitor/commit/6f4d858ea879d97109c0c7da2d664d04806adc2a))\n- **android:** prevent app from loading if server.url is invalid ([d4a0dea](https://github.com/ionic-team/capacitor/commit/d4a0deaa37eda4476f0be030e266c2c1260fc6e8))\n\n### Features\n\n- **android:** don't allow server.androidScheme to be set to schemes handled by WebView ([01285ba](https://github.com/ionic-team/capacitor/commit/01285ba253d602b08a41240ad2ccf370730d51a3))\n- **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3))\n- **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad))\n- **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672))\n\n## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04)\n\n### Bug Fixes\n\n- **android:** move initialFocus on webview into config ([#5579](https://github.com/ionic-team/capacitor/issues/5579)) ([8b4e861](https://github.com/ionic-team/capacitor/commit/8b4e861514b0fbe08e9296f49c280234f54742e1))\n\n# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22)\n\n### Features\n\n- **android:** Add overridable routing for WebViewLocalServer ([#5553](https://github.com/ionic-team/capacitor/issues/5553)) ([3bb288e](https://github.com/ionic-team/capacitor/commit/3bb288e848c5c0e49c1e58c0782e0b1ffd7b1f31))\n\n# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25)\n\n### Features\n\n- **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3))\n- **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad))\n\n## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.4.0](https://github.com/ionic-team/capacitor/compare/3.3.4...3.4.0) (2022-01-19)\n\n### Bug Fixes\n\n- **android:** prevent input file crash if accept has . ([#5363](https://github.com/ionic-team/capacitor/issues/5363)) ([bdacb30](https://github.com/ionic-team/capacitor/commit/bdacb300bb6391dc4b84bb2bab075df993a15cba))\n\n### Features\n\n- **android:** Add getLong helper on PluginCall ([#5235](https://github.com/ionic-team/capacitor/issues/5235)) ([26261fb](https://github.com/ionic-team/capacitor/commit/26261fb49211330c4db72c259359565da7d7bc4b))\n\n## [3.3.4](https://github.com/ionic-team/capacitor/compare/3.3.3...3.3.4) (2022-01-05)\n\n### Bug Fixes\n\n- **android:** Prevent crash if activity killed on input file ([#5328](https://github.com/ionic-team/capacitor/issues/5328)) ([a206841](https://github.com/ionic-team/capacitor/commit/a20684180a9b6fd50547ae578f21531faa116da5))\n\n## [3.3.3](https://github.com/ionic-team/capacitor/compare/3.3.2...3.3.3) (2021-12-08)\n\n### Bug Fixes\n\n- **android:** Prevent crash in restoreInstanceState if bundleData is null ([#5289](https://github.com/ionic-team/capacitor/issues/5289)) ([622d62f](https://github.com/ionic-team/capacitor/commit/622d62fc0d7cd79558bf6f11331bd7d6690aa4f9))\n\n## [3.3.2](https://github.com/ionic-team/capacitor/compare/3.3.1...3.3.2) (2021-11-17)\n\n### Bug Fixes\n\n- **android:** Allow web geolocation if only COARSE_LOCATION is granted ([#5236](https://github.com/ionic-team/capacitor/issues/5236)) ([bc7b24e](https://github.com/ionic-team/capacitor/commit/bc7b24e9b58b194b32b750c5816c8d8ef180834a))\n\n## [3.3.1](https://github.com/ionic-team/capacitor/compare/3.3.0...3.3.1) (2021-11-05)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.3.0](https://github.com/ionic-team/capacitor/compare/3.2.5...3.3.0) (2021-11-03)\n\n### Bug Fixes\n\n- **core:** avoid crash on logging circular objects ([#5186](https://github.com/ionic-team/capacitor/issues/5186)) ([1451ec8](https://github.com/ionic-team/capacitor/commit/1451ec850a9ef73267a032638e73f1fc440647b9))\n\n### Features\n\n- **android:** ability to reload the webview ([#5184](https://github.com/ionic-team/capacitor/issues/5184)) ([c495bed](https://github.com/ionic-team/capacitor/commit/c495bed216ddf05450f185d2d3f09b4052b281a8))\n\n## [3.2.5](https://github.com/ionic-team/capacitor/compare/3.2.4...3.2.5) (2021-10-13)\n\n### Bug Fixes\n\n- **android:** Avoid ConcurrentModificationException on notifyListeners ([#5125](https://github.com/ionic-team/capacitor/issues/5125)) ([b82bfe0](https://github.com/ionic-team/capacitor/commit/b82bfe0db2e38fa286eb18391b1d5e2f86a1b35c))\n- **android:** Support cordova-android 10 ([#5103](https://github.com/ionic-team/capacitor/issues/5103)) ([e238233](https://github.com/ionic-team/capacitor/commit/e238233dcf34a183af4861176789d1feb1eb51fa))\n\n## [3.2.4](https://github.com/ionic-team/capacitor/compare/3.2.3...3.2.4) (2021-09-27)\n\n### Bug Fixes\n\n- **ios:** show correct line number on console logs ([#5073](https://github.com/ionic-team/capacitor/issues/5073)) ([ec41e74](https://github.com/ionic-team/capacitor/commit/ec41e743aa4ba81e791ad446fac461b7f43b46ed))\n\n## [3.2.3](https://github.com/ionic-team/capacitor/compare/3.2.2...3.2.3) (2021-09-15)\n\n### Bug Fixes\n\n- **android:** proguard rules ([#5048](https://github.com/ionic-team/capacitor/issues/5048)) ([cf15c0f](https://github.com/ionic-team/capacitor/commit/cf15c0fb3bd67315011865fedb4157d5076965fd))\n- **android:** save activity result launcher calls ([#5004](https://github.com/ionic-team/capacitor/issues/5004)) ([2c1eb60](https://github.com/ionic-team/capacitor/commit/2c1eb603c79b94f6fcc74f0cbef523590b656a1e))\n\n## [3.2.2](https://github.com/ionic-team/capacitor/compare/3.2.1...3.2.2) (2021-09-02)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [3.2.1](https://github.com/ionic-team/capacitor/compare/3.2.0...3.2.1) (2021-09-01)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.2.0](https://github.com/ionic-team/capacitor/compare/3.1.2...3.2.0) (2021-08-18)\n\n### Bug Fixes\n\n- **android:** Don't inject map files into capacitor script ([#4893](https://github.com/ionic-team/capacitor/issues/4893)) ([992bebc](https://github.com/ionic-team/capacitor/commit/992bebce5a54128ec09b4905c4424fbe392719be))\n\n## [3.1.2](https://github.com/ionic-team/capacitor/compare/3.1.1...3.1.2) (2021-07-21)\n\n### Bug Fixes\n\n- **android:** add missing android webview lifecycle events ([6a7c4e3](https://github.com/ionic-team/capacitor/commit/6a7c4e3b3a250270ac5c4b0f09da2a613ef2cf17))\n- **android:** Set theme an content view on onCreate ([#4841](https://github.com/ionic-team/capacitor/issues/4841)) ([8950c60](https://github.com/ionic-team/capacitor/commit/8950c600bb6e3804b79c62e83fef2253c2cc2389))\n\n## [3.1.1](https://github.com/ionic-team/capacitor/compare/3.1.0...3.1.1) (2021-07-07)\n\n### Bug Fixes\n\n- fixing peer deps issues in android and ios libs ([310d9f4](https://github.com/ionic-team/capacitor/commit/310d9f486db976cb258fcda5ac893f019667617f))\n\n# [3.1.0](https://github.com/ionic-team/capacitor/compare/3.0.2...3.1.0) (2021-07-07)\n\n**Note:** Version bump only for package @capacitor/android\n\n## [3.0.2](https://github.com/ionic-team/capacitor/compare/3.0.1...3.0.2) (2021-06-23)\n\n### Bug Fixes\n\n- **android:** Set WEBVIEW_SERVER_URL before injecting native-bridge ([#4748](https://github.com/ionic-team/capacitor/issues/4748)) ([5d6b179](https://github.com/ionic-team/capacitor/commit/5d6b17994abc7ad770b95e3a9fc29aecf5d9fc05))\n- **core:** cordova events not firing ([#4712](https://github.com/ionic-team/capacitor/issues/4712)) ([ca4e3b6](https://github.com/ionic-team/capacitor/commit/ca4e3b62dba6a40e593a1404ba2fe2b416a4ac14))\n\n## [3.0.1](https://github.com/ionic-team/capacitor/compare/3.0.0...3.0.1) (2021-06-09)\n\n### Bug Fixes\n\n- **android:** Avoid crash on input file ([#4707](https://github.com/ionic-team/capacitor/issues/4707)) ([883c0fe](https://github.com/ionic-team/capacitor/commit/883c0fe4a8a33d2e14894d9b307f4d7ce6d13bad))\n- **android:** Make proxy handle user info in server url ([#4699](https://github.com/ionic-team/capacitor/issues/4699)) ([baeed45](https://github.com/ionic-team/capacitor/commit/baeed45038134d446aef7747e5ad5ce4ac07c438))\n- **android:** Reset bridge on onPageStarted only ([#4634](https://github.com/ionic-team/capacitor/issues/4634)) ([96e4830](https://github.com/ionic-team/capacitor/commit/96e483046c9128dbcaec21efb0f5d619c6b1185f))\n- Make isPluginAvailable available on bridge ([#4589](https://github.com/ionic-team/capacitor/issues/4589)) ([151e7a8](https://github.com/ionic-team/capacitor/commit/151e7a899d9646dbd5625a2539fd3f2297349bc5))\n\n# [3.0.0](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.4...3.0.0) (2021-05-18)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-rc.4](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.3...3.0.0-rc.4) (2021-05-18)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.2...3.0.0-rc.3) (2021-05-11)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.1...3.0.0-rc.2) (2021-05-07)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.0...3.0.0-rc.1) (2021-04-29)\n\n### Bug Fixes\n\n- generate Capacitor.Plugins object ([#4496](https://github.com/ionic-team/capacitor/issues/4496)) ([1c71b7a](https://github.com/ionic-team/capacitor/commit/1c71b7adb2c325e34d980dbf578dc22afb2c332b))\n- **android:** Release the call after reject/resolve ([#4318](https://github.com/ionic-team/capacitor/issues/4318)) ([a9f30a8](https://github.com/ionic-team/capacitor/commit/a9f30a88bf3cf239a59c4e901e2a9a2a141a9044))\n- **android:** resolve issue with activity result API registration for fragments ([#4402](https://github.com/ionic-team/capacitor/issues/4402)) ([ac6c6bc](https://github.com/ionic-team/capacitor/commit/ac6c6bc031e0c8236004dfb9e1b04f1f849c1519))\n\n### Features\n\n- Unify logging behavior across environments ([#4416](https://github.com/ionic-team/capacitor/issues/4416)) ([bae0f3d](https://github.com/ionic-team/capacitor/commit/bae0f3d2cee84978636d0f589bc7e2f745671baf))\n- **android:** ability to add listeners to the Capacitor WebView ([#4405](https://github.com/ionic-team/capacitor/issues/4405)) ([7bdcc15](https://github.com/ionic-team/capacitor/commit/7bdcc15a20248fc17b5867b215bba0c43e29b2c0))\n\n# [3.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.6...3.0.0-rc.0) (2021-03-10)\n\n### Bug Fixes\n\n- **android:** calls re-saved during permission/activity result callbacks were being released ([502a2d1](https://github.com/ionic-team/capacitor/commit/502a2d18ddce870f4caf810b405f7363f2340067))\n- **android:** live reload not working when using adb reverse ([362f221](https://github.com/ionic-team/capacitor/commit/362f2219767a5f28e3ce1f6857a0e20024adc6b6))\n\n### Features\n\n- **android:** add configurable app path for embedded capacitor ([#4264](https://github.com/ionic-team/capacitor/issues/4264)) ([e433691](https://github.com/ionic-team/capacitor/commit/e43369144f7f378edc4f5d4f8dbbafe6cff6a70d))\n- **android:** Unifying saving plugin calls ([#4254](https://github.com/ionic-team/capacitor/issues/4254)) ([a648c51](https://github.com/ionic-team/capacitor/commit/a648c51588627404b5ad30c35943fed18af4a546))\n\n# [3.0.0-beta.6](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.5...3.0.0-beta.6) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-beta.5](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.4...3.0.0-beta.5) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-beta.4](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.3...3.0.0-beta.4) (2021-02-26)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.2...3.0.0-beta.3) (2021-02-18)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.1...3.0.0-beta.2) (2021-02-08)\n\n### Bug Fixes\n\n- address bug in `isPluginAvailable()` for web and native ([#4114](https://github.com/ionic-team/capacitor/issues/4114)) ([2fbd954](https://github.com/ionic-team/capacitor/commit/2fbd95465a321b8f4c50d4daf22a63d8043cee9b))\n- **android:** get PermissionState enum by state value ([#4100](https://github.com/ionic-team/capacitor/issues/4100)) ([194ae86](https://github.com/ionic-team/capacitor/commit/194ae8699944bf016132fb64fe48010679a6d64e))\n- **android:** requestPermission call rejects if permission missing in manifest ([55ef5ff](https://github.com/ionic-team/capacitor/commit/55ef5ff38e87729412c44bfa4b2f29e53044cecc))\n\n### Features\n\n- **android:** activity result use new API and update permission result callbacks to match ([#4127](https://github.com/ionic-team/capacitor/issues/4127)) ([002f1e5](https://github.com/ionic-team/capacitor/commit/002f1e55173a50b9fe918b4eda73b5113b713282))\n- **android:** androidxActivityVersion & androidxFragmentVersion gradle variables ([#4103](https://github.com/ionic-team/capacitor/issues/4103)) ([4f77b96](https://github.com/ionic-team/capacitor/commit/4f77b962be85fc6bfc555a106c5b3e6707526626))\n\n# [3.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.0...3.0.0-beta.1) (2021-01-14)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.14...3.0.0-beta.0) (2021-01-13)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-alpha.14](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.13...3.0.0-alpha.14) (2021-01-13)\n\n### Bug Fixes\n\n- **android:** append missing new lines on injected cordova files ([#4058](https://github.com/ionic-team/capacitor/issues/4058)) ([dbdc78d](https://github.com/ionic-team/capacitor/commit/dbdc78dc08e016dfbc2454d4f53a49f16f744b3e))\n\n### Features\n\n- **android:** method to check permission for an alias ([#4062](https://github.com/ionic-team/capacitor/issues/4062)) ([c88c4b4](https://github.com/ionic-team/capacitor/commit/c88c4b46b949a87c1b89476b75273adef725242b))\n\n# [3.0.0-alpha.12](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.11...3.0.0-alpha.12) (2021-01-08)\n\n### Features\n\n- **android:** switch to new callback-style permission requests ([#4033](https://github.com/ionic-team/capacitor/issues/4033)) ([cc459de](https://github.com/ionic-team/capacitor/commit/cc459de7fc070c0227e066f3e8b92062728ab45d))\n\n# [3.0.0-alpha.11](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.10...3.0.0-alpha.11) (2020-12-26)\n\n### Features\n\n- **android:** expose CapConfig.loadDefault(), deprecate v2 constructor ([#3964](https://github.com/ionic-team/capacitor/issues/3964)) ([94ae977](https://github.com/ionic-team/capacitor/commit/94ae9774d2467fa7ba0336e7183f6d28cae45908))\n\n# [3.0.0-alpha.10](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.9...3.0.0-alpha.10) (2020-12-15)\n\n### Bug Fixes\n\n- **android:** include lint.xml for downstream lint tasks ([efa72f3](https://github.com/ionic-team/capacitor/commit/efa72f38c5f64d3b91cc4c4c7d4d87ab38219893))\n\n# [3.0.0-alpha.9](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.8...3.0.0-alpha.9) (2020-12-15)\n\n### Bug Fixes\n\n- **android:** include lint-baseline.xml for downstream lint tasks ([20ccaa0](https://github.com/ionic-team/capacitor/commit/20ccaa0311dcf8468019325ad976156d92ed0202))\n\n# [3.0.0-alpha.8](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.7...3.0.0-alpha.8) (2020-12-15)\n\n### Bug Fixes\n\n- **Android:** Use plugin's getPermissionStates() to support overriding ([#3939](https://github.com/ionic-team/capacitor/issues/3939)) ([855a607](https://github.com/ionic-team/capacitor/commit/855a60711bcf6cff3215a36fac7e5314a2c4d159))\n\n### Features\n\n- **android:** add onConfigurationChanged() activity lifecycle hook ([#3936](https://github.com/ionic-team/capacitor/issues/3936)) ([29e9e2c](https://github.com/ionic-team/capacitor/commit/29e9e2c5c30f23eb3ea2e88b1427eed0636e8125))\n- **android:** Add WebColor utility for parsing color ([#3947](https://github.com/ionic-team/capacitor/issues/3947)) ([3746404](https://github.com/ionic-team/capacitor/commit/3746404240459ca9ea8175f2bb241d80746e8328))\n- **Android:** Refactoring configuration ([#3778](https://github.com/ionic-team/capacitor/issues/3778)) ([9820a30](https://github.com/ionic-team/capacitor/commit/9820a30688f0a774eced1676f1927cacde53301f))\n\n# [3.0.0-alpha.7](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.6...3.0.0-alpha.7) (2020-12-02)\n\n### Bug Fixes\n\n- **android:** dont release calls that are manually saved, eg listeners/watchers ([#3857](https://github.com/ionic-team/capacitor/issues/3857)) ([f1c8fe9](https://github.com/ionic-team/capacitor/commit/f1c8fe9e039d25eff2122fe915f17e84477427eb))\n- **android:** fixed breaking change to `handleOnActivityResult` ([#3888](https://github.com/ionic-team/capacitor/issues/3888)) ([5fd60e6](https://github.com/ionic-team/capacitor/commit/5fd60e607b79b46cec08c6af1674305b1199d0a4))\n- **android:** resolve undefined for both checkPermissions and requestPermissions by default ([#3855](https://github.com/ionic-team/capacitor/issues/3855)) ([383f62b](https://github.com/ionic-team/capacitor/commit/383f62b2b6531c579aac469e29b7c1c0c1f7540f))\n\n### Features\n\n- automatically import Android plugins ([#3788](https://github.com/ionic-team/capacitor/issues/3788)) ([aa1e1c6](https://github.com/ionic-team/capacitor/commit/aa1e1c604e260cc8babb0e7f5230f692bdcf6f09))\n- **android:** Add handlePermissions function for plugins to call ([#3768](https://github.com/ionic-team/capacitor/issues/3768)) ([3a7e282](https://github.com/ionic-team/capacitor/commit/3a7e282a7515784dd343bbf1e3d52e0299bac887))\n- **android:** modified plugin annotation format for multi-permissions and empty (auto-grant) ([#3822](https://github.com/ionic-team/capacitor/issues/3822)) ([1b5a3bd](https://github.com/ionic-team/capacitor/commit/1b5a3bdeb1b35612cf04e58bdf2fca68a0832a14))\n\n# [3.0.0-alpha.6](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.5...3.0.0-alpha.6) (2020-10-30)\n\n### Bug Fixes\n\n- **android:** avoid crash on input file capture ([#3715](https://github.com/ionic-team/capacitor/issues/3715)) ([f502a99](https://github.com/ionic-team/capacitor/commit/f502a9964e28012980d636014043e86e918031d7))\n\n### Features\n\n- improve permissions ([eec61a6](https://github.com/ionic-team/capacitor/commit/eec61a6d8d8edfe94aea1a361787d1e6c736e20d))\n- unified errors and error codes ([#3673](https://github.com/ionic-team/capacitor/issues/3673)) ([f9e0803](https://github.com/ionic-team/capacitor/commit/f9e08038aa88f7453e8235f380d2767a12a7a073))\n\n# [3.0.0-alpha.5](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.4...3.0.0-alpha.5) (2020-10-06)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.3...3.0.0-alpha.4) (2020-09-23)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.2...3.0.0-alpha.3) (2020-09-15)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.1...3.0.0-alpha.2) (2020-08-31)\n\n**Note:** Version bump only for package @capacitor/android\n\n# [3.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/2.4.0...3.0.0-alpha.1) (2020-08-21)\n\n# [3.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/2.3.0...3.0.0-alpha.0) (2020-07-23)\n\n### Features\n\n- **android:** add custom plugins to BridgeFragment ([#3280](https://github.com/ionic-team/capacitor/issues/3280)) ([d131a5f](https://github.com/ionic-team/capacitor/commit/d131a5fed2b9ae29b6952397ec2f81104545b749))\n"
  },
  {
    "path": "android/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017-present Drifty Co.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "android/capacitor/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "android/capacitor/build.gradle",
    "content": "ext {\n    androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.11.0'\n    androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'\n    androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.3.0'\n    androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.17.0'\n    androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.8.9'\n    androidxWebkitVersion = project.hasProperty('androidxWebkitVersion') ? rootProject.ext.androidxWebkitVersion : '1.14.0'\n    junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'\n    androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'\n    androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'\n    cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '14.0.1'\n}\n\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            url = \"https://plugins.gradle.org/m2/\"\n        }\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.13.0'\n\n        if (System.getenv(\"CAP_PUBLISH\") == \"true\") {\n            classpath 'io.github.gradle-nexus:publish-plugin:1.3.0'\n        }\n    }\n}\n\ntasks.withType(Javadoc).all { enabled = false }\n\napply plugin: 'com.android.library'\n\nif (System.getenv(\"CAP_PUBLISH\") == \"true\") {\n    apply plugin: 'io.github.gradle-nexus.publish-plugin'\n    apply from: file('../scripts/publish-root.gradle')\n    apply from: file('../scripts/publish-module.gradle')\n}\n\nandroid {\n    namespace = \"com.getcapacitor.android\"\n    compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36\n    defaultConfig {\n        minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24\n        targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36\n        versionCode 1\n        versionName \"1.0\"\n        consumerProguardFiles 'proguard-rules.pro'\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        baseline file(\"lint-baseline.xml\")\n        abortOnError = true\n        warningsAsErrors = true\n        lintConfig = file('lint.xml')\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_21\n        targetCompatibility JavaVersion.VERSION_21\n    }\n    publishing {\n        singleVariant(\"release\")\n    }\n}\n\nrepositories {\n    google()\n    mavenCentral()\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"androidx.appcompat:appcompat:$androidxAppCompatVersion\"\n    implementation \"androidx.core:core:$androidxCoreVersion\"\n    implementation \"androidx.activity:activity:$androidxActivityVersion\"\n    implementation \"androidx.fragment:fragment:$androidxFragmentVersion\"\n    implementation \"androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion\"\n    implementation \"androidx.webkit:webkit:$androidxWebkitVersion\"\n    testImplementation \"junit:junit:$junitVersion\"\n    androidTestImplementation \"androidx.test.ext:junit:$androidxJunitVersion\"\n    androidTestImplementation \"androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion\"\n    implementation \"org.apache.cordova:framework:$cordovaAndroidVersion\"\n    testImplementation 'org.json:json:20250517'\n    testImplementation 'org.mockito:mockito-core:5.20.0'\n}\n\n"
  },
  {
    "path": "android/capacitor/gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n"
  },
  {
    "path": "android/capacitor/lint-baseline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<issues format=\"5\" by=\"lint 4.1.1\" client=\"gradle\" variant=\"all\" version=\"4.1.1\">\n\n    <issue\n        id=\"DefaultLocale\"\n        message=\"Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead\"\n        errorLine1=\"            String msg = String.format(\"\n        errorLine2=\"                         ^\">\n        <location\n            file=\"src/main/java/com/getcapacitor/BridgeWebChromeClient.java\"\n            line=\"474\"\n            column=\"26\"/>\n    </issue>\n\n    <issue\n        id=\"DefaultLocale\"\n        message=\"Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`.\"\n        errorLine1=\"                return mask.toUpperCase().equals(string.toUpperCase());\"\n        errorLine2=\"                            ~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/util/HostMask.java\"\n            line=\"110\"\n            column=\"29\"/>\n    </issue>\n\n    <issue\n        id=\"DefaultLocale\"\n        message=\"Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`.\"\n        errorLine1=\"                return mask.toUpperCase().equals(string.toUpperCase());\"\n        errorLine2=\"                                                        ~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/util/HostMask.java\"\n            line=\"110\"\n            column=\"57\"/>\n    </issue>\n\n    <issue\n        id=\"DefaultLocale\"\n        message=\"Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`.\"\n        errorLine1=\"                switch (spinnerStyle.toLowerCase()) {\"\n        errorLine2=\"                                     ~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/Splash.java\"\n            line=\"127\"\n            column=\"38\"/>\n    </issue>\n\n    <issue\n        id=\"DefaultLocale\"\n        message=\"Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`.\"\n        errorLine1=\"                    if (header.getKey().equalsIgnoreCase(&quot;Accept&quot;) &amp;&amp; header.getValue().toLowerCase().contains(&quot;text/html&quot;)) {\"\n        errorLine2=\"                                                                                        ~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/WebViewLocalServer.java\"\n            line=\"327\"\n            column=\"89\"/>\n    </issue>\n\n    <issue\n        id=\"SimpleDateFormat\"\n        message=\"To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, or `getTimeInstance()`, or use `new SimpleDateFormat(String template, Locale locale)` with for example `Locale.US` for ASCII dates.\"\n        errorLine1=\"        String timeStamp = new SimpleDateFormat(&quot;yyyyMMdd_HHmmss&quot;).format(new Date());\"\n        errorLine2=\"                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/BridgeWebChromeClient.java\"\n            line=\"511\"\n            column=\"28\"/>\n    </issue>\n\n    <issue\n        id=\"SimpleDateFormat\"\n        message=\"To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, or `getTimeInstance()`, or use `new SimpleDateFormat(String template, Locale locale)` with for example `Locale.US` for ASCII dates.\"\n        errorLine1=\"        DateFormat df = new SimpleDateFormat(&quot;yyyy-MM-dd&apos;T&apos;HH:mm&apos;Z&apos;&quot;);\"\n        errorLine2=\"                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/PluginResult.java\"\n            line=\"44\"\n            column=\"25\"/>\n    </issue>\n\n    <issue\n        id=\"SetJavaScriptEnabled\"\n        message=\"Using `setJavaScriptEnabled` can introduce XSS vulnerabilities into your application, review carefully\"\n        errorLine1=\"        settings.setJavaScriptEnabled(true);\"\n        errorLine2=\"        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/Bridge.java\"\n            line=\"384\"\n            column=\"9\"/>\n    </issue>\n\n    <issue\n        id=\"Recycle\"\n        message=\"This `TypedArray` should be recycled after use with `#recycle()`\"\n        errorLine1=\"        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.bridge_fragment);\"\n        errorLine2=\"                               ~~~~~~~~~~~~~~~~~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/BridgeFragment.java\"\n            line=\"84\"\n            column=\"32\"/>\n    </issue>\n\n    <issue\n        id=\"StaticFieldLeak\"\n        message=\"Do not place Android context classes in static fields; this is a memory leak\"\n        errorLine1=\"    private static ImageView splashImage;\"\n        errorLine2=\"            ~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/Splash.java\"\n            line=\"41\"\n            column=\"13\"/>\n    </issue>\n\n    <issue\n        id=\"StaticFieldLeak\"\n        message=\"Do not place Android context classes in static fields; this is a memory leak\"\n        errorLine1=\"    private static ProgressBar spinnerBar;\"\n        errorLine2=\"            ~~~~~~\">\n        <location\n            file=\"src/main/java/com/getcapacitor/Splash.java\"\n            line=\"42\"\n            column=\"13\"/>\n    </issue>\n\n    <issue\n        id=\"Overdraw\"\n        message=\"Possible overdraw: Root element paints background `#F0FF1414` with a theme that also paints a background (inferred theme is `@android:style/Theme.Holo`)\"\n        errorLine1=\"    android:background=&quot;#F0FF1414&quot;\"\n        errorLine2=\"    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\">\n        <location\n            file=\"src/main/res/layout/fragment_bridge.xml\"\n            line=\"5\"\n            column=\"5\"/>\n    </issue>\n\n</issues>\n"
  },
  {
    "path": "android/capacitor/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n    <issue id=\"GradleDependency\" severity=\"ignore\" />\n    <issue id=\"AndroidGradlePluginVersion\" severity=\"ignore\" />\n    <issue id=\"DiscouragedApi\">\n        <ignore path=\"src/main/java/com/getcapacitor/plugin/util/AssetUtil.java\" />\n    </issue>\n    <issue id=\"ObsoleteSdkInt\" severity=\"informational\" />\n</lint>\n"
  },
  {
    "path": "android/capacitor/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Rules for Capacitor v3 plugins and annotations\n -keep @com.getcapacitor.annotation.CapacitorPlugin public class * {\n     @com.getcapacitor.annotation.PermissionCallback <methods>;\n     @com.getcapacitor.annotation.ActivityCallback <methods>;\n     @com.getcapacitor.annotation.Permission <methods>;\n     @com.getcapacitor.PluginMethod public <methods>;\n }\n\n -keep public class * extends com.getcapacitor.Plugin { *; }\n\n# Rules for Capacitor v2 plugins and annotations\n# These are deprecated but can still be used with Capacitor for now\n-keep @com.getcapacitor.NativePlugin public class * {\n  @com.getcapacitor.PluginMethod public <methods>;\n}\n\n# Rules for Cordova plugins\n-keep public class * extends org.apache.cordova.* {\n  public <methods>;\n  public <fields>;\n}"
  },
  {
    "path": "android/capacitor/settings.gradle",
    "content": ""
  },
  {
    "path": "android/capacitor/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java",
    "content": "package com.getcapacitor.android;\n\nimport static org.junit.Assert.*;\n\nimport android.content.Context;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();\n\n        assertEquals(\"com.getcapacitor.android.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n</manifest>"
  },
  {
    "path": "android/capacitor/src/main/assets/native-bridge.js",
    "content": "\n/*! Capacitor: https://capacitorjs.com/ - MIT License */\n/* Generated File. Do not edit. */\n\nvar nativeBridge = (function (exports) {\n    'use strict';\n\n    var ExceptionCode;\n    (function (ExceptionCode) {\n        /**\n         * API is not implemented.\n         *\n         * This usually means the API can't be used because it is not implemented for\n         * the current platform.\n         */\n        ExceptionCode[\"Unimplemented\"] = \"UNIMPLEMENTED\";\n        /**\n         * API is not available.\n         *\n         * This means the API can't be used right now because:\n         *   - it is currently missing a prerequisite, such as network connectivity\n         *   - it requires a particular platform or browser version\n         */\n        ExceptionCode[\"Unavailable\"] = \"UNAVAILABLE\";\n    })(ExceptionCode || (ExceptionCode = {}));\n    class CapacitorException extends Error {\n        constructor(message, code, data) {\n            super(message);\n            this.message = message;\n            this.code = code;\n            this.data = data;\n        }\n    }\n\n    // For removing exports for iOS/Android, keep let for reassignment\n    // eslint-disable-next-line\n    let dummy = {};\n    const readFileAsBase64 = (file) => new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.onloadend = () => {\n            const data = reader.result;\n            resolve(btoa(data));\n        };\n        reader.onerror = reject;\n        reader.readAsBinaryString(file);\n    });\n    const convertFormData = async (formData) => {\n        const newFormData = [];\n        for (const pair of formData.entries()) {\n            const [key, value] = pair;\n            if (value instanceof File) {\n                const base64File = await readFileAsBase64(value);\n                newFormData.push({\n                    key,\n                    value: base64File,\n                    type: 'base64File',\n                    contentType: value.type,\n                    fileName: value.name,\n                });\n            }\n            else {\n                newFormData.push({ key, value, type: 'string' });\n            }\n        }\n        return newFormData;\n    };\n    const convertBody = async (body, contentType) => {\n        if (body instanceof ReadableStream || body instanceof Uint8Array) {\n            let encodedData;\n            if (body instanceof ReadableStream) {\n                const reader = body.getReader();\n                const chunks = [];\n                while (true) {\n                    const { done, value } = await reader.read();\n                    if (done)\n                        break;\n                    chunks.push(value);\n                }\n                const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));\n                let position = 0;\n                for (const chunk of chunks) {\n                    concatenated.set(chunk, position);\n                    position += chunk.length;\n                }\n                encodedData = concatenated;\n            }\n            else {\n                encodedData = body;\n            }\n            let data = new TextDecoder().decode(encodedData);\n            let type;\n            if (contentType === 'application/json') {\n                try {\n                    data = JSON.parse(data);\n                }\n                catch (ignored) {\n                    // ignore\n                }\n                type = 'json';\n            }\n            else if (contentType === 'multipart/form-data') {\n                type = 'formData';\n            }\n            else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) {\n                type = 'image';\n            }\n            else if (contentType === 'application/octet-stream') {\n                type = 'binary';\n            }\n            else {\n                type = 'text';\n            }\n            return {\n                data,\n                type,\n                headers: { 'Content-Type': contentType || 'application/octet-stream' },\n            };\n        }\n        else if (body instanceof URLSearchParams) {\n            return {\n                data: body.toString(),\n                type: 'text',\n            };\n        }\n        else if (body instanceof FormData) {\n            return {\n                data: await convertFormData(body),\n                type: 'formData',\n            };\n        }\n        else if (body instanceof File) {\n            const fileData = await readFileAsBase64(body);\n            return {\n                data: fileData,\n                type: 'file',\n                headers: { 'Content-Type': body.type },\n            };\n        }\n        return { data: body, type: 'json' };\n    };\n    const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_';\n    const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u';\n    // TODO: export as Cap function\n    const isRelativeOrProxyUrl = (url) => !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1;\n    // TODO: export as Cap function\n    const createProxyUrl = (url, win) => {\n        var _a, _b;\n        if (isRelativeOrProxyUrl(url))\n            return url;\n        const bridgeUrl = new URL((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 ? _b : '');\n        bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR;\n        bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url);\n        return bridgeUrl.toString();\n    };\n    const initBridge = (w) => {\n        const getPlatformId = (win) => {\n            var _a, _b;\n            if (win === null || win === void 0 ? void 0 : win.androidBridge) {\n                return 'android';\n            }\n            else if ((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) {\n                return 'ios';\n            }\n            else {\n                return 'web';\n            }\n        };\n        const convertFileSrcServerUrl = (webviewServerUrl, filePath) => {\n            if (typeof filePath === 'string') {\n                if (filePath.startsWith('/')) {\n                    return webviewServerUrl + '/_capacitor_file_' + filePath;\n                }\n                else if (filePath.startsWith('file://')) {\n                    return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_');\n                }\n                else if (filePath.startsWith('content://')) {\n                    return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_');\n                }\n            }\n            return filePath;\n        };\n        const initEvents = (win, cap) => {\n            cap.addListener = (pluginName, eventName, callback) => {\n                const callbackId = cap.nativeCallback(pluginName, 'addListener', {\n                    eventName: eventName,\n                }, callback);\n                return {\n                    remove: async () => {\n                        var _a;\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.debug('Removing listener', pluginName, eventName);\n                        cap.removeListener(pluginName, callbackId, eventName, callback);\n                    },\n                };\n            };\n            cap.removeListener = (pluginName, callbackId, eventName, callback) => {\n                cap.nativeCallback(pluginName, 'removeListener', {\n                    callbackId: callbackId,\n                    eventName: eventName,\n                }, callback);\n            };\n            cap.createEvent = (eventName, eventData) => {\n                const doc = win.document;\n                if (doc) {\n                    const ev = doc.createEvent('Events');\n                    ev.initEvent(eventName, false, false);\n                    if (eventData && typeof eventData === 'object') {\n                        for (const i in eventData) {\n                            // eslint-disable-next-line no-prototype-builtins\n                            if (eventData.hasOwnProperty(i)) {\n                                ev[i] = eventData[i];\n                            }\n                        }\n                    }\n                    return ev;\n                }\n                return null;\n            };\n            cap.triggerEvent = (eventName, target, eventData) => {\n                const doc = win.document;\n                const cordova = win.cordova;\n                eventData = eventData || {};\n                const ev = cap.createEvent(eventName, eventData);\n                if (ev) {\n                    if (target === 'document') {\n                        if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) {\n                            cordova.fireDocumentEvent(eventName, eventData);\n                            return true;\n                        }\n                        else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) {\n                            return doc.dispatchEvent(ev);\n                        }\n                    }\n                    else if (target === 'window' && win.dispatchEvent) {\n                        return win.dispatchEvent(ev);\n                    }\n                    else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) {\n                        const targetEl = doc.querySelector(target);\n                        if (targetEl) {\n                            return targetEl.dispatchEvent(ev);\n                        }\n                    }\n                }\n                return false;\n            };\n            win.Capacitor = cap;\n        };\n        const initLegacyHandlers = (win, cap) => {\n            // define cordova if it's not there already\n            win.cordova = win.cordova || {};\n            const doc = win.document;\n            const nav = win.navigator;\n            if (nav) {\n                nav.app = nav.app || {};\n                nav.app.exitApp = () => {\n                    var _a;\n                    if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) {\n                        win.console.warn('App plugin not installed');\n                    }\n                    else {\n                        cap.nativeCallback('App', 'exitApp', {});\n                    }\n                };\n            }\n            if (doc) {\n                const docAddEventListener = doc.addEventListener;\n                doc.addEventListener = (...args) => {\n                    var _a;\n                    const eventName = args[0];\n                    const handler = args[1];\n                    if (eventName === 'deviceready' && handler) {\n                        Promise.resolve().then(handler);\n                    }\n                    else if (eventName === 'backbutton' && cap.Plugins.App) {\n                        // Add a dummy listener so Capacitor doesn't do the default\n                        // back button action\n                        if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) {\n                            win.console.warn('App plugin not installed');\n                        }\n                        else {\n                            cap.Plugins.App.addListener('backButton', () => {\n                                // ignore\n                            });\n                        }\n                    }\n                    return docAddEventListener.apply(doc, args);\n                };\n            }\n            win.Capacitor = cap;\n        };\n        const initVendor = (win, cap) => {\n            const Ionic = (win.Ionic = win.Ionic || {});\n            const IonicWebView = (Ionic.WebView = Ionic.WebView || {});\n            const Plugins = cap.Plugins;\n            IonicWebView.getServerBasePath = (callback) => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.getServerBasePath().then((result) => {\n                    callback(result.path);\n                });\n            };\n            IonicWebView.setServerAssetPath = (path) => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerAssetPath({ path });\n            };\n            IonicWebView.setServerBasePath = (path) => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerBasePath({ path });\n            };\n            IonicWebView.persistServerBasePath = () => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.persistServerBasePath();\n            };\n            IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url);\n            win.Capacitor = cap;\n            win.Ionic.WebView = IonicWebView;\n        };\n        const initLogger = (win, cap) => {\n            const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn'];\n            const createLogFromNative = (c) => (result) => {\n                if (isFullConsole(c)) {\n                    const success = result.success === true;\n                    const tagStyles = success\n                        ? 'font-style: italic; font-weight: lighter; color: gray'\n                        : 'font-style: italic; font-weight: lighter; color: red';\n                    c.groupCollapsed('%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', tagStyles, 'font-style: italic; font-weight: bold; color: #444');\n                    if (result.success === false) {\n                        c.error(result.error);\n                    }\n                    else {\n                        c.dir(JSON.stringify(result.data));\n                    }\n                    c.groupEnd();\n                }\n                else {\n                    if (result.success === false) {\n                        c.error('LOG FROM NATIVE', result.error);\n                    }\n                    else {\n                        c.log('LOG FROM NATIVE', result.data);\n                    }\n                }\n            };\n            const createLogToNative = (c) => (call) => {\n                if (isFullConsole(c)) {\n                    c.groupCollapsed('%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', 'font-weight: lighter; color: gray', 'font-weight: bold; color: #000');\n                    c.dir(call);\n                    c.groupEnd();\n                }\n                else {\n                    c.log('LOG TO NATIVE: ', call);\n                }\n            };\n            const isFullConsole = (c) => {\n                if (!c) {\n                    return false;\n                }\n                return typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function';\n            };\n            const serializeConsoleMessage = (msg) => {\n                try {\n                    if (typeof msg === 'object') {\n                        msg = JSON.stringify(msg);\n                    }\n                    return String(msg);\n                }\n                catch (e) {\n                    return '';\n                }\n            };\n            const platform = getPlatformId(win);\n            if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') {\n                // add DOM ready listener for System Bars\n                document.addEventListener('DOMContentLoaded', function () {\n                    win.CapacitorSystemBarsAndroidInterface.onDOMReady();\n                });\n            }\n            if (platform == 'android' || platform == 'ios') {\n                // patch document.cookie on Android/iOS\n                win.CapacitorCookiesDescriptor =\n                    Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||\n                        Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');\n                let doPatchCookies = false;\n                // check if capacitor cookies is disabled before patching\n                if (platform === 'ios') {\n                    // Use prompt to synchronously get capacitor cookies config.\n                    // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                    const payload = {\n                        type: 'CapacitorCookies.isEnabled',\n                    };\n                    const isCookiesEnabled = prompt(JSON.stringify(payload));\n                    if (isCookiesEnabled === 'true') {\n                        doPatchCookies = true;\n                    }\n                }\n                else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n                    const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled();\n                    if (isCookiesEnabled === true) {\n                        doPatchCookies = true;\n                    }\n                }\n                if (doPatchCookies) {\n                    Object.defineProperty(document, 'cookie', {\n                        get: function () {\n                            var _a, _b, _c;\n                            if (platform === 'ios') {\n                                // Use prompt to synchronously get cookies.\n                                // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                                const payload = {\n                                    type: 'CapacitorCookies.get',\n                                };\n                                const res = prompt(JSON.stringify(payload));\n                                return res;\n                            }\n                            else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n                                // return original document.cookie since Android does not support filtering of `httpOnly` cookies\n                                return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : '';\n                            }\n                        },\n                        set: function (val) {\n                            const cookiePairs = val.split(';');\n                            const domainSection = val.toLowerCase().split('domain=')[1];\n                            const domain = cookiePairs.length > 1 && domainSection != null && domainSection.length > 0\n                                ? domainSection.split(';')[0].trim()\n                                : '';\n                            if (platform === 'ios') {\n                                // Use prompt to synchronously set cookies.\n                                // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                                const payload = {\n                                    type: 'CapacitorCookies.set',\n                                    action: val,\n                                    domain,\n                                };\n                                prompt(JSON.stringify(payload));\n                            }\n                            else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n                                win.CapacitorCookiesAndroidInterface.setCookie(domain, val);\n                            }\n                        },\n                    });\n                }\n                // patch fetch / XHR on Android/iOS\n                // store original fetch & XHR functions\n                win.CapacitorWebFetch = window.fetch;\n                win.CapacitorWebXMLHttpRequest = {\n                    abort: window.XMLHttpRequest.prototype.abort,\n                    constructor: window.XMLHttpRequest.prototype.constructor,\n                    fullObject: window.XMLHttpRequest,\n                    getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders,\n                    getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader,\n                    open: window.XMLHttpRequest.prototype.open,\n                    prototype: window.XMLHttpRequest.prototype,\n                    send: window.XMLHttpRequest.prototype.send,\n                    setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader,\n                };\n                let doPatchHttp = false;\n                // check if capacitor http is disabled before patching\n                if (platform === 'ios') {\n                    // Use prompt to synchronously get capacitor http config.\n                    // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                    const payload = {\n                        type: 'CapacitorHttp',\n                    };\n                    const isHttpEnabled = prompt(JSON.stringify(payload));\n                    if (isHttpEnabled === 'true') {\n                        doPatchHttp = true;\n                    }\n                }\n                else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') {\n                    const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled();\n                    if (isHttpEnabled === true) {\n                        doPatchHttp = true;\n                    }\n                }\n                if (doPatchHttp) {\n                    // fetch patch\n                    window.fetch = async (resource, options) => {\n                        const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers);\n                        const contentType = headers.get('Content-Type') || headers.get('content-type');\n                        if ((options === null || options === void 0 ? void 0 : options.body) instanceof FormData &&\n                            (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) &&\n                            !contentType.includes('boundary')) {\n                            headers.delete('Content-Type');\n                            headers.delete('content-type');\n                            options.headers = headers;\n                        }\n                        const request = new Request(resource, options);\n                        if (request.url.startsWith(`${cap.getServerUrl()}/`)) {\n                            return win.CapacitorWebFetch(resource, options);\n                        }\n                        const { method } = request;\n                        if (method.toLocaleUpperCase() === 'GET' ||\n                            method.toLocaleUpperCase() === 'HEAD' ||\n                            method.toLocaleUpperCase() === 'OPTIONS' ||\n                            method.toLocaleUpperCase() === 'TRACE') {\n                            // a workaround for following android webview issue:\n                            // https://issues.chromium.org/issues/40450316\n                            // Sets the user-agent header to a custom value so that its not stripped\n                            // on its way to the native layer\n                            if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) {\n                                const userAgent = headers.get('User-Agent') || headers.get('user-agent');\n                                if (userAgent !== null) {\n                                    headers.set('x-cap-user-agent', userAgent);\n                                    options.headers = headers;\n                                }\n                            }\n                            if (typeof resource === 'string') {\n                                return await win.CapacitorWebFetch(createProxyUrl(resource, win), options);\n                            }\n                            else if (resource instanceof URL) {\n                                const modifiedURL = new URL(createProxyUrl(resource.toString(), win));\n                                return await win.CapacitorWebFetch(modifiedURL, options);\n                            }\n                            else if (resource instanceof Request) {\n                                const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource);\n                                return await win.CapacitorWebFetch(modifiedRequest, options);\n                            }\n                        }\n                        const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`;\n                        console.time(tag);\n                        try {\n                            const { body } = request;\n                            const optionHeaders = Object.fromEntries(request.headers.entries());\n                            const { data: requestData, type, headers: requestHeaders, } = await convertBody((options === null || options === void 0 ? void 0 : options.body) || body || undefined, optionHeaders['Content-Type'] || optionHeaders['content-type']);\n                            const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders);\n                            if (platform === 'android') {\n                                if (headers.has('User-Agent')) {\n                                    nativeHeaders['User-Agent'] = headers.get('User-Agent');\n                                }\n                                if (headers.has('user-agent')) {\n                                    nativeHeaders['user-agent'] = headers.get('user-agent');\n                                }\n                            }\n                            const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', {\n                                url: request.url,\n                                method: method,\n                                data: requestData,\n                                dataType: type,\n                                headers: nativeHeaders,\n                            });\n                            const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'];\n                            let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json'))\n                                ? JSON.stringify(nativeResponse.data)\n                                : nativeResponse.data;\n                            // use null data for 204 No Content HTTP response\n                            if (nativeResponse.status === 204) {\n                                data = null;\n                            }\n                            // intercept & parse response before returning\n                            const response = new Response(data, {\n                                headers: nativeResponse.headers,\n                                status: nativeResponse.status,\n                            });\n                            /*\n                             * copy url to response, `cordova-plugin-ionic` uses this url from the response\n                             * we need `Object.defineProperty` because url is an inherited getter on the Response\n                             * see: https://stackoverflow.com/a/57382543\n                             * */\n                            Object.defineProperty(response, 'url', {\n                                value: nativeResponse.url,\n                            });\n                            console.timeEnd(tag);\n                            return response;\n                        }\n                        catch (error) {\n                            console.timeEnd(tag);\n                            return Promise.reject(error);\n                        }\n                    };\n                    window.XMLHttpRequest = function () {\n                        const xhr = new win.CapacitorWebXMLHttpRequest.constructor();\n                        Object.defineProperties(xhr, {\n                            _headers: {\n                                value: {},\n                                writable: true,\n                            },\n                            _method: {\n                                value: xhr.method,\n                                writable: true,\n                            },\n                        });\n                        const prototype = win.CapacitorWebXMLHttpRequest.prototype;\n                        const isProgressEventAvailable = () => typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event;\n                        // XHR patch abort\n                        prototype.abort = function () {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.abort.call(this);\n                            }\n                            this.readyState = 0;\n                            setTimeout(() => {\n                                this.dispatchEvent(new Event('abort'));\n                                this.dispatchEvent(new Event('loadend'));\n                            });\n                        };\n                        // XHR patch open\n                        prototype.open = function (method, url) {\n                            this._method = method.toLocaleUpperCase();\n                            this._url = url;\n                            if (!this._method ||\n                                this._method === 'GET' ||\n                                this._method === 'HEAD' ||\n                                this._method === 'OPTIONS' ||\n                                this._method === 'TRACE') {\n                                if (isRelativeOrProxyUrl(url)) {\n                                    return win.CapacitorWebXMLHttpRequest.open.call(this, method, url);\n                                }\n                                this._url = createProxyUrl(this._url, win);\n                                return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url);\n                            }\n                            Object.defineProperties(this, {\n                                readyState: {\n                                    get: function () {\n                                        var _a;\n                                        return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0;\n                                    },\n                                    set: function (val) {\n                                        this._readyState = val;\n                                        setTimeout(() => {\n                                            this.dispatchEvent(new Event('readystatechange'));\n                                        });\n                                    },\n                                },\n                            });\n                            setTimeout(() => {\n                                this.dispatchEvent(new Event('loadstart'));\n                            });\n                            this.readyState = 1;\n                        };\n                        // XHR patch set request header\n                        prototype.setRequestHeader = function (header, value) {\n                            // a workaround for the following android web view issue:\n                            // https://issues.chromium.org/issues/40450316\n                            // Sets the user-agent header to a custom value so that its not stripped\n                            // on its way to the native layer\n                            if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) {\n                                header = 'x-cap-user-agent';\n                            }\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value);\n                            }\n                            this._headers[header] = value;\n                        };\n                        // XHR patch send\n                        prototype.send = function (body) {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.send.call(this, body);\n                            }\n                            const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`;\n                            console.time(tag);\n                            try {\n                                this.readyState = 2;\n                                Object.defineProperties(this, {\n                                    response: {\n                                        value: '',\n                                        writable: true,\n                                    },\n                                    responseText: {\n                                        value: '',\n                                        writable: true,\n                                    },\n                                    responseURL: {\n                                        value: '',\n                                        writable: true,\n                                    },\n                                    status: {\n                                        value: 0,\n                                        writable: true,\n                                    },\n                                });\n                                convertBody(body).then(({ data, type, headers }) => {\n                                    let otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined;\n                                    if (body instanceof FormData) {\n                                        if (!this._headers['Content-Type'] && !this._headers['content-type']) {\n                                            otherHeaders = Object.assign(Object.assign({}, otherHeaders), { 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}` });\n                                        }\n                                    }\n                                    // intercept request & pass to the bridge\n                                    cap\n                                        .nativePromise('CapacitorHttp', 'request', {\n                                        url: this._url,\n                                        method: this._method,\n                                        data: data !== null ? data : undefined,\n                                        headers: Object.assign(Object.assign({}, headers), otherHeaders),\n                                        dataType: type,\n                                    })\n                                        .then((nativeResponse) => {\n                                        var _a;\n                                        // intercept & parse response before returning\n                                        if (this.readyState == 2) {\n                                            //TODO: Add progress event emission on native side\n                                            if (isProgressEventAvailable()) {\n                                                this.dispatchEvent(new ProgressEvent('progress', {\n                                                    lengthComputable: true,\n                                                    loaded: nativeResponse.data.length,\n                                                    total: nativeResponse.data.length,\n                                                }));\n                                            }\n                                            this._headers = nativeResponse.headers;\n                                            this.status = nativeResponse.status;\n                                            if (this.responseType === '' || this.responseType === 'text') {\n                                                this.response =\n                                                    typeof nativeResponse.data !== 'string'\n                                                        ? JSON.stringify(nativeResponse.data)\n                                                        : nativeResponse.data;\n                                            }\n                                            else {\n                                                this.response = nativeResponse.data;\n                                            }\n                                            this.responseText = ((_a = (nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'])) === null || _a === void 0 ? void 0 : _a.startsWith('application/json'))\n                                                ? JSON.stringify(nativeResponse.data)\n                                                : nativeResponse.data;\n                                            this.responseURL = nativeResponse.url;\n                                            this.readyState = 4;\n                                            setTimeout(() => {\n                                                this.dispatchEvent(new Event('load'));\n                                                this.dispatchEvent(new Event('loadend'));\n                                            });\n                                        }\n                                        console.timeEnd(tag);\n                                    })\n                                        .catch((error) => {\n                                        this.status = error.status;\n                                        this._headers = error.headers;\n                                        this.response = error.data;\n                                        this.responseText = JSON.stringify(error.data);\n                                        this.responseURL = error.url;\n                                        this.readyState = 4;\n                                        if (isProgressEventAvailable()) {\n                                            this.dispatchEvent(new ProgressEvent('progress', {\n                                                lengthComputable: false,\n                                                loaded: 0,\n                                                total: 0,\n                                            }));\n                                        }\n                                        setTimeout(() => {\n                                            this.dispatchEvent(new Event('error'));\n                                            this.dispatchEvent(new Event('loadend'));\n                                        });\n                                        console.timeEnd(tag);\n                                    });\n                                });\n                            }\n                            catch (error) {\n                                this.status = 500;\n                                this._headers = {};\n                                this.response = error;\n                                this.responseText = error.toString();\n                                this.responseURL = this._url;\n                                this.readyState = 4;\n                                if (isProgressEventAvailable()) {\n                                    this.dispatchEvent(new ProgressEvent('progress', {\n                                        lengthComputable: false,\n                                        loaded: 0,\n                                        total: 0,\n                                    }));\n                                }\n                                setTimeout(() => {\n                                    this.dispatchEvent(new Event('error'));\n                                    this.dispatchEvent(new Event('loadend'));\n                                });\n                                console.timeEnd(tag);\n                            }\n                        };\n                        // XHR patch getAllResponseHeaders\n                        prototype.getAllResponseHeaders = function () {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this);\n                            }\n                            let returnString = '';\n                            for (const key in this._headers) {\n                                if (key != 'Set-Cookie') {\n                                    returnString += key + ': ' + this._headers[key] + '\\r\\n';\n                                }\n                            }\n                            return returnString;\n                        };\n                        // XHR patch getResponseHeader\n                        prototype.getResponseHeader = function (name) {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name);\n                            }\n                            return this._headers[name];\n                        };\n                        Object.setPrototypeOf(xhr, prototype);\n                        return xhr;\n                    };\n                    Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject);\n                }\n            }\n            // patch window.console on iOS and store original console fns\n            const isIos = getPlatformId(win) === 'ios';\n            if (win.console && isIos) {\n                Object.defineProperties(win.console, BRIDGED_CONSOLE_METHODS.reduce((props, method) => {\n                    const consoleMethod = win.console[method].bind(win.console);\n                    props[method] = {\n                        value: (...args) => {\n                            const msgs = [...args];\n                            cap.toNative('Console', 'log', {\n                                level: method,\n                                message: msgs.map(serializeConsoleMessage).join(' '),\n                            });\n                            return consoleMethod(...args);\n                        },\n                    };\n                    return props;\n                }, {}));\n            }\n            cap.logJs = (msg, level) => {\n                switch (level) {\n                    case 'error':\n                        win.console.error(msg);\n                        break;\n                    case 'warn':\n                        win.console.warn(msg);\n                        break;\n                    case 'info':\n                        win.console.info(msg);\n                        break;\n                    default:\n                        win.console.log(msg);\n                }\n            };\n            cap.logToNative = createLogToNative(win.console);\n            cap.logFromNative = createLogFromNative(win.console);\n            cap.handleError = (err) => win.console.error(err);\n            win.Capacitor = cap;\n        };\n        function initNativeBridge(win) {\n            const cap = win.Capacitor || {};\n            // keep a collection of callbacks for native response data\n            const callbacks = new Map();\n            const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : '';\n            cap.getServerUrl = () => webviewServerUrl;\n            cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath);\n            // Counter of callback ids, randomized to avoid\n            // any issues during reloads if a call comes back with\n            // an existing callback id from an old session\n            let callbackIdCount = Math.floor(Math.random() * 134217728);\n            let postToNative = null;\n            const isNativePlatform = () => true;\n            const getPlatform = () => getPlatformId(win);\n            cap.getPlatform = getPlatform;\n            cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name);\n            cap.isNativePlatform = isNativePlatform;\n            // create the postToNative() fn if needed\n            if (getPlatformId(win) === 'android') {\n                // android platform\n                postToNative = (data) => {\n                    var _a;\n                    try {\n                        win.androidBridge.postMessage(JSON.stringify(data));\n                    }\n                    catch (e) {\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e);\n                    }\n                };\n            }\n            else if (getPlatformId(win) === 'ios') {\n                // ios platform\n                postToNative = (data) => {\n                    var _a;\n                    try {\n                        data.type = data.type ? data.type : 'message';\n                        win.webkit.messageHandlers.bridge.postMessage(data);\n                    }\n                    catch (e) {\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e);\n                    }\n                };\n            }\n            cap.handleWindowError = (msg, url, lineNo, columnNo, err) => {\n                const str = msg.toLowerCase();\n                if (str.indexOf('script error') > -1) ;\n                else {\n                    const errObj = {\n                        type: 'js.error',\n                        error: {\n                            message: msg,\n                            url: url,\n                            line: lineNo,\n                            col: columnNo,\n                            errorObject: JSON.stringify(err),\n                        },\n                    };\n                    if (err !== null) {\n                        cap.handleError(err);\n                    }\n                    postToNative(errObj);\n                }\n                return false;\n            };\n            if (cap.DEBUG) {\n                window.onerror = cap.handleWindowError;\n            }\n            initLogger(win, cap);\n            /**\n             * Send a plugin method call to the native layer\n             */\n            cap.toNative = (pluginName, methodName, options, storedCallback) => {\n                var _a, _b;\n                try {\n                    if (typeof postToNative === 'function') {\n                        let callbackId = '-1';\n                        if (storedCallback &&\n                            (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function')) {\n                            // store the call for later lookup\n                            callbackId = String(++callbackIdCount);\n                            callbacks.set(callbackId, storedCallback);\n                        }\n                        const callData = {\n                            callbackId: callbackId,\n                            pluginId: pluginName,\n                            methodName: methodName,\n                            options: options || {},\n                        };\n                        if (cap.isLoggingEnabled && pluginName !== 'Console') {\n                            cap.logToNative(callData);\n                        }\n                        // post the call data to native\n                        postToNative(callData);\n                        return callbackId;\n                    }\n                    else {\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(`implementation unavailable for: ${pluginName}`);\n                    }\n                }\n                catch (e) {\n                    (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e);\n                }\n                return null;\n            };\n            if (win === null || win === void 0 ? void 0 : win.androidBridge) {\n                win.androidBridge.onmessage = function (event) {\n                    returnResult(JSON.parse(event.data));\n                };\n            }\n            /**\n             * Process a response from the native layer.\n             */\n            cap.fromNative = (result) => {\n                returnResult(result);\n            };\n            const returnResult = (result) => {\n                var _a, _b;\n                if (cap.isLoggingEnabled && result.pluginId !== 'Console') {\n                    cap.logFromNative(result);\n                }\n                // get the stored call, if it exists\n                try {\n                    const storedCall = callbacks.get(result.callbackId);\n                    if (storedCall) {\n                        // looks like we've got a stored call\n                        if (result.error) {\n                            // ensure stacktraces by copying error properties to an Error\n                            result.error = Object.keys(result.error).reduce((err, key) => {\n                                // use any type to avoid importing util and compiling most of .ts files\n                                err[key] = result.error[key];\n                                return err;\n                            }, new cap.Exception(''));\n                        }\n                        if (typeof storedCall.callback === 'function') {\n                            // callback\n                            if (result.success) {\n                                storedCall.callback(result.data);\n                            }\n                            else {\n                                storedCall.callback(null, result.error);\n                            }\n                        }\n                        else if (typeof storedCall.resolve === 'function') {\n                            // promise\n                            if (result.success) {\n                                storedCall.resolve(result.data);\n                            }\n                            else {\n                                storedCall.reject(result.error);\n                            }\n                            // no need to keep this stored callback\n                            // around for a one time resolve promise\n                            callbacks.delete(result.callbackId);\n                        }\n                    }\n                    else if (!result.success && result.error) {\n                        // no stored callback, but if there was an error let's log it\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(result.error);\n                    }\n                    if (result.save === false) {\n                        callbacks.delete(result.callbackId);\n                    }\n                }\n                catch (e) {\n                    (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e);\n                }\n                // always delete to prevent memory leaks\n                // overkill but we're not sure what apps will do with this data\n                delete result.data;\n                delete result.error;\n            };\n            cap.nativeCallback = (pluginName, methodName, options, callback) => {\n                if (typeof options === 'function') {\n                    console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`);\n                    callback = options;\n                    options = null;\n                }\n                return cap.toNative(pluginName, methodName, options, { callback });\n            };\n            cap.nativePromise = (pluginName, methodName, options) => {\n                return new Promise((resolve, reject) => {\n                    cap.toNative(pluginName, methodName, options, {\n                        resolve: resolve,\n                        reject: reject,\n                    });\n                });\n            };\n            // eslint-disable-next-line @typescript-eslint/no-unused-vars\n            cap.withPlugin = (_pluginId, _fn) => dummy;\n            cap.Exception = CapacitorException;\n            initEvents(win, cap);\n            initLegacyHandlers(win, cap);\n            initVendor(win, cap);\n            win.Capacitor = cap;\n        }\n        initNativeBridge(w);\n    };\n    initBridge(typeof globalThis !== 'undefined'\n        ? globalThis\n        : typeof self !== 'undefined'\n            ? self\n            : typeof window !== 'undefined'\n                ? window\n                : typeof global !== 'undefined'\n                    ? global\n                    : {});\n\n    dummy = initBridge;\n\n    Object.defineProperty(exports, '__esModule', { value: true });\n\n    return exports;\n\n})({});\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/AndroidProtocolHandler.java",
    "content": "// Copyright 2012 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\npackage com.getcapacitor;\n\nimport android.content.Context;\nimport android.content.res.AssetManager;\nimport android.net.Uri;\nimport android.util.TypedValue;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\npublic class AndroidProtocolHandler {\n\n    private Context context;\n\n    public AndroidProtocolHandler(Context context) {\n        this.context = context;\n    }\n\n    public InputStream openAsset(String path) throws IOException {\n        return context.getAssets().open(path, AssetManager.ACCESS_STREAMING);\n    }\n\n    public InputStream openResource(Uri uri) {\n        assert uri.getPath() != null;\n        // The path must be of the form \".../asset_type/asset_name.ext\".\n        List<String> pathSegments = uri.getPathSegments();\n        String assetType = pathSegments.get(pathSegments.size() - 2);\n        String assetName = pathSegments.get(pathSegments.size() - 1);\n\n        // Drop the file extension.\n        assetName = assetName.split(\"\\\\.\")[0];\n        try {\n            // Use the application context for resolving the resource package name so that we do\n            // not use the browser's own resources. Note that if 'context' here belongs to the\n            // test suite, it does not have a separate application context. In that case we use\n            // the original context object directly.\n            if (context.getApplicationContext() != null) {\n                context = context.getApplicationContext();\n            }\n            int fieldId = getFieldId(context, assetType, assetName);\n            int valueType = getValueType(context, fieldId);\n            if (valueType == TypedValue.TYPE_STRING) {\n                return context.getResources().openRawResource(fieldId);\n            } else {\n                Logger.error(\"Asset not of type string: \" + uri);\n            }\n        } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {\n            Logger.error(\"Unable to open resource URL: \" + uri, e);\n        }\n        return null;\n    }\n\n    private static int getFieldId(Context context, String assetType, String assetName)\n        throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {\n        Class<?> d = context.getClassLoader().loadClass(context.getPackageName() + \".R$\" + assetType);\n        java.lang.reflect.Field field = d.getField(assetName);\n        return field.getInt(null);\n    }\n\n    public InputStream openFile(String filePath) throws IOException {\n        String realPath = filePath.replace(Bridge.CAPACITOR_FILE_START, \"\");\n        File localFile = new File(realPath);\n        return new FileInputStream(localFile);\n    }\n\n    public InputStream openContentUrl(Uri uri) throws IOException {\n        Integer port = uri.getPort();\n        String baseUrl = uri.getScheme() + \"://\" + uri.getHost();\n        if (port != -1) {\n            baseUrl += \":\" + port;\n        }\n        String realPath = uri.toString().replace(baseUrl + Bridge.CAPACITOR_CONTENT_START, \"content:/\");\n\n        InputStream stream = null;\n        try {\n            stream = context.getContentResolver().openInputStream(Uri.parse(realPath));\n        } catch (SecurityException e) {\n            Logger.error(\"Unable to open content URL: \" + uri, e);\n        }\n        return stream;\n    }\n\n    private static int getValueType(Context context, int fieldId) {\n        TypedValue value = new TypedValue();\n        context.getResources().getValue(fieldId, value, true);\n        return value.type;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/App.java",
    "content": "package com.getcapacitor;\n\nimport androidx.annotation.Nullable;\n\npublic class App {\n\n    /**\n     * Interface for callbacks when app status changes.\n     */\n    public interface AppStatusChangeListener {\n        void onAppStatusChanged(Boolean isActive);\n    }\n\n    /**\n     * Interface for callbacks when app is restored with pending plugin call.\n     */\n    public interface AppRestoredListener {\n        void onAppRestored(PluginResult result);\n    }\n\n    @Nullable\n    private AppStatusChangeListener statusChangeListener;\n\n    @Nullable\n    private AppRestoredListener appRestoredListener;\n\n    private boolean isActive = false;\n\n    public boolean isActive() {\n        return isActive;\n    }\n\n    /**\n     * Set the object to receive callbacks.\n     * @param listener\n     */\n    public void setStatusChangeListener(@Nullable AppStatusChangeListener listener) {\n        this.statusChangeListener = listener;\n    }\n\n    /**\n     * Set the object to receive callbacks.\n     * @param listener\n     */\n    public void setAppRestoredListener(@Nullable AppRestoredListener listener) {\n        this.appRestoredListener = listener;\n    }\n\n    protected void fireRestoredResult(PluginResult result) {\n        if (appRestoredListener != null) {\n            appRestoredListener.onAppRestored(result);\n        }\n    }\n\n    public void fireStatusChange(boolean isActive) {\n        this.isActive = isActive;\n        if (statusChangeListener != null) {\n            statusChangeListener.onAppStatusChanged(isActive);\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/AppUUID.java",
    "content": "package com.getcapacitor;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport androidx.appcompat.app.AppCompatActivity;\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Objects;\nimport java.util.UUID;\n\npublic final class AppUUID {\n\n    private static final String KEY = \"CapacitorAppUUID\";\n\n    public static String getAppUUID(AppCompatActivity activity) throws Exception {\n        assertAppUUID(activity);\n        return readUUID(activity);\n    }\n\n    public static void regenerateAppUUID(AppCompatActivity activity) throws Exception {\n        try {\n            String uuid = generateUUID();\n            writeUUID(activity, uuid);\n        } catch (NoSuchAlgorithmException ex) {\n            throw new Exception(\"Capacitor App UUID could not be generated.\");\n        }\n    }\n\n    private static void assertAppUUID(AppCompatActivity activity) throws Exception {\n        String uuid = readUUID(activity);\n        if (uuid.equals(\"\")) {\n            regenerateAppUUID(activity);\n        }\n    }\n\n    private static String generateUUID() throws NoSuchAlgorithmException {\n        MessageDigest salt = MessageDigest.getInstance(\"SHA-256\");\n        salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));\n        return bytesToHex(salt.digest());\n    }\n\n    private static String readUUID(AppCompatActivity activity) {\n        SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);\n        return sharedPref.getString(KEY, \"\");\n    }\n\n    private static void writeUUID(AppCompatActivity activity, String uuid) {\n        SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);\n        SharedPreferences.Editor editor = sharedPref.edit();\n        editor.putString(KEY, uuid);\n        editor.apply();\n    }\n\n    private static String bytesToHex(byte[] bytes) {\n        byte[] HEX_ARRAY = \"0123456789ABCDEF\".getBytes(StandardCharsets.US_ASCII);\n        byte[] hexChars = new byte[bytes.length * 2];\n        for (int j = 0; j < bytes.length; j++) {\n            int v = bytes[j] & 0xFF;\n            hexChars[j * 2] = HEX_ARRAY[v >>> 4];\n            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];\n        }\n        return new String(hexChars, StandardCharsets.UTF_8);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/Bridge.java",
    "content": "package com.getcapacitor;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.res.Configuration;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.webkit.ServiceWorkerClient;\nimport android.webkit.ServiceWorkerController;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport android.webkit.WebSettings;\nimport android.webkit.WebView;\nimport androidx.activity.result.ActivityResultCallback;\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContract;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.app.ActivityCompat;\nimport androidx.core.content.pm.PackageInfoCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.webkit.WebViewCompat;\nimport androidx.webkit.WebViewFeature;\nimport com.getcapacitor.android.R;\nimport com.getcapacitor.annotation.CapacitorPlugin;\nimport com.getcapacitor.annotation.Permission;\nimport com.getcapacitor.cordova.MockCordovaInterfaceImpl;\nimport com.getcapacitor.cordova.MockCordovaWebViewImpl;\nimport com.getcapacitor.util.HostMask;\nimport com.getcapacitor.util.InternalUtils;\nimport com.getcapacitor.util.PermissionHelper;\nimport com.getcapacitor.util.WebColor;\nimport java.io.File;\nimport java.net.SocketTimeoutException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport org.apache.cordova.ConfigXmlParser;\nimport org.apache.cordova.CordovaPreferences;\nimport org.apache.cordova.CordovaWebView;\nimport org.apache.cordova.PluginEntry;\nimport org.apache.cordova.PluginManager;\nimport org.json.JSONException;\n\n/**\n * The Bridge class is the main engine of Capacitor. It manages\n * loading and communicating with all Plugins,\n * proxying Native events to Plugins, executing Plugin methods,\n * communicating with the WebView, and a whole lot more.\n *\n * Generally, you'll not use Bridge directly, instead, extend from BridgeActivity\n * to get a WebView instance and proxy native events automatically.\n *\n * If you want to use this Bridge in an existing Android app, please\n * see the source for BridgeActivity for the methods you'll need to\n * pass through to Bridge:\n * <a href=\"https://github.com/ionic-team/capacitor/blob/HEAD/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java\">\n *   BridgeActivity.java</a>\n */\npublic class Bridge {\n\n    private static final String PERMISSION_PREFS_NAME = \"PluginPermStates\";\n    private static final String BUNDLE_LAST_PLUGIN_ID_KEY = \"capacitorLastActivityPluginId\";\n    private static final String BUNDLE_LAST_PLUGIN_CALL_METHOD_NAME_KEY = \"capacitorLastActivityPluginMethod\";\n    private static final String BUNDLE_PLUGIN_CALL_OPTIONS_SAVED_KEY = \"capacitorLastPluginCallOptions\";\n    private static final String BUNDLE_PLUGIN_CALL_BUNDLE_KEY = \"capacitorLastPluginCallBundle\";\n    private static final String LAST_BINARY_VERSION_CODE = \"lastBinaryVersionCode\";\n    private static final String LAST_BINARY_VERSION_NAME = \"lastBinaryVersionName\";\n    private static final String MINIMUM_ANDROID_WEBVIEW_ERROR = \"System WebView is not supported\";\n\n    // The name of the directory we use to look for index.html and the rest of our web assets\n    public static final String DEFAULT_WEB_ASSET_DIR = \"public\";\n    public static final String CAPACITOR_HTTP_SCHEME = \"http\";\n    public static final String CAPACITOR_HTTPS_SCHEME = \"https\";\n    public static final String CAPACITOR_FILE_START = \"/_capacitor_file_\";\n    public static final String CAPACITOR_CONTENT_START = \"/_capacitor_content_\";\n    public static final String CAPACITOR_HTTP_INTERCEPTOR_START = \"/_capacitor_http_interceptor_\";\n\n    /** @deprecated CAPACITOR_HTTPS_INTERCEPTOR_START is no longer required. All proxied requests are handled via CAPACITOR_HTTP_INTERCEPTOR_START instead */\n    @Deprecated\n    public static final String CAPACITOR_HTTPS_INTERCEPTOR_START = \"/_capacitor_https_interceptor_\";\n\n    public static final String CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = \"u\";\n\n    public static final int DEFAULT_ANDROID_WEBVIEW_VERSION = 60;\n    public static final int MINIMUM_ANDROID_WEBVIEW_VERSION = 55;\n    public static final int DEFAULT_HUAWEI_WEBVIEW_VERSION = 10;\n    public static final int MINIMUM_HUAWEI_WEBVIEW_VERSION = 10;\n\n    // Loaded Capacitor config\n    private CapConfig config;\n\n    // A reference to the main activity for the app\n    private final AppCompatActivity context;\n    // A reference to the containing Fragment if used\n    private final Fragment fragment;\n    private WebViewLocalServer localServer;\n    private String localUrl;\n    private String appUrl;\n    private String appUrlConfig;\n    private HostMask appAllowNavigationMask;\n    private Set<String> allowedOriginRules = new HashSet<String>();\n    private ArrayList<String> authorities = new ArrayList<>();\n    private ArrayList<String> miscJSFileInjections = new ArrayList<String>();\n    private Boolean canInjectJS = true;\n    // A reference to the main WebView for the app\n    private final WebView webView;\n    public final MockCordovaInterfaceImpl cordovaInterface;\n    private CordovaWebView cordovaWebView;\n    private CordovaPreferences preferences;\n    private BridgeWebViewClient webViewClient;\n    private App app;\n\n    // Our MessageHandler for sending and receiving data to the WebView\n    private final MessageHandler msgHandler;\n\n    // The ThreadHandler for executing plugin calls\n    private final HandlerThread handlerThread = new HandlerThread(\"CapacitorPlugins\");\n\n    // Our Handler for posting plugin calls. Created from the ThreadHandler\n    private Handler taskHandler = null;\n\n    private final List<Class<? extends Plugin>> initialPlugins;\n\n    private final List<Plugin> pluginInstances;\n\n    // A map of Plugin Id's to PluginHandle's\n    private Map<String, PluginHandle> plugins = new HashMap<>();\n\n    // Stored plugin calls that we're keeping around to call again someday\n    private Map<String, PluginCall> savedCalls = new HashMap<>();\n\n    // The call IDs of saved plugin calls with associated plugin id for handling permissions\n    private Map<String, LinkedList<String>> savedPermissionCallIds = new HashMap<>();\n\n    // Store a plugin that started a new activity, in case we need to resume\n    // the app and return that data back\n    private PluginCall pluginCallForLastActivity;\n\n    // Any URI that was passed to the app on start\n    private Uri intentUri;\n\n    // A list of listeners that trigger when webView events occur\n    private List<WebViewListener> webViewListeners = new ArrayList<>();\n\n    // An interface to manipulate route resolving\n    private RouteProcessor routeProcessor;\n\n    // A pre-determined path to load the bridge\n    private ServerPath serverPath;\n\n    /**\n     * Create the Bridge with a reference to the main {@link Activity} for the\n     * app, and a reference to the {@link WebView} our app will use.\n     * @param context\n     * @param webView\n     * @deprecated Use {@link Bridge.Builder} to create Bridge instances\n     */\n    @Deprecated\n    public Bridge(\n        AppCompatActivity context,\n        WebView webView,\n        List<Class<? extends Plugin>> initialPlugins,\n        MockCordovaInterfaceImpl cordovaInterface,\n        PluginManager pluginManager,\n        CordovaPreferences preferences,\n        CapConfig config\n    ) {\n        this(context, null, null, webView, initialPlugins, new ArrayList<>(), cordovaInterface, pluginManager, preferences, config);\n    }\n\n    private Bridge(\n        AppCompatActivity context,\n        ServerPath serverPath,\n        Fragment fragment,\n        WebView webView,\n        List<Class<? extends Plugin>> initialPlugins,\n        List<Plugin> pluginInstances,\n        MockCordovaInterfaceImpl cordovaInterface,\n        PluginManager pluginManager,\n        CordovaPreferences preferences,\n        CapConfig config\n    ) {\n        this.app = new App();\n        this.serverPath = serverPath;\n        this.context = context;\n        this.fragment = fragment;\n        this.webView = webView;\n        this.webViewClient = new BridgeWebViewClient(this);\n        this.initialPlugins = initialPlugins;\n        this.pluginInstances = pluginInstances;\n        this.cordovaInterface = cordovaInterface;\n        this.preferences = preferences;\n\n        // Start our plugin execution threads and handlers\n        handlerThread.start();\n        taskHandler = new Handler(handlerThread.getLooper());\n\n        this.config = config != null ? config : CapConfig.loadDefault(getActivity());\n        Logger.init(this.config);\n\n        // Initialize web view and message handler for it\n        this.initWebView();\n        this.setAllowedOriginRules();\n        this.msgHandler = new MessageHandler(this, webView, pluginManager);\n\n        // Grab any intent info that our app was launched with\n        Intent intent = context.getIntent();\n        this.intentUri = intent.getData();\n        // Register our core plugins\n        this.registerAllPlugins();\n\n        this.loadWebView();\n    }\n\n    private void setAllowedOriginRules() {\n        String[] appAllowNavigationConfig = this.config.getAllowNavigation();\n        String authority = this.getHost();\n        String scheme = this.getScheme();\n        allowedOriginRules.add(scheme + \"://\" + authority);\n        if (this.getServerUrl() != null) {\n            allowedOriginRules.add(this.getServerUrl());\n        }\n        if (appAllowNavigationConfig != null) {\n            for (String allowNavigation : appAllowNavigationConfig) {\n                if (!allowNavigation.startsWith(\"http\")) {\n                    allowedOriginRules.add(\"https://\" + allowNavigation);\n                } else {\n                    allowedOriginRules.add(allowNavigation);\n                }\n            }\n            authorities.addAll(Arrays.asList(appAllowNavigationConfig));\n        }\n        this.appAllowNavigationMask = HostMask.Parser.parse(appAllowNavigationConfig);\n    }\n\n    public App getApp() {\n        return app;\n    }\n\n    private void loadWebView() {\n        final boolean html5mode = this.config.isHTML5Mode();\n\n        // Start the local web server\n        JSInjector injector = getJSInjector();\n        if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {\n            String allowedOrigin = Uri.parse(appUrl).buildUpon().path(null).fragment(null).clearQuery().build().toString();\n            try {\n                WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), Collections.singleton(allowedOrigin));\n                injector = null;\n            } catch (IllegalArgumentException ex) {\n                Logger.warn(\"Invalid url, using fallback\");\n            }\n        }\n        localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode);\n        localServer.hostAssets(DEFAULT_WEB_ASSET_DIR);\n\n        Logger.debug(\"Loading app at \" + appUrl);\n\n        webView.setWebChromeClient(new BridgeWebChromeClient(this));\n        webView.setWebViewClient(this.webViewClient);\n\n        if (config.isResolveServiceWorkerRequests()) {\n            ServiceWorkerController swController = ServiceWorkerController.getInstance();\n            swController.setServiceWorkerClient(\n                new ServiceWorkerClient() {\n                    @Override\n                    public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {\n                        return getLocalServer().shouldInterceptRequest(request);\n                    }\n                }\n            );\n        }\n\n        if (!isDeployDisabled() && !isNewBinary()) {\n            SharedPreferences prefs = getContext().getSharedPreferences(\n                com.getcapacitor.plugin.WebView.WEBVIEW_PREFS_NAME,\n                Activity.MODE_PRIVATE\n            );\n            String path = prefs.getString(com.getcapacitor.plugin.WebView.CAP_SERVER_PATH, null);\n            if (path != null && !path.isEmpty() && new File(path).exists()) {\n                setServerBasePath(path);\n            }\n        }\n        if (!this.isMinimumWebViewInstalled()) {\n            String errorUrl = this.getErrorUrl();\n            if (errorUrl != null) {\n                webView.loadUrl(errorUrl);\n                return;\n            } else {\n                Logger.error(MINIMUM_ANDROID_WEBVIEW_ERROR);\n            }\n        }\n\n        // If serverPath configured, start server based on provided path\n        if (serverPath != null) {\n            if (serverPath.getType() == ServerPath.PathType.ASSET_PATH) {\n                setServerAssetPath(serverPath.getPath());\n            } else {\n                setServerBasePath(serverPath.getPath());\n            }\n        } else {\n            // Get to work\n            webView.loadUrl(appUrl);\n        }\n    }\n\n    @SuppressLint(\"WebViewApiAvailability\")\n    public boolean isMinimumWebViewInstalled() {\n        PackageManager pm = getContext().getPackageManager();\n\n        // Check getCurrentWebViewPackage() directly if above Android 8\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            PackageInfo info = WebView.getCurrentWebViewPackage();\n            Pattern pattern = Pattern.compile(\"(\\\\d+)\");\n            Matcher matcher = pattern.matcher(info.versionName);\n            if (matcher.find()) {\n                String majorVersionStr = matcher.group(0);\n                int majorVersion = Integer.parseInt(majorVersionStr);\n                if (info.packageName.equals(\"com.huawei.webview\")) {\n                    return majorVersion >= config.getMinHuaweiWebViewVersion();\n                }\n                return majorVersion >= config.getMinWebViewVersion();\n            } else {\n                return false;\n            }\n        }\n\n        // Otherwise manually check WebView versions\n        try {\n            PackageInfo info = InternalUtils.getPackageInfo(pm, \"com.android.chrome\");\n            String majorVersionStr = info.versionName.split(\"\\\\.\")[0];\n            int majorVersion = Integer.parseInt(majorVersionStr);\n            return majorVersion >= config.getMinWebViewVersion();\n        } catch (Exception ex) {\n            Logger.warn(\"Unable to get package info for 'com.google.android.webview'\" + ex.toString());\n        }\n\n        try {\n            PackageInfo info = InternalUtils.getPackageInfo(pm, \"com.android.webview\");\n            String majorVersionStr = info.versionName.split(\"\\\\.\")[0];\n            int majorVersion = Integer.parseInt(majorVersionStr);\n            return majorVersion >= config.getMinWebViewVersion();\n        } catch (Exception ex) {\n            Logger.warn(\"Unable to get package info for 'com.android.webview'\" + ex.toString());\n        }\n\n        final int amazonFireMajorWebViewVersion = extractWebViewMajorVersion(pm, \"com.amazon.webview.chromium\");\n        if (amazonFireMajorWebViewVersion >= config.getMinWebViewVersion()) {\n            return true;\n        }\n\n        // Could not detect any webview, return false\n        return false;\n    }\n\n    private int extractWebViewMajorVersion(final PackageManager pm, final String webViewPackageName) {\n        try {\n            final PackageInfo info = InternalUtils.getPackageInfo(pm, webViewPackageName);\n            final String majorVersionStr = info.versionName.split(\"\\\\.\")[0];\n            final int majorVersion = Integer.parseInt(majorVersionStr);\n            return majorVersion;\n        } catch (Exception ex) {\n            Logger.warn(String.format(\"Unable to get package info for '%s' with err '%s'\", webViewPackageName, ex));\n        }\n        return 0;\n    }\n\n    public boolean launchIntent(Uri url) {\n        /*\n         * Give plugins the chance to handle the url\n         */\n        for (Map.Entry<String, PluginHandle> entry : plugins.entrySet()) {\n            Plugin plugin = entry.getValue().getInstance();\n            if (plugin != null) {\n                Boolean shouldOverrideLoad = plugin.shouldOverrideLoad(url);\n                if (shouldOverrideLoad != null) {\n                    return shouldOverrideLoad;\n                }\n            }\n        }\n\n        if (url.getScheme().equals(\"data\") || url.getScheme().equals(\"blob\")) {\n            return false;\n        }\n\n        Uri appUri = Uri.parse(appUrl);\n        if (\n            !(appUri.getHost().equals(url.getHost()) && url.getScheme().equals(appUri.getScheme())) &&\n            !appAllowNavigationMask.matches(url.getHost())\n        ) {\n            try {\n                Intent openIntent = new Intent(Intent.ACTION_VIEW, url);\n                getContext().startActivity(openIntent);\n            } catch (ActivityNotFoundException e) {\n                // TODO - trigger an event\n            }\n            return true;\n        }\n        return false;\n    }\n\n    private boolean isNewBinary() {\n        String versionCode = \"\";\n        String versionName = \"\";\n        SharedPreferences prefs = getContext().getSharedPreferences(\n            com.getcapacitor.plugin.WebView.WEBVIEW_PREFS_NAME,\n            Activity.MODE_PRIVATE\n        );\n        String lastVersionCode = prefs.getString(LAST_BINARY_VERSION_CODE, null);\n        String lastVersionName = prefs.getString(LAST_BINARY_VERSION_NAME, null);\n\n        try {\n            PackageManager pm = getContext().getPackageManager();\n            PackageInfo pInfo = InternalUtils.getPackageInfo(pm, getContext().getPackageName());\n            versionCode = Integer.toString((int) PackageInfoCompat.getLongVersionCode(pInfo));\n            versionName = pInfo.versionName;\n        } catch (Exception ex) {\n            Logger.error(\"Unable to get package info\", ex);\n        }\n\n        if (!versionCode.equals(lastVersionCode) || !versionName.equals(lastVersionName)) {\n            SharedPreferences.Editor editor = prefs.edit();\n            editor.putString(LAST_BINARY_VERSION_CODE, versionCode);\n            editor.putString(LAST_BINARY_VERSION_NAME, versionName);\n            editor.putString(com.getcapacitor.plugin.WebView.CAP_SERVER_PATH, \"\");\n            editor.apply();\n            return true;\n        }\n        return false;\n    }\n\n    public boolean isDeployDisabled() {\n        return preferences.getBoolean(\"DisableDeploy\", false);\n    }\n\n    public boolean shouldKeepRunning() {\n        return preferences.getBoolean(\"KeepRunning\", true);\n    }\n\n    public void handleAppUrlLoadError(Exception ex) {\n        if (ex instanceof SocketTimeoutException) {\n            Logger.error(\n                \"Unable to load app. Ensure the server is running at \" +\n                    appUrl +\n                    \", or modify the \" +\n                    \"appUrl setting in capacitor.config.json (make sure to npx cap copy after to commit changes).\",\n                ex\n            );\n        }\n    }\n\n    public boolean isDevMode() {\n        return (getActivity().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;\n    }\n\n    protected void setCordovaWebView(CordovaWebView cordovaWebView) {\n        this.cordovaWebView = cordovaWebView;\n    }\n\n    /**\n     * Get the Context for the App\n     * @return\n     */\n    public Context getContext() {\n        return this.context;\n    }\n\n    /**\n     * Get the activity for the app\n     * @return\n     */\n    public AppCompatActivity getActivity() {\n        return this.context;\n    }\n\n    /**\n     * Get the fragment for the app, if applicable. This will likely be null unless Capacitor\n     * is being used embedded in a Native Android app.\n     *\n     * @return The fragment containing the Capacitor WebView.\n     */\n    public Fragment getFragment() {\n        return this.fragment;\n    }\n\n    /**\n     * Get the core WebView under Capacitor's control\n     * @return\n     */\n    public WebView getWebView() {\n        return this.webView;\n    }\n\n    /**\n     * Get the URI that was used to launch the app (if any)\n     * @return\n     */\n    public Uri getIntentUri() {\n        return intentUri;\n    }\n\n    /**\n     * Get scheme that is used to serve content\n     * @return\n     */\n    public String getScheme() {\n        return this.config.getAndroidScheme();\n    }\n\n    /**\n     * Get host name that is used to serve content\n     * @return\n     */\n    public String getHost() {\n        return this.config.getHostname();\n    }\n\n    /**\n     * Get the server url that is used to serve content\n     * @return\n     */\n    public String getServerUrl() {\n        return this.config.getServerUrl();\n    }\n\n    public String getErrorUrl() {\n        String errorPath = this.config.getErrorPath();\n\n        if (errorPath != null && !errorPath.trim().isEmpty()) {\n            String authority = this.getHost();\n            String scheme = this.getScheme();\n\n            String localUrl = scheme + \"://\" + authority;\n\n            return localUrl + \"/\" + errorPath;\n        }\n\n        return null;\n    }\n\n    public String getAppUrl() {\n        return appUrl;\n    }\n\n    public CapConfig getConfig() {\n        return this.config;\n    }\n\n    public void reset() {\n        savedCalls = new HashMap<>();\n        for (PluginHandle handle : this.plugins.values()) {\n            handle.getInstance().removeAllListeners();\n        }\n    }\n\n    /**\n     * Initialize the WebView, setting required flags\n     */\n    @SuppressLint(\"SetJavaScriptEnabled\")\n    private void initWebView() {\n        WebSettings settings = webView.getSettings();\n        settings.setJavaScriptEnabled(true);\n        settings.setDomStorageEnabled(true);\n        settings.setGeolocationEnabled(true);\n        settings.setMediaPlaybackRequiresUserGesture(false);\n        settings.setJavaScriptCanOpenWindowsAutomatically(true);\n        if (this.config.isMixedContentAllowed()) {\n            settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);\n        }\n\n        String appendUserAgent = this.config.getAppendedUserAgentString();\n        if (appendUserAgent != null) {\n            String defaultUserAgent = settings.getUserAgentString();\n            settings.setUserAgentString(defaultUserAgent + \" \" + appendUserAgent);\n        }\n        String overrideUserAgent = this.config.getOverriddenUserAgentString();\n        if (overrideUserAgent != null) {\n            settings.setUserAgentString(overrideUserAgent);\n        }\n\n        String backgroundColor = this.config.getBackgroundColor();\n        try {\n            if (backgroundColor != null) {\n                webView.setBackgroundColor(WebColor.parseColor(backgroundColor));\n            }\n        } catch (IllegalArgumentException ex) {\n            Logger.debug(\"WebView background color not applied\");\n        }\n\n        settings.setDisplayZoomControls(false);\n        settings.setBuiltInZoomControls(this.config.isZoomableWebView());\n\n        if (config.isInitialFocus()) {\n            webView.requestFocusFromTouch();\n        }\n\n        WebView.setWebContentsDebuggingEnabled(this.config.isWebContentsDebuggingEnabled());\n\n        appUrlConfig = this.getServerUrl();\n        String authority = this.getHost();\n        authorities.add(authority);\n        String scheme = this.getScheme();\n\n        localUrl = scheme + \"://\" + authority;\n\n        if (appUrlConfig != null) {\n            try {\n                URL appUrlObject = new URL(appUrlConfig);\n                authorities.add(appUrlObject.getAuthority());\n                localUrl = appUrlObject.getProtocol() + \"://\" + appUrlObject.getAuthority();\n            } catch (Exception ex) {\n                Logger.error(\"Provided server url is invalid: \" + ex.getMessage());\n                return;\n            }\n            appUrl = appUrlConfig;\n        } else {\n            appUrl = localUrl;\n            // custom URL schemes requires path ending with /\n            if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !scheme.equals(CAPACITOR_HTTPS_SCHEME)) {\n                appUrl += \"/\";\n            }\n        }\n\n        String appUrlPath = this.config.getStartPath();\n        if (appUrlPath != null && !appUrlPath.trim().isEmpty()) {\n            appUrl += appUrlPath;\n        }\n    }\n\n    /**\n     * Register our core Plugin APIs\n     */\n    private void registerAllPlugins() {\n        this.registerPlugin(com.getcapacitor.plugin.CapacitorCookies.class);\n        this.registerPlugin(com.getcapacitor.plugin.WebView.class);\n        this.registerPlugin(com.getcapacitor.plugin.CapacitorHttp.class);\n        this.registerPlugin(com.getcapacitor.plugin.SystemBars.class);\n\n        for (Class<? extends Plugin> pluginClass : this.initialPlugins) {\n            this.registerPlugin(pluginClass);\n        }\n\n        for (Plugin plugin : pluginInstances) {\n            registerPluginInstance(plugin);\n        }\n    }\n\n    /**\n     * Register additional plugins\n     * @param pluginClasses the plugins to register\n     */\n    public void registerPlugins(Class<? extends Plugin>[] pluginClasses) {\n        for (Class<? extends Plugin> plugin : pluginClasses) {\n            this.registerPlugin(plugin);\n        }\n    }\n\n    public void registerPluginInstances(Plugin[] pluginInstances) {\n        for (Plugin plugin : pluginInstances) {\n            this.registerPluginInstance(plugin);\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private String getLegacyPluginName(Class<? extends Plugin> pluginClass) {\n        NativePlugin legacyPluginAnnotation = pluginClass.getAnnotation(NativePlugin.class);\n        if (legacyPluginAnnotation == null) {\n            Logger.error(\"Plugin doesn't have the @CapacitorPlugin annotation. Please add it\");\n            return null;\n        }\n\n        return legacyPluginAnnotation.name();\n    }\n\n    /**\n     * Register a plugin class\n     * @param pluginClass a class inheriting from Plugin\n     */\n    public void registerPlugin(Class<? extends Plugin> pluginClass) {\n        String pluginId = pluginId(pluginClass);\n        if (pluginId == null) return;\n\n        try {\n            this.plugins.put(pluginId, new PluginHandle(this, pluginClass));\n        } catch (InvalidPluginException ex) {\n            logInvalidPluginException(pluginClass);\n        } catch (PluginLoadException ex) {\n            logPluginLoadException(pluginClass, ex);\n        }\n    }\n\n    public void registerPluginInstance(Plugin plugin) {\n        Class<? extends Plugin> clazz = plugin.getClass();\n        String pluginId = pluginId(clazz);\n        if (pluginId == null) return;\n\n        try {\n            this.plugins.put(pluginId, new PluginHandle(this, plugin));\n        } catch (InvalidPluginException ex) {\n            logInvalidPluginException(clazz);\n        }\n    }\n\n    private String pluginId(Class<? extends Plugin> clazz) {\n        String pluginName = pluginName(clazz);\n        String pluginId = clazz.getSimpleName();\n        if (pluginName == null) return null;\n\n        if (!pluginName.equals(\"\")) {\n            pluginId = pluginName;\n        }\n        Logger.debug(\"Registering plugin instance: \" + pluginId);\n        return pluginId;\n    }\n\n    private String pluginName(Class<? extends Plugin> clazz) {\n        String pluginName;\n        CapacitorPlugin pluginAnnotation = clazz.getAnnotation(CapacitorPlugin.class);\n        if (pluginAnnotation == null) {\n            pluginName = this.getLegacyPluginName(clazz);\n        } else {\n            pluginName = pluginAnnotation.name();\n        }\n\n        return pluginName;\n    }\n\n    private void logInvalidPluginException(Class<? extends Plugin> clazz) {\n        Logger.error(\n            \"NativePlugin \" +\n                clazz.getName() +\n                \" is invalid. Ensure the @CapacitorPlugin annotation exists on the plugin class and\" +\n                \" the class extends Plugin\"\n        );\n    }\n\n    private void logPluginLoadException(Class<? extends Plugin> clazz, Exception ex) {\n        Logger.error(\"NativePlugin \" + clazz.getName() + \" failed to load\", ex);\n    }\n\n    public PluginHandle getPlugin(String pluginId) {\n        return this.plugins.get(pluginId);\n    }\n\n    /**\n     * Find the plugin handle that responds to the given request code. This will\n     * fire after certain Android OS intent results/permission checks/etc.\n     * @param requestCode\n     * @return\n     */\n    @Deprecated\n    @SuppressWarnings(\"deprecation\")\n    public PluginHandle getPluginWithRequestCode(int requestCode) {\n        for (PluginHandle handle : this.plugins.values()) {\n            int[] requestCodes;\n\n            CapacitorPlugin pluginAnnotation = handle.getPluginAnnotation();\n            if (pluginAnnotation == null) {\n                // Check for legacy plugin annotation, @NativePlugin\n                NativePlugin legacyPluginAnnotation = handle.getLegacyPluginAnnotation();\n                if (legacyPluginAnnotation == null) {\n                    continue;\n                }\n\n                if (legacyPluginAnnotation.permissionRequestCode() == requestCode) {\n                    return handle;\n                }\n\n                requestCodes = legacyPluginAnnotation.requestCodes();\n\n                for (int rc : requestCodes) {\n                    if (rc == requestCode) {\n                        return handle;\n                    }\n                }\n            } else {\n                requestCodes = pluginAnnotation.requestCodes();\n\n                for (int rc : requestCodes) {\n                    if (rc == requestCode) {\n                        return handle;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Call a method on a plugin.\n     * @param pluginId the plugin id to use to lookup the plugin handle\n     * @param methodName the name of the method to call\n     * @param call the call object to pass to the method\n     */\n    public void callPluginMethod(String pluginId, final String methodName, final PluginCall call) {\n        try {\n            final PluginHandle plugin = this.getPlugin(pluginId);\n\n            if (plugin == null) {\n                Logger.error(\"unable to find plugin : \" + pluginId);\n                call.errorCallback(\"unable to find plugin : \" + pluginId);\n                return;\n            }\n\n            if (Logger.shouldLog()) {\n                Logger.verbose(\n                    \"callback: \" +\n                        call.getCallbackId() +\n                        \", pluginId: \" +\n                        plugin.getId() +\n                        \", methodName: \" +\n                        methodName +\n                        \", methodData: \" +\n                        call.getData().toString()\n                );\n            }\n\n            Runnable currentThreadTask = () -> {\n                try {\n                    plugin.invoke(methodName, call);\n\n                    if (call.isKeptAlive()) {\n                        saveCall(call);\n                    }\n                } catch (PluginLoadException | InvalidPluginMethodException ex) {\n                    Logger.error(\"Unable to execute plugin method\", ex);\n                } catch (Exception ex) {\n                    Logger.error(\"Serious error executing plugin\", ex);\n                    throw new RuntimeException(ex);\n                }\n            };\n\n            taskHandler.post(currentThreadTask);\n        } catch (Exception ex) {\n            Logger.error(Logger.tags(\"callPluginMethod\"), \"error : \" + ex, null);\n            call.errorCallback(ex.toString());\n        }\n    }\n\n    /**\n     * Evaluate JavaScript in the web view. This method\n     * executes on the main thread automatically.\n     * @param js the JS to execute\n     * @param callback an optional ValueCallback that will synchronously receive a value\n     *                 after calling the JS\n     */\n    public void eval(final String js, final ValueCallback<String> callback) {\n        Handler mainHandler = new Handler(context.getMainLooper());\n        mainHandler.post(() -> webView.evaluateJavascript(js, callback));\n    }\n\n    public void logToJs(final String message, final String level) {\n        eval(\"window.Capacitor.logJs(\\\"\" + message + \"\\\", \\\"\" + level + \"\\\")\", null);\n    }\n\n    public void logToJs(final String message) {\n        logToJs(message, \"log\");\n    }\n\n    public void triggerJSEvent(final String eventName, final String target) {\n        eval(\"window.Capacitor.triggerEvent(\\\"\" + eventName + \"\\\", \\\"\" + target + \"\\\")\", (s) -> {});\n    }\n\n    public void triggerJSEvent(final String eventName, final String target, final String data) {\n        eval(\"window.Capacitor.triggerEvent(\\\"\" + eventName + \"\\\", \\\"\" + target + \"\\\", \" + data + \")\", (s) -> {});\n    }\n\n    public void triggerWindowJSEvent(final String eventName) {\n        this.triggerJSEvent(eventName, \"window\");\n    }\n\n    public void triggerWindowJSEvent(final String eventName, final String data) {\n        this.triggerJSEvent(eventName, \"window\", data);\n    }\n\n    public void triggerDocumentJSEvent(final String eventName) {\n        this.triggerJSEvent(eventName, \"document\");\n    }\n\n    public void triggerDocumentJSEvent(final String eventName, final String data) {\n        this.triggerJSEvent(eventName, \"document\", data);\n    }\n\n    public void execute(Runnable runnable) {\n        taskHandler.post(runnable);\n    }\n\n    public void executeOnMainThread(Runnable runnable) {\n        Handler mainHandler = new Handler(context.getMainLooper());\n\n        mainHandler.post(runnable);\n    }\n\n    /**\n     * Retain a call between plugin invocations\n     * @param call\n     */\n    public void saveCall(PluginCall call) {\n        this.savedCalls.put(call.getCallbackId(), call);\n    }\n\n    /**\n     * Get a retained plugin call\n     * @param callbackId the callbackId to use to lookup the call with\n     * @return the stored call\n     */\n    public PluginCall getSavedCall(String callbackId) {\n        if (callbackId == null) {\n            return null;\n        }\n\n        return this.savedCalls.get(callbackId);\n    }\n\n    PluginCall getPluginCallForLastActivity() {\n        PluginCall pluginCallForLastActivity = this.pluginCallForLastActivity;\n        this.pluginCallForLastActivity = null;\n        return pluginCallForLastActivity;\n    }\n\n    void setPluginCallForLastActivity(PluginCall pluginCallForLastActivity) {\n        this.pluginCallForLastActivity = pluginCallForLastActivity;\n    }\n\n    /**\n     * Release a retained call\n     * @param call a call to release\n     */\n    public void releaseCall(PluginCall call) {\n        releaseCall(call.getCallbackId());\n    }\n\n    /**\n     * Release a retained call by its ID\n     * @param callbackId an ID of a callback to release\n     */\n    public void releaseCall(String callbackId) {\n        this.savedCalls.remove(callbackId);\n    }\n\n    /**\n     * Removes the earliest saved call prior to a permissions request for a given plugin and\n     * returns it.\n     *\n     * @return The saved plugin call\n     */\n    protected PluginCall getPermissionCall(String pluginId) {\n        LinkedList<String> permissionCallIds = this.savedPermissionCallIds.get(pluginId);\n        String savedCallId = null;\n        if (permissionCallIds != null) {\n            savedCallId = permissionCallIds.poll();\n        }\n\n        return getSavedCall(savedCallId);\n    }\n\n    /**\n     * Save a call to be retrieved after requesting permissions. Calls are saved in order.\n     *\n     * @param call The plugin call to save.\n     */\n    protected void savePermissionCall(PluginCall call) {\n        if (call != null) {\n            if (!savedPermissionCallIds.containsKey(call.getPluginId())) {\n                savedPermissionCallIds.put(call.getPluginId(), new LinkedList<>());\n            }\n\n            savedPermissionCallIds.get(call.getPluginId()).add(call.getCallbackId());\n            saveCall(call);\n        }\n    }\n\n    /**\n     * Register an Activity Result Launcher to the containing Fragment or Activity.\n     *\n     * @param contract A contract specifying that an activity can be called with an input of\n     *                 type I and produce an output of type O.\n     * @param callback The callback run on Activity Result.\n     * @return A registered Activity Result Launcher.\n     */\n    public <I, O> ActivityResultLauncher<I> registerForActivityResult(\n        @NonNull final ActivityResultContract<I, O> contract,\n        @NonNull final ActivityResultCallback<O> callback\n    ) {\n        if (fragment != null) {\n            return fragment.registerForActivityResult(contract, callback);\n        } else {\n            return context.registerForActivityResult(contract, callback);\n        }\n    }\n\n    /**\n     * Build the JSInjector that will be used to inject JS into files served to the app,\n     * to ensure that Capacitor's JS and the JS for all the plugins is loaded each time.\n     */\n    private JSInjector getJSInjector() {\n        try {\n            String globalJS = JSExport.getGlobalJS(context, config.isLoggingEnabled(), isDevMode());\n            String bridgeJS = JSExport.getBridgeJS(context);\n            String pluginJS = JSExport.getPluginJS(plugins.values());\n            String cordovaJS = JSExport.getCordovaJS(context);\n            String cordovaPluginsJS = JSExport.getCordovaPluginJS(context);\n            String cordovaPluginsFileJS = JSExport.getCordovaPluginsFileJS(context);\n            String localUrlJS = \"window.WEBVIEW_SERVER_URL = '\" + localUrl + \"';\";\n            String miscJS = JSExport.getMiscFileJS(miscJSFileInjections, context);\n\n            miscJSFileInjections = new ArrayList<>();\n            canInjectJS = false;\n\n            return new JSInjector(globalJS, bridgeJS, pluginJS, cordovaJS, cordovaPluginsJS, cordovaPluginsFileJS, localUrlJS, miscJS);\n        } catch (Exception ex) {\n            Logger.error(\"Unable to export Capacitor JS. App will not function!\", ex);\n        }\n        return null;\n    }\n\n    /**\n     * Inject JavaScript from an external file before the WebView loads.\n     * @param path relative to public folder\n     */\n    public void injectScriptBeforeLoad(String path) {\n        if (canInjectJS) {\n            miscJSFileInjections.add(path);\n        }\n    }\n\n    /**\n     * Restore any saved bundle state data\n     * @param savedInstanceState\n     */\n    public void restoreInstanceState(Bundle savedInstanceState) {\n        String lastPluginId = savedInstanceState.getString(BUNDLE_LAST_PLUGIN_ID_KEY);\n        String lastPluginCallMethod = savedInstanceState.getString(BUNDLE_LAST_PLUGIN_CALL_METHOD_NAME_KEY);\n        String lastOptionsJson = savedInstanceState.getString(BUNDLE_PLUGIN_CALL_OPTIONS_SAVED_KEY);\n\n        if (lastPluginId != null) {\n            // If we have JSON blob saved, create a new plugin call with the original options\n            if (lastOptionsJson != null) {\n                try {\n                    JSObject options = new JSObject(lastOptionsJson);\n\n                    pluginCallForLastActivity = new PluginCall(\n                        msgHandler,\n                        lastPluginId,\n                        PluginCall.CALLBACK_ID_DANGLING,\n                        lastPluginCallMethod,\n                        options\n                    );\n                } catch (JSONException ex) {\n                    Logger.error(\"Unable to restore plugin call, unable to parse persisted JSON object\", ex);\n                }\n            }\n\n            // Let the plugin restore any state it needs\n            Bundle bundleData = savedInstanceState.getBundle(BUNDLE_PLUGIN_CALL_BUNDLE_KEY);\n            PluginHandle lastPlugin = getPlugin(lastPluginId);\n            if (bundleData != null && lastPlugin != null) {\n                lastPlugin.getInstance().restoreState(bundleData);\n            } else {\n                Logger.error(\"Unable to restore last plugin call\");\n            }\n        }\n    }\n\n    public void saveInstanceState(Bundle outState) {\n        Logger.debug(\"Saving instance state!\");\n\n        // If there was a last PluginCall for a started activity, we need to\n        // persist it so we can load it again in case our app gets terminated\n        if (pluginCallForLastActivity != null) {\n            PluginCall call = pluginCallForLastActivity;\n            PluginHandle handle = getPlugin(call.getPluginId());\n\n            if (handle != null) {\n                Bundle bundle = handle.getInstance().saveInstanceState();\n                if (bundle != null) {\n                    outState.putString(BUNDLE_LAST_PLUGIN_ID_KEY, call.getPluginId());\n                    outState.putString(BUNDLE_LAST_PLUGIN_CALL_METHOD_NAME_KEY, call.getMethodName());\n                    outState.putString(BUNDLE_PLUGIN_CALL_OPTIONS_SAVED_KEY, call.getData().toString());\n                    outState.putBundle(BUNDLE_PLUGIN_CALL_BUNDLE_KEY, bundle);\n                } else {\n                    Logger.error(\"Couldn't save last \" + call.getPluginId() + \"'s Plugin \" + call.getMethodName() + \" call\");\n                }\n            }\n        }\n    }\n\n    @Deprecated\n    @SuppressWarnings(\"deprecation\")\n    public void startActivityForPluginWithResult(PluginCall call, Intent intent, int requestCode) {\n        Logger.debug(\"Starting activity for result\");\n\n        pluginCallForLastActivity = call;\n\n        getActivity().startActivityForResult(intent, requestCode);\n    }\n\n    /**\n     * Check for legacy Capacitor or Cordova plugins that may have registered to handle a permission\n     * request, and handle them if so. If not handled, false is returned.\n     *\n     * @param requestCode the code that was requested\n     * @param permissions the permissions requested\n     * @param grantResults the set of granted/denied permissions\n     * @return true if permission code was handled by a plugin explicitly, false if not\n     */\n    @SuppressWarnings(\"deprecation\")\n    boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {\n        PluginHandle plugin = getPluginWithRequestCode(requestCode);\n\n        if (plugin == null) {\n            boolean permissionHandled = false;\n            Logger.debug(\"Unable to find a Capacitor plugin to handle permission requestCode, trying Cordova plugins \" + requestCode);\n            try {\n                permissionHandled = cordovaInterface.handlePermissionResult(requestCode, permissions, grantResults);\n            } catch (JSONException e) {\n                Logger.debug(\"Error on Cordova plugin permissions request \" + e.getMessage());\n            }\n            return permissionHandled;\n        }\n\n        // Call deprecated method if using deprecated NativePlugin annotation\n        if (plugin.getPluginAnnotation() == null) {\n            plugin.getInstance().handleRequestPermissionsResult(requestCode, permissions, grantResults);\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Saves permission states and rejects if permissions were not correctly defined in\n     * the AndroidManifest.xml file.\n     *\n     * @param plugin\n     * @param savedCall\n     * @param permissions\n     * @return true if permissions were saved and defined correctly, false if not\n     */\n    protected boolean validatePermissions(Plugin plugin, PluginCall savedCall, Map<String, Boolean> permissions) {\n        SharedPreferences prefs = getContext().getSharedPreferences(PERMISSION_PREFS_NAME, Activity.MODE_PRIVATE);\n\n        for (Map.Entry<String, Boolean> permission : permissions.entrySet()) {\n            String permString = permission.getKey();\n            boolean isGranted = permission.getValue();\n\n            if (isGranted) {\n                // Permission granted. If previously denied, remove cached state\n                String state = prefs.getString(permString, null);\n\n                if (state != null) {\n                    SharedPreferences.Editor editor = prefs.edit();\n                    editor.remove(permString);\n                    editor.apply();\n                }\n            } else {\n                SharedPreferences.Editor editor = prefs.edit();\n\n                if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permString)) {\n                    // Permission denied, can prompt again with rationale\n                    editor.putString(permString, PermissionState.PROMPT_WITH_RATIONALE.toString());\n                } else {\n                    // Permission denied permanently, store this state for future reference\n                    editor.putString(permString, PermissionState.DENIED.toString());\n                }\n\n                editor.apply();\n            }\n        }\n\n        String[] permStrings = permissions.keySet().toArray(new String[0]);\n\n        if (!PermissionHelper.hasDefinedPermissions(getContext(), permStrings)) {\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"Missing the following permissions in AndroidManifest.xml:\\n\");\n            String[] missing = PermissionHelper.getUndefinedPermissions(getContext(), permStrings);\n            for (String perm : missing) {\n                builder.append(perm + \"\\n\");\n            }\n            savedCall.reject(builder.toString());\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Helper to check all permissions and see the current states of each permission.\n     *\n     * @since 3.0.0\n     * @return A mapping of permission aliases to the associated granted status.\n     */\n    protected Map<String, PermissionState> getPermissionStates(Plugin plugin) {\n        Map<String, PermissionState> permissionsResults = new HashMap<>();\n        CapacitorPlugin annotation = plugin.getPluginHandle().getPluginAnnotation();\n        for (Permission perm : annotation.permissions()) {\n            // If a permission is defined with no permission constants, return GRANTED for it.\n            // Otherwise, get its true state.\n            if (perm.strings().length == 0 || (perm.strings().length == 1 && perm.strings()[0].isEmpty())) {\n                String key = perm.alias();\n                if (!key.isEmpty()) {\n                    PermissionState existingResult = permissionsResults.get(key);\n\n                    // auto set permission state to GRANTED if the alias is empty.\n                    if (existingResult == null) {\n                        permissionsResults.put(key, PermissionState.GRANTED);\n                    }\n                }\n            } else {\n                for (String permString : perm.strings()) {\n                    String key = perm.alias().isEmpty() ? permString : perm.alias();\n                    PermissionState permissionStatus;\n                    if (ActivityCompat.checkSelfPermission(this.getContext(), permString) == PackageManager.PERMISSION_GRANTED) {\n                        permissionStatus = PermissionState.GRANTED;\n                    } else {\n                        permissionStatus = PermissionState.PROMPT;\n\n                        // Check if there is a cached permission state for the \"Never ask again\" state\n                        SharedPreferences prefs = getContext().getSharedPreferences(PERMISSION_PREFS_NAME, Activity.MODE_PRIVATE);\n                        String state = prefs.getString(permString, null);\n\n                        if (state != null) {\n                            permissionStatus = PermissionState.byState(state);\n                        }\n                    }\n\n                    PermissionState existingResult = permissionsResults.get(key);\n\n                    // multiple permissions with the same alias must all be true, otherwise all false.\n                    if (existingResult == null || existingResult == PermissionState.GRANTED) {\n                        permissionsResults.put(key, permissionStatus);\n                    }\n                }\n            }\n        }\n\n        return permissionsResults;\n    }\n\n    /**\n     * Handle an activity result and pass it to a plugin that has indicated it wants to\n     * handle the result.\n     * @param requestCode\n     * @param resultCode\n     * @param data\n     */\n    @SuppressWarnings(\"deprecation\")\n    boolean onActivityResult(int requestCode, int resultCode, Intent data) {\n        PluginHandle plugin = getPluginWithRequestCode(requestCode);\n\n        if (plugin == null || plugin.getInstance() == null) {\n            Logger.debug(\"Unable to find a Capacitor plugin to handle requestCode, trying Cordova plugins \" + requestCode);\n            return cordovaInterface.onActivityResult(requestCode, resultCode, data);\n        }\n\n        // deprecated, to be removed\n        PluginCall lastCall = plugin.getInstance().getSavedCall();\n\n        // If we don't have a saved last call (because our app was killed and restarted, for example),\n        // Then we should see if we have any saved plugin call information and generate a new,\n        // \"dangling\" plugin call (a plugin call that doesn't have a corresponding web callback)\n        // and then send that to the plugin\n        if (lastCall == null && pluginCallForLastActivity != null) {\n            plugin.getInstance().saveCall(pluginCallForLastActivity);\n        }\n\n        plugin.getInstance().handleOnActivityResult(requestCode, resultCode, data);\n\n        // Clear the plugin call we may have re-hydrated on app launch\n        pluginCallForLastActivity = null;\n\n        return true;\n    }\n\n    /**\n     * Handle an onNewIntent lifecycle event and notify the plugins\n     * @param intent\n     */\n    public void onNewIntent(Intent intent) {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnNewIntent(intent);\n        }\n\n        if (cordovaWebView != null) {\n            cordovaWebView.onNewIntent(intent);\n        }\n    }\n\n    /**\n     * Handle an onConfigurationChanged event and notify the plugins\n     * @param newConfig\n     */\n    public void onConfigurationChanged(Configuration newConfig) {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnConfigurationChanged(newConfig);\n        }\n    }\n\n    /**\n     * Handle onRestart lifecycle event and notify the plugins\n     */\n    public void onRestart() {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnRestart();\n        }\n    }\n\n    /**\n     * Handle onStart lifecycle event and notify the plugins\n     */\n    public void onStart() {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnStart();\n        }\n\n        if (cordovaWebView != null) {\n            cordovaWebView.handleStart();\n        }\n    }\n\n    /**\n     * Handle onResume lifecycle event and notify the plugins\n     */\n    public void onResume() {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnResume();\n        }\n\n        if (cordovaWebView != null) {\n            cordovaWebView.handleResume(this.shouldKeepRunning());\n        }\n    }\n\n    /**\n     * Handle onPause lifecycle event and notify the plugins\n     */\n    public void onPause() {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnPause();\n        }\n\n        if (cordovaWebView != null) {\n            boolean keepRunning = this.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null;\n            cordovaWebView.handlePause(keepRunning);\n        }\n    }\n\n    /**\n     * Handle onStop lifecycle event and notify the plugins\n     */\n    public void onStop() {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnStop();\n        }\n\n        if (cordovaWebView != null) {\n            cordovaWebView.handleStop();\n        }\n    }\n\n    /**\n     * Handle onDestroy lifecycle event and notify the plugins\n     */\n    public void onDestroy() {\n        for (PluginHandle plugin : plugins.values()) {\n            plugin.getInstance().handleOnDestroy();\n        }\n\n        handlerThread.quitSafely();\n\n        if (cordovaWebView != null) {\n            cordovaWebView.handleDestroy();\n        }\n    }\n\n    /**\n     * Handle onDetachedFromWindow lifecycle event\n     */\n    public void onDetachedFromWindow() {\n        webView.removeAllViews();\n        webView.destroy();\n    }\n\n    public String getServerBasePath() {\n        return this.localServer.getBasePath();\n    }\n\n    /**\n     * Tell the local server to load files from the given\n     * file path instead of the assets path.\n     * @param path\n     */\n    public void setServerBasePath(String path) {\n        localServer.hostFiles(path);\n        webView.post(() -> webView.loadUrl(appUrl));\n    }\n\n    /**\n     * Tell the local server to load files from the given\n     * asset path.\n     * @param path\n     */\n    public void setServerAssetPath(String path) {\n        localServer.hostAssets(path);\n        webView.post(() -> webView.loadUrl(appUrl));\n    }\n\n    /**\n     * Reload the WebView\n     */\n    public void reload() {\n        webView.post(() -> webView.loadUrl(appUrl));\n    }\n\n    public String getLocalUrl() {\n        return localUrl;\n    }\n\n    public WebViewLocalServer getLocalServer() {\n        return localServer;\n    }\n\n    public HostMask getAppAllowNavigationMask() {\n        return appAllowNavigationMask;\n    }\n\n    public Set<String> getAllowedOriginRules() {\n        return allowedOriginRules;\n    }\n\n    public BridgeWebViewClient getWebViewClient() {\n        return this.webViewClient;\n    }\n\n    public void setWebViewClient(BridgeWebViewClient client) {\n        this.webViewClient = client;\n        webView.setWebViewClient(client);\n    }\n\n    List<WebViewListener> getWebViewListeners() {\n        return webViewListeners;\n    }\n\n    void setWebViewListeners(List<WebViewListener> webViewListeners) {\n        this.webViewListeners = webViewListeners;\n    }\n\n    RouteProcessor getRouteProcessor() {\n        return routeProcessor;\n    }\n\n    void setRouteProcessor(RouteProcessor routeProcessor) {\n        this.routeProcessor = routeProcessor;\n    }\n\n    ServerPath getServerPath() {\n        return serverPath;\n    }\n\n    /**\n     * Add a listener that the WebViewClient can trigger on certain events.\n     * @param webViewListener A {@link WebViewListener} to add.\n     */\n    public void addWebViewListener(WebViewListener webViewListener) {\n        webViewListeners.add(webViewListener);\n    }\n\n    /**\n     * Remove a listener that the WebViewClient triggers on certain events.\n     * @param webViewListener A {@link WebViewListener} to remove.\n     */\n    public void removeWebViewListener(WebViewListener webViewListener) {\n        webViewListeners.remove(webViewListener);\n    }\n\n    public static class Builder {\n\n        private Bundle instanceState = null;\n        private CapConfig config = null;\n        private List<Class<? extends Plugin>> plugins = new ArrayList<>();\n        private List<Plugin> pluginInstances = new ArrayList<>();\n        private AppCompatActivity activity;\n        private Fragment fragment;\n        private RouteProcessor routeProcessor;\n        private final List<WebViewListener> webViewListeners = new ArrayList<>();\n        private ServerPath serverPath;\n\n        public Builder(AppCompatActivity activity) {\n            this.activity = activity;\n        }\n\n        public Builder(Fragment fragment) {\n            this.activity = (AppCompatActivity) fragment.getActivity();\n            this.fragment = fragment;\n        }\n\n        public Builder setInstanceState(Bundle instanceState) {\n            this.instanceState = instanceState;\n            return this;\n        }\n\n        public Builder setConfig(CapConfig config) {\n            this.config = config;\n            return this;\n        }\n\n        public Builder setPlugins(List<Class<? extends Plugin>> plugins) {\n            this.plugins = plugins;\n            return this;\n        }\n\n        public Builder addPlugin(Class<? extends Plugin> plugin) {\n            this.plugins.add(plugin);\n            return this;\n        }\n\n        public Builder addPlugins(List<Class<? extends Plugin>> plugins) {\n            for (Class<? extends Plugin> cls : plugins) {\n                this.addPlugin(cls);\n            }\n\n            return this;\n        }\n\n        public Builder addPluginInstance(Plugin plugin) {\n            this.pluginInstances.add(plugin);\n            return this;\n        }\n\n        public Builder addPluginInstances(List<Plugin> plugins) {\n            this.pluginInstances.addAll(plugins);\n            return this;\n        }\n\n        public Builder addWebViewListener(WebViewListener webViewListener) {\n            webViewListeners.add(webViewListener);\n            return this;\n        }\n\n        public Builder addWebViewListeners(List<WebViewListener> webViewListeners) {\n            for (WebViewListener listener : webViewListeners) {\n                this.addWebViewListener(listener);\n            }\n\n            return this;\n        }\n\n        public Builder setRouteProcessor(RouteProcessor routeProcessor) {\n            this.routeProcessor = routeProcessor;\n            return this;\n        }\n\n        public Builder setServerPath(ServerPath serverPath) {\n            this.serverPath = serverPath;\n            return this;\n        }\n\n        public Bridge create() {\n            // Cordova initialization\n            ConfigXmlParser parser = new ConfigXmlParser();\n            parser.parse(activity.getApplicationContext());\n            CordovaPreferences preferences = parser.getPreferences();\n            preferences.setPreferencesBundle(activity.getIntent().getExtras());\n            List<PluginEntry> pluginEntries = parser.getPluginEntries();\n\n            MockCordovaInterfaceImpl cordovaInterface = new MockCordovaInterfaceImpl(activity);\n            if (instanceState != null) {\n                cordovaInterface.restoreInstanceState(instanceState);\n            }\n\n            WebView webView = this.fragment != null ? fragment.getView().findViewById(R.id.webview) : activity.findViewById(R.id.webview);\n            MockCordovaWebViewImpl mockWebView = new MockCordovaWebViewImpl(activity.getApplicationContext());\n            mockWebView.init(cordovaInterface, pluginEntries, preferences, webView);\n            PluginManager pluginManager = mockWebView.getPluginManager();\n            cordovaInterface.onCordovaInit(pluginManager);\n\n            // Bridge initialization\n            Bridge bridge = new Bridge(\n                activity,\n                serverPath,\n                fragment,\n                webView,\n                plugins,\n                pluginInstances,\n                cordovaInterface,\n                pluginManager,\n                preferences,\n                config\n            );\n\n            if (webView instanceof CapacitorWebView capacitorWebView) {\n                capacitorWebView.setBridge(bridge);\n            }\n\n            bridge.setCordovaWebView(mockWebView);\n            bridge.setWebViewListeners(webViewListeners);\n            bridge.setRouteProcessor(routeProcessor);\n\n            if (instanceState != null) {\n                bridge.restoreInstanceState(instanceState);\n            }\n\n            return bridge;\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java",
    "content": "package com.getcapacitor;\n\nimport android.content.Intent;\nimport android.content.res.Configuration;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport com.getcapacitor.android.R;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class BridgeActivity extends AppCompatActivity {\n\n    protected Bridge bridge;\n    protected boolean keepRunning = true;\n    protected CapConfig config;\n\n    protected int activityDepth = 0;\n    protected List<Class<? extends Plugin>> initialPlugins = new ArrayList<>();\n    protected final Bridge.Builder bridgeBuilder = new Bridge.Builder(this);\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        bridgeBuilder.setInstanceState(savedInstanceState);\n        getApplication().setTheme(R.style.AppTheme_NoActionBar);\n        setTheme(R.style.AppTheme_NoActionBar);\n        try {\n            setContentView(R.layout.capacitor_bridge_layout_main);\n        } catch (Exception ex) {\n            setContentView(R.layout.no_webview);\n            return;\n        }\n\n        PluginManager loader = new PluginManager(getAssets());\n\n        try {\n            bridgeBuilder.addPlugins(loader.loadPluginClasses());\n        } catch (PluginLoadException ex) {\n            Logger.error(\"Error loading plugins.\", ex);\n        }\n\n        this.load();\n    }\n\n    protected void load() {\n        Logger.debug(\"Starting BridgeActivity\");\n\n        bridge = bridgeBuilder.addPlugins(initialPlugins).setConfig(config).create();\n\n        this.keepRunning = bridge.shouldKeepRunning();\n        this.onNewIntent(getIntent());\n    }\n\n    public void registerPlugin(Class<? extends Plugin> plugin) {\n        bridgeBuilder.addPlugin(plugin);\n    }\n\n    public void registerPlugins(List<Class<? extends Plugin>> plugins) {\n        bridgeBuilder.addPlugins(plugins);\n    }\n\n    public Bridge getBridge() {\n        return this.bridge;\n    }\n\n    @Override\n    public void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        if (bridge != null) {\n            bridge.saveInstanceState(outState);\n        }\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        activityDepth++;\n        if (this.bridge != null) {\n            this.bridge.onStart();\n            Logger.debug(\"App started\");\n        }\n    }\n\n    @Override\n    public void onRestart() {\n        super.onRestart();\n        if (this.bridge != null) {\n            this.bridge.onRestart();\n            Logger.debug(\"App restarted\");\n        }\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        if (bridge != null) {\n            bridge.getApp().fireStatusChange(true);\n            this.bridge.onResume();\n            Logger.debug(\"App resumed\");\n        }\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n        if (bridge != null) {\n            this.bridge.onPause();\n            Logger.debug(\"App paused\");\n        }\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n        if (bridge != null) {\n            activityDepth = Math.max(0, activityDepth - 1);\n            if (activityDepth == 0) {\n                bridge.getApp().fireStatusChange(false);\n            }\n\n            this.bridge.onStop();\n            Logger.debug(\"App stopped\");\n        }\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (this.bridge != null) {\n            this.bridge.onDestroy();\n            Logger.debug(\"App destroyed\");\n        }\n    }\n\n    @Override\n    public void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        if (this.bridge != null) {\n            this.bridge.onDetachedFromWindow();\n        }\n    }\n\n    /**\n     * Handles permission request results.\n     *\n     * Capacitor is backwards compatible such that plugins using legacy permission request codes\n     * may coexist with plugins using the AndroidX Activity v1.2 permission callback flow introduced\n     * in Capacitor 3.0.\n     *\n     * In this method, plugins are checked first for ownership of the legacy permission request code.\n     * If the {@link Bridge#onRequestPermissionsResult(int, String[], int[])} method indicates it has\n     * handled the permission, then the permission callback will be considered complete. Otherwise,\n     * the permission will be handled using the AndroidX Activity flow.\n     *\n     * @param requestCode the request code associated with the permission request\n     * @param permissions the Android permission strings requested\n     * @param grantResults the status result of the permission request\n     */\n    @Override\n    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {\n        if (this.bridge == null) {\n            return;\n        }\n\n        if (!bridge.onRequestPermissionsResult(requestCode, permissions, grantResults)) {\n            super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n        }\n    }\n\n    /**\n     * Handles activity results.\n     *\n     * Capacitor is backwards compatible such that plugins using legacy activity result codes\n     * may coexist with plugins using the AndroidX Activity v1.2 activity callback flow introduced\n     * in Capacitor 3.0.\n     *\n     * In this method, plugins are checked first for ownership of the legacy request code. If the\n     * {@link Bridge#onActivityResult(int, int, Intent)} method indicates it has handled the activity\n     * result, then the callback will be considered complete. Otherwise, the result will be handled\n     * using the AndroidX Activiy flow.\n     *\n     * @param requestCode the request code associated with the activity result\n     * @param resultCode the result code\n     * @param data any data included with the activity result\n     */\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        if (this.bridge == null) {\n            return;\n        }\n\n        if (!bridge.onActivityResult(requestCode, resultCode, data)) {\n            super.onActivityResult(requestCode, resultCode, data);\n        }\n    }\n\n    @Override\n    protected void onNewIntent(Intent intent) {\n        super.onNewIntent(intent);\n\n        if (this.bridge == null || intent == null) {\n            return;\n        }\n\n        this.bridge.onNewIntent(intent);\n    }\n\n    @Override\n    public void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n\n        if (this.bridge == null) {\n            return;\n        }\n\n        this.bridge.onConfigurationChanged(newConfig);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java",
    "content": "package com.getcapacitor;\n\nimport android.Manifest;\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.ActivityNotFoundException;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.provider.MediaStore;\nimport android.view.View;\nimport android.webkit.ConsoleMessage;\nimport android.webkit.GeolocationPermissions;\nimport android.webkit.JsPromptResult;\nimport android.webkit.JsResult;\nimport android.webkit.MimeTypeMap;\nimport android.webkit.PermissionRequest;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebView;\nimport android.widget.EditText;\nimport androidx.activity.result.ActivityResult;\nimport androidx.activity.result.ActivityResultCallback;\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts;\nimport androidx.core.content.FileProvider;\nimport com.getcapacitor.util.PermissionHelper;\nimport java.io.File;\nimport java.io.IOException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\n/**\n * Custom WebChromeClient handler, required for showing dialogs, confirms, etc. in our\n * WebView instance.\n */\npublic class BridgeWebChromeClient extends WebChromeClient {\n\n    private interface PermissionListener {\n        void onPermissionSelect(Boolean isGranted);\n    }\n\n    private interface ActivityResultListener {\n        void onActivityResult(ActivityResult result);\n    }\n\n    private ActivityResultLauncher permissionLauncher;\n    private ActivityResultLauncher activityLauncher;\n    private PermissionListener permissionListener;\n    private ActivityResultListener activityListener;\n\n    private Bridge bridge;\n\n    public BridgeWebChromeClient(Bridge bridge) {\n        this.bridge = bridge;\n\n        ActivityResultCallback<Map<String, Boolean>> permissionCallback = (Map<String, Boolean> isGranted) -> {\n            if (permissionListener != null) {\n                boolean granted = true;\n                for (Map.Entry<String, Boolean> permission : isGranted.entrySet()) {\n                    if (!permission.getValue()) granted = false;\n                }\n                permissionListener.onPermissionSelect(granted);\n            }\n        };\n\n        permissionLauncher = bridge.registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), permissionCallback);\n        activityLauncher = bridge.registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), (result) -> {\n            if (activityListener != null) {\n                activityListener.onActivityResult(result);\n            }\n        });\n    }\n\n    /**\n     * Render web content in `view`.\n     *\n     * Both this method and {@link #onHideCustomView()} are required for\n     * rendering web content in full screen.\n     *\n     * @see <a href=\"https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback)\">onShowCustomView() docs</a>\n     */\n    @Override\n    public void onShowCustomView(View view, CustomViewCallback callback) {\n        callback.onCustomViewHidden();\n        super.onShowCustomView(view, callback);\n    }\n\n    /**\n     * Render web content in the original Web View again.\n     *\n     * Do not remove this method--@see #onShowCustomView(View, CustomViewCallback).\n     */\n    @Override\n    public void onHideCustomView() {\n        super.onHideCustomView();\n    }\n\n    @Override\n    public void onPermissionRequest(final PermissionRequest request) {\n        List<String> permissionList = new ArrayList<>();\n        if (Arrays.asList(request.getResources()).contains(\"android.webkit.resource.VIDEO_CAPTURE\")) {\n            permissionList.add(Manifest.permission.CAMERA);\n        }\n        if (Arrays.asList(request.getResources()).contains(\"android.webkit.resource.AUDIO_CAPTURE\")) {\n            permissionList.add(Manifest.permission.MODIFY_AUDIO_SETTINGS);\n            permissionList.add(Manifest.permission.RECORD_AUDIO);\n        }\n        if (!permissionList.isEmpty()) {\n            String[] permissions = permissionList.toArray(new String[0]);\n            permissionListener = (isGranted) -> {\n                if (isGranted) {\n                    request.grant(request.getResources());\n                } else {\n                    request.deny();\n                }\n            };\n            permissionLauncher.launch(permissions);\n        } else {\n            request.grant(request.getResources());\n        }\n    }\n\n    /**\n     * Show the browser alert modal\n     * @param view\n     * @param url\n     * @param message\n     * @param result\n     * @return\n     */\n    @Override\n    public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {\n        if (bridge.getActivity().isFinishing()) {\n            return true;\n        }\n\n        AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());\n        builder\n            .setMessage(message)\n            .setPositiveButton(\"OK\", (dialog, buttonIndex) -> {\n                dialog.dismiss();\n                result.confirm();\n            })\n            .setOnCancelListener((dialog) -> {\n                dialog.dismiss();\n                result.cancel();\n            });\n\n        AlertDialog dialog = builder.create();\n\n        dialog.show();\n\n        return true;\n    }\n\n    /**\n     * Show the browser confirm modal\n     * @param view\n     * @param url\n     * @param message\n     * @param result\n     * @return\n     */\n    @Override\n    public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {\n        if (bridge.getActivity().isFinishing()) {\n            return true;\n        }\n\n        final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());\n\n        builder\n            .setMessage(message)\n            .setPositiveButton(\"OK\", (dialog, buttonIndex) -> {\n                dialog.dismiss();\n                result.confirm();\n            })\n            .setNegativeButton(\"Cancel\", (dialog, buttonIndex) -> {\n                dialog.dismiss();\n                result.cancel();\n            })\n            .setOnCancelListener((dialog) -> {\n                dialog.dismiss();\n                result.cancel();\n            });\n\n        AlertDialog dialog = builder.create();\n\n        dialog.show();\n\n        return true;\n    }\n\n    /**\n     * Show the browser prompt modal\n     * @param view\n     * @param url\n     * @param message\n     * @param defaultValue\n     * @param result\n     * @return\n     */\n    @Override\n    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {\n        if (bridge.getActivity().isFinishing()) {\n            return true;\n        }\n\n        final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());\n        final EditText input = new EditText(view.getContext());\n\n        builder\n            .setMessage(message)\n            .setView(input)\n            .setPositiveButton(\"OK\", (dialog, buttonIndex) -> {\n                dialog.dismiss();\n\n                String inputText1 = input.getText().toString().trim();\n                result.confirm(inputText1);\n            })\n            .setNegativeButton(\"Cancel\", (dialog, buttonIndex) -> {\n                dialog.dismiss();\n                result.cancel();\n            })\n            .setOnCancelListener((dialog) -> {\n                dialog.dismiss();\n                result.cancel();\n            });\n\n        AlertDialog dialog = builder.create();\n\n        dialog.show();\n\n        return true;\n    }\n\n    /**\n     * Handle the browser geolocation permission prompt\n     * @param origin\n     * @param callback\n     */\n    @Override\n    public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {\n        super.onGeolocationPermissionsShowPrompt(origin, callback);\n        Logger.debug(\"onGeolocationPermissionsShowPrompt: DOING IT HERE FOR ORIGIN: \" + origin);\n        final String[] geoPermissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION };\n\n        if (!PermissionHelper.hasPermissions(bridge.getContext(), geoPermissions)) {\n            permissionListener = (isGranted) -> {\n                if (isGranted) {\n                    callback.invoke(origin, true, false);\n                } else {\n                    final String[] coarsePermission = { Manifest.permission.ACCESS_COARSE_LOCATION };\n                    if (\n                        Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&\n                        PermissionHelper.hasPermissions(bridge.getContext(), coarsePermission)\n                    ) {\n                        callback.invoke(origin, true, false);\n                    } else {\n                        callback.invoke(origin, false, false);\n                    }\n                }\n            };\n            permissionLauncher.launch(geoPermissions);\n        } else {\n            // permission is already granted\n            callback.invoke(origin, true, false);\n            Logger.debug(\"onGeolocationPermissionsShowPrompt: has required permission\");\n        }\n    }\n\n    @Override\n    public boolean onShowFileChooser(\n        WebView webView,\n        final ValueCallback<Uri[]> filePathCallback,\n        final FileChooserParams fileChooserParams\n    ) {\n        List<String> acceptTypes = Arrays.asList(fileChooserParams.getAcceptTypes());\n        boolean captureEnabled = fileChooserParams.isCaptureEnabled();\n        boolean capturePhoto = captureEnabled && acceptTypes.contains(\"image/*\");\n        final boolean captureVideo = captureEnabled && acceptTypes.contains(\"video/*\");\n        if ((capturePhoto || captureVideo)) {\n            if (isMediaCaptureSupported()) {\n                showMediaCaptureOrFilePicker(filePathCallback, fileChooserParams, captureVideo);\n            } else {\n                permissionListener = (isGranted) -> {\n                    if (isGranted) {\n                        showMediaCaptureOrFilePicker(filePathCallback, fileChooserParams, captureVideo);\n                    } else {\n                        Logger.warn(Logger.tags(\"FileChooser\"), \"Camera permission not granted\");\n                        filePathCallback.onReceiveValue(null);\n                    }\n                };\n                final String[] camPermission = { Manifest.permission.CAMERA };\n                permissionLauncher.launch(camPermission);\n            }\n        } else {\n            showFilePicker(filePathCallback, fileChooserParams);\n        }\n\n        return true;\n    }\n\n    private boolean isMediaCaptureSupported() {\n        String[] permissions = { Manifest.permission.CAMERA };\n        return (\n            PermissionHelper.hasPermissions(bridge.getContext(), permissions) ||\n            !PermissionHelper.hasDefinedPermission(bridge.getContext(), Manifest.permission.CAMERA)\n        );\n    }\n\n    private void showMediaCaptureOrFilePicker(ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams, boolean isVideo) {\n        boolean shown;\n        if (isVideo) {\n            shown = showVideoCapturePicker(filePathCallback);\n        } else {\n            shown = showImageCapturePicker(filePathCallback);\n        }\n        if (!shown) {\n            Logger.warn(Logger.tags(\"FileChooser\"), \"Media capture intent could not be launched. Falling back to default file picker.\");\n            showFilePicker(filePathCallback, fileChooserParams);\n        }\n    }\n\n    @SuppressLint(\"QueryPermissionsNeeded\")\n    private boolean showImageCapturePicker(final ValueCallback<Uri[]> filePathCallback) {\n        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n        if (takePictureIntent.resolveActivity(bridge.getActivity().getPackageManager()) == null) {\n            return false;\n        }\n\n        final Uri imageFileUri;\n        try {\n            imageFileUri = createImageFileUri();\n        } catch (Exception ex) {\n            Logger.error(\"Unable to create temporary media capture file: \" + ex.getMessage());\n            return false;\n        }\n        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);\n        activityListener = (activityResult) -> {\n            Uri[] result = null;\n            if (activityResult.getResultCode() == Activity.RESULT_OK) {\n                result = new Uri[] { imageFileUri };\n            }\n            filePathCallback.onReceiveValue(result);\n        };\n        activityLauncher.launch(takePictureIntent);\n\n        return true;\n    }\n\n    @SuppressLint(\"QueryPermissionsNeeded\")\n    private boolean showVideoCapturePicker(final ValueCallback<Uri[]> filePathCallback) {\n        Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);\n        if (takeVideoIntent.resolveActivity(bridge.getActivity().getPackageManager()) == null) {\n            return false;\n        }\n\n        activityListener = (activityResult) -> {\n            Uri[] result = null;\n            if (activityResult.getResultCode() == Activity.RESULT_OK) {\n                result = new Uri[] { activityResult.getData().getData() };\n            }\n            filePathCallback.onReceiveValue(result);\n        };\n        activityLauncher.launch(takeVideoIntent);\n\n        return true;\n    }\n\n    private void showFilePicker(final ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {\n        Intent intent = fileChooserParams.createIntent();\n        if (fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE) {\n            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);\n        }\n        if (fileChooserParams.getAcceptTypes().length > 1 || intent.getType().startsWith(\".\")) {\n            String[] validTypes = getValidTypes(fileChooserParams.getAcceptTypes());\n            intent.putExtra(Intent.EXTRA_MIME_TYPES, validTypes);\n            if (intent.getType().startsWith(\".\")) {\n                intent.setType(validTypes[0]);\n            }\n        }\n        try {\n            activityListener = (activityResult) -> {\n                Uri[] result;\n                Intent resultIntent = activityResult.getData();\n                if (activityResult.getResultCode() == Activity.RESULT_OK && resultIntent.getClipData() != null) {\n                    final int numFiles = resultIntent.getClipData().getItemCount();\n                    result = new Uri[numFiles];\n                    for (int i = 0; i < numFiles; i++) {\n                        result[i] = resultIntent.getClipData().getItemAt(i).getUri();\n                    }\n                } else {\n                    result = WebChromeClient.FileChooserParams.parseResult(activityResult.getResultCode(), resultIntent);\n                }\n                filePathCallback.onReceiveValue(result);\n            };\n            activityLauncher.launch(intent);\n        } catch (ActivityNotFoundException e) {\n            filePathCallback.onReceiveValue(null);\n        }\n    }\n\n    private String[] getValidTypes(String[] currentTypes) {\n        List<String> validTypes = new ArrayList<>();\n        MimeTypeMap mtm = MimeTypeMap.getSingleton();\n        for (String mime : currentTypes) {\n            if (mime.startsWith(\".\")) {\n                String extension = mime.substring(1);\n                String extensionMime = mtm.getMimeTypeFromExtension(extension);\n                if (extensionMime != null && !validTypes.contains(extensionMime)) {\n                    validTypes.add(extensionMime);\n                }\n            } else if (!validTypes.contains(mime)) {\n                validTypes.add(mime);\n            }\n        }\n        Object[] validObj = validTypes.toArray();\n        return Arrays.copyOf(validObj, validObj.length, String[].class);\n    }\n\n    @Override\n    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {\n        String tag = Logger.tags(\"Console\");\n        if (consoleMessage.message() != null && isValidMsg(consoleMessage.message())) {\n            String msg = String.format(\n                \"File: %s - Line %d - Msg: %s\",\n                consoleMessage.sourceId(),\n                consoleMessage.lineNumber(),\n                consoleMessage.message()\n            );\n            String level = consoleMessage.messageLevel().name();\n            if (\"ERROR\".equalsIgnoreCase(level)) {\n                Logger.error(tag, msg, null);\n            } else if (\"WARNING\".equalsIgnoreCase(level)) {\n                Logger.warn(tag, msg);\n            } else if (\"TIP\".equalsIgnoreCase(level)) {\n                Logger.debug(tag, msg);\n            } else {\n                Logger.info(tag, msg);\n            }\n        }\n        return true;\n    }\n\n    public boolean isValidMsg(String msg) {\n        return !(msg.contains(\"%cresult %c\") || (msg.contains(\"%cnative %c\")) || msg.equalsIgnoreCase(\"console.groupEnd\"));\n    }\n\n    private Uri createImageFileUri() throws IOException {\n        Activity activity = bridge.getActivity();\n        File photoFile = createImageFile(activity);\n        return FileProvider.getUriForFile(activity, bridge.getContext().getPackageName() + \".fileprovider\", photoFile);\n    }\n\n    private File createImageFile(Activity activity) throws IOException {\n        // Create an image file name\n        String timeStamp = new SimpleDateFormat(\"yyyyMMdd_HHmmss\").format(new Date());\n        String imageFileName = \"JPEG_\" + timeStamp + \"_\";\n        File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);\n\n        return File.createTempFile(imageFileName, \".jpg\", storageDir);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java",
    "content": "package com.getcapacitor;\n\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.webkit.RenderProcessGoneDetail;\nimport android.webkit.WebResourceError;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\nimport java.util.List;\n\npublic class BridgeWebViewClient extends WebViewClient {\n\n    private Bridge bridge;\n\n    public BridgeWebViewClient(Bridge bridge) {\n        this.bridge = bridge;\n    }\n\n    @Override\n    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\n        return bridge.getLocalServer().shouldInterceptRequest(request);\n    }\n\n    @Override\n    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n        Uri url = request.getUrl();\n        return bridge.launchIntent(url);\n    }\n\n    @Override\n    public void onPageFinished(WebView view, String url) {\n        super.onPageFinished(view, url);\n        List<WebViewListener> webViewListeners = bridge.getWebViewListeners();\n\n        if (webViewListeners != null && view.getProgress() == 100) {\n            for (WebViewListener listener : bridge.getWebViewListeners()) {\n                listener.onPageLoaded(view);\n            }\n        }\n    }\n\n    @Override\n    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {\n        super.onReceivedError(view, request, error);\n\n        List<WebViewListener> webViewListeners = bridge.getWebViewListeners();\n        if (webViewListeners != null) {\n            for (WebViewListener listener : bridge.getWebViewListeners()) {\n                listener.onReceivedError(view);\n            }\n        }\n\n        String errorPath = bridge.getErrorUrl();\n        if (errorPath != null && request.isForMainFrame()) {\n            view.loadUrl(errorPath);\n        }\n    }\n\n    @Override\n    public void onPageStarted(WebView view, String url, Bitmap favicon) {\n        super.onPageStarted(view, url, favicon);\n        bridge.reset();\n        List<WebViewListener> webViewListeners = bridge.getWebViewListeners();\n\n        if (webViewListeners != null) {\n            for (WebViewListener listener : bridge.getWebViewListeners()) {\n                listener.onPageStarted(view);\n            }\n        }\n    }\n\n    @Override\n    public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {\n        super.onReceivedHttpError(view, request, errorResponse);\n\n        List<WebViewListener> webViewListeners = bridge.getWebViewListeners();\n        if (webViewListeners != null) {\n            for (WebViewListener listener : bridge.getWebViewListeners()) {\n                listener.onReceivedHttpError(view);\n            }\n        }\n\n        String errorPath = bridge.getErrorUrl();\n        if (errorPath != null && request.isForMainFrame()) {\n            view.loadUrl(errorPath);\n        }\n    }\n\n    @Override\n    public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {\n        super.onRenderProcessGone(view, detail);\n        boolean result = false;\n\n        List<WebViewListener> webViewListeners = bridge.getWebViewListeners();\n        if (webViewListeners != null) {\n            for (WebViewListener listener : bridge.getWebViewListeners()) {\n                result = listener.onRenderProcessGone(view, detail) || result;\n            }\n        }\n\n        return result;\n    }\n\n    @Override\n    public void onPageCommitVisible(WebView view, String url) {\n        super.onPageCommitVisible(view, url);\n\n        List<WebViewListener> webViewListeners = bridge.getWebViewListeners();\n        if (webViewListeners != null) {\n            for (WebViewListener listener : bridge.getWebViewListeners()) {\n                listener.onPageCommitVisible(view, url);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/CapConfig.java",
    "content": "package com.getcapacitor;\n\nimport static com.getcapacitor.Bridge.CAPACITOR_HTTPS_SCHEME;\nimport static com.getcapacitor.Bridge.DEFAULT_ANDROID_WEBVIEW_VERSION;\nimport static com.getcapacitor.Bridge.DEFAULT_HUAWEI_WEBVIEW_VERSION;\nimport static com.getcapacitor.Bridge.MINIMUM_ANDROID_WEBVIEW_VERSION;\nimport static com.getcapacitor.Bridge.MINIMUM_HUAWEI_WEBVIEW_VERSION;\nimport static com.getcapacitor.FileUtils.readFileFromAssets;\n\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.AssetManager;\nimport androidx.annotation.Nullable;\nimport com.getcapacitor.util.JSONUtils;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n/**\n * Represents the configuration options for Capacitor\n */\npublic class CapConfig {\n\n    private static final String LOG_BEHAVIOR_NONE = \"none\";\n    private static final String LOG_BEHAVIOR_DEBUG = \"debug\";\n    private static final String LOG_BEHAVIOR_PRODUCTION = \"production\";\n\n    // Server Config\n    private boolean html5mode = true;\n    private String serverUrl;\n    private String hostname = \"localhost\";\n    private String androidScheme = CAPACITOR_HTTPS_SCHEME;\n    private String[] allowNavigation;\n\n    // Android Config\n    private String overriddenUserAgentString;\n    private String appendedUserAgentString;\n    private String backgroundColor;\n    private boolean allowMixedContent = false;\n    private boolean captureInput = false;\n    private boolean webContentsDebuggingEnabled = false;\n    private boolean loggingEnabled = true;\n    private boolean initialFocus = true;\n    private boolean useLegacyBridge = false;\n    private int minWebViewVersion = DEFAULT_ANDROID_WEBVIEW_VERSION;\n    private int minHuaweiWebViewVersion = DEFAULT_HUAWEI_WEBVIEW_VERSION;\n    private String errorPath;\n    private boolean zoomableWebView = false;\n    private boolean resolveServiceWorkerRequests = true;\n\n    // Embedded\n    private String startPath;\n\n    // Plugins\n    private Map<String, PluginConfig> pluginsConfiguration = null;\n\n    // Config Object JSON (legacy)\n    private JSONObject configJSON = new JSONObject();\n\n    /**\n     * Constructs an empty config file.\n     */\n    private CapConfig() {}\n\n    /**\n     * Get an instance of the Config file object.\n     * @deprecated use {@link #loadDefault(Context)} to load an instance of the Config object\n     * from the capacitor.config.json file, or use the {@link CapConfig.Builder} to construct\n     * a CapConfig for embedded use.\n     *\n     * @param assetManager The AssetManager used to load the config file\n     * @param config JSON describing a configuration to use\n     */\n    @Deprecated\n    public CapConfig(AssetManager assetManager, JSONObject config) {\n        if (config != null) {\n            this.configJSON = config;\n        } else {\n            // Load the capacitor.config.json\n            loadConfigFromAssets(assetManager, null);\n        }\n\n        deserializeConfig(null);\n    }\n\n    /**\n     * Constructs a Capacitor Configuration from config.json file.\n     *\n     * @param context The context.\n     * @return A loaded config file, if successful.\n     */\n    public static CapConfig loadDefault(Context context) {\n        CapConfig config = new CapConfig();\n\n        if (context == null) {\n            Logger.error(\"Capacitor Config could not be created from file. Context must not be null.\");\n            return config;\n        }\n\n        config.loadConfigFromAssets(context.getAssets(), null);\n        config.deserializeConfig(context);\n        return config;\n    }\n\n    /**\n     * Constructs a Capacitor Configuration from config.json file within the app assets.\n     *\n     * @param context The context.\n     * @param path A path relative to the root assets directory.\n     * @return A loaded config file, if successful.\n     */\n    public static CapConfig loadFromAssets(Context context, String path) {\n        CapConfig config = new CapConfig();\n\n        if (context == null) {\n            Logger.error(\"Capacitor Config could not be created from file. Context must not be null.\");\n            return config;\n        }\n\n        config.loadConfigFromAssets(context.getAssets(), path);\n        config.deserializeConfig(context);\n        return config;\n    }\n\n    /**\n     * Constructs a Capacitor Configuration from config.json file within the app file-space.\n     *\n     * @param context The context.\n     * @param path A path relative to the root of the app file-space.\n     * @return A loaded config file, if successful.\n     */\n    public static CapConfig loadFromFile(Context context, String path) {\n        CapConfig config = new CapConfig();\n\n        if (context == null) {\n            Logger.error(\"Capacitor Config could not be created from file. Context must not be null.\");\n            return config;\n        }\n\n        config.loadConfigFromFile(path);\n        config.deserializeConfig(context);\n        return config;\n    }\n\n    /**\n     * Constructs a Capacitor Configuration using ConfigBuilder.\n     *\n     * @param builder A config builder initialized with values\n     */\n    private CapConfig(Builder builder) {\n        // Server Config\n        this.html5mode = builder.html5mode;\n        this.serverUrl = builder.serverUrl;\n        this.hostname = builder.hostname;\n\n        if (this.validateScheme(builder.androidScheme)) {\n            this.androidScheme = builder.androidScheme;\n        }\n\n        this.allowNavigation = builder.allowNavigation;\n\n        // Android Config\n        this.overriddenUserAgentString = builder.overriddenUserAgentString;\n        this.appendedUserAgentString = builder.appendedUserAgentString;\n        this.backgroundColor = builder.backgroundColor;\n        this.allowMixedContent = builder.allowMixedContent;\n        this.captureInput = builder.captureInput;\n        this.webContentsDebuggingEnabled = builder.webContentsDebuggingEnabled;\n        this.loggingEnabled = builder.loggingEnabled;\n        this.initialFocus = builder.initialFocus;\n        this.useLegacyBridge = builder.useLegacyBridge;\n        this.minWebViewVersion = builder.minWebViewVersion;\n        this.minHuaweiWebViewVersion = builder.minHuaweiWebViewVersion;\n        this.errorPath = builder.errorPath;\n        this.zoomableWebView = builder.zoomableWebView;\n        this.resolveServiceWorkerRequests = builder.resolveServiceWorkerRequests;\n\n        // Embedded\n        this.startPath = builder.startPath;\n\n        // Plugins Config\n        this.pluginsConfiguration = builder.pluginsConfiguration;\n    }\n\n    /**\n     * Loads a Capacitor Configuration JSON file into a Capacitor Configuration object.\n     * An optional path string can be provided to look for the config in a subdirectory path.\n     */\n    private void loadConfigFromAssets(AssetManager assetManager, String path) {\n        if (path == null) {\n            path = \"\";\n        } else {\n            // Add slash at the end to form a proper file path if going deeper in assets dir\n            if (path.charAt(path.length() - 1) != '/') {\n                path = path + \"/\";\n            }\n        }\n\n        try {\n            String jsonString = readFileFromAssets(assetManager, path + \"capacitor.config.json\");\n            configJSON = new JSONObject(jsonString);\n        } catch (IOException ex) {\n            Logger.error(\"Unable to load capacitor.config.json. Run npx cap copy first\", ex);\n        } catch (JSONException ex) {\n            Logger.error(\"Unable to parse capacitor.config.json. Make sure it's valid json\", ex);\n        }\n    }\n\n    /**\n     * Loads a Capacitor Configuration JSON file into a Capacitor Configuration object.\n     * An optional path string can be provided to look for the config in a subdirectory path.\n     */\n    private void loadConfigFromFile(String path) {\n        if (path == null) {\n            path = \"\";\n        } else {\n            // Add slash at the end to form a proper file path if going deeper in assets dir\n            if (path.charAt(path.length() - 1) != '/') {\n                path = path + \"/\";\n            }\n        }\n\n        try {\n            File configFile = new File(path + \"capacitor.config.json\");\n            String jsonString = FileUtils.readFileFromDisk(configFile);\n            configJSON = new JSONObject(jsonString);\n        } catch (JSONException ex) {\n            Logger.error(\"Unable to parse capacitor.config.json. Make sure it's valid json\", ex);\n        } catch (IOException ex) {\n            Logger.error(\"Unable to load capacitor.config.json.\", ex);\n        }\n    }\n\n    /**\n     * Deserializes the config from JSON into a Capacitor Configuration object.\n     */\n    private void deserializeConfig(@Nullable Context context) {\n        boolean isDebug = context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;\n\n        // Server\n        html5mode = JSONUtils.getBoolean(configJSON, \"server.html5mode\", html5mode);\n        serverUrl = JSONUtils.getString(configJSON, \"server.url\", null);\n        hostname = JSONUtils.getString(configJSON, \"server.hostname\", hostname);\n        errorPath = JSONUtils.getString(configJSON, \"server.errorPath\", null);\n        startPath = JSONUtils.getString(configJSON, \"server.appStartPath\", null);\n\n        String configSchema = JSONUtils.getString(configJSON, \"server.androidScheme\", androidScheme);\n        if (this.validateScheme(configSchema)) {\n            androidScheme = configSchema;\n        }\n\n        allowNavigation = JSONUtils.getArray(configJSON, \"server.allowNavigation\", null);\n\n        // Android\n        overriddenUserAgentString = JSONUtils.getString(\n            configJSON,\n            \"android.overrideUserAgent\",\n            JSONUtils.getString(configJSON, \"overrideUserAgent\", null)\n        );\n        appendedUserAgentString = JSONUtils.getString(\n            configJSON,\n            \"android.appendUserAgent\",\n            JSONUtils.getString(configJSON, \"appendUserAgent\", null)\n        );\n        backgroundColor = JSONUtils.getString(\n            configJSON,\n            \"android.backgroundColor\",\n            JSONUtils.getString(configJSON, \"backgroundColor\", null)\n        );\n        allowMixedContent = JSONUtils.getBoolean(\n            configJSON,\n            \"android.allowMixedContent\",\n            JSONUtils.getBoolean(configJSON, \"allowMixedContent\", allowMixedContent)\n        );\n        minWebViewVersion = JSONUtils.getInt(configJSON, \"android.minWebViewVersion\", DEFAULT_ANDROID_WEBVIEW_VERSION);\n        minHuaweiWebViewVersion = JSONUtils.getInt(configJSON, \"android.minHuaweiWebViewVersion\", DEFAULT_HUAWEI_WEBVIEW_VERSION);\n        captureInput = JSONUtils.getBoolean(configJSON, \"android.captureInput\", captureInput);\n        useLegacyBridge = JSONUtils.getBoolean(configJSON, \"android.useLegacyBridge\", useLegacyBridge);\n        webContentsDebuggingEnabled = JSONUtils.getBoolean(configJSON, \"android.webContentsDebuggingEnabled\", isDebug);\n        zoomableWebView = JSONUtils.getBoolean(configJSON, \"android.zoomEnabled\", JSONUtils.getBoolean(configJSON, \"zoomEnabled\", false));\n        resolveServiceWorkerRequests = JSONUtils.getBoolean(configJSON, \"android.resolveServiceWorkerRequests\", true);\n\n        String logBehavior = JSONUtils.getString(\n            configJSON,\n            \"android.loggingBehavior\",\n            JSONUtils.getString(configJSON, \"loggingBehavior\", LOG_BEHAVIOR_DEBUG)\n        );\n        switch (logBehavior.toLowerCase(Locale.ROOT)) {\n            case LOG_BEHAVIOR_PRODUCTION:\n                loggingEnabled = true;\n                break;\n            case LOG_BEHAVIOR_NONE:\n                loggingEnabled = false;\n                break;\n            default: // LOG_BEHAVIOR_DEBUG\n                loggingEnabled = isDebug;\n        }\n\n        initialFocus = JSONUtils.getBoolean(\n            configJSON,\n            \"android.initialFocus\",\n            JSONUtils.getBoolean(configJSON, \"initialFocus\", initialFocus)\n        );\n\n        // Plugins\n        pluginsConfiguration = deserializePluginsConfig(JSONUtils.getObject(configJSON, \"plugins\"));\n    }\n\n    private boolean validateScheme(String scheme) {\n        List<String> invalidSchemes = Arrays.asList(\"file\", \"ftp\", \"ftps\", \"ws\", \"wss\", \"about\", \"blob\", \"data\");\n        if (invalidSchemes.contains(scheme)) {\n            Logger.warn(scheme + \" is not an allowed scheme.  Defaulting to https.\");\n            return false;\n        }\n\n        // Non-http(s) schemes are not allowed to modify the URL path as of Android Webview 117\n        if (!scheme.equals(\"http\") && !scheme.equals(\"https\")) {\n            Logger.warn(\n                \"Using a non-standard scheme: \" + scheme + \" for Android. This is known to cause issues as of Android Webview 117.\"\n            );\n        }\n\n        return true;\n    }\n\n    public boolean isHTML5Mode() {\n        return html5mode;\n    }\n\n    public String getServerUrl() {\n        return serverUrl;\n    }\n\n    public String getErrorPath() {\n        return errorPath;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public String getStartPath() {\n        return startPath;\n    }\n\n    public String getAndroidScheme() {\n        return androidScheme;\n    }\n\n    public String[] getAllowNavigation() {\n        return allowNavigation;\n    }\n\n    public String getOverriddenUserAgentString() {\n        return overriddenUserAgentString;\n    }\n\n    public String getAppendedUserAgentString() {\n        return appendedUserAgentString;\n    }\n\n    public String getBackgroundColor() {\n        return backgroundColor;\n    }\n\n    public boolean isMixedContentAllowed() {\n        return allowMixedContent;\n    }\n\n    public boolean isInputCaptured() {\n        return captureInput;\n    }\n\n    public boolean isResolveServiceWorkerRequests() {\n        return resolveServiceWorkerRequests;\n    }\n\n    public boolean isWebContentsDebuggingEnabled() {\n        return webContentsDebuggingEnabled;\n    }\n\n    public boolean isZoomableWebView() {\n        return zoomableWebView;\n    }\n\n    public boolean isLoggingEnabled() {\n        return loggingEnabled;\n    }\n\n    public boolean isInitialFocus() {\n        return initialFocus;\n    }\n\n    public boolean isUsingLegacyBridge() {\n        return useLegacyBridge;\n    }\n\n    public int getMinWebViewVersion() {\n        if (minWebViewVersion < MINIMUM_ANDROID_WEBVIEW_VERSION) {\n            Logger.warn(\"Specified minimum webview version is too low, defaulting to \" + MINIMUM_ANDROID_WEBVIEW_VERSION);\n            return MINIMUM_ANDROID_WEBVIEW_VERSION;\n        }\n\n        return minWebViewVersion;\n    }\n\n    public int getMinHuaweiWebViewVersion() {\n        if (minHuaweiWebViewVersion < MINIMUM_HUAWEI_WEBVIEW_VERSION) {\n            Logger.warn(\"Specified minimum Huawei webview version is too low, defaulting to \" + MINIMUM_HUAWEI_WEBVIEW_VERSION);\n            return MINIMUM_HUAWEI_WEBVIEW_VERSION;\n        }\n\n        return minHuaweiWebViewVersion;\n    }\n\n    public PluginConfig getPluginConfiguration(String pluginId) {\n        PluginConfig pluginConfig = pluginsConfiguration.get(pluginId);\n        if (pluginConfig == null) {\n            pluginConfig = new PluginConfig(new JSONObject());\n        }\n\n        return pluginConfig;\n    }\n\n    /**\n     * Get a JSON object value from the Capacitor config.\n     * @deprecated use {@link PluginConfig#getObject(String)}  to access plugin config values.\n     * For main Capacitor config values, use the appropriate getter.\n     *\n     * @param key A key to fetch from the config\n     * @return The value from the config, if exists. Null if not\n     */\n    @Deprecated\n    public JSONObject getObject(String key) {\n        try {\n            return configJSON.getJSONObject(key);\n        } catch (Exception ex) {}\n        return null;\n    }\n\n    /**\n     * Get a string value from the Capacitor config.\n     * @deprecated use {@link PluginConfig#getString(String, String)} to access plugin config\n     * values. For main Capacitor config values, use the appropriate getter.\n     *\n     * @param key A key to fetch from the config\n     * @return The value from the config, if exists. Null if not\n     */\n    @Deprecated\n    public String getString(String key) {\n        return JSONUtils.getString(configJSON, key, null);\n    }\n\n    /**\n     * Get a string value from the Capacitor config.\n     * @deprecated use {@link PluginConfig#getString(String, String)} to access plugin config\n     * values. For main Capacitor config values, use the appropriate getter.\n     *\n     * @param key A key to fetch from the config\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    @Deprecated\n    public String getString(String key, String defaultValue) {\n        return JSONUtils.getString(configJSON, key, defaultValue);\n    }\n\n    /**\n     * Get a boolean value from the Capacitor config.\n     * @deprecated use {@link PluginConfig#getBoolean(String, boolean)} to access plugin config\n     * values. For main Capacitor config values, use the appropriate getter.\n     *\n     * @param key A key to fetch from the config\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    @Deprecated\n    public boolean getBoolean(String key, boolean defaultValue) {\n        return JSONUtils.getBoolean(configJSON, key, defaultValue);\n    }\n\n    /**\n     * Get an integer value from the Capacitor config.\n     * @deprecated use {@link PluginConfig#getInt(String, int)}  to access the plugin config\n     * values. For main Capacitor config values, use the appropriate getter.\n     *\n     * @param key A key to fetch from the config\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    @Deprecated\n    public int getInt(String key, int defaultValue) {\n        return JSONUtils.getInt(configJSON, key, defaultValue);\n    }\n\n    /**\n     * Get a string array value from the Capacitor config.\n     * @deprecated use {@link PluginConfig#getArray(String)}  to access the plugin config\n     * values. For main Capacitor config values, use the appropriate getter.\n     *\n     * @param key A key to fetch from the config\n     * @return The value from the config, if exists. Null if not\n     */\n    @Deprecated\n    public String[] getArray(String key) {\n        return JSONUtils.getArray(configJSON, key, null);\n    }\n\n    /**\n     * Get a string array value from the Capacitor config.\n     * @deprecated use {@link PluginConfig#getArray(String, String[])}  to access the plugin\n     * config values. For main Capacitor config values, use the appropriate getter.\n     *\n     * @param key A key to fetch from the config\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    @Deprecated\n    public String[] getArray(String key, String[] defaultValue) {\n        return JSONUtils.getArray(configJSON, key, defaultValue);\n    }\n\n    private static Map<String, PluginConfig> deserializePluginsConfig(JSONObject pluginsConfig) {\n        Map<String, PluginConfig> pluginsMap = new HashMap<>();\n\n        // return an empty map if there is no pluginsConfig json\n        if (pluginsConfig == null) {\n            return pluginsMap;\n        }\n\n        Iterator<String> pluginIds = pluginsConfig.keys();\n\n        while (pluginIds.hasNext()) {\n            String pluginId = pluginIds.next();\n            JSONObject value = null;\n\n            try {\n                value = pluginsConfig.getJSONObject(pluginId);\n                PluginConfig pluginConfig = new PluginConfig(value);\n                pluginsMap.put(pluginId, pluginConfig);\n            } catch (JSONException e) {\n                e.printStackTrace();\n            }\n        }\n\n        return pluginsMap;\n    }\n\n    /**\n     * Builds a Capacitor Configuration in code\n     */\n    public static class Builder {\n\n        private Context context;\n\n        // Server Config Values\n        private boolean html5mode = true;\n        private String serverUrl;\n        private String errorPath;\n        private String hostname = \"localhost\";\n        private String androidScheme = CAPACITOR_HTTPS_SCHEME;\n        private String[] allowNavigation;\n\n        // Android Config Values\n        private String overriddenUserAgentString;\n        private String appendedUserAgentString;\n        private String backgroundColor;\n        private boolean allowMixedContent = false;\n        private boolean captureInput = false;\n        private Boolean webContentsDebuggingEnabled = null;\n        private boolean loggingEnabled = true;\n        private boolean initialFocus = false;\n        private boolean useLegacyBridge = false;\n        private int minWebViewVersion = DEFAULT_ANDROID_WEBVIEW_VERSION;\n        private int minHuaweiWebViewVersion = DEFAULT_HUAWEI_WEBVIEW_VERSION;\n        private boolean zoomableWebView = false;\n        private boolean resolveServiceWorkerRequests = true;\n\n        // Embedded\n        private String startPath = null;\n\n        // Plugins Config Object\n        private Map<String, PluginConfig> pluginsConfiguration = new HashMap<>();\n\n        /**\n         * Constructs a new CapConfig Builder.\n         *\n         * @param context The context\n         */\n        public Builder(Context context) {\n            this.context = context;\n        }\n\n        /**\n         * Builds a Capacitor Config from the builder.\n         *\n         * @return A new Capacitor Config\n         */\n        public CapConfig create() {\n            if (webContentsDebuggingEnabled == null) {\n                webContentsDebuggingEnabled = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;\n            }\n\n            return new CapConfig(this);\n        }\n\n        public Builder setPluginsConfiguration(JSONObject pluginsConfiguration) {\n            this.pluginsConfiguration = deserializePluginsConfig(pluginsConfiguration);\n            return this;\n        }\n\n        public Builder setHTML5mode(boolean html5mode) {\n            this.html5mode = html5mode;\n            return this;\n        }\n\n        public Builder setServerUrl(String serverUrl) {\n            this.serverUrl = serverUrl;\n            return this;\n        }\n\n        public Builder setErrorPath(String errorPath) {\n            this.errorPath = errorPath;\n            return this;\n        }\n\n        public Builder setHostname(String hostname) {\n            this.hostname = hostname;\n            return this;\n        }\n\n        public Builder setStartPath(String path) {\n            this.startPath = path;\n            return this;\n        }\n\n        public Builder setAndroidScheme(String androidScheme) {\n            this.androidScheme = androidScheme;\n            return this;\n        }\n\n        public Builder setAllowNavigation(String[] allowNavigation) {\n            this.allowNavigation = allowNavigation;\n            return this;\n        }\n\n        public Builder setOverriddenUserAgentString(String overriddenUserAgentString) {\n            this.overriddenUserAgentString = overriddenUserAgentString;\n            return this;\n        }\n\n        public Builder setAppendedUserAgentString(String appendedUserAgentString) {\n            this.appendedUserAgentString = appendedUserAgentString;\n            return this;\n        }\n\n        public Builder setBackgroundColor(String backgroundColor) {\n            this.backgroundColor = backgroundColor;\n            return this;\n        }\n\n        public Builder setAllowMixedContent(boolean allowMixedContent) {\n            this.allowMixedContent = allowMixedContent;\n            return this;\n        }\n\n        public Builder setCaptureInput(boolean captureInput) {\n            this.captureInput = captureInput;\n            return this;\n        }\n\n        public Builder setUseLegacyBridge(boolean useLegacyBridge) {\n            this.useLegacyBridge = useLegacyBridge;\n            return this;\n        }\n\n        public Builder setResolveServiceWorkerRequests(boolean resolveServiceWorkerRequests) {\n            this.resolveServiceWorkerRequests = resolveServiceWorkerRequests;\n            return this;\n        }\n\n        public Builder setWebContentsDebuggingEnabled(boolean webContentsDebuggingEnabled) {\n            this.webContentsDebuggingEnabled = webContentsDebuggingEnabled;\n            return this;\n        }\n\n        public Builder setZoomableWebView(boolean zoomableWebView) {\n            this.zoomableWebView = zoomableWebView;\n            return this;\n        }\n\n        public Builder setLoggingEnabled(boolean enabled) {\n            this.loggingEnabled = enabled;\n            return this;\n        }\n\n        public Builder setInitialFocus(boolean focus) {\n            this.initialFocus = focus;\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java",
    "content": "package com.getcapacitor;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.KeyEvent;\nimport android.view.inputmethod.BaseInputConnection;\nimport android.view.inputmethod.EditorInfo;\nimport android.view.inputmethod.InputConnection;\nimport android.webkit.WebView;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\npublic class CapacitorWebView extends WebView {\n\n    private BaseInputConnection capInputConnection;\n    private Bridge bridge;\n\n    public CapacitorWebView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public void setBridge(Bridge bridge) {\n        this.bridge = bridge;\n    }\n\n    @Override\n    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {\n        CapConfig config;\n        if (bridge != null) {\n            config = bridge.getConfig();\n        } else {\n            config = CapConfig.loadDefault(getContext());\n        }\n\n        boolean captureInput = config.isInputCaptured();\n        if (captureInput) {\n            if (capInputConnection == null) {\n                capInputConnection = new BaseInputConnection(this, false);\n            }\n            return capInputConnection;\n        }\n        return super.onCreateInputConnection(outAttrs);\n    }\n\n    @Override\n    @SuppressWarnings(\"deprecation\")\n    public boolean dispatchKeyEvent(KeyEvent event) {\n        if (event.getAction() == KeyEvent.ACTION_MULTIPLE) {\n            evaluateJavascript(\"document.activeElement.value = document.activeElement.value + '\" + event.getCharacters() + \"';\", null);\n            return false;\n        }\n        return super.dispatchKeyEvent(event);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/FileUtils.java",
    "content": "/**\n * Portions adopted from react-native-image-crop-picker\n *\n * MIT License\n\n * Copyright (c) 2017 Ivan Pusic\n\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\npackage com.getcapacitor;\n\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.content.res.AssetManager;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Environment;\nimport android.provider.DocumentsContract;\nimport android.provider.MediaStore;\nimport android.provider.OpenableColumns;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\n\n/**\n * Common File utilities, such as resolve content URIs and\n * creating portable web paths from low-level files\n */\npublic class FileUtils {\n\n    private static String CapacitorFileScheme = Bridge.CAPACITOR_FILE_START;\n\n    public enum Type {\n        IMAGE(\"image\");\n\n        private String type;\n\n        Type(String type) {\n            this.type = type;\n        }\n    }\n\n    public static String getPortablePath(Context c, String host, Uri u) {\n        String path = getFileUrlForUri(c, u);\n        if (path.startsWith(\"file://\")) {\n            path = path.replace(\"file://\", \"\");\n        }\n        return host + Bridge.CAPACITOR_FILE_START + path;\n    }\n\n    public static String getFileUrlForUri(final Context context, final Uri uri) {\n        // DocumentProvider\n        if (DocumentsContract.isDocumentUri(context, uri)) {\n            // ExternalStorageProvider\n            if (isExternalStorageDocument(uri)) {\n                final String docId = DocumentsContract.getDocumentId(uri);\n                final String[] split = docId.split(\":\");\n                final String type = split[0];\n\n                if (\"primary\".equalsIgnoreCase(type)) {\n                    return legacyPrimaryPath(split[1]);\n                } else {\n                    final int splitIndex = docId.indexOf(':', 1);\n                    final String tag = docId.substring(0, splitIndex);\n                    final String path = docId.substring(splitIndex + 1);\n\n                    String nonPrimaryVolume = getPathToNonPrimaryVolume(context, tag);\n                    if (nonPrimaryVolume != null) {\n                        String result = nonPrimaryVolume + \"/\" + path;\n                        File file = new File(result);\n                        if (file.exists() && file.canRead()) {\n                            return result;\n                        }\n                        return null;\n                    }\n                }\n            }\n            // DownloadsProvider\n            else if (isDownloadsDocument(uri)) {\n                final String id = DocumentsContract.getDocumentId(uri);\n                final Uri contentUri = ContentUris.withAppendedId(Uri.parse(\"content://downloads/public_downloads\"), Long.valueOf(id));\n\n                return getDataColumn(context, contentUri, null, null);\n            }\n            // MediaProvider\n            else if (isMediaDocument(uri)) {\n                final String docId = DocumentsContract.getDocumentId(uri);\n                final String[] split = docId.split(\":\");\n                final String type = split[0];\n\n                Uri contentUri = null;\n                if (\"image\".equals(type)) {\n                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"video\".equals(type)) {\n                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"audio\".equals(type)) {\n                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n                }\n\n                final String selection = \"_id=?\";\n                final String[] selectionArgs = new String[] { split[1] };\n\n                return getDataColumn(context, contentUri, selection, selectionArgs);\n            }\n        }\n        // MediaStore (and general)\n        else if (\"content\".equalsIgnoreCase(uri.getScheme())) {\n            // Return the remote address\n            if (isGooglePhotosUri(uri)) return uri.getLastPathSegment();\n            return getDataColumn(context, uri, null, null);\n        }\n        // File\n        else if (\"file\".equalsIgnoreCase(uri.getScheme())) {\n            return uri.getPath();\n        }\n\n        return null;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private static String legacyPrimaryPath(String pathPart) {\n        return Environment.getExternalStorageDirectory() + \"/\" + pathPart;\n    }\n\n    /**\n     * Read a plaintext file from the assets directory.\n     *\n     * @param assetManager Used to open the file.\n     * @param fileName The path of the file to read.\n     * @return The contents of the file path.\n     * @throws IOException Thrown if any issues reading the provided file path.\n     */\n    static String readFileFromAssets(AssetManager assetManager, String fileName) throws IOException {\n        try (BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open(fileName)))) {\n            StringBuilder buffer = new StringBuilder();\n            String line;\n            while ((line = reader.readLine()) != null) {\n                buffer.append(line).append(\"\\n\");\n            }\n\n            return buffer.toString();\n        }\n    }\n\n    /**\n     * Read a plaintext file from within the app disk space.\n     *\n     * @param file The file to read.\n     * @return The contents of the file path.\n     * @throws IOException Thrown if any issues reading the provided file path.\n     */\n    static String readFileFromDisk(File file) throws IOException {\n        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {\n            StringBuilder buffer = new StringBuilder();\n            String line;\n            while ((line = reader.readLine()) != null) {\n                buffer.append(line).append(\"\\n\");\n            }\n\n            return buffer.toString();\n        }\n    }\n\n    /**\n     * Get the value of the data column for this Uri. This is useful for\n     * MediaStore Uris, and other file-based ContentProviders.\n     *\n     * @param context The context.\n     * @param uri The Uri to query.\n     * @param selection (Optional) Filter used in the query.\n     * @param selectionArgs (Optional) Selection arguments used in the query.\n     * @return The value of the _data column, which is typically a file path.\n     */\n    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {\n        String path = null;\n        Cursor cursor = null;\n        final String column = \"_data\";\n        final String[] projection = { column };\n\n        try {\n            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);\n            if (cursor != null && cursor.moveToFirst()) {\n                final int index = cursor.getColumnIndexOrThrow(column);\n                path = cursor.getString(index);\n            }\n        } catch (IllegalArgumentException ex) {\n            return getCopyFilePath(uri, context);\n        } finally {\n            if (cursor != null) cursor.close();\n        }\n        if (path == null) {\n            return getCopyFilePath(uri, context);\n        }\n        return path;\n    }\n\n    private static String getCopyFilePath(Uri uri, Context context) {\n        Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);\n        int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);\n        cursor.moveToFirst();\n        String name = (cursor.getString(nameIndex));\n        String fileName = sanitizeFilename(name);\n        File file = new File(context.getFilesDir(), fileName);\n        try {\n            InputStream inputStream = context.getContentResolver().openInputStream(uri);\n            FileOutputStream outputStream = new FileOutputStream(file);\n            int read = 0;\n            int maxBufferSize = 1024 * 1024;\n            int bufferSize = Math.min(inputStream.available(), maxBufferSize);\n            final byte[] buffers = new byte[bufferSize];\n            while ((read = inputStream.read(buffers)) != -1) {\n                outputStream.write(buffers, 0, read);\n            }\n            inputStream.close();\n            outputStream.close();\n        } catch (Exception e) {\n            return null;\n        } finally {\n            if (cursor != null) cursor.close();\n        }\n        return file.getPath();\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is ExternalStorageProvider.\n     */\n    private static boolean isExternalStorageDocument(Uri uri) {\n        return \"com.android.externalstorage.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is DownloadsProvider.\n     */\n    private static boolean isDownloadsDocument(Uri uri) {\n        return \"com.android.providers.downloads.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is MediaProvider.\n     */\n    private static boolean isMediaDocument(Uri uri) {\n        return \"com.android.providers.media.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is Google Photos.\n     */\n    private static boolean isGooglePhotosUri(Uri uri) {\n        return \"com.google.android.apps.photos.content\".equals(uri.getAuthority());\n    }\n\n    private static String getPathToNonPrimaryVolume(Context context, String tag) {\n        File[] volumes = context.getExternalCacheDirs();\n        if (volumes != null) {\n            for (File volume : volumes) {\n                if (volume != null) {\n                    String path = volume.getAbsolutePath();\n                    if (path != null) {\n                        int index = path.indexOf(tag);\n                        if (index != -1) {\n                            return path.substring(0, index) + tag;\n                        }\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    private static String sanitizeFilename(String displayName) {\n        String[] badCharacters = new String[] { \"..\", \"/\" };\n        String[] segments = displayName.split(\"/\");\n        String fileName = segments[segments.length - 1];\n        for (String suspString : badCharacters) {\n            fileName = fileName.replace(suspString, \"_\");\n        }\n        return fileName;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/InvalidPluginException.java",
    "content": "package com.getcapacitor;\n\nclass InvalidPluginException extends Exception {\n\n    public InvalidPluginException(String s) {\n        super(s);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/InvalidPluginMethodException.java",
    "content": "package com.getcapacitor;\n\nclass InvalidPluginMethodException extends Exception {\n\n    public InvalidPluginMethodException(String s) {\n        super(s);\n    }\n\n    public InvalidPluginMethodException(Throwable t) {\n        super(t);\n    }\n\n    public InvalidPluginMethodException(String s, Throwable t) {\n        super(s, t);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/JSArray.java",
    "content": "package com.getcapacitor;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport org.json.JSONArray;\nimport org.json.JSONException;\n\npublic class JSArray extends JSONArray {\n\n    public JSArray() {\n        super();\n    }\n\n    public JSArray(String json) throws JSONException {\n        super(json);\n    }\n\n    public JSArray(Collection copyFrom) {\n        super(copyFrom);\n    }\n\n    public JSArray(Object array) throws JSONException {\n        super(array);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <E> List<E> toList() throws JSONException {\n        List<E> items = new ArrayList<>();\n        Object o = null;\n        for (int i = 0; i < this.length(); i++) {\n            o = this.get(i);\n            try {\n                items.add((E) this.get(i));\n            } catch (Exception ex) {\n                throw new JSONException(\"Not all items are instances of the given type\");\n            }\n        }\n        return items;\n    }\n\n    /**\n     * Create a new JSArray without throwing a error\n     */\n    public static JSArray from(Object array) {\n        try {\n            return new JSArray(array);\n        } catch (JSONException ex) {}\n        return null;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/JSExport.java",
    "content": "package com.getcapacitor;\n\nimport static com.getcapacitor.FileUtils.readFileFromAssets;\n\nimport android.content.Context;\nimport android.text.TextUtils;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class JSExport {\n\n    private static String CATCHALL_OPTIONS_PARAM = \"_options\";\n    private static String CALLBACK_PARAM = \"_callback\";\n\n    public static String getGlobalJS(Context context, boolean loggingEnabled, boolean isDebug) {\n        return \"window.Capacitor = { DEBUG: \" + isDebug + \", isLoggingEnabled: \" + loggingEnabled + \", Plugins: {} };\";\n    }\n\n    public static String getMiscFileJS(ArrayList<String> paths, Context context) {\n        List<String> lines = new ArrayList<>();\n\n        for (String path : paths) {\n            try {\n                String fileContent = readFileFromAssets(context.getAssets(), \"public/\" + path);\n                lines.add(fileContent);\n            } catch (IOException ex) {\n                Logger.error(\"Unable to read public/\" + path);\n            }\n        }\n\n        return TextUtils.join(\"\\n\", lines);\n    }\n\n    public static String getCordovaJS(Context context) {\n        String fileContent = \"\";\n        try {\n            fileContent = readFileFromAssets(context.getAssets(), \"public/cordova.js\");\n        } catch (IOException ex) {\n            Logger.error(\"Unable to read public/cordova.js file, Cordova plugins will not work\");\n        }\n        return fileContent;\n    }\n\n    public static String getCordovaPluginsFileJS(Context context) {\n        String fileContent = \"\";\n        try {\n            fileContent = readFileFromAssets(context.getAssets(), \"public/cordova_plugins.js\");\n        } catch (IOException ex) {\n            Logger.error(\"Unable to read public/cordova_plugins.js file, Cordova plugins will not work\");\n        }\n        return fileContent;\n    }\n\n    public static String getPluginJS(Collection<PluginHandle> plugins) {\n        List<String> lines = new ArrayList<>();\n        JSONArray pluginArray = new JSONArray();\n\n        lines.add(\"// Begin: Capacitor Plugin JS\");\n        for (PluginHandle plugin : plugins) {\n            lines.add(\n                \"(function(w) {\\n\" +\n                    \"var a = (w.Capacitor = w.Capacitor || {});\\n\" +\n                    \"var p = (a.Plugins = a.Plugins || {});\\n\" +\n                    \"var t = (p['\" +\n                    plugin.getId() +\n                    \"'] = {});\\n\" +\n                    \"t.addListener = function(eventName, callback) {\\n\" +\n                    \"  return w.Capacitor.addListener('\" +\n                    plugin.getId() +\n                    \"', eventName, callback);\\n\" +\n                    \"}\"\n            );\n            Collection<PluginMethodHandle> methods = plugin.getMethods();\n            for (PluginMethodHandle method : methods) {\n                if (method.getName().equals(\"addListener\") || method.getName().equals(\"removeListener\")) {\n                    // Don't export add/remove listener, we do that automatically above as they are \"special snowflakes\"\n                    continue;\n                }\n                lines.add(generateMethodJS(plugin, method));\n            }\n\n            lines.add(\"})(window);\\n\");\n            pluginArray.put(createPluginHeader(plugin));\n        }\n\n        return TextUtils.join(\"\\n\", lines) + \"\\nwindow.Capacitor.PluginHeaders = \" + pluginArray.toString() + \";\";\n    }\n\n    public static String getCordovaPluginJS(Context context) {\n        return getFilesContent(context, \"public/plugins\");\n    }\n\n    public static String getFilesContent(Context context, String path) {\n        StringBuilder builder = new StringBuilder();\n        try {\n            String[] content = context.getAssets().list(path);\n            if (content.length > 0) {\n                for (String file : content) {\n                    if (!file.endsWith(\".map\")) {\n                        builder.append(getFilesContent(context, path + \"/\" + file));\n                    }\n                }\n            } else {\n                return readFileFromAssets(context.getAssets(), path);\n            }\n        } catch (IOException ex) {\n            Logger.warn(\"Unable to read file at path \" + path);\n        }\n        return builder.toString();\n    }\n\n    private static JSONObject createPluginHeader(PluginHandle plugin) {\n        JSONObject pluginObj = new JSONObject();\n        Collection<PluginMethodHandle> methods = plugin.getMethods();\n        try {\n            String id = plugin.getId();\n            JSONArray methodArray = new JSONArray();\n            pluginObj.put(\"name\", id);\n\n            for (PluginMethodHandle method : methods) {\n                methodArray.put(createPluginMethodHeader(method));\n            }\n\n            pluginObj.put(\"methods\", methodArray);\n        } catch (JSONException e) {\n            // ignore\n        }\n        return pluginObj;\n    }\n\n    private static JSONObject createPluginMethodHeader(PluginMethodHandle method) {\n        JSONObject methodObj = new JSONObject();\n\n        try {\n            methodObj.put(\"name\", method.getName());\n            if (!method.getReturnType().equals(PluginMethod.RETURN_NONE)) {\n                methodObj.put(\"rtype\", method.getReturnType());\n            }\n        } catch (JSONException e) {\n            // ignore\n        }\n\n        return methodObj;\n    }\n\n    public static String getBridgeJS(Context context) throws JSExportException {\n        return getFilesContent(context, \"native-bridge.js\");\n    }\n\n    private static String generateMethodJS(PluginHandle plugin, PluginMethodHandle method) {\n        List<String> lines = new ArrayList<>();\n\n        List<String> args = new ArrayList<>();\n        // Add the catch all param that will take a full javascript object to pass to the plugin\n        args.add(CATCHALL_OPTIONS_PARAM);\n\n        String returnType = method.getReturnType();\n        if (returnType.equals(PluginMethod.RETURN_CALLBACK)) {\n            args.add(CALLBACK_PARAM);\n        }\n\n        // Create the method function declaration\n        lines.add(\"t['\" + method.getName() + \"'] = function(\" + TextUtils.join(\", \", args) + \") {\");\n\n        switch (returnType) {\n            case PluginMethod.RETURN_NONE:\n                lines.add(\n                    \"return w.Capacitor.nativeCallback('\" +\n                        plugin.getId() +\n                        \"', '\" +\n                        method.getName() +\n                        \"', \" +\n                        CATCHALL_OPTIONS_PARAM +\n                        \")\"\n                );\n                break;\n            case PluginMethod.RETURN_PROMISE:\n                lines.add(\n                    \"return w.Capacitor.nativePromise('\" + plugin.getId() + \"', '\" + method.getName() + \"', \" + CATCHALL_OPTIONS_PARAM + \")\"\n                );\n                break;\n            case PluginMethod.RETURN_CALLBACK:\n                lines.add(\n                    \"return w.Capacitor.nativeCallback('\" +\n                        plugin.getId() +\n                        \"', '\" +\n                        method.getName() +\n                        \"', \" +\n                        CATCHALL_OPTIONS_PARAM +\n                        \", \" +\n                        CALLBACK_PARAM +\n                        \")\"\n                );\n                break;\n            default:\n            // TODO: Do something here?\n        }\n\n        lines.add(\"}\");\n\n        return TextUtils.join(\"\\n\", lines);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/JSExportException.java",
    "content": "package com.getcapacitor;\n\npublic class JSExportException extends Exception {\n\n    public JSExportException(String s) {\n        super(s);\n    }\n\n    public JSExportException(Throwable t) {\n        super(t);\n    }\n\n    public JSExportException(String s, Throwable t) {\n        super(s, t);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/JSInjector.java",
    "content": "package com.getcapacitor;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * JSInject is responsible for returning Capacitor's core\n * runtime JS and any plugin JS back into HTML page responses\n * to the client.\n */\nclass JSInjector {\n\n    private String globalJS;\n    private String bridgeJS;\n    private String pluginJS;\n    private String cordovaJS;\n    private String cordovaPluginsJS;\n    private String cordovaPluginsFileJS;\n    private String localUrlJS;\n    private String miscJS;\n\n    public JSInjector(\n        String globalJS,\n        String bridgeJS,\n        String pluginJS,\n        String cordovaJS,\n        String cordovaPluginsJS,\n        String cordovaPluginsFileJS,\n        String localUrlJS\n    ) {\n        this(globalJS, bridgeJS, pluginJS, cordovaJS, cordovaPluginsJS, cordovaPluginsFileJS, localUrlJS, null);\n    }\n\n    public JSInjector(\n        String globalJS,\n        String bridgeJS,\n        String pluginJS,\n        String cordovaJS,\n        String cordovaPluginsJS,\n        String cordovaPluginsFileJS,\n        String localUrlJS,\n        String miscJS\n    ) {\n        this.globalJS = globalJS;\n        this.bridgeJS = bridgeJS;\n        this.pluginJS = pluginJS;\n        this.cordovaJS = cordovaJS;\n        this.cordovaPluginsJS = cordovaPluginsJS;\n        this.cordovaPluginsFileJS = cordovaPluginsFileJS;\n        this.localUrlJS = localUrlJS;\n        this.miscJS = miscJS;\n    }\n\n    /**\n     * Generates injectable JS content.\n     * This may be used in other forms of injecting that aren't using an InputStream.\n     * @return\n     */\n    public String getScriptString() {\n        String scriptString =\n            globalJS +\n            \"\\n\\n\" +\n            localUrlJS +\n            \"\\n\\n\" +\n            bridgeJS +\n            \"\\n\\n\" +\n            pluginJS +\n            \"\\n\\n\" +\n            cordovaJS +\n            \"\\n\\n\" +\n            cordovaPluginsFileJS +\n            \"\\n\\n\" +\n            cordovaPluginsJS;\n\n        if (miscJS != null) {\n            scriptString += \"\\n\\n\" + miscJS;\n        }\n\n        return scriptString;\n    }\n\n    /**\n     * Given an InputStream from the web server, prepend it with\n     * our JS stream\n     * @param responseStream\n     * @return\n     */\n    public InputStream getInjectedStream(InputStream responseStream) {\n        String js = \"<script type=\\\"text/javascript\\\">\" + getScriptString() + \"</script>\";\n        String html = this.readAssetStream(responseStream);\n\n        // Insert the js string at the position after <head> or before </head> using StringBuilder\n        StringBuilder modifiedHtml = new StringBuilder(html);\n        if (html.contains(\"<head>\")) {\n            modifiedHtml.insert(html.indexOf(\"<head>\") + \"<head>\".length(), \"\\n\" + js + \"\\n\");\n            html = modifiedHtml.toString();\n        } else if (html.contains(\"</head>\")) {\n            modifiedHtml.insert(html.indexOf(\"</head>\"), \"\\n\" + js + \"\\n\");\n            html = modifiedHtml.toString();\n        } else {\n            Logger.error(\"Unable to inject Capacitor, Plugins won't work\");\n        }\n        return new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8));\n    }\n\n    private String readAssetStream(InputStream stream) {\n        try {\n            final int bufferSize = 1024;\n            final char[] buffer = new char[bufferSize];\n            final StringBuilder out = new StringBuilder();\n            Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);\n            for (;;) {\n                int rsz = in.read(buffer, 0, buffer.length);\n                if (rsz < 0) break;\n                out.append(buffer, 0, rsz);\n            }\n            return out.toString();\n        } catch (Exception e) {\n            Logger.error(\"Unable to process HTML asset file. This is a fatal error\", e);\n        }\n\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/JSObject.java",
    "content": "package com.getcapacitor;\n\nimport androidx.annotation.Nullable;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n/**\n * A wrapper around JSONObject that isn't afraid to do simple\n * JSON put operations without having to throw an exception\n * for every little thing jeez\n */\npublic class JSObject extends JSONObject {\n\n    public JSObject() {\n        super();\n    }\n\n    public JSObject(String json) throws JSONException {\n        super(json);\n    }\n\n    public JSObject(JSONObject obj, String[] names) throws JSONException {\n        super(obj, names);\n    }\n\n    /**\n     * Convert a pathetic JSONObject into a JSObject\n     * @param obj\n     */\n    public static JSObject fromJSONObject(JSONObject obj) throws JSONException {\n        Iterator<String> keysIter = obj.keys();\n        List<String> keys = new ArrayList<>();\n        while (keysIter.hasNext()) {\n            keys.add(keysIter.next());\n        }\n\n        return new JSObject(obj, keys.toArray(new String[keys.size()]));\n    }\n\n    @Override\n    @Nullable\n    public String getString(String key) {\n        return getString(key, null);\n    }\n\n    @Nullable\n    public String getString(String key, @Nullable String defaultValue) {\n        try {\n            String value = super.getString(key);\n            if (!super.isNull(key)) {\n                return value;\n            }\n        } catch (JSONException ex) {}\n        return defaultValue;\n    }\n\n    @Nullable\n    public Integer getInteger(String key) {\n        return getInteger(key, null);\n    }\n\n    @Nullable\n    public Integer getInteger(String key, @Nullable Integer defaultValue) {\n        try {\n            return super.getInt(key);\n        } catch (JSONException e) {}\n        return defaultValue;\n    }\n\n    @Nullable\n    public Boolean getBoolean(String key, @Nullable Boolean defaultValue) {\n        try {\n            return super.getBoolean(key);\n        } catch (JSONException e) {}\n        return defaultValue;\n    }\n\n    /**\n     * Fetch boolean from jsonObject\n     */\n    @Nullable\n    public Boolean getBool(String key) {\n        return getBoolean(key, null);\n    }\n\n    @Nullable\n    public JSObject getJSObject(String name) {\n        try {\n            return getJSObject(name, null);\n        } catch (JSONException e) {}\n        return null;\n    }\n\n    @Nullable\n    public JSObject getJSObject(String name, @Nullable JSObject defaultValue) throws JSONException {\n        try {\n            Object obj = get(name);\n            if (obj instanceof JSONObject) {\n                Iterator<String> keysIter = ((JSONObject) obj).keys();\n                List<String> keys = new ArrayList<>();\n                while (keysIter.hasNext()) {\n                    keys.add(keysIter.next());\n                }\n\n                return new JSObject((JSONObject) obj, keys.toArray(new String[keys.size()]));\n            }\n        } catch (JSONException ex) {}\n        return defaultValue;\n    }\n\n    @Override\n    public JSObject put(String key, boolean value) {\n        try {\n            super.put(key, value);\n        } catch (JSONException ex) {}\n        return this;\n    }\n\n    @Override\n    public JSObject put(String key, int value) {\n        try {\n            super.put(key, value);\n        } catch (JSONException ex) {}\n        return this;\n    }\n\n    @Override\n    public JSObject put(String key, long value) {\n        try {\n            super.put(key, value);\n        } catch (JSONException ex) {}\n        return this;\n    }\n\n    @Override\n    public JSObject put(String key, double value) {\n        try {\n            super.put(key, value);\n        } catch (JSONException ex) {}\n        return this;\n    }\n\n    @Override\n    public JSObject put(String key, Object value) {\n        try {\n            super.put(key, value);\n        } catch (JSONException ex) {}\n        return this;\n    }\n\n    public JSObject put(String key, String value) {\n        try {\n            super.put(key, value);\n        } catch (JSONException ex) {}\n        return this;\n    }\n\n    public JSObject putSafe(String key, Object value) throws JSONException {\n        return (JSObject) super.put(key, value);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/JSValue.java",
    "content": "package com.getcapacitor;\n\nimport org.json.JSONException;\n\n/**\n * Represents a single user-data value of any type on the capacitor PluginCall object.\n */\npublic class JSValue {\n\n    private final Object value;\n\n    /**\n     * @param call The capacitor plugin call, used for accessing the value safely.\n     * @param name The name of the property to access.\n     */\n    public JSValue(PluginCall call, String name) {\n        this.value = this.toValue(call, name);\n    }\n\n    /**\n     * Returns the coerced but uncasted underlying value.\n     */\n    public Object getValue() {\n        return this.value;\n    }\n\n    @Override\n    public String toString() {\n        return this.getValue().toString();\n    }\n\n    /**\n     * Returns the underlying value as a JSObject, or throwing if it cannot.\n     *\n     * @throws JSONException If the underlying value is not a JSObject.\n     */\n    public JSObject toJSObject() throws JSONException {\n        if (this.value instanceof JSObject) return (JSObject) this.value;\n        throw new JSONException(\"JSValue could not be coerced to JSObject.\");\n    }\n\n    /**\n     * Returns the underlying value as a JSArray, or throwing if it cannot.\n     *\n     * @throws JSONException If the underlying value is not a JSArray.\n     */\n    public JSArray toJSArray() throws JSONException {\n        if (this.value instanceof JSArray) return (JSArray) this.value;\n        throw new JSONException(\"JSValue could not be coerced to JSArray.\");\n    }\n\n    /**\n     * Returns the underlying value this object represents, coercing it into a capacitor-friendly object if supported.\n     */\n    private Object toValue(PluginCall call, String name) {\n        Object value = null;\n        value = call.getArray(name, null);\n        if (value != null) return value;\n        value = call.getObject(name, null);\n        if (value != null) return value;\n        value = call.getString(name, null);\n        if (value != null) return value;\n        return call.getData().opt(name);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/Logger.java",
    "content": "package com.getcapacitor;\n\nimport android.text.TextUtils;\nimport android.util.Log;\n\npublic class Logger {\n\n    public static final String LOG_TAG_CORE = \"Capacitor\";\n    public static CapConfig config;\n\n    private static Logger instance;\n\n    private static Logger getInstance() {\n        if (instance == null) {\n            instance = new Logger();\n        }\n        return instance;\n    }\n\n    public static void init(CapConfig config) {\n        Logger.getInstance().loadConfig(config);\n    }\n\n    private void loadConfig(CapConfig config) {\n        Logger.config = config;\n    }\n\n    public static String tags(String... subtags) {\n        if (subtags != null && subtags.length > 0) {\n            return LOG_TAG_CORE + \"/\" + TextUtils.join(\"/\", subtags);\n        }\n\n        return LOG_TAG_CORE;\n    }\n\n    public static void verbose(String message) {\n        verbose(LOG_TAG_CORE, message);\n    }\n\n    public static void verbose(String tag, String message) {\n        if (!shouldLog()) {\n            return;\n        }\n\n        Log.v(tag, message);\n    }\n\n    public static void debug(String message) {\n        debug(LOG_TAG_CORE, message);\n    }\n\n    public static void debug(String tag, String message) {\n        if (!shouldLog()) {\n            return;\n        }\n\n        Log.d(tag, message);\n    }\n\n    public static void info(String message) {\n        info(LOG_TAG_CORE, message);\n    }\n\n    public static void info(String tag, String message) {\n        if (!shouldLog()) {\n            return;\n        }\n\n        Log.i(tag, message);\n    }\n\n    public static void warn(String message) {\n        warn(LOG_TAG_CORE, message);\n    }\n\n    public static void warn(String tag, String message) {\n        if (!shouldLog()) {\n            return;\n        }\n\n        Log.w(tag, message);\n    }\n\n    public static void error(String message) {\n        error(LOG_TAG_CORE, message, null);\n    }\n\n    public static void error(String message, Throwable e) {\n        error(LOG_TAG_CORE, message, e);\n    }\n\n    public static void error(String tag, String message, Throwable e) {\n        if (!shouldLog()) {\n            return;\n        }\n\n        Log.e(tag, message, e);\n    }\n\n    public static boolean shouldLog() {\n        return config == null || config.isLoggingEnabled();\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java",
    "content": "package com.getcapacitor;\n\nimport android.webkit.JavascriptInterface;\nimport android.webkit.WebView;\nimport androidx.webkit.JavaScriptReplyProxy;\nimport androidx.webkit.WebViewCompat;\nimport androidx.webkit.WebViewFeature;\nimport org.apache.cordova.PluginManager;\n\n/**\n * MessageHandler handles messages from the WebView, dispatching them\n * to plugins.\n */\npublic class MessageHandler {\n\n    private Bridge bridge;\n    private WebView webView;\n    private PluginManager cordovaPluginManager;\n    private JavaScriptReplyProxy javaScriptReplyProxy;\n\n    public MessageHandler(Bridge bridge, WebView webView, PluginManager cordovaPluginManager) {\n        this.bridge = bridge;\n        this.webView = webView;\n        this.cordovaPluginManager = cordovaPluginManager;\n\n        if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && !bridge.getConfig().isUsingLegacyBridge()) {\n            WebViewCompat.WebMessageListener capListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {\n                if (isMainFrame) {\n                    postMessage(message.getData());\n                    javaScriptReplyProxy = replyProxy;\n                } else {\n                    Logger.warn(\"Plugin execution is allowed in Main Frame only\");\n                }\n            };\n            try {\n                WebViewCompat.addWebMessageListener(webView, \"androidBridge\", bridge.getAllowedOriginRules(), capListener);\n            } catch (Exception ex) {\n                webView.addJavascriptInterface(this, \"androidBridge\");\n            }\n        } else {\n            webView.addJavascriptInterface(this, \"androidBridge\");\n        }\n    }\n\n    /**\n     * The main message handler that will be called from JavaScript\n     * to send a message to the native bridge.\n     * @param jsonStr\n     */\n    @JavascriptInterface\n    @SuppressWarnings(\"unused\")\n    public void postMessage(String jsonStr) {\n        try {\n            JSObject postData = new JSObject(jsonStr);\n\n            String type = postData.getString(\"type\");\n\n            boolean typeIsNotNull = type != null;\n            boolean isCordovaPlugin = typeIsNotNull && type.equals(\"cordova\");\n            boolean isJavaScriptError = typeIsNotNull && type.equals(\"js.error\");\n\n            String callbackId = postData.getString(\"callbackId\");\n\n            if (isCordovaPlugin) {\n                String service = postData.getString(\"service\");\n                String action = postData.getString(\"action\");\n                String actionArgs = postData.getString(\"actionArgs\");\n\n                Logger.verbose(\n                    Logger.tags(\"Plugin\"),\n                    \"To native (Cordova plugin): callbackId: \" +\n                        callbackId +\n                        \", service: \" +\n                        service +\n                        \", action: \" +\n                        action +\n                        \", actionArgs: \" +\n                        actionArgs\n                );\n\n                this.callCordovaPluginMethod(callbackId, service, action, actionArgs);\n            } else if (isJavaScriptError) {\n                Logger.error(\"JavaScript Error: \" + jsonStr);\n            } else {\n                String pluginId = postData.getString(\"pluginId\");\n                String methodName = postData.getString(\"methodName\");\n                JSObject methodData = postData.getJSObject(\"options\", new JSObject());\n\n                Logger.verbose(\n                    Logger.tags(\"Plugin\"),\n                    \"To native (Capacitor plugin): callbackId: \" + callbackId + \", pluginId: \" + pluginId + \", methodName: \" + methodName\n                );\n\n                this.callPluginMethod(callbackId, pluginId, methodName, methodData);\n            }\n        } catch (Exception ex) {\n            Logger.error(\"Post message error:\", ex);\n        }\n    }\n\n    public void sendResponseMessage(PluginCall call, PluginResult successResult, PluginResult errorResult) {\n        try {\n            PluginResult data = new PluginResult();\n            data.put(\"save\", call.isKeptAlive());\n            data.put(\"callbackId\", call.getCallbackId());\n            data.put(\"pluginId\", call.getPluginId());\n            data.put(\"methodName\", call.getMethodName());\n\n            boolean pluginResultInError = errorResult != null;\n            if (pluginResultInError) {\n                data.put(\"success\", false);\n                data.put(\"error\", errorResult);\n                Logger.debug(\"Sending plugin error: \" + data.toString());\n            } else {\n                data.put(\"success\", true);\n                if (successResult != null) {\n                    data.put(\"data\", successResult);\n                }\n            }\n\n            boolean isValidCallbackId = !call.getCallbackId().equals(PluginCall.CALLBACK_ID_DANGLING);\n            if (isValidCallbackId) {\n                if (bridge.getConfig().isUsingLegacyBridge()) {\n                    legacySendResponseMessage(data);\n                } else if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && javaScriptReplyProxy != null) {\n                    javaScriptReplyProxy.postMessage(data.toString());\n                } else {\n                    legacySendResponseMessage(data);\n                }\n            } else {\n                bridge.getApp().fireRestoredResult(data);\n            }\n        } catch (Exception ex) {\n            Logger.error(\"sendResponseMessage: error: \" + ex);\n        }\n        if (!call.isKeptAlive()) {\n            call.release(bridge);\n        }\n    }\n\n    private void legacySendResponseMessage(PluginResult data) {\n        final String runScript = \"window.Capacitor.fromNative(\" + data.toString() + \")\";\n        final WebView webView = this.webView;\n        webView.post(() -> webView.evaluateJavascript(runScript, null));\n    }\n\n    private void callPluginMethod(String callbackId, String pluginId, String methodName, JSObject methodData) {\n        PluginCall call = new PluginCall(this, pluginId, callbackId, methodName, methodData);\n        bridge.callPluginMethod(pluginId, methodName, call);\n    }\n\n    private void callCordovaPluginMethod(String callbackId, String service, String action, String actionArgs) {\n        bridge.execute(() -> {\n            cordovaPluginManager.exec(service, action, callbackId, actionArgs);\n        });\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/NativePlugin.java",
    "content": "package com.getcapacitor;\n\nimport com.getcapacitor.annotation.CapacitorPlugin;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Base annotation for all Plugins\n * @deprecated\n * <p> Use {@link CapacitorPlugin} instead\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Deprecated\npublic @interface NativePlugin {\n    /**\n     * Request codes this plugin uses and responds to, in order to tie\n     * Android events back the plugin to handle\n     */\n    int[] requestCodes() default {};\n\n    /**\n     * Permissions this plugin needs, in order to make permission requests\n     * easy if the plugin only needs basic permission prompting\n     */\n    String[] permissions() default {};\n\n    /**\n     * The request code to use when automatically requesting permissions\n     */\n    int permissionRequestCode() default 9000;\n\n    /**\n     * A custom name for the plugin, otherwise uses the\n     * simple class name.\n     */\n    String name() default \"\";\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PermissionState.java",
    "content": "package com.getcapacitor;\n\nimport java.util.Locale;\n\n/**\n * Represents the state of a permission\n *\n * @since 3.0.0\n */\npublic enum PermissionState {\n    GRANTED(\"granted\"),\n    DENIED(\"denied\"),\n    PROMPT(\"prompt\"),\n    PROMPT_WITH_RATIONALE(\"prompt-with-rationale\");\n\n    private String state;\n\n    PermissionState(String state) {\n        this.state = state;\n    }\n\n    @Override\n    public String toString() {\n        return state;\n    }\n\n    public static PermissionState byState(String state) {\n        state = state.toUpperCase(Locale.ROOT).replace('-', '_');\n        return valueOf(state);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/Plugin.java",
    "content": "package com.getcapacitor;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.content.res.Configuration;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport androidx.activity.result.ActivityResult;\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.app.ActivityCompat;\nimport com.getcapacitor.annotation.ActivityCallback;\nimport com.getcapacitor.annotation.CapacitorPlugin;\nimport com.getcapacitor.annotation.Permission;\nimport com.getcapacitor.annotation.PermissionCallback;\nimport com.getcapacitor.util.PermissionHelper;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport org.json.JSONException;\n\n/**\n * Plugin is the base class for all plugins, containing a number of\n * convenient features for interacting with the {@link Bridge}, managing\n * plugin permissions, tracking lifecycle events, and more.\n *\n * You should inherit from this class when creating new plugins, along with\n * adding the {@link CapacitorPlugin} annotation to add additional required\n * metadata about the Plugin\n */\npublic class Plugin {\n\n    // The key we will use inside of a persisted Bundle for the JSON blob\n    // for a plugin call options.\n    private static final String BUNDLE_PERSISTED_OPTIONS_JSON_KEY = \"_json\";\n\n    // Reference to the Bridge\n    protected Bridge bridge;\n\n    // Reference to the PluginHandle wrapper for this Plugin\n    protected PluginHandle handle;\n\n    /**\n     * A way for plugins to quickly save a call that they will need to reference\n     * between activity/permissions starts/requests\n     *\n     * @deprecated store calls on the bridge using the methods\n     * {@link com.getcapacitor.Bridge#saveCall(PluginCall)},\n     * {@link com.getcapacitor.Bridge#getSavedCall(String)} and\n     * {@link com.getcapacitor.Bridge#releaseCall(PluginCall)}\n     */\n    @Deprecated\n    protected PluginCall savedLastCall;\n\n    // Stored event listeners\n    private final Map<String, List<PluginCall>> eventListeners;\n\n    /**\n     * Launchers used by the plugin to handle activity results\n     */\n    private final Map<String, ActivityResultLauncher<Intent>> activityLaunchers = new HashMap<>();\n\n    /**\n     * Launchers used by the plugin to handle permission results\n     */\n    private final Map<String, ActivityResultLauncher<String[]>> permissionLaunchers = new HashMap<>();\n\n    private String lastPluginCallId;\n\n    // Stored results of an event if an event was fired and\n    // no listeners were attached yet. Only stores the last value.\n    private final Map<String, List<JSObject>> retainedEventArguments;\n\n    public Plugin() {\n        eventListeners = new HashMap<>();\n        retainedEventArguments = new HashMap<>();\n    }\n\n    /**\n     * Called when the plugin has been connected to the bridge\n     * and is ready to start initializing.\n     */\n    public void load() {}\n\n    /**\n     * Registers activity result launchers defined on plugins, used for permission requests and\n     * activities started for result.\n     */\n    void initializeActivityLaunchers() {\n        List<Method> pluginClassMethods = new ArrayList<>();\n        for (\n            Class<?> pluginCursor = getClass();\n            !pluginCursor.getName().equals(Object.class.getName());\n            pluginCursor = pluginCursor.getSuperclass()\n        ) {\n            pluginClassMethods.addAll(Arrays.asList(pluginCursor.getDeclaredMethods()));\n        }\n\n        for (final Method method : pluginClassMethods) {\n            if (method.isAnnotationPresent(ActivityCallback.class)) {\n                // register callbacks annotated with ActivityCallback for activity results\n                ActivityResultLauncher<Intent> launcher = bridge.registerForActivityResult(\n                    new ActivityResultContracts.StartActivityForResult(),\n                    (result) -> triggerActivityCallback(method, result)\n                );\n\n                activityLaunchers.put(method.getName(), launcher);\n            } else if (method.isAnnotationPresent(PermissionCallback.class)) {\n                // register callbacks annotated with PermissionCallback for permission results\n                ActivityResultLauncher<String[]> launcher = bridge.registerForActivityResult(\n                    new ActivityResultContracts.RequestMultiplePermissions(),\n                    (permissions) -> triggerPermissionCallback(method, permissions)\n                );\n\n                permissionLaunchers.put(method.getName(), launcher);\n            }\n        }\n    }\n\n    private void triggerPermissionCallback(Method method, Map<String, Boolean> permissionResultMap) {\n        PluginCall savedCall = bridge.getPermissionCall(handle.getId());\n\n        // validate permissions and invoke the permission result callback\n        if (bridge.validatePermissions(this, savedCall, permissionResultMap)) {\n            try {\n                method.setAccessible(true);\n                method.invoke(this, savedCall);\n            } catch (IllegalAccessException | InvocationTargetException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private void triggerActivityCallback(Method method, ActivityResult result) {\n        PluginCall savedCall = bridge.getSavedCall(lastPluginCallId);\n        if (savedCall == null) {\n            savedCall = bridge.getPluginCallForLastActivity();\n        }\n        // invoke the activity result callback\n        try {\n            method.setAccessible(true);\n            method.invoke(this, savedCall, result);\n        } catch (IllegalAccessException | InvocationTargetException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * Start activity for result with the provided Intent and resolve with the provided callback method name.\n     * <p>\n     * If there is no registered activity callback for the method name passed in, the call will\n     * be rejected. Make sure a valid activity result callback method is registered using the\n     * {@link ActivityCallback} annotation.\n     *\n     * @param call the plugin call\n     * @param intent the intent used to start an activity\n     * @param callbackName the name of the callback to run when the launched activity is finished\n     * @since 3.0.0\n     */\n    public void startActivityForResult(PluginCall call, Intent intent, String callbackName) {\n        ActivityResultLauncher<Intent> activityResultLauncher = getActivityLauncherOrReject(call, callbackName);\n        if (activityResultLauncher == null) {\n            // return when null since call was rejected in getLauncherOrReject\n            return;\n        }\n        bridge.setPluginCallForLastActivity(call);\n        lastPluginCallId = call.getCallbackId();\n        bridge.saveCall(call);\n        activityResultLauncher.launch(intent);\n    }\n\n    private void permissionActivityResult(PluginCall call, String[] permissionStrings, String callbackName) {\n        ActivityResultLauncher<String[]> permissionResultLauncher = getPermissionLauncherOrReject(call, callbackName);\n        if (permissionResultLauncher == null) {\n            // return when null since call was rejected in getLauncherOrReject\n            return;\n        }\n\n        bridge.savePermissionCall(call);\n        permissionResultLauncher.launch(permissionStrings);\n    }\n\n    /**\n     * Get the main {@link Context} for the current Activity (your app)\n     * @return the Context for the current activity\n     */\n    public Context getContext() {\n        return this.bridge.getContext();\n    }\n\n    /**\n     * Get the main {@link Activity} for the app\n     * @return the Activity for the current app\n     */\n    public AppCompatActivity getActivity() {\n        return this.bridge.getActivity();\n    }\n\n    /**\n     * Set the Bridge instance for this plugin\n     * @param bridge\n     */\n    public void setBridge(Bridge bridge) {\n        this.bridge = bridge;\n    }\n\n    /**\n     * Get the Bridge instance for this plugin\n     */\n    public Bridge getBridge() {\n        return this.bridge;\n    }\n\n    /**\n     * Set the wrapper {@link PluginHandle} instance for this plugin that\n     * contains additional metadata about the Plugin instance (such\n     * as indexed methods for reflection, and {@link CapacitorPlugin} annotation data).\n     * @param pluginHandle\n     */\n    public void setPluginHandle(PluginHandle pluginHandle) {\n        this.handle = pluginHandle;\n    }\n\n    /**\n     * Return the wrapper {@link PluginHandle} for this plugin.\n     *\n     * This wrapper contains additional metadata about the plugin instance,\n     * such as indexed methods for reflection, and {@link CapacitorPlugin} annotation data).\n     * @return\n     */\n    public PluginHandle getPluginHandle() {\n        return this.handle;\n    }\n\n    /**\n     * Get the root App ID\n     * @return\n     */\n    public String getAppId() {\n        return getContext().getPackageName();\n    }\n\n    /**\n     * Called to save a {@link PluginCall} in order to reference it\n     * later, such as in an activity or permissions result handler\n     * @deprecated use {@link Bridge#saveCall(PluginCall)}\n     *\n     * @param lastCall\n     */\n    @Deprecated\n    public void saveCall(PluginCall lastCall) {\n        this.savedLastCall = lastCall;\n    }\n\n    /**\n     * Set the last saved call to null to free memory\n     * @deprecated use {@link PluginCall#release(Bridge)}\n     */\n    @Deprecated\n    public void freeSavedCall() {\n        this.savedLastCall.release(bridge);\n        this.savedLastCall = null;\n    }\n\n    /**\n     * Get the last saved call, if any\n     * @deprecated use {@link Bridge#getSavedCall(String)}\n     *\n     * @return\n     */\n    @Deprecated\n    public PluginCall getSavedCall() {\n        return this.savedLastCall;\n    }\n\n    /**\n     * Get the config options for this plugin.\n     *\n     * @return a config object representing the plugin config options, or an empty config\n     * if none exists\n     */\n    public PluginConfig getConfig() {\n        return bridge.getConfig().getPluginConfiguration(handle.getId());\n    }\n\n    /**\n     * Get the value for a key on the config for this plugin.\n     * @deprecated use {@link #getConfig()} and access config values using the methods available\n     * depending on the type.\n     *\n     * @param key the key for the config value\n     * @return some object containing the value from the config\n     */\n    @Deprecated\n    public Object getConfigValue(String key) {\n        try {\n            PluginConfig pluginConfig = getConfig();\n            return pluginConfig.getConfigJSON().get(key);\n        } catch (JSONException ex) {\n            return null;\n        }\n    }\n\n    /**\n     * Check whether any of the given permissions has been defined in the AndroidManifest.xml\n     * @deprecated use {@link #isPermissionDeclared(String)}\n     *\n     * @param permissions\n     * @return\n     */\n    @Deprecated\n    public boolean hasDefinedPermissions(String[] permissions) {\n        for (String permission : permissions) {\n            if (!PermissionHelper.hasDefinedPermission(getContext(), permission)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Check if all annotated permissions have been defined in the AndroidManifest.xml\n     * @deprecated use {@link #isPermissionDeclared(String)}\n     *\n     * @return true if permissions are all defined in the Manifest\n     */\n    @Deprecated\n    public boolean hasDefinedRequiredPermissions() {\n        CapacitorPlugin annotation = handle.getPluginAnnotation();\n        if (annotation == null) {\n            // Check for legacy plugin annotation, @NativePlugin\n            NativePlugin legacyAnnotation = handle.getLegacyPluginAnnotation();\n            return hasDefinedPermissions(legacyAnnotation.permissions());\n        } else {\n            for (Permission perm : annotation.permissions()) {\n                for (String permString : perm.strings()) {\n                    if (!PermissionHelper.hasDefinedPermission(getContext(), permString)) {\n                        return false;\n                    }\n                }\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Checks if the given permission alias is correctly declared in AndroidManifest.xml\n     * @param alias a permission alias defined on the plugin\n     * @return true only if all permissions associated with the given alias are declared in the manifest\n     */\n    public boolean isPermissionDeclared(String alias) {\n        CapacitorPlugin annotation = handle.getPluginAnnotation();\n        if (annotation != null) {\n            for (Permission perm : annotation.permissions()) {\n                if (alias.equalsIgnoreCase(perm.alias())) {\n                    boolean result = true;\n                    for (String permString : perm.strings()) {\n                        result = result && PermissionHelper.hasDefinedPermission(getContext(), permString);\n                    }\n\n                    return result;\n                }\n            }\n        }\n\n        Logger.error(String.format(\"isPermissionDeclared: No alias defined for %s \" + \"or missing @CapacitorPlugin annotation.\", alias));\n        return false;\n    }\n\n    /**\n     * Check whether the given permission has been granted by the user\n     * @deprecated use {@link #getPermissionState(String)} and {@link #getPermissionStates()} to get\n     * the states of permissions defined on the Plugin in conjunction with the @CapacitorPlugin\n     * annotation. Use the Android API {@link ActivityCompat#checkSelfPermission(Context, String)}\n     * methods to check permissions with Android permission strings\n     *\n     * @param permission\n     * @return\n     */\n    @Deprecated\n    public boolean hasPermission(String permission) {\n        return ActivityCompat.checkSelfPermission(this.getContext(), permission) == PackageManager.PERMISSION_GRANTED;\n    }\n\n    /**\n     * If the plugin annotation specified a set of permissions, this method checks if each is\n     * granted\n     * @deprecated use {@link #getPermissionState(String)} or {@link #getPermissionStates()} to\n     * check whether permissions are granted or not\n     *\n     * @return\n     */\n    @Deprecated\n    public boolean hasRequiredPermissions() {\n        CapacitorPlugin annotation = handle.getPluginAnnotation();\n        if (annotation == null) {\n            // Check for legacy plugin annotation, @NativePlugin\n            NativePlugin legacyAnnotation = handle.getLegacyPluginAnnotation();\n            for (String perm : legacyAnnotation.permissions()) {\n                if (ActivityCompat.checkSelfPermission(this.getContext(), perm) != PackageManager.PERMISSION_GRANTED) {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        for (Permission perm : annotation.permissions()) {\n            for (String permString : perm.strings()) {\n                if (ActivityCompat.checkSelfPermission(this.getContext(), permString) != PackageManager.PERMISSION_GRANTED) {\n                    return false;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Request all of the specified permissions in the CapacitorPlugin annotation (if any)\n     *\n     * If there is no registered permission callback for the PluginCall passed in, the call will\n     * be rejected. Make sure a valid permission callback method is registered using the\n     * {@link PermissionCallback} annotation.\n     *\n     * @since 3.0.0\n     * @param call the plugin call\n     * @param callbackName the name of the callback to run when the permission request is complete\n     */\n    protected void requestAllPermissions(@NonNull PluginCall call, @NonNull String callbackName) {\n        CapacitorPlugin annotation = handle.getPluginAnnotation();\n        if (annotation != null) {\n            HashSet<String> perms = new HashSet<>();\n            for (Permission perm : annotation.permissions()) {\n                perms.addAll(Arrays.asList(perm.strings()));\n            }\n\n            permissionActivityResult(call, perms.toArray(new String[0]), callbackName);\n        }\n    }\n\n    /**\n     * Request permissions using an alias defined on the plugin.\n     *\n     * If there is no registered permission callback for the PluginCall passed in, the call will\n     * be rejected. Make sure a valid permission callback method is registered using the\n     * {@link PermissionCallback} annotation.\n     *\n     * @param alias an alias defined on the plugin\n     * @param call  the plugin call involved in originating the request\n     * @param callbackName the name of the callback to run when the permission request is complete\n     */\n    protected void requestPermissionForAlias(@NonNull String alias, @NonNull PluginCall call, @NonNull String callbackName) {\n        requestPermissionForAliases(new String[] { alias }, call, callbackName);\n    }\n\n    /**\n     * Request permissions using aliases defined on the plugin.\n     *\n     * If there is no registered permission callback for the PluginCall passed in, the call will\n     * be rejected. Make sure a valid permission callback method is registered using the\n     * {@link PermissionCallback} annotation.\n     *\n     * @param aliases a set of aliases defined on the plugin\n     * @param call    the plugin call involved in originating the request\n     * @param callbackName the name of the callback to run when the permission request is complete\n     */\n    protected void requestPermissionForAliases(@NonNull String[] aliases, @NonNull PluginCall call, @NonNull String callbackName) {\n        if (aliases.length == 0) {\n            Logger.error(\"No permission alias was provided\");\n            return;\n        }\n\n        String[] permissions = getPermissionStringsForAliases(aliases);\n\n        if (permissions.length > 0) {\n            permissionActivityResult(call, permissions, callbackName);\n        }\n    }\n\n    /**\n     * Gets the Android permission strings defined on the {@link CapacitorPlugin} annotation with\n     * the provided aliases.\n     *\n     * @param aliases aliases for permissions defined on the plugin\n     * @return Android permission strings associated with the provided aliases, if exists\n     */\n    private String[] getPermissionStringsForAliases(@NonNull String[] aliases) {\n        CapacitorPlugin annotation = handle.getPluginAnnotation();\n        HashSet<String> perms = new HashSet<>();\n        for (Permission perm : annotation.permissions()) {\n            if (Arrays.asList(aliases).contains(perm.alias())) {\n                perms.addAll(Arrays.asList(perm.strings()));\n            }\n        }\n\n        return perms.toArray(new String[0]);\n    }\n\n    /**\n     * Gets the activity launcher associated with the calling methodName, or rejects the call if\n     * no registered launcher exists\n     *\n     * @param call       the plugin call\n     * @param methodName the name of the activity callback method\n     * @return a launcher, or null if none found\n     */\n    private @Nullable ActivityResultLauncher<Intent> getActivityLauncherOrReject(PluginCall call, String methodName) {\n        ActivityResultLauncher<Intent> activityLauncher = activityLaunchers.get(methodName);\n\n        // if there is no registered launcher, reject the call with an error and return null\n        if (activityLauncher == null) {\n            String registerError =\n                \"There is no ActivityCallback method registered for the name: %s. \" +\n                \"Please define a callback method annotated with @ActivityCallback \" +\n                \"that receives arguments: (PluginCall, ActivityResult)\";\n            registerError = String.format(Locale.US, registerError, methodName);\n            Logger.error(registerError);\n            call.reject(registerError);\n            return null;\n        }\n\n        return activityLauncher;\n    }\n\n    /**\n     * Gets the permission launcher associated with the calling methodName, or rejects the call if\n     * no registered launcher exists\n     *\n     * @param call       the plugin call\n     * @param methodName the name of the permission callback method\n     * @return a launcher, or null if none found\n     */\n    private @Nullable ActivityResultLauncher<String[]> getPermissionLauncherOrReject(PluginCall call, String methodName) {\n        ActivityResultLauncher<String[]> permissionLauncher = permissionLaunchers.get(methodName);\n\n        // if there is no registered launcher, reject the call with an error and return null\n        if (permissionLauncher == null) {\n            String registerError =\n                \"There is no PermissionCallback method registered for the name: %s. \" +\n                \"Please define a callback method annotated with @PermissionCallback \" +\n                \"that receives arguments: (PluginCall)\";\n            registerError = String.format(Locale.US, registerError, methodName);\n            Logger.error(registerError);\n            call.reject(registerError);\n            return null;\n        }\n\n        return permissionLauncher;\n    }\n\n    /**\n     * Request all of the specified permissions in the CapacitorPlugin annotation (if any)\n     *\n     * @deprecated use {@link #requestAllPermissions(PluginCall, String)} in conjunction with @CapacitorPlugin\n     */\n    @Deprecated\n    public void pluginRequestAllPermissions() {\n        NativePlugin legacyAnnotation = handle.getLegacyPluginAnnotation();\n        ActivityCompat.requestPermissions(getActivity(), legacyAnnotation.permissions(), legacyAnnotation.permissionRequestCode());\n    }\n\n    /**\n     * Helper for requesting a specific permission\n     *\n     * @param permission  the permission to request\n     * @param requestCode the requestCode to use to associate the result with the plugin\n     * @deprecated use {@link #requestPermissionForAlias(String, PluginCall, String)} in conjunction with @CapacitorPlugin\n     */\n    @Deprecated\n    public void pluginRequestPermission(String permission, int requestCode) {\n        ActivityCompat.requestPermissions(getActivity(), new String[] { permission }, requestCode);\n    }\n\n    /**\n     * Helper for requesting specific permissions\n     * @deprecated use {@link #requestPermissionForAliases(String[], PluginCall, String)} in conjunction\n     * with @CapacitorPlugin\n     *\n     * @param permissions the set of permissions to request\n     * @param requestCode the requestCode to use to associate the result with the plugin\n     */\n    @Deprecated\n    public void pluginRequestPermissions(String[] permissions, int requestCode) {\n        ActivityCompat.requestPermissions(getActivity(), permissions, requestCode);\n    }\n\n    /**\n     * Get the permission state for the provided permission alias.\n     *\n     * @param alias the permission alias to get\n     * @return the state of the provided permission alias or null\n     */\n    public PermissionState getPermissionState(String alias) {\n        return getPermissionStates().get(alias);\n    }\n\n    /**\n     * Helper to check all permissions defined on a plugin and see the state of each.\n     *\n     * @since 3.0.0\n     * @return A mapping of permission aliases to the associated granted status.\n     */\n    public Map<String, PermissionState> getPermissionStates() {\n        return bridge.getPermissionStates(this);\n    }\n\n    /**\n     * Add a listener for the given event\n     * @param eventName\n     * @param call\n     */\n    private void addEventListener(String eventName, PluginCall call) {\n        List<PluginCall> listeners = eventListeners.get(eventName);\n        if (listeners == null || listeners.isEmpty()) {\n            listeners = new ArrayList<>();\n            eventListeners.put(eventName, listeners);\n\n            // Must add the call before sending retained arguments\n            listeners.add(call);\n\n            sendRetainedArgumentsForEvent(eventName);\n        } else {\n            listeners.add(call);\n        }\n    }\n\n    /**\n     * Remove a listener from the given event\n     * @param eventName\n     * @param call\n     */\n    private void removeEventListener(String eventName, PluginCall call) {\n        List<PluginCall> listeners = eventListeners.get(eventName);\n        if (listeners == null) {\n            return;\n        }\n\n        listeners.remove(call);\n    }\n\n    /**\n     * Notify all listeners that an event occurred\n     * @param eventName\n     * @param data\n     */\n    protected void notifyListeners(String eventName, JSObject data, boolean retainUntilConsumed) {\n        Logger.verbose(getLogTag(), \"Notifying listeners for event \" + eventName);\n        List<PluginCall> listeners = eventListeners.get(eventName);\n        if (listeners == null || listeners.isEmpty()) {\n            Logger.debug(getLogTag(), \"No listeners found for event \" + eventName);\n            if (retainUntilConsumed) {\n                List<JSObject> argList = retainedEventArguments.get(eventName);\n\n                if (argList == null) {\n                    argList = new ArrayList<JSObject>();\n                }\n\n                argList.add(data);\n                retainedEventArguments.put(eventName, argList);\n            }\n            return;\n        }\n\n        CopyOnWriteArrayList<PluginCall> listenersCopy = new CopyOnWriteArrayList(listeners);\n        for (PluginCall call : listenersCopy) {\n            call.resolve(data);\n        }\n    }\n\n    /**\n     * Notify all listeners that an event occurred\n     * This calls {@link Plugin#notifyListeners(String, JSObject, boolean)}\n     * with retainUntilConsumed set to false\n     * @param eventName\n     * @param data\n     */\n    protected void notifyListeners(String eventName, JSObject data) {\n        notifyListeners(eventName, data, false);\n    }\n\n    /**\n     * Check if there are any listeners for the given event\n     */\n    protected boolean hasListeners(String eventName) {\n        List<PluginCall> listeners = eventListeners.get(eventName);\n        if (listeners == null) {\n            return false;\n        }\n        return !listeners.isEmpty();\n    }\n\n    /**\n     * Send retained arguments (if any) for this event. This\n     * is called only when the first listener for an event is added\n     * @param eventName\n     */\n    private void sendRetainedArgumentsForEvent(String eventName) {\n        // copy retained args and null source to prevent potential race conditions\n        List<JSObject> retainedArgs = retainedEventArguments.get(eventName);\n        if (retainedArgs == null) {\n            return;\n        }\n\n        retainedEventArguments.remove(eventName);\n\n        for (JSObject retained : retainedArgs) {\n            notifyListeners(eventName, retained);\n        }\n    }\n\n    /**\n     * Exported plugin call for adding a listener to this plugin\n     * @param call\n     */\n    @SuppressWarnings(\"unused\")\n    @PluginMethod(returnType = PluginMethod.RETURN_NONE)\n    public void addListener(PluginCall call) {\n        String eventName = call.getString(\"eventName\");\n        call.setKeepAlive(true);\n        addEventListener(eventName, call);\n    }\n\n    /**\n     * Exported plugin call to remove a listener from this plugin\n     * @param call\n     */\n    @SuppressWarnings(\"unused\")\n    @PluginMethod(returnType = PluginMethod.RETURN_NONE)\n    public void removeListener(PluginCall call) {\n        String eventName = call.getString(\"eventName\");\n        String callbackId = call.getString(\"callbackId\");\n        PluginCall savedCall = bridge.getSavedCall(callbackId);\n        if (savedCall != null) {\n            removeEventListener(eventName, savedCall);\n            bridge.releaseCall(savedCall);\n        }\n    }\n\n    /**\n     * Exported plugin call to remove all listeners from this plugin\n     * @param call\n     */\n    @SuppressWarnings(\"unused\")\n    @PluginMethod(returnType = PluginMethod.RETURN_PROMISE)\n    public void removeAllListeners(PluginCall call) {\n        eventListeners.clear();\n        call.resolve();\n    }\n\n    public void removeAllListeners() {\n        eventListeners.clear();\n    }\n\n    /**\n     * Exported plugin call for checking the granted status for each permission\n     * declared on the plugin. This plugin call responds with a mapping of permissions to\n     * the associated granted status.\n     *\n     * @since 3.0.0\n     */\n    @PluginMethod\n    @PermissionCallback\n    public void checkPermissions(PluginCall pluginCall) {\n        Map<String, PermissionState> permissionsResult = getPermissionStates();\n\n        if (permissionsResult.size() == 0) {\n            // if no permissions are defined on the plugin, resolve undefined\n            pluginCall.resolve();\n        } else {\n            JSObject permissionsResultJSON = new JSObject();\n            for (Map.Entry<String, PermissionState> entry : permissionsResult.entrySet()) {\n                permissionsResultJSON.put(entry.getKey(), entry.getValue());\n            }\n\n            pluginCall.resolve(permissionsResultJSON);\n        }\n    }\n\n    /**\n     * Exported plugin call to request all permissions for this plugin.\n     * To manually request permissions within a plugin use:\n     * {@link #requestAllPermissions(PluginCall, String)}, or\n     * {@link #requestPermissionForAlias(String, PluginCall, String)}, or\n     * {@link #requestPermissionForAliases(String[], PluginCall, String)}\n     *\n     * @param call the plugin call\n     */\n    @PluginMethod\n    public void requestPermissions(PluginCall call) {\n        CapacitorPlugin annotation = handle.getPluginAnnotation();\n        if (annotation == null) {\n            handleLegacyPermission(call);\n        } else {\n            // handle permission requests for plugins defined with @CapacitorPlugin (since 3.0.0)\n            String[] permAliases = null;\n            Set<String> autoGrantPerms = new HashSet<>();\n\n            // If call was made with a list of specific permission aliases to request, save them\n            // to be requested\n            JSArray providedPerms = call.getArray(\"permissions\");\n            List<String> providedPermsList = null;\n\n            if (providedPerms != null) {\n                try {\n                    providedPermsList = providedPerms.toList();\n                } catch (JSONException ignore) {\n                    // do nothing\n                }\n            }\n\n            // If call was made without any custom permissions, request all from plugin annotation\n            Set<String> aliasSet = new HashSet<>();\n            if (providedPermsList == null || providedPermsList.isEmpty()) {\n                for (Permission perm : annotation.permissions()) {\n                    // If a permission is defined with no permission strings, separate it for auto-granting.\n                    // Otherwise, the alias is added to the list to be requested.\n                    if (perm.strings().length == 0 || (perm.strings().length == 1 && perm.strings()[0].isEmpty())) {\n                        if (!perm.alias().isEmpty()) {\n                            autoGrantPerms.add(perm.alias());\n                        }\n                    } else {\n                        aliasSet.add(perm.alias());\n                    }\n                }\n\n                permAliases = aliasSet.toArray(new String[0]);\n            } else {\n                for (Permission perm : annotation.permissions()) {\n                    if (providedPermsList.contains(perm.alias())) {\n                        aliasSet.add(perm.alias());\n                    }\n                }\n\n                if (aliasSet.isEmpty()) {\n                    call.reject(\"No valid permission alias was requested of this plugin.\");\n                } else {\n                    permAliases = aliasSet.toArray(new String[0]);\n                }\n            }\n\n            if (permAliases != null && permAliases.length > 0) {\n                // request permissions using provided aliases or all defined on the plugin\n                requestPermissionForAliases(permAliases, call, \"checkPermissions\");\n            } else if (!autoGrantPerms.isEmpty()) {\n                // if the plugin only has auto-grant permissions, return all as GRANTED\n                JSObject permissionsResults = new JSObject();\n\n                for (String perm : autoGrantPerms) {\n                    permissionsResults.put(perm, PermissionState.GRANTED.toString());\n                }\n\n                call.resolve(permissionsResults);\n            } else {\n                // no permissions are defined on the plugin, resolve undefined\n                call.resolve();\n            }\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private void handleLegacyPermission(PluginCall call) {\n        // handle permission requests for plugins defined with @NativePlugin (prior to 3.0.0)\n        NativePlugin legacyAnnotation = this.handle.getLegacyPluginAnnotation();\n        String[] perms = legacyAnnotation.permissions();\n        if (perms.length > 0) {\n            saveCall(call);\n            pluginRequestPermissions(perms, legacyAnnotation.permissionRequestCode());\n        } else {\n            call.resolve();\n        }\n    }\n\n    /**\n     * Handle request permissions result. A plugin using the deprecated {@link NativePlugin}\n     * should override this to handle the result, or this method will handle the result\n     * for our convenient requestPermissions call.\n     * @deprecated in favor of using callbacks in conjunction with {@link CapacitorPlugin}\n     *\n     * @param requestCode\n     * @param permissions\n     * @param grantResults\n     */\n    @Deprecated\n    protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {\n        if (!hasDefinedPermissions(permissions)) {\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"Missing the following permissions in AndroidManifest.xml:\\n\");\n            String[] missing = PermissionHelper.getUndefinedPermissions(getContext(), permissions);\n            for (String perm : missing) {\n                builder.append(perm + \"\\n\");\n            }\n            savedLastCall.reject(builder.toString());\n            savedLastCall = null;\n        }\n    }\n\n    /**\n     * Called before the app is destroyed to give a plugin the chance to\n     * save the last call options for a saved plugin. By default, this\n     * method saves the full JSON blob of the options call. Since Bundle sizes\n     * may be limited, plugins that expect to be called with large data\n     * objects (such as a file), should override this method and selectively\n     * store option values in a {@link Bundle} to avoid exceeding limits.\n     * @return a new {@link Bundle} with fields set from the options of the last saved {@link PluginCall}\n     */\n    protected Bundle saveInstanceState() {\n        PluginCall savedCall = bridge.getSavedCall(lastPluginCallId);\n\n        if (savedCall == null) {\n            return null;\n        }\n\n        Bundle ret = new Bundle();\n        JSObject callData = savedCall.getData();\n\n        if (callData != null) {\n            ret.putString(BUNDLE_PERSISTED_OPTIONS_JSON_KEY, callData.toString());\n        }\n\n        return ret;\n    }\n\n    /**\n     * Called when the app is opened with a previously un-handled\n     * activity response. If the plugin that started the activity\n     * stored data in {@link Plugin#saveInstanceState()} then this\n     * method will be called to allow the plugin to restore from that.\n     * @param state\n     */\n    protected void restoreState(Bundle state) {}\n\n    /**\n     * Handle activity result, should be overridden by each plugin\n     *\n     * @deprecated provide a callback method using the {@link ActivityCallback} annotation and use\n     * the {@link #startActivityForResult(PluginCall, Intent, String)} method\n     *\n     * @param requestCode\n     * @param resultCode\n     * @param data\n     */\n    @Deprecated\n    protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) {}\n\n    /**\n     * Handle onNewIntent\n     * @param intent\n     */\n    protected void handleOnNewIntent(Intent intent) {}\n\n    /**\n     * Handle onConfigurationChanged\n     * @param newConfig\n     */\n    protected void handleOnConfigurationChanged(Configuration newConfig) {}\n\n    /**\n     * Handle onStart\n     */\n    protected void handleOnStart() {}\n\n    /**\n     * Handle onRestart\n     */\n    protected void handleOnRestart() {}\n\n    /**\n     * Handle onResume\n     */\n    protected void handleOnResume() {}\n\n    /**\n     * Handle onPause\n     */\n    protected void handleOnPause() {}\n\n    /**\n     * Handle onStop\n     */\n    protected void handleOnStop() {}\n\n    /**\n     * Handle onDestroy\n     */\n    protected void handleOnDestroy() {}\n\n    /**\n     * Give the plugins a chance to take control when a URL is about to be loaded in the WebView.\n     * Returning true causes the WebView to abort loading the URL.\n     * Returning false causes the WebView to continue loading the URL.\n     * Returning null will defer to the default Capacitor policy\n     */\n    @SuppressWarnings(\"unused\")\n    public Boolean shouldOverrideLoad(Uri url) {\n        return null;\n    }\n\n    /**\n     * Start a new Activity.\n     *\n     * Note: This method must be used by all plugins instead of calling\n     * {@link Activity#startActivityForResult} as it associates the plugin with\n     * any resulting data from the new Activity even if this app\n     * is destroyed by the OS (to free up memory, for example).\n     * @param intent\n     * @param resultCode\n     */\n    @Deprecated\n    protected void startActivityForResult(PluginCall call, Intent intent, int resultCode) {\n        bridge.startActivityForPluginWithResult(call, intent, resultCode);\n    }\n\n    /**\n     * Execute the given runnable on the Bridge's task handler\n     * @param runnable\n     */\n    public void execute(Runnable runnable) {\n        bridge.execute(runnable);\n    }\n\n    /**\n     * Shortcut for getting the plugin log tag\n     * @param subTags\n     */\n    protected String getLogTag(String... subTags) {\n        return Logger.tags(subTags);\n    }\n\n    /**\n     * Gets a plugin log tag with the child's class name as subTag.\n     */\n    protected String getLogTag() {\n        return Logger.tags(this.getClass().getSimpleName());\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginCall.java",
    "content": "package com.getcapacitor;\n\nimport androidx.annotation.Nullable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n/**\n * Wraps a call from the web layer to native\n */\npublic class PluginCall {\n\n    /**\n     * A special callback id that indicates there is no matching callback\n     * on the client to associate any PluginCall results back to. This is used\n     * in the case of an app resuming with saved instance data, for example.\n     */\n    public static final String CALLBACK_ID_DANGLING = \"-1\";\n\n    private final MessageHandler msgHandler;\n    private final String pluginId;\n    private final String callbackId;\n    private final String methodName;\n    private final JSObject data;\n\n    private boolean keepAlive = false;\n\n    /**\n     * Indicates that this PluginCall was released, and should no longer be used\n     */\n    @Deprecated\n    private boolean isReleased = false;\n\n    public PluginCall(MessageHandler msgHandler, String pluginId, String callbackId, String methodName, JSObject data) {\n        this.msgHandler = msgHandler;\n        this.pluginId = pluginId;\n        this.callbackId = callbackId;\n        this.methodName = methodName;\n        this.data = data;\n    }\n\n    public void successCallback(PluginResult successResult) {\n        if (CALLBACK_ID_DANGLING.equals(this.callbackId)) {\n            // don't send back response if the callbackId was \"-1\"\n            return;\n        }\n\n        this.msgHandler.sendResponseMessage(this, successResult, null);\n    }\n\n    public void resolve(JSObject data) {\n        PluginResult result = new PluginResult(data);\n        this.msgHandler.sendResponseMessage(this, result, null);\n    }\n\n    public void resolve() {\n        this.msgHandler.sendResponseMessage(this, null, null);\n    }\n\n    public void errorCallback(String msg) {\n        PluginResult errorResult = new PluginResult();\n\n        try {\n            errorResult.put(\"message\", msg);\n        } catch (Exception jsonEx) {\n            Logger.error(Logger.tags(\"Plugin\"), jsonEx.toString(), null);\n        }\n\n        this.msgHandler.sendResponseMessage(this, null, errorResult);\n    }\n\n    public void reject(String msg, String code, Exception ex, JSObject data) {\n        PluginResult errorResult = new PluginResult();\n\n        if (ex != null) {\n            Logger.error(Logger.tags(\"Plugin\"), msg, ex);\n        }\n\n        try {\n            errorResult.put(\"message\", msg);\n            errorResult.put(\"code\", code);\n            if (null != data) {\n                errorResult.put(\"data\", data);\n            }\n        } catch (Exception jsonEx) {\n            Logger.error(Logger.tags(\"Plugin\"), jsonEx.getMessage(), jsonEx);\n        }\n\n        this.msgHandler.sendResponseMessage(this, null, errorResult);\n    }\n\n    public void reject(String msg, Exception ex, JSObject data) {\n        reject(msg, null, ex, data);\n    }\n\n    public void reject(String msg, String code, JSObject data) {\n        reject(msg, code, null, data);\n    }\n\n    public void reject(String msg, String code, Exception ex) {\n        reject(msg, code, ex, null);\n    }\n\n    public void reject(String msg, JSObject data) {\n        reject(msg, null, null, data);\n    }\n\n    public void reject(String msg, Exception ex) {\n        reject(msg, null, ex, null);\n    }\n\n    public void reject(String msg, String code) {\n        reject(msg, code, null, null);\n    }\n\n    public void reject(String msg) {\n        reject(msg, null, null, null);\n    }\n\n    public void unimplemented() {\n        unimplemented(\"not implemented\");\n    }\n\n    public void unimplemented(String msg) {\n        reject(msg, \"UNIMPLEMENTED\", null, null);\n    }\n\n    public void unavailable() {\n        unavailable(\"not available\");\n    }\n\n    public void unavailable(String msg) {\n        reject(msg, \"UNAVAILABLE\", null, null);\n    }\n\n    public String getPluginId() {\n        return this.pluginId;\n    }\n\n    public String getCallbackId() {\n        return this.callbackId;\n    }\n\n    public String getMethodName() {\n        return this.methodName;\n    }\n\n    public JSObject getData() {\n        return this.data;\n    }\n\n    @Nullable\n    public String getString(String name) {\n        return this.getString(name, null);\n    }\n\n    @Nullable\n    public String getString(String name, @Nullable String defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof String) {\n            return (String) value;\n        }\n        return defaultValue;\n    }\n\n    @Nullable\n    public Integer getInt(String name) {\n        return this.getInt(name, null);\n    }\n\n    @Nullable\n    public Integer getInt(String name, @Nullable Integer defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof Integer) {\n            return (Integer) value;\n        }\n        return defaultValue;\n    }\n\n    @Nullable\n    public Long getLong(String name) {\n        return this.getLong(name, null);\n    }\n\n    @Nullable\n    public Long getLong(String name, @Nullable Long defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof Long) {\n            return (Long) value;\n        }\n        return defaultValue;\n    }\n\n    @Nullable\n    public Float getFloat(String name) {\n        return this.getFloat(name, null);\n    }\n\n    @Nullable\n    public Float getFloat(String name, @Nullable Float defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof Float) {\n            return (Float) value;\n        }\n        if (value instanceof Double) {\n            return ((Double) value).floatValue();\n        }\n        if (value instanceof Integer) {\n            return ((Integer) value).floatValue();\n        }\n        return defaultValue;\n    }\n\n    @Nullable\n    public Double getDouble(String name) {\n        return this.getDouble(name, null);\n    }\n\n    @Nullable\n    public Double getDouble(String name, @Nullable Double defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof Double) {\n            return (Double) value;\n        }\n        if (value instanceof Float) {\n            return ((Float) value).doubleValue();\n        }\n        if (value instanceof Integer) {\n            return ((Integer) value).doubleValue();\n        }\n        return defaultValue;\n    }\n\n    @Nullable\n    public Boolean getBoolean(String name) {\n        return this.getBoolean(name, null);\n    }\n\n    @Nullable\n    public Boolean getBoolean(String name, @Nullable Boolean defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof Boolean) {\n            return (Boolean) value;\n        }\n        return defaultValue;\n    }\n\n    public JSObject getObject(String name) {\n        return this.getObject(name, null);\n    }\n\n    @Nullable\n    public JSObject getObject(String name, JSObject defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof JSONObject) {\n            try {\n                return JSObject.fromJSONObject((JSONObject) value);\n            } catch (JSONException ex) {\n                return defaultValue;\n            }\n        }\n        return defaultValue;\n    }\n\n    public JSArray getArray(String name) {\n        return this.getArray(name, null);\n    }\n\n    /**\n     * Get a JSONArray and turn it into a JSArray\n     * @param name\n     * @param defaultValue\n     * @return\n     */\n    @Nullable\n    public JSArray getArray(String name, JSArray defaultValue) {\n        Object value = this.data.opt(name);\n        if (value == null) {\n            return defaultValue;\n        }\n\n        if (value instanceof JSONArray) {\n            try {\n                JSONArray valueArray = (JSONArray) value;\n                List<Object> items = new ArrayList<>();\n                for (int i = 0; i < valueArray.length(); i++) {\n                    items.add(valueArray.get(i));\n                }\n                return new JSArray(items.toArray());\n            } catch (JSONException ex) {\n                return defaultValue;\n            }\n        }\n        return defaultValue;\n    }\n\n    /**\n     * @param name of the option to check\n     * @return boolean indicating if the plugin call has an option for the provided name.\n     * @deprecated Presence of a key should not be considered significant.\n     * Use typed accessors to check the value instead.\n     */\n    @Deprecated\n    public boolean hasOption(String name) {\n        return this.data.has(name);\n    }\n\n    /**\n     * Indicate that the Bridge should cache this call in order to call\n     * it again later. For example, the addListener system uses this to\n     * continuously call the call's callback (😆).\n     * @deprecated use {@link #setKeepAlive(Boolean)} instead\n     */\n    @Deprecated\n    public void save() {\n        setKeepAlive(true);\n    }\n\n    /**\n     * Indicate that the Bridge should cache this call in order to call\n     * it again later. For example, the addListener system uses this to\n     * continuously call the call's callback.\n     *\n     * @param keepAlive whether to keep the callback saved\n     */\n    public void setKeepAlive(Boolean keepAlive) {\n        this.keepAlive = keepAlive;\n    }\n\n    public void release(Bridge bridge) {\n        this.keepAlive = false;\n        bridge.releaseCall(this);\n        this.isReleased = true;\n    }\n\n    /**\n     * @deprecated use {@link #isKeptAlive()}\n     * @return true if the plugin call is kept alive\n     */\n    @Deprecated\n    public boolean isSaved() {\n        return isKeptAlive();\n    }\n\n    /**\n     * Gets the keepAlive value of the plugin call\n     * @return true if the plugin call is kept alive\n     */\n    public boolean isKeptAlive() {\n        return keepAlive;\n    }\n\n    @Deprecated\n    public boolean isReleased() {\n        return isReleased;\n    }\n\n    class PluginCallDataTypeException extends Exception {\n\n        PluginCallDataTypeException(String m) {\n            super(m);\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginConfig.java",
    "content": "package com.getcapacitor;\n\nimport com.getcapacitor.util.JSONUtils;\nimport org.json.JSONObject;\n\n/**\n * Represents the configuration options for plugins used by Capacitor\n */\npublic class PluginConfig {\n\n    /**\n     * The object containing plugin config values.\n     */\n    private final JSONObject config;\n\n    /**\n     * Constructs a PluginsConfig with the provided JSONObject value.\n     *\n     * @param config A plugin configuration expressed as a JSON Object\n     */\n    PluginConfig(JSONObject config) {\n        this.config = config;\n    }\n\n    /**\n     * Get a string value for a plugin in the Capacitor config.\n     *\n     * @param configKey The key of the value to retrieve\n     * @return The value from the config, if exists. Null if not\n     */\n    public String getString(String configKey) {\n        return getString(configKey, null);\n    }\n\n    /**\n     * Get a string value for a plugin in the Capacitor config.\n     *\n     * @param configKey The key of the value to retrieve\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    public String getString(String configKey, String defaultValue) {\n        return JSONUtils.getString(config, configKey, defaultValue);\n    }\n\n    /**\n     * Get a boolean value for a plugin in the Capacitor config.\n     *\n     * @param configKey The key of the value to retrieve\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    public boolean getBoolean(String configKey, boolean defaultValue) {\n        return JSONUtils.getBoolean(config, configKey, defaultValue);\n    }\n\n    /**\n     * Get an integer value for a plugin in the Capacitor config.\n     *\n     * @param configKey The key of the value to retrieve\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    public int getInt(String configKey, int defaultValue) {\n        return JSONUtils.getInt(config, configKey, defaultValue);\n    }\n\n    /**\n     * Get a string array value for a plugin in the Capacitor config.\n     *\n     * @param configKey The key of the value to retrieve\n     * @return The value from the config, if exists. Null if not\n     */\n    public String[] getArray(String configKey) {\n        return getArray(configKey, null);\n    }\n\n    /**\n     * Get a string array value for a plugin in the Capacitor config.\n     *\n     * @param configKey The key of the value to retrieve\n     * @param defaultValue A default value to return if the key does not exist in the config\n     * @return The value from the config, if key exists. Default value returned if not\n     */\n    public String[] getArray(String configKey, String[] defaultValue) {\n        return JSONUtils.getArray(config, configKey, defaultValue);\n    }\n\n    /**\n     * Get a JSON object value for a plugin in the Capacitor config.\n     *\n     * @param configKey The key of the value to retrieve\n     * @return The value from the config, if exists. Null if not\n     */\n    public JSONObject getObject(String configKey) {\n        return JSONUtils.getObject(config, configKey);\n    }\n\n    /**\n     * Check if the PluginConfig is empty.\n     *\n     * @return true if the plugin config has no entries\n     */\n    public boolean isEmpty() {\n        return config.length() == 0;\n    }\n\n    /**\n     * Gets the JSON Object containing the config of the the provided plugin ID.\n     *\n     * @return The config for that plugin\n     */\n    public JSONObject getConfigJSON() {\n        return config;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginHandle.java",
    "content": "package com.getcapacitor;\n\nimport com.getcapacitor.annotation.CapacitorPlugin;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * PluginHandle is an instance of a plugin that has been registered\n * and indexed. Think of it as a Plugin instance with extra metadata goodies\n */\npublic class PluginHandle {\n\n    private final Bridge bridge;\n    private final Class<? extends Plugin> pluginClass;\n\n    private final Map<String, PluginMethodHandle> pluginMethods = new HashMap<>();\n\n    private final String pluginId;\n\n    @SuppressWarnings(\"deprecation\")\n    private NativePlugin legacyPluginAnnotation;\n\n    private CapacitorPlugin pluginAnnotation;\n\n    private Plugin instance;\n\n    @SuppressWarnings(\"deprecation\")\n    private PluginHandle(Class<? extends Plugin> clazz, Bridge bridge) throws InvalidPluginException {\n        this.bridge = bridge;\n        this.pluginClass = clazz;\n\n        CapacitorPlugin pluginAnnotation = pluginClass.getAnnotation(CapacitorPlugin.class);\n        if (pluginAnnotation == null) {\n            // Check for legacy plugin annotation, @NativePlugin\n            NativePlugin legacyPluginAnnotation = pluginClass.getAnnotation(NativePlugin.class);\n            if (legacyPluginAnnotation == null) {\n                throw new InvalidPluginException(\"No @CapacitorPlugin annotation found for plugin \" + pluginClass.getName());\n            }\n\n            if (!legacyPluginAnnotation.name().equals(\"\")) {\n                this.pluginId = legacyPluginAnnotation.name();\n            } else {\n                this.pluginId = pluginClass.getSimpleName();\n            }\n\n            this.legacyPluginAnnotation = legacyPluginAnnotation;\n        } else {\n            if (!pluginAnnotation.name().equals(\"\")) {\n                this.pluginId = pluginAnnotation.name();\n            } else {\n                this.pluginId = pluginClass.getSimpleName();\n            }\n\n            this.pluginAnnotation = pluginAnnotation;\n        }\n\n        this.indexMethods(clazz);\n    }\n\n    public PluginHandle(Bridge bridge, Class<? extends Plugin> pluginClass) throws InvalidPluginException, PluginLoadException {\n        this(pluginClass, bridge);\n        this.load();\n    }\n\n    public PluginHandle(Bridge bridge, Plugin plugin) throws InvalidPluginException {\n        this(plugin.getClass(), bridge);\n        this.loadInstance(plugin);\n    }\n\n    public Class<? extends Plugin> getPluginClass() {\n        return pluginClass;\n    }\n\n    public String getId() {\n        return this.pluginId;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public NativePlugin getLegacyPluginAnnotation() {\n        return this.legacyPluginAnnotation;\n    }\n\n    public CapacitorPlugin getPluginAnnotation() {\n        return this.pluginAnnotation;\n    }\n\n    public Plugin getInstance() {\n        return this.instance;\n    }\n\n    public Collection<PluginMethodHandle> getMethods() {\n        return this.pluginMethods.values();\n    }\n\n    public Plugin load() throws PluginLoadException {\n        if (this.instance != null) {\n            return this.instance;\n        }\n\n        try {\n            this.instance = this.pluginClass.getDeclaredConstructor().newInstance();\n            return this.loadInstance(instance);\n        } catch (Exception ex) {\n            throw new PluginLoadException(\"Unable to load plugin instance. Ensure plugin is publicly accessible\");\n        }\n    }\n\n    public Plugin loadInstance(Plugin plugin) {\n        this.instance = plugin;\n        this.instance.setPluginHandle(this);\n        this.instance.setBridge(this.bridge);\n        this.instance.load();\n        this.instance.initializeActivityLaunchers();\n        return this.instance;\n    }\n\n    /**\n     * Call a method on a plugin.\n     * @param methodName the name of the method to call\n     * @param call the constructed PluginCall with parameters from the caller\n     * @throws InvalidPluginMethodException if no method was found on that plugin\n     */\n    public void invoke(String methodName, PluginCall call)\n        throws PluginLoadException, InvalidPluginMethodException, InvocationTargetException, IllegalAccessException {\n        if (this.instance == null) {\n            // Can throw PluginLoadException\n            this.load();\n        }\n\n        PluginMethodHandle methodMeta = pluginMethods.get(methodName);\n        if (methodMeta == null) {\n            throw new InvalidPluginMethodException(\"No method \" + methodName + \" found for plugin \" + pluginClass.getName());\n        }\n\n        methodMeta.getMethod().invoke(this.instance, call);\n    }\n\n    /**\n     * Index all the known callable methods for a plugin for faster\n     * invocation later\n     */\n    private void indexMethods(Class<? extends Plugin> plugin) {\n        //Method[] methods = pluginClass.getDeclaredMethods();\n        Method[] methods = pluginClass.getMethods();\n\n        for (Method methodReflect : methods) {\n            PluginMethod method = methodReflect.getAnnotation(PluginMethod.class);\n\n            if (method == null) {\n                continue;\n            }\n\n            PluginMethodHandle methodMeta = new PluginMethodHandle(methodReflect, method);\n            pluginMethods.put(methodReflect.getName(), methodMeta);\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginInvocationException.java",
    "content": "package com.getcapacitor;\n\nclass PluginInvocationException extends Exception {\n\n    public PluginInvocationException(String s) {\n        super(s);\n    }\n\n    public PluginInvocationException(Throwable t) {\n        super(t);\n    }\n\n    public PluginInvocationException(String s, Throwable t) {\n        super(s, t);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginLoadException.java",
    "content": "package com.getcapacitor;\n\n/**\n * Thrown when a plugin fails to instantiate\n */\npublic class PluginLoadException extends Exception {\n\n    public PluginLoadException(String s) {\n        super(s);\n    }\n\n    public PluginLoadException(Throwable t) {\n        super(t);\n    }\n\n    public PluginLoadException(String s, Throwable t) {\n        super(s, t);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginManager.java",
    "content": "package com.getcapacitor;\n\nimport android.content.res.AssetManager;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class PluginManager {\n\n    private final AssetManager assetManager;\n\n    public PluginManager(AssetManager assetManager) {\n        this.assetManager = assetManager;\n    }\n\n    public List<Class<? extends Plugin>> loadPluginClasses() throws PluginLoadException {\n        JSONArray pluginsJSON = parsePluginsJSON();\n        ArrayList<Class<? extends Plugin>> pluginList = new ArrayList<>();\n\n        try {\n            for (int i = 0, size = pluginsJSON.length(); i < size; i++) {\n                JSONObject pluginJSON = pluginsJSON.getJSONObject(i);\n                String classPath = pluginJSON.getString(\"classpath\");\n                Class<?> c = Class.forName(classPath);\n                pluginList.add(c.asSubclass(Plugin.class));\n            }\n        } catch (JSONException e) {\n            throw new PluginLoadException(\"Could not parse capacitor.plugins.json as JSON\");\n        } catch (ClassNotFoundException e) {\n            throw new PluginLoadException(\"Could not find class by class path: \" + e.getMessage());\n        }\n\n        return pluginList;\n    }\n\n    private JSONArray parsePluginsJSON() throws PluginLoadException {\n        try (BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open(\"capacitor.plugins.json\")))) {\n            StringBuilder builder = new StringBuilder();\n            String line;\n            while ((line = reader.readLine()) != null) {\n                builder.append(line);\n            }\n            String jsonString = builder.toString();\n            return new JSONArray(jsonString);\n        } catch (IOException e) {\n            throw new PluginLoadException(\"Could not load capacitor.plugins.json\");\n        } catch (JSONException e) {\n            throw new PluginLoadException(\"Could not parse capacitor.plugins.json as JSON\");\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginMethod.java",
    "content": "package com.getcapacitor;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface PluginMethod {\n    String RETURN_PROMISE = \"promise\";\n\n    String RETURN_CALLBACK = \"callback\";\n\n    String RETURN_NONE = \"none\";\n\n    String returnType() default RETURN_PROMISE;\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginMethodHandle.java",
    "content": "package com.getcapacitor;\n\nimport java.lang.reflect.Method;\n\npublic class PluginMethodHandle {\n\n    // The reflect method reference\n    private final Method method;\n    // The name of the method\n    private final String name;\n    // The return type of the method (see PluginMethod for constants)\n    private final String returnType;\n\n    public PluginMethodHandle(Method method, PluginMethod methodDecorator) {\n        this.method = method;\n\n        this.name = method.getName();\n\n        this.returnType = methodDecorator.returnType();\n    }\n\n    public String getReturnType() {\n        return returnType;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Method getMethod() {\n        return method;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/PluginResult.java",
    "content": "package com.getcapacitor;\n\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.TimeZone;\n\n/**\n * Wraps a result for web from calling a native plugin.\n */\npublic class PluginResult {\n\n    private final JSObject json;\n\n    public PluginResult() {\n        this(new JSObject());\n    }\n\n    public PluginResult(JSObject json) {\n        this.json = json;\n    }\n\n    public PluginResult put(String name, boolean value) {\n        return this.jsonPut(name, value);\n    }\n\n    public PluginResult put(String name, double value) {\n        return this.jsonPut(name, value);\n    }\n\n    public PluginResult put(String name, int value) {\n        return this.jsonPut(name, value);\n    }\n\n    public PluginResult put(String name, long value) {\n        return this.jsonPut(name, value);\n    }\n\n    /**\n     * Format a date as an ISO string\n     */\n    public PluginResult put(String name, Date value) {\n        TimeZone tz = TimeZone.getTimeZone(\"UTC\");\n        DateFormat df = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm'Z'\");\n        df.setTimeZone(tz);\n        return this.jsonPut(name, df.format(value));\n    }\n\n    public PluginResult put(String name, Object value) {\n        return this.jsonPut(name, value);\n    }\n\n    public PluginResult put(String name, PluginResult value) {\n        return this.jsonPut(name, value.json);\n    }\n\n    PluginResult jsonPut(String name, Object value) {\n        try {\n            this.json.put(name, value);\n        } catch (Exception ex) {\n            Logger.error(Logger.tags(\"Plugin\"), \"\", ex);\n        }\n        return this;\n    }\n\n    public String toString() {\n        return this.json.toString();\n    }\n\n    /**\n     * Return plugin metadata and information about the result, if it succeeded the data, or error information if it didn't.\n     * This is used for appRestoredResult, as it's technically a raw data response from a plugin.\n     * @return the raw data response from the plugin.\n     */\n    public JSObject getWrappedResult() {\n        JSObject ret = new JSObject();\n        ret.put(\"pluginId\", this.json.getString(\"pluginId\"));\n        ret.put(\"methodName\", this.json.getString(\"methodName\"));\n        ret.put(\"success\", this.json.getBoolean(\"success\", false));\n        ret.put(\"data\", this.json.getJSObject(\"data\"));\n        ret.put(\"error\", this.json.getJSObject(\"error\"));\n        return ret;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/ProcessedRoute.java",
    "content": "package com.getcapacitor;\n\n/**\n * An data class used in conjunction with RouteProcessor.\n *\n * @see com.getcapacitor.RouteProcessor\n */\npublic class ProcessedRoute {\n\n    private String path;\n    private boolean isAsset;\n    private boolean ignoreAssetPath;\n\n    public String getPath() {\n        return path;\n    }\n\n    public void setPath(String path) {\n        this.path = path;\n    }\n\n    public boolean isAsset() {\n        return isAsset;\n    }\n\n    public void setAsset(boolean asset) {\n        isAsset = asset;\n    }\n\n    public boolean isIgnoreAssetPath() {\n        return ignoreAssetPath;\n    }\n\n    public void setIgnoreAssetPath(boolean ignoreAssetPath) {\n        this.ignoreAssetPath = ignoreAssetPath;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/RouteProcessor.java",
    "content": "package com.getcapacitor;\n\n/**\n * An interface used in the processing of routes\n */\npublic interface RouteProcessor {\n    ProcessedRoute process(String basePath, String path);\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/ServerPath.java",
    "content": "package com.getcapacitor;\n\npublic class ServerPath {\n\n    public enum PathType {\n        BASE_PATH,\n        ASSET_PATH\n    }\n\n    private final PathType type;\n    private final String path;\n\n    public ServerPath(PathType type, String path) {\n        this.type = type;\n        this.path = path;\n    }\n\n    public PathType getType() {\n        return type;\n    }\n\n    public String getPath() {\n        return path;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/UriMatcher.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//package com.google.webviewlocalserver.third_party.android;\npackage com.getcapacitor;\n\nimport android.net.Uri;\nimport com.getcapacitor.util.HostMask;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\npublic class UriMatcher {\n\n    /**\n     * Creates the root node of the URI tree.\n     *\n     * @param code the code to match for the root URI\n     */\n    public UriMatcher(Object code) {\n        mCode = code;\n        mWhich = -1;\n        mChildren = new ArrayList<>();\n        mText = null;\n    }\n\n    private UriMatcher() {\n        mCode = null;\n        mWhich = -1;\n        mChildren = new ArrayList<>();\n        mText = null;\n    }\n\n    /**\n     * Add a URI to match, and the code to return when this URI is\n     * matched. URI nodes may be exact match string, the token \"*\"\n     * that matches any text, or the token \"#\" that matches only\n     * numbers.\n     * <p>\n     * Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},\n     * this method will accept a leading slash in the path.\n     *\n     * @param authority the authority to match\n     * @param path      the path to match. * may be used as a wild card for\n     *                  any text, and # may be used as a wild card for numbers.\n     * @param code      the code that is returned when a URI is matched\n     *                  against the given components. Must be positive.\n     */\n    public void addURI(String scheme, String authority, String path, Object code) {\n        if (code == null) {\n            throw new IllegalArgumentException(\"Code can't be null\");\n        }\n\n        String[] tokens = null;\n        if (path != null) {\n            String newPath = path;\n            // Strip leading slash if present.\n            if (!path.isEmpty() && path.charAt(0) == '/') {\n                newPath = path.substring(1);\n            }\n            tokens = PATH_SPLIT_PATTERN.split(newPath);\n        }\n\n        int numTokens = tokens != null ? tokens.length : 0;\n        UriMatcher node = this;\n        for (int i = -2; i < numTokens; i++) {\n            String token;\n            if (i == -2) token = scheme;\n            else if (i == -1) token = authority;\n            else token = tokens[i];\n            ArrayList<UriMatcher> children = node.mChildren;\n            int numChildren = children.size();\n            UriMatcher child;\n            int j;\n            for (j = 0; j < numChildren; j++) {\n                child = children.get(j);\n                if (token.equals(child.mText)) {\n                    node = child;\n                    break;\n                }\n            }\n            if (j == numChildren) {\n                // Child not found, create it\n                child = new UriMatcher();\n                if (i == -1 && token.contains(\"*\")) {\n                    child.mWhich = MASK;\n                } else if (token.equals(\"**\")) {\n                    child.mWhich = REST;\n                } else if (token.equals(\"*\")) {\n                    child.mWhich = TEXT;\n                } else {\n                    child.mWhich = EXACT;\n                }\n                child.mText = token;\n                node.mChildren.add(child);\n                node = child;\n            }\n        }\n        node.mCode = code;\n    }\n\n    static final Pattern PATH_SPLIT_PATTERN = Pattern.compile(\"/\");\n\n    /**\n     * Try to match against the path in a url.\n     *\n     * @param uri The url whose path we will match against.\n     * @return The code for the matched node (added using addURI),\n     * or null if there is no matched node.\n     */\n    public Object match(Uri uri) {\n        final List<String> pathSegments = uri.getPathSegments();\n        final int li = pathSegments.size();\n\n        UriMatcher node = this;\n\n        if (li == 0 && uri.getAuthority() == null) {\n            return this.mCode;\n        }\n\n        for (int i = -2; i < li; i++) {\n            String u;\n            if (i == -2) u = uri.getScheme();\n            else if (i == -1) u = uri.getAuthority();\n            else u = pathSegments.get(i);\n            ArrayList<UriMatcher> list = node.mChildren;\n            if (list == null) {\n                break;\n            }\n            node = null;\n            int lj = list.size();\n            for (int j = 0; j < lj; j++) {\n                UriMatcher n = list.get(j);\n                which_switch: switch (n.mWhich) {\n                    case MASK:\n                        if (HostMask.Parser.parse(n.mText).matches(u)) {\n                            node = n;\n                        }\n                        break;\n                    case EXACT:\n                        if (n.mText.equals(u)) {\n                            node = n;\n                        }\n                        break;\n                    case TEXT:\n                        node = n;\n                        break;\n                    case REST:\n                        return n.mCode;\n                }\n                if (node != null) {\n                    break;\n                }\n            }\n            if (node == null) {\n                return null;\n            }\n        }\n\n        return node.mCode;\n    }\n\n    private static final int EXACT = 0;\n    private static final int TEXT = 1;\n    private static final int REST = 2;\n    private static final int MASK = 3;\n\n    private Object mCode;\n    private int mWhich;\n    private String mText;\n    private ArrayList<UriMatcher> mChildren;\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/WebViewListener.java",
    "content": "package com.getcapacitor;\n\nimport android.webkit.RenderProcessGoneDetail;\nimport android.webkit.WebView;\n\n/**\n * Provides callbacks associated with the {@link BridgeWebViewClient}\n */\npublic abstract class WebViewListener {\n\n    /**\n     * Callback for page load event.\n     *\n     * @param webView The WebView that loaded\n     */\n    public void onPageLoaded(WebView webView) {\n        // Override me to add behavior to the page loaded event\n    }\n\n    /**\n     * Callback for onReceivedError event.\n     *\n     * @param webView The WebView that loaded\n     */\n    public void onReceivedError(WebView webView) {\n        // Override me to add behavior to handle the onReceivedError event\n    }\n\n    /**\n     * Callback for onReceivedHttpError event.\n     *\n     * @param webView The WebView that loaded\n     */\n    public void onReceivedHttpError(WebView webView) {\n        // Override me to add behavior to handle the onReceivedHttpError event\n    }\n\n    /**\n     * Callback for page start event.\n     *\n     * @param webView The WebView that loaded\n     */\n    public void onPageStarted(WebView webView) {\n        // Override me to add behavior to the page started event\n    }\n\n    /**\n     * Callback for render process gone event. Return true if the state is handled.\n     *\n     * @param webView The WebView that loaded\n     * @return returns false by default if the listener is not overridden and used\n     */\n    public boolean onRenderProcessGone(WebView webView, RenderProcessGoneDetail detail) {\n        // Override me to add behavior to the web view render process gone event\n        return false;\n    }\n\n    /**\n     * Callback for page start event.\n     *\n     * @param view The WebView for which the navigation occurred.\n     * @param url The URL corresponding to the page navigation that triggered this callback.\n     */\n    public void onPageCommitVisible(WebView view, String url) {\n        // Override me to add behavior to handle the onPageCommitVisible event\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java",
    "content": "/*\nCopyright 2015 Google Inc. All rights reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n */\npackage com.getcapacitor;\n\nimport static com.getcapacitor.plugin.util.HttpRequestHandler.isDomainExcludedFromSSL;\n\nimport android.content.Context;\nimport android.net.Uri;\nimport android.util.Base64;\nimport android.webkit.CookieManager;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport com.getcapacitor.plugin.util.CapacitorHttpUrlConnection;\nimport com.getcapacitor.plugin.util.HttpRequestHandler;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Helper class meant to be used with the android.webkit.WebView class to enable hosting assets,\n * resources and other data on 'virtual' https:// URL.\n * Hosting assets and resources on https:// URLs is desirable as it is compatible with the\n * Same-Origin policy.\n * <p>\n * This class is intended to be used from within the\n * {@link android.webkit.WebViewClient#shouldInterceptRequest(android.webkit.WebView, String)} and\n * {@link android.webkit.WebViewClient#shouldInterceptRequest(android.webkit.WebView,\n * android.webkit.WebResourceRequest)}\n * methods.\n */\npublic class WebViewLocalServer {\n\n    private static final String capacitorFileStart = Bridge.CAPACITOR_FILE_START;\n    private static final String capacitorContentStart = Bridge.CAPACITOR_CONTENT_START;\n    private String basePath;\n\n    private final UriMatcher uriMatcher;\n    private final AndroidProtocolHandler protocolHandler;\n    private final ArrayList<String> authorities;\n    private boolean isAsset;\n    // Whether to route all requests to paths without extensions back to `index.html`\n    private final boolean html5mode;\n    private final JSInjector jsInjector;\n    private final Bridge bridge;\n\n    /**\n     * A handler that produces responses for paths on the virtual asset server.\n     * <p>\n     * Methods of this handler will be invoked on a background thread and care must be taken to\n     * correctly synchronize access to any shared state.\n     * <p>\n     * On Android KitKat and above these methods may be called on more than one thread. This thread\n     * may be different than the thread on which the shouldInterceptRequest method was invoke.\n     * This means that on Android KitKat and above it is possible to block in this method without\n     * blocking other resources from loading. The number of threads used to parallelize loading\n     * is an internal implementation detail of the WebView and may change between updates which\n     * means that the amount of time spend blocking in this method should be kept to an absolute\n     * minimum.\n     */\n    public abstract static class PathHandler {\n\n        protected String mimeType;\n        private String encoding;\n        private String charset;\n        private int statusCode;\n        private String reasonPhrase;\n        private Map<String, String> responseHeaders;\n\n        public PathHandler() {\n            this(null, null, 200, \"OK\", null);\n        }\n\n        public PathHandler(String encoding, String charset, int statusCode, String reasonPhrase, Map<String, String> responseHeaders) {\n            this.encoding = encoding;\n            this.charset = charset;\n            this.statusCode = statusCode;\n            this.reasonPhrase = reasonPhrase;\n            Map<String, String> tempResponseHeaders;\n            if (responseHeaders == null) {\n                tempResponseHeaders = new HashMap<>();\n            } else {\n                tempResponseHeaders = responseHeaders;\n            }\n            tempResponseHeaders.put(\"Cache-Control\", \"no-cache\");\n            this.responseHeaders = tempResponseHeaders;\n        }\n\n        public InputStream handle(WebResourceRequest request) {\n            return handle(request.getUrl());\n        }\n\n        public abstract InputStream handle(Uri url);\n\n        public String getEncoding() {\n            return encoding;\n        }\n\n        public String getCharset() {\n            return charset;\n        }\n\n        public int getStatusCode() {\n            return statusCode;\n        }\n\n        public String getReasonPhrase() {\n            return reasonPhrase;\n        }\n\n        /**\n         * @deprecated This method may return incorrect headers in concurrent range requests.\n         * <p>\n         * Use {@link #buildDefaultResponseHeaders()} instead, which returns a copy of the map.\n         * </p>\n         * This method will be removed in a future major version of Capacitor.\n         * </p>\n         */\n        @Deprecated(forRemoval = true) // adjust version as appropriate\n        public Map<String, String> getResponseHeaders() {\n            return responseHeaders;\n        }\n\n        public Map<String, String> buildDefaultResponseHeaders() {\n            return new HashMap<>(responseHeaders);\n        }\n    }\n\n    WebViewLocalServer(Context context, Bridge bridge, JSInjector jsInjector, ArrayList<String> authorities, boolean html5mode) {\n        uriMatcher = new UriMatcher(null);\n        this.html5mode = html5mode;\n        this.protocolHandler = new AndroidProtocolHandler(context.getApplicationContext());\n        this.authorities = authorities;\n        this.bridge = bridge;\n        this.jsInjector = jsInjector;\n    }\n\n    private static Uri parseAndVerifyUrl(String url) {\n        if (url == null) {\n            return null;\n        }\n        Uri uri = Uri.parse(url);\n        if (uri == null) {\n            Logger.error(\"Malformed URL: \" + url);\n            return null;\n        }\n        String path = uri.getPath();\n        if (path == null || path.isEmpty()) {\n            Logger.error(\"URL does not have a path: \" + url);\n            return null;\n        }\n        return uri;\n    }\n\n    /**\n     * Attempt to retrieve the WebResourceResponse associated with the given <code>request</code>.\n     * This method should be invoked from within\n     * {@link android.webkit.WebViewClient#shouldInterceptRequest(android.webkit.WebView,\n     * android.webkit.WebResourceRequest)}.\n     *\n     * @param request the request to process.\n     * @return a response if the request URL had a matching handler, null if no handler was found.\n     */\n    public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {\n        Uri loadingUrl = request.getUrl();\n\n        if (null != loadingUrl.getPath() && loadingUrl.getPath().startsWith(Bridge.CAPACITOR_HTTP_INTERCEPTOR_START)) {\n            Logger.debug(\"Handling CapacitorHttp request: \" + loadingUrl);\n            try {\n                return handleCapacitorHttpRequest(request);\n            } catch (Exception e) {\n                Logger.error(e.getLocalizedMessage());\n                return null;\n            }\n        }\n\n        PathHandler handler;\n        synchronized (uriMatcher) {\n            handler = (PathHandler) uriMatcher.match(request.getUrl());\n        }\n        if (handler == null) {\n            return null;\n        }\n\n        if (isLocalFile(loadingUrl) || isMainUrl(loadingUrl) || !isAllowedUrl(loadingUrl) || isErrorUrl(loadingUrl)) {\n            Logger.debug(\"Handling local request: \" + request.getUrl().toString());\n            return handleLocalRequest(request, handler);\n        } else {\n            return handleProxyRequest(request, handler);\n        }\n    }\n\n    private boolean isLocalFile(Uri uri) {\n        String path = uri.getPath();\n        return path.startsWith(capacitorContentStart) || path.startsWith(capacitorFileStart);\n    }\n\n    private boolean isErrorUrl(Uri uri) {\n        String url = uri.toString();\n        return url.equals(bridge.getErrorUrl());\n    }\n\n    private boolean isMainUrl(Uri loadingUrl) {\n        return (bridge.getServerUrl() == null && loadingUrl.getHost().equalsIgnoreCase(bridge.getHost()));\n    }\n\n    private boolean isAllowedUrl(Uri loadingUrl) {\n        return !(bridge.getServerUrl() == null && !bridge.getAppAllowNavigationMask().matches(loadingUrl.getHost()));\n    }\n\n    private String getReasonPhraseFromResponseCode(int code) {\n        return switch (code) {\n            case 100 -> \"Continue\";\n            case 101 -> \"Switching Protocols\";\n            case 200 -> \"OK\";\n            case 201 -> \"Created\";\n            case 202 -> \"Accepted\";\n            case 203 -> \"Non-Authoritative Information\";\n            case 204 -> \"No Content\";\n            case 205 -> \"Reset Content\";\n            case 206 -> \"Partial Content\";\n            case 300 -> \"Multiple Choices\";\n            case 301 -> \"Moved Permanently\";\n            case 302 -> \"Found\";\n            case 303 -> \"See Other\";\n            case 304 -> \"Not Modified\";\n            case 400 -> \"Bad Request\";\n            case 401 -> \"Unauthorized\";\n            case 403 -> \"Forbidden\";\n            case 404 -> \"Not Found\";\n            case 405 -> \"Method Not Allowed\";\n            case 406 -> \"Not Acceptable\";\n            case 407 -> \"Proxy Authentication Required\";\n            case 408 -> \"Request Timeout\";\n            case 409 -> \"Conflict\";\n            case 410 -> \"Gone\";\n            case 500 -> \"Internal Server Error\";\n            case 501 -> \"Not Implemented\";\n            case 502 -> \"Bad Gateway\";\n            case 503 -> \"Service Unavailable\";\n            case 504 -> \"Gateway Timeout\";\n            case 505 -> \"HTTP Version Not Supported\";\n            default -> \"Unknown\";\n        };\n    }\n\n    private WebResourceResponse handleCapacitorHttpRequest(WebResourceRequest request) throws IOException {\n        String urlString = request.getUrl().getQueryParameter(Bridge.CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM);\n        URL url = new URL(urlString);\n        JSObject headers = new JSObject();\n\n        for (Map.Entry<String, String> header : request.getRequestHeaders().entrySet()) {\n            headers.put(header.getKey(), header.getValue());\n        }\n\n        // a workaround for the following android web view issue:\n        // https://issues.chromium.org/issues/40450316\n        // x-cap-user-agent contains the user agent set in JavaScript\n        String userAgentValue = headers.getString(\"x-cap-user-agent\");\n        if (userAgentValue != null) {\n            headers.put(\"User-Agent\", userAgentValue);\n        }\n        headers.remove(\"x-cap-user-agent\");\n\n        HttpRequestHandler.HttpURLConnectionBuilder connectionBuilder = new HttpRequestHandler.HttpURLConnectionBuilder()\n            .setUrl(url)\n            .setMethod(request.getMethod())\n            .setHeaders(headers)\n            .openConnection();\n\n        CapacitorHttpUrlConnection connection = connectionBuilder.build();\n\n        if (!isDomainExcludedFromSSL(bridge, url)) {\n            connection.setSSLSocketFactory(bridge);\n        }\n\n        connection.connect();\n\n        String mimeType = null;\n        String encoding = null;\n        Map<String, String> responseHeaders = new LinkedHashMap<>();\n        for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {\n            StringBuilder builder = new StringBuilder();\n            for (String value : entry.getValue()) {\n                builder.append(value);\n                builder.append(\", \");\n            }\n            builder.setLength(builder.length() - 2);\n\n            if (\"Content-Type\".equalsIgnoreCase(entry.getKey())) {\n                String[] contentTypeParts = builder.toString().split(\";\");\n                mimeType = contentTypeParts[0].trim();\n                if (contentTypeParts.length > 1) {\n                    String[] encodingParts = contentTypeParts[1].split(\"=\");\n                    if (encodingParts.length > 1) {\n                        encoding = encodingParts[1].trim();\n                    }\n                }\n            } else {\n                responseHeaders.put(entry.getKey(), builder.toString());\n            }\n        }\n\n        InputStream inputStream = connection.getErrorStream();\n        if (inputStream == null) {\n            inputStream = connection.getInputStream();\n        }\n\n        if (null == mimeType) {\n            mimeType = getMimeType(request.getUrl().getPath(), inputStream);\n        }\n\n        int responseCode = connection.getResponseCode();\n        String reasonPhrase = getReasonPhraseFromResponseCode(responseCode);\n\n        return new WebResourceResponse(mimeType, encoding, responseCode, reasonPhrase, responseHeaders, inputStream);\n    }\n\n    private WebResourceResponse handleLocalRequest(WebResourceRequest request, PathHandler handler) {\n        String path = request.getUrl().getPath();\n\n        Map<String, String> requestHeaders = request.getRequestHeaders();\n        String rangeString = requestHeaders.get(\"Range\") != null ? requestHeaders.get(\"Range\") : requestHeaders.get(\"range\");\n\n        if (rangeString != null) {\n            InputStream responseStream = new LollipopLazyInputStream(handler, request);\n            String mimeType = getMimeType(path, responseStream);\n            Map<String, String> tempResponseHeaders = handler.buildDefaultResponseHeaders();\n            int statusCode = 206;\n            try {\n                int totalRange = responseStream.available();\n                String[] parts = rangeString.split(\"=\");\n                String[] streamParts = parts[1].split(\"-\");\n                String fromRange = streamParts[0];\n                int range = totalRange - 1;\n                if (streamParts.length > 1) {\n                    range = Integer.parseInt(streamParts[1]);\n                }\n                tempResponseHeaders.put(\"Accept-Ranges\", \"bytes\");\n                tempResponseHeaders.put(\"Content-Range\", \"bytes \" + fromRange + \"-\" + range + \"/\" + totalRange);\n            } catch (IOException e) {\n                statusCode = 404;\n            }\n            return new WebResourceResponse(\n                mimeType,\n                handler.getEncoding(),\n                statusCode,\n                handler.getReasonPhrase(),\n                tempResponseHeaders,\n                responseStream\n            );\n        }\n\n        if (isLocalFile(request.getUrl()) || isErrorUrl(request.getUrl())) {\n            InputStream responseStream = new LollipopLazyInputStream(handler, request);\n            String mimeType = getMimeType(request.getUrl().getPath(), responseStream);\n            int statusCode = getStatusCode(responseStream, handler.getStatusCode());\n            return new WebResourceResponse(\n                mimeType,\n                handler.getEncoding(),\n                statusCode,\n                handler.getReasonPhrase(),\n                handler.buildDefaultResponseHeaders(),\n                responseStream\n            );\n        }\n\n        if (path.equals(\"/cordova.js\")) {\n            return new WebResourceResponse(\n                \"application/javascript\",\n                handler.getEncoding(),\n                handler.getStatusCode(),\n                handler.getReasonPhrase(),\n                handler.buildDefaultResponseHeaders(),\n                null\n            );\n        }\n\n        if (path.equals(\"/\") || (!request.getUrl().getLastPathSegment().contains(\".\") && html5mode)) {\n            InputStream responseStream;\n            try {\n                String startPath = this.basePath + \"/index.html\";\n                if (bridge.getRouteProcessor() != null) {\n                    ProcessedRoute processedRoute = bridge.getRouteProcessor().process(this.basePath, \"/index.html\");\n                    startPath = processedRoute.getPath();\n                    isAsset = processedRoute.isAsset();\n                }\n\n                if (isAsset) {\n                    responseStream = protocolHandler.openAsset(startPath);\n                } else {\n                    responseStream = protocolHandler.openFile(startPath);\n                }\n            } catch (IOException e) {\n                Logger.error(\"Unable to open index.html\", e);\n                return null;\n            }\n\n            if (jsInjector != null) {\n                responseStream = jsInjector.getInjectedStream(responseStream);\n            }\n\n            int statusCode = getStatusCode(responseStream, handler.getStatusCode());\n            return new WebResourceResponse(\n                \"text/html\",\n                handler.getEncoding(),\n                statusCode,\n                handler.getReasonPhrase(),\n                handler.buildDefaultResponseHeaders(),\n                responseStream\n            );\n        }\n\n        if (\"/favicon.ico\".equalsIgnoreCase(path)) {\n            try {\n                return new WebResourceResponse(\"image/png\", null, null);\n            } catch (Exception e) {\n                Logger.error(\"favicon handling failed\", e);\n            }\n        }\n\n        int periodIndex = path.lastIndexOf(\".\");\n        if (periodIndex >= 0) {\n            String ext = path.substring(path.lastIndexOf(\".\"));\n\n            InputStream responseStream = new LollipopLazyInputStream(handler, request);\n\n            // TODO: Conjure up a bit more subtlety than this\n            if (ext.equals(\".html\") && jsInjector != null) {\n                responseStream = jsInjector.getInjectedStream(responseStream);\n            }\n\n            String mimeType = getMimeType(path, responseStream);\n            int statusCode = getStatusCode(responseStream, handler.getStatusCode());\n            return new WebResourceResponse(\n                mimeType,\n                handler.getEncoding(),\n                statusCode,\n                handler.getReasonPhrase(),\n                handler.buildDefaultResponseHeaders(),\n                responseStream\n            );\n        }\n\n        return null;\n    }\n\n    /**\n     * Prepends an {@code InputStream} with the JavaScript required by Capacitor.\n     * This method only changes the original {@code InputStream} if {@code WebView} does not\n     * support the {@code DOCUMENT_START_SCRIPT} feature.\n     * @param original the original {@code InputStream}\n     * @return the modified {@code InputStream}\n     */\n    public InputStream getJavaScriptInjectedStream(InputStream original) {\n        if (jsInjector != null) {\n            return jsInjector.getInjectedStream(original);\n        }\n        return original;\n    }\n\n    /**\n     * Instead of reading files from the filesystem/assets, proxy through to the URL\n     * and let an external server handle it.\n     * @param request\n     * @param handler\n     * @return\n     */\n    private WebResourceResponse handleProxyRequest(WebResourceRequest request, PathHandler handler) {\n        if (jsInjector != null) {\n            final String method = request.getMethod();\n            if (method.equals(\"GET\")) {\n                try {\n                    String url = request.getUrl().toString();\n                    Map<String, String> headers = request.getRequestHeaders();\n                    boolean isHtmlText = false;\n                    for (Map.Entry<String, String> header : headers.entrySet()) {\n                        if (header.getKey().equalsIgnoreCase(\"Accept\") && header.getValue().toLowerCase().contains(\"text/html\")) {\n                            isHtmlText = true;\n                            break;\n                        }\n                    }\n                    if (isHtmlText) {\n                        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();\n                        for (Map.Entry<String, String> header : headers.entrySet()) {\n                            conn.setRequestProperty(header.getKey(), header.getValue());\n                        }\n                        String getCookie = CookieManager.getInstance().getCookie(url);\n                        if (getCookie != null) {\n                            conn.setRequestProperty(\"Cookie\", getCookie);\n                        }\n                        conn.setRequestMethod(method);\n                        conn.setReadTimeout(30 * 1000);\n                        conn.setConnectTimeout(30 * 1000);\n                        if (request.getUrl().getUserInfo() != null) {\n                            byte[] userInfoBytes = request.getUrl().getUserInfo().getBytes(StandardCharsets.UTF_8);\n                            String base64 = Base64.encodeToString(userInfoBytes, Base64.NO_WRAP);\n                            conn.setRequestProperty(\"Authorization\", \"Basic \" + base64);\n                        }\n\n                        List<String> cookies = conn.getHeaderFields().get(\"Set-Cookie\");\n                        if (cookies != null) {\n                            for (String cookie : cookies) {\n                                CookieManager.getInstance().setCookie(url, cookie);\n                            }\n                        }\n                        InputStream responseStream = conn.getInputStream();\n                        responseStream = jsInjector.getInjectedStream(responseStream);\n\n                        return new WebResourceResponse(\n                            \"text/html\",\n                            handler.getEncoding(),\n                            handler.getStatusCode(),\n                            handler.getReasonPhrase(),\n                            handler.buildDefaultResponseHeaders(),\n                            responseStream\n                        );\n                    }\n                } catch (Exception ex) {\n                    bridge.handleAppUrlLoadError(ex);\n                }\n            }\n        }\n        return null;\n    }\n\n    private String getMimeType(String path, InputStream stream) {\n        String mimeType = null;\n        try {\n            mimeType = URLConnection.guessContentTypeFromName(path); // Does not recognize *.js\n            if (mimeType != null && path.endsWith(\".js\") && mimeType.equals(\"image/x-icon\")) {\n                Logger.debug(\"We shouldn't be here\");\n            }\n            if (mimeType == null) {\n                if (path.endsWith(\".js\") || path.endsWith(\".mjs\")) {\n                    // Make sure JS files get the proper mimetype to support ES modules\n                    mimeType = \"application/javascript\";\n                } else if (path.endsWith(\".wasm\")) {\n                    mimeType = \"application/wasm\";\n                } else {\n                    mimeType = URLConnection.guessContentTypeFromStream(stream);\n                }\n            }\n        } catch (Exception ex) {\n            Logger.error(\"Unable to get mime type\" + path, ex);\n        }\n        return mimeType;\n    }\n\n    private int getStatusCode(InputStream stream, int defaultCode) {\n        int finalStatusCode = defaultCode;\n        try {\n            if (stream.available() == -1) {\n                finalStatusCode = 404;\n            }\n        } catch (IOException e) {\n            finalStatusCode = 500;\n        }\n        return finalStatusCode;\n    }\n\n    /**\n     * Registers a handler for the given <code>uri</code>. The <code>handler</code> will be invoked\n     * every time the <code>shouldInterceptRequest</code> method of the instance is called with\n     * a matching <code>uri</code>.\n     *\n     * @param uri     the uri to use the handler for. The scheme and authority (domain) will be matched\n     *                exactly. The path may contain a '*' element which will match a single element of\n     *                a path (so a handler registered for /a/* will be invoked for /a/b and /a/c.html\n     *                but not for /a/b/b) or the '**' element which will match any number of path\n     *                elements.\n     * @param handler the handler to use for the uri.\n     */\n    void register(Uri uri, PathHandler handler) {\n        synchronized (uriMatcher) {\n            uriMatcher.addURI(uri.getScheme(), uri.getAuthority(), uri.getPath(), handler);\n        }\n    }\n\n    /**\n     * Hosts the application's assets on an https:// URL. Assets from the local path\n     * <code>assetPath/...</code> will be available under\n     * <code>https://{uuid}.androidplatform.net/assets/...</code>.\n     *\n     * @param assetPath the local path in the application's asset folder which will be made\n     *                  available by the server (for example \"/www\").\n     * @return prefixes under which the assets are hosted.\n     */\n    public void hostAssets(String assetPath) {\n        this.isAsset = true;\n        this.basePath = assetPath;\n        createHostingDetails();\n    }\n\n    /**\n     * Hosts the application's files on an https:// URL. Files from the basePath\n     * <code>basePath/...</code> will be available under\n     * <code>https://{uuid}.androidplatform.net/...</code>.\n     *\n     * @param basePath the local path in the application's data folder which will be made\n     *                  available by the server (for example \"/www\").\n     * @return prefixes under which the assets are hosted.\n     */\n    public void hostFiles(final String basePath) {\n        this.isAsset = false;\n        this.basePath = basePath;\n        createHostingDetails();\n    }\n\n    private void createHostingDetails() {\n        final String assetPath = this.basePath;\n\n        if (assetPath.indexOf('*') != -1) {\n            throw new IllegalArgumentException(\"assetPath cannot contain the '*' character.\");\n        }\n\n        PathHandler handler = new PathHandler() {\n            @Override\n            public InputStream handle(Uri url) {\n                InputStream stream = null;\n                String path = url.getPath();\n\n                // Pass path to routeProcessor if present\n                RouteProcessor routeProcessor = bridge.getRouteProcessor();\n                boolean ignoreAssetPath = false;\n                if (routeProcessor != null) {\n                    ProcessedRoute processedRoute = bridge.getRouteProcessor().process(\"\", path);\n                    path = processedRoute.getPath();\n                    isAsset = processedRoute.isAsset();\n                    ignoreAssetPath = processedRoute.isIgnoreAssetPath();\n                }\n\n                try {\n                    if (path.startsWith(capacitorContentStart)) {\n                        stream = protocolHandler.openContentUrl(url);\n                    } else if (path.startsWith(capacitorFileStart)) {\n                        stream = protocolHandler.openFile(path);\n                    } else if (!isAsset) {\n                        if (routeProcessor == null) {\n                            path = basePath + url.getPath();\n                        }\n\n                        stream = protocolHandler.openFile(path);\n                    } else if (ignoreAssetPath) {\n                        stream = protocolHandler.openAsset(path);\n                    } else {\n                        stream = protocolHandler.openAsset(assetPath + path);\n                    }\n                } catch (IOException e) {\n                    Logger.error(\"Unable to open asset URL: \" + url);\n                    return null;\n                }\n\n                return stream;\n            }\n        };\n\n        for (String authority : authorities) {\n            registerUriForScheme(Bridge.CAPACITOR_HTTP_SCHEME, handler, authority);\n            registerUriForScheme(Bridge.CAPACITOR_HTTPS_SCHEME, handler, authority);\n\n            String customScheme = this.bridge.getScheme();\n            if (!customScheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !customScheme.equals(Bridge.CAPACITOR_HTTPS_SCHEME)) {\n                registerUriForScheme(customScheme, handler, authority);\n            }\n        }\n    }\n\n    private void registerUriForScheme(String scheme, PathHandler handler, String authority) {\n        Uri.Builder uriBuilder = new Uri.Builder();\n        uriBuilder.scheme(scheme);\n        uriBuilder.authority(authority);\n        uriBuilder.path(\"\");\n        Uri uriPrefix = uriBuilder.build();\n\n        register(Uri.withAppendedPath(uriPrefix, \"/\"), handler);\n        register(Uri.withAppendedPath(uriPrefix, \"**\"), handler);\n    }\n\n    /**\n     * The KitKat WebView reads the InputStream on a separate threadpool. We can use that to\n     * parallelize loading.\n     */\n    private abstract static class LazyInputStream extends InputStream {\n\n        protected final PathHandler handler;\n        private InputStream is = null;\n\n        public LazyInputStream(PathHandler handler) {\n            this.handler = handler;\n        }\n\n        private InputStream getInputStream() {\n            if (is == null) {\n                is = handle();\n            }\n            return is;\n        }\n\n        protected abstract InputStream handle();\n\n        @Override\n        public int available() throws IOException {\n            InputStream is = getInputStream();\n            return (is != null) ? is.available() : -1;\n        }\n\n        @Override\n        public int read() throws IOException {\n            InputStream is = getInputStream();\n            return (is != null) ? is.read() : -1;\n        }\n\n        @Override\n        public int read(byte[] b) throws IOException {\n            InputStream is = getInputStream();\n            return (is != null) ? is.read(b) : -1;\n        }\n\n        @Override\n        public int read(byte[] b, int off, int len) throws IOException {\n            InputStream is = getInputStream();\n            return (is != null) ? is.read(b, off, len) : -1;\n        }\n\n        @Override\n        public long skip(long n) throws IOException {\n            InputStream is = getInputStream();\n            return (is != null) ? is.skip(n) : 0;\n        }\n    }\n\n    // For L and above.\n    private static class LollipopLazyInputStream extends LazyInputStream {\n\n        private WebResourceRequest request;\n        private InputStream is;\n\n        public LollipopLazyInputStream(PathHandler handler, WebResourceRequest request) {\n            super(handler);\n            this.request = request;\n        }\n\n        @Override\n        protected InputStream handle() {\n            return handler.handle(request);\n        }\n    }\n\n    public String getBasePath() {\n        return this.basePath;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/annotation/ActivityCallback.java",
    "content": "package com.getcapacitor.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface ActivityCallback {}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/annotation/CapacitorPlugin.java",
    "content": "package com.getcapacitor.annotation;\n\nimport android.content.Intent;\nimport com.getcapacitor.PluginCall;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Base annotation for all Plugins\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface CapacitorPlugin {\n    /**\n     * A custom name for the plugin, otherwise uses the\n     * simple class name.\n     */\n    String name() default \"\";\n\n    /**\n     * Request codes this plugin uses and responds to, in order to tie\n     * Android events back the plugin to handle.\n     *\n     * NOTE: This is a legacy option provided to support third party libraries\n     * not currently implementing the new AndroidX Activity Results API. Plugins\n     * without this limitation should use a registered callback with\n     * {@link com.getcapacitor.Plugin#startActivityForResult(PluginCall, Intent, String)}\n     */\n    int[] requestCodes() default {};\n\n    /**\n     * Permissions this plugin needs, in order to make permission requests\n     * easy if the plugin only needs basic permission prompting\n     */\n    Permission[] permissions() default {};\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/annotation/Permission.java",
    "content": "package com.getcapacitor.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Permission annotation for use with @CapacitorPlugin\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Permission {\n    /**\n     * An array of Android permission strings.\n     * Eg: {Manifest.permission.ACCESS_COARSE_LOCATION}\n     *     or {\"android.permission.ACCESS_COARSE_LOCATION\"}\n     */\n    String[] strings() default {};\n\n    /**\n     * An optional name to use instead of the Android permission string.\n     */\n    String alias() default \"\";\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/annotation/PermissionCallback.java",
    "content": "package com.getcapacitor.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface PermissionCallback {}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java",
    "content": "package com.getcapacitor.cordova;\n\nimport android.webkit.CookieManager;\nimport android.webkit.WebView;\nimport org.apache.cordova.ICordovaCookieManager;\n\nclass CapacitorCordovaCookieManager implements ICordovaCookieManager {\n\n    protected final WebView webView;\n    private final CookieManager cookieManager;\n\n    public CapacitorCordovaCookieManager(WebView webview) {\n        webView = webview;\n        cookieManager = CookieManager.getInstance();\n        cookieManager.setAcceptThirdPartyCookies(webView, true);\n    }\n\n    @Override\n    public void setCookiesEnabled(boolean accept) {\n        cookieManager.setAcceptCookie(accept);\n    }\n\n    @Override\n    public void setCookie(final String url, final String value) {\n        cookieManager.setCookie(url, value);\n    }\n\n    @Override\n    public String getCookie(final String url) {\n        return cookieManager.getCookie(url);\n    }\n\n    @Override\n    public void clearCookies() {\n        cookieManager.removeAllCookies(null);\n    }\n\n    @Override\n    public void flush() {\n        cookieManager.flush();\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java",
    "content": "package com.getcapacitor.cordova;\n\nimport android.util.Pair;\nimport androidx.appcompat.app.AppCompatActivity;\nimport java.util.concurrent.Executors;\nimport org.apache.cordova.CordovaInterfaceImpl;\nimport org.apache.cordova.CordovaPlugin;\nimport org.json.JSONException;\n\npublic class MockCordovaInterfaceImpl extends CordovaInterfaceImpl {\n\n    public MockCordovaInterfaceImpl(AppCompatActivity activity) {\n        super(activity, Executors.newCachedThreadPool());\n    }\n\n    public CordovaPlugin getActivityResultCallback() {\n        return this.activityResultCallback;\n    }\n\n    /**\n     * Checks Cordova permission callbacks to handle permissions defined by a Cordova plugin.\n     * Returns true if Cordova is handling the permission request with a registered code.\n     *\n     * @param requestCode\n     * @param permissions\n     * @param grantResults\n     * @return true if Cordova handled the permission request, false if not\n     */\n    @SuppressWarnings(\"deprecation\")\n    public boolean handlePermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {\n        Pair<CordovaPlugin, Integer> callback = permissionResultCallbacks.getAndRemoveCallback(requestCode);\n        if (callback != null) {\n            callback.first.onRequestPermissionResult(callback.second, permissions, grantResults);\n            return true;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java",
    "content": "package com.getcapacitor.cordova;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Handler;\nimport android.view.View;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebView;\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.cordova.CordovaInterface;\nimport org.apache.cordova.CordovaPreferences;\nimport org.apache.cordova.CordovaResourceApi;\nimport org.apache.cordova.CordovaWebView;\nimport org.apache.cordova.CordovaWebViewEngine;\nimport org.apache.cordova.ICordovaCookieManager;\nimport org.apache.cordova.NativeToJsMessageQueue;\nimport org.apache.cordova.PluginEntry;\nimport org.apache.cordova.PluginManager;\nimport org.apache.cordova.PluginResult;\n\npublic class MockCordovaWebViewImpl implements CordovaWebView {\n\n    private Context context;\n    private PluginManager pluginManager;\n    private CordovaPreferences preferences;\n    private CordovaResourceApi resourceApi;\n    private NativeToJsMessageQueue nativeToJsMessageQueue;\n    private CordovaInterface cordova;\n    private CapacitorCordovaCookieManager cookieManager;\n    private WebView webView;\n    private boolean hasPausedEver;\n\n    public MockCordovaWebViewImpl(Context context) {\n        this.context = context;\n    }\n\n    @Override\n    public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences) {\n        this.cordova = cordova;\n        this.preferences = preferences;\n        this.pluginManager = new PluginManager(this, this.cordova, pluginEntries);\n        this.resourceApi = new CordovaResourceApi(this.context, this.pluginManager);\n        this.pluginManager.init();\n    }\n\n    public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences, WebView webView) {\n        this.cordova = cordova;\n        this.webView = webView;\n        this.preferences = preferences;\n        this.pluginManager = new PluginManager(this, this.cordova, pluginEntries);\n        this.resourceApi = new CordovaResourceApi(this.context, this.pluginManager);\n        nativeToJsMessageQueue = new NativeToJsMessageQueue();\n        nativeToJsMessageQueue.addBridgeMode(new CapacitorEvalBridgeMode(webView, this.cordova));\n        nativeToJsMessageQueue.setBridgeMode(0);\n        this.cookieManager = new CapacitorCordovaCookieManager(webView);\n        this.pluginManager.init();\n    }\n\n    public static class CapacitorEvalBridgeMode extends NativeToJsMessageQueue.BridgeMode {\n\n        private final WebView webView;\n        private final CordovaInterface cordova;\n\n        public CapacitorEvalBridgeMode(WebView webView, CordovaInterface cordova) {\n            this.webView = webView;\n            this.cordova = cordova;\n        }\n\n        @Override\n        public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) {\n            cordova\n                .getActivity()\n                .runOnUiThread(() -> {\n                    String js = queue.popAndEncodeAsJs();\n                    if (js != null) {\n                        webView.evaluateJavascript(js, null);\n                    }\n                });\n        }\n    }\n\n    @Override\n    public boolean isInitialized() {\n        return cordova != null;\n    }\n\n    @Override\n    public View getView() {\n        return this.webView;\n    }\n\n    @Override\n    public void loadUrlIntoView(String url, boolean recreatePlugins) {\n        if (url.equals(\"about:blank\") || url.startsWith(\"javascript:\")) {\n            webView.loadUrl(url);\n            return;\n        }\n    }\n\n    @Override\n    public void stopLoading() {}\n\n    @Override\n    public boolean canGoBack() {\n        return false;\n    }\n\n    @Override\n    public void clearCache() {}\n\n    @Deprecated\n    @Override\n    public void clearCache(boolean b) {}\n\n    @Override\n    public void clearHistory() {}\n\n    @Override\n    public boolean backHistory() {\n        return false;\n    }\n\n    @Override\n    public void handlePause(boolean keepRunning) {\n        if (!isInitialized()) {\n            return;\n        }\n        hasPausedEver = true;\n        pluginManager.onPause(keepRunning);\n        triggerDocumentEvent(\"pause\");\n        // If app doesn't want to run in background\n        if (!keepRunning) {\n            // Pause JavaScript timers. This affects all webviews within the app!\n            this.setPaused(true);\n        }\n    }\n\n    @Override\n    public void onNewIntent(Intent intent) {\n        if (this.pluginManager != null) {\n            this.pluginManager.onNewIntent(intent);\n        }\n    }\n\n    @Override\n    public void handleResume(boolean keepRunning) {\n        if (!isInitialized()) {\n            return;\n        }\n        this.setPaused(false);\n        this.pluginManager.onResume(keepRunning);\n        if (hasPausedEver) {\n            triggerDocumentEvent(\"resume\");\n        }\n    }\n\n    @Override\n    public void handleStart() {\n        if (!isInitialized()) {\n            return;\n        }\n        pluginManager.onStart();\n    }\n\n    @Override\n    public void handleStop() {\n        if (!isInitialized()) {\n            return;\n        }\n        pluginManager.onStop();\n    }\n\n    @Override\n    public void handleDestroy() {\n        if (!isInitialized()) {\n            return;\n        }\n        this.pluginManager.onDestroy();\n    }\n\n    @Deprecated\n    @Override\n    public void sendJavascript(String statememt) {\n        nativeToJsMessageQueue.addJavaScript(statememt);\n    }\n\n    public void eval(final String js, final ValueCallback<String> callback) {\n        Handler mainHandler = new Handler(context.getMainLooper());\n        mainHandler.post(() -> webView.evaluateJavascript(js, callback));\n    }\n\n    public void triggerDocumentEvent(final String eventName) {\n        eval(\"window.Capacitor.triggerEvent('\" + eventName + \"', 'document');\", (s) -> {});\n    }\n\n    @Override\n    public void showWebPage(String url, boolean openExternal, boolean clearHistory, Map<String, Object> params) {}\n\n    @Deprecated\n    @Override\n    public boolean isCustomViewShowing() {\n        return false;\n    }\n\n    @Deprecated\n    @Override\n    public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) {}\n\n    @Deprecated\n    @Override\n    public void hideCustomView() {}\n\n    @Override\n    public CordovaResourceApi getResourceApi() {\n        return this.resourceApi;\n    }\n\n    @Override\n    public void setButtonPlumbedToJs(int keyCode, boolean override) {}\n\n    @Override\n    public boolean isButtonPlumbedToJs(int keyCode) {\n        return false;\n    }\n\n    @Override\n    public void sendPluginResult(PluginResult cr, String callbackId) {\n        nativeToJsMessageQueue.addPluginResult(cr, callbackId);\n    }\n\n    @Override\n    public PluginManager getPluginManager() {\n        return this.pluginManager;\n    }\n\n    @Override\n    public CordovaWebViewEngine getEngine() {\n        return null;\n    }\n\n    @Override\n    public CordovaPreferences getPreferences() {\n        return this.preferences;\n    }\n\n    @Override\n    public ICordovaCookieManager getCookieManager() {\n        return cookieManager;\n    }\n\n    @Override\n    public String getUrl() {\n        return webView.getUrl();\n    }\n\n    @Override\n    public Context getContext() {\n        return this.webView.getContext();\n    }\n\n    @Override\n    public void loadUrl(String url) {\n        loadUrlIntoView(url, true);\n    }\n\n    @Override\n    public Object postMessage(String id, Object data) {\n        return pluginManager.postMessage(id, data);\n    }\n\n    public void setPaused(boolean value) {\n        if (value) {\n            webView.onPause();\n            webView.pauseTimers();\n        } else {\n            webView.onResume();\n            webView.resumeTimers();\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java",
    "content": "package com.getcapacitor.plugin;\n\nimport com.getcapacitor.Bridge;\nimport com.getcapacitor.Logger;\nimport java.net.CookieManager;\nimport java.net.CookiePolicy;\nimport java.net.CookieStore;\nimport java.net.HttpCookie;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\n\npublic class CapacitorCookieManager extends CookieManager {\n\n    private final android.webkit.CookieManager webkitCookieManager;\n\n    private final String localUrl;\n\n    private final String serverUrl;\n\n    private final String TAG = \"CapacitorCookies\";\n\n    /**\n     * Create a new cookie manager with the default cookie store and policy\n     */\n    public CapacitorCookieManager(Bridge bridge) {\n        this(null, null, bridge);\n    }\n\n    /**\n     * Create a new cookie manager with specified cookie store and cookie policy.\n     * @param store a {@code CookieStore} to be used by CookieManager. if {@code null}, cookie\n     *              manager will use a default one, which is an in-memory CookieStore implementation.\n     * @param policy a {@code CookiePolicy} instance to be used by cookie manager as policy\n     *               callback. if {@code null}, ACCEPT_ORIGINAL_SERVER will be used.\n     */\n    public CapacitorCookieManager(CookieStore store, CookiePolicy policy, Bridge bridge) {\n        super(store, policy);\n        webkitCookieManager = android.webkit.CookieManager.getInstance();\n        this.localUrl = bridge.getLocalUrl();\n        this.serverUrl = bridge.getServerUrl();\n    }\n\n    public void removeSessionCookies() {\n        this.webkitCookieManager.removeSessionCookies(null);\n    }\n\n    public String getSanitizedDomain(String url) throws URISyntaxException {\n        if (this.serverUrl != null && !this.serverUrl.isEmpty() && (url == null || url.isEmpty() || this.serverUrl.contains(url))) {\n            url = this.serverUrl;\n        } else if (this.localUrl != null && !this.localUrl.isEmpty() && (url == null || url.isEmpty() || this.localUrl.contains(url))) {\n            url = this.localUrl;\n        } else try {\n            URI uri = new URI(url);\n            String scheme = uri.getScheme();\n            if (scheme == null || scheme.isEmpty()) {\n                url = \"https://\" + url;\n            }\n        } catch (URISyntaxException e) {\n            Logger.error(TAG, \"Failed to get scheme from URL.\", e);\n        }\n\n        try {\n            new URI(url);\n        } catch (Exception error) {\n            Logger.error(TAG, \"Failed to get sanitized URL.\", error);\n            throw error;\n        }\n        return url;\n    }\n\n    private String getDomainFromCookieString(String cookie) throws URISyntaxException {\n        String[] domain = cookie.toLowerCase(Locale.ROOT).split(\"domain=\");\n        return getSanitizedDomain(domain.length <= 1 ? null : domain[1].split(\";\")[0].trim());\n    }\n\n    /**\n     * Gets the cookies for the given URL.\n     * @param url the URL for which the cookies are requested\n     * @return value the cookies as a string, using the format of the 'Cookie' HTTP request header\n     */\n    public String getCookieString(String url) {\n        try {\n            url = getSanitizedDomain(url);\n            Logger.info(TAG, \"Getting cookies at: '\" + url + \"'\");\n            return webkitCookieManager.getCookie(url);\n        } catch (Exception error) {\n            Logger.error(TAG, \"Failed to get cookies at the given URL.\", error);\n        }\n\n        return null;\n    }\n\n    /**\n     * Gets a cookie value for the given URL and key.\n     * @param url the URL for which the cookies are requested\n     * @param key the key of the cookie to search for\n     * @return the {@code HttpCookie} value of the cookie at the key,\n     *         otherwise it will return a new empty {@code HttpCookie}\n     */\n    public HttpCookie getCookie(String url, String key) {\n        HttpCookie[] cookies = getCookies(url);\n        for (HttpCookie cookie : cookies) {\n            if (cookie.getName().equals(key)) {\n                return cookie;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Gets an array of {@code HttpCookie} given a URL.\n     * @param url the URL for which the cookies are requested\n     * @return an {@code HttpCookie} array of non-expired cookies\n     */\n    public HttpCookie[] getCookies(String url) {\n        try {\n            ArrayList<HttpCookie> cookieList = new ArrayList<>();\n            String cookieString = getCookieString(url);\n            if (cookieString != null) {\n                String[] singleCookie = cookieString.split(\";\");\n                for (String c : singleCookie) {\n                    HttpCookie parsed = HttpCookie.parse(c).get(0);\n                    parsed.setValue(parsed.getValue());\n                    cookieList.add(parsed);\n                }\n            }\n            HttpCookie[] cookies = new HttpCookie[cookieList.size()];\n            return cookieList.toArray(cookies);\n        } catch (Exception ex) {\n            return new HttpCookie[0];\n        }\n    }\n\n    /**\n     * Sets a cookie for the given URL. Any existing cookie with the same host, path and name will\n     *  be replaced with the new cookie. The cookie being set will be ignored if it is expired.\n     * @param url the URL for which the cookie is to be set\n     * @param value the cookie as a string, using the format of the 'Set-Cookie' HTTP response header\n     */\n    public void setCookie(String url, String value) {\n        try {\n            url = getSanitizedDomain(url);\n            Logger.info(TAG, \"Setting cookie '\" + value + \"' at: '\" + url + \"'\");\n            webkitCookieManager.setCookie(url, value);\n            flush();\n        } catch (Exception error) {\n            Logger.error(TAG, \"Failed to set cookie.\", error);\n        }\n    }\n\n    /**\n     * Sets a cookie for the given URL. Any existing cookie with the same host, path and name will\n     *  be replaced with the new cookie. The cookie being set will be ignored if it is expired.\n     * @param url the URL for which the cookie is to be set\n     * @param key the {@code HttpCookie} name to use for lookup\n     * @param value the value of the {@code HttpCookie} given a key\n     */\n    public void setCookie(String url, String key, String value) {\n        String cookieValue = key + \"=\" + value;\n        setCookie(url, cookieValue);\n    }\n\n    public void setCookie(String url, String key, String value, String expires, String path) {\n        String cookieValue = key + \"=\" + value + \"; expires=\" + expires + \"; path=\" + path;\n        setCookie(url, cookieValue);\n    }\n\n    /**\n     * Removes all cookies. This method is asynchronous.\n     */\n    public void removeAllCookies() {\n        webkitCookieManager.removeAllCookies(null);\n        flush();\n    }\n\n    /**\n     * Ensures all cookies currently accessible through the getCookie API are written to persistent\n     *  storage. This call will block the caller until it is done and may perform I/O.\n     */\n    public void flush() {\n        webkitCookieManager.flush();\n    }\n\n    @Override\n    public void put(URI uri, Map<String, List<String>> responseHeaders) {\n        // make sure our args are valid\n        if ((uri == null) || (responseHeaders == null)) return;\n\n        // go over the headers\n        for (String headerKey : responseHeaders.keySet()) {\n            // ignore headers which aren't cookie related\n            if ((headerKey == null) || !(headerKey.equalsIgnoreCase(\"Set-Cookie2\") || headerKey.equalsIgnoreCase(\"Set-Cookie\"))) continue;\n\n            // process each of the headers\n            for (String headerValue : Objects.requireNonNull(responseHeaders.get(headerKey))) {\n                try {\n                    // Set at the requested server url\n                    setCookie(uri.toString(), headerValue);\n\n                    // Set at the defined domain in the response or at default capacitor hosted url\n                    setCookie(getDomainFromCookieString(headerValue), headerValue);\n                } catch (Exception ignored) {}\n            }\n        }\n    }\n\n    @Override\n    public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) {\n        // make sure our args are valid\n        if ((uri == null) || (requestHeaders == null)) throw new IllegalArgumentException(\"Argument is null\");\n\n        // save our url once\n        String url = uri.toString();\n\n        // prepare our response\n        Map<String, List<String>> res = new HashMap<>();\n\n        // get the cookie\n        String cookie = getCookieString(url);\n\n        // return it\n        if (cookie != null) res.put(\"Cookie\", Collections.singletonList(cookie));\n        return res;\n    }\n\n    @Override\n    public CookieStore getCookieStore() {\n        // we don't want anyone to work with this cookie store directly\n        throw new UnsupportedOperationException();\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java",
    "content": "package com.getcapacitor.plugin;\n\nimport android.webkit.JavascriptInterface;\nimport com.getcapacitor.JSObject;\nimport com.getcapacitor.Plugin;\nimport com.getcapacitor.PluginCall;\nimport com.getcapacitor.PluginConfig;\nimport com.getcapacitor.PluginMethod;\nimport com.getcapacitor.annotation.CapacitorPlugin;\nimport java.io.UnsupportedEncodingException;\nimport java.net.CookieHandler;\nimport java.net.HttpCookie;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\n\n@CapacitorPlugin\npublic class CapacitorCookies extends Plugin {\n\n    CapacitorCookieManager cookieManager;\n\n    @Override\n    public void load() {\n        this.bridge.getWebView().addJavascriptInterface(this, \"CapacitorCookiesAndroidInterface\");\n        this.cookieManager = new CapacitorCookieManager(null, java.net.CookiePolicy.ACCEPT_ALL, this.bridge);\n        this.cookieManager.removeSessionCookies();\n        CookieHandler.setDefault(this.cookieManager);\n        super.load();\n    }\n\n    @Override\n    protected void handleOnDestroy() {\n        super.handleOnDestroy();\n        this.cookieManager.removeSessionCookies();\n    }\n\n    @JavascriptInterface\n    public boolean isEnabled() {\n        PluginConfig pluginConfig = getBridge().getConfig().getPluginConfiguration(\"CapacitorCookies\");\n        return pluginConfig.getBoolean(\"enabled\", false);\n    }\n\n    @JavascriptInterface\n    public void setCookie(String domain, String action) {\n        cookieManager.setCookie(domain, action);\n    }\n\n    @PluginMethod\n    public void getCookies(PluginCall call) {\n        this.bridge.eval(\"document.cookie\", (value) -> {\n            String cookies = value.substring(1, value.length() - 1);\n            String[] cookieArray = cookies.split(\";\");\n\n            JSObject cookieMap = new JSObject();\n\n            for (String cookie : cookieArray) {\n                if (cookie.length() > 0) {\n                    String[] keyValue = cookie.split(\"=\", 2);\n\n                    if (keyValue.length == 2) {\n                        String key = keyValue[0].trim();\n                        String val = keyValue[1].trim();\n                        try {\n                            key = URLDecoder.decode(keyValue[0].trim(), StandardCharsets.UTF_8.name());\n                            val = URLDecoder.decode(keyValue[1].trim(), StandardCharsets.UTF_8.name());\n                        } catch (UnsupportedEncodingException ignored) {}\n\n                        cookieMap.put(key, val);\n                    }\n                }\n            }\n\n            call.resolve(cookieMap);\n        });\n    }\n\n    @PluginMethod\n    public void setCookie(PluginCall call) {\n        String key = call.getString(\"key\");\n        if (null == key) {\n            call.reject(\"Must provide key\");\n        }\n        String value = call.getString(\"value\");\n        if (null == value) {\n            call.reject(\"Must provide value\");\n        }\n        String url = call.getString(\"url\");\n        String expires = call.getString(\"expires\", \"\");\n        String path = call.getString(\"path\", \"/\");\n        cookieManager.setCookie(url, key, value, expires, path);\n        call.resolve();\n    }\n\n    @PluginMethod\n    public void deleteCookie(PluginCall call) {\n        String key = call.getString(\"key\");\n        if (null == key) {\n            call.reject(\"Must provide key\");\n        }\n        String url = call.getString(\"url\");\n        cookieManager.setCookie(url, key + \"=; Expires=Wed, 31 Dec 2000 23:59:59 GMT\");\n        call.resolve();\n    }\n\n    @PluginMethod\n    public void clearCookies(PluginCall call) {\n        String url = call.getString(\"url\");\n        HttpCookie[] cookies = cookieManager.getCookies(url);\n        for (HttpCookie cookie : cookies) {\n            cookieManager.setCookie(url, cookie.getName() + \"=; Expires=Wed, 31 Dec 2000 23:59:59 GMT\");\n        }\n        call.resolve();\n    }\n\n    @PluginMethod\n    public void clearAllCookies(PluginCall call) {\n        cookieManager.removeAllCookies();\n        call.resolve();\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java",
    "content": "package com.getcapacitor.plugin;\n\nimport android.Manifest;\nimport android.webkit.JavascriptInterface;\nimport com.getcapacitor.JSObject;\nimport com.getcapacitor.Plugin;\nimport com.getcapacitor.PluginCall;\nimport com.getcapacitor.PluginConfig;\nimport com.getcapacitor.PluginMethod;\nimport com.getcapacitor.annotation.CapacitorPlugin;\nimport com.getcapacitor.annotation.Permission;\nimport com.getcapacitor.plugin.util.CapacitorHttpUrlConnection;\nimport com.getcapacitor.plugin.util.HttpRequestHandler;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n@CapacitorPlugin(\n    permissions = {\n        @Permission(strings = { Manifest.permission.WRITE_EXTERNAL_STORAGE }, alias = \"HttpWrite\"),\n        @Permission(strings = { Manifest.permission.READ_EXTERNAL_STORAGE }, alias = \"HttpRead\")\n    }\n)\npublic class CapacitorHttp extends Plugin {\n\n    private final Map<Runnable, PluginCall> activeRequests = new ConcurrentHashMap<>();\n    private final ExecutorService executor = Executors.newCachedThreadPool();\n\n    @Override\n    public void load() {\n        this.bridge.getWebView().addJavascriptInterface(this, \"CapacitorHttpAndroidInterface\");\n        super.load();\n    }\n\n    @Override\n    protected void handleOnDestroy() {\n        super.handleOnDestroy();\n\n        for (Map.Entry<Runnable, PluginCall> entry : activeRequests.entrySet()) {\n            Runnable job = entry.getKey();\n            PluginCall call = entry.getValue();\n\n            if (call.getData().has(\"activeCapacitorHttpUrlConnection\")) {\n                try {\n                    CapacitorHttpUrlConnection connection = (CapacitorHttpUrlConnection) call\n                        .getData()\n                        .get(\"activeCapacitorHttpUrlConnection\");\n                    connection.disconnect();\n                    call.getData().remove(\"activeCapacitorHttpUrlConnection\");\n                } catch (Exception ignored) {}\n            }\n\n            getBridge().releaseCall(call);\n        }\n\n        activeRequests.clear();\n        executor.shutdownNow();\n    }\n\n    private void http(final PluginCall call, final String httpMethod) {\n        Runnable asyncHttpCall = new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    JSObject response = HttpRequestHandler.request(call, httpMethod, getBridge());\n                    call.resolve(response);\n                } catch (Exception e) {\n                    call.reject(e.getLocalizedMessage(), e.getClass().getSimpleName(), e);\n                } finally {\n                    activeRequests.remove(this);\n                }\n            }\n        };\n\n        if (!executor.isShutdown()) {\n            activeRequests.put(asyncHttpCall, call);\n            executor.submit(asyncHttpCall);\n        } else {\n            call.reject(\"Failed to execute request - Http Plugin was shutdown\");\n        }\n    }\n\n    @JavascriptInterface\n    public boolean isEnabled() {\n        PluginConfig pluginConfig = getBridge().getConfig().getPluginConfiguration(\"CapacitorHttp\");\n        return pluginConfig.getBoolean(\"enabled\", false);\n    }\n\n    @PluginMethod\n    public void request(final PluginCall call) {\n        this.http(call, null);\n    }\n\n    @PluginMethod\n    public void get(final PluginCall call) {\n        this.http(call, \"GET\");\n    }\n\n    @PluginMethod\n    public void post(final PluginCall call) {\n        this.http(call, \"POST\");\n    }\n\n    @PluginMethod\n    public void put(final PluginCall call) {\n        this.http(call, \"PUT\");\n    }\n\n    @PluginMethod\n    public void patch(final PluginCall call) {\n        this.http(call, \"PATCH\");\n    }\n\n    @PluginMethod\n    public void delete(final PluginCall call) {\n        this.http(call, \"DELETE\");\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java",
    "content": "package com.getcapacitor.plugin;\n\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.os.Build;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.view.Window;\nimport android.webkit.JavascriptInterface;\nimport android.webkit.WebView;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.core.view.WindowInsetsControllerCompat;\nimport androidx.webkit.WebViewCompat;\nimport com.getcapacitor.Plugin;\nimport com.getcapacitor.PluginCall;\nimport com.getcapacitor.PluginMethod;\nimport com.getcapacitor.WebViewListener;\nimport com.getcapacitor.annotation.CapacitorPlugin;\nimport java.util.Locale;\n\n@CapacitorPlugin\npublic class SystemBars extends Plugin {\n\n    static final String STYLE_LIGHT = \"LIGHT\";\n    static final String STYLE_DARK = \"DARK\";\n    static final String STYLE_DEFAULT = \"DEFAULT\";\n    static final String BAR_STATUS_BAR = \"StatusBar\";\n    static final String BAR_GESTURE_BAR = \"NavigationBar\";\n\n    static final String INSETS_HANDLING_CSS = \"css\";\n    static final String INSETS_HANDLING_DISABLE = \"disable\";\n\n    // https://issues.chromium.org/issues/40699457\n    private static final int WEBVIEW_VERSION_WITH_SAFE_AREA_FIX = 140;\n    // https://issues.chromium.org/issues/457682720\n    private static final int WEBVIEW_VERSION_WITH_SAFE_AREA_KEYBOARD_FIX = 144;\n\n    static final String viewportMetaJSFunction = \"\"\"\n        function capacitorSystemBarsCheckMetaViewport() {\n            const meta = document.querySelectorAll(\"meta[name=viewport]\");\n            if (meta.length == 0) {\n                return false;\n            }\n            // get the last found meta viewport tag\n            const metaContent = meta[meta.length - 1].content;\n            return metaContent.includes(\"viewport-fit=cover\");\n        }\n        capacitorSystemBarsCheckMetaViewport();\n        \"\"\";\n\n    private boolean insetHandlingEnabled = true;\n    private boolean hasViewportCover = false;\n\n    private String currentStyle = STYLE_DEFAULT;\n\n    @Override\n    public void load() {\n        getBridge().getWebView().addJavascriptInterface(this, \"CapacitorSystemBarsAndroidInterface\");\n        super.load();\n\n        initSystemBars();\n    }\n\n    @Override\n    protected void handleOnStart() {\n        super.handleOnStart();\n\n        this.getBridge().addWebViewListener(\n            new WebViewListener() {\n                @Override\n                public void onPageCommitVisible(WebView view, String url) {\n                    super.onPageCommitVisible(view, url);\n                    getBridge().getWebView().requestApplyInsets();\n                }\n            }\n        );\n    }\n\n    @Override\n    protected void handleOnConfigurationChanged(Configuration newConfig) {\n        super.handleOnConfigurationChanged(newConfig);\n\n        setStyle(currentStyle, \"\");\n    }\n\n    private void initSystemBars() {\n        String style = getConfig().getString(\"style\", STYLE_DEFAULT).toUpperCase(Locale.US);\n        boolean hidden = getConfig().getBoolean(\"hidden\", false);\n\n        String insetsHandling = getConfig().getString(\"insetsHandling\", \"css\");\n        if (insetsHandling.equals(INSETS_HANDLING_DISABLE)) {\n            insetHandlingEnabled = false;\n        }\n\n        initWindowInsetsListener();\n        initSafeAreaCSSVariables();\n\n        getBridge().executeOnMainThread(() -> {\n            setStyle(style, \"\");\n            setHidden(hidden, \"\");\n        });\n    }\n\n    @PluginMethod\n    public void setStyle(final PluginCall call) {\n        String bar = call.getString(\"bar\", \"\");\n        String style = call.getString(\"style\", STYLE_DEFAULT);\n\n        getBridge().executeOnMainThread(() -> {\n            setStyle(style, bar);\n            call.resolve();\n        });\n    }\n\n    @PluginMethod\n    public void show(final PluginCall call) {\n        String bar = call.getString(\"bar\", \"\");\n\n        getBridge().executeOnMainThread(() -> {\n            setHidden(false, bar);\n            call.resolve();\n        });\n    }\n\n    @PluginMethod\n    public void hide(final PluginCall call) {\n        String bar = call.getString(\"bar\", \"\");\n\n        getBridge().executeOnMainThread(() -> {\n            setHidden(true, bar);\n            call.resolve();\n        });\n    }\n\n    @PluginMethod\n    public void setAnimation(final PluginCall call) {\n        call.resolve();\n    }\n\n    @JavascriptInterface\n    public void onDOMReady() {\n        getActivity().runOnUiThread(() -> {\n            this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {\n                hasViewportCover = res.equals(\"true\");\n\n                getBridge().getWebView().requestApplyInsets();\n            });\n        });\n    }\n\n    private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {\n        Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());\n        if (insets.isVisible(WindowInsetsCompat.Type.ime())) {\n            return Insets.of(safeArea.left, safeArea.top, safeArea.right, 0);\n        }\n        return Insets.of(safeArea.left, safeArea.top, safeArea.right, safeArea.bottom);\n    }\n\n    private void initSafeAreaCSSVariables() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {\n            View v = (View) this.getBridge().getWebView().getParent();\n            WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);\n            if (insets != null) {\n                Insets safeAreaInsets = calcSafeAreaInsets(insets);\n                injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);\n            }\n        }\n    }\n\n    private void initWindowInsetsListener() {\n        ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {\n            boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover;\n\n            Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());\n            Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());\n            boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());\n\n            if (shouldPassthroughInsets) {\n                // We need to correct for a possible shown IME\n                v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);\n\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {\n                    Insets safeAreaInsets = calcSafeAreaInsets(insets);\n                    injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);\n                }\n\n                return new WindowInsetsCompat.Builder(insets)\n                    .setInsets(\n                        WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(),\n                        Insets.of(\n                            systemBarsInsets.left,\n                            systemBarsInsets.top,\n                            systemBarsInsets.right,\n                            getBottomInset(systemBarsInsets, keyboardVisible)\n                        )\n                    )\n                    .build();\n            }\n\n            // We need to correct for a possible shown IME\n            v.setPadding(\n                systemBarsInsets.left,\n                systemBarsInsets.top,\n                systemBarsInsets.right,\n                keyboardVisible ? imeInsets.bottom : systemBarsInsets.bottom\n            );\n\n            // Returning `WindowInsetsCompat.CONSUMED` breaks recalculation of safe area insets\n            // So we have to explicitly set insets to `0`\n            // See: https://issues.chromium.org/issues/461332423\n            WindowInsetsCompat newInsets = new WindowInsetsCompat.Builder(insets)\n                .setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0))\n                .build();\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {\n                Insets safeAreaInsets = calcSafeAreaInsets(newInsets);\n                injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);\n            }\n\n            return newInsets;\n        });\n    }\n\n    private void injectSafeAreaCSS(int top, int right, int bottom, int left) {\n        // Convert pixels to density-independent pixels\n        float density = getActivity().getResources().getDisplayMetrics().density;\n        float topPx = top / density;\n        float rightPx = right / density;\n        float bottomPx = bottom / density;\n        float leftPx = left / density;\n\n        // Execute JavaScript to inject the CSS\n        getBridge().executeOnMainThread(() -> {\n            if (bridge != null && bridge.getWebView() != null) {\n                String script = String.format(\n                    Locale.US,\n                    \"\"\"\n                    try {\n                      document.documentElement.style.setProperty(\"--safe-area-inset-top\", \"%dpx\");\n                      document.documentElement.style.setProperty(\"--safe-area-inset-right\", \"%dpx\");\n                      document.documentElement.style.setProperty(\"--safe-area-inset-bottom\", \"%dpx\");\n                      document.documentElement.style.setProperty(\"--safe-area-inset-left\", \"%dpx\");\n                    } catch(e) { console.error('Error injecting safe area CSS:', e); }\n                    \"\"\",\n                    (int) topPx,\n                    (int) rightPx,\n                    (int) bottomPx,\n                    (int) leftPx\n                );\n\n                bridge.getWebView().evaluateJavascript(script, null);\n            }\n        });\n    }\n\n    private void setStyle(String style, String bar) {\n        currentStyle = style;\n\n        if (style.equals(STYLE_DEFAULT)) {\n            style = getStyleForTheme();\n        }\n\n        Window window = getActivity().getWindow();\n        WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());\n        if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {\n            windowInsetsControllerCompat.setAppearanceLightStatusBars(!style.equals(STYLE_DARK));\n        }\n\n        if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {\n            windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals(STYLE_DARK));\n        }\n\n        getActivity().getWindow().getDecorView().setBackgroundColor(getThemeColor(getContext(), android.R.attr.windowBackground));\n    }\n\n    private void setHidden(boolean hide, String bar) {\n        Window window = getActivity().getWindow();\n        WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());\n\n        if (hide) {\n            if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {\n                windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.statusBars());\n            }\n            if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {\n                windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.navigationBars());\n            }\n            return;\n        }\n\n        if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {\n            windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars());\n        }\n        if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {\n            windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());\n        }\n    }\n\n    private String getStyleForTheme() {\n        int currentNightMode = getActivity().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;\n        if (currentNightMode != Configuration.UI_MODE_NIGHT_YES) {\n            return STYLE_LIGHT;\n        }\n        return STYLE_DARK;\n    }\n\n    public int getThemeColor(Context context, int attrRes) {\n        TypedValue typedValue = new TypedValue();\n\n        Resources.Theme theme = context.getTheme();\n        theme.resolveAttribute(attrRes, typedValue, true);\n        return typedValue.data;\n    }\n\n    private Integer getWebViewMajorVersion() {\n        PackageInfo info = WebViewCompat.getCurrentWebViewPackage(getContext());\n        if (info != null && info.versionName != null) {\n            String[] versionSegments = info.versionName.split(\"\\\\.\");\n            return Integer.valueOf(versionSegments[0]);\n        }\n\n        return 0;\n    }\n\n    private int getBottomInset(Insets systemBarsInsets, boolean keyboardVisible) {\n        if (getWebViewMajorVersion() < WEBVIEW_VERSION_WITH_SAFE_AREA_KEYBOARD_FIX) {\n            // This is a workaround for webview versions that have a bug\n            // that causes the bottom inset to be incorrect if the IME is visible\n            // See: https://issues.chromium.org/issues/457682720\n\n            if (keyboardVisible) {\n                return 0;\n            }\n        }\n\n        return systemBarsInsets.bottom;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/WebView.java",
    "content": "package com.getcapacitor.plugin;\n\nimport android.app.Activity;\nimport android.content.SharedPreferences;\nimport com.getcapacitor.JSObject;\nimport com.getcapacitor.Plugin;\nimport com.getcapacitor.PluginCall;\nimport com.getcapacitor.PluginMethod;\nimport com.getcapacitor.annotation.CapacitorPlugin;\n\n@CapacitorPlugin\npublic class WebView extends Plugin {\n\n    public static final String WEBVIEW_PREFS_NAME = \"CapWebViewSettings\";\n    public static final String CAP_SERVER_PATH = \"serverBasePath\";\n\n    @PluginMethod\n    public void setServerAssetPath(PluginCall call) {\n        String path = call.getString(\"path\");\n        bridge.setServerAssetPath(path);\n        call.resolve();\n    }\n\n    @PluginMethod\n    public void setServerBasePath(PluginCall call) {\n        String path = call.getString(\"path\");\n        bridge.setServerBasePath(path);\n        call.resolve();\n    }\n\n    @PluginMethod\n    public void getServerBasePath(PluginCall call) {\n        String path = bridge.getServerBasePath();\n        JSObject ret = new JSObject();\n        ret.put(\"path\", path);\n        call.resolve(ret);\n    }\n\n    @PluginMethod\n    public void persistServerBasePath(PluginCall call) {\n        String path = bridge.getServerBasePath();\n        SharedPreferences prefs = getContext().getSharedPreferences(WEBVIEW_PREFS_NAME, Activity.MODE_PRIVATE);\n        SharedPreferences.Editor editor = prefs.edit();\n        editor.putString(CAP_SERVER_PATH, path);\n        editor.apply();\n        call.resolve();\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/util/AssetUtil.java",
    "content": "package com.getcapacitor.plugin.util;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.res.AssetManager;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.net.Uri;\nimport android.os.StrictMode;\nimport androidx.core.content.FileProvider;\nimport com.getcapacitor.Logger;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.UUID;\n\n/**\n * Manager for assets.\n */\npublic final class AssetUtil {\n\n    public static final int RESOURCE_ID_ZERO_VALUE = 0;\n    // Name of the storage folder\n    private static final String STORAGE_FOLDER = \"/capacitorassets\";\n\n    // Ref to the context passed through the constructor to access the\n    // resources and app directory.\n    private final Context context;\n\n    /**\n     * Constructor\n     *\n     * @param context Application context.\n     */\n    private AssetUtil(Context context) {\n        this.context = context;\n    }\n\n    /**\n     * Static method to retrieve class instance.\n     *\n     * @param context Application context.\n     */\n    public static AssetUtil getInstance(Context context) {\n        return new AssetUtil(context);\n    }\n\n    /**\n     * The URI for a path.\n     *\n     * @param path The given path.\n     */\n    public Uri parse(String path) {\n        if (path == null || path.isEmpty()) {\n            return Uri.EMPTY;\n        } else if (path.startsWith(\"res:\")) {\n            return getUriForResourcePath(path);\n        } else if (path.startsWith(\"file:///\")) {\n            return getUriFromPath(path);\n        } else if (path.startsWith(\"file://\")) {\n            return getUriFromAsset(path);\n        } else if (path.startsWith(\"http\")) {\n            return getUriFromRemote(path);\n        } else if (path.startsWith(\"content://\")) {\n            return Uri.parse(path);\n        }\n\n        return Uri.EMPTY;\n    }\n\n    /**\n     * URI for a file.\n     *\n     * @param path Absolute path like file:///...\n     *\n     * @return URI pointing to the given path.\n     */\n    private Uri getUriFromPath(String path) {\n        String absPath = path.replaceFirst(\"file://\", \"\").replaceFirst(\"\\\\?.*$\", \"\");\n        File file = new File(absPath);\n\n        if (!file.exists()) {\n            Logger.error(\"File not found: \" + file.getAbsolutePath());\n            return Uri.EMPTY;\n        }\n\n        return getUriFromFile(file);\n    }\n\n    /**\n     * URI for an asset.\n     *\n     * @param path Asset path like file://...\n     *\n     * @return URI pointing to the given path.\n     */\n    private Uri getUriFromAsset(String path) {\n        String resPath = path.replaceFirst(\"file:/\", \"www\").replaceFirst(\"\\\\?.*$\", \"\");\n        String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);\n        File file = getTmpFile(fileName);\n\n        if (file == null) return Uri.EMPTY;\n\n        try {\n            AssetManager assets = context.getAssets();\n            InputStream in = assets.open(resPath);\n            FileOutputStream out = new FileOutputStream(file);\n            copyFile(in, out);\n        } catch (Exception e) {\n            Logger.error(\"File not found: assets/\" + resPath);\n            return Uri.EMPTY;\n        }\n\n        return getUriFromFile(file);\n    }\n\n    /**\n     * The URI for a resource.\n     *\n     * @param path The given relative path.\n     *\n     * @return URI pointing to the given path.\n     */\n    private Uri getUriForResourcePath(String path) {\n        Resources res = context.getResources();\n        String resPath = path.replaceFirst(\"res://\", \"\");\n        int resId = getResId(resPath);\n\n        if (resId == 0) {\n            Logger.error(\"File not found: \" + resPath);\n            return Uri.EMPTY;\n        }\n\n        return new Uri.Builder()\n            .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)\n            .authority(res.getResourcePackageName(resId))\n            .appendPath(res.getResourceTypeName(resId))\n            .appendPath(res.getResourceEntryName(resId))\n            .build();\n    }\n\n    /**\n     * Uri from remote located content.\n     *\n     * @param path Remote address.\n     *\n     * @return Uri of the downloaded file.\n     */\n    private Uri getUriFromRemote(String path) {\n        File file = getTmpFile();\n\n        if (file == null) return Uri.EMPTY;\n\n        try {\n            URL url = new URL(path);\n            HttpURLConnection connection = (HttpURLConnection) url.openConnection();\n\n            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();\n\n            StrictMode.setThreadPolicy(policy);\n\n            connection.setRequestProperty(\"Connection\", \"close\");\n            connection.setConnectTimeout(5000);\n            connection.connect();\n\n            InputStream in = connection.getInputStream();\n            FileOutputStream out = new FileOutputStream(file);\n\n            copyFile(in, out);\n            return getUriFromFile(file);\n        } catch (MalformedURLException e) {\n            Logger.error(Logger.tags(\"Asset\"), \"Incorrect URL\", e);\n        } catch (FileNotFoundException e) {\n            Logger.error(Logger.tags(\"Asset\"), \"Failed to create new File from HTTP Content\", e);\n        } catch (IOException e) {\n            Logger.error(Logger.tags(\"Asset\"), \"No Input can be created from http Stream\", e);\n        }\n\n        return Uri.EMPTY;\n    }\n\n    /**\n     * Copy content from input stream into output stream.\n     *\n     * @param in  The input stream.\n     * @param out The output stream.\n     */\n    private void copyFile(InputStream in, FileOutputStream out) {\n        byte[] buffer = new byte[1024];\n        int read;\n\n        try {\n            while ((read = in.read(buffer)) != -1) {\n                out.write(buffer, 0, read);\n            }\n            out.flush();\n            out.close();\n        } catch (Exception e) {\n            Logger.error(\"Error copying\", e);\n        }\n    }\n\n    /**\n     * Resource ID for drawable.\n     *\n     * @param resPath Resource path as string.\n     *\n     * @return The resource ID or 0 if not found.\n     */\n    public int getResId(String resPath) {\n        int resId = getResId(context.getResources(), resPath);\n\n        if (resId == 0) {\n            resId = getResId(Resources.getSystem(), resPath);\n        }\n\n        return resId;\n    }\n\n    /**\n     * Get resource ID.\n     *\n     * @param res     The resources where to look for.\n     * @param resPath The name of the resource.\n     *\n     * @return The resource ID or 0 if not found.\n     */\n    private int getResId(Resources res, String resPath) {\n        String pkgName = getPkgName(res);\n        String resName = getBaseName(resPath);\n        int resId;\n\n        resId = res.getIdentifier(resName, \"mipmap\", pkgName);\n\n        if (resId == 0) {\n            resId = res.getIdentifier(resName, \"drawable\", pkgName);\n        }\n\n        if (resId == 0) {\n            resId = res.getIdentifier(resName, \"raw\", pkgName);\n        }\n\n        return resId;\n    }\n\n    /**\n     * Convert URI to Bitmap.\n     *\n     * @param uri Internal image URI\n     */\n    public Bitmap getIconFromUri(Uri uri) throws IOException {\n        InputStream input = context.getContentResolver().openInputStream(uri);\n        return BitmapFactory.decodeStream(input);\n    }\n\n    /**\n     * Extract name of drawable resource from path.\n     *\n     * @param resPath Resource path as string.\n     */\n    private String getBaseName(String resPath) {\n        String drawable = resPath;\n\n        if (drawable.contains(\"/\")) {\n            drawable = drawable.substring(drawable.lastIndexOf('/') + 1);\n        }\n\n        if (resPath.contains(\".\")) {\n            drawable = drawable.substring(0, drawable.lastIndexOf('.'));\n        }\n\n        return drawable;\n    }\n\n    /**\n     * Returns a file located under the external cache dir of that app.\n     *\n     * @return File with a random UUID name.\n     */\n    private File getTmpFile() {\n        return getTmpFile(UUID.randomUUID().toString());\n    }\n\n    /**\n     * Returns a file located under the external cache dir of that app.\n     *\n     * @param name The name of the file.\n     *\n     * @return File with the provided name.\n     */\n    private File getTmpFile(String name) {\n        File dir = context.getExternalCacheDir();\n\n        if (dir == null) {\n            dir = context.getCacheDir();\n        }\n\n        if (dir == null) {\n            Logger.error(Logger.tags(\"Asset\"), \"Missing cache dir\", null);\n            return null;\n        }\n\n        String storage = dir.toString() + STORAGE_FOLDER;\n\n        //noinspection ResultOfMethodCallIgnored\n        new File(storage).mkdir();\n\n        return new File(storage, name);\n    }\n\n    /**\n     * Get content URI for the specified file.\n     *\n     * @param file The file to get the URI.\n     *\n     * @return content://...\n     */\n    private Uri getUriFromFile(File file) {\n        try {\n            String authority = context.getPackageName() + \".provider\";\n            return FileProvider.getUriForFile(context, authority, file);\n        } catch (IllegalArgumentException e) {\n            Logger.error(\"File not supported by provider\", e);\n            return Uri.EMPTY;\n        }\n    }\n\n    /**\n     * Package name specified by the resource bundle.\n     */\n    private String getPkgName(Resources res) {\n        return res == Resources.getSystem() ? \"android\" : context.getPackageName();\n    }\n\n    public static int getResourceID(Context context, String resourceName, String dir) {\n        return context.getResources().getIdentifier(resourceName, dir, context.getPackageName());\n    }\n\n    public static String getResourceBaseName(String resPath) {\n        if (resPath == null) return null;\n\n        if (resPath.contains(\"/\")) {\n            return resPath.substring(resPath.lastIndexOf('/') + 1);\n        }\n\n        if (resPath.contains(\".\")) {\n            return resPath.substring(0, resPath.lastIndexOf('.'));\n        }\n\n        return resPath;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java",
    "content": "package com.getcapacitor.plugin.util;\n\nimport android.os.Build;\nimport android.os.LocaleList;\nimport android.text.TextUtils;\nimport com.getcapacitor.Bridge;\nimport com.getcapacitor.JSArray;\nimport com.getcapacitor.JSObject;\nimport com.getcapacitor.JSValue;\nimport com.getcapacitor.PluginCall;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Method;\nimport java.net.HttpURLConnection;\nimport java.net.ProtocolException;\nimport java.net.SocketTimeoutException;\nimport java.net.URL;\nimport java.net.URLEncoder;\nimport java.net.UnknownServiceException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport javax.net.ssl.HttpsURLConnection;\nimport javax.net.ssl.SSLSocketFactory;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class CapacitorHttpUrlConnection implements ICapacitorHttpUrlConnection {\n\n    private final HttpURLConnection connection;\n\n    /**\n     * Make a new CapacitorHttpUrlConnection instance, which wraps around HttpUrlConnection\n     * and provides some helper functions for setting request headers and the request body\n     * @param conn the base HttpUrlConnection. You can pass the value from\n     *             {@code (HttpUrlConnection) URL.openConnection()}\n     */\n    public CapacitorHttpUrlConnection(HttpURLConnection conn) {\n        connection = conn;\n        this.setDefaultRequestProperties();\n    }\n\n    /**\n     * Returns the underlying HttpUrlConnection value\n     * @return the underlying HttpUrlConnection value\n     */\n    public HttpURLConnection getHttpConnection() {\n        return connection;\n    }\n\n    public void disconnect() {\n        connection.disconnect();\n    }\n\n    /**\n     * Set the value of the {@code allowUserInteraction} field of\n     * this {@code URLConnection}.\n     *\n     * @param   isAllowedInteraction   the new value.\n     * @throws IllegalStateException if already connected\n     */\n    public void setAllowUserInteraction(boolean isAllowedInteraction) {\n        connection.setAllowUserInteraction(isAllowedInteraction);\n    }\n\n    /**\n     * Set the method for the URL request, one of:\n     * <UL>\n     *  <LI>GET\n     *  <LI>POST\n     *  <LI>HEAD\n     *  <LI>OPTIONS\n     *  <LI>PUT\n     *  <LI>DELETE\n     *  <LI>TRACE\n     * </UL> are legal, subject to protocol restrictions.  The default\n     * method is GET.\n     *\n     * @param method the HTTP method\n     * @exception ProtocolException if the method cannot be reset or if\n     *              the requested method isn't valid for HTTP.\n     * @exception SecurityException if a security manager is set and the\n     *              method is \"TRACE\", but the \"allowHttpTrace\"\n     *              NetPermission is not granted.\n     */\n    public void setRequestMethod(String method) throws ProtocolException {\n        connection.setRequestMethod(method);\n    }\n\n    /**\n     * Sets a specified timeout value, in milliseconds, to be used\n     * when opening a communications link to the resource referenced\n     * by this URLConnection.  If the timeout expires before the\n     * connection can be established, a\n     * java.net.SocketTimeoutException is raised. A timeout of zero is\n     * interpreted as an infinite timeout.\n     *\n     * <p><strong>Warning</strong>: If the hostname resolves to multiple IP\n     * addresses, Android's default implementation of {@link HttpURLConnection}\n     * will try each in\n     * <a href=\"http://www.ietf.org/rfc/rfc3484.txt\">RFC 3484</a> order. If\n     * connecting to each of these addresses fails, multiple timeouts will\n     * elapse before the connect attempt throws an exception. Host names\n     * that support both IPv6 and IPv4 always have at least 2 IP addresses.\n     *\n     * @param timeout an {@code int} that specifies the connect\n     *               timeout value in milliseconds\n     * @throws IllegalArgumentException if the timeout parameter is negative\n     */\n    public void setConnectTimeout(int timeout) {\n        if (timeout < 0) {\n            throw new IllegalArgumentException(\"timeout can not be negative\");\n        }\n        connection.setConnectTimeout(timeout);\n    }\n\n    /**\n     * Sets the read timeout to a specified timeout, in\n     * milliseconds. A non-zero value specifies the timeout when\n     * reading from Input stream when a connection is established to a\n     * resource. If the timeout expires before there is data available\n     * for read, a java.net.SocketTimeoutException is raised. A\n     * timeout of zero is interpreted as an infinite timeout.\n     *\n     * @param timeout an {@code int} that specifies the timeout\n     * value to be used in milliseconds\n     * @throws IllegalArgumentException if the timeout parameter is negative\n     */\n    public void setReadTimeout(int timeout) {\n        if (timeout < 0) {\n            throw new IllegalArgumentException(\"timeout can not be negative\");\n        }\n        connection.setReadTimeout(timeout);\n    }\n\n    /**\n     * Sets whether automatic HTTP redirects should be disabled\n     * @param disableRedirects the flag to determine if redirects should be followed\n     */\n    public void setDisableRedirects(boolean disableRedirects) {\n        connection.setInstanceFollowRedirects(!disableRedirects);\n    }\n\n    /**\n     * Sets the request headers given a JSObject of key-value pairs\n     * @param headers the JSObject values to map to the HttpUrlConnection request headers\n     */\n    public void setRequestHeaders(JSObject headers) {\n        Iterator<String> keys = headers.keys();\n        while (keys.hasNext()) {\n            String key = keys.next();\n            String value = headers.getString(key);\n            connection.setRequestProperty(key, value);\n        }\n    }\n\n    /**\n     * Sets the value of the {@code doOutput} field for this\n     * {@code URLConnection} to the specified value.\n     * <p>\n     * A URL connection can be used for input and/or output.  Set the DoOutput\n     * flag to true if you intend to use the URL connection for output,\n     * false if not.  The default is false.\n     *\n     * @param  shouldDoOutput   the new value.\n     * @throws IllegalStateException if already connected\n     */\n    public void setDoOutput(boolean shouldDoOutput) {\n        connection.setDoOutput(shouldDoOutput);\n    }\n\n    /**\n     *\n     * @param call\n     * @throws JSONException\n     * @throws IOException\n     */\n    public void setRequestBody(PluginCall call, JSValue body) throws JSONException, IOException {\n        setRequestBody(call, body, null);\n    }\n\n    /**\n     *\n     * @param call\n     * @throws JSONException\n     * @throws IOException\n     */\n    public void setRequestBody(PluginCall call, JSValue body, String bodyType) throws JSONException, IOException {\n        String contentType = connection.getRequestProperty(\"Content-Type\");\n        String dataString = \"\";\n\n        if (contentType == null || contentType.isEmpty()) return;\n\n        if (contentType.contains(\"application/json\")) {\n            JSArray jsArray = null;\n            if (body != null) {\n                dataString = body.toString();\n            } else {\n                jsArray = call.getArray(\"data\", null);\n            }\n            if (jsArray != null) {\n                dataString = jsArray.toString();\n            } else if (body == null) {\n                dataString = call.getString(\"data\");\n            }\n            this.writeRequestBody(dataString != null ? dataString : \"\");\n        } else if (bodyType != null && bodyType.equals(\"file\")) {\n            try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                    os.write(Base64.getDecoder().decode(body.toString()));\n                }\n                os.flush();\n            }\n        } else if (contentType.contains(\"application/x-www-form-urlencoded\")) {\n            try {\n                JSObject obj = body.toJSObject();\n                this.writeObjectRequestBody(obj);\n            } catch (Exception e) {\n                // Body is not a valid JSON, treat it as an already formatted string\n                this.writeRequestBody(body.toString());\n            }\n        } else if (bodyType != null && bodyType.equals(\"formData\")) {\n            this.writeFormDataRequestBody(contentType, body.toJSArray());\n        } else {\n            this.writeRequestBody(body.toString());\n        }\n    }\n\n    /**\n     * Writes the provided string to the HTTP connection managed by this instance.\n     *\n     * @param body The string value to write to the connection stream.\n     */\n    private void writeRequestBody(String body) throws IOException {\n        try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {\n            os.write(body.getBytes(StandardCharsets.UTF_8));\n            os.flush();\n        }\n    }\n\n    private void writeObjectRequestBody(JSObject object) throws IOException, JSONException {\n        try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {\n            Iterator<String> keys = object.keys();\n            while (keys.hasNext()) {\n                String key = keys.next();\n                Object d = object.get(key);\n                os.writeBytes(URLEncoder.encode(key, \"UTF-8\"));\n                os.writeBytes(\"=\");\n                os.writeBytes(URLEncoder.encode(d.toString(), \"UTF-8\"));\n\n                if (keys.hasNext()) {\n                    os.writeBytes(\"&\");\n                }\n            }\n            os.flush();\n        }\n    }\n\n    private void writeFormDataRequestBody(String contentType, JSArray entries) throws IOException, JSONException {\n        try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {\n            String boundary = contentType.split(\";\")[1].split(\"=\")[1];\n            String lineEnd = \"\\r\\n\";\n            String twoHyphens = \"--\";\n\n            for (Object e : entries.toList()) {\n                if (e instanceof JSONObject) {\n                    JSONObject entry = (JSONObject) e;\n                    String type = entry.getString(\"type\");\n                    String key = entry.getString(\"key\");\n                    String value = entry.getString(\"value\");\n                    if (type.equals(\"string\")) {\n                        os.writeBytes(twoHyphens + boundary + lineEnd);\n                        os.writeBytes(\"Content-Disposition: form-data; name=\\\"\" + key + \"\\\"\" + lineEnd + lineEnd);\n                        os.write(value.getBytes(StandardCharsets.UTF_8));\n                        os.writeBytes(lineEnd);\n                    } else if (type.equals(\"base64File\")) {\n                        String fileName = entry.getString(\"fileName\");\n                        String fileContentType = entry.getString(\"contentType\");\n\n                        os.writeBytes(twoHyphens + boundary + lineEnd);\n                        os.writeBytes(\"Content-Disposition: form-data; name=\\\"\" + key + \"\\\"; filename=\\\"\" + fileName + \"\\\"\" + lineEnd);\n                        os.writeBytes(\"Content-Type: \" + fileContentType + lineEnd);\n                        os.writeBytes(\"Content-Transfer-Encoding: binary\" + lineEnd);\n                        os.writeBytes(lineEnd);\n\n                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                            os.write(Base64.getDecoder().decode(value));\n                        } else {\n                            os.write(android.util.Base64.decode(value, android.util.Base64.DEFAULT));\n                        }\n\n                        os.writeBytes(lineEnd);\n                    }\n                }\n            }\n\n            os.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);\n            os.flush();\n        }\n    }\n\n    /**\n     * Opens a communications link to the resource referenced by this\n     * URL, if such a connection has not already been established.\n     * <p>\n     * If the {@code connect} method is called when the connection\n     * has already been opened (indicated by the {@code connected}\n     * field having the value {@code true}), the call is ignored.\n     * <p>\n     * URLConnection objects go through two phases: first they are\n     * created, then they are connected.  After being created, and\n     * before being connected, various options can be specified\n     * (e.g., doInput and UseCaches).  After connecting, it is an\n     * error to try to set them.  Operations that depend on being\n     * connected, like getContentLength, will implicitly perform the\n     * connection, if necessary.\n     *\n     * @throws SocketTimeoutException if the timeout expires before\n     *               the connection can be established\n     * @exception  IOException  if an I/O error occurs while opening the\n     *               connection.\n     */\n    public void connect() throws IOException {\n        connection.connect();\n    }\n\n    /**\n     * Gets the status code from an HTTP response message.\n     * For example, in the case of the following status lines:\n     * <PRE>\n     * HTTP/1.0 200 OK\n     * HTTP/1.0 401 Unauthorized\n     * </PRE>\n     * It will return 200 and 401 respectively.\n     * Returns -1 if no code can be discerned\n     * from the response (i.e., the response is not valid HTTP).\n     * @throws IOException if an error occurred connecting to the server.\n     * @return the HTTP Status-Code, or -1\n     */\n    public int getResponseCode() throws IOException {\n        return connection.getResponseCode();\n    }\n\n    /**\n     * Returns the value of this {@code URLConnection}'s {@code URL}\n     * field.\n     *\n     * @return  the value of this {@code URLConnection}'s {@code URL}\n     *          field.\n     */\n    public URL getURL() {\n        return connection.getURL();\n    }\n\n    /**\n     * Returns the error stream if the connection failed\n     * but the server sent useful data nonetheless. The\n     * typical example is when an HTTP server responds\n     * with a 404, which will cause a FileNotFoundException\n     * to be thrown in connect, but the server sent an HTML\n     * help page with suggestions as to what to do.\n     *\n     * <p>This method will not cause a connection to be initiated.  If\n     * the connection was not connected, or if the server did not have\n     * an error while connecting or if the server had an error but\n     * no error data was sent, this method will return null. This is\n     * the default.\n     *\n     * @return an error stream if any, null if there have been no\n     * errors, the connection is not connected or the server sent no\n     * useful data.\n     */\n    @Override\n    public InputStream getErrorStream() {\n        return connection.getErrorStream();\n    }\n\n    /**\n     * Returns the value of the named header field.\n     * <p>\n     * If called on a connection that sets the same header multiple times\n     * with possibly different values, only the last value is returned.\n     *\n     *\n     * @param   name   the name of a header field.\n     * @return  the value of the named header field, or {@code null}\n     *          if there is no such field in the header.\n     */\n    @Override\n    public String getHeaderField(String name) {\n        return connection.getHeaderField(name);\n    }\n\n    /**\n     * Returns an input stream that reads from this open connection.\n     *\n     * A SocketTimeoutException can be thrown when reading from the\n     * returned input stream if the read timeout expires before data\n     * is available for read.\n     *\n     * @return     an input stream that reads from this open connection.\n     * @exception  IOException              if an I/O error occurs while\n     *               creating the input stream.\n     * @exception UnknownServiceException  if the protocol does not support\n     *               input.\n     * @see #setReadTimeout(int)\n     */\n    @Override\n    public InputStream getInputStream() throws IOException {\n        return connection.getInputStream();\n    }\n\n    /**\n     * Returns an unmodifiable Map of the header fields.\n     * The Map keys are Strings that represent the\n     * response-header field names. Each Map value is an\n     * unmodifiable List of Strings that represents\n     * the corresponding field values.\n     *\n     * @return a Map of header fields\n     */\n    public Map<String, List<String>> getHeaderFields() {\n        return connection.getHeaderFields();\n    }\n\n    /**\n     * Sets the default request properties on the newly created connection.\n     * This is called as early as possible to allow overrides by user-provided values.\n     */\n    private void setDefaultRequestProperties() {\n        String acceptLanguage = buildDefaultAcceptLanguageProperty();\n        if (!TextUtils.isEmpty(acceptLanguage)) {\n            connection.setRequestProperty(\"Accept-Language\", acceptLanguage);\n        }\n    }\n\n    /**\n     * Builds and returns a locale string describing the device's current locale preferences.\n     */\n    private String buildDefaultAcceptLanguageProperty() {\n        Locale locale = LocaleList.getDefault().get(0);\n        String result = \"\";\n        String lang = locale.getLanguage();\n        String country = locale.getCountry();\n        if (!TextUtils.isEmpty(lang)) {\n            if (!TextUtils.isEmpty(country)) {\n                result = String.format(\"%s-%s,%s;q=0.5\", lang, country, lang);\n            } else {\n                result = String.format(\"%s;q=0.5\", lang);\n            }\n        }\n        return result;\n    }\n\n    public void setSSLSocketFactory(Bridge bridge) {\n        // Attach SSL Certificates if Enterprise Plugin is available\n        try {\n            Class<?> sslPinningImpl = Class.forName(\"io.ionic.sslpinning.SSLPinning\");\n            Method method = sslPinningImpl.getDeclaredMethod(\"getSSLSocketFactory\", Bridge.class);\n            SSLSocketFactory sslSocketFactory = (SSLSocketFactory) method.invoke(\n                sslPinningImpl.getDeclaredConstructor().newInstance(),\n                bridge\n            );\n            if (sslSocketFactory != null) {\n                ((HttpsURLConnection) this.connection).setSSLSocketFactory(sslSocketFactory);\n            }\n        } catch (Exception ignored) {}\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java",
    "content": "package com.getcapacitor.plugin.util;\n\nimport android.text.TextUtils;\nimport android.util.Base64;\nimport com.getcapacitor.Bridge;\nimport com.getcapacitor.JSArray;\nimport com.getcapacitor.JSObject;\nimport com.getcapacitor.JSValue;\nimport com.getcapacitor.PluginCall;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.UnsupportedEncodingException;\nimport java.lang.reflect.Method;\nimport java.net.HttpURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLEncoder;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class HttpRequestHandler {\n\n    /**\n     * An enum specifying conventional HTTP Response Types\n     * See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType\n     */\n    public enum ResponseType {\n        ARRAY_BUFFER(\"arraybuffer\"),\n        BLOB(\"blob\"),\n        DOCUMENT(\"document\"),\n        JSON(\"json\"),\n        TEXT(\"text\");\n\n        private final String name;\n\n        ResponseType(String name) {\n            this.name = name;\n        }\n\n        static final ResponseType DEFAULT = TEXT;\n\n        public static ResponseType parse(String value) {\n            for (ResponseType responseType : values()) {\n                if (responseType.name.equalsIgnoreCase(value)) {\n                    return responseType;\n                }\n            }\n            return DEFAULT;\n        }\n    }\n\n    /**\n     * Internal builder class for building a CapacitorHttpUrlConnection\n     */\n    public static class HttpURLConnectionBuilder {\n\n        public Integer connectTimeout;\n        public Integer readTimeout;\n        public Boolean disableRedirects;\n        public JSObject headers;\n        public String method;\n        public URL url;\n\n        public CapacitorHttpUrlConnection connection;\n\n        public HttpURLConnectionBuilder setConnectTimeout(Integer connectTimeout) {\n            this.connectTimeout = connectTimeout;\n            return this;\n        }\n\n        public HttpURLConnectionBuilder setReadTimeout(Integer readTimeout) {\n            this.readTimeout = readTimeout;\n            return this;\n        }\n\n        public HttpURLConnectionBuilder setDisableRedirects(Boolean disableRedirects) {\n            this.disableRedirects = disableRedirects;\n            return this;\n        }\n\n        public HttpURLConnectionBuilder setHeaders(JSObject headers) {\n            this.headers = headers;\n            return this;\n        }\n\n        public HttpURLConnectionBuilder setMethod(String method) {\n            this.method = method;\n            return this;\n        }\n\n        public HttpURLConnectionBuilder setUrl(URL url) {\n            this.url = url;\n            return this;\n        }\n\n        public HttpURLConnectionBuilder openConnection() throws IOException {\n            connection = new CapacitorHttpUrlConnection((HttpURLConnection) url.openConnection());\n\n            connection.setAllowUserInteraction(false);\n            connection.setRequestMethod(method);\n\n            if (connectTimeout != null) connection.setConnectTimeout(connectTimeout);\n            if (readTimeout != null) connection.setReadTimeout(readTimeout);\n            if (disableRedirects != null) connection.setDisableRedirects(disableRedirects);\n\n            connection.setRequestHeaders(headers);\n            return this;\n        }\n\n        public HttpURLConnectionBuilder setUrlParams(JSObject params) throws MalformedURLException, URISyntaxException, JSONException {\n            return this.setUrlParams(params, true);\n        }\n\n        public HttpURLConnectionBuilder setUrlParams(JSObject params, boolean shouldEncode)\n            throws URISyntaxException, MalformedURLException {\n            String initialQuery = url.getQuery();\n            String initialQueryBuilderStr = initialQuery == null ? \"\" : initialQuery;\n\n            Iterator<String> keys = params.keys();\n\n            if (!keys.hasNext()) {\n                return this;\n            }\n\n            StringBuilder urlQueryBuilder = new StringBuilder(initialQueryBuilderStr);\n\n            // Build the new query string\n            while (keys.hasNext()) {\n                String key = keys.next();\n\n                // Attempt as JSONArray and fallback to string if it fails\n                try {\n                    StringBuilder value = new StringBuilder();\n                    JSONArray arr = params.getJSONArray(key);\n                    for (int x = 0; x < arr.length(); x++) {\n                        this.addUrlParam(value, key, arr.getString(x), shouldEncode);\n                        if (x != arr.length() - 1) {\n                            value.append(\"&\");\n                        }\n                    }\n                    if (urlQueryBuilder.length() > 0) {\n                        urlQueryBuilder.append(\"&\");\n                    }\n                    urlQueryBuilder.append(value);\n                } catch (JSONException e) {\n                    if (urlQueryBuilder.length() > 0) {\n                        urlQueryBuilder.append(\"&\");\n                    }\n                    this.addUrlParam(urlQueryBuilder, key, params.getString(key), shouldEncode);\n                }\n            }\n\n            String urlQuery = urlQueryBuilder.toString();\n\n            URI uri = url.toURI();\n            String unEncodedUrlString =\n                uri.getScheme() +\n                \"://\" +\n                uri.getAuthority() +\n                uri.getPath() +\n                ((!urlQuery.equals(\"\")) ? \"?\" + urlQuery : \"\") +\n                ((uri.getFragment() != null) ? uri.getFragment() : \"\");\n            this.url = new URL(unEncodedUrlString);\n\n            return this;\n        }\n\n        private static void addUrlParam(StringBuilder sb, String key, String value, boolean shouldEncode) {\n            if (shouldEncode) {\n                try {\n                    key = URLEncoder.encode(key, \"UTF-8\");\n                    value = URLEncoder.encode(value, \"UTF-8\");\n                } catch (UnsupportedEncodingException ex) {\n                    throw new RuntimeException(ex.getCause());\n                }\n            }\n            sb.append(key).append(\"=\").append(value);\n        }\n\n        public CapacitorHttpUrlConnection build() {\n            return connection;\n        }\n    }\n\n    /**\n     * Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects.\n     *   Defaults to ResponseType.DEFAULT\n     * @param connection The CapacitorHttpUrlConnection to respond with\n     * @throws IOException Thrown if the InputStream is unable to be parsed correctly\n     * @throws JSONException Thrown if the JSON is unable to be parsed\n     */\n    public static JSObject buildResponse(CapacitorHttpUrlConnection connection) throws IOException, JSONException {\n        return buildResponse(connection, ResponseType.DEFAULT);\n    }\n\n    /**\n     * Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects\n     * @param connection The CapacitorHttpUrlConnection to respond with\n     * @param responseType The requested ResponseType\n     * @return A JSObject that contains the HTTPResponse to return to the browser\n     * @throws IOException Thrown if the InputStream is unable to be parsed correctly\n     * @throws JSONException Thrown if the JSON is unable to be parsed\n     */\n    public static JSObject buildResponse(CapacitorHttpUrlConnection connection, ResponseType responseType)\n        throws IOException, JSONException {\n        int statusCode = connection.getResponseCode();\n\n        JSObject output = new JSObject();\n        output.put(\"status\", statusCode);\n        output.put(\"headers\", buildResponseHeaders(connection));\n        output.put(\"url\", connection.getURL());\n        output.put(\"data\", readData(connection, responseType));\n\n        InputStream errorStream = connection.getErrorStream();\n        if (errorStream != null) {\n            output.put(\"error\", true);\n        }\n\n        return output;\n    }\n\n    /**\n     * Read the existing ICapacitorHttpUrlConnection data\n     * @param connection The ICapacitorHttpUrlConnection object to read in\n     * @param responseType The type of HTTP response to return to the API\n     * @return The parsed data from the connection\n     * @throws IOException Thrown if the InputStreams cannot be properly parsed\n     * @throws JSONException Thrown if the JSON is malformed when parsing as JSON\n     */\n    public static Object readData(ICapacitorHttpUrlConnection connection, ResponseType responseType) throws IOException, JSONException {\n        InputStream errorStream = connection.getErrorStream();\n        String contentType = connection.getHeaderField(\"Content-Type\");\n\n        if (errorStream != null) {\n            if (isOneOf(contentType, MimeType.APPLICATION_JSON, MimeType.APPLICATION_VND_API_JSON)) {\n                return parseJSON(readStreamAsString(errorStream));\n            } else {\n                return readStreamAsString(errorStream);\n            }\n        } else if (contentType != null && contentType.contains(MimeType.APPLICATION_JSON.getValue())) {\n            // backward compatibility\n            return parseJSON(readStreamAsString(connection.getInputStream()));\n        } else {\n            InputStream stream = connection.getInputStream();\n            switch (responseType) {\n                case ARRAY_BUFFER:\n                case BLOB:\n                    return readStreamAsBase64(stream);\n                case JSON:\n                    return parseJSON(readStreamAsString(stream));\n                case DOCUMENT:\n                case TEXT:\n                default:\n                    return readStreamAsString(stream);\n            }\n        }\n    }\n\n    /**\n     * Helper function for determining if the Content-Type is a typeof an existing Mime-Type\n     * @param contentType The Content-Type string to check for\n     * @param mimeTypes The Mime-Type values to check against\n     * @return\n     */\n    public static boolean isOneOf(String contentType, MimeType... mimeTypes) {\n        if (contentType != null) {\n            for (MimeType mimeType : mimeTypes) {\n                if (contentType.contains(mimeType.getValue())) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Build the JSObject response headers based on the connection header map\n     * @param connection The CapacitorHttpUrlConnection connection\n     * @return A JSObject of the header values from the CapacitorHttpUrlConnection\n     */\n    public static JSObject buildResponseHeaders(CapacitorHttpUrlConnection connection) {\n        JSObject output = new JSObject();\n\n        for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {\n            String valuesString = TextUtils.join(\", \", entry.getValue());\n            output.put(entry.getKey(), valuesString);\n        }\n\n        return output;\n    }\n\n    /**\n     * Returns a JSObject or a JSArray based on a string-ified input\n     * @param input String-ified JSON that needs parsing\n     * @return A JSObject or JSArray\n     * @throws JSONException thrown if the JSON is malformed\n     */\n    public static Object parseJSON(String input) throws JSONException {\n        JSONObject json = new JSONObject();\n        try {\n            if (\"null\".equals(input.trim())) {\n                return JSONObject.NULL;\n            } else if (\"true\".equals(input.trim())) {\n                return true;\n            } else if (\"false\".equals(input.trim())) {\n                return false;\n            } else if (input.trim().length() <= 0) {\n                return \"\";\n            } else if (input.trim().matches(\"^\\\".*\\\"$\")) {\n                // a string enclosed in \" \" is a json value, return the string without the quotes\n                return input.trim().substring(1, input.trim().length() - 1);\n            } else if (input.trim().matches(\"^-?\\\\d+$\")) {\n                return Integer.parseInt(input.trim());\n            } else if (input.trim().matches(\"^-?\\\\d+(\\\\.\\\\d+)?$\")) {\n                return Double.parseDouble(input.trim());\n            } else {\n                try {\n                    return new JSObject(input);\n                } catch (JSONException e) {\n                    return new JSArray(input);\n                }\n            }\n        } catch (JSONException e) {\n            return input;\n        }\n    }\n\n    /**\n     * Returns a string based on a base64 InputStream\n     * @param in The base64 InputStream to convert to a String\n     * @return String value of InputStream\n     * @throws IOException thrown if the InputStream is unable to be read as base64\n     */\n    public static String readStreamAsBase64(InputStream in) throws IOException {\n        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {\n            byte[] buffer = new byte[1024];\n            int readBytes;\n            while ((readBytes = in.read(buffer)) != -1) {\n                out.write(buffer, 0, readBytes);\n            }\n            byte[] result = out.toByteArray();\n            return Base64.encodeToString(result, 0, result.length, Base64.DEFAULT);\n        }\n    }\n\n    /**\n     * Returns a string based on an InputStream\n     * @param in The InputStream to convert to a String\n     * @return String value of InputStream\n     * @throws IOException thrown if the InputStream is unable to be read\n     */\n    public static String readStreamAsString(InputStream in) throws IOException {\n        try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {\n            StringBuilder builder = new StringBuilder();\n            String line = reader.readLine();\n            while (line != null) {\n                builder.append(line);\n                line = reader.readLine();\n                if (line != null) {\n                    builder.append(System.getProperty(\"line.separator\"));\n                }\n            }\n            return builder.toString();\n        }\n    }\n\n    /**\n     * Makes an Http Request based on the PluginCall parameters\n     * @param call The Capacitor PluginCall that contains the options need for an Http request\n     * @param httpMethod The HTTP method that overrides the PluginCall HTTP method\n     * @throws IOException throws an IO request when a connection can't be made\n     * @throws URISyntaxException thrown when the URI is malformed\n     * @throws JSONException thrown when the incoming JSON is malformed\n     */\n    public static JSObject request(PluginCall call, String httpMethod, Bridge bridge)\n        throws IOException, URISyntaxException, JSONException {\n        String urlString = call.getString(\"url\", \"\");\n        JSObject headers = call.getObject(\"headers\", new JSObject());\n        JSObject params = call.getObject(\"params\", new JSObject());\n        Integer connectTimeout = call.getInt(\"connectTimeout\");\n        Integer readTimeout = call.getInt(\"readTimeout\");\n        Boolean disableRedirects = call.getBoolean(\"disableRedirects\");\n        Boolean shouldEncode = call.getBoolean(\"shouldEncodeUrlParams\", true);\n        ResponseType responseType = ResponseType.parse(call.getString(\"responseType\"));\n        String dataType = call.getString(\"dataType\");\n\n        String method = httpMethod != null ? httpMethod.toUpperCase(Locale.ROOT) : call.getString(\"method\", \"GET\").toUpperCase(Locale.ROOT);\n\n        boolean isHttpMutate = method.equals(\"DELETE\") || method.equals(\"PATCH\") || method.equals(\"POST\") || method.equals(\"PUT\");\n\n        // a workaround for the following android web view issue:\n        // https://issues.chromium.org/issues/40450316\n        // x-cap-user-agent contains the user agent set in JavaScript\n        String userAgentValue = headers.getString(\"x-cap-user-agent\");\n        if (userAgentValue != null) {\n            headers.put(\"User-Agent\", userAgentValue);\n        }\n        headers.remove(\"x-cap-user-agent\");\n\n        if (!headers.has(\"User-Agent\") && !headers.has(\"user-agent\")) {\n            headers.put(\"User-Agent\", bridge.getConfig().getOverriddenUserAgentString());\n        }\n\n        URL url = new URL(urlString);\n        HttpURLConnectionBuilder connectionBuilder = new HttpURLConnectionBuilder()\n            .setUrl(url)\n            .setMethod(method)\n            .setHeaders(headers)\n            .setUrlParams(params, shouldEncode)\n            .setConnectTimeout(connectTimeout)\n            .setReadTimeout(readTimeout)\n            .setDisableRedirects(disableRedirects)\n            .openConnection();\n\n        CapacitorHttpUrlConnection connection = connectionBuilder.build();\n\n        if (null != bridge && !isDomainExcludedFromSSL(bridge, url)) {\n            connection.setSSLSocketFactory(bridge);\n        }\n\n        // Set HTTP body on a non GET or HEAD request\n        if (isHttpMutate) {\n            JSValue data = new JSValue(call, \"data\");\n            if (data.getValue() != null) {\n                connection.setDoOutput(true);\n                connection.setRequestBody(call, data, dataType);\n            }\n        }\n\n        call.getData().put(\"activeCapacitorHttpUrlConnection\", connection);\n        connection.connect();\n\n        JSObject response = buildResponse(connection, responseType);\n\n        connection.disconnect();\n        call.getData().remove(\"activeCapacitorHttpUrlConnection\");\n\n        return response;\n    }\n\n    public static Boolean isDomainExcludedFromSSL(Bridge bridge, URL url) {\n        try {\n            Class<?> sslPinningImpl = Class.forName(\"io.ionic.sslpinning.SSLPinning\");\n            Method method = sslPinningImpl.getDeclaredMethod(\"isDomainExcluded\", Bridge.class, URL.class);\n            return (Boolean) method.invoke(sslPinningImpl.getDeclaredConstructor().newInstance(), bridge, url);\n        } catch (Exception ignored) {\n            return false;\n        }\n    }\n\n    @FunctionalInterface\n    public interface ProgressEmitter {\n        void emit(Integer bytes, Integer contentLength);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java",
    "content": "package com.getcapacitor.plugin.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * This interface was extracted from {@link CapacitorHttpUrlConnection} to enable mocking that class.\n */\npublic interface ICapacitorHttpUrlConnection {\n    InputStream getErrorStream();\n\n    String getHeaderField(String name);\n\n    InputStream getInputStream() throws IOException;\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java",
    "content": "package com.getcapacitor.plugin.util;\n\nenum MimeType {\n    APPLICATION_JSON(\"application/json\"),\n    APPLICATION_VND_API_JSON(\"application/vnd.api+json\"), // https://jsonapi.org\n    TEXT_HTML(\"text/html\");\n\n    private final String value;\n\n    MimeType(String value) {\n        this.value = value;\n    }\n\n    String getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/util/HostMask.java",
    "content": "package com.getcapacitor.util;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic interface HostMask {\n    boolean matches(String host);\n\n    class Parser {\n\n        private static HostMask NOTHING = new Nothing();\n\n        public static HostMask parse(String[] masks) {\n            return masks == null ? NOTHING : HostMask.Any.parse(masks);\n        }\n\n        public static HostMask parse(String mask) {\n            return mask == null ? NOTHING : HostMask.Simple.parse(mask);\n        }\n    }\n\n    class Simple implements HostMask {\n\n        private final List<String> maskParts;\n\n        private Simple(List<String> maskParts) {\n            if (maskParts == null) {\n                throw new IllegalArgumentException(\"Mask parts can not be null\");\n            }\n            this.maskParts = maskParts;\n        }\n\n        static Simple parse(String mask) {\n            List<String> parts = Util.splitAndReverse(mask);\n            return new Simple(parts);\n        }\n\n        @Override\n        public boolean matches(String host) {\n            if (host == null) {\n                return false;\n            }\n            List<String> hostParts = Util.splitAndReverse(host);\n            int hostSize = hostParts.size();\n            int maskSize = maskParts.size();\n            if (maskSize > 1 && hostSize != maskSize) {\n                return false;\n            }\n\n            int minSize = Math.min(hostSize, maskSize);\n\n            for (int i = 0; i < minSize; i++) {\n                String maskPart = maskParts.get(i);\n                String hostPart = hostParts.get(i);\n                if (!Util.matches(maskPart, hostPart)) {\n                    return false;\n                }\n            }\n            return true;\n        }\n    }\n\n    class Any implements HostMask {\n\n        private final List<? extends HostMask> masks;\n\n        Any(List<? extends HostMask> masks) {\n            this.masks = masks;\n        }\n\n        @Override\n        public boolean matches(String host) {\n            for (HostMask mask : masks) {\n                if (mask.matches(host)) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        static Any parse(String... rawMasks) {\n            List<HostMask.Simple> masks = new ArrayList<>();\n            for (String raw : rawMasks) {\n                masks.add(HostMask.Simple.parse(raw));\n            }\n            return new Any(masks);\n        }\n    }\n\n    class Nothing implements HostMask {\n\n        @Override\n        public boolean matches(String host) {\n            return false;\n        }\n    }\n\n    class Util {\n\n        static boolean matches(String mask, String string) {\n            if (mask == null) {\n                return false;\n            } else if (\"*\".equals(mask)) {\n                return true;\n            } else if (string == null) {\n                return false;\n            } else {\n                return mask.toUpperCase().equals(string.toUpperCase());\n            }\n        }\n\n        static List<String> splitAndReverse(String string) {\n            if (string == null) {\n                throw new IllegalArgumentException(\"Can not split null argument\");\n            }\n            List<String> parts = Arrays.asList(string.split(\"\\\\.\"));\n            Collections.reverse(parts);\n            return parts;\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/util/InternalUtils.java",
    "content": "package com.getcapacitor.util;\n\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.os.Build;\n\npublic class InternalUtils {\n\n    public static PackageInfo getPackageInfo(PackageManager pm, String packageName) throws PackageManager.NameNotFoundException {\n        return InternalUtils.getPackageInfo(pm, packageName, 0);\n    }\n\n    public static PackageInfo getPackageInfo(PackageManager pm, String packageName, long flags)\n        throws PackageManager.NameNotFoundException {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            return pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags));\n        } else {\n            return getPackageInfoLegacy(pm, packageName, (int) flags);\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private static PackageInfo getPackageInfoLegacy(PackageManager pm, String packageName, long flags)\n        throws PackageManager.NameNotFoundException {\n        return pm.getPackageInfo(packageName, (int) flags);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.java",
    "content": "package com.getcapacitor.util;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n/**\n * Helper methods for parsing JSON objects.\n */\npublic class JSONUtils {\n\n    /**\n     * Get a string value from the given JSON object.\n     *\n     * @param jsonObject A JSON object to search\n     * @param key A key to fetch from the JSON object\n     * @param defaultValue A default value to return if the key cannot be found\n     * @return The value at the given key in the JSON object, or the default value\n     */\n    public static String getString(JSONObject jsonObject, String key, String defaultValue) {\n        String k = getDeepestKey(key);\n        try {\n            JSONObject o = getDeepestObject(jsonObject, key);\n\n            String value = o.getString(k);\n            if (value == null) {\n                return defaultValue;\n            }\n            return value;\n        } catch (JSONException ignore) {\n            // value was not found\n        }\n\n        return defaultValue;\n    }\n\n    /**\n     * Get a boolean value from the given JSON object.\n     *\n     * @param jsonObject A JSON object to search\n     * @param key A key to fetch from the JSON object\n     * @param defaultValue A default value to return if the key cannot be found\n     * @return The value at the given key in the JSON object, or the default value\n     */\n    public static boolean getBoolean(JSONObject jsonObject, String key, boolean defaultValue) {\n        String k = getDeepestKey(key);\n        try {\n            JSONObject o = getDeepestObject(jsonObject, key);\n\n            return o.getBoolean(k);\n        } catch (JSONException ignore) {\n            // value was not found\n        }\n\n        return defaultValue;\n    }\n\n    /**\n     * Get an int value from the given JSON object.\n     *\n     * @param jsonObject A JSON object to search\n     * @param key A key to fetch from the JSON object\n     * @param defaultValue A default value to return if the key cannot be found\n     * @return The value at the given key in the JSON object, or the default value\n     */\n    public static int getInt(JSONObject jsonObject, String key, int defaultValue) {\n        String k = getDeepestKey(key);\n        try {\n            JSONObject o = getDeepestObject(jsonObject, key);\n            return o.getInt(k);\n        } catch (JSONException ignore) {\n            // value was not found\n        }\n\n        return defaultValue;\n    }\n\n    /**\n     * Get a JSON object value from the given JSON object.\n     *\n     * @param jsonObject A JSON object to search\n     * @param key A key to fetch from the JSON object\n     * @return The value from the config, if exists. Null if not\n     */\n    public static JSONObject getObject(JSONObject jsonObject, String key) {\n        String k = getDeepestKey(key);\n        try {\n            JSONObject o = getDeepestObject(jsonObject, key);\n\n            return o.getJSONObject(k);\n        } catch (JSONException ignore) {\n            // value was not found\n        }\n\n        return null;\n    }\n\n    /**\n     * Get a string array value from the given JSON object.\n     *\n     * @param jsonObject A JSON object to search\n     * @param key A key to fetch from the JSON object\n     * @param defaultValue A default value to return if the key cannot be found\n     * @return The value at the given key in the JSON object, or the default value\n     */\n    public static String[] getArray(JSONObject jsonObject, String key, String[] defaultValue) {\n        String k = getDeepestKey(key);\n        try {\n            JSONObject o = getDeepestObject(jsonObject, key);\n\n            JSONArray a = o.getJSONArray(k);\n            if (a == null) {\n                return defaultValue;\n            }\n\n            int l = a.length();\n            String[] value = new String[l];\n\n            for (int i = 0; i < l; i++) {\n                value[i] = (String) a.get(i);\n            }\n\n            return value;\n        } catch (JSONException ignore) {\n            // value was not found\n        }\n\n        return defaultValue;\n    }\n\n    /**\n     * Given a JSON key path, gets the deepest key.\n     *\n     * @param key The key path\n     * @return The deepest key\n     */\n    private static String getDeepestKey(String key) {\n        String[] parts = key.split(\"\\\\.\");\n        if (parts.length > 0) {\n            return parts[parts.length - 1];\n        }\n\n        return null;\n    }\n\n    /**\n     * Given a JSON object and key path, gets the deepest object in the path.\n     *\n     * @param jsonObject A JSON object\n     * @param key The key path to follow\n     * @return The deepest object along the key path\n     * @throws JSONException Thrown if any JSON errors\n     */\n    private static JSONObject getDeepestObject(JSONObject jsonObject, String key) throws JSONException {\n        String[] parts = key.split(\"\\\\.\");\n        JSONObject o = jsonObject;\n\n        // Search until the second to last part of the key\n        for (int i = 0; i < parts.length - 1; i++) {\n            String k = parts[i];\n            o = o.getJSONObject(k);\n        }\n\n        return o;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/util/PermissionHelper.java",
    "content": "package com.getcapacitor.util;\n\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport androidx.core.app.ActivityCompat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * A helper class for checking permissions.\n *\n * @since 3.0.0\n */\npublic class PermissionHelper {\n\n    /**\n     * Checks if a list of given permissions are all granted by the user\n     *\n     * @since 3.0.0\n     * @param permissions Permissions to check.\n     * @return True if all permissions are granted, false if at least one is not.\n     */\n    public static boolean hasPermissions(Context context, String[] permissions) {\n        for (String perm : permissions) {\n            if (ActivityCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Check whether the given permission has been defined in the AndroidManifest.xml\n     *\n     * @since 3.0.0\n     * @param permission A permission to check.\n     * @return True if the permission has been defined in the Manifest, false if not.\n     */\n    public static boolean hasDefinedPermission(Context context, String permission) {\n        boolean hasPermission = false;\n        String[] requestedPermissions = PermissionHelper.getManifestPermissions(context);\n        if (requestedPermissions != null && requestedPermissions.length > 0) {\n            List<String> requestedPermissionsList = Arrays.asList(requestedPermissions);\n            ArrayList<String> requestedPermissionsArrayList = new ArrayList<>(requestedPermissionsList);\n            if (requestedPermissionsArrayList.contains(permission)) {\n                hasPermission = true;\n            }\n        }\n        return hasPermission;\n    }\n\n    /**\n     * Check whether all of the given permissions have been defined in the AndroidManifest.xml\n     * @param context the app context\n     * @param permissions a list of permissions\n     * @return true only if all permissions are defined in the AndroidManifest.xml\n     */\n    public static boolean hasDefinedPermissions(Context context, String[] permissions) {\n        for (String permission : permissions) {\n            if (!PermissionHelper.hasDefinedPermission(context, permission)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Get the permissions defined in AndroidManifest.xml\n     *\n     * @since 3.0.0\n     * @return The permissions defined in AndroidManifest.xml\n     */\n    public static String[] getManifestPermissions(Context context) {\n        String[] requestedPermissions = null;\n        try {\n            PackageManager pm = context.getPackageManager();\n            PackageInfo packageInfo = InternalUtils.getPackageInfo(pm, context.getPackageName(), PackageManager.GET_PERMISSIONS);\n\n            if (packageInfo != null) {\n                requestedPermissions = packageInfo.requestedPermissions;\n            }\n        } catch (Exception ex) {}\n        return requestedPermissions;\n    }\n\n    /**\n     * Given a list of permissions, return a new list with the ones not present in AndroidManifest.xml\n     *\n     * @since 3.0.0\n     * @param neededPermissions The permissions needed.\n     * @return The permissions not present in AndroidManifest.xml\n     */\n    public static String[] getUndefinedPermissions(Context context, String[] neededPermissions) {\n        ArrayList<String> undefinedPermissions = new ArrayList<>();\n        String[] requestedPermissions = getManifestPermissions(context);\n        if (requestedPermissions != null && requestedPermissions.length > 0) {\n            List<String> requestedPermissionsList = Arrays.asList(requestedPermissions);\n            ArrayList<String> requestedPermissionsArrayList = new ArrayList<>(requestedPermissionsList);\n            for (String permission : neededPermissions) {\n                if (!requestedPermissionsArrayList.contains(permission)) {\n                    undefinedPermissions.add(permission);\n                }\n            }\n            String[] undefinedPermissionArray = new String[undefinedPermissions.size()];\n            undefinedPermissionArray = undefinedPermissions.toArray(undefinedPermissionArray);\n\n            return undefinedPermissionArray;\n        }\n        return neededPermissions;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/java/com/getcapacitor/util/WebColor.java",
    "content": "package com.getcapacitor.util;\n\nimport android.graphics.Color;\n\npublic class WebColor {\n\n    /**\n     * Parse the color string, and return the corresponding color-int. If the string cannot be parsed, throws an IllegalArgumentException exception.\n     * @param colorString The hexadecimal color string. The format is an RGB or RGBA hex string.\n     * @return The corresponding color as an int.\n     */\n    public static int parseColor(String colorString) {\n        String formattedColor = colorString;\n        if (colorString.charAt(0) != '#') {\n            formattedColor = \"#\" + formattedColor;\n        }\n\n        if (formattedColor.length() != 7 && formattedColor.length() != 9) {\n            throw new IllegalArgumentException(\"The encoded color space is invalid or unknown\");\n        } else if (formattedColor.length() == 7) {\n            return Color.parseColor(formattedColor);\n        } else {\n            // Convert to Android format #AARRGGBB from #RRGGBBAA\n            formattedColor = \"#\" + formattedColor.substring(7) + formattedColor.substring(1, 7);\n            return Color.parseColor(formattedColor);\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/main/res/layout/capacitor_bridge_layout_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"com.getcapacitor.BridgeActivity\"\n    >\n\n    <com.getcapacitor.CapacitorWebView\n        android:id=\"@+id/webview\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" />\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "android/capacitor/src/main/res/layout/no_webview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/textView\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_weight=\"1\"\n        android:text=\"@string/no_webview_text\"\n        android:gravity=\"center\"\n        android:textSize=\"48sp\" />\n</LinearLayout>"
  },
  {
    "path": "android/capacitor/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <color tools:ignore=\"UnusedResources\" name=\"colorPrimary\">#3F51B5</color>\n    <color tools:ignore=\"UnusedResources\" name=\"colorPrimaryDark\">#303F9F</color>\n    <color tools:ignore=\"UnusedResources\" name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "android/capacitor/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"no_webview_text\">This app requires a WebView to work</string>\n</resources>\n"
  },
  {
    "path": "android/capacitor/src/main/res/values/styles.xml",
    "content": "<resources>\n    <style name=\"AppTheme.NoActionBar\" parent=\"Theme.AppCompat.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/capacitor/src/test/java/android/util/Log.java",
    "content": "package android.util;\n\npublic class Log {\n\n    public static int d(String tag, String msg) {\n        System.out.println(\"DEBUG: \" + tag + \": \" + msg);\n        return 0;\n    }\n\n    public static int i(String tag, String msg) {\n        System.out.println(\"INFO: \" + tag + \": \" + msg);\n        return 0;\n    }\n\n    public static int w(String tag, String msg) {\n        System.out.println(\"WARN: \" + tag + \": \" + msg);\n        return 0;\n    }\n\n    public static int e(String tag, String msg) {\n        System.out.println(\"ERROR: \" + tag + \": \" + msg);\n        return 0;\n    }\n\n    public static int v(String tag, String msg) {\n        System.out.println(\"VERBOSE: \" + tag + \": \" + msg);\n        return 0;\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java",
    "content": "package com.getcapacitor;\n\nimport static org.junit.Assert.*;\n\nimport android.app.Activity;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mockito;\n\npublic class ConfigBuildingTest {\n\n    final String TEST_PLUGIN_NAME = \"TestPlugin\";\n\n    Activity context = Mockito.mock(Activity.class);\n\n    JSONObject pluginConfig = new JSONObject();\n    JSONObject testPluginObject = new JSONObject();\n    JSONObject testPluginNestedObject = new JSONObject();\n    JSONArray testPluginArray = new JSONArray();\n\n    CapConfig config = null;\n\n    @Before\n    public void setup() {\n        try {\n            testPluginNestedObject.put(\"var10\", true);\n\n            testPluginArray.put(\"5\");\n            testPluginArray.put(\"6\");\n            testPluginArray.put(\"7\");\n            testPluginArray.put(\"8\");\n\n            testPluginObject.put(\"var1\", true);\n            testPluginObject.put(\"var2\", \"hello\");\n            testPluginObject.put(\"var3\", testPluginNestedObject);\n            testPluginObject.put(\"var4\", 2);\n            testPluginObject.put(\"var5\", testPluginArray);\n\n            pluginConfig.put(TEST_PLUGIN_NAME, testPluginObject);\n\n            config = new CapConfig.Builder(context)\n                .setAllowMixedContent(true)\n                .setAllowNavigation(new String[] { \"http://www.google.com\" })\n                .setAndroidScheme(\"test\")\n                .setCaptureInput(true)\n                .setLoggingEnabled(true)\n                .setHTML5mode(false)\n                .setOverriddenUserAgentString(\"test-user-agent\")\n                .setAppendedUserAgentString(\"test-append\")\n                .setWebContentsDebuggingEnabled(true)\n                .setZoomableWebView(false)\n                .setBackgroundColor(\"red\")\n                .setPluginsConfiguration(pluginConfig)\n                .setServerUrl(\"http://www.google.com\")\n                .setResolveServiceWorkerRequests(false)\n                .create();\n        } catch (Exception e) {\n            fail();\n        }\n    }\n\n    @Test\n    public void getCoreConfigValues() {\n        assertTrue(config.isMixedContentAllowed());\n        assertArrayEquals(new String[] { \"http://www.google.com\" }, config.getAllowNavigation());\n        assertEquals(\"test\", config.getAndroidScheme());\n        assertTrue(config.isInputCaptured());\n        assertTrue(config.isLoggingEnabled());\n        assertFalse(config.isHTML5Mode());\n        assertEquals(\"test-user-agent\", config.getOverriddenUserAgentString());\n        assertEquals(\"test-append\", config.getAppendedUserAgentString());\n        assertTrue(config.isWebContentsDebuggingEnabled());\n        assertEquals(\"red\", config.getBackgroundColor());\n        assertEquals(\"http://www.google.com\", config.getServerUrl());\n        assertFalse(config.isResolveServiceWorkerRequests());\n    }\n\n    @Test\n    public void getPluginString() {\n        String testString = config.getPluginConfiguration(TEST_PLUGIN_NAME).getString(\"var2\");\n        assertEquals(\"hello\", testString);\n    }\n\n    @Test\n    public void getPluginBoolean() {\n        boolean testBool = config.getPluginConfiguration(TEST_PLUGIN_NAME).getBoolean(\"var1\", false);\n        assertTrue(testBool);\n    }\n\n    @Test\n    public void getPluginInt() {\n        int testInt = config.getPluginConfiguration(TEST_PLUGIN_NAME).getInt(\"var4\", -1);\n        assertEquals(2, testInt);\n    }\n\n    @Test\n    public void getPluginArray() {\n        String[] comparison = new String[] { \"5\", \"6\", \"7\", \"8\" };\n        String[] testArray = config.getPluginConfiguration(TEST_PLUGIN_NAME).getArray(\"var5\");\n        assertArrayEquals(comparison, testArray);\n    }\n\n    @Test\n    public void getPluginObject() {\n        JSONObject testObject = config.getPluginConfiguration(TEST_PLUGIN_NAME).getObject(\"var3\");\n        assertEquals(testPluginNestedObject, testObject);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/java/com/getcapacitor/ConfigReadingTest.java",
    "content": "package com.getcapacitor;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\nimport static org.mockito.Mockito.*;\n\nimport android.app.Activity;\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.AssetManager;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\npublic class ConfigReadingTest {\n\n    private static final String FLAT_TEST = \"configs/flat.json\";\n    private static final String BAD_TEST = \"configs/bad.json\";\n    private static final String HIERARCHY_TEST = \"configs/hierarchy.json\";\n    private static final String NONJSON_TEST = \"configs/nonjson.json\";\n    private static final String SERVER_TEST = \"configs/server.json\";\n\n    Activity context = Mockito.mock(Activity.class);\n    AssetManager assetManager = Mockito.mock(AssetManager.class);\n    ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);\n\n    private InputStream getTestInputStream(String testPath) {\n        return this.getClass().getClassLoader().getResourceAsStream(testPath);\n    }\n\n    @Before\n    public void before() {\n        when(context.getAssets()).thenReturn(assetManager);\n        when(context.getApplicationInfo()).thenReturn(applicationInfo);\n    }\n\n    @Test\n    public void bad() {\n        try {\n            when(assetManager.open(\"capacitor.config.json\")).thenReturn(getTestInputStream(BAD_TEST));\n\n            CapConfig config = CapConfig.loadDefault(context);\n            assertEquals(\"not a real domain\", config.getServerUrl());\n            assertNull(config.getBackgroundColor());\n            assertFalse(config.isLoggingEnabled());\n        } catch (IOException e) {\n            fail();\n        }\n    }\n\n    @Test\n    public void flat() {\n        try {\n            when(assetManager.open(\"capacitor.config.json\")).thenReturn(getTestInputStream(FLAT_TEST));\n\n            CapConfig config = CapConfig.loadDefault(context);\n            assertEquals(\"level 1 override\", config.getOverriddenUserAgentString());\n            assertEquals(\"level 1 append\", config.getAppendedUserAgentString());\n            assertEquals(\"#ffffff\", config.getBackgroundColor());\n            assertFalse(config.isLoggingEnabled());\n            assertEquals(1, config.getPluginConfiguration(\"SplashScreen\").getInt(\"launchShowDuration\", 0));\n        } catch (IOException e) {\n            fail();\n        }\n    }\n\n    @Test\n    public void hierarchy() {\n        try {\n            when(assetManager.open(\"capacitor.config.json\")).thenReturn(getTestInputStream(HIERARCHY_TEST));\n\n            CapConfig config = CapConfig.loadDefault(context);\n            assertEquals(\"level 2 override\", config.getOverriddenUserAgentString());\n            assertEquals(\"level 2 append\", config.getAppendedUserAgentString());\n            assertEquals(\"#000000\", config.getBackgroundColor());\n            assertFalse(config.isLoggingEnabled());\n        } catch (IOException e) {\n            fail();\n        }\n    }\n\n    @Test\n    public void nonJSON() {\n        try {\n            final String errText = \"Unable to parse capacitor.config.json. Make sure it's valid json\";\n\n            when(assetManager.open(\"capacitor.config.json\")).thenReturn(getTestInputStream(NONJSON_TEST));\n\n            try (MockedStatic<Logger> logger = mockStatic(Logger.class)) {\n                CapConfig config = CapConfig.loadDefault(context);\n                logger.verify(() -> Logger.error(eq(errText), any()), times(1));\n            }\n        } catch (IOException e) {\n            fail();\n        }\n    }\n\n    @Test\n    public void server() {\n        try {\n            when(assetManager.open(\"capacitor.config.json\")).thenReturn(getTestInputStream(SERVER_TEST));\n\n            CapConfig config = CapConfig.loadDefault(context);\n            assertEquals(\"myhost\", config.getHostname());\n            assertEquals(\"http://192.168.100.1:2057\", config.getServerUrl());\n            assertEquals(\"override\", config.getAndroidScheme());\n        } catch (IOException e) {\n            fail();\n        }\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/java/com/getcapacitor/ExampleUnitTest.java",
    "content": "package com.getcapacitor;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/java/com/getcapacitor/JSObjectTest.java",
    "content": "package com.getcapacitor;\n\nimport static org.junit.Assert.*;\n\nimport org.json.JSONException;\nimport org.junit.Test;\n\npublic class JSObjectTest {\n\n    @Test\n    public void getStringReturnsNull_WhenJSObject_IsConstructed_WithNoInitialJSONObject() {\n        JSObject jsObject = new JSObject();\n\n        String actualValue = jsObject.getString(\"should be null\");\n\n        assertNull(actualValue);\n    }\n\n    @Test\n    public void getStringReturnsExpectedValue_WhenJSObject_IsConstructed_WithAValidJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject(\"{\\\"thisKeyExists\\\": \\\"this is the key value\\\"}\");\n\n        String expectedValue = jsObject.getString(\"thisKeyExists\");\n        String actualValue = \"this is the key value\";\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getStringReturnsDefaultValue_WhenJSObject_IsConstructed_WithNoInitialJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject();\n\n        String expectedValue = jsObject.getString(\"thisKeyDoesNotExist\", \"default value\");\n        String actualValue = \"default value\";\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getStringReturnsDefaultValue_WhenJSObject_IsConstructed_WithAValueAsInteger() throws JSONException {\n        JSObject jsObject = new JSObject(\"{\\\"thisKeyExists\\\": 1}\");\n\n        String expectedValue = jsObject.getString(\"thisKeyExists\", \"default value\");\n        String actualValue = \"default value\";\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getIntegerReturnsNull_WhenJSObject_IsConstructed_WithNoInitialJSONObject() {\n        JSObject jsObject = new JSObject();\n\n        Integer actualValue = jsObject.getInteger(\"should be null\");\n\n        assertNull(actualValue);\n    }\n\n    @Test\n    public void getIntegerReturnsExpectedValue_WhenJSObject_IsConstructed_WithAValidJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject(\"{\\\"thisKeyExists\\\": 1}\");\n\n        Integer expectedValue = jsObject.getInteger(\"thisKeyExists\");\n        Integer actualValue = 1;\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getIntegerReturnsDefaultValue_WhenJSObject_IsConstructed_WithNoInitialJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject();\n\n        Integer expectedValue = jsObject.getInteger(\"thisKeyDoesNotExist\", 1);\n        Integer actualValue = 1;\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getStringReturnsDefaultValue_WhenJSObject_IsConstructed_WithAValueAsString() throws JSONException {\n        JSObject jsObject = new JSObject(\"{\\\"thisKeyExists\\\": \\\"not an integer\\\"}\");\n\n        Integer expectedValue = jsObject.getInteger(\"thisKeyExists\", 1);\n        Integer actualValue = 1;\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getBoolReturnsNull_WhenJSObject_IsConstructed_WithNoInitialJSONObject() {\n        JSObject jsObject = new JSObject();\n\n        Boolean actualValue = jsObject.getBool(\"should be null\");\n\n        assertNull(actualValue);\n    }\n\n    @Test\n    public void getBoolReturnsExpectedValue_WhenJSObject_IsConstructed_WithAValidJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject(\"{\\\"thisKeyExists\\\": true}\");\n\n        Boolean expectedValue = jsObject.getBool(\"thisKeyExists\");\n        Boolean actualValue = true;\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getBooleanReturnsDefaultValue_WhenJSObject_IsConstructed_WithNoInitialJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject();\n\n        Boolean expectedValue = jsObject.getBoolean(\"thisKeyDoesNotExist\", true);\n        Boolean actualValue = true;\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getBooleanReturnsDefaultValue_WhenJSObject_IsConstructed_WithAValueAsString() throws JSONException {\n        JSObject jsObject = new JSObject(\"{\\\"thisKeyExists\\\": \\\"not an integer\\\"}\");\n\n        Boolean expectedValue = jsObject.getBoolean(\"thisKeyExists\", true);\n        Boolean actualValue = true;\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getJSObjectReturnsNull_WhenJSObject_IsConstructed_WithNoInitialJSONObject() {\n        JSObject jsObject = new JSObject();\n\n        JSObject actualValue = jsObject.getJSObject(\"should be null\");\n\n        assertNull(actualValue);\n    }\n\n    @Test\n    public void getJsObjectReturnsExpectedValue_WhenJSObject_IsConstructed_WithAValidJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject(\"{\\\"thisKeyExists\\\": { \\\"innerObjectKey\\\": \\\"innerObjectValue\\\" }}\");\n\n        String actualValue = jsObject.getJSObject(\"thisKeyExists\").getString(\"innerObjectKey\");\n        String expectedValue = \"innerObjectValue\";\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void getJSObjectReturnsDefaultValue_WhenJSObject_IsConstructed_WithNoInitialJSONObject() throws JSONException {\n        JSObject jsObject = new JSObject();\n\n        String actualValue = jsObject\n            .getJSObject(\"thisKeyExists\", new JSObject(\"{\\\"thisKeyExists\\\": \\\"default string\\\"}\"))\n            .getString(\"thisKeyExists\");\n        String expectedValue = \"default string\";\n\n        assertEquals(expectedValue, actualValue);\n    }\n\n    @Test\n    public void putBoolean_AddsValueToJSObject_UnderCorrectKey() {\n        JSObject jsObject = new JSObject();\n        jsObject.put(\"bool\", true);\n\n        Boolean actualValue = jsObject.getBool(\"bool\");\n\n        assertTrue(actualValue);\n    }\n\n    @Test\n    public void putInteger_AddsValueToJSObject_UnderCorrectKey() {\n        JSObject jsObject = new JSObject();\n        jsObject.put(\"integer\", 1);\n\n        Integer expectedValue = 1;\n        Integer actualValue = jsObject.getInteger(\"integer\");\n\n        assertEquals(actualValue, expectedValue);\n    }\n\n    @Test\n    public void putLong_AddsValueToJSObject_UnderCorrectKey() throws JSONException {\n        JSObject jsObject = new JSObject();\n        jsObject.put(\"long\", 1l);\n\n        Long expectedValue = 1l;\n        Long actualValue = jsObject.getLong(\"long\");\n\n        assertEquals(actualValue, expectedValue);\n    }\n\n    @Test\n    public void putDouble_AddsValueToJSObject_UnderCorrectKey() throws JSONException {\n        JSObject jsObject = new JSObject();\n        jsObject.put(\"double\", 1d);\n\n        Double expectedValue = 1d;\n        Double actualValue = jsObject.getDouble(\"double\");\n\n        assertEquals(actualValue, expectedValue);\n    }\n\n    @Test\n    public void putString_AddsValueToJSObject_UnderCorrectKey() throws JSONException {\n        JSObject jsObject = new JSObject();\n        jsObject.put(\"string\", \"test\");\n\n        String expectedValue = \"test\";\n        String actualValue = jsObject.getString(\"string\");\n\n        assertEquals(actualValue, expectedValue);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/java/com/getcapacitor/PluginMethodHandleTest.java",
    "content": "package com.getcapacitor;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.lang.reflect.Method;\nimport org.junit.Test;\n\npublic class PluginMethodHandleTest {\n\n    @Test\n    public void getNameReturnsMethodName() {\n        PluginMethod pluginMethod = mock(PluginMethod.class);\n        Method mockMethod = mock(Method.class);\n\n        given(mockMethod.getName()).willReturn(\"methodName\");\n        PluginMethodHandle pluginMethodHandle = new PluginMethodHandle(mockMethod, pluginMethod);\n\n        assertEquals(pluginMethodHandle.getName(), \"methodName\");\n    }\n\n    @Test\n    public void getMethodHandleReturnsMethodHandle() {\n        PluginMethod pluginMethod = mock(PluginMethod.class);\n        Method mockMethod = mock(Method.class);\n\n        given(pluginMethod.returnType()).willReturn(\"returnType\");\n        PluginMethodHandle pluginMethodHandle = new PluginMethodHandle(mockMethod, pluginMethod);\n\n        assertEquals(pluginMethodHandle.getReturnType(), \"returnType\");\n    }\n\n    @Test\n    public void getMethodReturnsMethod() {\n        PluginMethod pluginMethod = mock(PluginMethod.class);\n        Method mockMethod = mock(Method.class);\n\n        PluginMethodHandle pluginMethodHandle = new PluginMethodHandle(mockMethod, pluginMethod);\n\n        assertEquals(pluginMethodHandle.getMethod(), mockMethod);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/java/com/getcapacitor/plugin/util/HttpRequestHandlerTest.java",
    "content": "package com.getcapacitor.plugin.util;\n\nimport static org.junit.Assert.*;\n\nimport com.getcapacitor.JSObject;\nimport com.getcapacitor.plugin.util.HttpRequestHandler.HttpURLConnectionBuilder;\nimport java.net.URL;\nimport org.junit.Test;\n\npublic class HttpRequestHandlerTest {\n\n    static final String BASE_URL = \"https://httpbin.org/get\";\n    static final String PARAMS_JSON = \"\"\"\n        {\"k\": \"a&b\"}\n        \"\"\";\n\n    @Test\n    public void testHttpURLConnectionBuilderSetUrlParamsEncoded() throws Exception {\n        String expectedQuery = \"k=a%26b\";\n        String expectedUrl = BASE_URL + \"?\" + expectedQuery;\n        String actualUrl = new HttpURLConnectionBuilder()\n            .setUrl(new URL(BASE_URL))\n            .setUrlParams(new JSObject(PARAMS_JSON), true)\n            .url.toString();\n        assertEquals(expectedUrl, actualUrl);\n    }\n\n    @Test\n    public void testHttpURLConnectionBuilderSetUrlParamsNotEncoded() throws Exception {\n        String expectedQuery = \"k=a&b\";\n        String expectedUrl = BASE_URL + \"?\" + expectedQuery;\n        String actualUrl = new HttpURLConnectionBuilder()\n            .setUrl(new URL(BASE_URL))\n            .setUrlParams(new JSObject(PARAMS_JSON), false)\n            .url.toString();\n        assertEquals(expectedUrl, actualUrl);\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/java/com/getcapacitor/util/HostMaskTest.java",
    "content": "package com.getcapacitor.util;\n\nimport static org.junit.Assert.*;\n\nimport com.getcapacitor.util.HostMask.Util;\nimport org.junit.Test;\n\npublic class HostMaskTest {\n\n    @Test\n    public void testParser() {\n        assertEquals(HostMask.Any.class, HostMask.Parser.parse(\"*,example.org,*.example.org\".split(\",\")).getClass());\n        assertEquals(HostMask.Simple.class, HostMask.Parser.parse(\"*\").getClass());\n        assertEquals(HostMask.Nothing.class, HostMask.Parser.parse((String) null).getClass());\n    }\n\n    @Test\n    public void testAny() {\n        HostMask mask = HostMask.Any.parse(\"*.example.org\", \"example.org\");\n        assertFalse(mask.matches(\"org\"));\n        assertTrue(mask.matches(\"example.org\"));\n        assertTrue(mask.matches(\"www.example.org\"));\n        assertFalse(mask.matches(\"imap.mail.example.org\"));\n        assertFalse(mask.matches(\"another.org\"));\n        assertFalse(mask.matches(\"www.another.org\"));\n        assertFalse(mask.matches(null));\n    }\n\n    @Test\n    public void testAnyWildcard() {\n        HostMask mask = HostMask.Any.parse(\"*\");\n        assertTrue(mask.matches(\"org\"));\n        assertTrue(mask.matches(\"example.org\"));\n        assertTrue(mask.matches(\"www.example.org\"));\n        assertTrue(mask.matches(\"imap.mail.example.org\"));\n        assertTrue(mask.matches(\"another.org\"));\n        assertTrue(mask.matches(\"www.another.org\"));\n        assertFalse(mask.matches(null));\n    }\n\n    @Test\n    public void testSimple() {\n        HostMask mask = HostMask.Simple.parse(\"*.org\");\n        assertTrue(mask.matches(\"example.org\"));\n        assertFalse(mask.matches(\"org\"));\n        assertFalse(mask.matches(\"www.example.org\"));\n        assertFalse(\"Null host never matches\", mask.matches(null));\n    }\n\n    @Test\n    public void testSimpleExample1() {\n        HostMask mask = HostMask.Simple.parse(\"*.example.org\");\n        assertFalse(\"Null host never matches\", mask.matches(\"example.org\"));\n    }\n\n    @Test\n    public void testSimpleExample2() {\n        HostMask mask = HostMask.Simple.parse(\"*\");\n        assertTrue(\"Single star matches everything\", mask.matches(\"example.org\"));\n    }\n\n    @Test\n    public void test192168ForLocalTestingSakes() {\n        HostMask mask = HostMask.Simple.parse(\"192.168.*.*\");\n        assertTrue(\"Matches 192.168.*.*\", mask.matches(\"192.168.2.5\"));\n        assertFalse(\"Matches NOT 192.168.*.*\", mask.matches(\"192.66.2.5\"));\n    }\n\n    @Test\n    public void testUtil() {\n        assertTrue(\"Everything matches *\", Util.matches(\"*\", \"*\"));\n        assertTrue(\"Everything matches *\", Util.matches(\"*\", \"org\"));\n        assertTrue(Util.matches(\"org\", \"org\"));\n        assertTrue(\"Match is case insensitive\", Util.matches(\"ORG\", \"org\"));\n        assertFalse(\"Nothing matches null mask\", Util.matches(null, \"org\"));\n        assertFalse(\"Nothing matches null mask\", Util.matches(null, null));\n    }\n}\n"
  },
  {
    "path": "android/capacitor/src/test/resources/configs/bad.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": 0,\n  \"ios\": {\n    \"allowsLinkPreview\": false,\n    \"scrollEnabled\": false,\n    \"contentInset\": \"what's an axis?\"\n  },\n  \"server\": {\n    \"iosScheme\": \"http\",\n    \"allowNavigation\": [\"capacitorjs.com\", \"ionic.io\", \"192.168.0.1\"],\n    \"hostname\": \"myhost\",\n    \"url\": \"not a real domain\"\n  },\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 0\n    }\n  },\n  \"cordova\": {}\n}"
  },
  {
    "path": "android/capacitor/src/test/resources/configs/flat.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"#ffffff\",\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 1\n    }\n  },\n  \"cordova\": {}\n}"
  },
  {
    "path": "android/capacitor/src/test/resources/configs/hierarchy.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"#ffffff\",\n  \"android\": {\n    \"overrideUserAgent\": \"level 2 override\",\n    \"appendUserAgent\": \"level 2 append\",\n    \"backgroundColor\": \"#000000\",\n    \"allowsLinkPreview\": false,\n    \"scrollEnabled\": false,\n    \"contentInset\": \"scrollableAxes\"\n  },\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 0\n    }\n  },\n  \"cordova\": {}\n}"
  },
  {
    "path": "android/capacitor/src/test/resources/configs/nonjson.json",
    "content": "This is not even JSON."
  },
  {
    "path": "android/capacitor/src/test/resources/configs/server.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"#ffffff\",\n  \"server\": {\n    \"androidScheme\": \"override\",\n    \"allowNavigation\": [\"capacitorjs.com\", \"ionic.io\", \"192.168.0.1\"],\n    \"hostname\": \"myhost\",\n    \"url\": \"http://192.168.100.1:2057\"\n  },\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 0\n    }\n  },\n  \"cordova\": {}\n}"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.3-all.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n"
  },
  {
    "path": "android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "android/package.json",
    "content": "{\n  \"name\": \"@capacitor/android\",\n  \"version\": \"8.2.0\",\n  \"description\": \"Capacitor: Cross-platform apps with JavaScript and the web\",\n  \"homepage\": \"https://capacitorjs.com\",\n  \"author\": \"Ionic Team <hi@ionic.io> (https://ionic.io)\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/ionic-team/capacitor.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/ionic-team/capacitor/issues\"\n  },\n  \"files\": [\n    \"capacitor/build.gradle\",\n    \"capacitor/lint-baseline.xml\",\n    \"capacitor/lint.xml\",\n    \"capacitor/proguard-rules.pro\",\n    \"capacitor/src/main/\"\n  ],\n  \"scripts\": {\n    \"verify\": \"./gradlew clean lint build test -b capacitor/build.gradle\"\n  },\n  \"peerDependencies\": {\n    \"@capacitor/core\": \"^8.2.0\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  }\n}\n"
  },
  {
    "path": "android/scripts/publish-module.gradle",
    "content": "apply plugin: 'maven-publish'\napply plugin: 'signing'\n\ndef LIB_VERSION = System.getenv('CAP_VERSION')\n\ntask androidSourcesJar(type: Jar) {\n    dependsOn(':bundleReleaseAar')\n    archiveClassifier.set('sources')\n    if (project.plugins.findPlugin(\"com.android.library\")) {\n        from android.sourceSets.main.java.srcDirs\n    } else {\n        from sourceSets.main.java.srcDirs\n    }\n}\n\nartifacts {\n    archives androidSourcesJar\n}\n\ngroup = 'com.capacitorjs'\nversion = LIB_VERSION\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                // Coordinates\n                groupId 'com.capacitorjs'\n                artifactId 'core'\n                version LIB_VERSION\n\n                // Two artifacts, the `aar` (or `jar`) and the sources\n                if (project.plugins.findPlugin(\"com.android.library\")) {\n                    from components.release\n                } else {\n                    artifact(\"$buildDir/libs/${project.getName()}-${version}.jar\")\n                }\n\n                artifact androidSourcesJar\n\n                // POM Data\n                pom {\n                    name = 'core'\n                    description = 'Capacitor Android Core Native Library'\n                    url = 'https://github.com/ionic-team/capacitor'\n                    licenses {\n                        license {\n                            name = 'MIT'\n                            url = 'https://github.com/ionic-team/capacitor/blob/main/LICENSE'\n                        }\n                    }\n                    developers {\n                        developer {\n                            name = 'Ionic'\n                            email = 'hi@ionic.io'\n                        }\n                    }\n\n                    // Version Control Info\n                    scm {\n                        connection = 'scm:git:github.com:ionic-team/capacitor.git'\n                        developerConnection = 'scm:git:ssh://github.com:ionic-team/capacitor.git'\n                        url = 'https://github.com/ionic-team/capacitor/tree/main'\n                    }\n                }\n            }\n        }\n    }\n}\n\nsigning {\n    useInMemoryPgpKeys(\n            rootProject.ext[\"signing.keyId\"],\n            rootProject.ext[\"signing.key\"],\n            rootProject.ext[\"signing.password\"],\n    )\n    sign publishing.publications\n}\n"
  },
  {
    "path": "android/scripts/publish-root.gradle",
    "content": "// Create variables with empty default values\next[\"signing.keyId\"] = ''\next[\"signing.key\"] = ''\next[\"signing.password\"] = ''\next[\"ossrhUsername\"] = ''\next[\"ossrhPassword\"] = ''\next[\"sonatypeStagingProfileId\"] = ''\n\nFile secretPropsFile = file('../local.properties')\nif (secretPropsFile.exists()) {\n    // Read local.properties file first if it exists\n    Properties p = new Properties()\n    new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }\n    p.each { name, value -> ext[name] = value }\n} else {\n    // Use system environment variables\n    ext[\"ossrhUsername\"] = System.getenv('ANDROID_OSSRH_USERNAME')\n    ext[\"ossrhPassword\"] = System.getenv('ANDROID_OSSRH_PASSWORD')\n    ext[\"sonatypeStagingProfileId\"] = System.getenv('ANDROID_SONATYPE_STAGING_PROFILE_ID')\n    ext[\"signing.keyId\"] = System.getenv('ANDROID_SIGNING_KEY_ID')\n    ext[\"signing.key\"] = System.getenv('ANDROID_SIGNING_KEY')\n    ext[\"signing.password\"] = System.getenv('ANDROID_SIGNING_PASSWORD')\n}\n\n// Set up Sonatype repository\nnexusPublishing {\n    repositories {\n        sonatype {\n            stagingProfileId = sonatypeStagingProfileId\n            username = ossrhUsername\n            password = ossrhPassword\n            nexusUrl.set(uri(\"https://ossrh-staging-api.central.sonatype.com/service/local/\"))\n            snapshotRepositoryUrl.set(uri(\"https://central.sonatype.com/repository/maven-snapshots/\"))\n        }\n    }\n    repositoryDescription = 'Capacitor Android Core v' + System.getenv('CAP_VERSION')\n}\n"
  },
  {
    "path": "android/settings.gradle",
    "content": ""
  },
  {
    "path": "android-template/.gitignore",
    "content": "# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore\n\n# Built application files\n*.apk\n*.aar\n*.ap_\n*.aab\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n#  Uncomment the following line in case you need and you don't have the release build type files in your app\n# release/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Android Studio Navigation editor temp files\n.navigation/\n\n# Android Studio captures folder\ncaptures/\n\n# IntelliJ\n*.iml\n.idea/workspace.xml\n.idea/tasks.xml\n.idea/gradle.xml\n.idea/assetWizardSettings.xml\n.idea/dictionaries\n.idea/libraries\n# Android Studio 3 in .gitignore file.\n.idea/caches\n.idea/modules.xml\n# Comment next line if keeping position of elements in Navigation Editor is relevant for you\n.idea/navEditor.xml\n\n# Keystore files\n# Uncomment the following lines if you do not want to check your keystore files in.\n#*.jks\n#*.keystore\n\n# External native build folder generated in Android Studio 2.2 and later\n.externalNativeBuild\n.cxx/\n\n# Google Services (e.g. APIs or Firebase)\n# google-services.json\n\n# Freeline\nfreeline.py\nfreeline/\nfreeline_project_description.json\n\n# fastlane\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots\nfastlane/test_output\nfastlane/readme.md\n\n# Version control\nvcs.xml\n\n# lint\nlint/intermediates/\nlint/generated/\nlint/outputs/\nlint/tmp/\n# lint/reports/\n\n# Android Profiling\n*.hprof\n\n# Cordova plugins for Capacitor\ncapacitor-cordova-android-plugins\n\n# Copied web assets\napp/src/main/assets/public\n\n# Generated Config files\napp/src/main/assets/capacitor.config.json\napp/src/main/assets/capacitor.plugins.json\napp/src/main/res/xml/config.xml\n"
  },
  {
    "path": "android-template/app/.gitignore",
    "content": "/build/*\n!/build/.npmkeep\n"
  },
  {
    "path": "android-template/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    namespace = \"com.getcapacitor.myapp\"\n    compileSdk = rootProject.ext.compileSdkVersion\n    defaultConfig {\n        applicationId \"com.getcapacitor.app\"\n        minSdkVersion rootProject.ext.minSdkVersion\n        targetSdkVersion rootProject.ext.targetSdkVersion\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        aaptOptions {\n             // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.\n             // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61\n            ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'\n        }\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\nrepositories {\n    flatDir{\n        dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation \"androidx.appcompat:appcompat:$androidxAppCompatVersion\"\n    implementation \"androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion\"\n    implementation \"androidx.core:core-splashscreen:$coreSplashScreenVersion\"\n    implementation project(':capacitor-android')\n    testImplementation \"junit:junit:$junitVersion\"\n    androidTestImplementation \"androidx.test.ext:junit:$androidxJunitVersion\"\n    androidTestImplementation \"androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion\"\n    implementation project(':capacitor-cordova-android-plugins')\n}\n\napply from: 'capacitor.build.gradle'\n\ntry {\n    def servicesJSON = file('google-services.json')\n    if (servicesJSON.text) {\n        apply plugin: 'com.google.gms.google-services'\n    }\n} catch(Exception e) {\n    logger.info(\"google-services.json not found, google-services plugin not applied. Push Notifications won't work\")\n}\n"
  },
  {
    "path": "android-template/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "android-template/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java",
    "content": "package com.getcapacitor.myapp;\n\nimport static org.junit.Assert.*;\n\nimport android.content.Context;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();\n\n        assertEquals(\"com.getcapacitor.app\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "android-template/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density\"\n            android:name=\".MainActivity\"\n            android:label=\"@string/title_activity_main\"\n            android:theme=\"@style/AppTheme.NoActionBarLaunch\"\n            android:launchMode=\"singleTask\"\n            android:exported=\"true\">\n\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n        </activity>\n\n        <provider\n            android:name=\"androidx.core.content.FileProvider\"\n            android:authorities=\"${applicationId}.fileprovider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/file_paths\"></meta-data>\n        </provider>\n    </application>\n\n    <!-- Permissions -->\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n</manifest>\n"
  },
  {
    "path": "android-template/app/src/main/java/com/getcapacitor/myapp/MainActivity.java",
    "content": "package com.getcapacitor.myapp;\n\nimport com.getcapacitor.BridgeActivity;\n\npublic class MainActivity extends BridgeActivity {}\n"
  },
  {
    "path": "android-template/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "android-template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\" />\n</vector>\n"
  },
  {
    "path": "android-template/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n    <WebView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "android-template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "android-template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "android-template/app/src/main/res/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n</resources>"
  },
  {
    "path": "android-template/app/src/main/res/values/strings.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<resources>\n    <string name=\"app_name\">My App</string>\n    <string name=\"title_activity_main\">My App</string>\n    <string name=\"package_name\">com.getcapacitor.myapp</string>\n    <string name=\"custom_url_scheme\">com.getcapacitor.myapp</string>\n</resources>\n"
  },
  {
    "path": "android-template/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n    <style name=\"AppTheme.NoActionBar\" parent=\"Theme.AppCompat.DayNight.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:background\">@null</item>\n    </style>\n\n\n    <style name=\"AppTheme.NoActionBarLaunch\" parent=\"Theme.SplashScreen\">\n        <item name=\"android:background\">@drawable/splash</item>\n    </style>\n</resources>"
  },
  {
    "path": "android-template/app/src/main/res/xml/file_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <external-path name=\"my_images\" path=\".\" />\n    <cache-path name=\"my_cache_images\" path=\".\" />\n</paths>"
  },
  {
    "path": "android-template/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java",
    "content": "package com.getcapacitor.myapp;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}\n"
  },
  {
    "path": "android-template/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    \n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.13.0'\n        classpath 'com.google.gms:google-services:4.4.4'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\napply from: \"variables.gradle\"\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "android-template/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.3-all.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "android-template/gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n"
  },
  {
    "path": "android-template/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "android-template/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "android-template/settings.gradle",
    "content": "include ':app'\ninclude ':capacitor-cordova-android-plugins'\nproject(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')\n\napply from: 'capacitor.settings.gradle'"
  },
  {
    "path": "android-template/variables.gradle",
    "content": "ext {\n    minSdkVersion = 24\n    compileSdkVersion = 36\n    targetSdkVersion = 36\n    androidxActivityVersion = '1.11.0'\n    androidxAppCompatVersion = '1.7.1'\n    androidxCoordinatorLayoutVersion = '1.3.0'\n    androidxCoreVersion = '1.17.0'\n    androidxFragmentVersion = '1.8.9'\n    coreSplashScreenVersion = '1.2.0'\n    androidxWebkitVersion = '1.14.0'\n    junitVersion = '4.13.2'\n    androidxJunitVersion = '1.3.0'\n    androidxEspressoCoreVersion = '3.7.0'\n    cordovaAndroidVersion = '14.0.1'\n}"
  },
  {
    "path": "capacitor-cordova-android-plugins/build.gradle",
    "content": "ext {\n    androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'\n    cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '14.0.1'\n}\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.13.0'\n    }\n}\n\napply plugin: 'com.android.library'\n\nandroid {\n    namespace = \"capacitor.cordova.android.plugins\"\n    compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36\n    defaultConfig {\n        minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24\n        targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36\n        versionCode 1\n        versionName \"1.0\"\n    }\n    lintOptions {\n        abortOnError = false\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_21\n        targetCompatibility JavaVersion.VERSION_21\n    }\n}\n\nrepositories {\n    google()\n    mavenCentral()\n    flatDir{\n        dirs 'src/main/libs', 'libs'\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'src/main/libs', include: ['*.jar'])\n    implementation \"androidx.appcompat:appcompat:$androidxAppCompatVersion\"\n    implementation \"org.apache.cordova:framework:$cordovaAndroidVersion\"\n    // SUB-PROJECT DEPENDENCIES START\n    \n    // SUB-PROJECT DEPENDENCIES END\n}\n\n// PLUGIN GRADLE EXTENSIONS START\n\n// PLUGIN GRADLE EXTENSIONS END\n\nfor (def func : cdvPluginPostBuildExtras) {\n    func()\n}"
  },
  {
    "path": "capacitor-cordova-android-plugins/src/main/AndroidManifest.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<manifest xmlns:android='http://schemas.android.com/apk/res/android'>\n</manifest>"
  },
  {
    "path": "capacitor-cordova-android-plugins/src/main/java/.gitkeep",
    "content": ""
  },
  {
    "path": "capacitor-cordova-android-plugins/src/main/res/.gitkeep",
    "content": "\n"
  },
  {
    "path": "capacitor-cordova-ios-plugins/CordovaPluginsResources.podspec",
    "content": "Pod::Spec.new do |s|\n  s.name = 'CordovaPluginsResources'\n  s.version = '0.0.105'\n  s.summary = 'Resources for Cordova plugins'\n  s.license = 'MIT'\n  s.homepage = 'https://capacitorjs.com/'\n  s.authors = { 'Ionic Team' => 'hi@ionicframework.com' }\n  s.source = { :git => 'https://github.com/ionic-team/capacitor.git', :tag => s.version.to_s }\n  s.resources = ['resources/*']\nend\n"
  },
  {
    "path": "capacitor-cordova-ios-plugins/resources/.gitkeep",
    "content": "\n"
  },
  {
    "path": "capacitor-cordova-ios-plugins/sources/.gitkeep",
    "content": "\n"
  },
  {
    "path": "cli/.gitignore",
    "content": "assets/**/*.tar.gz\ndist/\n"
  },
  {
    "path": "cli/.npmignore",
    "content": "assets/android-template/app/build/*\n!assets/android-template/app/build/.npmkeep\nassets/android-template/.gradle\nassets/android-template/local.properties\nassets/ios-template/App/Pods/\nPodfile.lock"
  },
  {
    "path": "cli/.npmrc",
    "content": "package-lock=false"
  },
  {
    "path": "cli/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n# [8.2.0](https://github.com/ionic-team/capacitor/compare/8.1.0...8.2.0) (2026-03-06)\n\n### Bug Fixes\n\n- **cli:** Allow to run update on non macOS ([#8344](https://github.com/ionic-team/capacitor/issues/8344)) ([a441280](https://github.com/ionic-team/capacitor/commit/a441280d7c6b310ca516d6fb2736c09525987774))\n- **cli:** Don't overwrite config.server section with `--live-reload` ([#7528](https://github.com/ionic-team/capacitor/issues/7528)) ([782b9d9](https://github.com/ionic-team/capacitor/commit/782b9d9c26dcf1282b918996becb0224c0baca1d))\n\n### Features\n\n- **cli:** Add --https option for --live-reload ([#8194](https://github.com/ionic-team/capacitor/issues/8194)) ([5db81e6](https://github.com/ionic-team/capacitor/commit/5db81e68c67652e9d2b29d7ad30629b423d2ad30))\n\n# [8.1.0](https://github.com/ionic-team/capacitor/compare/8.0.2...8.1.0) (2026-02-11)\n\n### Features\n\n- **cli:** Add packageManager to iOS config ([#8321](https://github.com/ionic-team/capacitor/issues/8321)) ([a125498](https://github.com/ionic-team/capacitor/commit/a1254983bbb9dcb273d93f3c5f639b792e516406))\n\n### Reverts\n\n- revert version bump from [#8319](https://github.com/ionic-team/capacitor/issues/8319) and [#8320](https://github.com/ionic-team/capacitor/issues/8320) ([a48ebb6](https://github.com/ionic-team/capacitor/commit/a48ebb622ea4ebe92927bf1756a4d8ac6012884b))\n\n## [8.0.2](https://github.com/ionic-team/capacitor/compare/8.0.1...8.0.2) (2026-01-27)\n\n### Bug Fixes\n\n- **cli:** Update tar package ([#8311](https://github.com/ionic-team/capacitor/issues/8311)) ([0969c5c](https://github.com/ionic-team/capacitor/commit/0969c5cd0b16cf23d2489a85a3b8fa1bee2ebf3b))\n\n## [8.0.1](https://github.com/ionic-team/capacitor/compare/8.0.0...8.0.1) (2026-01-13)\n\n### Bug Fixes\n\n- **cli:** Support wireless iOS devices in `cap run` ([#8301](https://github.com/ionic-team/capacitor/issues/8301)) ([dcb368c](https://github.com/ionic-team/capacitor/commit/dcb368c33555487f1b6f46a6d8e30736bdd78955))\n- **cli:** use latest native-run ([#8296](https://github.com/ionic-team/capacitor/issues/8296)) ([121d830](https://github.com/ionic-team/capacitor/commit/121d83013f39e25009121533a2c3ad86e428d6b2))\n\n# [8.0.0](https://github.com/ionic-team/capacitor/compare/8.0.0-beta.0...8.0.0) (2025-12-08)\n\n### Bug Fixes\n\n- **cli:** Android apk name multi flavor dimensions parsing ([#7598](https://github.com/ionic-team/capacitor/issues/7598)) ([2dc20ee](https://github.com/ionic-team/capacitor/commit/2dc20ee894c2f5761431caa8269e9f418fc222b6))\n- **cli:** make migrate update to 8.0.0 ([#8250](https://github.com/ionic-team/capacitor/issues/8250)) ([ee8ba7b](https://github.com/ionic-team/capacitor/commit/ee8ba7bbeeeef6db0461f7a59c91095fd7cbd78b))\n\n### Features\n\n- **android:** Improving SystemBars inset handling ([#8268](https://github.com/ionic-team/capacitor/issues/8268)) ([81ae30a](https://github.com/ionic-team/capacitor/commit/81ae30a503797e417dd125b06262dabc4696c88a))\n\n# [8.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.3...8.0.0-beta.0) (2025-11-14)\n\n### Bug Fixes\n\n- **android:** add command not changing namespace ([#8224](https://github.com/ionic-team/capacitor/issues/8224)) ([60cf666](https://github.com/ionic-team/capacitor/commit/60cf66675159fb09cd41c94ce657202d01e05c74))\n- **cli:** fix cap run command for yarn pnp mode ([#7754](https://github.com/ionic-team/capacitor/issues/7754)) ([79ace5c](https://github.com/ionic-team/capacitor/commit/79ace5c7054d86d5b33ce325751ff738c63d4b0c))\n\n### Features\n\n- **cli:** Select a cap run target by target name ([#8199](https://github.com/ionic-team/capacitor/issues/8199)) ([b30c472](https://github.com/ionic-team/capacitor/commit/b30c472490117a5dbb14a9556ef1b6ca6e39b45b))\n- **iOS:** Allow plugins to hook into handling WebView URL authentication challenges ([#8216](https://github.com/ionic-team/capacitor/issues/8216)) ([e8507cf](https://github.com/ionic-team/capacitor/commit/e8507cfe4d93337ddee8ab6597aab5209ebb4858))\n- System Bars Plugin ([#8180](https://github.com/ionic-team/capacitor/issues/8180)) ([a32216a](https://github.com/ionic-team/capacitor/commit/a32216ac0607172a3a9c7ae5cdbfc598769294a6))\n\n# [8.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.2...8.0.0-alpha.3) (2025-10-22)\n\n### Bug Fixes\n\n- **cli:** change SPM incompatible plugin message ([#8129](https://github.com/ionic-team/capacitor/issues/8129)) ([28c2a96](https://github.com/ionic-team/capacitor/commit/28c2a96d898b07a05ae763f798e8903601cc3f67))\n- **cli:** Don't exclude Cordova plugins from SPM management ([#8140](https://github.com/ionic-team/capacitor/issues/8140)) ([69bbd3d](https://github.com/ionic-team/capacitor/commit/69bbd3dc59cd6f56252c06f23b351952af8661be))\n- **cli:** prefer studio executable over studio.sh on linux if available ([#8110](https://github.com/ionic-team/capacitor/issues/8110)) ([62bd16f](https://github.com/ionic-team/capacitor/commit/62bd16f675cb492b52c0b73483fddfd63c608189))\n\n# [8.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.1...8.0.0-alpha.2) (2025-08-20)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [8.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/7.4.2...8.0.0-alpha.1) (2025-08-20)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [7.4.2](https://github.com/ionic-team/capacitor/compare/7.4.1...7.4.2) (2025-07-10)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [7.4.1](https://github.com/ionic-team/capacitor/compare/7.4.0...7.4.1) (2025-07-03)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [7.4.0](https://github.com/ionic-team/capacitor/compare/7.3.0...7.4.0) (2025-06-18)\n\n### Features\n\n- **cli:** add spm-migration-assistant command ([#7963](https://github.com/ionic-team/capacitor/issues/7963)) ([ef42722](https://github.com/ionic-team/capacitor/commit/ef427225f63056b1731b3cd8941aea2311d3bc4d))\n- **cli:** fail cap update on uninstalled cordova dependencies ([#8032](https://github.com/ionic-team/capacitor/issues/8032)) ([b393de8](https://github.com/ionic-team/capacitor/commit/b393de8bc9e68aff2cb70428a7c86c98367feade))\n\n# [7.3.0](https://github.com/ionic-team/capacitor/compare/7.2.0...7.3.0) (2025-06-05)\n\n### Bug Fixes\n\n- add error code checking to `runPlatformHook` ([#7994](https://github.com/ionic-team/capacitor/issues/7994)) ([8717680](https://github.com/ionic-team/capacitor/commit/871768049187afe9f5d358d3fe08b6f18f70533e))\n- **cli:** Don't downgrade deployment target on migrate ([#7953](https://github.com/ionic-team/capacitor/issues/7953)) ([46e8792](https://github.com/ionic-team/capacitor/commit/46e87925b675933b413737854b1376eb1c71a23e))\n- **cli:** ensures the execution order of CLI hooks ([#7947](https://github.com/ionic-team/capacitor/issues/7947)) ([c3ea809](https://github.com/ionic-team/capacitor/commit/c3ea8094cdd385ac02a5aae89f8b44b6c53d493a))\n- **cli:** unexpected error on target device list ([#8015](https://github.com/ionic-team/capacitor/issues/8015)) ([f827c48](https://github.com/ionic-team/capacitor/commit/f827c48f08b13a265e8671291c24f3506e14a094))\n- **cli:** use proper build params ([#8016](https://github.com/ionic-team/capacitor/issues/8016)) ([12ce2a4](https://github.com/ionic-team/capacitor/commit/12ce2a486542011c8d6f4cd89a67464f3f27e28d))\n\n### Features\n\n- **cli:** Initial Cordova plugin SPM support ([#7999](https://github.com/ionic-team/capacitor/issues/7999)) ([c030354](https://github.com/ionic-team/capacitor/commit/c03035426343ddc4e303ca3420e476bb7f1e2201))\n- expose `appStartPath` on cap config server configuration ([#8019](https://github.com/ionic-team/capacitor/issues/8019)) ([a274fef](https://github.com/ionic-team/capacitor/commit/a274fef069176a8e528a22f4734d1e29a539709c))\n\n# [7.2.0](https://github.com/ionic-team/capacitor/compare/7.1.0...7.2.0) (2025-03-31)\n\n### Bug Fixes\n\n- **cli:** Don't migrate Podfile on SPM projects ([#7939](https://github.com/ionic-team/capacitor/issues/7939)) ([ab4e700](https://github.com/ionic-team/capacitor/commit/ab4e700035289f445668dfd2f86278ffc71ab269))\n\n# [7.1.0](https://github.com/ionic-team/capacitor/compare/7.0.1...7.1.0) (2025-03-12)\n\n### Bug Fixes\n\n- **cli:** don't run bundle if not installed ([#7896](https://github.com/ionic-team/capacitor/issues/7896)) ([ee55f6c](https://github.com/ionic-team/capacitor/commit/ee55f6c54639c1a61fa4f9eed1f366ba2d968b86))\n\n### Features\n\n- **android:** add adjustMarginsForEdgeToEdge configuration option ([#7885](https://github.com/ionic-team/capacitor/issues/7885)) ([1ea86d1](https://github.com/ionic-team/capacitor/commit/1ea86d166afd315e72847c5e734a8c175fb90e04))\n- **cli:** add more configurations to build command ([#7769](https://github.com/ionic-team/capacitor/issues/7769)) ([90f95d1](https://github.com/ionic-team/capacitor/commit/90f95d1a829f3d87cb46af827b5bfaac319a9694))\n\n## [7.0.1](https://github.com/ionic-team/capacitor/compare/7.0.0...7.0.1) (2025-01-21)\n\n### Bug Fixes\n\n- make migrate use 7.0.0 ([#7837](https://github.com/ionic-team/capacitor/issues/7837)) ([5dc309e](https://github.com/ionic-team/capacitor/commit/5dc309ea8dd7905e9e6236744f29162df80b5ed8))\n- use Capacitor 7 for SPM dependency ([#7835](https://github.com/ionic-team/capacitor/issues/7835)) ([640c3cb](https://github.com/ionic-team/capacitor/commit/640c3cb22a62e4b8fb13cd36031d39307622c60a))\n\n# [7.0.0](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.1...7.0.0) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [7.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.0...7.0.0-rc.1) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [7.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/7.0.0-beta.0...7.0.0-rc.0) (2024-12-20)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [7.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.2...7.0.0-beta.0) (2024-12-20)\n\n### Bug Fixes\n\n- **cli:** correct rimraf import ([#7811](https://github.com/ionic-team/capacitor/issues/7811)) ([0891624](https://github.com/ionic-team/capacitor/commit/0891624c46b78a0fd39f617f834d5cdd1f54f5e6))\n- **cli:** update link to telemetry information ([e922e2b](https://github.com/ionic-team/capacitor/commit/e922e2b718f5c6f2e4062cdccdeb92da3321e67d))\n- **ios:** Make Bridge webView first responder ([#7753](https://github.com/ionic-team/capacitor/issues/7753)) ([77e4668](https://github.com/ionic-team/capacitor/commit/77e4668fa8dbb24b4561387e101547f74e37538e))\n\n### Features\n\n- Add global initialFocus configuration ([#7775](https://github.com/ionic-team/capacitor/issues/7775)) ([61d0165](https://github.com/ionic-team/capacitor/commit/61d01653685d8e3594d2d8a6bd870fa9643ba95c))\n\n# [7.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.1...7.0.0-alpha.2) (2024-11-19)\n\n### Bug Fixes\n\n- **cli:** make Cordova plugins use same default kotlin version as Capacitor ([#7752](https://github.com/ionic-team/capacitor/issues/7752)) ([a4aeb55](https://github.com/ionic-team/capacitor/commit/a4aeb55720ecc83b3870bf32bf9825d6aee4644f))\n\n# [7.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/6.1.2...7.0.0-alpha.1) (2024-10-14)\n\n### Bug Fixes\n\n- **cli:** replace app-store deprecated method on build ([#7637](https://github.com/ionic-team/capacitor/issues/7637)) ([942b108](https://github.com/ionic-team/capacitor/commit/942b108c1d506539c0c53276ed4ec35eed36634e))\n\n## [6.1.2](https://github.com/ionic-team/capacitor/compare/6.1.1...6.1.2) (2024-08-07)\n\n### Bug Fixes\n\n- support dependencies with \".swift\" at the end of name ([#7583](https://github.com/ionic-team/capacitor/issues/7583)) ([ceee68a](https://github.com/ionic-team/capacitor/commit/ceee68a2db363e9d9a638aa4ed8569fd82d1013a))\n\n## [6.1.1](https://github.com/ionic-team/capacitor/compare/6.1.0...6.1.1) (2024-07-17)\n\n### Bug Fixes\n\n- **cli:** enable cleartext for live reload ([#7563](https://github.com/ionic-team/capacitor/issues/7563)) ([e06648f](https://github.com/ionic-team/capacitor/commit/e06648f4566c0b80ab420f977e02f1b4cd73fc1f))\n- **ios:** Match CapApp-SPM iOS version with project version ([#7556](https://github.com/ionic-team/capacitor/issues/7556)) ([df4dc9a](https://github.com/ionic-team/capacitor/commit/df4dc9a17f1b999a68093091d201ba15d234c5f8))\n\n# [6.1.0](https://github.com/ionic-team/capacitor/compare/6.0.0...6.1.0) (2024-06-11)\n\n### Bug Fixes\n\n- **cli:** Avoid duplicate entries in packageClassList ([#7470](https://github.com/ionic-team/capacitor/issues/7470)) ([cca0b80](https://github.com/ionic-team/capacitor/commit/cca0b805291bd002c515be0a1c15de84ee23c5d3))\n- **cli:** Run sync before updating gradle ([#7497](https://github.com/ionic-team/capacitor/issues/7497)) ([f27786e](https://github.com/ionic-team/capacitor/commit/f27786ea1367bc0ec53697eeb6d654a7e12a1507))\n\n### Features\n\n- **cli:** run plugin hooks ([#7499](https://github.com/ionic-team/capacitor/issues/7499)) ([3b847ea](https://github.com/ionic-team/capacitor/commit/3b847eac42673c812a4538c319b81c8f8afc9955))\n\n# [6.0.0](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.2...6.0.0) (2024-04-15)\n\n### Bug Fixes\n\n- **cli:** Change Capacitor plugin files check ([#7375](https://github.com/ionic-team/capacitor/issues/7375)) ([fbfb778](https://github.com/ionic-team/capacitor/commit/fbfb77825fdd3343e85868ea28b8e14a482dcf4a))\n- **cli:** Don't downgrade gradle version on migrate ([#7385](https://github.com/ionic-team/capacitor/issues/7385)) ([c79211e](https://github.com/ionic-team/capacitor/commit/c79211ec9dde81a57aca5cee8ab09cdef0c9ad86))\n- **cli:** Removing extra dash in android apk name flavor parsing ([#7382](https://github.com/ionic-team/capacitor/issues/7382)) ([3c411fd](https://github.com/ionic-team/capacitor/commit/3c411fd0a400b18c2d032e3b8be3fc4b5449bcbd))\n\n### Features\n\n- **cli:** Support bun when running cap migrate ([#7386](https://github.com/ionic-team/capacitor/issues/7386)) ([3f96ff9](https://github.com/ionic-team/capacitor/commit/3f96ff911588c517dc2d924f55ef28a25c30bd33))\n\n# [6.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.1...6.0.0-rc.2) (2024-03-25)\n\n### Bug Fixes\n\n- **cli:** also generate packageClassList on copy ([#7358](https://github.com/ionic-team/capacitor/issues/7358)) ([865cceb](https://github.com/ionic-team/capacitor/commit/865cceb1bf7e1b973e8f83f3c77040add71b403c))\n- **cli:** use correct pass signature for apksigner signing ([#7365](https://github.com/ionic-team/capacitor/issues/7365)) ([e4f8610](https://github.com/ionic-team/capacitor/commit/e4f86103c2806df4ca2729600361e6338c30f083))\n- inject cordova files if a cordova plugin is present ([#7363](https://github.com/ionic-team/capacitor/issues/7363)) ([ce9d505](https://github.com/ionic-team/capacitor/commit/ce9d50585b1cab183245197878bf625cf0289275))\n\n# [6.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.0...6.0.0-rc.1) (2024-03-15)\n\n### Bug Fixes\n\n- **cli:** adjust SPM parameters for build and run commands ([#7342](https://github.com/ionic-team/capacitor/issues/7342)) ([02f8983](https://github.com/ionic-team/capacitor/commit/02f8983300c102125db6b41b0bb174fe41e1785b))\n- **cli:** apkName for multi-dimensional flavors ([#6704](https://github.com/ionic-team/capacitor/issues/6704)) ([d7b23f2](https://github.com/ionic-team/capacitor/commit/d7b23f21c554c94029b24d6e0c9ad4635f055759))\n- **cli:** Attempt to verify non-cjs modules exist if cjs resolution fails ([#7310](https://github.com/ionic-team/capacitor/issues/7310)) ([663e7b3](https://github.com/ionic-team/capacitor/commit/663e7b383be0ceee1d23b929ceb6374d00faf011))\n- **cli:** correct build path for non flavor builds ([#7281](https://github.com/ionic-team/capacitor/issues/7281)) ([0f9651d](https://github.com/ionic-team/capacitor/commit/0f9651d99cdd9cb463e494ed016838cd6d4a34c4))\n- **cli:** Migrate variables to newer dependency versions ([#7235](https://github.com/ionic-team/capacitor/issues/7235)) ([aaf01ab](https://github.com/ionic-team/capacitor/commit/aaf01ab938e02220d7e6b5cd9329baa3edd02b68))\n- **cli:** remove certificate file extension check on copy ([#7240](https://github.com/ionic-team/capacitor/issues/7240)) ([93a0044](https://github.com/ionic-team/capacitor/commit/93a004473d7c4008bf02e8077460dfbdc3042312))\n- **spm:** add product lines to Package.swift ([#7278](https://github.com/ionic-team/capacitor/issues/7278)) ([e45d60d](https://github.com/ionic-team/capacitor/commit/e45d60d15445fa55320eb6274de67d4b4191f30a))\n\n# [6.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.2...6.0.0-rc.0) (2024-01-23)\n\n### Bug Fixes\n\n- **cli:** correctly build and sign Android apps using Flavors ([#7082](https://github.com/ionic-team/capacitor/issues/7082)) ([7d3a99d](https://github.com/ionic-team/capacitor/commit/7d3a99d15ba024b4bde9e698e57937002da38bd4))\n- **cli:** Specify keystore alias and alias password build options for apksigner ([#7073](https://github.com/ionic-team/capacitor/issues/7073)) ([6612c49](https://github.com/ionic-team/capacitor/commit/6612c49f5fde5ef2fee555206018391fc00e2be0))\n- **cli:** type only import in config ([#6765](https://github.com/ionic-team/capacitor/issues/6765)) ([2b493ae](https://github.com/ionic-team/capacitor/commit/2b493ae77b9ee6e5fbcb2fafb27bc04693d7e02b))\n- **cli:** Update plugins with breaking changes ([#7148](https://github.com/ionic-team/capacitor/issues/7148)) ([b8c2a92](https://github.com/ionic-team/capacitor/commit/b8c2a92390f21fe653563d7bad73a3f2b5628acb))\n- stop crashing from `objc_getClassList` ([#7187](https://github.com/ionic-team/capacitor/issues/7187)) ([e148db7](https://github.com/ionic-team/capacitor/commit/e148db7758e4186ad45fd74185a10fb757ff9b29))\n\n### Features\n\n- **cli:** allow async in capacitor config file ([#4299](https://github.com/ionic-team/capacitor/issues/4299)) ([5cecc3e](https://github.com/ionic-team/capacitor/commit/5cecc3ee22868f9397792ec80a4b753a07b68f30))\n\n# [6.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.1...6.0.0-beta.2) (2023-12-14)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [6.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.0...6.0.0-beta.1) (2023-12-14)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [6.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.2...6.0.0-beta.0) (2023-12-13)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [6.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.1...6.0.0-alpha.2) (2023-11-15)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [6.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/5.2.3...6.0.0-alpha.1) (2023-11-08)\n\n### Bug Fixes\n\n- allow double quotes in Gemfile ([#6903](https://github.com/ionic-team/capacitor/issues/6903)) ([3abdbed](https://github.com/ionic-team/capacitor/commit/3abdbed38844d5d59d244f6f0dfc2647f29ce446))\n- **cli:** force latest native-run version for iOS 17 support ([#6926](https://github.com/ionic-team/capacitor/issues/6926)) ([7e7c8b9](https://github.com/ionic-team/capacitor/commit/7e7c8b9113f541d530c5883dea1f52b2957c0859))\n- **cli:** Pin @ionic/utils-subprocess version ([#7057](https://github.com/ionic-team/capacitor/issues/7057)) ([0ac019a](https://github.com/ionic-team/capacitor/commit/0ac019a36070b4cb9917a82e406453169c7d5559))\n- **cli:** use helper in Podfile with correct path ([#6878](https://github.com/ionic-team/capacitor/issues/6878)) ([8e95be9](https://github.com/ionic-team/capacitor/commit/8e95be9f91169e258ab5cdd8fd673106392b8429))\n- **cli:** Use latest native-run ([#7023](https://github.com/ionic-team/capacitor/issues/7023)) ([4125160](https://github.com/ionic-team/capacitor/commit/412516069e15fbdbc17ad130c2f3a67891b6bc45))\n- Update migrate to Capacitor 6 ([#6872](https://github.com/ionic-team/capacitor/issues/6872)) ([98eec8f](https://github.com/ionic-team/capacitor/commit/98eec8fe9fd332d6669965fa5a21412233b3e06e))\n\n### Features\n\n- add livereload to run command ([#6831](https://github.com/ionic-team/capacitor/issues/6831)) ([4099969](https://github.com/ionic-team/capacitor/commit/4099969f70e9b995182bacecc16e160d89bbc746))\n- better support monorepos ([#6811](https://github.com/ionic-team/capacitor/issues/6811)) ([ae35e29](https://github.com/ionic-team/capacitor/commit/ae35e29fb8c886dea867683a23a558d2d344073b))\n- modify package.swift on update and sync ([#7042](https://github.com/ionic-team/capacitor/issues/7042)) ([24573fb](https://github.com/ionic-team/capacitor/commit/24573fb864c43551e2ce42721b45ff901155627d))\n\n# [5.6.0](https://github.com/ionic-team/capacitor/compare/5.5.1...5.6.0) (2023-12-14)\n\n### Bug Fixes\n\n- **cli:** Use latest native-run ([#7030](https://github.com/ionic-team/capacitor/issues/7030)) ([1d948d4](https://github.com/ionic-team/capacitor/commit/1d948d4df6b6b6f8cfdc02e72d84ae8be963f4a0))\n\n## [5.5.1](https://github.com/ionic-team/capacitor/compare/5.5.0...5.5.1) (2023-10-25)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [5.5.0](https://github.com/ionic-team/capacitor/compare/5.4.2...5.5.0) (2023-10-11)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [5.4.2](https://github.com/ionic-team/capacitor/compare/5.4.1...5.4.2) (2023-10-04)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [5.4.1](https://github.com/ionic-team/capacitor/compare/5.4.0...5.4.1) (2023-09-21)\n\n### Bug Fixes\n\n- **cli:** force latest native-run version for iOS 17 support ([#6928](https://github.com/ionic-team/capacitor/issues/6928)) ([f9be9f5](https://github.com/ionic-team/capacitor/commit/f9be9f5791e6f0881be2c73bb8fbe7a8c1b10848))\n- **cookies:** retrieve cookies when using a custom android scheme ([6b5ddad](https://github.com/ionic-team/capacitor/commit/6b5ddad8b36e33ef4171f6da5cc311ed3f634ac6))\n\n# [5.4.0](https://github.com/ionic-team/capacitor/compare/5.3.0...5.4.0) (2023-09-14)\n\n### Bug Fixes\n\n- **cli:** use helper in Podfile with correct path ([#6888](https://github.com/ionic-team/capacitor/issues/6888)) ([9048432](https://github.com/ionic-team/capacitor/commit/9048432755095ce3dcca9d3bab39894f2b6c3967))\n\n### Features\n\n- add livereload to run command ([#6831](https://github.com/ionic-team/capacitor/issues/6831)) ([54a63ae](https://github.com/ionic-team/capacitor/commit/54a63ae0a5f0845d5ef2c0d10bd0c27682866940))\n\n# [5.3.0](https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0) (2023-08-23)\n\n### Features\n\n- better support monorepos ([#6811](https://github.com/ionic-team/capacitor/issues/6811)) ([ae35e29](https://github.com/ionic-team/capacitor/commit/ae35e29fb8c886dea867683a23a558d2d344073b))\n\n## [5.2.3](https://github.com/ionic-team/capacitor/compare/5.2.2...5.2.3) (2023-08-10)\n\n### Bug Fixes\n\n- **cli:** remove package related checks on doctor command ([#6773](https://github.com/ionic-team/capacitor/issues/6773)) ([4499b6b](https://github.com/ionic-team/capacitor/commit/4499b6bb6c52e9bc7fdfdb35ee2519881996eedf))\n- **cli:** signing type option issue ([#6716](https://github.com/ionic-team/capacitor/issues/6716)) ([ee0f745](https://github.com/ionic-team/capacitor/commit/ee0f7457e458ca4bb4eb74f67552ac2ace76016b))\n\n## [5.2.2](https://github.com/ionic-team/capacitor/compare/5.2.1...5.2.2) (2023-07-19)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [5.2.1](https://github.com/ionic-team/capacitor/compare/5.2.0...5.2.1) (2023-07-13)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [5.2.0](https://github.com/ionic-team/capacitor/compare/5.1.1...5.2.0) (2023-07-12)\n\n### Bug Fixes\n\n- **cli:** make migrate not error if there are no dependencies ([#6707](https://github.com/ionic-team/capacitor/issues/6707)) ([25ca83a](https://github.com/ionic-team/capacitor/commit/25ca83a8a76fe0eaf73c0db24fd950b33fd16063))\n\n## [5.1.1](https://github.com/ionic-team/capacitor/compare/5.1.0...5.1.1) (2023-07-05)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [5.1.0](https://github.com/ionic-team/capacitor/compare/5.0.5...5.1.0) (2023-06-29)\n\n### Features\n\n- **cli:** add apksigner as a build option ([#6442](https://github.com/ionic-team/capacitor/issues/6442)) ([9818a76](https://github.com/ionic-team/capacitor/commit/9818a76ab4ea6660b444354f239344d37c77d3b3))\n\n## [5.0.5](https://github.com/ionic-team/capacitor/compare/5.0.4...5.0.5) (2023-06-09)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [5.0.4](https://github.com/ionic-team/capacitor/compare/5.0.3...5.0.4) (2023-05-23)\n\n### Bug Fixes\n\n- **cli:** correct migration of package from AndroidManifest.xml to build.gradle ([#6607](https://github.com/ionic-team/capacitor/issues/6607)) ([1c26a3e](https://github.com/ionic-team/capacitor/commit/1c26a3e57f356a0972bd43854ca86770a49f2d63))\n- **cli:** Don't succeed migration if npm install failed ([#6595](https://github.com/ionic-team/capacitor/issues/6595)) ([6843d96](https://github.com/ionic-team/capacitor/commit/6843d9642fad9a322579cbe5f01563929a83dbf5))\n- **cli:** proper plugin module patch in monorepos ([#6589](https://github.com/ionic-team/capacitor/issues/6589)) ([d49e632](https://github.com/ionic-team/capacitor/commit/d49e6324ab5e0bea58ff6ca32feb7ea39d33a772))\n\n## [5.0.3](https://github.com/ionic-team/capacitor/compare/5.0.2...5.0.3) (2023-05-10)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [5.0.2](https://github.com/ionic-team/capacitor/compare/5.0.1...5.0.2) (2023-05-09)\n\n### Bug Fixes\n\n- **cli:** handle unrecognized java --version ([#6577](https://github.com/ionic-team/capacitor/issues/6577)) ([56b0037](https://github.com/ionic-team/capacitor/commit/56b0037a70d64019563b6e55e53de423f471fe2f))\n- **cli:** Move package to build.gradle in Capacitor plugins ([#6569](https://github.com/ionic-team/capacitor/issues/6569)) ([8cb26cd](https://github.com/ionic-team/capacitor/commit/8cb26cd97a4f9cf59abb6b3828a07555a6af0b15))\n- fallback to plain `pod` if `Gemfile` does not contain CocoaPods ([#6581](https://github.com/ionic-team/capacitor/issues/6581)) ([3a41b4c](https://github.com/ionic-team/capacitor/commit/3a41b4c1b70af7a45201fb11b04dc5558893aa7e))\n\n## [5.0.1](https://github.com/ionic-team/capacitor/compare/5.0.0...5.0.1) (2023-05-05)\n\n### Bug Fixes\n\n- **cli:** install minor Capacitor 5 version ([#6562](https://github.com/ionic-team/capacitor/issues/6562)) ([f4af0a2](https://github.com/ionic-team/capacitor/commit/f4af0a298fb5a5f8257f175327058341a230ae4f))\n- **cli:** Update migration link ([#6560](https://github.com/ionic-team/capacitor/issues/6560)) ([e03062e](https://github.com/ionic-team/capacitor/commit/e03062e6025fea0edfabbff2081b3f91017aece4))\n\n# [5.0.0](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.3...5.0.0) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [5.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.2...5.0.0-rc.3) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [5.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.1...5.0.0-rc.2) (2023-05-03)\n\n### Bug Fixes\n\n- check for android and JDK ([#6554](https://github.com/ionic-team/capacitor/issues/6554)) ([ddcc818](https://github.com/ionic-team/capacitor/commit/ddcc818e828b290459d3ddffe9102fc312139823))\n\n# [5.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.0...5.0.0-rc.1) (2023-05-02)\n\n### Bug Fixes\n\n- **cli:** Avoid infinite loop in namespace migration ([#6551](https://github.com/ionic-team/capacitor/issues/6551)) ([d3aacde](https://github.com/ionic-team/capacitor/commit/d3aacdeb0c86d3941464954e7d1f582e405be489))\n- **cli:** Migrate more plugin variables ([#6552](https://github.com/ionic-team/capacitor/issues/6552)) ([b7da5b9](https://github.com/ionic-team/capacitor/commit/b7da5b988ce7da5ea3991eaec46b9e52ff3635f1))\n\n# [5.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.3...5.0.0-rc.0) (2023-05-01)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [5.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.2...5.0.0-beta.3) (2023-04-21)\n\n### Features\n\n- **cli:** android manifest to build.gradle migration ([#6533](https://github.com/ionic-team/capacitor/issues/6533)) ([245b6ab](https://github.com/ionic-team/capacitor/commit/245b6ab85b0f481f08c21e25f2b2a7eb6da9996c))\n- **cli:** Migrate update to gradle 8 ([#6530](https://github.com/ionic-team/capacitor/issues/6530)) ([da3ac0e](https://github.com/ionic-team/capacitor/commit/da3ac0e72c0559223e9b91f31830810d39638734))\n\n# [5.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.1...5.0.0-beta.2) (2023-04-13)\n\n### Features\n\n- **ios:** Add Bundler support ([#5205](https://github.com/ionic-team/capacitor/issues/5205)) ([f21c6d0](https://github.com/ionic-team/capacitor/commit/f21c6d01fc30e46c151afc93da9727dbf6c9ddcf))\n- **ios:** add webContentsDebuggingEnabled configuration ([#6495](https://github.com/ionic-team/capacitor/issues/6495)) ([c691e4a](https://github.com/ionic-team/capacitor/commit/c691e4aecbfb7a45ce0465d1fe9020ab715815d3))\n\n# [5.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.0...5.0.0-beta.1) (2023-04-03)\n\n### Features\n\n- **cli:** add npm update step to migrate ([#6462](https://github.com/ionic-team/capacitor/issues/6462)) ([65520c3](https://github.com/ionic-team/capacitor/commit/65520c36cdb4ac6f8811eb72624c447f2a0d884a))\n- **cli:** non interactive migrate ([#6461](https://github.com/ionic-team/capacitor/issues/6461)) ([53dfeaf](https://github.com/ionic-team/capacitor/commit/53dfeaf77ace5b165260b68351eae8e5bf72ea0a))\n\n# [5.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/5.0.0-alpha.1...5.0.0-beta.0) (2023-03-31)\n\n### Bug Fixes\n\n- **cli:** Fix cordova plugin config checker over checking 5.x ([#6444](https://github.com/ionic-team/capacitor/issues/6444)) ([9d21a0e](https://github.com/ionic-team/capacitor/commit/9d21a0e363141fa0792c12d977b13692be67cf6d))\n\n### Features\n\n- **android:** Fix for [#6258](https://github.com/ionic-team/capacitor/issues/6258), Add support for modern Huawei devices ([#6402](https://github.com/ionic-team/capacitor/issues/6402)) ([17f2f4a](https://github.com/ionic-team/capacitor/commit/17f2f4ac744a038c1dae3cbd74a670d5ede92ef3))\n- **cli:** Add missing Cap 4 to Cap 5 migration tasks ([#6453](https://github.com/ionic-team/capacitor/issues/6453)) ([7dff363](https://github.com/ionic-team/capacitor/commit/7dff36376d6efa6ea8793b81550979ee9254991b))\n- **cli:** Add support for Android build `--flavor` ([#6437](https://github.com/ionic-team/capacitor/issues/6437)) ([e91a8e7](https://github.com/ionic-team/capacitor/commit/e91a8e795604042684ec9e20b620eee36e0dfda2))\n- **cli:** update migrate command for cap4 -> cap5 ([#6447](https://github.com/ionic-team/capacitor/issues/6447)) ([b1f0a37](https://github.com/ionic-team/capacitor/commit/b1f0a3744d3158dab083ea31ea3e747b937729d2))\n- Upgrade to Typescript 5.x ([#6433](https://github.com/ionic-team/capacitor/issues/6433)) ([88d0ded](https://github.com/ionic-team/capacitor/commit/88d0ded9e7356531ffc4563b9b81a0f3f069484b))\n\n# [5.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/4.7.0...5.0.0-alpha.1) (2023-03-16)\n\n### Bug Fixes\n\n- **cli:** point build to proper workspace name ([#6371](https://github.com/ionic-team/capacitor/issues/6371)) ([78b7a59](https://github.com/ionic-team/capacitor/commit/78b7a591429b5fc1dc7bf87e06e9a1afd3599408))\n\n### Features\n\n- **cli:** Add --forwardPorts option to Capacitor CLI ([#5645](https://github.com/ionic-team/capacitor/issues/5645)) ([2f04d29](https://github.com/ionic-team/capacitor/commit/2f04d29a17eb46f9bd140b93e821e11380699367))\n- **cli:** update init to set androidScheme to https ([#6342](https://github.com/ionic-team/capacitor/issues/6342)) ([158b27e](https://github.com/ionic-team/capacitor/commit/158b27e53fd803c87de32e8da9f0c5117f81a3a5))\n\n# [4.7.0](https://github.com/ionic-team/capacitor/compare/4.6.3...4.7.0) (2023-02-22)\n\n### Bug Fixes\n\n- **cli:** prevent error on manifest element without children ([#6278](https://github.com/ionic-team/capacitor/issues/6278)) ([a7e374f](https://github.com/ionic-team/capacitor/commit/a7e374fc4d834ded437edb4c8a0be98b6691be4c))\n- **cli:** Remove buildOptions from platform capacitor.config.json ([#6292](https://github.com/ionic-team/capacitor/issues/6292)) ([acddcd9](https://github.com/ionic-team/capacitor/commit/acddcd95b40a7d4cc6c7682d2d1019f96dacf68d))\n\n### Features\n\n- **cli:** add ssl pinning copy logic ([#6312](https://github.com/ionic-team/capacitor/issues/6312)) ([cce66c1](https://github.com/ionic-team/capacitor/commit/cce66c1d59370ba35db879f4d7a3620d22175ab0))\n\n## [4.6.3](https://github.com/ionic-team/capacitor/compare/4.6.2...4.6.3) (2023-02-03)\n\n### Bug Fixes\n\n- **cli/cordova:** Exclude private framework Headers ([#6229](https://github.com/ionic-team/capacitor/issues/6229)) ([6c2726d](https://github.com/ionic-team/capacitor/commit/6c2726dc0817c9e57c8b7909e4a69a9b376c425d))\n\n## [4.6.2](https://github.com/ionic-team/capacitor/compare/4.6.1...4.6.2) (2023-01-17)\n\n### Bug Fixes\n\n- **cli:** config file android release type not overridden ([#6205](https://github.com/ionic-team/capacitor/issues/6205)) ([1441c55](https://github.com/ionic-team/capacitor/commit/1441c551737ce42dd2b82fc1a9da1e8311e27f1a))\n- **cli:** flavor flag not using correct apk ([#6151](https://github.com/ionic-team/capacitor/issues/6151)) ([f4e7f19](https://github.com/ionic-team/capacitor/commit/f4e7f19f186e334404b2cd0decc3205e57bf4469))\n\n## [4.6.1](https://github.com/ionic-team/capacitor/compare/4.6.0...4.6.1) (2022-12-05)\n\n### Bug Fixes\n\n- **cli:** support variables in config warn checks ([#6136](https://github.com/ionic-team/capacitor/issues/6136)) ([b460add](https://github.com/ionic-team/capacitor/commit/b460add5e22139f234ca8fae98f174bb7c447292))\n\n# [4.6.0](https://github.com/ionic-team/capacitor/compare/4.5.0...4.6.0) (2022-12-01)\n\n### Bug Fixes\n\n- **cli:** useLegacyBridge should be optional ([#6095](https://github.com/ionic-team/capacitor/issues/6095)) ([20f68fe](https://github.com/ionic-team/capacitor/commit/20f68feab2cb88cf8a79a987533839aa49255607))\n- **cli:** warns about config that is present ([#6060](https://github.com/ionic-team/capacitor/issues/6060)) ([7ac43e7](https://github.com/ionic-team/capacitor/commit/7ac43e722139a61129cfecf98da373659b1aeac8))\n\n# [4.5.0](https://github.com/ionic-team/capacitor/compare/4.4.0...4.5.0) (2022-11-16)\n\n### Bug Fixes\n\n- **cli:** add vite config to framework detection ([#6039](https://github.com/ionic-team/capacitor/issues/6039)) ([3796d42](https://github.com/ionic-team/capacitor/commit/3796d42665f3150f99c761aa561a9e34d03cae28))\n- **cli:** calculate padding of super.onCreate(savedInstanceState); line ([#6016](https://github.com/ionic-team/capacitor/issues/6016)) ([5729ac1](https://github.com/ionic-team/capacitor/commit/5729ac19e7880713ec52bac431a2756da5aa3109))\n- **cli:** Update gradle versions only if they are older ([#6015](https://github.com/ionic-team/capacitor/issues/6015)) ([ae94101](https://github.com/ionic-team/capacitor/commit/ae941017fff3bcfa75e0788535f356a56ce6fa05))\n- **cli/ios:** Read handleApplicationNotifications configuration option ([#6030](https://github.com/ionic-team/capacitor/issues/6030)) ([99ccf18](https://github.com/ionic-team/capacitor/commit/99ccf181f6ee8a00ed97bdbf9076e2b2ea27cd57))\n\n### Features\n\n- **android/cli:** Allow to use the old addJavascriptInterface bridge ([#6043](https://github.com/ionic-team/capacitor/issues/6043)) ([a6e7c54](https://github.com/ionic-team/capacitor/commit/a6e7c5422687b703492a5fcc49369eacc376143d))\n\n# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21)\n\n### Features\n\n- **cli:** add build command for android ([#5891](https://github.com/ionic-team/capacitor/issues/5891)) ([6d4e620](https://github.com/ionic-team/capacitor/commit/6d4e620308b6dd97376e3af7de1dd1a530083f1c))\n- **cli:** add build command for ios ([#5925](https://github.com/ionic-team/capacitor/issues/5925)) ([8e8414f](https://github.com/ionic-team/capacitor/commit/8e8414fa6f4ccb245576cc113eb969937613bbf7))\n- **cli:** supports secure live updates in Portals for Capacitor config ([#5955](https://github.com/ionic-team/capacitor/issues/5955)) ([a309b45](https://github.com/ionic-team/capacitor/commit/a309b455fdd190613353bdf0eb04469cf4aa6ccd))\n\n# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21)\n\n### Bug Fixes\n\n- **cli:** Find the Info.plist when using scheme ([#5914](https://github.com/ionic-team/capacitor/issues/5914)) ([f7029ac](https://github.com/ionic-team/capacitor/commit/f7029acb885ec60f85a434b6f71e4f2a633c7651))\n- **cli:** Make migrator update gradle wrapper files ([#5910](https://github.com/ionic-team/capacitor/issues/5910)) ([b8b9b1f](https://github.com/ionic-team/capacitor/commit/b8b9b1f96249908435017eea6c427221f1971836))\n- **cli:** Make update from windows use proper paths on Podfile ([#5906](https://github.com/ionic-team/capacitor/issues/5906)) ([c41d28f](https://github.com/ionic-team/capacitor/commit/c41d28f8cc829c6bf69d776280c9f1fdba9f866f))\n- **cli:** show error if npm install on migration failed ([#5904](https://github.com/ionic-team/capacitor/issues/5904)) ([aa60a75](https://github.com/ionic-team/capacitor/commit/aa60a75d9c2c784e127a4d89e4079b412fbe7262))\n\n### Features\n\n- Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45))\n\n# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08)\n\n### Features\n\n- **cli:** add inline option to copy command ([#5901](https://github.com/ionic-team/capacitor/issues/5901)) ([17fbabb](https://github.com/ionic-team/capacitor/commit/17fbabb2a77d1b356d24048efc5883bd4d049104))\n- **cli:** add scheme and flavor options to run command ([#5873](https://github.com/ionic-team/capacitor/issues/5873)) ([e4c143d](https://github.com/ionic-team/capacitor/commit/e4c143d4da653533570215964808c2f32f5469d3))\n- **cli:** copy signature when using secure live updates ([#5896](https://github.com/ionic-team/capacitor/issues/5896)) ([0f17177](https://github.com/ionic-team/capacitor/commit/0f17177b1c64c0f69f86e990e4e150b820da497b))\n\n# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18)\n\n### Bug Fixes\n\n- **cli:** Also update preferences plugin if present ([#5831](https://github.com/ionic-team/capacitor/issues/5831)) ([b9d5954](https://github.com/ionic-team/capacitor/commit/b9d5954ca0b333f2caa20179b96b049379860ea5))\n- **cli:** Don't add google-services plugin if missing ([#5825](https://github.com/ionic-team/capacitor/issues/5825)) ([48ff9e6](https://github.com/ionic-team/capacitor/commit/48ff9e6461e8037a5c6da87c90efc6bc872d7f08))\n- **cli:** make migrator also update plugin variables ([#5871](https://github.com/ionic-team/capacitor/issues/5871)) ([478d48c](https://github.com/ionic-team/capacitor/commit/478d48c3e322cffc6f0ff7ce590b635de4b41279))\n- **cli:** Migrator put registerPlugin before super.onCreate ([#5828](https://github.com/ionic-team/capacitor/issues/5828)) ([8cd3373](https://github.com/ionic-team/capacitor/commit/8cd3373133903f97a836fd6ac6b7ce4e1ba9317e))\n- **cli:** prevent error on migrate when devDependencies is missing ([#5863](https://github.com/ionic-team/capacitor/issues/5863)) ([474ad1f](https://github.com/ionic-team/capacitor/commit/474ad1f4d4a9ea0636a457836c938dac9f6534e8))\n- **cli:** remove double space in cap 2 variables file ([#5826](https://github.com/ionic-team/capacitor/issues/5826)) ([7184097](https://github.com/ionic-team/capacitor/commit/7184097da88ed34f3e754119f967d262aa5e2add))\n- **cli:** Support of BoM dependencies on cordova plugins ([#5827](https://github.com/ionic-team/capacitor/issues/5827)) ([ea2d95b](https://github.com/ionic-team/capacitor/commit/ea2d95ba43467cd2d4c4637aacab6bf655d9c596))\n\n### Features\n\n- **cli:** Option to inline JS source maps during sync ([#5843](https://github.com/ionic-team/capacitor/issues/5843)) ([7ce6dd4](https://github.com/ionic-team/capacitor/commit/7ce6dd4b6fb5cdc395add6f656fbedc785178ae3))\n\n## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28)\n\n### Bug Fixes\n\n- **cli:** Correct Splash theme update ([#5805](https://github.com/ionic-team/capacitor/issues/5805)) ([25b82a8](https://github.com/ionic-team/capacitor/commit/25b82a84425bf09b2be45b213788b0e13982b9b3))\n- **cli:** Revert some splash migration errors ([#5806](https://github.com/ionic-team/capacitor/issues/5806)) ([471feed](https://github.com/ionic-team/capacitor/commit/471feedc07bef357ac798fcba664bd373e9f8ebf))\n\n# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27)\n\n### Features\n\n- **android:** Add android.minWebviewVersion configuration option ([#5768](https://github.com/ionic-team/capacitor/issues/5768)) ([ad83827](https://github.com/ionic-team/capacitor/commit/ad838279e9cd190ce6f1a020a0ac9e3916786324))\n- **cli:** add migrator for cap3 to cap4 ([#5762](https://github.com/ionic-team/capacitor/issues/5762)) ([7cb660a](https://github.com/ionic-team/capacitor/commit/7cb660a34d9a87274761d4492d0d77c9ef44ace8))\n- Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3))\n\n# [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [4.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.0...4.0.0-beta.1) (2022-06-27)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [4.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.6.0...4.0.0-beta.0) (2022-06-17)\n\n### Features\n\n- **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672))\n- **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))\n\n# [3.6.0](https://github.com/ionic-team/capacitor/compare/3.5.1...3.6.0) (2022-06-17)\n\n### Features\n\n- **android:** update support for Portals for Capacitor to include Live Updates ([#5660](https://github.com/ionic-team/capacitor/issues/5660)) ([62f0a5e](https://github.com/ionic-team/capacitor/commit/62f0a5eaa40776aad79dbf8f8c0900037d3cc97e))\n\n# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12)\n\n### Features\n\n- **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672))\n- **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))\n\n## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04)\n\n### Bug Fixes\n\n- **android:** move initialFocus on webview into config ([#5579](https://github.com/ionic-team/capacitor/issues/5579)) ([8b4e861](https://github.com/ionic-team/capacitor/commit/8b4e861514b0fbe08e9296f49c280234f54742e1))\n\n# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22)\n\n### Features\n\n- **cli:** support capacitor portals plugin changes needed ([#5558](https://github.com/ionic-team/capacitor/issues/5558)) ([6810a19](https://github.com/ionic-team/capacitor/commit/6810a19ae2bbda1f4b2afad61d37ca822ca157f5))\n\n# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04)\n\n### Bug Fixes\n\n- **cli:** avoid srcFiles is not iterable on kotlinNeededCheck ([#5481](https://github.com/ionic-team/capacitor/issues/5481)) ([3c2b579](https://github.com/ionic-team/capacitor/commit/3c2b579c6edb1fc69d85689b268eb92067b7821b))\n\n## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03)\n\n### Bug Fixes\n\n- **android:** compatibility with cordova kotlin plugins ([#5438](https://github.com/ionic-team/capacitor/issues/5438)) ([55bf004](https://github.com/ionic-team/capacitor/commit/55bf004897b3feb280ab6b6575d2a2c1a0a183e2))\n\n## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09)\n\n### Bug Fixes\n\n- **cli:** Better error on gradlew permission problems ([#5405](https://github.com/ionic-team/capacitor/issues/5405)) ([9420f08](https://github.com/ionic-team/capacitor/commit/9420f08dedad78cfaa5500cccf8bdbf1a9140684))\n\n# [3.4.0](https://github.com/ionic-team/capacitor/compare/3.3.4...3.4.0) (2022-01-19)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [3.3.4](https://github.com/ionic-team/capacitor/compare/3.3.3...3.3.4) (2022-01-05)\n\n### Bug Fixes\n\n- **cli:** Escape appName from invalid characters on add ([#5325](https://github.com/ionic-team/capacitor/issues/5325)) ([033f4ee](https://github.com/ionic-team/capacitor/commit/033f4eef59fdb7cc32018b162114511448bc46a6))\n- **cli:** sync failing if Info.plist is localized ([#5333](https://github.com/ionic-team/capacitor/issues/5333)) ([df7a104](https://github.com/ionic-team/capacitor/commit/df7a1041c4e2d9a5a1ceef247ed00f9f8467df76))\n\n## [3.3.3](https://github.com/ionic-team/capacitor/compare/3.3.2...3.3.3) (2021-12-08)\n\n### Bug Fixes\n\n- **android:** restrict android run command to configured flavour ([#5256](https://github.com/ionic-team/capacitor/issues/5256)) ([ba84443](https://github.com/ionic-team/capacitor/commit/ba84443dce9c81e09140def57a60018b527b5bb5))\n- **cli:** Add onesignal-cordova-plugin to the static list again ([#5262](https://github.com/ionic-team/capacitor/issues/5262)) ([e67ca99](https://github.com/ionic-team/capacitor/commit/e67ca9964c5a923d35f5cf41eb802c665563726f))\n\n## [3.3.2](https://github.com/ionic-team/capacitor/compare/3.3.1...3.3.2) (2021-11-17)\n\n### Bug Fixes\n\n- **cli:** add cordova-plugin-google-analytics to static list ([#5220](https://github.com/ionic-team/capacitor/issues/5220)) ([67a996c](https://github.com/ionic-team/capacitor/commit/67a996c0a6896e32c41ea01822d6435fdd706b84))\n- **cli:** Add plugin to static list if pod has use-frameworks ([#5232](https://github.com/ionic-team/capacitor/issues/5232)) ([8a0518b](https://github.com/ionic-team/capacitor/commit/8a0518be9f6f6a4be4a9f1366cb8dcb191225b9d))\n- **cli:** sync gradle from android folder ([#5233](https://github.com/ionic-team/capacitor/issues/5233)) ([cd779c4](https://github.com/ionic-team/capacitor/commit/cd779c4b6ed4ffc96777be7c94a0af4baca6d6d5))\n\n## [3.3.1](https://github.com/ionic-team/capacitor/compare/3.3.0...3.3.1) (2021-11-05)\n\n### Bug Fixes\n\n- **cli:** Make config don't error if iOS is missing ([#5212](https://github.com/ionic-team/capacitor/issues/5212)) ([db9f12b](https://github.com/ionic-team/capacitor/commit/db9f12b545994b2ed88098c0168bb051f8191771))\n\n# [3.3.0](https://github.com/ionic-team/capacitor/compare/3.2.5...3.3.0) (2021-11-03)\n\n### Bug Fixes\n\n- **cli:** Add Batch plugin to static list ([#5138](https://github.com/ionic-team/capacitor/issues/5138)) ([9470633](https://github.com/ionic-team/capacitor/commit/94706338c096b30390fa288c9b107e253923a644))\n- **cli:** Add onesignal-cordova-plugin to static pod list ([#5143](https://github.com/ionic-team/capacitor/issues/5143)) ([937e240](https://github.com/ionic-team/capacitor/commit/937e2408f9bb60691e653b70d8b7cb02f540b251))\n- **cli:** detect and register multiple plugins from same package ([#5098](https://github.com/ionic-team/capacitor/issues/5098)) ([25e770c](https://github.com/ionic-team/capacitor/commit/25e770c3f598bf3a1e05e21d607ab3ad70268674))\n\n### Features\n\n- **cli:** add support for 'pod install' in VM based environments ([#5144](https://github.com/ionic-team/capacitor/issues/5144)) ([32ecf22](https://github.com/ionic-team/capacitor/commit/32ecf22de0a550756dbfa68b3b17c2333c89a430))\n- **cli:** Allow to configure access origin tags on cordova config.xml ([#5134](https://github.com/ionic-team/capacitor/issues/5134)) ([0841a09](https://github.com/ionic-team/capacitor/commit/0841a093bf73ed4acac9a90be44a8e8a3aedbcdb))\n- **cli:** Allow users to include Cordova plugins to the static list ([#5175](https://github.com/ionic-team/capacitor/issues/5175)) ([664149a](https://github.com/ionic-team/capacitor/commit/664149aadbe80e66dd757315a826ec1ab305edb9))\n\n## [3.2.5](https://github.com/ionic-team/capacitor/compare/3.2.4...3.2.5) (2021-10-13)\n\n### Bug Fixes\n\n- **cli:** create only static pod if needed ([#5099](https://github.com/ionic-team/capacitor/issues/5099)) ([8304744](https://github.com/ionic-team/capacitor/commit/83047445562a52cc927c7c77d55b48288cfc1fcc))\n\n## [3.2.4](https://github.com/ionic-team/capacitor/compare/3.2.3...3.2.4) (2021-09-27)\n\n### Bug Fixes\n\n- **cli:** await sync on add to avoid telemetry hang ([833bc20](https://github.com/ionic-team/capacitor/commit/833bc20525a2558e03cd0e56c6765ce6828cdfac))\n\n## [3.2.3](https://github.com/ionic-team/capacitor/compare/3.2.2...3.2.3) (2021-09-15)\n\n### Bug Fixes\n\n- Add SalesforceMobileSDK-CordovaPlugin to iOS incompatible list ([#5031](https://github.com/ionic-team/capacitor/issues/5031)) ([6f3f79f](https://github.com/ionic-team/capacitor/commit/6f3f79f412b77b0c90988226ec5ade5d0198c706))\n- Define cordovaConfig gradle variable ([#5024](https://github.com/ionic-team/capacitor/issues/5024)) ([55c217e](https://github.com/ionic-team/capacitor/commit/55c217e6898d0270c23c3a7158a5102e9b84ff40))\n\n## [3.2.2](https://github.com/ionic-team/capacitor/compare/3.2.1...3.2.2) (2021-09-02)\n\n**Note:** Version bump only for package @capacitor/cli\n\n## [3.2.1](https://github.com/ionic-team/capacitor/compare/3.2.0...3.2.1) (2021-09-01)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.2.0](https://github.com/ionic-team/capacitor/compare/3.1.2...3.2.0) (2021-08-18)\n\n### Bug Fixes\n\n- **cli:** Put cordova git pod dependencies in Podfile ([#4940](https://github.com/ionic-team/capacitor/issues/4940)) ([642dbf4](https://github.com/ionic-team/capacitor/commit/642dbf433e22bb695e5f782bd685de42eb2afada))\n- **cli:** Separate Swift plugins from ObjC plugins ([#4925](https://github.com/ionic-team/capacitor/issues/4925)) ([43ce803](https://github.com/ionic-team/capacitor/commit/43ce803975ccd66823aab1e8c0d44d0ca81c6b2f))\n\n## [3.1.2](https://github.com/ionic-team/capacitor/compare/3.1.1...3.1.2) (2021-07-21)\n\n### Bug Fixes\n\n- **cli:** Don't warn about hideLogs on some commands ([#4813](https://github.com/ionic-team/capacitor/issues/4813)) ([dc279cc](https://github.com/ionic-team/capacitor/commit/dc279cc0a4ba8332296c65ca00647829f43ed1d9))\n\n## [3.1.1](https://github.com/ionic-team/capacitor/compare/3.1.0...3.1.1) (2021-07-07)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.1.0](https://github.com/ionic-team/capacitor/compare/3.0.2...3.1.0) (2021-07-07)\n\n### Bug Fixes\n\n- **cli:** Don't error if there are no scripts ([#4763](https://github.com/ionic-team/capacitor/issues/4763)) ([dec3fb2](https://github.com/ionic-team/capacitor/commit/dec3fb285239912980f2abea1cf48c583da6a163))\n\n### Features\n\n- **cli:** Add hooks to call into npm scripts when capacitor commands run ([#4739](https://github.com/ionic-team/capacitor/issues/4739)) ([515230c](https://github.com/ionic-team/capacitor/commit/515230ccefec76d4b7ed03ef1122709d1b63b58a))\n- **cli:** allow run command to use flavors ([#4782](https://github.com/ionic-team/capacitor/issues/4782)) ([05cb853](https://github.com/ionic-team/capacitor/commit/05cb8533d4479efd3dc823b18f48699302f462ba))\n- **ios:** Add limitsNavigationsToAppBoundDomains configuration option ([#4789](https://github.com/ionic-team/capacitor/issues/4789)) ([2b7016f](https://github.com/ionic-team/capacitor/commit/2b7016f3b4d62fd8c9d03fde2745b3d515bf08b2))\n\n## [3.0.2](https://github.com/ionic-team/capacitor/compare/3.0.1...3.0.2) (2021-06-23)\n\n### Bug Fixes\n\n- **cli:** correctly show EACCES error on run ([#4742](https://github.com/ionic-team/capacitor/issues/4742)) ([2ab8778](https://github.com/ionic-team/capacitor/commit/2ab877881a292bba0aed946b20c3c6bb58808e58))\n- **cli:** Don't error on ios sync on non macOS ([#4723](https://github.com/ionic-team/capacitor/issues/4723)) ([368ffad](https://github.com/ionic-team/capacitor/commit/368ffad03612841a8f228c6a174c141659f5293d))\n\n## [3.0.1](https://github.com/ionic-team/capacitor/compare/3.0.0...3.0.1) (2021-06-09)\n\n### Bug Fixes\n\n- **cli:** add priority to framework detection ([#4617](https://github.com/ionic-team/capacitor/issues/4617)) ([6a22f03](https://github.com/ionic-team/capacitor/commit/6a22f0375921fc7c015bc72e036dee014b1baed9))\n- **cli:** Better native-run error ([#4676](https://github.com/ionic-team/capacitor/issues/4676)) ([39eebd0](https://github.com/ionic-team/capacitor/commit/39eebd0a2dc45fd5d07f79fb9ad5b919556c5fc5))\n- **cli:** Don't prompt cordova preferences if non interactive ([#4680](https://github.com/ionic-team/capacitor/issues/4680)) ([293527c](https://github.com/ionic-team/capacitor/commit/293527c85296a9b79ce6dbd12e0f3e3e43fbce0b))\n- **cli:** Remove v3 prefix from docs urls ([#4596](https://github.com/ionic-team/capacitor/issues/4596)) ([f99f11a](https://github.com/ionic-team/capacitor/commit/f99f11a6ee65020a8b2e58665e3427de814fba3a))\n- **cli:** Throw error if native-run didn't find targets ([#4681](https://github.com/ionic-team/capacitor/issues/4681)) ([bfbf2b5](https://github.com/ionic-team/capacitor/commit/bfbf2b5ffb48bf1617404385f7407baefcfe3282))\n\n# [3.0.0](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.4...3.0.0) (2021-05-18)\n\n### Bug Fixes\n\n- **cli:** create-react-app framework detection should not look for react-dev-utils ([#4585](https://github.com/ionic-team/capacitor/issues/4585)) ([9f7910e](https://github.com/ionic-team/capacitor/commit/9f7910ee39ea7721d01428ec65b3d101b8ba963e))\n\n# [3.0.0-rc.4](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.3...3.0.0-rc.4) (2021-05-18)\n\n### Features\n\n- **cli:** auto detect framework's webDir ([#4550](https://github.com/ionic-team/capacitor/issues/4550)) ([329448a](https://github.com/ionic-team/capacitor/commit/329448a26846b5167b16f9169d62a9ff61eef87d))\n\n# [3.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.2...3.0.0-rc.3) (2021-05-11)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.1...3.0.0-rc.2) (2021-05-07)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.0...3.0.0-rc.1) (2021-04-29)\n\n### Bug Fixes\n\n- **cli:** Allow prereleases on node version check ([#4469](https://github.com/ionic-team/capacitor/issues/4469)) ([dd26a98](https://github.com/ionic-team/capacitor/commit/dd26a98bde9c4487178bc4ee45587f86ec53df2a))\n- **cli:** filter targets without id from run device list ([#4397](https://github.com/ionic-team/capacitor/issues/4397)) ([9ec444f](https://github.com/ionic-team/capacitor/commit/9ec444f034d435c7c945e9a20e3ca99a3b1f54d6))\n\n### Features\n\n- Unify logging behavior across environments ([#4416](https://github.com/ionic-team/capacitor/issues/4416)) ([bae0f3d](https://github.com/ionic-team/capacitor/commit/bae0f3d2cee84978636d0f589bc7e2f745671baf))\n- **cli:** Add signup prompt on first init ([#4440](https://github.com/ionic-team/capacitor/issues/4440)) ([b3faa97](https://github.com/ionic-team/capacitor/commit/b3faa97d9b0ee542a8d90f433545f7e83402c485))\n\n# [3.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.6...3.0.0-rc.0) (2021-03-10)\n\n### Bug Fixes\n\n- **cli:** Make tests work on npm 7 ([#4297](https://github.com/ionic-team/capacitor/issues/4297)) ([676c907](https://github.com/ionic-team/capacitor/commit/676c907c7ce84fdc57d00152024144a74d24b137))\n\n# [3.0.0-beta.6](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.5...3.0.0-beta.6) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-beta.5](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.4...3.0.0-beta.5) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-beta.4](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.3...3.0.0-beta.4) (2021-02-26)\n\n### Bug Fixes\n\n- **cli:** init failure if config.xml is present ([#4227](https://github.com/ionic-team/capacitor/issues/4227)) ([f1703dc](https://github.com/ionic-team/capacitor/commit/f1703dcb3ebaa83df9f2b72ca00eb6721e96dfd9))\n\n# [3.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.2...3.0.0-beta.3) (2021-02-18)\n\n### Bug Fixes\n\n- **cli:** do not error if webDir is missing when adding platforms ([#4215](https://github.com/ionic-team/capacitor/issues/4215)) ([4583add](https://github.com/ionic-team/capacitor/commit/4583add61f5f7ac60de8722664811d96d4095459))\n\n### Features\n\n- **cli:** do not require webDir when server.url is set ([#4200](https://github.com/ionic-team/capacitor/issues/4200)) ([91ddfbd](https://github.com/ionic-team/capacitor/commit/91ddfbd3cd1f598906b2ddc5cab8904420f231f6))\n\n# [3.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.1...3.0.0-beta.2) (2021-02-08)\n\n### Features\n\n- **cli:** configurable iOS build scheme ([#4073](https://github.com/ionic-team/capacitor/issues/4073)) ([e6374dc](https://github.com/ionic-team/capacitor/commit/e6374dc88c388a30186e8bfea22ce129ca1a9e02))\n- **cli:** send config, rootDir, and webDir to custom platform hooks ([#4084](https://github.com/ionic-team/capacitor/issues/4084)) ([13e9860](https://github.com/ionic-team/capacitor/commit/13e9860468126ba3c37b25d9093ab5f6cce2df2c))\n\n# [3.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.0...3.0.0-beta.1) (2021-01-14)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.14...3.0.0-beta.0) (2021-01-13)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-alpha.14](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.13...3.0.0-alpha.14) (2021-01-13)\n\n### Bug Fixes\n\n- **cli:** bump minimum node version to 12.4.0 ([#4059](https://github.com/ionic-team/capacitor/issues/4059)) ([61e3be0](https://github.com/ionic-team/capacitor/commit/61e3be0c865a3591a0d6bcfc27d0bbb72ee98395))\n- **cli:** default to new directory instead of crashing ([70fdf0b](https://github.com/ionic-team/capacitor/commit/70fdf0be0e0f06b4f20e20a3ae4bfef4de2374e9))\n\n# [3.0.0-alpha.13](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.12...3.0.0-alpha.13) (2021-01-13)\n\n### Bug Fixes\n\n- **cli:** use stderr for logs when using --json option ([#4044](https://github.com/ionic-team/capacitor/issues/4044)) ([452a101](https://github.com/ionic-team/capacitor/commit/452a101648fe6da4232d18985c5d814920505920))\n\n### Features\n\n- **cli:** create TS configuration files in `init` ([#3999](https://github.com/ionic-team/capacitor/issues/3999)) ([fa7003e](https://github.com/ionic-team/capacitor/commit/fa7003e4ef1d988633abb85b1b109c51b94fda42))\n\n# [3.0.0-alpha.12](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.11...3.0.0-alpha.12) (2021-01-08)\n\n### Bug Fixes\n\n- move `public` into iOS target directory ([#4002](https://github.com/ionic-team/capacitor/issues/4002)) ([4f41296](https://github.com/ionic-team/capacitor/commit/4f41296a109cf73fdf8e695849e95f292a543f85))\n- **cli:** run an actual debug build for iOS ([#4014](https://github.com/ionic-team/capacitor/issues/4014)) ([dc6399c](https://github.com/ionic-team/capacitor/commit/dc6399cf0b5eb6afb50274a84dc71486cd3e4173))\n\n### Features\n\n- **cli:** allow 'export default' style TS config files ([#4031](https://github.com/ionic-team/capacitor/issues/4031)) ([9393667](https://github.com/ionic-team/capacitor/commit/9393667bbe629d6c18a22b16fe3f3c6fe83e11f6))\n- **cli:** opt-in anonymous usage data ([#4022](https://github.com/ionic-team/capacitor/issues/4022)) ([3facfb7](https://github.com/ionic-team/capacitor/commit/3facfb790bff79b00ba1ab6dd8cb331989937da7))\n\n# [3.0.0-alpha.11](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.10...3.0.0-alpha.11) (2020-12-26)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-alpha.9](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.8...3.0.0-alpha.9) (2020-12-15)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-alpha.8](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.7...3.0.0-alpha.8) (2020-12-15)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-alpha.7](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.6...3.0.0-alpha.7) (2020-12-02)\n\n### Bug Fixes\n\n- **cli:** Properly detect cocoapods on multiple platforms ([#3810](https://github.com/ionic-team/capacitor/issues/3810)) ([8753694](https://github.com/ionic-team/capacitor/commit/8753694b12033feb01c82bd5985dce2584bae80c))\n- **cli:** run sync instead of copy during run ([#3816](https://github.com/ionic-team/capacitor/issues/3816)) ([ff45340](https://github.com/ionic-team/capacitor/commit/ff4534064c7f47331721d086889a42a97cf30945))\n- **cli:** use correct path for native-run ([02cf1ba](https://github.com/ionic-team/capacitor/commit/02cf1ba4b7d1c419551b6494f08cb90553fd93be))\n\n### Features\n\n- automatically import Android plugins ([#3788](https://github.com/ionic-team/capacitor/issues/3788)) ([aa1e1c6](https://github.com/ionic-team/capacitor/commit/aa1e1c604e260cc8babb0e7f5230f692bdcf6f09))\n- **cli:** add --no-sync option to run ([#3819](https://github.com/ionic-team/capacitor/issues/3819)) ([8def829](https://github.com/ionic-team/capacitor/commit/8def8290bb182436204380fc711d84fa36c17004))\n- **cli:** add config command ([#3817](https://github.com/ionic-team/capacitor/issues/3817)) ([d3d7f89](https://github.com/ionic-team/capacitor/commit/d3d7f893179a170017a36dd76e9ed7bd374a22b3))\n- **cli:** Add configurable pod path ([#3811](https://github.com/ionic-team/capacitor/issues/3811)) ([88f9187](https://github.com/ionic-team/capacitor/commit/88f9187f41f95ef62f6bf854c63f0b1c91dbc2f7))\n- **cli:** extendable plugin configuration types ([#3858](https://github.com/ionic-team/capacitor/issues/3858)) ([f789526](https://github.com/ionic-team/capacitor/commit/f789526e42283c2a166ff32a2b16b70e65b94ba4))\n- **cli:** locate plugins by allowlist ([#3762](https://github.com/ionic-team/capacitor/issues/3762)) ([81963b6](https://github.com/ionic-team/capacitor/commit/81963b615ccbdc8993d0befbefe87173db4ba108))\n- **cli:** STUDIO_PATH environment variable ([#3755](https://github.com/ionic-team/capacitor/issues/3755)) ([65cef53](https://github.com/ionic-team/capacitor/commit/65cef53277444c173ff928dc4ef196f0dae4c8a7))\n- **cli:** support TS/JS config files ([#3756](https://github.com/ionic-team/capacitor/issues/3756)) ([a52775f](https://github.com/ionic-team/capacitor/commit/a52775fb635a5cbcccf2dcaa955ad12804ad5986))\n\n# [3.0.0-alpha.6](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.5...3.0.0-alpha.6) (2020-10-30)\n\n### Features\n\n- **cli:** add `capacitor run` command ([#3599](https://github.com/ionic-team/capacitor/issues/3599)) ([8576b0f](https://github.com/ionic-team/capacitor/commit/8576b0ff6a048981a07cab91135a0071b724e043))\n\n# [3.0.0-alpha.5](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.4...3.0.0-alpha.5) (2020-10-06)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.3...3.0.0-alpha.4) (2020-09-23)\n\n### Features\n\n- **cli:** ability to specify custom platform directories ([#3565](https://github.com/ionic-team/capacitor/issues/3565)) ([c6eda55](https://github.com/ionic-team/capacitor/commit/c6eda55482ef56abdfe9a33444e828b771af9386))\n\n# [3.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.2...3.0.0-alpha.3) (2020-09-15)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.1...3.0.0-alpha.2) (2020-08-31)\n\n**Note:** Version bump only for package @capacitor/cli\n\n# [3.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/2.4.0...3.0.0-alpha.1) (2020-08-21)\n\n### Features\n\n- **cli:** add fmt script to plugin template ([#3354](https://github.com/ionic-team/capacitor/issues/3354)) ([9ca1e72](https://github.com/ionic-team/capacitor/commit/9ca1e723334f5d21706a8586c11d73162b47a13a))\n\n# [3.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/2.3.0...3.0.0-alpha.0) (2020-07-23)\n\n### Features\n\n- **core:** add `registerPlugin` for importing from plugin packages ([#3305](https://github.com/ionic-team/capacitor/issues/3305)) ([95475cc](https://github.com/ionic-team/capacitor/commit/95475cceb4cbd5be2cc7e18f2cf3045eb6c6f7fd))\n"
  },
  {
    "path": "cli/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017-present Drifty Co.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "cli/README.md",
    "content": "# Capacitor CLI\n\nThe Capacitor command-line interface (CLI) is a tool for creating and managing Capacitor applications. While it can be installed globally, it's recommended to install it locally in your project and execute through `npm` scripts.\n\n## Installation\n\n### Project Installation (Recommended)\n\nInstall the CLI locally in your project:\n\n```bash\nnpm install @capacitor/cli --save-dev\n```\n\n### Global Installation\n\nWhile not recommended for project use, you can install the CLI globally:\n\n```bash\nnpm install -g @capacitor/cli\n```\n\n## Using Capacitor CLI\n\nThe CLI can be used through the `capacitor` or `cap` command. When installed locally, use it through your project's `npm` scripts or `npx`.\n\nCommon commands:\n\n- `cap init`: Initialize a new Capacitor project\n- `cap add`: Add a native platform (ios, android)\n- `cap sync`: Sync your web code to your native projects\n\nFor detailed information, consult the [Getting Started guide](https://capacitorjs.com/docs/getting-started).\n\n## Local Development\n\nIf you're contributing to the Capacitor CLI or testing local changes:\n\n1. Clone and setup:\n\n   ```bash\n   git clone https://github.com/ionic-team/capacitor.git\n   cd cli\n   npm install\n   ```\n\n2. Build the CLI:\n\n   ```bash\n   npm run build\n   ```\n\n3. Create a local link:\n\n   ```bash\n   npm link\n   ```\n\n4. Development workflow:\n   - Run `npm run watch` to automatically rebuild on changes\n   - Use `capacitor` or `cap` commands to test your changes\n   - Run `npm test` to execute the test suite\n\n## Debugging\n\n### Using VS Code Launch Configurations\n\nThe CLI includes VS Code launch configurations for debugging. To debug a CLI command:\n\n1. Open the project in VS Code\n2. Right now we don't have debugging working in the ts files, so select one of the .js files inside of /dist/\\*\\*.js\n3. Place a breakpoint\n4. Press F5 or go to Run > Start Debugging\n5. Select a launch config and run filling out the path you want to run the cli in, and the command that you want run.\n\nYou can add more configurations by copying and modifying the existing ones in `.vscode/launch.json`.\n\n## Contributing\n\nContributions are welcome! Please read our [Contributing Guide](https://github.com/ionic-team/capacitor/blob/main/CONTRIBUTING.md) for details.\n\n### License\n\n- [MIT](https://github.com/ionic-team/capacitor/blob/HEAD/LICENSE)\n"
  },
  {
    "path": "cli/bin/capacitor",
    "content": "#!/usr/bin/env node\n'use strict';\n\nvar satisfies = require('semver/functions/satisfies');\nvar packageJson = require('../package.json');\nvar requiresNodeVersion = packageJson.engines.node;\n\nif (!satisfies(process.version, requiresNodeVersion, { includePrerelease: true})) {\n  process.stdout.write(\n    '\\x1b[31m[fatal]\\x1b[39m The Capacitor CLI requires NodeJS ' + requiresNodeVersion + '\\n' +\n    '        Please install the latest LTS version.\\n'\n  );\n\n  process.exit(1);\n}\n\nvar cli = require('../dist/index');\ncli.run();\n"
  },
  {
    "path": "cli/package.json",
    "content": "{\n  \"name\": \"@capacitor/cli\",\n  \"version\": \"8.2.0\",\n  \"description\": \"Capacitor: Cross-platform apps with JavaScript and the web\",\n  \"homepage\": \"https://capacitorjs.com\",\n  \"author\": \"Ionic Team <hi@ionic.io> (https://ionic.io)\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/ionic-team/capacitor.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/ionic-team/capacitor/issues\"\n  },\n  \"files\": [\n    \"assets/\",\n    \"bin/\",\n    \"dist/**/*.js\",\n    \"dist/declarations.d.ts\"\n  ],\n  \"keywords\": [\n    \"ionic\",\n    \"ionic framework\",\n    \"capacitor\",\n    \"universal app\",\n    \"progressive web apps\",\n    \"cross platform\"\n  ],\n  \"engines\": {\n    \"node\": \">=22.0.0\"\n  },\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/declarations.d.ts\",\n  \"bin\": {\n    \"capacitor\": \"./bin/capacitor\",\n    \"cap\": \"./bin/capacitor\"\n  },\n  \"scripts\": {\n    \"build\": \"npm run clean && npm run assets && tsc\",\n    \"clean\": \"rimraf ./dist\",\n    \"assets\": \"node ../scripts/pack-cli-assets.mjs\",\n    \"prepublishOnly\": \"npm run build\",\n    \"test\": \"jest -i\",\n    \"watch\": \"npm run assets && tsc -w\"\n  },\n  \"dependencies\": {\n    \"@ionic/cli-framework-output\": \"^2.2.8\",\n    \"@ionic/utils-subprocess\": \"^3.0.1\",\n    \"@ionic/utils-terminal\": \"^2.3.5\",\n    \"commander\": \"^12.1.0\",\n    \"debug\": \"^4.4.0\",\n    \"env-paths\": \"^2.2.0\",\n    \"fs-extra\": \"^11.2.0\",\n    \"kleur\": \"^4.1.5\",\n    \"native-run\": \"^2.0.3\",\n    \"open\": \"^8.4.0\",\n    \"plist\": \"^3.1.0\",\n    \"prompts\": \"^2.4.2\",\n    \"rimraf\": \"^6.0.1\",\n    \"semver\": \"^7.6.3\",\n    \"tar\": \"^7.5.3\",\n    \"tslib\": \"^2.8.1\",\n    \"xml2js\": \"^0.6.2\"\n  },\n  \"devDependencies\": {\n    \"@types/debug\": \"^4.1.12\",\n    \"@types/fs-extra\": \"^11.0.4\",\n    \"@types/jest\": \"^29.5.14\",\n    \"@types/plist\": \"^3.0.5\",\n    \"@types/prompts\": \"^2.4.9\",\n    \"@types/semver\": \"^7.5.8\",\n    \"@types/tmp\": \"^0.2.6\",\n    \"@types/xml2js\": \"0.4.5\",\n    \"jest\": \"^29.7.0\",\n    \"jest-environment-jsdom\": \"^29.7.0\",\n    \"jest-jasmine2\": \"^29.7.0\",\n    \"tmp\": \"^0.2.3\",\n    \"ts-jest\": \"^29.0.5\",\n    \"typescript\": \"~5.0.2\"\n  },\n  \"jest\": {\n    \"preset\": \"ts-jest\",\n    \"testRunner\": \"jest-jasmine2\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  }\n}\n"
  },
  {
    "path": "cli/src/android/add.ts",
    "content": "import { pathExists, writeFile } from 'fs-extra';\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nimport c from '../colors';\nimport { runTask } from '../common';\nimport type { Config } from '../definitions';\nimport { runCommand } from '../util/subprocess';\nimport { extractTemplate } from '../util/template';\n\nexport async function addAndroid(config: Config): Promise<void> {\n  await runTask(`Adding native android project in ${c.strong(config.android.platformDir)}`, async () => {\n    return extractTemplate(config.cli.assets.android.platformTemplateArchiveAbs, config.android.platformDirAbs);\n  });\n}\n\nexport async function createLocalProperties(platformDir: string): Promise<void> {\n  const defaultAndroidPath = join(homedir(), 'Library/Android/sdk');\n  if (await pathExists(defaultAndroidPath)) {\n    const localSettings = `\n## This file is automatically generated by Android Studio.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file should *NOT* be checked into Version Control Systems,\n# as it contains information specific to your local configuration.\n#\n# Location of the SDK. This is only used by Gradle.\n# For customization when using a Version Control System, please read the\n# header note.\nsdk.dir=${defaultAndroidPath}\n  `;\n    await writeFile(join(platformDir, 'local.properties'), localSettings, {\n      encoding: 'utf-8',\n    });\n\n    // Only sync if we were able to create the local properties above, otherwise\n    // this will fail\n    try {\n      await gradleSync(platformDir);\n    } catch (e) {\n      console.error('Error running gradle sync', e);\n      console.error(\n        'Unable to infer default Android SDK settings. This is fine, just run npx cap open android and import and sync gradle manually',\n      );\n    }\n  }\n}\n\nasync function gradleSync(platformDir: string) {\n  await runCommand(`./gradlew`, [], {\n    cwd: platformDir,\n  });\n}\n"
  },
  {
    "path": "cli/src/android/build.ts",
    "content": "import { join } from 'path';\n\nimport c from '../colors';\nimport { runTask } from '../common';\nimport type { Config } from '../definitions';\nimport { logSuccess } from '../log';\nimport type { BuildCommandOptions } from '../tasks/build';\nimport { runCommand } from '../util/subprocess';\n\nexport async function buildAndroid(config: Config, buildOptions: BuildCommandOptions): Promise<void> {\n  const releaseType = buildOptions.androidreleasetype ?? 'AAB';\n  const releaseTypeIsAAB = releaseType === 'AAB';\n  const flavor = buildOptions.flavor ?? '';\n  const arg = releaseTypeIsAAB ? `:app:bundle${flavor}Release` : `assemble${flavor}Release`;\n  const gradleArgs = [arg];\n\n  try {\n    await runTask('Running Gradle build', async () =>\n      runCommand('./gradlew', gradleArgs, {\n        cwd: config.android.platformDirAbs,\n      }),\n    );\n  } catch (e) {\n    if ((e as any).includes('EACCES')) {\n      throw `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${c.strong(\n        `chmod +x ./${config.android.platformDir}/gradlew`,\n      )} and try again.`;\n    } else {\n      throw e;\n    }\n  }\n\n  const releaseDir = releaseTypeIsAAB\n    ? flavor !== ''\n      ? `${flavor}Release`\n      : 'release'\n    : flavor !== ''\n      ? join(flavor, 'release')\n      : 'release';\n\n  const releasePath = join(\n    config.android.appDirAbs,\n    'build',\n    'outputs',\n    releaseTypeIsAAB ? 'bundle' : 'apk',\n    releaseDir,\n  );\n\n  const unsignedReleaseName = `app${flavor !== '' ? `-${flavor}` : ''}-release${\n    releaseTypeIsAAB ? '' : '-unsigned'\n  }.${releaseType.toLowerCase()}`;\n\n  const signedReleaseName = unsignedReleaseName.replace(\n    `-release${releaseTypeIsAAB ? '' : '-unsigned'}.${releaseType.toLowerCase()}`,\n    `-release-signed.${releaseType.toLowerCase()}`,\n  );\n\n  if (buildOptions.signingtype == 'jarsigner') {\n    await signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);\n  } else {\n    await signWithApkSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);\n  }\n\n  logSuccess(`Successfully generated ${signedReleaseName} at: ${releasePath}`);\n}\n\nasync function signWithApkSigner(\n  config: Config,\n  buildOptions: BuildCommandOptions,\n  releasePath: string,\n  signedReleaseName: string,\n  unsignedReleaseName: string,\n) {\n  if (!buildOptions.keystorepath || !buildOptions.keystorepass) {\n    throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password)';\n  }\n\n  const signingArgs = [\n    'sign',\n    '--ks',\n    buildOptions.keystorepath,\n    '--ks-pass',\n    `pass:${buildOptions.keystorepass}`,\n    '--in',\n    `${join(releasePath, unsignedReleaseName)}`,\n    '--out',\n    `${join(releasePath, signedReleaseName)}`,\n  ];\n\n  if (buildOptions.keystorealias) {\n    signingArgs.push('--ks-key-alias', buildOptions.keystorealias);\n  }\n\n  if (buildOptions.keystorealiaspass) {\n    signingArgs.push('--key-pass', `pass:${buildOptions.keystorealiaspass}`);\n  }\n\n  await runTask('Signing Release', async () => {\n    await runCommand('apksigner', signingArgs, {\n      cwd: config.android.platformDirAbs,\n    });\n  });\n}\n\nasync function signWithJarSigner(\n  config: Config,\n  buildOptions: BuildCommandOptions,\n  releasePath: string,\n  signedReleaseName: string,\n  unsignedReleaseName: string,\n) {\n  if (\n    !buildOptions.keystorepath ||\n    !buildOptions.keystorealias ||\n    !buildOptions.keystorealiaspass ||\n    !buildOptions.keystorepass\n  ) {\n    throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password, Keystore Key Alias, Keystore Key Password)';\n  }\n\n  const signingArgs = [\n    '-sigalg',\n    'SHA1withRSA',\n    '-digestalg',\n    'SHA1',\n    '-keystore',\n    buildOptions.keystorepath,\n    '-keypass',\n    buildOptions.keystorealiaspass,\n    '-storepass',\n    buildOptions.keystorepass,\n    `-signedjar`,\n    `${join(releasePath, signedReleaseName)}`,\n    `${join(releasePath, unsignedReleaseName)}`,\n    buildOptions.keystorealias,\n  ];\n\n  await runTask('Signing Release', async () => {\n    await runCommand('jarsigner', signingArgs, {\n      cwd: config.android.platformDirAbs,\n    });\n  });\n}\n"
  },
  {
    "path": "cli/src/android/common.ts",
    "content": "import { copy, remove, mkdirp, readFile, pathExists, writeFile } from 'fs-extra';\nimport { join, resolve } from 'path';\n\nimport { checkCapacitorPlatform } from '../common';\nimport { getIncompatibleCordovaPlugins } from '../cordova';\nimport type { Config } from '../definitions';\nimport { PluginType, getPluginPlatform } from '../plugin';\nimport type { Plugin } from '../plugin';\nimport { convertToUnixPath } from '../util/fs';\n\nexport async function checkAndroidPackage(config: Config): Promise<string | null> {\n  return checkCapacitorPlatform(config, 'android');\n}\n\nexport async function getAndroidPlugins(allPlugins: Plugin[]): Promise<Plugin[]> {\n  const resolved = await Promise.all(allPlugins.map(async (plugin) => await resolvePlugin(plugin)));\n  return resolved.filter((plugin): plugin is Plugin => !!plugin);\n}\n\nexport async function resolvePlugin(plugin: Plugin): Promise<Plugin | null> {\n  const platform = 'android';\n  if (plugin.manifest?.android) {\n    let pluginFilesPath = plugin.manifest.android.src ? plugin.manifest.android.src : platform;\n    const absolutePath = join(plugin.rootPath, pluginFilesPath, plugin.id);\n    // Android folder shouldn't have subfolders, but they used to, so search for them for compatibility reasons\n    if (await pathExists(absolutePath)) {\n      pluginFilesPath = join(platform, plugin.id);\n    }\n    plugin.android = {\n      type: PluginType.Core,\n      path: convertToUnixPath(pluginFilesPath),\n    };\n  } else if (plugin.xml) {\n    plugin.android = {\n      type: PluginType.Cordova,\n      path: 'src/' + platform,\n    };\n    if (getIncompatibleCordovaPlugins(platform).includes(plugin.id) || !getPluginPlatform(plugin, platform)) {\n      plugin.android.type = PluginType.Incompatible;\n    }\n  } else {\n    return null;\n  }\n  return plugin;\n}\n\n/**\n * Update an Android project with the desired app name and appId.\n * This is a little trickier for Android because the appId becomes\n * the package name.\n */\nexport async function editProjectSettingsAndroid(config: Config): Promise<void> {\n  const appId = config.app.appId;\n  const appName = config.app.appName\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/\"/g, '\\\\\"')\n    .replace(/'/g, \"\\\\'\");\n\n  const buildGradlePath = resolve(config.android.appDirAbs, 'build.gradle');\n\n  const domainPath = appId.split('.').join('/');\n  // Make the package source path to the new plugin Java file\n  const newJavaPath = resolve(config.android.srcMainDirAbs, `java/${domainPath}`);\n\n  if (!(await pathExists(newJavaPath))) {\n    await mkdirp(newJavaPath);\n  }\n\n  await copy(\n    resolve(config.android.srcMainDirAbs, 'java/com/getcapacitor/myapp/MainActivity.java'),\n    resolve(newJavaPath, 'MainActivity.java'),\n  );\n\n  if (appId.split('.')[1] !== 'getcapacitor') {\n    await remove(resolve(config.android.srcMainDirAbs, 'java/com/getcapacitor'));\n  }\n\n  // Remove our template 'com' folder if their ID doesn't have it\n  if (appId.split('.')[0] !== 'com') {\n    await remove(resolve(config.android.srcMainDirAbs, 'java/com/'));\n  }\n\n  // Update the package in the MainActivity java file\n  const activityPath = resolve(newJavaPath, 'MainActivity.java');\n  let activityContent = await readFile(activityPath, { encoding: 'utf-8' });\n\n  activityContent = activityContent.replace(/package ([^;]*)/, `package ${appId}`);\n  await writeFile(activityPath, activityContent, { encoding: 'utf-8' });\n\n  // Update the applicationId in build.gradle\n  let gradleContent = await readFile(buildGradlePath, { encoding: 'utf-8' });\n  gradleContent = gradleContent.replace(/applicationId \"[^\"]+\"/, `applicationId \"${appId}\"`);\n  // Update the namespace in build.gradle\n  gradleContent = gradleContent.replace(/namespace = \"[^\"]+\"/, `namespace = \"${appId}\"`);\n\n  await writeFile(buildGradlePath, gradleContent, { encoding: 'utf-8' });\n\n  // Update the settings in res/values/strings.xml\n  const stringsPath = resolve(config.android.resDirAbs, 'values/strings.xml');\n  let stringsContent = await readFile(stringsPath, { encoding: 'utf-8' });\n  stringsContent = stringsContent.replace(/com.getcapacitor.myapp/g, appId);\n  stringsContent = stringsContent.replace(/My App/g, appName);\n\n  await writeFile(stringsPath, stringsContent);\n}\n"
  },
  {
    "path": "cli/src/android/doctor.ts",
    "content": "import { pathExists, readFile } from 'fs-extra';\nimport { join, extname, parse } from 'path';\n\nimport c from '../colors';\nimport { check } from '../common';\nimport type { Config } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { logSuccess } from '../log';\nimport { readdirp } from '../util/fs';\nimport { readXML } from '../util/xml';\n\nexport async function doctorAndroid(config: Config): Promise<void> {\n  try {\n    await check([checkAndroidInstalled, () => checkGradlew(config), () => checkAppSrcDirs(config)]);\n    logSuccess('Android looking great! 👌');\n  } catch (e: any) {\n    if (!isFatal(e)) {\n      fatal(e.stack ?? e);\n    }\n\n    throw e;\n  }\n}\n\nasync function checkAppSrcDirs(config: Config): Promise<string | null> {\n  if (!(await pathExists(config.android.appDirAbs))) {\n    return `${c.strong(config.android.appDir)} directory is missing in ${c.strong(config.android.platformDir)}`;\n  }\n\n  if (!(await pathExists(config.android.srcMainDirAbs))) {\n    return `${c.strong(config.android.srcMainDir)} directory is missing in ${c.strong(config.android.platformDir)}`;\n  }\n\n  if (!(await pathExists(config.android.assetsDirAbs))) {\n    return `${c.strong(config.android.assetsDir)} directory is missing in ${c.strong(config.android.platformDir)}`;\n  }\n\n  if (!(await pathExists(config.android.webDirAbs))) {\n    return `${c.strong(config.android.webDir)} directory is missing in ${c.strong(config.android.platformDir)}`;\n  }\n\n  const appSrcMainAssetsWwwIndexHtmlDir = join(config.android.webDirAbs, 'index.html');\n  if (!(await pathExists(appSrcMainAssetsWwwIndexHtmlDir))) {\n    return `${c.strong('index.html')} file is missing in ${c.strong(config.android.webDirAbs)}`;\n  }\n\n  return checkAndroidManifestFile(config);\n}\n\nasync function checkAndroidManifestFile(config: Config): Promise<string | null> {\n  const manifestFileName = 'AndroidManifest.xml';\n  const manifestFilePath = join(config.android.srcMainDirAbs, manifestFileName);\n\n  if (!(await pathExists(manifestFilePath))) {\n    return `${c.strong(manifestFileName)} is missing in ${c.strong(config.android.srcMainDir)}`;\n  }\n\n  try {\n    const xmlData = await readXML(manifestFilePath);\n    return checkAndroidManifestData(config, xmlData);\n  } catch (e: any) {\n    return e;\n  }\n}\n\nasync function checkAndroidManifestData(config: Config, xmlData: any): Promise<string | null> {\n  const manifestNode: any = xmlData.manifest;\n  if (!manifestNode) {\n    return `Missing ${c.input('<manifest>')} XML node in ${c.strong(config.android.srcMainDir)}`;\n  }\n\n  const applicationChildNodes: any[] = manifestNode.application;\n  if (!Array.isArray(manifestNode.application)) {\n    return `Missing ${c.input('<application>')} XML node as a child node of ${c.input('<manifest>')} in ${c.strong(\n      config.android.srcMainDir,\n    )}`;\n  }\n\n  let mainActivityClassPath = '';\n\n  const mainApplicationNode = applicationChildNodes.find((applicationChildNode) => {\n    const activityChildNodes: any[] = applicationChildNode.activity;\n    if (!Array.isArray(activityChildNodes)) {\n      return false;\n    }\n\n    const mainActivityNode = activityChildNodes.find((activityChildNode) => {\n      const intentFilterChildNodes: any[] = activityChildNode['intent-filter'];\n      if (!Array.isArray(intentFilterChildNodes)) {\n        return false;\n      }\n\n      return intentFilterChildNodes.find((intentFilterChildNode) => {\n        const actionChildNodes: any[] = intentFilterChildNode.action;\n        if (!Array.isArray(actionChildNodes)) {\n          return false;\n        }\n\n        const mainActionChildNode = actionChildNodes.find((actionChildNode) => {\n          const androidName = actionChildNode.$['android:name'];\n          return androidName === 'android.intent.action.MAIN';\n        });\n\n        if (!mainActionChildNode) {\n          return false;\n        }\n\n        const categoryChildNodes: any[] = intentFilterChildNode.category;\n        if (!Array.isArray(categoryChildNodes)) {\n          return false;\n        }\n\n        return categoryChildNodes.find((categoryChildNode) => {\n          const androidName = categoryChildNode.$['android:name'];\n          return androidName === 'android.intent.category.LAUNCHER';\n        });\n      });\n    });\n\n    if (mainActivityNode) {\n      mainActivityClassPath = mainActivityNode.$['android:name'];\n    }\n\n    return mainActivityNode;\n  });\n\n  if (!mainApplicationNode) {\n    return `Missing main ${c.input('<activity>')} XML node in ${c.strong(config.android.srcMainDir)}`;\n  }\n\n  if (!mainActivityClassPath) {\n    return `Missing ${c.input('<activity android:name=\"\">')} attribute for MainActivity class in ${c.strong(\n      config.android.srcMainDir,\n    )}`;\n  }\n\n  return checkPackage(config, mainActivityClassPath);\n}\n\nasync function checkPackage(config: Config, mainActivityClassPath: string) {\n  const appSrcMainJavaDir = join(config.android.srcMainDirAbs, 'java');\n  if (!(await pathExists(appSrcMainJavaDir))) {\n    return `${c.strong('java')} directory is missing in ${c.strong(appSrcMainJavaDir)}`;\n  }\n\n  const mainActivityClassName: any = mainActivityClassPath.split('.').pop();\n\n  const srcFiles = await readdirp(appSrcMainJavaDir, {\n    filter: (entry) =>\n      !entry.stats.isDirectory() &&\n      ['.java', '.kt'].includes(extname(entry.path)) &&\n      mainActivityClassName === parse(entry.path).name,\n  });\n\n  if (srcFiles.length == 0) {\n    return `Main activity file (${mainActivityClassName}) is missing`;\n  }\n\n  return checkBuildGradle(config);\n}\n\nasync function checkBuildGradle(config: Config) {\n  const fileName = 'build.gradle';\n  const filePath = join(config.android.appDirAbs, fileName);\n\n  if (!(await pathExists(filePath))) {\n    return `${c.strong(fileName)} file is missing in ${c.strong(config.android.appDir)}`;\n  }\n\n  let fileContent = await readFile(filePath, { encoding: 'utf-8' });\n\n  fileContent = fileContent.replace(/'|\"/g, '').replace(/\\s+/g, ' ');\n\n  const searchFor = `applicationId`;\n\n  if (fileContent.indexOf(searchFor) === -1) {\n    return `${c.strong('build.gradle')} file missing ${c.input(`applicationId`)} config in ${filePath}`;\n  }\n\n  return null;\n}\n\nasync function checkGradlew(config: Config) {\n  const fileName = 'gradlew';\n  const filePath = join(config.android.platformDirAbs, fileName);\n\n  if (!(await pathExists(filePath))) {\n    return `${c.strong(fileName)} file is missing in ${c.strong(config.android.platformDir)}`;\n  }\n  return null;\n}\n\nasync function checkAndroidInstalled() {\n  /*\n  if (!await isInstalled('android')) {\n    return 'Android is not installed. For information: https://developer.android.com/studio/index.html';\n  }\n  */\n  return null;\n}\n"
  },
  {
    "path": "cli/src/android/open.ts",
    "content": "import Debug from 'debug';\nimport { pathExists } from 'fs-extra';\nimport open from 'open';\n\nimport c from '../colors';\nimport type { Config } from '../definitions';\nimport { logger } from '../log';\n\nconst debug = Debug('capacitor:android:open');\n\nexport async function openAndroid(config: Config): Promise<void> {\n  const androidStudioPath = await config.android.studioPath;\n  const dir = config.android.platformDirAbs;\n\n  try {\n    if (!(await pathExists(androidStudioPath))) {\n      throw new Error(`Android Studio does not exist at: ${androidStudioPath}`);\n    }\n\n    await open(dir, { app: { name: androidStudioPath }, wait: false });\n    logger.info(`Opening Android project at: ${c.strong(config.android.platformDir)}.`);\n  } catch (e) {\n    debug('Error opening Android Studio: %O', e);\n\n    logger.error(\n      'Unable to launch Android Studio. Is it installed?\\n' +\n        `Attempted to open Android Studio at: ${c.strong(androidStudioPath)}\\n` +\n        `You can configure this with the ${c.input('CAPACITOR_ANDROID_STUDIO_PATH')} environment variable.`,\n    );\n  }\n}\n"
  },
  {
    "path": "cli/src/android/run.ts",
    "content": "import Debug from 'debug';\nimport { resolve } from 'path';\n\nimport c from '../colors';\nimport { parseApkNameFromFlavor, promptForPlatformTarget, runTask } from '../common';\nimport type { Config } from '../definitions';\nimport type { RunCommandOptions } from '../tasks/run';\nimport { runNativeRun, getPlatformTargets } from '../util/native-run';\nimport { runCommand } from '../util/subprocess';\n\nconst debug = Debug('capacitor:android:run');\n\nexport async function runAndroid(\n  config: Config,\n  {\n    target: selectedTarget,\n    targetName: selectedTargetName,\n    targetNameSdkVersion: selectedTargetSdkVersion,\n    flavor: selectedFlavor,\n    forwardPorts: selectedPorts,\n  }: RunCommandOptions,\n): Promise<void> {\n  const target = await promptForPlatformTarget(\n    await getPlatformTargets('android'),\n    selectedTarget ?? selectedTargetName,\n    selectedTargetSdkVersion,\n    selectedTargetName !== undefined,\n  );\n\n  const runFlavor = selectedFlavor || config.android?.flavor || '';\n\n  const arg = `assemble${runFlavor}Debug`;\n  const gradleArgs = [arg];\n\n  debug('Invoking ./gradlew with args: %O', gradleArgs);\n\n  try {\n    await runTask('Running Gradle build', async () =>\n      runCommand('./gradlew', gradleArgs, {\n        cwd: config.android.platformDirAbs,\n      }),\n    );\n  } catch (e: any) {\n    if (e.includes('EACCES')) {\n      throw `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${c.strong(\n        `chmod +x ./${config.android.platformDir}/gradlew`,\n      )} and try again.`;\n    } else {\n      throw e;\n    }\n  }\n\n  const pathToApk = `${config.android.platformDirAbs}/${\n    config.android.appDir\n  }/build/outputs/apk${runFlavor !== '' ? '/' + runFlavor : ''}/debug`;\n\n  const apkName = parseApkNameFromFlavor(runFlavor);\n  const apkPath = resolve(pathToApk, apkName);\n\n  const nativeRunArgs = ['android', '--app', apkPath, '--target', target.id];\n\n  if (selectedPorts) {\n    nativeRunArgs.push('--forward', `${selectedPorts}`);\n  }\n\n  debug('Invoking native-run with args: %O', nativeRunArgs);\n\n  await runTask(`Deploying ${c.strong(apkName)} to ${c.input(target.id)}`, async () => runNativeRun(nativeRunArgs));\n}\n"
  },
  {
    "path": "cli/src/android/update.ts",
    "content": "import Debug from 'debug';\nimport { copy, remove, pathExists, readFile, writeFile, writeJSON } from 'fs-extra';\nimport { dirname, extname, join, relative, resolve } from 'path';\n\nimport c from '../colors';\nimport { checkPlatformVersions, runTask } from '../common';\nimport { checkPluginDependencies, handleCordovaPluginsJS, writeCordovaAndroidManifest } from '../cordova';\nimport type { Config } from '../definitions';\nimport { fatal } from '../errors';\nimport {\n  PluginType,\n  getAllElements,\n  getFilePath,\n  getPlatformElement,\n  getPluginPlatform,\n  getPluginType,\n  getPlugins,\n  printPlugins,\n} from '../plugin';\nimport type { Plugin } from '../plugin';\nimport { copy as copyTask } from '../tasks/copy';\nimport { readdirp, convertToUnixPath } from '../util/fs';\nimport { resolveNode } from '../util/node';\nimport { extractTemplate } from '../util/template';\n\nimport { getAndroidPlugins } from './common';\n\nconst platform = 'android';\nconst debug = Debug('capacitor:android:update');\n\nexport async function updateAndroid(config: Config): Promise<void> {\n  const plugins = await getPluginsTask(config);\n\n  const capacitorPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Core);\n\n  printPlugins(capacitorPlugins, 'android');\n\n  await writePluginsJson(config, capacitorPlugins);\n  await removePluginsNativeFiles(config);\n  const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova);\n  if (cordovaPlugins.length > 0) {\n    await copyPluginsNativeFiles(config, cordovaPlugins);\n  }\n  if (!(await pathExists(config.android.webDirAbs))) {\n    await copyTask(config, platform);\n  }\n  await handleCordovaPluginsJS(cordovaPlugins, config, platform);\n  await checkPluginDependencies(plugins, platform, config.app.extConfig.cordova?.failOnUninstalledPlugins);\n  await installGradlePlugins(config, capacitorPlugins, cordovaPlugins);\n  await handleCordovaPluginsGradle(config, cordovaPlugins);\n  await writeCordovaAndroidManifest(cordovaPlugins, config, platform);\n\n  const incompatibleCordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Incompatible);\n  printPlugins(incompatibleCordovaPlugins, platform, 'incompatible');\n  await checkPlatformVersions(config, platform);\n}\n\nfunction getGradlePackageName(id: string): string {\n  return id.replace('@', '').replace('/', '-');\n}\n\ninterface PluginsJsonEntry {\n  pkg: string;\n  classpath: string;\n}\n\nasync function writePluginsJson(config: Config, plugins: Plugin[]): Promise<void> {\n  const classes = await findAndroidPluginClasses(plugins);\n  const pluginsJsonPath = resolve(config.android.assetsDirAbs, 'capacitor.plugins.json');\n\n  await writeJSON(pluginsJsonPath, classes, { spaces: '\\t' });\n}\n\nasync function findAndroidPluginClasses(plugins: Plugin[]): Promise<PluginsJsonEntry[]> {\n  const entries: PluginsJsonEntry[] = [];\n\n  for (const plugin of plugins) {\n    entries.push(...(await findAndroidPluginClassesInPlugin(plugin)));\n  }\n\n  return entries;\n}\n\nasync function findAndroidPluginClassesInPlugin(plugin: Plugin): Promise<PluginsJsonEntry[]> {\n  if (!plugin.android || getPluginType(plugin, platform) !== PluginType.Core) {\n    return [];\n  }\n\n  const srcPath = resolve(plugin.rootPath, plugin.android.path, 'src/main');\n  const srcFiles = await readdirp(srcPath, {\n    filter: (entry) => !entry.stats.isDirectory() && ['.java', '.kt'].includes(extname(entry.path)),\n  });\n\n  const classRegex = /^@(?:CapacitorPlugin|NativePlugin)[\\s\\S]+?class ([\\w]+)/gm;\n  const packageRegex = /^package ([\\w.]+);?$/gm;\n\n  debug('Searching %O source files in %O by %O regex', srcFiles.length, srcPath, classRegex);\n\n  const entries = await Promise.all(\n    srcFiles.map(async (srcFile): Promise<PluginsJsonEntry | undefined> => {\n      const srcFileContents = await readFile(srcFile, { encoding: 'utf-8' });\n      classRegex.lastIndex = 0;\n      const classMatch = classRegex.exec(srcFileContents);\n\n      if (classMatch) {\n        const className = classMatch[1];\n\n        debug('Searching %O for package by %O regex', srcFile, packageRegex);\n\n        packageRegex.lastIndex = 0;\n        const packageMatch = packageRegex.exec(srcFileContents.substring(0, classMatch.index));\n\n        if (!packageMatch) {\n          fatal(`Package could not be parsed from Android plugin.\\n` + `Location: ${c.strong(srcFile)}`);\n        }\n\n        const packageName = packageMatch[1];\n        const classpath = `${packageName}.${className}`;\n\n        debug('%O is a suitable plugin class', classpath);\n\n        return {\n          pkg: plugin.id,\n          classpath,\n        };\n      }\n    }),\n  );\n\n  return entries.filter((entry): entry is PluginsJsonEntry => !!entry);\n}\n\nexport async function installGradlePlugins(\n  config: Config,\n  capacitorPlugins: Plugin[],\n  cordovaPlugins: Plugin[],\n): Promise<void> {\n  const capacitorAndroidPackagePath = resolveNode(config.app.rootDir, '@capacitor/android', 'package.json');\n  if (!capacitorAndroidPackagePath) {\n    fatal(\n      `Unable to find ${c.strong('node_modules/@capacitor/android')}.\\n` +\n        `Are you sure ${c.strong('@capacitor/android')} is installed?`,\n    );\n  }\n\n  const capacitorAndroidPath = resolve(dirname(capacitorAndroidPackagePath), 'capacitor');\n\n  const settingsPath = config.android.platformDirAbs;\n  const dependencyPath = config.android.appDirAbs;\n  const relativeCapcitorAndroidPath = convertToUnixPath(relative(settingsPath, capacitorAndroidPath));\n  const settingsLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME \"capacitor update\" IS RUN\ninclude ':capacitor-android'\nproject(':capacitor-android').projectDir = new File('${relativeCapcitorAndroidPath}')\n${capacitorPlugins\n  .map((p) => {\n    if (!p.android) {\n      return '';\n    }\n\n    const relativePluginPath = convertToUnixPath(relative(settingsPath, p.rootPath));\n\n    return `\ninclude ':${getGradlePackageName(p.id)}'\nproject(':${getGradlePackageName(p.id)}').projectDir = new File('${relativePluginPath}/${p.android.path}')\n`;\n  })\n  .join('')}`;\n\n  const applyArray: any[] = [];\n  const frameworksArray: any[] = [];\n  let prefsArray: any[] = [];\n  cordovaPlugins.map((p) => {\n    const relativePluginPath = convertToUnixPath(relative(dependencyPath, p.rootPath));\n    const frameworks = getPlatformElement(p, platform, 'framework');\n    frameworks.map((framework: any) => {\n      if (\n        framework.$.custom &&\n        framework.$.custom === 'true' &&\n        framework.$.type &&\n        framework.$.type === 'gradleReference'\n      ) {\n        applyArray.push(`apply from: \"${relativePluginPath}/${framework.$.src}\"`);\n      } else if (!framework.$.type && !framework.$.custom) {\n        if (framework.$.src.startsWith('platform(')) {\n          frameworksArray.push(`    implementation ${framework.$.src}`);\n        } else {\n          frameworksArray.push(`    implementation \"${framework.$.src}\"`);\n        }\n      }\n    });\n    prefsArray = prefsArray.concat(getAllElements(p, platform, 'preference'));\n  });\n  let frameworkString = frameworksArray.join('\\n');\n  frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString);\n  const dependencyLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME \"capacitor update\" IS RUN\n\nandroid {\n  compileOptions {\n      sourceCompatibility JavaVersion.VERSION_21\n      targetCompatibility JavaVersion.VERSION_21\n  }\n}\n\napply from: \"../capacitor-cordova-android-plugins/cordova.variables.gradle\"\ndependencies {\n${capacitorPlugins\n  .map((p) => {\n    return `    implementation project(':${getGradlePackageName(p.id)}')`;\n  })\n  .join('\\n')}\n${frameworkString}\n}\n${applyArray.join('\\n')}\n\nif (hasProperty('postBuildExtras')) {\n  postBuildExtras()\n}\n`;\n\n  await writeFile(join(settingsPath, 'capacitor.settings.gradle'), settingsLines);\n  await writeFile(join(dependencyPath, 'capacitor.build.gradle'), dependencyLines);\n}\n\nexport async function handleCordovaPluginsGradle(config: Config, cordovaPlugins: Plugin[]): Promise<void> {\n  const pluginsGradlePath = join(config.android.cordovaPluginsDirAbs, 'build.gradle');\n  const kotlinNeeded = await kotlinNeededCheck(config, cordovaPlugins);\n  const kotlinVersionString = config.app.extConfig.cordova?.preferences?.GradlePluginKotlinVersion ?? '2.2.20';\n  const frameworksArray: any[] = [];\n  let prefsArray: any[] = [];\n  const applyArray: any[] = [];\n  applyArray.push(`apply from: \"cordova.variables.gradle\"`);\n  cordovaPlugins.map((p) => {\n    const relativePluginPath = convertToUnixPath(relative(config.android.cordovaPluginsDirAbs, p.rootPath));\n    const frameworks = getPlatformElement(p, platform, 'framework');\n    frameworks.map((framework: any) => {\n      if (!framework.$.type && !framework.$.custom) {\n        frameworksArray.push(framework.$.src);\n      } else if (\n        framework.$.custom &&\n        framework.$.custom === 'true' &&\n        framework.$.type &&\n        framework.$.type === 'gradleReference'\n      ) {\n        applyArray.push(`apply from: \"${relativePluginPath}/${framework.$.src}\"`);\n      }\n    });\n    prefsArray = prefsArray.concat(getAllElements(p, platform, 'preference'));\n  });\n  let frameworkString = frameworksArray\n    .map((f) => {\n      if (f.startsWith('platform(')) {\n        return `    implementation ${f}`;\n      } else {\n        return `    implementation \"${f}\"`;\n      }\n    })\n    .join('\\n');\n  frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString);\n  if (kotlinNeeded) {\n    frameworkString += `\\n    implementation \"androidx.core:core-ktx:$androidxCoreKTXVersion\"`;\n    frameworkString += `\\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version\"`;\n  }\n  const applyString = applyArray.join('\\n');\n  let buildGradle = await readFile(pluginsGradlePath, { encoding: 'utf-8' });\n  buildGradle = buildGradle.replace(\n    /(SUB-PROJECT DEPENDENCIES START)[\\s\\S]*(\\/\\/ SUB-PROJECT DEPENDENCIES END)/,\n    '$1\\n' + frameworkString.concat('\\n') + '    $2',\n  );\n  buildGradle = buildGradle.replace(\n    /(PLUGIN GRADLE EXTENSIONS START)[\\s\\S]*(\\/\\/ PLUGIN GRADLE EXTENSIONS END)/,\n    '$1\\n' + applyString.concat('\\n') + '$2',\n  );\n  if (kotlinNeeded) {\n    buildGradle = buildGradle.replace(\n      /(buildscript\\s{\\n(\\t|\\s{4})repositories\\s{\\n((\\t{2}|\\s{8}).+\\n)+(\\t|\\s{4})}\\n(\\t|\\s{4})dependencies\\s{\\n(\\t{2}|\\s{8}).+)\\n((\\t|\\s{4})}\\n}\\n)/,\n      `$1\\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\\n$8`,\n    );\n    buildGradle = buildGradle.replace(\n      /(ext\\s{)/,\n      `$1\\n    androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.8.0'`,\n    );\n    buildGradle = buildGradle.replace(\n      /(buildscript\\s{)/,\n      `$1\\n    ext.kotlin_version = project.hasProperty('kotlin_version') ? rootProject.ext.kotlin_version : '${kotlinVersionString}'`,\n    );\n    buildGradle = buildGradle.replace(\n      /(apply\\splugin:\\s'com\\.android\\.library')/,\n      `$1\\napply plugin: 'kotlin-android'`,\n    );\n    buildGradle = buildGradle.replace(\n      /(compileOptions\\s{\\n((\\t{2}|\\s{8}).+\\n)+(\\t|\\s{4})})\\n(})/,\n      `$1\\n    sourceSets {\\n        main.java.srcDirs += 'src/main/kotlin'\\n    }\\n$5`,\n    );\n  }\n  await writeFile(pluginsGradlePath, buildGradle);\n  const cordovaVariables = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME \"capacitor update\" IS RUN\next {\n  cdvMinSdkVersion = project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : ${config.android.minVersion}\n  // Plugin gradle extensions can append to this to have code run at the end.\n  cdvPluginPostBuildExtras = []\n  cordovaConfig = [:]\n}`;\n  await writeFile(join(config.android.cordovaPluginsDirAbs, 'cordova.variables.gradle'), cordovaVariables);\n}\n\nasync function kotlinNeededCheck(config: Config, cordovaPlugins: Plugin[]) {\n  if (config.app.extConfig.cordova?.preferences?.GradlePluginKotlinEnabled !== 'true') {\n    for (const plugin of cordovaPlugins) {\n      const androidPlatform = getPluginPlatform(plugin, platform);\n      const sourceFiles = androidPlatform['source-file'];\n      if (sourceFiles) {\n        for (const srcFile of sourceFiles) {\n          if (/^.*\\.kt$/.test(srcFile['$'].src)) {\n            return true;\n          }\n        }\n      }\n    }\n    return false;\n  } else {\n    return true;\n  }\n}\n\nasync function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) {\n  const pluginsPath = join(config.android.cordovaPluginsDirAbs, 'src', 'main');\n  for (const p of cordovaPlugins) {\n    const androidPlatform = getPluginPlatform(p, platform);\n    if (androidPlatform) {\n      const sourceFiles = androidPlatform['source-file'];\n      if (sourceFiles) {\n        for (const sourceFile of sourceFiles) {\n          const fileName = sourceFile.$.src.split('/').pop();\n          let baseFolder = 'java/';\n          if (fileName.split('.').pop() === 'aidl') {\n            baseFolder = 'aidl/';\n          }\n          const target = sourceFile.$['target-dir'].replace('app/src/main/', '').replace('src/', baseFolder);\n          await copy(getFilePath(config, p, sourceFile.$.src), join(pluginsPath, target, fileName));\n        }\n      }\n      const resourceFiles = androidPlatform['resource-file'];\n      if (resourceFiles) {\n        for (const resourceFile of resourceFiles) {\n          const target = resourceFile.$['target'];\n          if (resourceFile.$.src.split('.').pop() === 'aar') {\n            await copy(getFilePath(config, p, resourceFile.$.src), join(pluginsPath, 'libs', target.split('/').pop()));\n          } else if (target !== '.') {\n            await copy(getFilePath(config, p, resourceFile.$.src), join(pluginsPath, target));\n          }\n        }\n      }\n      const libFiles = getPlatformElement(p, platform, 'lib-file');\n      for (const libFile of libFiles) {\n        await copy(getFilePath(config, p, libFile.$.src), join(pluginsPath, 'libs', libFile.$.src.split('/').pop()));\n      }\n    }\n  }\n}\n\nasync function removePluginsNativeFiles(config: Config) {\n  await remove(config.android.cordovaPluginsDirAbs);\n  await extractTemplate(\n    config.cli.assets.android.cordovaPluginsTemplateArchiveAbs,\n    config.android.cordovaPluginsDirAbs,\n  );\n}\n\nasync function getPluginsTask(config: Config) {\n  return await runTask('Updating Android plugins', async () => {\n    const allPlugins = await getPlugins(config, 'android');\n    const androidPlugins = await getAndroidPlugins(allPlugins);\n    return androidPlugins;\n  });\n}\n\nasync function getVariablesGradleFile(config: Config) {\n  const variablesFile = resolve(config.android.platformDirAbs, 'variables.gradle');\n  let variablesGradle = '';\n  if (await pathExists(variablesFile)) {\n    variablesGradle = await readFile(variablesFile, { encoding: 'utf-8' });\n  }\n  return variablesGradle;\n}\n\nasync function replaceFrameworkVariables(config: Config, prefsArray: any[], frameworkString: string) {\n  const variablesGradle = await getVariablesGradleFile(config);\n  prefsArray.map((preference: any) => {\n    if (!variablesGradle.includes(preference.$.name)) {\n      frameworkString = frameworkString.replace(\n        new RegExp(('$' + preference.$.name).replace('$', '\\\\$&'), 'g'),\n        preference.$.default,\n      );\n    }\n  });\n  return frameworkString;\n}\n"
  },
  {
    "path": "cli/src/colors.ts",
    "content": "import type { Colors } from '@ionic/cli-framework-output';\nimport kleur from 'kleur';\n\nexport const strong = kleur.bold;\nexport const weak = kleur.dim;\nexport const input = kleur.cyan;\nexport const success = kleur.green;\nexport const failure = kleur.red;\nexport const ancillary = kleur.cyan;\n\nconst COLORS: Colors = {\n  strong,\n  weak,\n  input,\n  success,\n  failure,\n  ancillary,\n  log: {\n    DEBUG: kleur.magenta,\n    INFO: kleur.cyan,\n    WARN: kleur.yellow,\n    ERROR: kleur.red,\n  },\n};\n\nexport default COLORS;\n"
  },
  {
    "path": "cli/src/common.ts",
    "content": "import { prettyPath } from '@ionic/utils-terminal';\nimport { readJSON, pathExists } from 'fs-extra';\nimport { dirname, join } from 'path';\n\nimport c from './colors';\nimport type { Config, PackageJson } from './definitions';\nimport { fatal } from './errors';\nimport { output, logger } from './log';\nimport { getPlugins } from './plugin';\nimport { findNXMonorepoRoot, isNXMonorepo } from './util/monorepotools';\nimport { resolveNode } from './util/node';\nimport { runCommand } from './util/subprocess';\n\nexport type CheckFunction = () => Promise<string | null>;\n\nexport async function check(checks: CheckFunction[]): Promise<void> {\n  const results = await Promise.all(checks.map((f) => f()));\n  const errors = results.filter((r) => r != null) as string[];\n  if (errors.length > 0) {\n    throw errors.join('\\n');\n  }\n}\n\nexport async function checkWebDir(config: Config): Promise<string | null> {\n  // We can skip checking the web dir if a server URL is set.\n  if (config.app.extConfig.server?.url) {\n    return null;\n  }\n\n  const invalidFolders = ['', '.', '..', '../', './'];\n  if (invalidFolders.includes(config.app.webDir)) {\n    return `\"${config.app.webDir}\" is not a valid value for webDir`;\n  }\n  if (!(await pathExists(config.app.webDirAbs))) {\n    return (\n      `Could not find the web assets directory: ${c.strong(prettyPath(config.app.webDirAbs))}.\\n` +\n      `Please create it and make sure it has an ${c.strong(\n        'index.html',\n      )} file. You can change the path of this directory in ${c.strong(config.app.extConfigName)} (${c.input(\n        'webDir',\n      )} option). You may need to compile the web assets for your app (typically ${c.input(\n        'npm run build',\n      )}). More info: ${c.strong('https://capacitorjs.com/docs/basics/workflow#sync-your-project')}`\n    );\n  }\n\n  if (!(await pathExists(join(config.app.webDirAbs, 'index.html')))) {\n    return (\n      `The web assets directory (${c.strong(\n        prettyPath(config.app.webDirAbs),\n      )}) must contain an ${c.strong('index.html')} file.\\n` +\n      `It will be the entry point for the web portion of the Capacitor app.`\n    );\n  }\n  return null;\n}\n\nexport async function checkPackage(): Promise<string | null> {\n  if (!(await pathExists('package.json'))) {\n    if (await pathExists('project.json')) {\n      return null;\n    } else {\n      return (\n        `The Capacitor CLI needs to run at the root of an npm package or in a valid NX monorepo.\\n` +\n        `Make sure you have a package.json or project.json file in the directory where you run the Capacitor CLI.\\n` +\n        `More info: ${c.strong('https://docs.npmjs.com/cli/init')}`\n      );\n    }\n  }\n  return null;\n}\n\nexport async function checkCapacitorPlatform(config: Config, platform: string): Promise<string | null> {\n  const pkg = await getCapacitorPackage(config, platform);\n\n  if (!pkg) {\n    return (\n      `Could not find the ${c.input(platform)} platform.\\n` +\n      `You must install it in your project first, e.g. w/ ${c.input(`npm install @capacitor/${platform}`)}`\n    );\n  }\n\n  return null;\n}\n\nexport async function checkAppConfig(config: Config): Promise<string | null> {\n  if (!config.app.appId) {\n    return (\n      `Missing ${c.input('appId')} for new platform.\\n` +\n      `Please add it in ${config.app.extConfigName} or run ${c.input('npx cap init')}.`\n    );\n  }\n  if (!config.app.appName) {\n    return (\n      `Missing ${c.input('appName')} for new platform.\\n` +\n      `Please add it in ${config.app.extConfigName} or run ${c.input('npx cap init')}.`\n    );\n  }\n\n  const appIdError = await checkAppId(config, config.app.appId);\n  if (appIdError) {\n    return appIdError;\n  }\n\n  const appNameError = await checkAppName(config, config.app.appName);\n  if (appNameError) {\n    return appNameError;\n  }\n\n  return null;\n}\n\nexport async function checkAppDir(config: Config, dir: string): Promise<string | null> {\n  if (!/^\\S*$/.test(dir)) {\n    return `Your app directory should not contain spaces`;\n  }\n  return null;\n}\n\nexport async function checkAppId(config: Config, id: string): Promise<string | null> {\n  if (!id) {\n    return `Invalid App ID. App ID is required and cannot be blank.`;\n  }\n  if (/^[a-zA-Z][\\w]*(?:\\.[a-zA-Z][\\w]*)+$/.test(id.toLowerCase())) {\n    return null;\n  }\n  return `\n    Invalid App ID \"${id}\". Your App ID must meet the following requirements to be valid on both iOS and Android:\n    - Must be in Java package form with no dashes (ex: com.example.app)\n    - It must have at least two segments (one or more dots).\n    - Each segment must start with a letter.\n    - All characters must be alphanumeric or an underscore [a-zA-Z][a-zA-Z0-9]+.\n\n    If you would like to skip validation, run \"cap init\" with the \"--skip-appid-validation\" flag.\n  `;\n}\n\nexport async function checkAppName(config: Config, name: string): Promise<string | null> {\n  // We allow pretty much anything right now, have fun\n  if (!name?.length) {\n    return `Must provide an app name. For example: 'Spacebook'`;\n  }\n  return null;\n}\n\nexport async function wait(time: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, time));\n}\n\nexport async function runHooks(config: Config, platformName: string, dir: string, hook: string): Promise<void> {\n  await runPlatformHook(config, platformName, dir, hook);\n\n  const allPlugins = await getPlugins(config, platformName);\n\n  for (const p of allPlugins) {\n    await runPlatformHook(config, platformName, p.rootPath, hook);\n  }\n}\n\nexport async function runPlatformHook(\n  config: Config,\n  platformName: string,\n  platformDir: string,\n  hook: string,\n): Promise<void> {\n  const { spawn } = await import('child_process');\n  let pkg;\n  if (isNXMonorepo(platformDir)) {\n    pkg = await readJSON(join(findNXMonorepoRoot(platformDir), 'package.json'));\n  } else {\n    pkg = await readJSON(join(platformDir, 'package.json'));\n  }\n  const cmd = pkg.scripts?.[hook];\n\n  if (!cmd) {\n    return;\n  }\n\n  return new Promise((resolve, reject) => {\n    const p = spawn(cmd, {\n      stdio: 'inherit',\n      shell: true,\n      cwd: platformDir,\n      env: {\n        INIT_CWD: platformDir,\n        CAPACITOR_ROOT_DIR: config.app.rootDir,\n        CAPACITOR_WEB_DIR: config.app.webDirAbs,\n        CAPACITOR_CONFIG: JSON.stringify(config.app.extConfig),\n        CAPACITOR_PLATFORM_NAME: platformName,\n        ...process.env,\n      },\n    });\n    p.on('close', (code) => {\n      if (code === 0) {\n        resolve();\n      } else {\n        reject(\n          new Error(`${hook} hook on ${platformName} failed with error code: ${code} while running command: ${cmd}`),\n        );\n      }\n    });\n    p.on('error', (err) => {\n      reject(err);\n    });\n  });\n}\n\nexport interface RunTaskOptions {\n  spinner?: boolean;\n}\n\nexport async function runTask<T>(title: string, fn: () => Promise<T>): Promise<T> {\n  const chain = output.createTaskChain();\n  chain.next(title);\n\n  try {\n    const value = await fn();\n    chain.end();\n    return value;\n  } catch (e) {\n    chain.fail();\n    throw e;\n  }\n}\n\nexport async function getCapacitorPackage(config: Config, name: string): Promise<PackageJson | null> {\n  const packagePath = resolveNode(config.app.rootDir, `@capacitor/${name}`, 'package.json');\n\n  if (!packagePath) {\n    return null;\n  }\n\n  return readJSON(packagePath);\n}\n\nexport async function requireCapacitorPackage(config: Config, name: string): Promise<PackageJson> {\n  const pkg = await getCapacitorPackage(config, name);\n\n  if (!pkg) {\n    fatal(\n      `Unable to find node_modules/@capacitor/${name}.\\n` +\n        `Are you sure ${c.strong(`@capacitor/${name}`)} is installed?`,\n    );\n  }\n  return pkg;\n}\n\nexport async function getCapacitorPackageVersion(config: Config, platform: string): Promise<string> {\n  return (await requireCapacitorPackage(config, platform)).version;\n}\n\nexport async function getCoreVersion(config: Config): Promise<string> {\n  return getCapacitorPackageVersion(config, 'core');\n}\n\nexport async function getCLIVersion(config: Config): Promise<string> {\n  return getCapacitorPackageVersion(config, 'cli');\n}\n\nfunction getPlatformDirectory(config: Config, platform: string): string | null {\n  switch (platform) {\n    case 'android':\n      return config.android.platformDirAbs;\n    case 'ios':\n      return config.ios.platformDirAbs;\n    case 'web':\n      return config.web.platformDirAbs;\n  }\n\n  return null;\n}\n\nexport async function getProjectPlatformDirectory(config: Config, platform: string): Promise<string | null> {\n  const platformPath = getPlatformDirectory(config, platform);\n\n  if (platformPath && (await pathExists(platformPath))) {\n    return platformPath;\n  }\n\n  return null;\n}\n\nexport async function selectPlatforms(config: Config, selectedPlatformName?: string): Promise<string[]> {\n  if (selectedPlatformName) {\n    // already passed in a platform name\n    const platformName = selectedPlatformName.toLowerCase().trim();\n\n    if (!(await isValidPlatform(platformName))) {\n      fatal(`Invalid platform: ${c.input(platformName)}`);\n    } else if (!(await getProjectPlatformDirectory(config, platformName))) {\n      if (platformName === 'web') {\n        fatal(`Could not find the web platform directory.\\n` + `Make sure ${c.strong(config.app.webDir)} exists.`);\n      }\n      fatal(\n        `${c.strong(platformName)} platform has not been added yet.\\n` +\n          `See the docs for adding the ${c.strong(platformName)} platform: ${c.strong(\n            `https://capacitorjs.com/docs/${platformName}#adding-the-${platformName}-platform`,\n          )}`,\n      );\n    }\n\n    // return the platform in an string array\n    return [platformName];\n  }\n\n  // wasn't given a platform name, so let's\n  // get the platforms that have already been created\n  return getAddedPlatforms(config);\n}\n\nexport async function getKnownPlatforms(): Promise<string[]> {\n  return ['web', 'android', 'ios'];\n}\n\nexport async function isValidPlatform(platform: string): Promise<boolean> {\n  return (await getKnownPlatforms()).includes(platform);\n}\n\nexport async function getKnownCommunityPlatforms(): Promise<string[]> {\n  return ['electron'];\n}\n\nexport async function isValidCommunityPlatform(platform: string): Promise<boolean> {\n  return (await getKnownCommunityPlatforms()).includes(platform);\n}\n\nexport async function getKnownEnterprisePlatforms(): Promise<string[]> {\n  return ['windows'];\n}\n\nexport async function isValidEnterprisePlatform(platform: string): Promise<boolean> {\n  return (await getKnownEnterprisePlatforms()).includes(platform);\n}\n\nexport async function promptForPlatform(\n  platforms: string[],\n  promptMessage: string,\n  selectedPlatformName?: string,\n): Promise<string> {\n  const { prompt } = await import('prompts');\n\n  if (!selectedPlatformName) {\n    const answers = await prompt(\n      [\n        {\n          type: 'select',\n          name: 'mode',\n          message: promptMessage,\n          choices: platforms.map((p) => ({ title: p, value: p })),\n        },\n      ],\n      { onCancel: () => process.exit(1) },\n    );\n\n    return answers.mode.toLowerCase().trim();\n  }\n\n  const platformName = selectedPlatformName.toLowerCase().trim();\n\n  if (!(await isValidPlatform(platformName))) {\n    const knownPlatforms = await getKnownPlatforms();\n\n    fatal(`Invalid platform: ${c.input(platformName)}.\\n` + `Valid platforms include: ${knownPlatforms.join(', ')}`);\n  }\n\n  return platformName;\n}\n\nexport interface PlatformTarget {\n  id: string;\n  platform: string;\n  virtual: boolean;\n  sdkVersion: string;\n  name?: string;\n  model?: string;\n}\n\nexport async function promptForPlatformTarget(\n  targets: PlatformTarget[],\n  selectedTarget?: string,\n  selectedTargetSdkVersion?: string,\n  selectByName?: boolean,\n): Promise<PlatformTarget> {\n  const { prompt } = await import('prompts');\n  const validTargets = targets.filter((t) => t.id !== undefined);\n  if (!selectedTarget) {\n    if (validTargets.length === 1) {\n      return validTargets[0];\n    } else {\n      const answers = await prompt(\n        [\n          {\n            type: 'select',\n            name: 'target',\n            message: 'Please choose a target device:',\n            choices: validTargets.map((t) => ({\n              title: `${getPlatformTargetName(t)} (${t.id})`,\n              value: t,\n            })),\n          },\n        ],\n        { onCancel: () => process.exit(1) },\n      );\n\n      return answers.target;\n    }\n  }\n\n  const targetID = selectedTarget.trim();\n  const target = targets.find((t) => {\n    if (selectByName === true) {\n      let name = t.name ?? t.model;\n      if (name) {\n        // Apple device names may have \"smart quotes\" in the name,\n        // strip them and replace them with the \"straight\" versions\n        name = name.replace(/[\\u2018\\u2019]/g, \"'\").replace(/[\\u201C\\u201D]/g, '\"');\n      }\n\n      if (selectedTargetSdkVersion) {\n        return name === targetID && t.sdkVersion === selectedTargetSdkVersion;\n      }\n\n      return name === targetID;\n    }\n\n    return t.id === targetID;\n  });\n\n  if (!target) {\n    if (selectByName) {\n      let invalidTargetName = targetID;\n      if (selectedTargetSdkVersion) {\n        invalidTargetName += ` [${selectedTargetSdkVersion}]`;\n      }\n      fatal(\n        `Invalid target name: ${c.input(invalidTargetName)}.\\n` +\n          `Valid targets are:\\n ${targets.map((t) => `${t.name ?? t.model} [${t.sdkVersion}]`).join('\\n')}`,\n      );\n    }\n    fatal(`Invalid target ID: ${c.input(targetID)}.\\n` + `Valid targets are:\\n ${targets.map((t) => t.id).join('\\n')}`);\n  }\n\n  return target;\n}\n\nexport function getPlatformTargetName(target: PlatformTarget): string {\n  return `${target.name ?? target.model ?? target.id ?? '?'}${\n    target.virtual ? ` (${target.platform === 'ios' ? 'simulator' : 'emulator'})` : ''\n  }`;\n}\n\nexport async function getAddedPlatforms(config: Config): Promise<string[]> {\n  const platforms: string[] = [];\n\n  if (await getProjectPlatformDirectory(config, config.android.name)) {\n    platforms.push(config.android.name);\n  }\n\n  if (await getProjectPlatformDirectory(config, config.ios.name)) {\n    platforms.push(config.ios.name);\n  }\n\n  platforms.push(config.web.name);\n\n  return platforms;\n}\n\nexport async function checkPlatformVersions(config: Config, platform: string): Promise<void> {\n  const semver = await import('semver');\n  const coreVersion = await getCoreVersion(config);\n  const platformVersion = await getCapacitorPackageVersion(config, platform);\n\n  if (semver.diff(coreVersion, platformVersion) === 'minor' || semver.diff(coreVersion, platformVersion) === 'major') {\n    logger.warn(\n      `${c.strong('@capacitor/core')}${c.weak(\n        `@${coreVersion}`,\n      )} version doesn't match ${c.strong(`@capacitor/${platform}`)}${c.weak(`@${platformVersion}`)} version.\\n` +\n        `Consider updating to a matching version, e.g. w/ ${c.input(`npm install @capacitor/core@${platformVersion}`)}`,\n    );\n  }\n}\n\nexport function resolvePlatform(config: Config, platform: string): string | null {\n  if (platform[0] !== '@') {\n    const core = resolveNode(config.app.rootDir, `@capacitor/${platform}`, 'package.json');\n\n    if (core) {\n      return dirname(core);\n    }\n\n    const community = resolveNode(config.app.rootDir, `@capacitor-community/${platform}`, 'package.json');\n\n    if (community) {\n      return dirname(community);\n    }\n\n    const enterprise = resolveNode(config.app.rootDir, `@ionic-enterprise/capacitor-${platform}`, 'package.json');\n\n    if (enterprise) {\n      return dirname(enterprise);\n    }\n  }\n\n  // third-party\n  const thirdParty = resolveNode(config.app.rootDir, platform, 'package.json');\n\n  if (thirdParty) {\n    return dirname(thirdParty);\n  }\n\n  return null;\n}\n\nexport async function checkJDKMajorVersion(): Promise<number> {\n  try {\n    const string = await runCommand('java', ['--version']);\n    const versionRegex = RegExp(/([0-9]+)\\.?([0-9]*)\\.?([0-9]*)/);\n    const versionMatch = versionRegex.exec(string);\n\n    if (versionMatch === null) {\n      return -1;\n    }\n\n    const firstVersionNumber = parseInt(versionMatch[1]);\n    const secondVersionNumber = parseInt(versionMatch[2]);\n\n    if (typeof firstVersionNumber === 'number' && firstVersionNumber != 1) {\n      return firstVersionNumber;\n    } else if (typeof secondVersionNumber === 'number' && firstVersionNumber == 1 && secondVersionNumber < 9) {\n      return secondVersionNumber;\n    } else {\n      return -1;\n    }\n  } catch (e) {\n    return -1;\n  }\n}\n\nexport function parseApkNameFromFlavor(flavor: string): string {\n  let convertedName = flavor.replace(/([A-Z])/g, '-$1').toLowerCase();\n\n  if (convertedName.startsWith('-')) convertedName = convertedName.replace('-', '');\n\n  return `app-${convertedName ? `${convertedName}-` : ''}debug.apk`;\n}\n"
  },
  {
    "path": "cli/src/config.ts",
    "content": "import Debug from 'debug';\nimport { existsSync, pathExists, readFile, readJSON, writeFile, writeJSON } from 'fs-extra';\nimport { dirname, extname, join, relative, resolve } from 'path';\n\nimport c from './colors';\nimport { parseApkNameFromFlavor } from './common';\nimport type {\n  AndroidConfig,\n  AppConfig,\n  CLIConfig,\n  Config,\n  ExternalConfig,\n  IOSConfig,\n  WebConfig,\n  XcodeExportMethod,\n  PackageManager,\n} from './definitions';\nimport { OS } from './definitions';\nimport { fatal, isFatal } from './errors';\nimport { logger } from './log';\nimport { tryFn } from './util/fn';\nimport { formatJSObject } from './util/js';\nimport { findNXMonorepoRoot, isNXMonorepo } from './util/monorepotools';\nimport { requireTS, resolveNode } from './util/node';\nimport { lazy } from './util/promise';\nimport { getCommandOutput, isInstalled } from './util/subprocess';\n\nconst debug = Debug('capacitor:config');\n\nexport const CONFIG_FILE_NAME_TS = 'capacitor.config.ts';\nexport const CONFIG_FILE_NAME_JS = 'capacitor.config.js';\nexport const CONFIG_FILE_NAME_JSON = 'capacitor.config.json';\n\nexport async function loadConfig(): Promise<Config> {\n  const appRootDir = process.cwd();\n  const cliRootDir = dirname(__dirname);\n  const conf = await loadExtConfig(appRootDir);\n\n  const depsForNx = await (async (): Promise<{ devDependencies: any; dependencies: any } | object> => {\n    if (isNXMonorepo(appRootDir)) {\n      const rootOfNXMonorepo = findNXMonorepoRoot(appRootDir);\n      const pkgJSONOfMonorepoRoot: any = await tryFn(readJSON, resolve(rootOfNXMonorepo, 'package.json'));\n      const devDependencies = pkgJSONOfMonorepoRoot?.devDependencies ?? {};\n      const dependencies = pkgJSONOfMonorepoRoot?.dependencies ?? {};\n      return {\n        devDependencies,\n        dependencies,\n      };\n    }\n    return {};\n  })();\n\n  const appId = conf.extConfig.appId ?? '';\n  const appName = conf.extConfig.appName ?? '';\n  const webDir = conf.extConfig.webDir ?? 'www';\n  const cli = await loadCLIConfig(cliRootDir);\n\n  const config: Config = {\n    android: await loadAndroidConfig(appRootDir, conf.extConfig, cli),\n    ios: await loadIOSConfig(appRootDir, conf.extConfig),\n    web: await loadWebConfig(appRootDir, webDir),\n    cli,\n    app: {\n      rootDir: appRootDir,\n      appId,\n      appName,\n      webDir,\n      webDirAbs: resolve(appRootDir, webDir),\n      package: (await tryFn(readJSON, resolve(appRootDir, 'package.json'))) ?? {\n        name: appName,\n        version: '1.0.0',\n        ...depsForNx,\n      },\n      ...conf,\n    },\n  };\n\n  debug('config: %O', config);\n\n  return config;\n}\n\nexport async function writeConfig(extConfig: ExternalConfig, extConfigFilePath: string): Promise<void> {\n  switch (extname(extConfigFilePath)) {\n    case '.json': {\n      await writeJSON(extConfigFilePath, extConfig, { spaces: 2 });\n      break;\n    }\n    case '.ts': {\n      await writeFile(extConfigFilePath, formatConfigTS(extConfig));\n      break;\n    }\n  }\n}\n\ntype ExtConfigPairs = Pick<AppConfig, 'extConfigType' | 'extConfigName' | 'extConfigFilePath' | 'extConfig'>;\n\nasync function loadExtConfigTS(\n  rootDir: string,\n  extConfigName: string,\n  extConfigFilePath: string,\n): Promise<ExtConfigPairs> {\n  try {\n    const tsPath = resolveNode(rootDir, 'typescript');\n\n    if (!tsPath) {\n      fatal(\n        'Could not find installation of TypeScript.\\n' +\n          `To use ${c.strong(extConfigName)} files, you must install TypeScript in your project, e.g. w/ ${c.input(\n            'npm install -D typescript',\n          )}`,\n      );\n    }\n\n    const ts = require(tsPath); // eslint-disable-line @typescript-eslint/no-var-requires\n    const extConfigObject = requireTS(ts, extConfigFilePath) as any;\n    const extConfig = extConfigObject.default ? await extConfigObject.default : extConfigObject;\n\n    return {\n      extConfigType: 'ts',\n      extConfigName,\n      extConfigFilePath: extConfigFilePath,\n      extConfig,\n    };\n  } catch (e: any) {\n    if (!isFatal(e)) {\n      fatal(`Parsing ${c.strong(extConfigName)} failed.\\n\\n${e.stack ?? e}`);\n    }\n\n    throw e;\n  }\n}\n\nasync function loadExtConfigJS(\n  rootDir: string,\n  extConfigName: string,\n  extConfigFilePath: string,\n): Promise<ExtConfigPairs> {\n  try {\n    return {\n      extConfigType: 'js',\n      extConfigName,\n      extConfigFilePath: extConfigFilePath,\n      extConfig: await require(extConfigFilePath),\n    };\n  } catch (e: any) {\n    fatal(`Parsing ${c.strong(extConfigName)} failed.\\n\\n${e.stack ?? e}`);\n  }\n}\n\nasync function loadExtConfig(rootDir: string): Promise<ExtConfigPairs> {\n  const extConfigFilePathTS = resolve(rootDir, CONFIG_FILE_NAME_TS);\n\n  if (await pathExists(extConfigFilePathTS)) {\n    return loadExtConfigTS(rootDir, CONFIG_FILE_NAME_TS, extConfigFilePathTS);\n  }\n\n  const extConfigFilePathJS = resolve(rootDir, CONFIG_FILE_NAME_JS);\n\n  if (await pathExists(extConfigFilePathJS)) {\n    return loadExtConfigJS(rootDir, CONFIG_FILE_NAME_JS, extConfigFilePathJS);\n  }\n\n  const extConfigFilePath = resolve(rootDir, CONFIG_FILE_NAME_JSON);\n\n  return {\n    extConfigType: 'json',\n    extConfigName: CONFIG_FILE_NAME_JSON,\n    extConfigFilePath: extConfigFilePath,\n    extConfig: (await tryFn(readJSON, extConfigFilePath)) ?? {},\n  };\n}\n\nasync function loadCLIConfig(rootDir: string): Promise<CLIConfig> {\n  const assetsDir = 'assets';\n  const assetsDirAbs = join(rootDir, assetsDir);\n  const iosPlatformTemplateArchive = 'ios-spm-template.tar.gz';\n  const iosCordovaPluginsTemplateArchive = 'capacitor-cordova-ios-plugins.tar.gz';\n  const androidPlatformTemplateArchive = 'android-template.tar.gz';\n  const androidCordovaPluginsTemplateArchive = 'capacitor-cordova-android-plugins.tar.gz';\n\n  return {\n    rootDir,\n    assetsDir,\n    assetsDirAbs,\n    assets: {\n      ios: {\n        platformTemplateArchive: iosPlatformTemplateArchive,\n        platformTemplateArchiveAbs: resolve(assetsDirAbs, iosPlatformTemplateArchive),\n        cordovaPluginsTemplateArchive: iosCordovaPluginsTemplateArchive,\n        cordovaPluginsTemplateArchiveAbs: resolve(assetsDirAbs, iosCordovaPluginsTemplateArchive),\n      },\n      android: {\n        platformTemplateArchive: androidPlatformTemplateArchive,\n        platformTemplateArchiveAbs: resolve(assetsDirAbs, androidPlatformTemplateArchive),\n        cordovaPluginsTemplateArchive: androidCordovaPluginsTemplateArchive,\n        cordovaPluginsTemplateArchiveAbs: resolve(assetsDirAbs, androidCordovaPluginsTemplateArchive),\n      },\n    },\n    package: await readJSON(resolve(rootDir, 'package.json')),\n    os: determineOS(process.platform),\n  };\n}\n\nasync function loadAndroidConfig(\n  rootDir: string,\n  extConfig: ExternalConfig,\n  cliConfig: CLIConfig,\n): Promise<AndroidConfig> {\n  const name = 'android';\n  const platformDir = extConfig.android?.path ?? 'android';\n  const platformDirAbs = resolve(rootDir, platformDir);\n  const appDir = 'app';\n  const srcDir = `${appDir}/src`;\n  const srcMainDir = `${srcDir}/main`;\n  const assetsDir = `${srcMainDir}/assets`;\n  const webDir = `${assetsDir}/public`;\n  const resDir = `${srcMainDir}/res`;\n  let apkPath = `${appDir}/build/outputs/apk/`;\n  const flavor = extConfig.android?.flavor || '';\n  if (extConfig.android?.flavor) {\n    apkPath = `${apkPath}/${extConfig.android?.flavor}`;\n  }\n  const apkName = parseApkNameFromFlavor(flavor);\n  const buildOutputDir = `${apkPath}/debug`;\n  const cordovaPluginsDir = 'capacitor-cordova-android-plugins';\n  const studioPath = lazy(() => determineAndroidStudioPath(cliConfig.os));\n  const buildOptions = {\n    keystorePath: extConfig.android?.buildOptions?.keystorePath,\n    keystorePassword: extConfig.android?.buildOptions?.keystorePassword,\n    keystoreAlias: extConfig.android?.buildOptions?.keystoreAlias,\n    keystoreAliasPassword: extConfig.android?.buildOptions?.keystoreAliasPassword,\n    signingType: extConfig.android?.buildOptions?.signingType,\n    releaseType: extConfig.android?.buildOptions?.releaseType,\n  };\n\n  return {\n    name,\n    minVersion: '24',\n    studioPath,\n    platformDir,\n    platformDirAbs,\n    cordovaPluginsDir,\n    cordovaPluginsDirAbs: resolve(platformDirAbs, cordovaPluginsDir),\n    appDir,\n    appDirAbs: resolve(platformDirAbs, appDir),\n    srcDir,\n    srcDirAbs: resolve(platformDirAbs, srcDir),\n    srcMainDir,\n    srcMainDirAbs: resolve(platformDirAbs, srcMainDir),\n    assetsDir,\n    assetsDirAbs: resolve(platformDirAbs, assetsDir),\n    webDir,\n    webDirAbs: resolve(platformDirAbs, webDir),\n    resDir,\n    resDirAbs: resolve(platformDirAbs, resDir),\n    apkName,\n    buildOutputDir,\n    buildOutputDirAbs: resolve(platformDirAbs, buildOutputDir),\n    flavor,\n    buildOptions,\n  };\n}\n\nasync function loadIOSConfig(rootDir: string, extConfig: ExternalConfig): Promise<IOSConfig> {\n  const name = 'ios';\n  const platformDir = extConfig.ios?.path ?? 'ios';\n  const platformDirAbs = resolve(rootDir, platformDir);\n  const scheme = extConfig.ios?.scheme ?? 'App';\n  const nativeProjectDir = 'App';\n  const nativeProjectDirAbs = resolve(platformDirAbs, nativeProjectDir);\n  const nativeTargetDir = `${nativeProjectDir}/App`;\n  const nativeTargetDirAbs = resolve(platformDirAbs, nativeTargetDir);\n  const nativeXcodeProjDir = `${nativeProjectDir}/App.xcodeproj`;\n  const nativeXcodeProjDirAbs = resolve(platformDirAbs, nativeXcodeProjDir);\n  const nativeXcodeWorkspaceDirAbs = lazy(() => determineXcodeWorkspaceDirAbs(nativeProjectDirAbs));\n  const podPath = lazy(() => determineCocoapodPath());\n  const packageManager = lazy(() => determinePackageManager(rootDir, platformDirAbs, nativeProjectDirAbs));\n  const webDirAbs = lazy(() => determineIOSWebDirAbs(nativeProjectDirAbs, nativeTargetDirAbs, nativeXcodeProjDirAbs));\n  const cordovaPluginsDir = 'capacitor-cordova-ios-plugins';\n  const buildOptions = {\n    exportMethod: extConfig.ios?.buildOptions?.exportMethod as XcodeExportMethod,\n    xcodeSigningStyle: extConfig.ios?.buildOptions?.signingStyle,\n    signingCertificate: extConfig.ios?.buildOptions?.signingCertificate,\n    provisioningProfile: extConfig.ios?.buildOptions?.provisioningProfile,\n  };\n  return {\n    name,\n    minVersion: '15.0',\n    platformDir,\n    platformDirAbs,\n    scheme,\n    cordovaPluginsDir,\n    cordovaPluginsDirAbs: resolve(platformDirAbs, cordovaPluginsDir),\n    nativeProjectDir,\n    nativeProjectDirAbs,\n    nativeTargetDir,\n    nativeTargetDirAbs,\n    nativeXcodeProjDir,\n    nativeXcodeProjDirAbs,\n    nativeXcodeWorkspaceDir: lazy(async () => relative(platformDirAbs, await nativeXcodeWorkspaceDirAbs)),\n    nativeXcodeWorkspaceDirAbs,\n    webDir: lazy(async () => relative(platformDirAbs, await webDirAbs)),\n    webDirAbs,\n    podPath,\n    packageManager,\n    buildOptions,\n  };\n}\n\nasync function loadWebConfig(rootDir: string, webDir: string): Promise<WebConfig> {\n  const platformDir = webDir;\n  const platformDirAbs = resolve(rootDir, platformDir);\n\n  return {\n    name: 'web',\n    platformDir,\n    platformDirAbs,\n  };\n}\n\nfunction determineOS(os: NodeJS.Platform): OS {\n  switch (os) {\n    case 'darwin':\n      return OS.Mac;\n    case 'win32':\n      return OS.Windows;\n    case 'linux':\n      return OS.Linux;\n  }\n\n  return OS.Unknown;\n}\n\nasync function determineXcodeWorkspaceDirAbs(nativeProjectDirAbs: string): Promise<string> {\n  return resolve(nativeProjectDirAbs, 'App.xcworkspace');\n}\n\nasync function determineIOSWebDirAbs(\n  nativeProjectDirAbs: string,\n  nativeTargetDirAbs: string,\n  nativeXcodeProjDirAbs: string,\n): Promise<string> {\n  const re = /path\\s=\\spublic[\\s\\S]+?sourceTree\\s=\\s([^;]+)/;\n  const pbxprojPath = resolve(nativeXcodeProjDirAbs, 'project.pbxproj');\n  try {\n    const pbxproj = await readFile(pbxprojPath, { encoding: 'utf8' });\n\n    const m = pbxproj.match(re);\n\n    if (m && m[1] === 'SOURCE_ROOT') {\n      logger.warn(\n        `Using the iOS project root for the ${c.strong('public')} directory is deprecated.\\n` +\n          `Please follow the Upgrade Guide to move ${c.strong('public')} inside the iOS target directory: ${c.strong(\n            'https://capacitorjs.com/docs/updating/3-0#move-public-into-the-ios-target-directory',\n          )}`,\n      );\n\n      return resolve(nativeProjectDirAbs, 'public');\n    }\n  } catch (e) {\n    // ignore\n  }\n\n  return resolve(nativeTargetDirAbs, 'public');\n}\n\nasync function determineAndroidStudioPath(os: OS): Promise<string> {\n  if (process.env.CAPACITOR_ANDROID_STUDIO_PATH) {\n    return process.env.CAPACITOR_ANDROID_STUDIO_PATH;\n  }\n\n  switch (os) {\n    case OS.Mac:\n      return '/Applications/Android Studio.app';\n    case OS.Windows: {\n      const { runCommand } = await import('./util/subprocess');\n\n      let p = 'C:\\\\Program Files\\\\Android\\\\Android Studio\\\\bin\\\\studio64.exe';\n\n      try {\n        if (!(await pathExists(p))) {\n          let commandResult = await runCommand('REG', [\n            'QUERY',\n            'HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Android Studio',\n            '/v',\n            'Path',\n          ]);\n          commandResult = commandResult.replace(/(\\r\\n|\\n|\\r)/gm, '');\n          const i = commandResult.indexOf('REG_SZ');\n          if (i > 0) {\n            p = commandResult.substring(i + 6).trim() + '\\\\bin\\\\studio64.exe';\n          }\n        }\n      } catch (e) {\n        debug(`Error checking registry for Android Studio path: %O`, e);\n        break;\n      }\n\n      return p;\n    }\n    case OS.Linux: {\n      const studioExecPath = '/usr/local/android-studio/bin/studio';\n      const studioShPath = '/usr/local/android-studio/bin/studio.sh';\n\n      try {\n        if (await pathExists(studioExecPath)) {\n          return studioExecPath;\n        }\n      } catch (e) {\n        debug(`Error checking for studio executable: %O`, e);\n      }\n\n      return studioShPath;\n    }\n  }\n\n  return '';\n}\n\nasync function determineCocoapodPath(): Promise<string> {\n  if (process.env.CAPACITOR_COCOAPODS_PATH) {\n    return process.env.CAPACITOR_COCOAPODS_PATH;\n  }\n  return 'pod';\n}\n\nasync function determinePackageManager(\n  rootDir: string,\n  platformDir: any,\n  nativeProjectDirAbs: string,\n): Promise<PackageManager> {\n  if (existsSync(resolve(nativeProjectDirAbs, 'CapApp-SPM'))) {\n    return 'SPM';\n  }\n\n  let gemfilePath = '';\n  if (await pathExists(resolve(rootDir, 'Gemfile'))) {\n    gemfilePath = resolve(rootDir, 'Gemfile');\n  } else if (await pathExists(resolve(platformDir, 'Gemfile'))) {\n    gemfilePath = resolve(platformDir, 'Gemfile');\n  } else if (await pathExists(resolve(nativeProjectDirAbs, 'Gemfile'))) {\n    gemfilePath = resolve(nativeProjectDirAbs, 'Gemfile');\n  }\n\n  const appSpecificGemfileExists = gemfilePath != '';\n\n  // Multi-app projects might share a single global 'Gemfile' at the Git repository root directory.\n  if (!appSpecificGemfileExists) {\n    try {\n      const output = await getCommandOutput('git', ['rev-parse', '--show-toplevel'], { cwd: rootDir });\n      if (output != null) {\n        gemfilePath = resolve(output, 'Gemfile');\n      }\n    } catch (e: any) {\n      // Nothing\n    }\n  }\n\n  try {\n    const gemfileText = (await readFile(gemfilePath)).toString();\n    if (!gemfileText) {\n      return 'Cocoapods';\n    }\n    const cocoapodsInGemfile = new RegExp(/gem\\s+['\"]cocoapods/).test(gemfileText);\n\n    if (cocoapodsInGemfile && (await isInstalled('bundle'))) {\n      return 'bundler';\n    } else {\n      return 'Cocoapods';\n    }\n  } catch {\n    return 'Cocoapods';\n  }\n}\n\nfunction formatConfigTS(extConfig: ExternalConfig): string {\n  // TODO: <reference> tags\n  return `import type { CapacitorConfig } from '@capacitor/cli';\n\nconst config: CapacitorConfig = ${formatJSObject(extConfig)};\n\nexport default config;\\n`;\n}\n"
  },
  {
    "path": "cli/src/cordova.ts",
    "content": "import { copy, ensureDir, mkdirp, pathExists, readFile, remove, writeFile } from 'fs-extra';\nimport { basename, extname, join, resolve } from 'path';\nimport plist from 'plist';\nimport type { PlistObject } from 'plist';\nimport prompts from 'prompts';\n\nimport { getAndroidPlugins } from './android/common';\nimport c from './colors';\nimport type { Config } from './definitions';\nimport { fatal } from './errors';\nimport { getIOSPlugins } from './ios/common';\nimport { logger, logPrompt } from './log';\nimport {\n  PluginType,\n  getAllElements,\n  getAssets,\n  getJSModules,\n  getPlatformElement,\n  getPluginPlatform,\n  getPluginType,\n  getPlugins,\n  printPlugins,\n} from './plugin';\nimport type { Plugin } from './plugin';\nimport { resolveNode } from './util/node';\nimport { isInteractive } from './util/term';\nimport { buildXmlElement, parseXML, readXML, writeXML } from './util/xml';\n\n/**\n * Build the root cordova_plugins.js file referencing each Plugin JS file.\n */\nexport function generateCordovaPluginsJSFile(config: Config, plugins: Plugin[], platform: string): string {\n  const pluginModules: any[] = [];\n  const pluginExports: string[] = [];\n  plugins.map((p) => {\n    const pluginId = p.xml.$.id;\n    const jsModules = getJSModules(p, platform);\n    jsModules.map((jsModule: any) => {\n      const clobbers: string[] = [];\n      const merges: string[] = [];\n      let clobbersModule = '';\n      let mergesModule = '';\n      let runsModule = '';\n      let clobberKey = '';\n      let mergeKey = '';\n      if (jsModule.clobbers) {\n        jsModule.clobbers.map((clobber: any) => {\n          clobbers.push(clobber.$.target);\n          clobberKey = clobber.$.target;\n        });\n        clobbersModule = `,\n        \"clobbers\": [\n          \"${clobbers.join('\",\\n          \"')}\"\n        ]`;\n      }\n      if (jsModule.merges) {\n        jsModule.merges.map((merge: any) => {\n          merges.push(merge.$.target);\n          mergeKey = merge.$.target;\n        });\n        mergesModule = `,\n        \"merges\": [\n          \"${merges.join('\",\\n          \"')}\"\n        ]`;\n      }\n      if (jsModule.runs) {\n        runsModule = ',\\n        \"runs\": true';\n      }\n      const pluginModule = {\n        clobber: clobberKey,\n        merge: mergeKey,\n        // mimics Cordova's module name logic if the name attr is missing\n        pluginContent: `{\n          \"id\": \"${pluginId + '.' + (jsModule.$.name || jsModule.$.src.match(/([^/]+)\\.js/)[1])}\",\n          \"file\": \"plugins/${pluginId}/${jsModule.$.src}\",\n          \"pluginId\": \"${pluginId}\"${clobbersModule}${mergesModule}${runsModule}\n        }`,\n      };\n      pluginModules.push(pluginModule);\n    });\n    pluginExports.push(`\"${pluginId}\": \"${p.xml.$.version}\"`);\n  });\n  return `\n  cordova.define('cordova/plugin_list', function(require, exports, module) {\n    module.exports = [\n      ${pluginModules\n        .sort(\n          (a, b) =>\n            a.clobber && b.clobber // Clobbers in alpha order\n              ? a.clobber.localeCompare(b.clobber)\n              : a.clobber || b.clobber // Clobbers before anything else\n                ? b.clobber.localeCompare(a.clobber)\n                : a.merge.localeCompare(b.merge), // Merges in alpha order\n        )\n        .map((e) => e.pluginContent)\n        .join(',\\n      ')}\n    ];\n    module.exports.metadata =\n    // TOP OF METADATA\n    {\n      ${pluginExports.join(',\\n      ')}\n    };\n    // BOTTOM OF METADATA\n    });\n    `;\n}\n\n/**\n * Build the plugins/* files for each Cordova plugin installed.\n */\nexport async function copyPluginsJS(config: Config, cordovaPlugins: Plugin[], platform: string): Promise<void> {\n  const webDir = await getWebDir(config, platform);\n  const pluginsDir = join(webDir, 'plugins');\n  const cordovaPluginsJSFile = join(webDir, 'cordova_plugins.js');\n  await removePluginFiles(config, platform);\n  await Promise.all(\n    cordovaPlugins.map(async (p) => {\n      const pluginId = p.xml.$.id;\n      const pluginDir = join(pluginsDir, pluginId, 'www');\n      await ensureDir(pluginDir);\n      const jsModules = getJSModules(p, platform);\n      await Promise.all(\n        jsModules.map(async (jsModule: any) => {\n          const filePath = join(webDir, 'plugins', pluginId, jsModule.$.src);\n          await copy(join(p.rootPath, jsModule.$.src), filePath);\n          let data = await readFile(filePath, { encoding: 'utf-8' });\n          data = data.trim();\n          // mimics Cordova's module name logic if the name attr is missing\n          const name = pluginId + '.' + (jsModule.$.name || basename(jsModule.$.src, extname(jsModule.$.src)));\n          data = `cordova.define(\"${name}\", function(require, exports, module) { \\n${data}\\n});`;\n          data = data.replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script\\s*>/gi, '');\n          await writeFile(filePath, data, { encoding: 'utf-8' });\n        }),\n      );\n      const assets = getAssets(p, platform);\n      await Promise.all(\n        assets.map(async (asset: any) => {\n          const filePath = join(webDir, asset.$.target);\n          await copy(join(p.rootPath, asset.$.src), filePath);\n        }),\n      );\n    }),\n  );\n  await writeFile(cordovaPluginsJSFile, generateCordovaPluginsJSFile(config, cordovaPlugins, platform));\n}\n\nexport async function copyCordovaJS(config: Config, platform: string): Promise<void> {\n  const cordovaPath = resolveNode(config.app.rootDir, '@capacitor/core', 'cordova.js');\n  if (!cordovaPath) {\n    fatal(\n      `Unable to find ${c.strong('node_modules/@capacitor/core/cordova.js')}.\\n` +\n        `Are you sure ${c.strong('@capacitor/core')} is installed?`,\n    );\n  }\n\n  return copy(cordovaPath, join(await getWebDir(config, platform), 'cordova.js'));\n}\n\nexport async function createEmptyCordovaJS(config: Config, platform: string): Promise<void> {\n  const webDir = await getWebDir(config, platform);\n  await writeFile(join(webDir, 'cordova.js'), '');\n  await writeFile(join(webDir, 'cordova_plugins.js'), '');\n}\n\nexport async function removePluginFiles(config: Config, platform: string): Promise<void> {\n  const webDir = await getWebDir(config, platform);\n  const pluginsDir = join(webDir, 'plugins');\n  const cordovaPluginsJSFile = join(webDir, 'cordova_plugins.js');\n  await remove(pluginsDir);\n  await remove(cordovaPluginsJSFile);\n}\n\nexport async function autoGenerateConfig(config: Config, cordovaPlugins: Plugin[], platform: string): Promise<void> {\n  let xmlDir = join(config.android.resDirAbs, 'xml');\n  const fileName = 'config.xml';\n  if (platform === 'ios') {\n    xmlDir = config.ios.nativeTargetDirAbs;\n  }\n  await ensureDir(xmlDir);\n  const cordovaConfigXMLFile = join(xmlDir, fileName);\n  await remove(cordovaConfigXMLFile);\n  const pluginEntries: any[] = [];\n  cordovaPlugins.map((p) => {\n    const currentPlatform = getPluginPlatform(p, platform);\n    if (currentPlatform) {\n      const configFiles = currentPlatform['config-file'];\n      if (configFiles) {\n        const configXMLEntries = configFiles.filter(function (item: any) {\n          return item.$?.target.includes(fileName);\n        });\n        configXMLEntries.map((entry: any) => {\n          if (entry.feature) {\n            const feature = { feature: entry.feature };\n            pluginEntries.push(feature);\n          }\n        });\n      }\n    }\n  });\n\n  let accessOriginString: string[] = [];\n  if (config.app.extConfig?.cordova?.accessOrigins) {\n    accessOriginString = await Promise.all(\n      config.app.extConfig.cordova.accessOrigins.map(async (host): Promise<string> => {\n        return `\n  <access origin=\"${host}\" />`;\n      }),\n    );\n  } else {\n    accessOriginString.push(`<access origin=\"*\" />`);\n  }\n  const pluginEntriesString: string[] = await Promise.all(\n    pluginEntries.map(async (item): Promise<string> => {\n      const xmlString = await writeXML(item);\n      return xmlString;\n    }),\n  );\n  let pluginPreferencesString: string[] = [];\n  if (config.app.extConfig?.cordova?.preferences) {\n    pluginPreferencesString = await Promise.all(\n      Object.entries(config.app.extConfig.cordova.preferences).map(async ([key, value]): Promise<string> => {\n        return `\n  <preference name=\"${key}\" value=\"${value}\" />`;\n      }),\n    );\n  }\n  const content = `<?xml version='1.0' encoding='utf-8'?>\n<widget version=\"1.0.0\" xmlns=\"http://www.w3.org/ns/widgets\" xmlns:cdv=\"http://cordova.apache.org/ns/1.0\">\n  ${accessOriginString.join('')}\n  ${pluginEntriesString.join('')}\n  ${pluginPreferencesString.join('')}\n</widget>`;\n  await writeFile(cordovaConfigXMLFile, content);\n}\n\nasync function getWebDir(config: Config, platform: string): Promise<string> {\n  if (platform === 'ios') {\n    return config.ios.webDirAbs;\n  }\n  if (platform === 'android') {\n    return config.android.webDirAbs;\n  }\n  return '';\n}\n\nexport async function handleCordovaPluginsJS(\n  cordovaPlugins: Plugin[],\n  config: Config,\n  platform: string,\n): Promise<void> {\n  const webDir = await getWebDir(config, platform);\n  await mkdirp(webDir);\n\n  if (cordovaPlugins.length > 0) {\n    printPlugins(cordovaPlugins, platform, 'cordova');\n    await copyCordovaJS(config, platform);\n    await copyPluginsJS(config, cordovaPlugins, platform);\n  } else {\n    await removePluginFiles(config, platform);\n    await createEmptyCordovaJS(config, platform);\n  }\n  await autoGenerateConfig(config, cordovaPlugins, platform);\n}\n\nexport async function getCordovaPlugins(config: Config, platform: string): Promise<Plugin[]> {\n  const allPlugins = await getPlugins(config, platform);\n  let plugins: Plugin[] = [];\n  if (platform === config.ios.name) {\n    plugins = await getIOSPlugins(allPlugins);\n  } else if (platform === config.android.name) {\n    plugins = await getAndroidPlugins(allPlugins);\n  }\n  return plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova);\n}\n\nexport async function logCordovaManualSteps(cordovaPlugins: Plugin[], config: Config, platform: string): Promise<void> {\n  cordovaPlugins.map((p) => {\n    const editConfig = getPlatformElement(p, platform, 'edit-config');\n    const configFile = getPlatformElement(p, platform, 'config-file');\n    editConfig.concat(configFile).map(async (configElement: any) => {\n      if (configElement.$ && !configElement.$.target.includes('config.xml')) {\n        if (platform === config.ios.name) {\n          if (configElement.$.target.includes('Info.plist')) {\n            logiOSPlist(configElement, config, p);\n          }\n        }\n      }\n    });\n  });\n}\n\nasync function logiOSPlist(configElement: any, config: Config, plugin: Plugin) {\n  let plistPath = resolve(config.ios.nativeTargetDirAbs, 'Info.plist');\n  if (config.app.extConfig.ios?.scheme) {\n    plistPath = resolve(config.ios.nativeProjectDirAbs, `${config.app.extConfig.ios?.scheme}-Info.plist`);\n  }\n  if (!(await pathExists(plistPath))) {\n    plistPath = resolve(config.ios.nativeTargetDirAbs, 'Base.lproj', 'Info.plist');\n  }\n  if (await pathExists(plistPath)) {\n    const xmlMeta = await readXML(plistPath);\n    const data = await readFile(plistPath, { encoding: 'utf-8' });\n    const trimmedPlistData = data.replace(/(\\t|\\r|\\n)/g, '');\n    const plistData = plist.parse(data) as PlistObject;\n    const dict = xmlMeta.plist.dict.pop();\n    if (!dict.key.includes(configElement.$.parent)) {\n      let xml = buildConfigFileXml(configElement);\n      xml = `<key>${configElement.$.parent}</key>${getConfigFileTagContent(xml)}`;\n      logger.warn(`Configuration required for ${c.strong(plugin.id)}.\\n` + `Add the following to Info.plist:\\n` + xml);\n    } else if (configElement.array || configElement.dict) {\n      if (configElement.array && configElement.array.length > 0 && configElement.array[0].string) {\n        let xml = '';\n        configElement.array[0].string.map((element: any) => {\n          const d = plistData[configElement.$.parent];\n          if (Array.isArray(d) && !d.includes(element)) {\n            xml = xml.concat(`<string>${element}</string>\\n`);\n          }\n        });\n        if (xml.length > 0) {\n          logger.warn(\n            `Configuration required for ${c.strong(plugin.id)}.\\n` +\n              `Add the following in the existing ${c.strong(configElement.$.parent)} array of your Info.plist:\\n` +\n              xml,\n          );\n        }\n      } else {\n        let xml = buildConfigFileXml(configElement);\n        xml = `<key>${configElement.$.parent}</key>${getConfigFileTagContent(xml)}`;\n        xml = `<plist version=\"1.0\"><dict>${xml}</dict></plist>`;\n\n        const parseXmlToSearchable = (childElementsObj: any[], arrayToAddTo: any[]) => {\n          for (const childElement of childElementsObj) {\n            const childElementName = childElement['#name'];\n            const toAdd: {\n              name: string;\n              attrs?: { [key: string]: any } | undefined;\n              children?: any[] | undefined;\n              value?: any | undefined;\n            } = { name: childElementName };\n            if (childElementName === 'key' || childElementName === 'string') {\n              toAdd.value = childElement['_'];\n            } else {\n              if (childElement['$']) {\n                toAdd.attrs = { ...childElement['$'] };\n              }\n              if (childElement['$$']) {\n                toAdd.children = [];\n                parseXmlToSearchable(childElement['$$'], toAdd['children']);\n              }\n            }\n            arrayToAddTo.push(toAdd);\n          }\n        };\n\n        const existingElements = parseXML(trimmedPlistData, {\n          explicitChildren: true,\n          trim: true,\n          preserveChildrenOrder: true,\n        });\n        const parsedExistingElements: any[] = [];\n        const rootKeyOfExistingElements = Object.keys(existingElements)[0];\n        const rootOfExistingElementsToAdd: {\n          name: string;\n          attrs?: { [key: string]: any } | undefined;\n          children: any[];\n        } = { name: rootKeyOfExistingElements, children: [] };\n        if (existingElements[rootKeyOfExistingElements]['$']) {\n          rootOfExistingElementsToAdd.attrs = {\n            ...existingElements[rootKeyOfExistingElements]['$'],\n          };\n        }\n        parseXmlToSearchable(\n          existingElements[rootKeyOfExistingElements]['$$'],\n          rootOfExistingElementsToAdd['children'],\n        );\n        parsedExistingElements.push(rootOfExistingElementsToAdd);\n\n        const requiredElements = parseXML(xml, {\n          explicitChildren: true,\n          trim: true,\n          preserveChildrenOrder: true,\n        });\n        const parsedRequiredElements: any[] = [];\n        const rootKeyOfRequiredElements = Object.keys(requiredElements)[0];\n        const rootOfRequiredElementsToAdd: {\n          name: string;\n          attrs?: { [key: string]: any } | undefined;\n          children: any[];\n        } = { name: rootKeyOfRequiredElements, children: [] };\n        if (requiredElements[rootKeyOfRequiredElements]['$']) {\n          rootOfRequiredElementsToAdd.attrs = {\n            ...requiredElements[rootKeyOfRequiredElements]['$'],\n          };\n        }\n        parseXmlToSearchable(\n          requiredElements[rootKeyOfRequiredElements]['$$'],\n          rootOfRequiredElementsToAdd['children'],\n        );\n        parsedRequiredElements.push(rootOfRequiredElementsToAdd);\n\n        const doesContainElements = (requiredElementsArray: any[], existingElementsArray: any[]) => {\n          for (const requiredElement of requiredElementsArray) {\n            if (requiredElement.name === 'key' || requiredElement.name === 'string') {\n              let foundMatch = false;\n              for (const existingElement of existingElementsArray) {\n                if (\n                  existingElement.name === requiredElement.name &&\n                  (existingElement.value === requiredElement.value ||\n                    /^[$].{1,}$/.test((requiredElement.value as string).trim()))\n                ) {\n                  foundMatch = true;\n                  break;\n                }\n              }\n              if (!foundMatch) {\n                return false;\n              }\n            } else {\n              let foundMatch = false;\n              for (const existingElement of existingElementsArray) {\n                if (existingElement.name === requiredElement.name) {\n                  if ((requiredElement.children !== undefined) === (existingElement.children !== undefined)) {\n                    if (doesContainElements(requiredElement.children, existingElement.children)) {\n                      foundMatch = true;\n                      break;\n                    }\n                  }\n                }\n              }\n              if (!foundMatch) {\n                return false;\n              }\n            }\n          }\n          return true;\n        };\n\n        if (!doesContainElements(parsedRequiredElements, parsedExistingElements)) {\n          logPossibleMissingItem(configElement, plugin);\n        }\n      }\n    }\n  } else {\n    logPossibleMissingItem(configElement, plugin);\n  }\n}\n\nfunction logPossibleMissingItem(configElement: any, plugin: Plugin) {\n  let xml = buildConfigFileXml(configElement);\n  xml = getConfigFileTagContent(xml);\n  xml = removeOuterTags(xml);\n  logger.warn(\n    `Configuration might be missing for ${c.strong(plugin.id)}.\\n` +\n      `Add the following to the existing ${c.strong(configElement.$.parent)} entry of Info.plist:\\n` +\n      xml,\n  );\n}\n\nfunction buildConfigFileXml(configElement: any) {\n  return buildXmlElement(configElement, 'config-file');\n}\n\nfunction getConfigFileTagContent(str: string) {\n  return str.replace(/<config-file.+\">|<\\/config-file>/g, '');\n}\n\nfunction removeOuterTags(str: string) {\n  const start = str.indexOf('>') + 1;\n  const end = str.lastIndexOf('<');\n  return str.substring(start, end);\n}\n\nexport async function checkPluginDependencies(\n  plugins: Plugin[],\n  platform: string,\n  failOnMissingDeps = false,\n): Promise<void> {\n  const pluginDeps: Map<string, string[]> = new Map();\n  const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova);\n  const incompatible = plugins.filter((p) => getPluginType(p, platform) === PluginType.Incompatible);\n  await Promise.all(\n    cordovaPlugins.map(async (p) => {\n      let allDependencies: string[] = [];\n      allDependencies = allDependencies.concat(getPlatformElement(p, platform, 'dependency'));\n      if (p.xml['dependency']) {\n        allDependencies = allDependencies.concat(p.xml['dependency']);\n      }\n      allDependencies = allDependencies.filter(\n        (dep: any) =>\n          !getIncompatibleCordovaPlugins(platform).includes(dep.$.id) &&\n          incompatible.filter((p) => p.id === dep.$.id || p.xml.$.id === dep.$.id).length === 0,\n      );\n      if (allDependencies) {\n        await Promise.all(\n          allDependencies.map(async (dep: any) => {\n            let plugin = dep.$.id;\n            let version = dep.$.version;\n            if (plugin.includes('@') && plugin.indexOf('@') !== 0) {\n              [plugin, version] = plugin.split('@');\n            }\n            if (cordovaPlugins.filter((p) => p.id === plugin || p.xml.$.id === plugin).length === 0) {\n              if (dep.$.url?.startsWith('http')) {\n                plugin = dep.$.url;\n                version = dep.$.commit;\n              }\n              const deps = pluginDeps.get(p.id) || [];\n              deps.push(`${plugin}${version ? c.weak(` (${version})`) : ''}`);\n              pluginDeps.set(p.id, deps);\n            }\n          }),\n        );\n      }\n    }),\n  );\n\n  if (pluginDeps.size > 0) {\n    let msg =\n      `${c.failure(c.strong('Plugins are missing dependencies.'))}\\n` +\n      `Cordova plugin dependencies must be installed in your project (e.g. w/ ${c.input('npm install')}).\\n`;\n    for (const [plugin, deps] of pluginDeps.entries()) {\n      msg += `\\n  ${c.strong(plugin)} is missing dependencies:\\n` + deps.map((d) => `    - ${d}`).join('\\n');\n    }\n\n    if (failOnMissingDeps) {\n      fatal(`${msg}\\n`);\n    }\n\n    logger.warn(`${msg}\\n`);\n  }\n}\n\nexport function getIncompatibleCordovaPlugins(platform: string): string[] {\n  const pluginList = [\n    'cordova-plugin-splashscreen',\n    'cordova-plugin-ionic-webview',\n    'cordova-plugin-crosswalk-webview',\n    'cordova-plugin-wkwebview-engine',\n    'cordova-plugin-console',\n    'cordova-plugin-music-controls',\n    'cordova-plugin-add-swift-support',\n    'cordova-plugin-ionic-keyboard',\n    'cordova-plugin-braintree',\n    '@ionic-enterprise/filesystem',\n    '@ionic-enterprise/keyboard',\n    '@ionic-enterprise/splashscreen',\n    'cordova-support-google-services',\n  ];\n  if (platform === 'ios') {\n    pluginList.push('cordova-plugin-statusbar', '@ionic-enterprise/statusbar', 'SalesforceMobileSDK-CordovaPlugin');\n  }\n  if (platform === 'android') {\n    pluginList.push('cordova-plugin-compat');\n  }\n  return pluginList;\n}\n\nexport function needsStaticPod(plugin: Plugin): boolean {\n  return useFrameworks(plugin);\n}\n\nfunction useFrameworks(plugin: Plugin): boolean {\n  const podspecs = getPlatformElement(plugin, 'ios', 'podspec');\n  const frameworkPods = podspecs.filter(\n    (podspec: any) => podspec.pods.filter((pods: any) => pods.$ && pods.$['use-frameworks'] === 'true').length > 0,\n  );\n  return frameworkPods.length > 0;\n}\n\nexport async function getCordovaPreferences(config: Config): Promise<any> {\n  const configXml = join(config.app.rootDir, 'config.xml');\n  let cordova: any = {};\n  if (await pathExists(configXml)) {\n    cordova.preferences = {};\n    const xmlMeta = await readXML(configXml);\n    if (xmlMeta.widget.preference) {\n      xmlMeta.widget.preference.map((pref: any) => {\n        cordova.preferences[pref.$.name] = pref.$.value;\n      });\n    }\n  }\n  if (cordova.preferences && Object.keys(cordova.preferences).length > 0) {\n    if (isInteractive()) {\n      const answers = await logPrompt(\n        `${c.strong(`Cordova preferences can be automatically ported to ${c.strong(config.app.extConfigName)}.`)}\\n` +\n          `Keep in mind: Not all values can be automatically migrated from ${c.strong(\n            'config.xml',\n          )}. There may be more work to do.\\n` +\n          `More info: ${c.strong('https://capacitorjs.com/docs/cordova/migrating-from-cordova-to-capacitor')}`,\n        {\n          type: 'confirm',\n          name: 'confirm',\n          message: `Migrate Cordova preferences from config.xml?`,\n          initial: true,\n        },\n      );\n      if (answers.confirm) {\n        if (config.app.extConfig?.cordova?.preferences) {\n          const answers = await prompts(\n            [\n              {\n                type: 'confirm',\n                name: 'confirm',\n                message: `${config.app.extConfigName} already contains Cordova preferences. Overwrite?`,\n              },\n            ],\n            { onCancel: () => process.exit(1) },\n          );\n          if (!answers.confirm) {\n            cordova = config.app.extConfig?.cordova;\n          }\n        }\n      } else {\n        cordova = config.app.extConfig?.cordova;\n      }\n    }\n  } else {\n    cordova = config.app.extConfig?.cordova;\n  }\n  return cordova;\n}\n\nexport async function writeCordovaAndroidManifest(\n  cordovaPlugins: Plugin[],\n  config: Config,\n  platform: string,\n  cleartext?: boolean,\n): Promise<void> {\n  const manifestPath = join(config.android.cordovaPluginsDirAbs, 'src', 'main', 'AndroidManifest.xml');\n  const rootXMLEntries: any[] = [];\n  const applicationXMLEntries: any[] = [];\n  const applicationXMLAttributes: any[] = [];\n  let prefsArray: any[] = [];\n  cordovaPlugins.map(async (p) => {\n    const editConfig = getPlatformElement(p, platform, 'edit-config');\n    const configFile = getPlatformElement(p, platform, 'config-file');\n    prefsArray = prefsArray.concat(getAllElements(p, platform, 'preference'));\n    editConfig.concat(configFile).map(async (configElement: any) => {\n      if (\n        configElement.$ &&\n        (configElement.$.target?.includes('AndroidManifest.xml') ||\n          configElement.$.file?.includes('AndroidManifest.xml'))\n      ) {\n        const keys = Object.keys(configElement).filter((k) => k !== '$');\n        keys.map((k) => {\n          configElement[k].map(async (e: any) => {\n            const xmlElement = buildXmlElement(e, k);\n            const pathParts = getPathParts(configElement.$.parent || configElement.$.target);\n            if (pathParts.length > 1) {\n              if (pathParts.pop() === 'application') {\n                if (configElement.$.mode && configElement.$.mode === 'merge' && xmlElement.startsWith('<application')) {\n                  Object.keys(e.$).map((ek: any) => {\n                    applicationXMLAttributes.push(`${ek}=\"${e.$[ek]}\"`);\n                  });\n                } else if (\n                  !applicationXMLEntries.includes(xmlElement) &&\n                  !contains(applicationXMLEntries, xmlElement, k)\n                ) {\n                  applicationXMLEntries.push(xmlElement);\n                }\n              } else {\n                const manifestPathOfCapApp = join(config.android.appDirAbs, 'src', 'main', 'AndroidManifest.xml');\n                const manifestContentTrimmed = (await readFile(manifestPathOfCapApp))\n                  .toString()\n                  .trim()\n                  .replace(/\\n|\\t|\\r/g, '')\n                  .replace(/[\\s]{1,}</g, '<')\n                  .replace(/>[\\s]{1,}/g, '>')\n                  .replace(/[\\s]{2,}/g, ' ');\n                const requiredManifestContentTrimmed = xmlElement\n                  .trim()\n                  .replace(/\\n|\\t|\\r/g, '')\n                  .replace(/[\\s]{1,}</g, '<')\n                  .replace(/>[\\s]{1,}/g, '>')\n                  .replace(/[\\s]{2,}/g, ' ');\n                const pathPartList = getPathParts(configElement.$.parent || configElement.$.target);\n\n                const doesXmlManifestContainRequiredInfo = (\n                  requiredElements: any,\n                  existingElements: any,\n                  pathTarget: string[],\n                ): boolean => {\n                  const findElementsToSearchIn = (existingElements: any[], pathTarget: string[]): any[] => {\n                    const parts = [...pathTarget];\n                    const elementsToSearchNextIn = [];\n                    for (const existingElement of existingElements) {\n                      if (existingElement.name === pathTarget[0]) {\n                        if (existingElement.children) {\n                          for (const el of existingElement.children) {\n                            elementsToSearchNextIn.push(el);\n                          }\n                        } else {\n                          elementsToSearchNextIn.push(existingElement);\n                        }\n                      }\n                    }\n                    if (elementsToSearchNextIn.length === 0) {\n                      return [];\n                    } else {\n                      parts.splice(0, 1);\n                      if (parts.length <= 0) {\n                        return elementsToSearchNextIn;\n                      } else {\n                        return findElementsToSearchIn(elementsToSearchNextIn, parts);\n                      }\n                    }\n                  };\n                  const parseXmlToSearchable = (childElementsObj: any, arrayToAddTo: any[]) => {\n                    for (const childElementKey of Object.keys(childElementsObj)) {\n                      for (const occurannceOfElement of childElementsObj[childElementKey]) {\n                        const toAdd: {\n                          name: string;\n                          attrs?: { [key: string]: any } | undefined;\n                          children?: any[] | undefined;\n                        } = { name: childElementKey };\n                        if (occurannceOfElement['$']) {\n                          toAdd.attrs = { ...occurannceOfElement['$'] };\n                        }\n                        if (occurannceOfElement['$$']) {\n                          toAdd.children = [];\n                          parseXmlToSearchable(occurannceOfElement['$$'], toAdd['children']);\n                        }\n                        arrayToAddTo.push(toAdd);\n                      }\n                    }\n                  };\n                  const doesElementMatch = (requiredElement: any, existingElement: any): boolean => {\n                    if (requiredElement.name !== existingElement.name) {\n                      return false;\n                    }\n                    if ((requiredElement.attrs !== undefined) !== (existingElement.attrs !== undefined)) {\n                      return false;\n                    } else {\n                      if (requiredElement.attrs !== undefined) {\n                        const requiredELementAttrKeys = Object.keys(requiredElement.attrs);\n                        for (const key of requiredELementAttrKeys) {\n                          if (!/^[$].{1,}$/.test((requiredElement.attrs[key] as string).trim())) {\n                            if (requiredElement.attrs[key] !== existingElement.attrs[key]) {\n                              return false;\n                            }\n                          }\n                        }\n                      }\n                    }\n                    if (\n                      (requiredElement.children !== undefined) !== (existingElement.children !== undefined) &&\n                      requiredElement.children?.length !== 0\n                    ) {\n                      return false;\n                    } else {\n                      if (requiredElement.children !== undefined) {\n                        // each req element is in existing element\n                        for (const requiredElementItem of requiredElement.children) {\n                          let foundRequiredElement = false;\n                          for (const existingElementItem of existingElement.children) {\n                            const foundRequiredElementIn = doesElementMatch(requiredElementItem, existingElementItem);\n                            if (foundRequiredElementIn) {\n                              foundRequiredElement = true;\n                              break;\n                            }\n                          }\n                          if (!foundRequiredElement) {\n                            return false;\n                          }\n                        }\n                      } else {\n                        if (requiredElement.children === undefined && existingElement.children === undefined) {\n                          return true;\n                        } else {\n                          let foundRequiredElement = false;\n                          for (const existingElementItem of existingElement.children) {\n                            const foundRequiredElementIn = doesElementMatch(requiredElement, existingElementItem);\n                            if (foundRequiredElementIn) {\n                              foundRequiredElement = true;\n                              break;\n                            }\n                          }\n                          if (!foundRequiredElement) {\n                            return false;\n                          }\n                        }\n                      }\n                    }\n                    return true;\n                  };\n                  const parsedExistingElements: any[] = [];\n                  const rootKeyOfExistingElements = Object.keys(existingElements)[0];\n                  const rootOfExistingElementsToAdd: {\n                    name: string;\n                    attrs?: { [key: string]: any } | undefined;\n                    children: any[];\n                  } = { name: rootKeyOfExistingElements, children: [] };\n                  if (existingElements[rootKeyOfExistingElements]['$']) {\n                    rootOfExistingElementsToAdd.attrs = {\n                      ...existingElements[rootKeyOfExistingElements]['$'],\n                    };\n                  }\n                  parseXmlToSearchable(\n                    existingElements[rootKeyOfExistingElements]['$$'],\n                    rootOfExistingElementsToAdd['children'],\n                  );\n                  parsedExistingElements.push(rootOfExistingElementsToAdd);\n                  const parsedRequiredElements: any[] = [];\n                  const rootKeyOfRequiredElements = Object.keys(requiredElements)[0];\n                  const rootOfRequiredElementsToAdd: {\n                    name: string;\n                    attrs?: { [key: string]: any } | undefined;\n                    children: any[];\n                  } = { name: rootKeyOfRequiredElements, children: [] };\n                  if (requiredElements[rootKeyOfRequiredElements]['$']) {\n                    rootOfRequiredElementsToAdd.attrs = {\n                      ...requiredElements[rootKeyOfRequiredElements]['$'],\n                    };\n                  }\n                  if (requiredElements[rootKeyOfRequiredElements]['$$'] !== undefined) {\n                    parseXmlToSearchable(\n                      requiredElements[rootKeyOfRequiredElements]['$$'],\n                      rootOfRequiredElementsToAdd['children'],\n                    );\n                  }\n                  parsedRequiredElements.push(rootOfRequiredElementsToAdd);\n                  const elementsToSearch = findElementsToSearchIn(parsedExistingElements, pathTarget);\n\n                  for (const requiredElement of parsedRequiredElements) {\n                    let foundMatch = false;\n                    for (const existingElement of elementsToSearch) {\n                      const doesContain = doesElementMatch(requiredElement, existingElement);\n                      if (doesContain) {\n                        foundMatch = true;\n                        break;\n                      }\n                    }\n                    if (!foundMatch) {\n                      return false;\n                    }\n                  }\n                  return true;\n                };\n\n                if (\n                  !doesXmlManifestContainRequiredInfo(\n                    parseXML(requiredManifestContentTrimmed, {\n                      explicitChildren: true,\n                      trim: true,\n                    }),\n                    parseXML(manifestContentTrimmed, {\n                      explicitChildren: true,\n                      trim: true,\n                    }),\n                    pathPartList,\n                  )\n                ) {\n                  logger.warn(\n                    `Android Configuration required for ${c.strong(p.id)}.\\n` +\n                      `Add the following to AndroidManifest.xml:\\n` +\n                      xmlElement,\n                  );\n                }\n              }\n            } else {\n              if (!rootXMLEntries.includes(xmlElement) && !contains(rootXMLEntries, xmlElement, k)) {\n                rootXMLEntries.push(xmlElement);\n              }\n            }\n          });\n        });\n      }\n    });\n  });\n  const cleartextString = 'android:usesCleartextTraffic=\"true\"';\n  const cleartextValue =\n    (cleartext || config.app.extConfig.server?.cleartext) && !applicationXMLAttributes.includes(cleartextString)\n      ? cleartextString\n      : '';\n  let content = `<?xml version='1.0' encoding='utf-8'?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\nxmlns:amazon=\"http://schemas.amazon.com/apk/res/android\">\n<application ${applicationXMLAttributes.join('\\n')} ${cleartextValue}>\n${applicationXMLEntries.join('\\n')}\n</application>\n${rootXMLEntries.join('\\n')}\n</manifest>`;\n  content = content.replace(new RegExp('$PACKAGE_NAME'.replace('$', '\\\\$&'), 'g'), '${applicationId}');\n  for (const preference of prefsArray) {\n    content = content.replace(new RegExp(('$' + preference.$.name).replace('$', '\\\\$&'), 'g'), preference.$.default);\n  }\n  if (await pathExists(manifestPath)) {\n    await writeFile(manifestPath, content);\n  }\n}\n\nfunction getPathParts(path: string) {\n  const rootPath = 'manifest';\n  path = path.replace('/*', rootPath);\n  const parts = path.split('/').filter((part) => part !== '');\n  if (parts.length > 1 || parts.includes(rootPath)) {\n    return parts;\n  }\n  return [rootPath, path];\n}\n\nfunction contains(entries: any[], obj: any, k: string) {\n  const element = parseXML(obj);\n  for (const entry of entries) {\n    const current = parseXML(entry);\n    if (\n      element &&\n      current &&\n      current[k] &&\n      element[k] &&\n      current[k].$ &&\n      element[k].$ &&\n      element[k].$['android:name'] === current[k].$['android:name']\n    ) {\n      return true;\n    }\n  }\n  return false;\n}\n"
  },
  {
    "path": "cli/src/declarations.ts",
    "content": "export interface CapacitorConfig {\n  /**\n   * The unique identifier of your packaged app.\n   *\n   * This is also known as the Bundle ID in iOS and the Application ID in\n   * Android. It must be in reverse domain name notation, generally\n   * representing a domain name that you or your company owns.\n   *\n   * @since 1.0.0\n   */\n  appId?: string;\n\n  /**\n   * The human-friendly name of your app.\n   *\n   * This should be what you'd see in the App Store, but can be changed after\n   * within each native platform after it is generated.\n   *\n   * @since 1.0.0\n   */\n  appName?: string;\n\n  /**\n   * The directory of your compiled web assets.\n   *\n   * This directory should contain the final `index.html` of your app.\n   *\n   * @since 1.0.0\n   */\n  webDir?: string;\n\n  /**\n   * The build configuration (as defined by the native app) under which Capacitor\n   * will send statements to the log system. This applies to log statements in\n   * native code as well as statements redirected from JavaScript (`console.debug`,\n   * `console.error`, etc.). Enabling logging will let statements render in the\n   * Xcode and Android Studio windows but can leak information on device if enabled\n   * in released builds.\n   *\n   * 'none' = logs are never produced\n   * 'debug' = logs are produced in debug builds but not production builds\n   * 'production' = logs are always produced\n   *\n   * @since 3.0.0\n   * @default debug\n   */\n  loggingBehavior?: 'none' | 'debug' | 'production';\n\n  /**\n   * User agent of Capacitor Web View.\n   *\n   * @since 1.4.0\n   */\n  overrideUserAgent?: string;\n\n  /**\n   * String to append to the original user agent of Capacitor Web View.\n   *\n   * This is disregarded if `overrideUserAgent` is used.\n   *\n   * @since 1.4.0\n   */\n  appendUserAgent?: string;\n\n  /**\n   * Background color of the Capacitor Web View.\n   *\n   * @since 1.1.0\n   */\n  backgroundColor?: string;\n\n  /**\n   * Enable zooming within the Capacitor Web View.\n   *\n   * @default false\n   * @since 6.0.0\n   */\n  zoomEnabled?: boolean;\n\n  /**\n   * Whether to give the webview initial focus.\n   *\n   * @since 7.0.0\n   * @default true\n   */\n  initialFocus?: boolean;\n\n  android?: {\n    /**\n     * Specify a custom path to the native Android project.\n     *\n     * @since 3.0.0\n     * @default android\n     */\n    path?: string;\n\n    /**\n     * User agent of Capacitor Web View on Android.\n     *\n     * Overrides global `overrideUserAgent` option.\n     *\n     * @since 1.4.0\n     */\n    overrideUserAgent?: string;\n\n    /**\n     * String to append to the original user agent of Capacitor Web View for Android.\n     *\n     * Overrides global `appendUserAgent` option.\n     *\n     * This is disregarded if `overrideUserAgent` is used.\n     *\n     * @since 1.4.0\n     */\n    appendUserAgent?: string;\n\n    /**\n     * Background color of the Capacitor Web View for Android.\n     *\n     * Overrides global `backgroundColor` option.\n     *\n     * @since 1.1.0\n     */\n    backgroundColor?: string;\n\n    /**\n     * Enable zooming within the Capacitor Web View for Android.\n     *\n     * @default false\n     * @since 6.0.0\n     */\n    zoomEnabled?: boolean;\n\n    /**\n     * Enable mixed content in the Capacitor Web View for Android.\n     *\n     * [Mixed\n     * content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content)\n     * is disabled by default for security. During development, you may need to\n     * enable it to allow the Web View to load files from different schemes.\n     *\n     * **This is not intended for use in production.**\n     *\n     * @since 1.0.0\n     * @default false\n     */\n    allowMixedContent?: boolean;\n\n    /**\n     * This enables a simpler keyboard which may have some limitations.\n     *\n     * This will capture JS keys using an alternative\n     * [`InputConnection`](https://developer.android.com/reference/android/view/inputmethod/InputConnection).\n     *\n     * @since 1.0.0\n     * @default false\n     */\n    captureInput?: boolean;\n\n    /**\n     * Always enable debuggable web content.\n     *\n     * This is automatically enabled during development.\n     *\n     * @since 1.0.0\n     * @default false\n     */\n    webContentsDebuggingEnabled?: boolean;\n\n    /**\n     * The build configuration under which Capacitor will generate logs on Android.\n     *\n     * Overrides global `loggingBehavior` option.\n     *\n     * @since 3.0.0\n     * @default debug\n     */\n    loggingBehavior?: 'none' | 'debug' | 'production';\n\n    /**\n     * Allowlist of plugins to include during `npx cap sync` for Android.\n     *\n     * Overrides global `includePlugins` option.\n     *\n     * @since 3.0.0\n     */\n    includePlugins?: string[];\n\n    /**\n     * Android flavor to use.\n     *\n     * If the app has flavors declared in the `build.gradle`\n     * configure the flavor you want to run with `npx cap run` command.\n     *\n     * @since 3.1.0\n     */\n    flavor?: string;\n\n    /**\n     * Whether to give the webview initial focus.\n     *\n     * Overrides global `initialFocus` option.\n     *\n     * @since 3.5.1\n     * @default true\n     */\n    initialFocus?: boolean;\n\n    /**\n     * The minimum supported webview version on Android supported by your app.\n     *\n     * The minimum supported cannot be lower than version `55`, which is required for Capacitor.\n     *\n     * If the device uses a lower WebView version, an error message will be shown on Logcat.\n     * If `server.errorPath` is configured, the WebView will redirect to that file, so can be\n     * used to show a custom error.\n     *\n     * @since 4.0.0\n     * @default 60\n     */\n    minWebViewVersion?: number;\n\n    /**\n     * The minimum supported Huawei webview version on Android supported by your app.\n     *\n     * The minimum supported cannot be lower than version `10`, which is required for Capacitor.\n     *\n     * If the device uses a lower WebView version, an error message will be shown on Logcat.\n     * If `server.errorPath` is configured, the WebView will redirect to that file, so can be\n     * used to show a custom error.\n     *\n     * @since 4.6.4\n     * @default 10\n     */\n    minHuaweiWebViewVersion?: number;\n\n    buildOptions?: {\n      /**\n       * Path to your keystore\n       *\n       * @since 4.4.0\n       */\n      keystorePath?: string;\n\n      /**\n       * Password to your keystore\n       *\n       * @since 4.4.0\n       */\n      keystorePassword?: string;\n\n      /**\n       * Alias in the keystore to use\n       *\n       * @since 4.4.0\n       */\n      keystoreAlias?: string;\n\n      /**\n       * Password for the alias in the keystore to use\n       *\n       * @since 4.4.0\n       */\n      keystoreAliasPassword?: string;\n\n      /**\n       * Bundle type for your release build\n       *\n       * @since 4.4.0\n       * @default \"AAB\"\n       */\n      releaseType?: 'AAB' | 'APK';\n\n      /**\n       * Program to sign your build with\n       *\n       * @since 5.1.0\n       * @default \"jarsigner\"\n       */\n      signingType?: 'apksigner' | 'jarsigner';\n    };\n\n    /**\n     * Use legacy [addJavascriptInterface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String))\n     * instead of the new and more secure [addWebMessageListener](https://developer.android.com/reference/androidx/webkit/WebViewCompat#addWebMessageListener(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E,androidx.webkit.WebViewCompat.WebMessageListener))\n     *\n     * @since 4.5.0\n     * @default false\n     */\n    useLegacyBridge?: boolean;\n\n    /**\n     * Make service worker requests go through Capacitor bridge.\n     * Set it to false to use your own handling.\n     *\n     * @since 7.0.0\n     * @default true\n     */\n    resolveServiceWorkerRequests?: boolean;\n  };\n\n  ios?: {\n    /**\n     * Specify a custom path to the native iOS project.\n     *\n     * @since 3.0.0\n     * @default ios\n     */\n    path?: string;\n\n    /**\n     * iOS build scheme to use.\n     *\n     * Usually this matches your app's target in Xcode. You can use the\n     * following command to list schemes:\n     *\n     * ```shell\n     * xcodebuild -workspace ios/App/App.xcworkspace -list\n     * ```\n     *\n     * @since 3.0.0\n     * @default App\n     */\n    scheme?: string;\n\n    /**\n     * User agent of Capacitor Web View on iOS.\n     *\n     * Overrides global `overrideUserAgent` option.\n     *\n     * @since 1.4.0\n     */\n    overrideUserAgent?: string;\n\n    /**\n     * String to append to the original user agent of Capacitor Web View for iOS.\n     *\n     * Overrides global `appendUserAgent` option.\n     *\n     * This is disregarded if `overrideUserAgent` is used.\n     *\n     * @since 1.4.0\n     */\n    appendUserAgent?: string;\n\n    /**\n     * Background color of the Capacitor Web View for iOS.\n     *\n     * Overrides global `backgroundColor` option.\n     *\n     * @since 1.1.0\n     */\n    backgroundColor?: string;\n\n    /**\n     * Enable zooming within the Capacitor Web View for iOS.\n     *\n     * @default false\n     * @since 6.0.0\n     */\n    zoomEnabled?: boolean;\n\n    /**\n     * Configure the scroll view's content inset adjustment behavior.\n     *\n     * This will set the\n     * [`contentInsetAdjustmentBehavior`](https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior)\n     * property on the Web View's\n     * [`UIScrollView`](https://developer.apple.com/documentation/uikit/uiscrollview).\n     *\n     * @since 2.0.0\n     * @default never\n     */\n    contentInset?: 'automatic' | 'scrollableAxes' | 'never' | 'always';\n\n    /**\n     * Configure whether the scroll view is scrollable.\n     *\n     * This will set the\n     * [`isScrollEnabled`](https://developer.apple.com/documentation/uikit/uiscrollview/1619395-isscrollenabled)\n     * property on the Web View's\n     * [`UIScrollView`](https://developer.apple.com/documentation/uikit/uiscrollview).\n     *\n     * @since 1.0.0\n     */\n    scrollEnabled?: boolean;\n\n    /**\n     * Configure custom linker flags for compiling Cordova plugins.\n     *\n     * @since 1.0.0\n     * @default []\n     */\n    cordovaLinkerFlags?: string[];\n\n    /**\n     * Allow destination previews when pressing on links.\n     *\n     * This will set the\n     * [`allowsLinkPreview`](https://developer.apple.com/documentation/webkit/wkwebview/1415000-allowslinkpreview)\n     * property on the Web View, instead of using the default value.\n     *\n     * @since 2.0.0\n     */\n    allowsLinkPreview?: boolean;\n\n    /**\n     * The build configuration under which Capacitor will generate logs on iOS.\n     *\n     * Overrides global `loggingBehavior` option.\n     *\n     * @since 3.0.0\n     * @default debug\n     */\n    loggingBehavior?: 'none' | 'debug' | 'production';\n\n    /**\n     * Allowlist of plugins to include during `npx cap sync` for iOS.\n     *\n     * Overrides global `includePlugins` option.\n     *\n     * @since 3.0.0\n     */\n    includePlugins?: string[];\n\n    /**\n     * Sets WKWebView configuration for limitsNavigationsToAppBoundDomains.\n     *\n     * If the Info.plist file includes `WKAppBoundDomains` key, it's recommended to\n     * set this option to true, otherwise some features won't work.\n     * But as side effect, it blocks navigation outside the domains in the\n     * `WKAppBoundDomains` list.\n     * `localhost` (or the value configured as `server.hostname`) also needs to be\n     * added to the `WKAppBoundDomains` list.\n     *\n     * @since 3.1.0\n     * @default false\n     */\n    limitsNavigationsToAppBoundDomains?: boolean;\n\n    /**\n     * The content mode for the web view to use when it loads and renders web content.\n     *\n     * - 'recommended': The content mode that is appropriate for the current device.\n     * - 'desktop': The content mode that represents a desktop experience.\n     * - 'mobile': The content mode that represents a mobile experience.\n     *\n     * @since 4.0.0\n     * @default recommended\n     */\n    preferredContentMode?: 'recommended' | 'desktop' | 'mobile';\n\n    /**\n     * Configure if Capacitor will handle local/push notifications.\n     * Set to false if you want to use your own UNUserNotificationCenter to handle notifications.\n     *\n     * @since 4.5.0\n     * @default true\n     */\n    handleApplicationNotifications?: boolean;\n\n    /**\n     * Using Xcode 14.3, on iOS 16.4 and greater, enable debuggable web content for release builds.\n     *\n     * If not set, it's `true` for development builds.\n     *\n     * @since 4.8.0\n     * @default false\n     */\n    webContentsDebuggingEnabled?: boolean;\n\n    /**\n     * Whether to give the webview initial focus.\n     *\n     * Overrides global `initialFocus` option.\n     *\n     * @since 7.0.0\n     * @default true\n     */\n    initialFocus?: boolean;\n\n    buildOptions?: {\n      /**\n       * The signing style to use when building the app for distribution.\n       *\n       * @since 7.1.0\n       * @default 'automatic'\n       */\n      signingStyle?: 'automatic' | 'manual';\n      /**\n       * The method used by xcodebuild to export the archive\n       *\n       * @since 7.1.0\n       * @default 'app-store-connect'\n       */\n      exportMethod?: string;\n      /**\n       * A certificate name, SHA-1 hash, or automatic selector to use for signing for iOS builds.\n       *\n       * @since 7.1.0\n       */\n      signingCertificate?: string;\n      /**\n       * A provisioning profile name or UUID for iOS builds.\n       *\n       * @since 7.1.0\n       */\n      provisioningProfile?: string;\n    };\n  };\n\n  server?: {\n    /**\n     * Configure the local hostname of the device.\n     *\n     * It is recommended to keep this as `localhost` as it allows the use of\n     * Web APIs that would otherwise require a [secure\n     * context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)\n     * such as\n     * [`navigator.geolocation`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/geolocation)\n     * and\n     * [`MediaDevices.getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia).\n     *\n     * @since 1.0.0\n     * @default localhost\n     */\n    hostname?: string;\n\n    /**\n     * Configure the local scheme on iOS.\n     *\n     * [Can't be set to schemes that the WKWebView already handles, such as http or https](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler)\n     * This can be useful when migrating from\n     * [`cordova-plugin-ionic-webview`](https://github.com/ionic-team/cordova-plugin-ionic-webview),\n     * where the default scheme on iOS is `ionic`.\n     *\n     * @since 1.2.0\n     * @default capacitor\n     */\n    iosScheme?: string;\n\n    /**\n     * Configure the local scheme on Android.\n     *\n     * Custom schemes on Android are unable to change the URL path as of Webview 117. Changing this value from anything other than `http` or `https` can result in your\n     * application unable to resolve routing. If you must change this for some reason, consider using a hash-based url strategy, but there are no guarentees that this\n     * will continue to work long term as allowing non-standard schemes to modify query parameters and url fragments is only allowed for compatibility reasons.\n     * https://ionic.io/blog/capacitor-android-customscheme-issue-with-chrome-117\n     *\n     * @since 1.2.0\n     * @default https\n     */\n    androidScheme?: string;\n\n    /**\n     * Load an external URL in the Web View.\n     *\n     * This is intended for use with live-reload servers.\n     *\n     * **This is not intended for use in production.**\n     *\n     * @since 1.0.0\n     */\n    url?: string;\n\n    /**\n     * Allow cleartext traffic in the Web View.\n     *\n     * On Android, all cleartext traffic is disabled by default as of API 28.\n     *\n     * This is intended for use with live-reload servers where unencrypted HTTP\n     * traffic is often used.\n     *\n     * **This is not intended for use in production.**\n     *\n     * @since 1.5.0\n     * @default false\n     */\n    cleartext?: boolean;\n\n    /**\n     * Set additional URLs the Web View can navigate to.\n     *\n     * By default, all external URLs are opened in the external browser (not\n     * the Web View).\n     *\n     * **This is not intended for use in production.**\n     *\n     * @since 1.0.0\n     * @default []\n     */\n    allowNavigation?: string[];\n\n    /**\n     * Specify path to a local html page to display in case of errors.\n     * On Android the html file won't have access to Capacitor plugins.\n     *\n     * @since 4.0.0\n     * @default null\n     */\n    errorPath?: string;\n\n    /**\n     * Append a path to the app URL.\n     *\n     * Allows loading from other paths than the default `/index.html`.\n     * @since 7.3.0\n     * @default null\n     */\n    appStartPath?: string;\n  };\n\n  cordova?: {\n    /**\n     * Populates <access> tags in the config.xml with the origin set to\n     * the values entered here.\n     * If not provided, a single <access origin=\"*\" /> tag gets included.\n     * It only has effect on a few Cordova plugins that respect the whitelist.\n     *\n     * @since 3.3.0\n     */\n    accessOrigins?: string[];\n\n    /**\n     * Configure Cordova preferences.\n     *\n     * @since 1.3.0\n     */\n    preferences?: { [key: string]: string | undefined };\n\n    /**\n     * Fail on cap update/sync if the CLI detects that a cordova plugin\n     * has uninstalled dependencies.\n     *\n     * @default false\n     * @since 7.4.0\n     */\n    failOnUninstalledPlugins?: boolean;\n  };\n\n  /**\n   * Configure plugins.\n   *\n   * This is an object with configuration values specified by plugin class\n   * name.\n   *\n   * @since 1.0.0\n   */\n  plugins?: PluginsConfig;\n\n  /**\n   * Allowlist of plugins to include during `npx cap sync`.\n   *\n   * This should be an array of strings representing the npm package name of\n   * plugins to include when running `npx cap sync`. If unset, Capacitor will\n   * inspect `package.json` for a list of potential plugins.\n   *\n   * @since 3.0.0\n   */\n  includePlugins?: string[];\n}\n\nexport interface PluginsConfig {\n  /**\n   * Plugin configuration by class name.\n   *\n   * @since 1.0.0\n   */\n  [key: string]:\n    | {\n        [key: string]: any;\n      }\n    | undefined;\n\n  /**\n   * Capacitor Cookies plugin configuration\n   *\n   * @since 4.3.0\n   */\n  CapacitorCookies?: {\n    /**\n     * Enable CapacitorCookies to override the global `document.cookie` on native.\n     *\n     * @default false\n     */\n    enabled?: boolean;\n  };\n\n  /**\n   * Capacitor Http plugin configuration\n   *\n   * @since 4.3.0\n   */\n  CapacitorHttp?: {\n    /**\n     * Enable CapacitorHttp to override the global `fetch` and `XMLHttpRequest` on native.\n     *\n     * @default false\n     */\n    enabled?: boolean;\n  };\n\n  /**\n   * System Bars plugin configuration\n   *\n   * @since 8.0.0\n   */\n  SystemBars?: {\n    /**\n     * Specifies how to handle problematic insets on Android.\n     *\n     * This option is only supported on Android.\n     *\n     * `css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.\n     *\n     * `disable` = Disable CSS variables injection.\n     *\n     * @default \"css\"\n     */\n    insetsHandling?: 'css' | 'disable';\n    /**\n     * The style of the text and icons of the system bars.\n     *\n     * This option is only supported on Android.\n     *\n     * @default `DEFAULT`\n     */\n    style?: string;\n\n    /**\n     * Hide the system bars on start.\n     *\n     * @default false\n     */\n    hidden?: boolean;\n\n    /**\n     * The type of status bar animation used when showing or hiding.\n     *\n     * This option is only supported on iOS.\n     *\n     * @default 'FADE'\n     *\n     */\n    animation?: 'FADE' | 'NONE';\n  };\n}\n"
  },
  {
    "path": "cli/src/definitions.ts",
    "content": "import type { CapacitorConfig, PluginsConfig } from './declarations';\n\ntype DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };\n\nexport type ExternalConfig = DeepReadonly<CapacitorConfig>;\nexport type Writable<T> = T extends object ? { -readonly [K in keyof T]: Writable<T[K]> } : T;\n\nexport const enum OS {\n  Unknown = 'unknown',\n  Mac = 'mac',\n  Windows = 'windows',\n  Linux = 'linux',\n}\n\nexport type PackageManager = 'Cocoapods' | 'bundler' | 'SPM';\n\nexport interface PackageJson {\n  readonly name: string;\n  readonly version: string;\n  readonly dependencies?: { readonly [key: string]: string | undefined };\n  readonly devDependencies?: { readonly [key: string]: string | undefined };\n}\n\nexport interface WindowsConfig {\n  readonly androidStudioPath: string;\n}\n\nexport interface LinuxConfig {\n  readonly androidStudioPath: string;\n}\n\nexport interface PlatformConfig {\n  readonly name: string;\n  readonly platformDir: string;\n  readonly platformDirAbs: string;\n}\n\nexport interface PlatformAssetsConfig {\n  readonly platformTemplateArchive: string;\n  readonly platformTemplateArchiveAbs: string;\n  readonly cordovaPluginsTemplateArchive: string;\n  readonly cordovaPluginsTemplateArchiveAbs: string;\n}\n\nexport interface CLIConfig {\n  readonly rootDir: string;\n  readonly assetsDir: string;\n  readonly assetsDirAbs: string;\n  readonly assets: {\n    readonly ios: PlatformAssetsConfig;\n    readonly android: PlatformAssetsConfig;\n  };\n  readonly package: PackageJson;\n  readonly os: OS;\n}\n\nexport interface AppConfig {\n  readonly rootDir: string;\n  readonly appId: string;\n  readonly appName: string;\n  readonly webDir: string;\n  readonly webDirAbs: string;\n  readonly package: PackageJson;\n  readonly extConfigType: 'json' | 'js' | 'ts';\n  readonly extConfigName: string;\n  readonly extConfigFilePath: string;\n  readonly extConfig: ExternalConfig;\n}\n\nexport interface AndroidConfig extends PlatformConfig {\n  readonly cordovaPluginsDir: string;\n  readonly cordovaPluginsDirAbs: string;\n  readonly studioPath: Promise<string>;\n  readonly minVersion: string;\n  readonly appDir: string;\n  readonly appDirAbs: string;\n  readonly srcDir: string;\n  readonly srcDirAbs: string;\n  readonly srcMainDir: string;\n  readonly srcMainDirAbs: string;\n  readonly webDir: string;\n  readonly webDirAbs: string;\n  readonly assetsDir: string;\n  readonly assetsDirAbs: string;\n  readonly resDir: string;\n  readonly resDirAbs: string;\n  readonly buildOutputDir: string;\n  /**\n   * @deprecated Will be removed in Cap. 5 as the `--flavor` option modifies this value.\n   */\n  readonly buildOutputDirAbs: string;\n  /**\n   * @deprecated Will be removed in Cap. 5 as the `--flavor` option modifies this value.\n   */\n  readonly apkName: string;\n  readonly flavor: string;\n  readonly buildOptions: {\n    keystorePath?: string;\n    keystorePassword?: string;\n    keystoreAlias?: string;\n    keystoreAliasPassword?: string;\n    releaseType?: 'AAB' | 'APK';\n    signingType?: 'apksigner' | 'jarsigner';\n  };\n}\n\nexport enum XcodeExportMethod {\n  AppStoreConnect = 'app-store-connect',\n  ReleaseTesting = 'release-testing',\n  Enterprise = 'enterprise',\n  Debugging = 'debugging',\n  DeveloperID = 'developer-id',\n  MacApplication = 'mac-application',\n  Validation = 'validation',\n}\n\nexport interface IOSConfig extends PlatformConfig {\n  readonly cordovaPluginsDir: string;\n  readonly cordovaPluginsDirAbs: string;\n  readonly minVersion: string;\n  readonly podPath: Promise<string>;\n  readonly packageManager: Promise<PackageManager>;\n  readonly scheme: string;\n  readonly webDir: Promise<string>;\n  readonly webDirAbs: Promise<string>;\n  readonly nativeProjectDir: string;\n  readonly nativeProjectDirAbs: string;\n  readonly nativeTargetDir: string;\n  readonly nativeTargetDirAbs: string;\n  readonly nativeXcodeProjDir: string;\n  readonly nativeXcodeProjDirAbs: string;\n  readonly nativeXcodeWorkspaceDir: Promise<string>;\n  readonly nativeXcodeWorkspaceDirAbs: Promise<string>;\n  readonly buildOptions: {\n    teamId?: string;\n    exportMethod?: XcodeExportMethod;\n    xcodeSigningStyle?: 'automatic' | 'manual';\n    signingCertificate?: string;\n    provisioningProfile?: string;\n  };\n}\n\nexport type WebConfig = PlatformConfig;\n\nexport interface Config {\n  readonly android: AndroidConfig;\n  readonly ios: IOSConfig;\n  readonly web: WebConfig;\n  readonly cli: CLIConfig;\n  readonly app: AppConfig;\n  readonly plugins?: PluginsConfig;\n}\n\nexport interface FrameworkConfig {\n  name: string;\n  isMatch: (config: Config) => boolean;\n  webDir: string;\n  /**\n   * Specific UI libraries (Ionic) and higher-level frameworks (NextJs/Gatsby)\n   * should be prioritorized over a more generic framework like React/Angular/Vue.\n   * Lower the priorty number the more important it is (1 has more priority over 2).\n   * This helps to make sure a specific framework like \"NextJs\" is chosen before\n   * the more generic \"React\".\n   */\n  priority: number;\n}\n"
  },
  {
    "path": "cli/src/errors.ts",
    "content": "export abstract class BaseException<T> extends Error {\n  constructor(\n    readonly message: string,\n    readonly code: T,\n  ) {\n    super(message);\n  }\n}\n\nexport class FatalException extends BaseException<'FATAL'> {\n  constructor(\n    readonly message: string,\n    readonly exitCode = 1,\n  ) {\n    super(message, 'FATAL');\n  }\n}\n\nexport function fatal(message: string): never {\n  throw new FatalException(message);\n}\n\nexport function isFatal(e: any): e is FatalException {\n  return e && e instanceof FatalException;\n}\n"
  },
  {
    "path": "cli/src/framework-configs.ts",
    "content": "import type { Config, FrameworkConfig } from './definitions';\n\nconst FRAMEWORK_CONFIGS: FrameworkConfig[] = [\n  {\n    name: 'Angular',\n    isMatch: (config) => hasDependency(config, '@angular/cli'),\n    webDir: 'dist',\n    priority: 3,\n  },\n  {\n    name: 'Create React App',\n    isMatch: (config) => hasDependency(config, 'react-scripts'),\n    webDir: 'build',\n    priority: 3,\n  },\n  {\n    name: 'Ember',\n    isMatch: (config) => hasDependency(config, 'ember-cli'),\n    webDir: 'dist',\n    priority: 3,\n  },\n  {\n    name: 'Gatsby',\n    isMatch: (config) => hasDependency(config, 'gatsby'),\n    webDir: 'public',\n    priority: 2,\n  },\n  {\n    name: 'Ionic Angular',\n    isMatch: (config) => hasDependency(config, '@ionic/angular'),\n    webDir: 'www',\n    priority: 1,\n  },\n  {\n    name: 'Ionic React',\n    isMatch: (config) => hasDependency(config, '@ionic/react'),\n    webDir: 'build',\n    priority: 1,\n  },\n  {\n    name: 'Ionic Vue',\n    isMatch: (config) => hasDependency(config, '@ionic/vue'),\n    webDir: 'public',\n    priority: 1,\n  },\n  {\n    name: 'Next',\n    isMatch: (config) => hasDependency(config, 'next'),\n    webDir: 'public',\n    priority: 2,\n  },\n  {\n    name: 'Preact',\n    isMatch: (config) => hasDependency(config, 'preact-cli'),\n    webDir: 'build',\n    priority: 3,\n  },\n  {\n    name: 'Stencil',\n    isMatch: (config) => hasDependency(config, '@stencil/core'),\n    webDir: 'www',\n    priority: 3,\n  },\n  {\n    name: 'Svelte',\n    isMatch: (config) => hasDependency(config, 'svelte') && hasDependency(config, 'sirv-cli'),\n    webDir: 'public',\n    priority: 3,\n  },\n  {\n    name: 'Vite',\n    isMatch: (config) => hasDependency(config, 'vite'),\n    webDir: 'dist',\n    priority: 2,\n  },\n  {\n    name: 'Vue',\n    isMatch: (config) => hasDependency(config, '@vue/cli-service'),\n    webDir: 'dist',\n    priority: 3,\n  },\n];\n\nexport function detectFramework(config: Config): FrameworkConfig | undefined {\n  const frameworks = FRAMEWORK_CONFIGS.filter((f) => f.isMatch(config)).sort((a, b) => {\n    if (a.priority < b.priority) return -1;\n    if (a.priority > b.priority) return 1;\n    return 0;\n  });\n  return frameworks[0];\n}\n\nfunction hasDependency(config: Config, depName: string): boolean {\n  const deps = getDependencies(config);\n  return deps.includes(depName);\n}\n\nfunction getDependencies(config: Config): string[] {\n  const deps: string[] = [];\n  if (config?.app?.package?.dependencies) {\n    deps.push(...Object.keys(config.app.package.dependencies));\n  }\n  if (config?.app?.package?.devDependencies) {\n    deps.push(...Object.keys(config.app.package.devDependencies));\n  }\n  return deps;\n}\n"
  },
  {
    "path": "cli/src/index.ts",
    "content": "import { Option, program } from 'commander';\nimport { resolve } from 'path';\n\nimport c from './colors';\nimport { loadConfig } from './config';\nimport type { Config, PackageManager, Writable } from './definitions';\nimport { fatal, isFatal } from './errors';\nimport { receive } from './ipc';\nimport { logger, output } from './log';\nimport { telemetryAction } from './telemetry';\nimport { wrapAction } from './util/cli';\nimport { emoji as _e } from './util/emoji';\n\nprocess.on('unhandledRejection', (error) => {\n  console.error(c.failure('[fatal]'), error);\n});\n\nprocess.on('message', receive);\n\nexport async function run(): Promise<void> {\n  try {\n    const config = await loadConfig();\n    runProgram(config);\n  } catch (e: any) {\n    process.exitCode = isFatal(e) ? e.exitCode : 1;\n    logger.error(e.message ? e.message : String(e));\n  }\n}\n\nasync function getPackageManager(config: Config, packageManager: any): Promise<PackageManager> {\n  if (packageManager === 'cocoapods') {\n    if ((await config.ios.packageManager) === 'bundler') {\n      return 'bundler';\n    }\n    return 'Cocoapods';\n  }\n  return 'SPM';\n}\n\nexport function runProgram(config: Config): void {\n  program.version(config.cli.package.version);\n\n  program\n    .command('config', { hidden: true })\n    .description(`print evaluated Capacitor config`)\n    .option('--json', 'Print in JSON format')\n    .action(\n      wrapAction(async ({ json }) => {\n        const { configCommand } = await import('./tasks/config');\n        await configCommand(config, json);\n      }),\n    );\n\n  program\n    .command('create [directory] [name] [id]', { hidden: true })\n    .description('Creates a new Capacitor project')\n    .action(\n      wrapAction(async () => {\n        const { createCommand } = await import('./tasks/create');\n        await createCommand();\n      }),\n    );\n\n  program\n    .command('init [appName] [appId]')\n    .description(`Initialize Capacitor configuration`)\n    .option('--web-dir <value>', 'Optional: Directory of your projects built web assets')\n    .option('--skip-appid-validation', 'Optional: Skip validating the app ID for iOS and Android compatibility')\n    .action(\n      wrapAction(\n        telemetryAction(config, async (appName, appId, { webDir, skipAppidValidation }) => {\n          const { initCommand } = await import('./tasks/init');\n          await initCommand(config, appName, appId, webDir, skipAppidValidation);\n        }),\n      ),\n    );\n\n  program\n    .command('serve', { hidden: true })\n    .description('Serves a Capacitor Progressive Web App in the browser')\n    .action(\n      wrapAction(async () => {\n        const { serveCommand } = await import('./tasks/serve');\n        await serveCommand();\n      }),\n    );\n\n  program\n    .command('sync [platform]')\n    .description(`${c.input('copy')} + ${c.input('update')}`)\n    .option('--deployment', 'Optional: if provided, pod install will use --deployment option')\n    .option(\n      '--inline',\n      'Optional: if true, all source maps will be inlined for easier debugging on mobile devices',\n      false,\n    )\n    .action(\n      wrapAction(\n        telemetryAction(config, async (platform, { deployment, inline }) => {\n          const { syncCommand } = await import('./tasks/sync');\n          await syncCommand(config, platform, deployment, inline);\n        }),\n      ),\n    );\n\n  program\n    .command('update [platform]')\n    .description(`updates the native plugins and dependencies based on ${c.strong('package.json')}`)\n    .option('--deployment', 'Optional: if provided, pod install will use --deployment option')\n    .action(\n      wrapAction(\n        telemetryAction(config, async (platform, { deployment }) => {\n          const { updateCommand } = await import('./tasks/update');\n          await updateCommand(config, platform, deployment);\n        }),\n      ),\n    );\n\n  program\n    .command('copy [platform]')\n    .description('copies the web app build into the native app')\n    .option(\n      '--inline',\n      'Optional: if true, all source maps will be inlined for easier debugging on mobile devices',\n      false,\n    )\n    .action(\n      wrapAction(\n        telemetryAction(config, async (platform, { inline }) => {\n          const { copyCommand } = await import('./tasks/copy');\n          await copyCommand(config, platform, inline);\n        }),\n      ),\n    );\n\n  program\n    .command('build <platform>')\n    .description('builds the release version of the selected platform')\n    .option('--scheme <schemeToBuild>', 'iOS Scheme to build')\n    .option('--flavor <flavorToBuild>', 'Android Flavor to build')\n    .option('--keystorepath <keystorePath>', 'Path to the keystore')\n    .option('--keystorepass <keystorePass>', 'Password to the keystore')\n    .option('--keystorealias <keystoreAlias>', 'Key Alias in the keystore')\n    .option('--configuration <name>', 'Configuration name of the iOS Scheme')\n    .option('--keystorealiaspass <keystoreAliasPass>', 'Password for the Key Alias')\n    .addOption(\n      new Option('--androidreleasetype <androidreleasetype>', 'Android release type; APK or AAB').choices([\n        'AAB',\n        'APK',\n      ]),\n    )\n    .addOption(\n      new Option('--signing-type <signingtype>', 'Program used to sign apps (default: jarsigner)').choices([\n        'apksigner',\n        'jarsigner',\n      ]),\n    )\n    .addOption(\n      new Option('--xcode-team-id <xcodeTeamID>', 'The Developer team to use for building and exporting the archive'),\n    )\n    .addOption(\n      new Option(\n        '--xcode-export-method <xcodeExportMethod>',\n        'Describes how xcodebuild should export the archive (default:  app-store-connect)',\n      ).choices([\n        'app-store-connect',\n        'release-testing',\n        'enterprise',\n        'debugging',\n        'developer-id',\n        'mac-application',\n        'validation',\n      ]),\n    )\n    .addOption(\n      new Option(\n        '--xcode-signing-style <xcodeSigningStyle>',\n        'The iOS signing style to use when building the app for distribution (default: automatic)',\n      ).choices(['automatic', 'manual']),\n    )\n    .addOption(\n      new Option(\n        '--xcode-signing-certificate <xcodeSigningCertificate>',\n        'A certificate name, SHA-1 hash, or automatic selector to use for signing for iOS builds',\n      ),\n    )\n    .addOption(\n      new Option(\n        '--xcode-provisioning-profile <xcodeProvisioningProfile>',\n        'A provisioning profile name or UUID for iOS builds',\n      ),\n    )\n    .action(\n      wrapAction(\n        telemetryAction(\n          config,\n          async (\n            platform,\n            {\n              scheme,\n              flavor,\n              keystorepath,\n              keystorepass,\n              keystorealias,\n              keystorealiaspass,\n              androidreleasetype,\n              signingType,\n              configuration,\n              xcodeTeamId,\n              xcodeExportMethod,\n              xcodeSigningStyle,\n              xcodeSigningCertificate,\n              xcodeProvisioningProfile,\n            },\n          ) => {\n            const { buildCommand } = await import('./tasks/build');\n            await buildCommand(config, platform, {\n              scheme,\n              flavor,\n              keystorepath,\n              keystorepass,\n              keystorealias,\n              keystorealiaspass,\n              androidreleasetype,\n              signingtype: signingType,\n              configuration,\n              xcodeTeamId,\n              xcodeExportMethod,\n              xcodeSigningType: xcodeSigningStyle,\n              xcodeSigningCertificate,\n              xcodeProvisioningProfile,\n            });\n          },\n        ),\n      ),\n    );\n  program\n    .command(`run [platform]`)\n    .description(`runs ${c.input('sync')}, then builds and deploys the native app`)\n    .option('--scheme <schemeName>', 'set the scheme of the iOS project')\n    .option('--flavor <flavorName>', 'set the flavor of the Android project (flavor dimensions not yet supported)')\n    .option('--list', 'list targets, then quit')\n    .addOption(new Option('--json').hideHelp())\n    .option('--target <id>', 'use a specific target')\n    .option('--target-name <name>', 'use a specific target by name')\n    .option(\n      '--target-name-sdk-version <version>',\n      'use a specific sdk version when using --target-name, ex: 26.0 (for iOS 26) or 35 (for Android API 35)',\n    )\n    .option('--no-sync', `do not run ${c.input('sync')}`)\n    .option('--forwardPorts <port:port>', 'Automatically run \"adb reverse\" for better live-reloading support')\n    .option('-l, --live-reload', 'Set live-reload URL via CLI (uses defaults, overrides server.url config)')\n    .option('--host <host>', 'Configure host for live-reload URL (used with --live-reload)')\n    .option('--port <port>', 'Configure port for live-reload URL (used with --live-reload)')\n    .option('--configuration <name>', 'Configuration name of the iOS Scheme')\n    .option('--https', 'Use https:// instead of http:// for live-reload URL (used with --live-reload)')\n    .action(\n      wrapAction(\n        telemetryAction(\n          config,\n          async (\n            platform,\n            {\n              scheme,\n              flavor,\n              list,\n              json,\n              target,\n              targetName,\n              targetNameSdkVersion,\n              sync,\n              forwardPorts,\n              liveReload,\n              host,\n              port,\n              configuration,\n              https,\n            },\n          ) => {\n            const { runCommand } = await import('./tasks/run');\n            await runCommand(config, platform, {\n              scheme,\n              flavor,\n              list,\n              json,\n              target,\n              targetName,\n              targetNameSdkVersion,\n              sync,\n              forwardPorts,\n              liveReload,\n              host,\n              port,\n              configuration,\n              https,\n            });\n          },\n        ),\n      ),\n    );\n\n  program\n    .command('open [platform]')\n    .description('opens the native project workspace (Xcode for iOS)')\n    .action(\n      wrapAction(\n        telemetryAction(config, async (platform) => {\n          const { openCommand } = await import('./tasks/open');\n          await openCommand(config, platform);\n        }),\n      ),\n    );\n\n  program\n    .command('add [platform]')\n    .description('add a native platform project')\n    .option(\n      '--packagemanager <packageManager>',\n      'The package manager to use for dependency installs (CocoaPods or SPM)',\n    )\n    .action(\n      wrapAction(\n        telemetryAction(config, async (platform, { packagemanager }) => {\n          const { addCommand } = await import('./tasks/add');\n\n          const configWritable: Writable<Config> = config as Writable<Config>;\n          configWritable.ios.packageManager = getPackageManager(config, packagemanager?.toLowerCase());\n          if (packagemanager?.toLowerCase() === 'CocoaPods'.toLowerCase()) {\n            configWritable.cli.assets.ios.platformTemplateArchive = 'ios-pods-template.tar.gz';\n            configWritable.cli.assets.ios.platformTemplateArchiveAbs = resolve(\n              configWritable.cli.assetsDirAbs,\n              configWritable.cli.assets.ios.platformTemplateArchive,\n            );\n          }\n\n          await addCommand(configWritable as Config, platform);\n        }),\n      ),\n    );\n\n  program\n    .command('ls [platform]')\n    .description('list installed Cordova and Capacitor plugins')\n    .action(\n      wrapAction(\n        telemetryAction(config, async (platform) => {\n          const { listCommand } = await import('./tasks/list');\n          await listCommand(config, platform);\n        }),\n      ),\n    );\n\n  program\n    .command('doctor [platform]')\n    .description('checks the current setup for common errors')\n    .action(\n      wrapAction(\n        telemetryAction(config, async (platform) => {\n          const { doctorCommand } = await import('./tasks/doctor');\n          await doctorCommand(config, platform);\n        }),\n      ),\n    );\n\n  program\n    .command('telemetry [on|off]', { hidden: true })\n    .description('enable or disable telemetry')\n    .action(\n      wrapAction(async (onOrOff) => {\n        const { telemetryCommand } = await import('./tasks/telemetry');\n        await telemetryCommand(onOrOff);\n      }),\n    );\n\n  program\n    .command('📡', { hidden: true })\n    .description('IPC receiver command')\n    .action(() => {\n      // no-op: IPC messages are received via `process.on('message')`\n    });\n\n  program\n    .command('plugin:generate', { hidden: true })\n    .description('start a new Capacitor plugin')\n    .action(\n      wrapAction(async () => {\n        const { newPluginCommand } = await import('./tasks/new-plugin');\n        await newPluginCommand();\n      }),\n    );\n\n  program\n    .command('migrate')\n    .option('--noprompt', 'do not prompt for confirmation')\n    .option('--packagemanager <packageManager>', 'The package manager to use for dependency installs (npm, pnpm, yarn)')\n    .description('Migrate your current Capacitor app to the latest major version of Capacitor.')\n    .action(\n      wrapAction(async ({ noprompt, packagemanager }) => {\n        const { migrateCommand } = await import('./tasks/migrate');\n        await migrateCommand(config, noprompt, packagemanager);\n      }),\n    );\n\n  program\n    .command('spm-migration-assistant')\n    .description('Remove Cocoapods from project and switch to Swift Package Manager')\n    .action(\n      wrapAction(async () => {\n        const { migrateToSPM } = await import('./tasks/migrate-spm');\n        await migrateToSPM(config);\n      }),\n    );\n\n  program.arguments('[command]').action(\n    wrapAction(async (cmd) => {\n      if (typeof cmd === 'undefined') {\n        output.write(\n          `\\n  ${_e('⚡️', '--')}  ${c.strong(\n            'Capacitor - Cross-Platform apps with JavaScript and the Web',\n          )}  ${_e('⚡️', '--')}\\n\\n`,\n        );\n        program.outputHelp();\n      } else {\n        fatal(`Unknown command: ${c.input(cmd)}`);\n      }\n    }),\n  );\n\n  program.parse(process.argv);\n}\n"
  },
  {
    "path": "cli/src/ios/add.ts",
    "content": "import c from '../colors';\nimport { runTask } from '../common';\nimport type { Config } from '../definitions';\nimport { extractTemplate } from '../util/template';\n\nexport async function addIOS(config: Config): Promise<void> {\n  await runTask(`Adding native Xcode project in ${c.strong(config.ios.platformDir)}`, () => {\n    return extractTemplate(config.cli.assets.ios.platformTemplateArchiveAbs, config.ios.platformDirAbs);\n  });\n}\n"
  },
  {
    "path": "cli/src/ios/build.ts",
    "content": "import { writeFileSync, unlinkSync } from 'fs-extra';\nimport { basename, join } from 'path';\nimport { rimraf } from 'rimraf';\n\nimport { runTask } from '../common';\nimport { XcodeExportMethod, type Config } from '../definitions';\nimport { logSuccess } from '../log';\nimport { type BuildCommandOptions } from '../tasks/build';\nimport { runCommand } from '../util/subprocess';\n\nexport async function buildiOS(config: Config, buildOptions: BuildCommandOptions): Promise<void> {\n  const theScheme = buildOptions.scheme ?? 'App';\n\n  let typeOfBuild: string;\n  let projectName: string;\n\n  if ((await config.ios.packageManager) !== 'SPM') {\n    typeOfBuild = '-workspace';\n    projectName = basename(await config.ios.nativeXcodeWorkspaceDirAbs);\n  } else {\n    typeOfBuild = '-project';\n    projectName = basename(await config.ios.nativeXcodeProjDirAbs);\n  }\n\n  if (\n    buildOptions.xcodeSigningType == 'manual' &&\n    (!buildOptions.xcodeSigningCertificate || !buildOptions.xcodeProvisioningProfile)\n  ) {\n    throw 'Manually signed Xcode builds require a signing certificate and provisioning profile.';\n  }\n\n  const buildArgs = [\n    typeOfBuild,\n    projectName,\n    '-scheme',\n    `${theScheme}`,\n    '-destination',\n    `generic/platform=iOS`,\n    '-archivePath',\n    `${theScheme}.xcarchive`,\n    'archive',\n    '-configuration',\n    buildOptions.configuration,\n  ];\n\n  if (buildOptions.xcodeTeamId) {\n    buildArgs.push(`DEVELOPMENT_TEAM=${buildOptions.xcodeTeamId}`);\n  }\n\n  if (buildOptions.xcodeSigningType == 'manual') {\n    buildArgs.push(`PROVISIONING_PROFILE_SPECIFIER=${buildOptions.xcodeProvisioningProfile}`);\n  }\n\n  await runTask('Building xArchive', async () =>\n    runCommand('xcodebuild', buildArgs, {\n      cwd: config.ios.nativeProjectDirAbs,\n    }),\n  );\n\n  const manualSigningContents = `<key>provisioningProfiles</key>\n<dict>\n<key>${config.app.appId}</key>\n<string>${buildOptions.xcodeProvisioningProfile ?? ''}</string>\n</dict>\n<key>signingCertificate</key>\n<string>${buildOptions.xcodeSigningCertificate ?? ''}</string>`;\n\n  const archivePlistContents = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n<key>method</key>\n<string>${buildOptions.xcodeExportMethod ?? XcodeExportMethod.AppStoreConnect}</string>\n<key>signingStyle</key>\n<string>${buildOptions.xcodeSigningType}</string>\n${buildOptions.xcodeSigningType == 'manual' ? manualSigningContents : ''}\n</dict>\n</plist>`;\n\n  const archivePlistPath = join(`${config.ios.nativeProjectDirAbs}`, 'archive.plist');\n\n  writeFileSync(archivePlistPath, archivePlistContents);\n\n  const archiveArgs = [\n    'archive',\n    '-archivePath',\n    `${theScheme}.xcarchive`,\n    '-exportArchive',\n    '-exportOptionsPlist',\n    'archive.plist',\n    '-exportPath',\n    'output',\n    '-configuration',\n    buildOptions.configuration,\n  ];\n\n  if (buildOptions.xcodeSigningType == 'automatic') {\n    archiveArgs.push('-allowProvisioningUpdates');\n  }\n\n  await runTask('Building IPA', async () =>\n    runCommand('xcodebuild', archiveArgs, {\n      cwd: config.ios.nativeProjectDirAbs,\n    }),\n  );\n\n  await runTask('Cleaning up', async () => {\n    unlinkSync(archivePlistPath);\n    rimraf.sync(join(config.ios.nativeProjectDirAbs, `${theScheme}.xcarchive`));\n  });\n\n  logSuccess(`Successfully generated an IPA at: ${join(config.ios.nativeProjectDirAbs, 'output')}`);\n}\n"
  },
  {
    "path": "cli/src/ios/common.ts",
    "content": "import { execSync } from 'child_process';\nimport { readFile, readFileSync, writeFile } from 'fs-extra';\nimport { join, resolve } from 'path';\n\nimport c from '../colors';\nimport { checkCapacitorPlatform } from '../common';\nimport type { CheckFunction } from '../common';\nimport { getIncompatibleCordovaPlugins } from '../cordova';\nimport { OS } from '../definitions';\nimport type { Config } from '../definitions';\nimport { logger } from '../log';\nimport { PluginType, getPluginPlatform } from '../plugin';\nimport type { Plugin } from '../plugin';\nimport { isInstalled, runCommand } from '../util/subprocess';\n\nexport async function checkIOSPackage(config: Config): Promise<string | null> {\n  return checkCapacitorPlatform(config, 'ios');\n}\n\nfunction execBundler() {\n  try {\n    const bundleOutput = execSync('bundle &> /dev/null ; echo $?');\n    return parseInt(bundleOutput.toString());\n  } catch (e: any) {\n    return -1;\n  }\n}\n\nexport async function getCommonChecks(config: Config): Promise<CheckFunction[]> {\n  const checks: CheckFunction[] = [];\n  if ((await config.ios.packageManager) === 'bundler') {\n    checks.push(() => checkBundler(config));\n  } else if ((await config.ios.packageManager) === 'Cocoapods') {\n    checks.push(() => checkCocoaPods(config));\n  }\n  return checks;\n}\n\nexport async function checkBundler(config: Config): Promise<string | null> {\n  if (config.cli.os === OS.Mac) {\n    let bundlerResult = execBundler();\n    if (bundlerResult === 1) {\n      // Bundler version is outdated\n      logger.info(`Using ${c.strong('Gemfile')}: Bundler update needed...`);\n      await runCommand('gem', ['install', 'bundler']);\n      bundlerResult = execBundler();\n    }\n    if (bundlerResult === 0) {\n      // Bundler in use, all gems current\n      logger.info(`Using ${c.strong('Gemfile')}: RubyGems bundle installed`);\n    }\n  }\n  return null;\n}\n\nexport async function checkCocoaPods(config: Config): Promise<string | null> {\n  if (!(await isInstalled(await config.ios.podPath)) && config.cli.os === OS.Mac) {\n    return (\n      `CocoaPods is not installed.\\n` +\n      `See this install guide: ${c.strong('https://capacitorjs.com/docs/getting-started/environment-setup#homebrew')}`\n    );\n  }\n  return null;\n}\n\nexport async function getIOSPlugins(allPlugins: Plugin[]): Promise<Plugin[]> {\n  const resolved = await Promise.all(allPlugins.map(async (plugin) => await resolvePlugin(plugin)));\n  return resolved.filter((plugin): plugin is Plugin => !!plugin);\n}\n\nexport async function resolvePlugin(plugin: Plugin): Promise<Plugin | null> {\n  const platform = 'ios';\n  if (plugin.manifest?.ios) {\n    plugin.ios = {\n      name: plugin.name,\n      type: PluginType.Core,\n      path: plugin.manifest.ios.src ?? platform,\n    };\n  } else if (plugin.xml) {\n    plugin.ios = {\n      name: plugin.name,\n      type: PluginType.Cordova,\n      path: 'src/' + platform,\n    };\n    if (getIncompatibleCordovaPlugins(platform).includes(plugin.id) || !getPluginPlatform(plugin, platform)) {\n      plugin.ios.type = PluginType.Incompatible;\n    }\n  } else {\n    return null;\n  }\n  return plugin;\n}\n\n/**\n * Update the native project files with the desired app id and app name\n */\nexport async function editProjectSettingsIOS(config: Config): Promise<void> {\n  const appId = config.app.appId;\n  const appName = config.app.appName.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n\n  const pbxPath = `${config.ios.nativeXcodeProjDirAbs}/project.pbxproj`;\n  const plistPath = resolve(config.ios.nativeTargetDirAbs, 'Info.plist');\n\n  let plistContent = await readFile(plistPath, { encoding: 'utf-8' });\n\n  plistContent = plistContent.replace(\n    /<key>CFBundleDisplayName<\\/key>[\\s\\S]?\\s+<string>([^<]*)<\\/string>/,\n    `<key>CFBundleDisplayName</key>\\n        <string>${appName}</string>`,\n  );\n\n  let pbxContent = await readFile(pbxPath, { encoding: 'utf-8' });\n  pbxContent = pbxContent.replace(/PRODUCT_BUNDLE_IDENTIFIER = ([^;]+)/g, `PRODUCT_BUNDLE_IDENTIFIER = ${appId}`);\n\n  await writeFile(plistPath, plistContent, { encoding: 'utf-8' });\n  await writeFile(pbxPath, pbxContent, { encoding: 'utf-8' });\n}\n\nexport function getMajoriOSVersion(config: Config): string {\n  const pbx = readFileSync(join(config.ios.nativeXcodeProjDirAbs, 'project.pbxproj'), 'utf-8');\n  const searchString = 'IPHONEOS_DEPLOYMENT_TARGET = ';\n  const iosVersion = pbx.substring(\n    pbx.indexOf(searchString) + searchString.length,\n    pbx.indexOf(searchString) + searchString.length + 2,\n  );\n  return iosVersion;\n}\n"
  },
  {
    "path": "cli/src/ios/doctor.ts",
    "content": "import { check, checkWebDir } from '../common';\nimport type { Config } from '../definitions';\nimport { fatal } from '../errors';\nimport { logSuccess } from '../log';\nimport { isInstalled } from '../util/subprocess';\n\nimport { getCommonChecks } from './common';\n\nexport async function doctorIOS(config: Config): Promise<void> {\n  // DOCTOR ideas for iOS:\n  // plugin specific warnings\n  // check cocoapods installed\n  // check projects exist\n  // check content in www === ios/www\n  // check CLI versions\n  // check plugins versions\n  // check native project deps are up-to-date === npm install\n  // check if npm install was updated\n  // check online datebase of common errors\n  // check if www folder is empty (index.html does not exist)\n  try {\n    await check([() => checkWebDir(config), checkXcode, ...(await getCommonChecks(config))]);\n    logSuccess('iOS looking great! 👌');\n  } catch (e: any) {\n    fatal(e.stack ?? e);\n  }\n}\n\nasync function checkXcode() {\n  if (!(await isInstalled('xcodebuild'))) {\n    return `Xcode is not installed`;\n  }\n  // const matches = output.match(/^Xcode (.*)/);\n  // if (matches && matches.length === 2) {\n  //   const minVersion = '9.0.0';\n  //   const semver = await import('semver');\n  //   console.log(matches[1]);\n  //   if (semver.gt(minVersion, matches[1])) {\n  //     return `Xcode version is too old, ${minVersion} is required`;\n  //   }\n  // }\n  return null;\n}\n"
  },
  {
    "path": "cli/src/ios/open.ts",
    "content": "import open from 'open';\n\nimport { wait } from '../common';\nimport type { Config } from '../definitions';\n\nexport async function openIOS(config: Config): Promise<void> {\n  if ((await config.ios.packageManager) == 'SPM') {\n    await open(config.ios.nativeXcodeProjDirAbs, { wait: false });\n  } else {\n    await open(await config.ios.nativeXcodeWorkspaceDirAbs, { wait: false });\n  }\n\n  await wait(3000);\n}\n"
  },
  {
    "path": "cli/src/ios/run.ts",
    "content": "import Debug from 'debug';\nimport { basename, resolve } from 'path';\n\nimport c from '../colors';\nimport { promptForPlatformTarget, runTask } from '../common';\nimport type { Config } from '../definitions';\nimport type { RunCommandOptions } from '../tasks/run';\nimport { runNativeRun, getPlatformTargets } from '../util/native-run';\nimport { runCommand } from '../util/subprocess';\n\nconst debug = Debug('capacitor:ios:run');\n\nexport async function runIOS(\n  config: Config,\n  {\n    target: selectedTarget,\n    targetName: selectedTargetName,\n    targetNameSdkVersion: selectedTargetSdkVersion,\n    scheme: selectedScheme,\n    configuration: selectedConfiguration,\n  }: RunCommandOptions,\n): Promise<void> {\n  const target = await promptForPlatformTarget(\n    await getPlatformTargets('ios'),\n    selectedTarget ?? selectedTargetName,\n    selectedTargetSdkVersion,\n    selectedTargetName !== undefined,\n  );\n\n  const runScheme = selectedScheme || config.ios.scheme;\n  const configuration = selectedConfiguration || 'Debug';\n\n  const derivedDataPath = resolve(config.ios.platformDirAbs, 'DerivedData', target.id);\n\n  const packageManager = await config.ios.packageManager;\n\n  let typeOfBuild: string;\n  let projectName: string;\n\n  if (packageManager !== 'SPM') {\n    typeOfBuild = '-workspace';\n    projectName = basename(await config.ios.nativeXcodeWorkspaceDirAbs);\n  } else {\n    typeOfBuild = '-project';\n    projectName = basename(await config.ios.nativeXcodeProjDirAbs);\n  }\n\n  const xcodebuildArgs = [\n    typeOfBuild,\n    projectName,\n    '-scheme',\n    runScheme,\n    '-configuration',\n    configuration,\n    '-destination',\n    `id=${target.id}`,\n    '-derivedDataPath',\n    derivedDataPath,\n  ];\n\n  debug('Invoking xcodebuild with args: %O', xcodebuildArgs);\n\n  await runTask('Running xcodebuild', async () =>\n    runCommand('xcrun', ['xcodebuild', ...xcodebuildArgs], {\n      cwd: config.ios.nativeProjectDirAbs,\n    }),\n  );\n\n  const appName = `${runScheme}.app`;\n  const appPath = resolve(\n    derivedDataPath,\n    'Build/Products',\n    target.virtual ? `${configuration}-iphonesimulator` : `${configuration}-iphoneos`,\n    appName,\n  );\n\n  const nativeRunArgs = ['ios', '--app', appPath, '--target', target.id];\n\n  debug('Invoking native-run with args: %O', nativeRunArgs);\n\n  await runTask(`Deploying ${c.strong(appName)} to ${c.input(target.id)}`, async () => runNativeRun(nativeRunArgs));\n}\n"
  },
  {
    "path": "cli/src/ios/update.ts",
    "content": "import { copy, remove, pathExists, readFile, realpath, writeFile } from 'fs-extra';\nimport { basename, dirname, join, relative } from 'path';\n\nimport c from '../colors';\nimport { checkPlatformVersions, getCapacitorPackageVersion, runTask } from '../common';\nimport { checkPluginDependencies, handleCordovaPluginsJS, logCordovaManualSteps, needsStaticPod } from '../cordova';\nimport type { Config } from '../definitions';\nimport { fatal } from '../errors';\nimport { logger } from '../log';\nimport {\n  PluginType,\n  getAllElements,\n  getFilePath,\n  getPlatformElement,\n  getPluginType,\n  getPlugins,\n  printPlugins,\n} from '../plugin';\nimport type { Plugin } from '../plugin';\nimport { copy as copyTask } from '../tasks/copy';\nimport { convertToUnixPath } from '../util/fs';\nimport { generateIOSPackageJSON } from '../util/iosplugin';\nimport { resolveNode } from '../util/node';\nimport { generatePackageFile, checkPluginsForPackageSwift } from '../util/spm';\nimport { runCommand, isInstalled } from '../util/subprocess';\nimport { extractTemplate } from '../util/template';\n\nimport { getIOSPlugins, getMajoriOSVersion } from './common';\n\nconst platform = 'ios';\n\nexport async function updateIOS(config: Config, deployment: boolean): Promise<void> {\n  const plugins = await getPluginsTask(config);\n\n  const capacitorPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Core);\n  await updatePluginFiles(config, plugins, deployment);\n  await checkPlatformVersions(config, platform);\n  generateIOSPackageJSON(config, plugins);\n\n  printPlugins(capacitorPlugins, 'ios');\n}\n\nasync function updatePluginFiles(config: Config, plugins: Plugin[], deployment: boolean) {\n  await removePluginsNativeFiles(config);\n  const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova);\n  if (cordovaPlugins.length > 0) {\n    await copyPluginsNativeFiles(config, cordovaPlugins);\n  }\n  if (!(await pathExists(await config.ios.webDirAbs))) {\n    await copyTask(config, platform);\n  }\n  await handleCordovaPluginsJS(cordovaPlugins, config, platform);\n  await checkPluginDependencies(plugins, platform, config.app.extConfig.cordova?.failOnUninstalledPlugins);\n  if ((await config.ios.packageManager) === 'SPM') {\n    await generateCordovaPackageFiles(cordovaPlugins, config);\n\n    const validSPMPackages = await checkPluginsForPackageSwift(config, plugins);\n\n    await generatePackageFile(config, validSPMPackages.concat(cordovaPlugins));\n  } else {\n    await generateCordovaPodspecs(cordovaPlugins, config);\n    await installCocoaPodsPlugins(config, plugins, deployment);\n  }\n  await logCordovaManualSteps(cordovaPlugins, config, platform);\n\n  const incompatibleCordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Incompatible);\n  printPlugins(incompatibleCordovaPlugins, platform, 'incompatible');\n}\n\nasync function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config) {\n  cordovaPlugins.map((plugin: any) => {\n    generateCordovaPackageFile(plugin, config);\n  });\n}\n\nasync function generateCordovaPackageFile(p: Plugin, config: Config) {\n  const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name);\n  const iosVersion = getMajoriOSVersion(config);\n  const headerFiles = getPlatformElement(p, platform, 'header-file');\n  let headersText = '';\n  if (headerFiles.length > 0) {\n    headersText = `,\n            publicHeadersPath: \".\"`;\n  }\n\n  const content = `// swift-tools-version: 5.9\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"${p.name}\",\n    platforms: [.iOS(.v${iosVersion})],\n    products: [\n        .library(\n            name: \"${p.name}\",\n            targets: [\"${p.name}\"]\n        )\n    ],\n    dependencies: [\n        .package(url: \"https://github.com/ionic-team/capacitor-swift-pm.git\", from: \"${iosPlatformVersion}\")\n    ],\n    targets: [\n        .target(\n            name: \"${p.name}\",\n            dependencies: [\n                .product(name: \"Cordova\", package: \"capacitor-swift-pm\")\n            ],\n            path: \".\"${headersText}\n        )\n    ]\n)`;\n  await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content);\n}\n\nexport async function installCocoaPodsPlugins(config: Config, plugins: Plugin[], deployment: boolean): Promise<void> {\n  await runTask(`Updating iOS native dependencies with ${c.input(`${await config.ios.podPath} install`)}`, () => {\n    return updatePodfile(config, plugins, deployment);\n  });\n}\n\nasync function updatePodfile(config: Config, plugins: Plugin[], deployment: boolean): Promise<void> {\n  const dependenciesContent = await generatePodFile(config, plugins);\n  const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config);\n  const podfilePath = join(config.ios.nativeProjectDirAbs, 'Podfile');\n  let podfileContent = await readFile(podfilePath, { encoding: 'utf-8' });\n  podfileContent = podfileContent.replace(/(def capacitor_pods)[\\s\\S]+?(\\nend)/, `$1${dependenciesContent}$2`);\n  podfileContent = podfileContent.replace(\n    /(require_relative)[\\s\\S]+?(@capacitor\\/ios\\/scripts\\/pods_helpers')/,\n    `require_relative '${relativeCapacitoriOSPath}/scripts/pods_helpers'`,\n  );\n  await writeFile(podfilePath, podfileContent, { encoding: 'utf-8' });\n\n  const podPath = await config.ios.podPath;\n  const useBundler = (await config.ios.packageManager) === 'bundler';\n  if (useBundler) {\n    await runCommand('bundle', ['exec', 'pod', 'install', ...(deployment ? ['--deployment'] : [])], {\n      cwd: config.ios.nativeProjectDirAbs,\n    });\n  } else if (await isInstalled('pod')) {\n    await runCommand(podPath, ['install', ...(deployment ? ['--deployment'] : [])], {\n      cwd: config.ios.nativeProjectDirAbs,\n    });\n  } else {\n    logger.warn('Skipping pod install because CocoaPods is not installed');\n  }\n\n  const isXcodebuildAvailable = await isInstalled('xcodebuild');\n  if (isXcodebuildAvailable) {\n    await runCommand('xcodebuild', ['-project', basename(`${config.ios.nativeXcodeProjDirAbs}`), 'clean'], {\n      cwd: config.ios.nativeProjectDirAbs,\n    });\n  } else {\n    logger.warn('Unable to find \"xcodebuild\". Skipping xcodebuild clean step...');\n  }\n}\n\nasync function getRelativeCapacitoriOSPath(config: Config) {\n  const capacitoriOSPath = resolveNode(config.app.rootDir, '@capacitor/ios', 'package.json');\n\n  if (!capacitoriOSPath) {\n    fatal(\n      `Unable to find ${c.strong('node_modules/@capacitor/ios')}.\\n` +\n        `Are you sure ${c.strong('@capacitor/ios')} is installed?`,\n    );\n  }\n\n  return convertToUnixPath(relative(config.ios.nativeProjectDirAbs, await realpath(dirname(capacitoriOSPath))));\n}\n\nasync function generatePodFile(config: Config, plugins: Plugin[]): Promise<string> {\n  const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config);\n\n  const capacitorPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Core);\n  const pods = await Promise.all(\n    capacitorPlugins.map(async (p) => {\n      if (!p.ios) {\n        return '';\n      }\n\n      return `  pod '${p.ios.name}', :path => '${convertToUnixPath(\n        relative(config.ios.nativeProjectDirAbs, await realpath(p.rootPath)),\n      )}'\\n`;\n    }),\n  );\n  const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova);\n  cordovaPlugins.map(async (p) => {\n    const podspecs = getPlatformElement(p, platform, 'podspec');\n    podspecs.map((podspec: any) => {\n      podspec.pods.map((pPods: any) => {\n        pPods.pod.map((pod: any) => {\n          if (pod.$.git) {\n            let gitRef = '';\n            if (pod.$.tag) {\n              gitRef = `, :tag => '${pod.$.tag}'`;\n            } else if (pod.$.branch) {\n              gitRef = `, :branch => '${pod.$.branch}'`;\n            } else if (pod.$.commit) {\n              gitRef = `, :commit => '${pod.$.commit}'`;\n            }\n            pods.push(`  pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\\n`);\n          }\n        });\n      });\n    });\n  });\n  const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p));\n  const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el));\n  if (noStaticPlugins.length > 0) {\n    pods.push(`  pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\\n`);\n  }\n  if (staticPlugins.length > 0) {\n    pods.push(`  pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\\n`);\n  }\n  const resourcesPlugins = cordovaPlugins.filter(filterResources);\n  if (resourcesPlugins.length > 0) {\n    pods.push(`  pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\\n`);\n  }\n  return `\n  pod 'Capacitor', :path => '${relativeCapacitoriOSPath}'\n  pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}'\n${pods.join('').trimRight()}`;\n}\n\nfunction getFrameworkName(framework: any) {\n  if (isFramework(framework)) {\n    if (framework.$.custom && framework.$.custom === 'true') {\n      return framework.$.src;\n    }\n    return framework.$.src.substr(0, framework.$.src.indexOf('.'));\n  }\n  return framework.$.src.substr(0, framework.$.src.indexOf('.')).replace('lib', '');\n}\n\nfunction isFramework(framework: any) {\n  return framework.$.src.split('.').pop().includes('framework');\n}\n\nasync function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config) {\n  const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p));\n  const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el));\n  generateCordovaPodspec(noStaticPlugins, config, false);\n  generateCordovaPodspec(staticPlugins, config, true);\n}\n\nasync function generateCordovaPodspec(cordovaPlugins: Plugin[], config: Config, isStatic: boolean) {\n  const weakFrameworks: string[] = [];\n  const linkedFrameworks: string[] = [];\n  const customFrameworks: string[] = [];\n  const systemLibraries: string[] = [];\n  const sourceFrameworks: string[] = [];\n  const frameworkDeps: string[] = [];\n  const compilerFlags: string[] = [];\n  let prefsArray: any[] = [];\n  let name = 'CordovaPlugins';\n  let sourcesFolderName = 'sources';\n  if (isStatic) {\n    name += 'Static';\n    frameworkDeps.push('s.static_framework = true');\n    sourcesFolderName += 'static';\n  }\n  cordovaPlugins.map((plugin: any) => {\n    const frameworks = getPlatformElement(plugin, platform, 'framework');\n    frameworks.map((framework: any) => {\n      if (!framework.$.type) {\n        const name = getFrameworkName(framework);\n        if (isFramework(framework)) {\n          if (framework.$.weak && framework.$.weak === 'true') {\n            if (!weakFrameworks.includes(name)) {\n              weakFrameworks.push(name);\n            }\n          } else if (framework.$.custom && framework.$.custom === 'true') {\n            const frameworktPath = join(sourcesFolderName, plugin.name, name);\n            if (!customFrameworks.includes(frameworktPath)) {\n              customFrameworks.push(frameworktPath);\n            }\n          } else {\n            if (!linkedFrameworks.includes(name)) {\n              linkedFrameworks.push(name);\n            }\n          }\n        } else {\n          if (!systemLibraries.includes(name)) {\n            systemLibraries.push(name);\n          }\n        }\n      } else if (framework.$.type && framework.$.type === 'podspec') {\n        let depString = `s.dependency '${framework.$.src}'`;\n        if (framework.$.spec && framework.$.spec !== '') {\n          depString += `, '${framework.$.spec}'`;\n        }\n        if (!frameworkDeps.includes(depString)) {\n          frameworkDeps.push(depString);\n        }\n      }\n    });\n    prefsArray = prefsArray.concat(getAllElements(plugin, platform, 'preference'));\n    const podspecs = getPlatformElement(plugin, platform, 'podspec');\n    podspecs.map((podspec: any) => {\n      podspec.pods.map((pods: any) => {\n        pods.pod.map((pod: any) => {\n          let depString = `s.dependency '${pod.$.name}'`;\n          if (pod.$.spec && pod.$.spec !== '') {\n            depString += `, '${pod.$.spec}'`;\n          }\n          if (!frameworkDeps.includes(depString)) {\n            frameworkDeps.push(depString);\n          }\n        });\n      });\n    });\n    const sourceFiles = getPlatformElement(plugin, platform, 'source-file');\n    sourceFiles.map((sourceFile: any) => {\n      if (sourceFile.$.framework && sourceFile.$.framework === 'true') {\n        let fileName = sourceFile.$.src.split('/').pop();\n        if (!fileName.startsWith('lib')) {\n          fileName = 'lib' + fileName;\n        }\n        const frameworktPath = join(sourcesFolderName, plugin.name, fileName);\n        if (!sourceFrameworks.includes(frameworktPath)) {\n          sourceFrameworks.push(frameworktPath);\n        }\n      } else if (sourceFile.$['compiler-flags']) {\n        const cFlag = sourceFile.$['compiler-flags'];\n        if (!compilerFlags.includes(cFlag)) {\n          compilerFlags.push(cFlag);\n        }\n      }\n    });\n  });\n  const onlySystemLibraries = systemLibraries.filter((library) => removeNoSystem(library, sourceFrameworks));\n  if (weakFrameworks.length > 0) {\n    frameworkDeps.push(`s.weak_frameworks = '${weakFrameworks.join(`', '`)}'`);\n  }\n  if (linkedFrameworks.length > 0) {\n    frameworkDeps.push(`s.frameworks = '${linkedFrameworks.join(`', '`)}'`);\n  }\n  if (onlySystemLibraries.length > 0) {\n    frameworkDeps.push(`s.libraries = '${onlySystemLibraries.join(`', '`)}'`);\n  }\n  if (customFrameworks.length > 0) {\n    frameworkDeps.push(`s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`);\n    frameworkDeps.push(\n      `s.exclude_files = 'sources/**/*.framework/Headers/*.h', 'sources/**/*.framework/PrivateHeaders/*.h'`,\n    );\n  }\n  if (sourceFrameworks.length > 0) {\n    frameworkDeps.push(`s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`);\n  }\n  if (compilerFlags.length > 0) {\n    frameworkDeps.push(`s.compiler_flags = '${compilerFlags.join(' ')}'`);\n  }\n  const arcPlugins = cordovaPlugins.filter(filterARCFiles);\n  if (arcPlugins.length > 0) {\n    frameworkDeps.push(`s.subspec 'noarc' do |sna|\n      sna.requires_arc = false\n      sna.source_files = 'noarc/**/*.{swift,h,m,c,cc,mm,cpp}'\n    end`);\n  }\n  let frameworksString = frameworkDeps.join('\\n    ');\n  frameworksString = await replaceFrameworkVariables(config, prefsArray, frameworksString);\n  const content = `\n  Pod::Spec.new do |s|\n    s.name = '${name}'\n    s.version = '${config.cli.package.version}'\n    s.summary = 'Autogenerated spec'\n    s.license = 'Unknown'\n    s.homepage = 'https://example.com'\n    s.authors = { 'Capacitor Generator' => 'hi@example.com' }\n    s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${config.cli.package.version}' }\n    s.source_files = '${sourcesFolderName}/**/*.{swift,h,m,c,cc,mm,cpp}'\n    s.ios.deployment_target  = '${config.ios.minVersion}'\n    s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1 WK_WEB_VIEW_ONLY=1' }\n    s.dependency 'CapacitorCordova'${getLinkerFlags(config)}\n    s.swift_version  = '5.1'\n    ${frameworksString}\n  end`;\n  await writeFile(join(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), content);\n}\n\nfunction getLinkerFlags(config: Config) {\n  if (config.app.extConfig.ios?.cordovaLinkerFlags) {\n    return `\\n    s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '${config.app.extConfig.ios.cordovaLinkerFlags.join(\n      ' ',\n    )}' }`;\n  }\n  return '';\n}\n\nasync function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) {\n  for (const p of cordovaPlugins) {\n    const sourceFiles = getPlatformElement(p, platform, 'source-file');\n    const headerFiles = getPlatformElement(p, platform, 'header-file');\n    const codeFiles = sourceFiles.concat(headerFiles);\n    const frameworks = getPlatformElement(p, platform, 'framework');\n    let sourcesFolderName = 'sources';\n    if (needsStaticPod(p)) {\n      sourcesFolderName += 'static';\n    }\n    const sourcesFolder = join(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name);\n    for (const codeFile of codeFiles) {\n      let fileName = codeFile.$.src.split('/').pop();\n      const fileExt = codeFile.$.src.split('.').pop();\n      if (fileExt === 'a' && !fileName.startsWith('lib')) {\n        fileName = 'lib' + fileName;\n      }\n      let destFolder = sourcesFolderName;\n      if (codeFile.$['compiler-flags'] && codeFile.$['compiler-flags'] === '-fno-objc-arc') {\n        destFolder = 'noarc';\n      }\n      const filePath = getFilePath(config, p, codeFile.$.src);\n      const fileDest = join(config.ios.cordovaPluginsDirAbs, destFolder, p.name, fileName);\n      await copy(filePath, fileDest);\n      if (!codeFile.$.framework) {\n        let fileContent = await readFile(fileDest, { encoding: 'utf-8' });\n        if (fileExt === 'swift') {\n          fileContent = 'import Cordova\\n' + fileContent;\n          await writeFile(fileDest, fileContent, { encoding: 'utf-8' });\n        } else {\n          if (fileContent.includes('@import Firebase;')) {\n            fileContent = fileContent.replace('@import Firebase;', '#import <Firebase/Firebase.h>');\n            await writeFile(fileDest, fileContent, { encoding: 'utf-8' });\n          }\n          if (\n            fileContent.includes('[NSBundle bundleForClass:[self class]]') ||\n            fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]')\n          ) {\n            fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', '[NSBundle mainBundle]');\n            fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', '[NSBundle mainBundle]');\n            await writeFile(fileDest, fileContent, { encoding: 'utf-8' });\n          }\n          if (fileContent.includes('[self.webView superview]') || fileContent.includes('self.webView.superview')) {\n            fileContent = fileContent.replace(/\\[self.webView superview\\]/g, 'self.viewController.view');\n            fileContent = fileContent.replace(/self.webView.superview/g, 'self.viewController.view');\n            await writeFile(fileDest, fileContent, { encoding: 'utf-8' });\n          }\n        }\n      }\n    }\n    const resourceFiles = getPlatformElement(p, platform, 'resource-file');\n    for (const resourceFile of resourceFiles) {\n      const fileName = resourceFile.$.src.split('/').pop();\n      await copy(\n        getFilePath(config, p, resourceFile.$.src),\n        join(config.ios.cordovaPluginsDirAbs, 'resources', fileName),\n      );\n    }\n    for (const framework of frameworks) {\n      if (framework.$.custom && framework.$.custom === 'true') {\n        await copy(getFilePath(config, p, framework.$.src), join(sourcesFolder, framework.$.src));\n      }\n    }\n  }\n}\n\nasync function removePluginsNativeFiles(config: Config) {\n  await remove(config.ios.cordovaPluginsDirAbs);\n  await extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs);\n}\n\nfunction filterResources(plugin: Plugin) {\n  const resources = getPlatformElement(plugin, platform, 'resource-file');\n  return resources.length > 0;\n}\n\nfunction filterARCFiles(plugin: Plugin) {\n  const sources = getPlatformElement(plugin, platform, 'source-file');\n  const sourcesARC = sources.filter(\n    (sourceFile: any) => sourceFile.$['compiler-flags'] && sourceFile.$['compiler-flags'] === '-fno-objc-arc',\n  );\n  return sourcesARC.length > 0;\n}\n\nfunction removeNoSystem(library: string, sourceFrameworks: string[]) {\n  const libraries = sourceFrameworks.filter((framework) => framework.includes(library));\n  return libraries.length === 0;\n}\n\nasync function getPluginsTask(config: Config) {\n  return await runTask('Updating iOS plugins', async () => {\n    const allPlugins = await getPlugins(config, 'ios');\n    const iosPlugins = await getIOSPlugins(allPlugins);\n    return iosPlugins;\n  });\n}\n\nasync function replaceFrameworkVariables(config: Config, prefsArray: any[], frameworkString: string) {\n  prefsArray.map((preference: any) => {\n    frameworkString = frameworkString.replace(\n      new RegExp(('$' + preference.$.name).replace('$', '\\\\$&'), 'g'),\n      preference.$.default,\n    );\n  });\n  return frameworkString;\n}\n"
  },
  {
    "path": "cli/src/ipc.ts",
    "content": "import { fork } from '@ionic/utils-subprocess';\nimport Debug from 'debug';\nimport { open, mkdirp } from 'fs-extra';\nimport { request } from 'https';\nimport { resolve } from 'path';\n\nimport type { Metric } from './telemetry';\nimport { ENV_PATHS } from './util/cli';\n\nconst debug = Debug('capacitor:ipc');\n\nexport interface TelemetryIPCMessage {\n  type: 'telemetry';\n  data: Metric<string, unknown>;\n}\n\nexport type IPCMessage = TelemetryIPCMessage;\n\n/**\n * Send an IPC message to a forked process.\n */\nexport async function send(msg: IPCMessage): Promise<void> {\n  const dir = ENV_PATHS.log;\n  await mkdirp(dir);\n  const logPath = resolve(dir, 'ipc.log');\n\n  debug('Sending %O IPC message to forked process (logs: %O)', msg.type, logPath);\n\n  const fd = await open(logPath, 'a');\n  const p = fork(process.argv[1], ['📡'], { stdio: ['ignore', fd, fd, 'ipc'] });\n\n  p.send(msg);\n  p.disconnect();\n  p.unref();\n}\n\n/**\n * Receive and handle an IPC message.\n *\n * Assume minimal context and keep external dependencies to a minimum.\n */\nexport async function receive(msg: IPCMessage): Promise<void> {\n  debug('Received %O IPC message', msg.type);\n\n  if (msg.type === 'telemetry') {\n    const now = new Date().toISOString();\n    const { data } = msg;\n\n    // This request is only made if telemetry is on.\n    const req = request(\n      {\n        hostname: 'api.ionicjs.com',\n        port: 443,\n        path: '/events/metrics',\n        method: 'POST',\n        headers: {\n          'Content-Type': 'application/json',\n        },\n      },\n      (response) => {\n        debug('Sent %O metric to events service (status: %O)', data.name, response.statusCode);\n\n        if (response.statusCode !== 204) {\n          response.on('data', (chunk) => {\n            debug('Bad response from events service. Request body: %O', chunk.toString());\n          });\n        }\n      },\n    );\n\n    const body = {\n      metrics: [data],\n      sent_at: now,\n    };\n\n    req.end(JSON.stringify(body));\n  }\n}\n"
  },
  {
    "path": "cli/src/log.ts",
    "content": "import {\n  LOGGER_LEVELS,\n  StreamOutputStrategy,\n  TTYOutputStrategy,\n  createDefaultLogger,\n} from '@ionic/cli-framework-output';\nimport type { LoggerLevelWeight } from '@ionic/cli-framework-output';\nimport type { Answers, PromptObject } from 'prompts';\n\nimport c from './colors';\nimport { isInteractive } from './util/term';\n\nconst options = {\n  colors: c,\n  stream: process.argv.includes('--json') ? process.stderr : process.stdout,\n};\n\nexport const output = isInteractive() ? new TTYOutputStrategy(options) : new StreamOutputStrategy(options);\n\nexport const logger = createDefaultLogger({\n  output,\n  formatterOptions: {\n    titleize: false,\n    tags: new Map<LoggerLevelWeight, string>([\n      [LOGGER_LEVELS.DEBUG, c.log.DEBUG('[debug]')],\n      [LOGGER_LEVELS.INFO, c.log.INFO('[info]')],\n      [LOGGER_LEVELS.WARN, c.log.WARN('[warn]')],\n      [LOGGER_LEVELS.ERROR, c.log.ERROR('[error]')],\n    ]),\n  },\n});\n\nexport async function logPrompt<T extends string>(msg: string, promptObject: PromptObject<T>): Promise<Answers<T>> {\n  const { wordWrap } = await import('@ionic/cli-framework-output');\n  const { prompt } = await import('prompts');\n\n  logger.log({\n    msg: `${c.input('[?]')} ${wordWrap(msg, { indentation: 4 })}`,\n    logger,\n    format: false,\n  });\n\n  return prompt(promptObject, { onCancel: () => process.exit(1) });\n}\n\nexport function logSuccess(msg: string): void {\n  logger.msg(`${c.success('[success]')} ${msg}`);\n}\n"
  },
  {
    "path": "cli/src/plugin.ts",
    "content": "import { readJSON } from 'fs-extra';\nimport { dirname, join } from 'path';\n\nimport c from './colors';\nimport type { Config } from './definitions';\nimport { fatal } from './errors';\nimport { logger } from './log';\nimport { resolveNode } from './util/node';\nimport { readXML } from './util/xml';\n\nexport const enum PluginType {\n  Core,\n  Cordova,\n  Incompatible,\n}\n\nexport interface PluginManifest {\n  readonly ios?: {\n    readonly src?: string;\n  };\n  readonly android?: {\n    readonly src?: string;\n  };\n}\n\nexport interface Plugin {\n  id: string;\n  name: string;\n  version: string;\n  rootPath: string;\n  manifest?: PluginManifest;\n  repository?: any;\n  xml?: any;\n  ios?: {\n    name: string;\n    type: PluginType;\n    path: string;\n  };\n  android?: {\n    type: PluginType;\n    path: string;\n  };\n}\n\nexport function getIncludedPluginPackages(config: Config, platform: string): readonly string[] | undefined {\n  const { extConfig } = config.app;\n\n  switch (platform) {\n    case 'android':\n      return extConfig.android?.includePlugins ?? extConfig.includePlugins;\n    case 'ios':\n      return extConfig.ios?.includePlugins ?? extConfig.includePlugins;\n  }\n}\n\nexport async function getPlugins(config: Config, platform: string): Promise<Plugin[]> {\n  const possiblePlugins = getIncludedPluginPackages(config, platform) ?? getDependencies(config);\n  const resolvedPlugins = await Promise.all(possiblePlugins.map(async (p) => resolvePlugin(config, p)));\n\n  return resolvedPlugins.filter((p): p is Plugin => !!p);\n}\n\nexport async function resolvePlugin(config: Config, name: string): Promise<Plugin | null> {\n  try {\n    const packagePath = resolveNode(config.app.rootDir, name, 'package.json');\n    if (!packagePath) {\n      fatal(`Unable to find ${c.strong(`node_modules/${name}`)}.\\n` + `Are you sure ${c.strong(name)} is installed?`);\n    }\n\n    const rootPath = dirname(packagePath);\n    const meta = await readJSON(packagePath);\n    if (!meta) {\n      return null;\n    }\n    if (meta.capacitor) {\n      return {\n        id: name,\n        name: fixName(name),\n        version: meta.version,\n        rootPath,\n        repository: meta.repository,\n        manifest: meta.capacitor,\n      };\n    }\n    const pluginXMLPath = join(rootPath, 'plugin.xml');\n    const xmlMeta = await readXML(pluginXMLPath);\n    return {\n      id: name,\n      name: fixName(name),\n      version: meta.version,\n      rootPath: rootPath,\n      repository: meta.repository,\n      xml: xmlMeta.plugin,\n    };\n  } catch (e) {\n    // ignore\n  }\n  return null;\n}\n\nexport function getDependencies(config: Config): string[] {\n  return [\n    ...Object.keys(config.app.package.dependencies ?? {}),\n    ...Object.keys(config.app.package.devDependencies ?? {}),\n  ];\n}\n\nexport function fixName(name: string): string {\n  name = name\n    .replace(/\\//g, '_')\n    .replace(/-/g, '_')\n    .replace(/@/g, '')\n    .replace(/_\\w/g, (m) => m[1].toUpperCase());\n\n  return name.charAt(0).toUpperCase() + name.slice(1);\n}\n\nexport function printPlugins(\n  plugins: Plugin[],\n  platform: string,\n  type: 'capacitor' | 'cordova' | 'incompatible' = 'capacitor',\n): void {\n  if (plugins.length === 0) {\n    return;\n  }\n\n  let msg: string;\n  const plural = plugins.length === 1 ? '' : 's';\n\n  switch (type) {\n    case 'cordova':\n      msg = `Found ${plugins.length} Cordova plugin${plural} for ${c.strong(platform)}:\\n`;\n      break;\n    case 'incompatible':\n      msg = `Found ${plugins.length} incompatible Cordova plugin${plural} for ${c.strong(\n        platform,\n      )}, skipped install:\\n`;\n      break;\n    case 'capacitor':\n      msg = `Found ${plugins.length} Capacitor plugin${plural} for ${c.strong(platform)}:\\n`;\n      break;\n  }\n\n  msg += plugins.map((p) => `${p.id}${c.weak(`@${p.version}`)}`).join('\\n');\n\n  logger.info(msg);\n}\n\nexport function getPluginPlatform(p: Plugin, platform: string): any {\n  const platforms = p.xml.platform;\n  if (platforms) {\n    const platforms = p.xml.platform.filter(function (item: any) {\n      return item.$.name === platform;\n    });\n    return platforms[0];\n  }\n  return [];\n}\n\nexport function getPlatformElement(p: Plugin, platform: string, elementName: string): any {\n  const platformTag = getPluginPlatform(p, platform);\n  if (platformTag) {\n    const element = platformTag[elementName];\n    if (element) {\n      return element;\n    }\n  }\n  return [];\n}\n\nexport function getPluginType(p: Plugin, platform: string): PluginType {\n  switch (platform) {\n    case 'ios':\n      return p.ios?.type ?? PluginType.Core;\n    case 'android':\n      return p.android?.type ?? PluginType.Core;\n  }\n\n  return PluginType.Core;\n}\n\n/**\n * Get each JavaScript Module for the given plugin\n */\nexport function getJSModules(p: Plugin, platform: string): any {\n  return getAllElements(p, platform, 'js-module');\n}\n\n/**\n * Get each asset tag for the given plugin\n */\nexport function getAssets(p: Plugin, platform: string): any {\n  return getAllElements(p, platform, 'asset');\n}\n\nexport function getFilePath(config: Config, plugin: Plugin, path: string): string {\n  if (path.startsWith('node_modules')) {\n    let pathSegments = path.split('/').slice(1);\n    if (pathSegments[0].startsWith('@')) {\n      pathSegments = [pathSegments[0] + '/' + pathSegments[1], ...pathSegments.slice(2)];\n    }\n\n    const filePath = resolveNode(config.app.rootDir, ...pathSegments);\n    if (!filePath) {\n      throw new Error(`Can't resolve module ${pathSegments[0]}`);\n    }\n\n    return filePath;\n  }\n  return join(plugin.rootPath, path);\n}\n\n/**\n * For a given plugin, return all the plugin.xml elements with elementName, checking root and specified platform\n */\nexport function getAllElements(p: Plugin, platform: string, elementName: string): any {\n  let modules: string[] = [];\n  if (p.xml[elementName]) {\n    modules = modules.concat(p.xml[elementName]);\n  }\n  const platformModules = getPluginPlatform(p, platform);\n  if (platformModules?.[elementName]) {\n    modules = modules.concat(platformModules[elementName]);\n  }\n  return modules;\n}\n"
  },
  {
    "path": "cli/src/sysconfig.ts",
    "content": "import Debug from 'debug';\nimport { readJSON, writeJSON, mkdirp } from 'fs-extra';\nimport { dirname, resolve } from 'path';\n\nimport { ENV_PATHS } from './util/cli';\nimport { uuidv4 } from './util/uuid';\n\nconst debug = Debug('capacitor:sysconfig');\n\nconst SYSCONFIG_FILE = 'sysconfig.json';\nconst SYSCONFIG_PATH = resolve(ENV_PATHS.config, SYSCONFIG_FILE);\n\nexport interface SystemConfig {\n  /**\n   * A UUID that anonymously identifies this computer.\n   */\n  readonly machine: string;\n\n  /**\n   * Whether telemetry is enabled or not.\n   *\n   * If undefined, a choice has not yet been made.\n   */\n  readonly telemetry?: boolean;\n\n  /**\n   * Wheter the user choose to signup or not.\n   *\n   * If undefined, the prompt has not been shown.\n   */\n  readonly signup?: boolean;\n}\n\nexport async function readConfig(): Promise<SystemConfig> {\n  debug('Reading from %O', SYSCONFIG_PATH);\n\n  try {\n    return await readJSON(SYSCONFIG_PATH);\n  } catch (e: any) {\n    if (e.code !== 'ENOENT') {\n      throw e;\n    }\n\n    const sysconfig: SystemConfig = {\n      machine: uuidv4(),\n    };\n\n    await writeConfig(sysconfig);\n\n    return sysconfig;\n  }\n}\n\nexport async function writeConfig(sysconfig: SystemConfig): Promise<void> {\n  debug('Writing to %O', SYSCONFIG_PATH);\n\n  await mkdirp(dirname(SYSCONFIG_PATH));\n  await writeJSON(SYSCONFIG_PATH, sysconfig, { spaces: '\\t' });\n}\n"
  },
  {
    "path": "cli/src/tasks/add.ts",
    "content": "import { prettyPath } from '@ionic/utils-terminal';\nimport { pathExists } from 'fs-extra';\n\nimport { addAndroid, createLocalProperties } from '../android/add';\nimport { editProjectSettingsAndroid, checkAndroidPackage } from '../android/common';\nimport c from '../colors';\nimport {\n  getKnownPlatforms,\n  check,\n  checkAppConfig,\n  checkPackage,\n  resolvePlatform,\n  runPlatformHook,\n  runTask,\n  isValidPlatform,\n  isValidCommunityPlatform,\n  promptForPlatform,\n  getProjectPlatformDirectory,\n  isValidEnterprisePlatform,\n} from '../common';\nimport type { CheckFunction } from '../common';\nimport type { Config } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { addIOS } from '../ios/add';\nimport { editProjectSettingsIOS, checkIOSPackage, getCommonChecks } from '../ios/common';\nimport { logger, logSuccess, output } from '../log';\n\nimport { sync } from './sync';\n\nexport async function addCommand(config: Config, selectedPlatformName: string): Promise<void> {\n  if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) {\n    const platformDir = resolvePlatform(config, selectedPlatformName);\n    if (platformDir) {\n      await runPlatformHook(config, selectedPlatformName, platformDir, 'capacitor:add');\n    } else {\n      let msg = `Platform ${c.input(selectedPlatformName)} not found.`;\n\n      if (await isValidCommunityPlatform(selectedPlatformName)) {\n        msg += `\\nTry installing ${c.strong(\n          `@capacitor-community/${selectedPlatformName}`,\n        )} and adding the platform again.`;\n      }\n\n      if (await isValidEnterprisePlatform(selectedPlatformName)) {\n        msg +=\n          `\\nThis is an enterprise platform and @ionic-enterprise/capacitor-${selectedPlatformName} is not installed.\\n` +\n          `To learn how to use this platform, visit https://ionic.io/docs/${selectedPlatformName}`;\n      }\n\n      logger.error(msg);\n    }\n  } else {\n    const knownPlatforms = await getKnownPlatforms();\n    const platformName = await promptForPlatform(\n      knownPlatforms,\n      `Please choose a platform to add:`,\n      selectedPlatformName,\n    );\n\n    if (platformName === config.web.name) {\n      webWarning();\n      return;\n    }\n\n    const existingPlatformDir = await getProjectPlatformDirectory(config, platformName);\n\n    if (existingPlatformDir) {\n      fatal(\n        `${c.input(platformName)} platform already exists.\\n` +\n          `To re-add this platform, first remove ${c.strong(\n            prettyPath(existingPlatformDir),\n          )}, then run this command again.\\n` +\n          `${c.strong('WARNING')}: Your native project will be completely removed.`,\n      );\n    }\n\n    try {\n      await check([() => checkPackage(), () => checkAppConfig(config), ...(await getAddChecks(config, platformName))]);\n      await doAdd(config, platformName);\n      await editPlatforms(config, platformName);\n\n      if (await pathExists(config.app.webDirAbs)) {\n        await sync(config, platformName, false, false);\n        if (platformName === config.android.name) {\n          await runTask('Syncing Gradle', async () => {\n            return createLocalProperties(config.android.platformDirAbs);\n          });\n        }\n      } else {\n        logger.warn(`${c.success(c.strong('sync'))} could not run--missing ${c.strong(config.app.webDir)} directory.`);\n      }\n\n      printNextSteps(platformName);\n    } catch (e: any) {\n      if (!isFatal(e)) {\n        fatal(e.stack ?? e);\n      }\n\n      throw e;\n    }\n  }\n}\n\nfunction printNextSteps(platformName: string) {\n  logSuccess(`${c.strong(platformName)} platform added!`);\n  output.write(\n    `Follow the Developer Workflow guide to get building:\\n${c.strong(\n      `https://capacitorjs.com/docs/basics/workflow`,\n    )}\\n`,\n  );\n}\n\nasync function getAddChecks(config: Config, platformName: string): Promise<CheckFunction[]> {\n  if (platformName === config.ios.name) {\n    return [() => checkIOSPackage(config), ...(await getCommonChecks(config))];\n  } else if (platformName === config.android.name) {\n    return [() => checkAndroidPackage(config)];\n  } else if (platformName === config.web.name) {\n    return [];\n  } else {\n    throw `Platform ${platformName} is not valid.`;\n  }\n}\n\nasync function doAdd(config: Config, platformName: string): Promise<void> {\n  await runTask(c.success(c.strong('add')), async () => {\n    if (platformName === config.ios.name) {\n      await addIOS(config);\n    } else if (platformName === config.android.name) {\n      await addAndroid(config);\n    }\n  });\n}\n\nasync function editPlatforms(config: Config, platformName: string) {\n  if (platformName === config.ios.name) {\n    await editProjectSettingsIOS(config);\n  } else if (platformName === config.android.name) {\n    await editProjectSettingsAndroid(config);\n  }\n}\n\nfunction webWarning() {\n  logger.error(\n    `Not adding platform ${c.strong('web')}.\\n` +\n      `In Capacitor, the web platform is just your web app! For example, if you have a React or Angular project, the web platform is that project.\\n` +\n      `To add Capacitor functionality to your web app, follow the Web Getting Started Guide: ${c.strong(\n        'https://capacitorjs.com/docs/web',\n      )}`,\n  );\n}\n"
  },
  {
    "path": "cli/src/tasks/build.ts",
    "content": "import { buildAndroid } from '../android/build';\nimport { selectPlatforms, promptForPlatform } from '../common';\nimport type { Config } from '../definitions';\nimport { XcodeExportMethod } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { buildiOS } from '../ios/build';\n\nexport interface BuildCommandOptions {\n  scheme?: string;\n  flavor?: string;\n  keystorepath?: string;\n  keystorepass?: string;\n  keystorealias?: string;\n  keystorealiaspass?: string;\n  androidreleasetype?: 'AAB' | 'APK';\n  signingtype?: 'apksigner' | 'jarsigner';\n  configuration: string;\n  xcodeTeamId?: string;\n  xcodeExportMethod?: XcodeExportMethod;\n  xcodeSigningType?: 'automatic' | 'manual';\n  xcodeSigningCertificate?: string;\n  xcodeProvisioningProfile?: string;\n}\n\nexport async function buildCommand(\n  config: Config,\n  selectedPlatformName: string,\n  buildOptions: BuildCommandOptions,\n): Promise<void> {\n  const platforms = await selectPlatforms(config, selectedPlatformName);\n  let platformName: string;\n  if (platforms.length === 1) {\n    platformName = platforms[0];\n  } else {\n    platformName = await promptForPlatform(\n      platforms.filter(createBuildablePlatformFilter(config)),\n      `Please choose a platform to build for:`,\n    );\n  }\n\n  const buildCommandOptions: BuildCommandOptions = {\n    scheme: buildOptions.scheme || config.ios.scheme,\n    flavor: buildOptions.flavor || config.android.flavor,\n    keystorepath: buildOptions.keystorepath || config.android.buildOptions.keystorePath,\n    keystorepass: buildOptions.keystorepass || config.android.buildOptions.keystorePassword,\n    keystorealias: buildOptions.keystorealias || config.android.buildOptions.keystoreAlias,\n    keystorealiaspass: buildOptions.keystorealiaspass || config.android.buildOptions.keystoreAliasPassword,\n    androidreleasetype: buildOptions.androidreleasetype || config.android.buildOptions.releaseType || 'AAB',\n    signingtype: buildOptions.signingtype || config.android.buildOptions.signingType || 'jarsigner',\n    configuration: buildOptions.configuration || 'Release',\n    xcodeTeamId: buildOptions.xcodeTeamId || config.ios.buildOptions.teamId,\n    xcodeExportMethod:\n      buildOptions.xcodeExportMethod || config.ios.buildOptions.exportMethod || XcodeExportMethod.AppStoreConnect,\n    xcodeSigningType: buildOptions.xcodeSigningType || config.ios.buildOptions.xcodeSigningStyle || 'automatic',\n    xcodeSigningCertificate: buildOptions.xcodeSigningCertificate || config.ios.buildOptions.signingCertificate,\n    xcodeProvisioningProfile: buildOptions.xcodeProvisioningProfile || config.ios.buildOptions.provisioningProfile,\n  };\n\n  try {\n    await build(config, platformName, buildCommandOptions);\n  } catch (e) {\n    if (!isFatal(e)) {\n      fatal((e as any).stack ?? e);\n    }\n    throw e;\n  }\n}\n\nexport async function build(config: Config, platformName: string, buildOptions: BuildCommandOptions): Promise<void> {\n  if (platformName == config.ios.name) {\n    await buildiOS(config, buildOptions);\n  } else if (platformName === config.android.name) {\n    await buildAndroid(config, buildOptions);\n  } else if (platformName === config.web.name) {\n    throw `Platform \"${platformName}\" is not available in the build command.`;\n  } else {\n    throw `Platform \"${platformName}\" is not valid.`;\n  }\n}\n\nfunction createBuildablePlatformFilter(config: Config): (platform: string) => boolean {\n  return (platform) => platform === config.ios.name || platform === config.android.name;\n}\n"
  },
  {
    "path": "cli/src/tasks/config.ts",
    "content": "import util from 'util';\n\nimport type { Config } from '../definitions';\nimport { output } from '../log';\n\nexport async function configCommand(config: Config, json: boolean): Promise<void> {\n  const evaluatedConfig = await deepAwait(config);\n\n  if (json) {\n    process.stdout.write(`${JSON.stringify(evaluatedConfig)}\\n`);\n  } else {\n    output.write(`${util.inspect(evaluatedConfig, { depth: Infinity, colors: true })}\\n`);\n  }\n}\n\nasync function deepAwait(obj: any): Promise<any> {\n  if (obj && !Array.isArray(obj) && typeof obj === 'object' && obj.constructor === Object) {\n    const o: any = {};\n\n    for (const [k, v] of Object.entries(obj)) {\n      o[k] = await deepAwait(v);\n    }\n\n    return o;\n  } else {\n    return await obj;\n  }\n}\n"
  },
  {
    "path": "cli/src/tasks/copy.ts",
    "content": "import { copy as fsCopy, pathExists, remove, writeJSON } from 'fs-extra';\nimport { basename, join, relative, resolve } from 'path';\n\nimport c from '../colors';\nimport {\n  checkWebDir,\n  resolvePlatform,\n  runHooks,\n  runPlatformHook,\n  runTask,\n  isValidPlatform,\n  selectPlatforms,\n} from '../common';\nimport { getCordovaPlugins, handleCordovaPluginsJS, writeCordovaAndroidManifest } from '../cordova';\nimport type { Config } from '../definitions';\nimport { isFatal } from '../errors';\nimport { getIOSPlugins } from '../ios/common';\nimport { logger } from '../log';\nimport { getPlugins } from '../plugin';\nimport { generateIOSPackageJSON } from '../util/iosplugin';\nimport { allSerial } from '../util/promise';\n\nimport { inlineSourceMaps } from './sourcemaps';\n\nexport async function copyCommand(config: Config, selectedPlatformName: string, inline = false): Promise<void> {\n  if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) {\n    const platformDir = resolvePlatform(config, selectedPlatformName);\n    if (platformDir) {\n      await runPlatformHook(config, selectedPlatformName, platformDir, 'capacitor:copy');\n    } else {\n      logger.error(`Platform ${c.input(selectedPlatformName)} not found.`);\n    }\n  } else {\n    const platforms = await selectPlatforms(config, selectedPlatformName);\n    try {\n      await allSerial(platforms.map((platformName) => () => copy(config, platformName, inline)));\n    } catch (e: any) {\n      if (isFatal(e)) {\n        throw e;\n      }\n\n      logger.error(e.stack ?? e);\n    }\n  }\n}\n\nexport async function copy(config: Config, platformName: string, inline = false): Promise<void> {\n  await runTask(c.success(c.strong(`copy ${platformName}`)), async () => {\n    const result = await checkWebDir(config);\n    if (result) {\n      throw result;\n    }\n\n    await runHooks(config, platformName, config.app.rootDir, 'capacitor:copy:before');\n\n    const allPlugins = await getPlugins(config, platformName);\n    let usesFederatedCapacitor = false;\n    if (allPlugins.filter((plugin) => plugin.id === '@ionic-enterprise/federated-capacitor').length > 0) {\n      usesFederatedCapacitor = true;\n    }\n\n    let usesLiveUpdates = false;\n    if (allPlugins.filter((plugin) => plugin.id === '@capacitor/live-updates').length > 0) {\n      usesLiveUpdates = true;\n    }\n\n    let usesSSLPinning = false;\n    if (\n      allPlugins.filter(\n        (plugin) => plugin.id === '@ionic-enterprise/ssl-pinning' || plugin.id === '@capacitor/ssl-pinning',\n      ).length > 0\n    ) {\n      usesSSLPinning = true;\n    }\n\n    if (platformName === config.ios.name) {\n      if (usesFederatedCapacitor) {\n        await copyFederatedWebDirs(config, await config.ios.webDirAbs);\n        if (config.app.extConfig?.plugins?.FederatedCapacitor?.liveUpdatesKey) {\n          await copySecureLiveUpdatesKey(\n            config.app.extConfig.plugins.FederatedCapacitor.liveUpdatesKey,\n            config.app.rootDir,\n            config.ios.nativeTargetDirAbs,\n          );\n        }\n      } else {\n        await copyWebDir(config, await config.ios.webDirAbs, config.app.webDirAbs);\n      }\n      if (usesLiveUpdates && config.app.extConfig?.plugins?.LiveUpdates?.key) {\n        await copySecureLiveUpdatesKey(\n          config.app.extConfig.plugins.LiveUpdates.key,\n          config.app.rootDir,\n          config.ios.nativeTargetDirAbs,\n        );\n      }\n      if (usesSSLPinning && config.app.extConfig?.plugins?.SSLPinning?.certs) {\n        await copySSLCert(\n          config.app.extConfig.plugins.SSLPinning?.certs as unknown as string[],\n          config.app.rootDir,\n          await config.ios.webDirAbs,\n        );\n      }\n      await copyCapacitorConfig(config, config.ios.nativeTargetDirAbs);\n      const cordovaPlugins = await getCordovaPlugins(config, platformName);\n      await handleCordovaPluginsJS(cordovaPlugins, config, platformName);\n      const iosPlugins = await getIOSPlugins(allPlugins);\n      await generateIOSPackageJSON(config, iosPlugins);\n    } else if (platformName === config.android.name) {\n      if (usesFederatedCapacitor) {\n        await copyFederatedWebDirs(config, config.android.webDirAbs);\n        if (config.app.extConfig?.plugins?.FederatedCapacitor?.liveUpdatesKey) {\n          await copySecureLiveUpdatesKey(\n            config.app.extConfig.plugins.FederatedCapacitor.liveUpdatesKey,\n            config.app.rootDir,\n            config.android.assetsDirAbs,\n          );\n        }\n      } else {\n        await copyWebDir(config, config.android.webDirAbs, config.app.webDirAbs);\n      }\n      if (usesLiveUpdates && config.app.extConfig?.plugins?.LiveUpdates?.key) {\n        await copySecureLiveUpdatesKey(\n          config.app.extConfig.plugins.LiveUpdates.key,\n          config.app.rootDir,\n          config.android.assetsDirAbs,\n        );\n      }\n      if (usesSSLPinning && config.app.extConfig?.plugins?.SSLPinning?.certs) {\n        await copySSLCert(\n          config.app.extConfig.plugins.SSLPinning?.certs as unknown as string[],\n          config.app.rootDir,\n          config.android.assetsDirAbs,\n        );\n      }\n      await copyCapacitorConfig(config, config.android.assetsDirAbs);\n      const cordovaPlugins = await getCordovaPlugins(config, platformName);\n      await handleCordovaPluginsJS(cordovaPlugins, config, platformName);\n      await writeCordovaAndroidManifest(cordovaPlugins, config, platformName);\n    } else if (platformName === config.web.name) {\n      if (usesFederatedCapacitor) {\n        logger.info('FederatedCapacitor Plugin installed, skipping web bundling...');\n      }\n    } else {\n      throw `Platform ${platformName} is not valid.`;\n    }\n    if (inline) {\n      await inlineSourceMaps(config, platformName);\n    }\n  });\n\n  await runHooks(config, platformName, config.app.rootDir, 'capacitor:copy:after');\n}\n\nasync function copyCapacitorConfig(config: Config, nativeAbsDir: string) {\n  const nativeRelDir = relative(config.app.rootDir, nativeAbsDir);\n  const nativeConfigFile = 'capacitor.config.json';\n  const nativeConfigFilePath = join(nativeAbsDir, nativeConfigFile);\n\n  await runTask(`Creating ${c.strong(nativeConfigFile)} in ${nativeRelDir}`, async () => {\n    delete (config.app.extConfig.android as any)?.buildOptions;\n    delete (config.app.extConfig.ios as any)?.buildOptions;\n    await writeJSON(nativeConfigFilePath, config.app.extConfig, {\n      spaces: '\\t',\n    });\n  });\n}\n\nasync function copyWebDir(config: Config, nativeAbsDir: string, webAbsDir: string) {\n  const webRelDir = basename(webAbsDir);\n  const nativeRelDir = relative(config.app.rootDir, nativeAbsDir);\n\n  if (config.app.extConfig.server?.url && !(await pathExists(webAbsDir))) {\n    logger.warn(\n      `Cannot copy web assets from ${c.strong(webRelDir)} to ${nativeRelDir}\\n` +\n        `Web asset directory specified by ${c.input('webDir')} does not exist. This is not an error because ${c.input(\n          'server.url',\n        )} is set in config.`,\n    );\n\n    return;\n  }\n\n  await runTask(`Copying web assets from ${c.strong(webRelDir)} to ${nativeRelDir}`, async () => {\n    await remove(nativeAbsDir);\n    return fsCopy(webAbsDir, nativeAbsDir);\n  });\n}\n\nasync function copyFederatedWebDirs(config: Config, nativeAbsDir: string) {\n  logger.info('FederatedCapacitor Plugin Loaded - Copying Web Assets');\n\n  if (!config.app.extConfig?.plugins?.FederatedCapacitor) {\n    throw `FederatedCapacitor plugin is present but no valid config is defined.`;\n  }\n\n  const federatedConfig = config.app.extConfig.plugins.FederatedCapacitor;\n  if (federatedConfig) {\n    if (federatedConfig.shell.name === undefined) {\n      throw `FederatedCapacitor plugin is present but no valid Shell application is defined in the config.`;\n    }\n\n    if (!federatedConfig.apps.every(isFederatedApp)) {\n      throw `FederatedCapacitor plugin is present but there is a problem with the apps defined in the config.`;\n    }\n\n    const copyApps = (): Promise<void>[] => {\n      return federatedConfig.apps.map((app) => {\n        const appDir = resolve(config.app.rootDir, app.webDir);\n        return copyWebDir(config, resolve(nativeAbsDir, app.name), appDir);\n      });\n    };\n\n    const copyShell = (): Promise<void> => {\n      return copyWebDir(config, resolve(nativeAbsDir, federatedConfig.shell.name), config.app.webDirAbs);\n    };\n\n    await Promise.all([...copyApps(), copyShell()]);\n  }\n}\n\nfunction isFederatedApp(config: any): config is FederatedApp {\n  return (config as FederatedApp).webDir !== undefined && (config as FederatedApp).name !== undefined;\n}\n\nasync function copySecureLiveUpdatesKey(secureLiveUpdatesKeyFile: string, rootDir: string, nativeAbsDir: string) {\n  const keyAbsFromPath = join(rootDir, secureLiveUpdatesKeyFile);\n  const keyAbsToPath = join(nativeAbsDir, basename(keyAbsFromPath));\n  const keyRelToDir = relative(rootDir, nativeAbsDir);\n\n  if (!(await pathExists(keyAbsFromPath))) {\n    logger.warn(\n      `Cannot copy Secure Live Updates signature file from ${c.strong(keyAbsFromPath)} to ${keyRelToDir}\\n` +\n        `Signature file does not exist at specified key path.`,\n    );\n\n    return;\n  }\n\n  await runTask(\n    `Copying Secure Live Updates key from ${c.strong(secureLiveUpdatesKeyFile)} to ${keyRelToDir}`,\n    async () => {\n      return fsCopy(keyAbsFromPath, keyAbsToPath);\n    },\n  );\n}\n\nasync function copySSLCert(sslCertPaths: string[], rootDir: string, targetDir: string) {\n  const validCertPaths: string[] = [];\n  for (const sslCertPath of sslCertPaths) {\n    const certAbsFromPath = join(rootDir, sslCertPath);\n    if (!(await pathExists(certAbsFromPath))) {\n      logger.warn(\n        `Cannot copy SSL Certificate file from ${c.strong(certAbsFromPath)}\\n` +\n          `SSL Certificate does not exist at specified path.`,\n      );\n\n      return;\n    }\n    validCertPaths.push(certAbsFromPath);\n  }\n  const certsDirAbsToPath = join(targetDir, 'certs');\n  const certsDirRelToDir = relative(rootDir, targetDir);\n  await runTask(`Copying SSL Certificates from to ${certsDirRelToDir}`, async () => {\n    const promises: Promise<void>[] = [];\n    for (const certPath of validCertPaths) {\n      promises.push(fsCopy(certPath, join(certsDirAbsToPath, basename(certPath))));\n    }\n    return Promise.all(promises);\n  });\n}\n\ninterface LiveUpdateConfig {\n  key?: string;\n}\n\ninterface FederatedApp {\n  name: string;\n  webDir: string;\n}\n\ninterface FederatedCapacitor {\n  shell: Omit<FederatedApp, 'webDir'>;\n  apps: FederatedApp[];\n  liveUpdatesKey?: string;\n}\n\ndeclare module '../declarations' {\n  interface PluginsConfig {\n    LiveUpdates?: LiveUpdateConfig;\n    FederatedCapacitor?: FederatedCapacitor;\n  }\n}\n"
  },
  {
    "path": "cli/src/tasks/create.ts",
    "content": "import c from '../colors';\nimport { fatal } from '../errors';\n\nexport async function createCommand(): Promise<void> {\n  fatal(`The create command has been removed.\\n` + `Use ${c.input('npm init @capacitor/app')}`);\n}\n"
  },
  {
    "path": "cli/src/tasks/doctor.ts",
    "content": "import { readJSON } from 'fs-extra';\n\nimport { doctorAndroid } from '../android/doctor';\nimport c from '../colors';\nimport { selectPlatforms } from '../common';\nimport type { Config } from '../definitions';\nimport { doctorIOS } from '../ios/doctor';\nimport { output } from '../log';\nimport { emoji as _e } from '../util/emoji';\nimport { resolveNode } from '../util/node';\nimport { getCommandOutput } from '../util/subprocess';\n\nexport async function doctorCommand(config: Config, selectedPlatformName: string): Promise<void> {\n  output.write(`${_e('💊', '')}   ${c.strong('Capacitor Doctor')}  ${_e('💊', '')} \\n\\n`);\n\n  await doctorCore(config);\n\n  const platforms = await selectPlatforms(config, selectedPlatformName);\n  await Promise.all(\n    platforms.map((platformName) => {\n      return doctor(config, platformName);\n    }),\n  );\n}\n\nexport async function doctorCore(config: Config): Promise<void> {\n  const [cliVersion, coreVersion, androidVersion, iosVersion] = await Promise.all([\n    getCommandOutput('npm', ['info', '@capacitor/cli', 'version']),\n    getCommandOutput('npm', ['info', '@capacitor/core', 'version']),\n    getCommandOutput('npm', ['info', '@capacitor/android', 'version']),\n    getCommandOutput('npm', ['info', '@capacitor/ios', 'version']),\n  ]);\n\n  output.write(\n    `${c.strong('Latest Dependencies:')}\\n\\n` +\n      `  @capacitor/cli: ${c.weak(cliVersion ?? 'unknown')}\\n` +\n      `  @capacitor/core: ${c.weak(coreVersion ?? 'unknown')}\\n` +\n      `  @capacitor/android: ${c.weak(androidVersion ?? 'unknown')}\\n` +\n      `  @capacitor/ios: ${c.weak(iosVersion ?? 'unknown')}\\n\\n` +\n      `${c.strong('Installed Dependencies:')}\\n\\n`,\n  );\n\n  await printInstalledPackages(config);\n\n  output.write('\\n');\n}\n\nasync function printInstalledPackages(config: Config) {\n  const packageNames = ['@capacitor/cli', '@capacitor/core', '@capacitor/android', '@capacitor/ios'];\n  await Promise.all(\n    packageNames.map(async (packageName) => {\n      const packagePath = resolveNode(config.app.rootDir, packageName, 'package.json');\n      await printPackageVersion(packageName, packagePath);\n    }),\n  );\n}\n\nasync function printPackageVersion(packageName: string, packagePath: string | null) {\n  let version;\n  if (packagePath) {\n    version = (await readJSON(packagePath)).version;\n  }\n  output.write(`  ${packageName}: ${c.weak(version || 'not installed')}\\n`);\n}\n\nexport async function doctor(config: Config, platformName: string): Promise<void> {\n  if (platformName === config.ios.name) {\n    await doctorIOS(config);\n  } else if (platformName === config.android.name) {\n    await doctorAndroid(config);\n  } else if (platformName === config.web.name) {\n    return Promise.resolve();\n  } else {\n    throw `Platform ${platformName} is not valid.`;\n  }\n}\n"
  },
  {
    "path": "cli/src/tasks/init.ts",
    "content": "import open from 'open';\nimport { basename, dirname, resolve } from 'path';\n\nimport c from '../colors';\nimport { check, checkAppId, checkAppName, runTask } from '../common';\nimport { CONFIG_FILE_NAME_JSON, CONFIG_FILE_NAME_TS, writeConfig } from '../config';\nimport { getCordovaPreferences } from '../cordova';\nimport type { Config, ExternalConfig } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { detectFramework } from '../framework-configs';\nimport { output, logSuccess, logPrompt } from '../log';\nimport { readConfig, writeConfig as sysWriteConfig } from '../sysconfig';\nimport { resolveNode } from '../util/node';\nimport { checkInteractive, isInteractive } from '../util/term';\n\nexport async function initCommand(\n  config: Config,\n  name: string,\n  id: string,\n  webDirFromCLI?: string,\n  skipAppIDValidation?: boolean,\n): Promise<void> {\n  try {\n    if (!checkInteractive(name, id)) {\n      return;\n    }\n\n    if (config.app.extConfigType !== 'json') {\n      fatal(\n        `Cannot run ${c.input('init')} for a project using a non-JSON configuration file.\\n` +\n          `Delete ${c.strong(config.app.extConfigName)} and try again.`,\n      );\n    }\n\n    const isNewConfig = Object.keys(config.app.extConfig).length === 0;\n    const tsInstalled = !!resolveNode(config.app.rootDir, 'typescript');\n    const appName = await getName(config, name);\n    const appId = await getAppId(config, id);\n    const webDir = isInteractive()\n      ? await getWebDir(config, webDirFromCLI)\n      : (webDirFromCLI ?? config.app.extConfig.webDir ?? 'www');\n\n    if (skipAppIDValidation === true) {\n      await check([() => checkAppName(config, appName)]);\n    } else {\n      await check([() => checkAppName(config, appName), () => checkAppId(config, appId)]);\n    }\n\n    const cordova = await getCordovaPreferences(config);\n\n    await runMergeConfig(\n      config,\n      {\n        appId,\n        appName,\n        webDir,\n        cordova,\n      },\n      isNewConfig && tsInstalled ? 'ts' : 'json',\n    );\n  } catch (e: any) {\n    if (!isFatal(e)) {\n      output.write('Usage: npx cap init appName appId\\n' + 'Example: npx cap init \"My App\" \"com.example.myapp\"\\n\\n');\n\n      fatal(e.stack ?? e);\n    }\n\n    throw e;\n  }\n}\n\nasync function getName(config: Config, name: string) {\n  if (!name) {\n    const answers = await logPrompt(\n      `${c.strong(`What is the name of your app?`)}\\n` +\n        `This should be a human-friendly app name, like what you'd see in the App Store.`,\n      {\n        type: 'text',\n        name: 'name',\n        message: `Name`,\n        initial: config.app.appName ? config.app.appName : (config.app.package.name ?? 'App'),\n      },\n    );\n    return answers.name;\n  }\n  return name;\n}\n\nasync function getAppId(config: Config, id: string) {\n  if (!id) {\n    const answers = await logPrompt(\n      `${c.strong(`What should be the Package ID for your app?`)}\\n` +\n        `Package IDs (aka Bundle ID in iOS and Application ID in Android) are unique identifiers for apps. They must be in reverse domain name notation, generally representing a domain name that you or your company owns.`,\n      {\n        type: 'text',\n        name: 'id',\n        message: `Package ID`,\n        initial: config.app.appId ? config.app.appId : 'com.example.app',\n      },\n    );\n    return answers.id;\n  }\n  return id;\n}\n\nasync function getWebDir(config: Config, webDir?: string) {\n  if (!webDir) {\n    const framework = detectFramework(config);\n    if (framework?.webDir) {\n      return framework.webDir;\n    }\n\n    const answers = await logPrompt(\n      `${c.strong(`What is the web asset directory for your app?`)}\\n` +\n        `This directory should contain the final ${c.strong('index.html')} of your app.`,\n      {\n        type: 'text',\n        name: 'webDir',\n        message: `Web asset directory`,\n        initial: config.app.webDir ? config.app.webDir : 'www',\n      },\n    );\n    return answers.webDir;\n  }\n  return webDir;\n}\n\nasync function runMergeConfig(config: Config, extConfig: ExternalConfig, type: 'json' | 'ts') {\n  const configDirectory = dirname(config.app.extConfigFilePath);\n  const newConfigPath = resolve(configDirectory, type === 'ts' ? CONFIG_FILE_NAME_TS : CONFIG_FILE_NAME_JSON);\n\n  await runTask(`Creating ${c.strong(basename(newConfigPath))} in ${c.input(config.app.rootDir)}`, async () => {\n    await mergeConfig(config, extConfig, newConfigPath);\n  });\n\n  printNextSteps(basename(newConfigPath));\n  if (isInteractive()) {\n    let sysconfig = await readConfig();\n    if (typeof sysconfig.signup === 'undefined') {\n      const signup = await promptToSignup();\n      sysconfig = { ...sysconfig, signup };\n      await sysWriteConfig(sysconfig);\n    }\n  }\n}\n\nasync function mergeConfig(config: Config, extConfig: ExternalConfig, newConfigPath: string): Promise<void> {\n  const oldConfig = { ...config.app.extConfig };\n  const newConfig = { ...oldConfig, ...extConfig };\n\n  await writeConfig(newConfig, newConfigPath);\n}\n\nfunction printNextSteps(newConfigName: string) {\n  logSuccess(`${c.strong(newConfigName)} created!`);\n  output.write(`\\nNext steps: \\n${c.strong(`https://capacitorjs.com/docs/getting-started#where-to-go-next`)}\\n`);\n}\n\nasync function promptToSignup(): Promise<boolean> {\n  const answers = await logPrompt(\n    `Join the Ionic Community! 💙\\n` +\n      `Connect with millions of developers on the Ionic Forum and get access to live events, news updates, and more.`,\n    {\n      type: 'confirm',\n      name: 'create',\n      message: `Create free Ionic account?`,\n      initial: true,\n    },\n  );\n\n  if (answers.create) {\n    open(`http://ionicframework.com/signup?source=capacitor`);\n  }\n  return answers.create;\n}\n"
  },
  {
    "path": "cli/src/tasks/list.ts",
    "content": "import { getAndroidPlugins } from '../android/common';\nimport c from '../colors';\nimport { selectPlatforms } from '../common';\nimport type { Config } from '../definitions';\nimport { isFatal } from '../errors';\nimport { getIOSPlugins } from '../ios/common';\nimport { logger } from '../log';\nimport { PluginType, getPluginType, getPlugins, printPlugins } from '../plugin';\nimport type { Plugin } from '../plugin';\nimport { allSerial } from '../util/promise';\n\nexport async function listCommand(config: Config, selectedPlatformName: string): Promise<void> {\n  const platforms = await selectPlatforms(config, selectedPlatformName);\n  try {\n    await allSerial(platforms.map((platformName) => () => list(config, platformName)));\n  } catch (e: any) {\n    if (isFatal(e)) {\n      throw e;\n    }\n\n    logger.error(e.stack ?? e);\n  }\n}\n\nexport async function list(config: Config, platform: string): Promise<void> {\n  const allPlugins = await getPlugins(config, platform);\n  let plugins: Plugin[] = [];\n  if (platform === config.ios.name) {\n    plugins = await getIOSPlugins(allPlugins);\n  } else if (platform === config.android.name) {\n    plugins = await getAndroidPlugins(allPlugins);\n  } else if (platform === config.web.name) {\n    logger.info(`Listing plugins for ${c.input(platform)} is not possible.`);\n    return;\n  } else {\n    throw `Platform ${c.input(platform)} is not valid.`;\n  }\n\n  const capacitorPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Core);\n  printPlugins(capacitorPlugins, platform);\n  const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova);\n  printPlugins(cordovaPlugins, platform, 'cordova');\n  const incompatibleCordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Incompatible);\n  printPlugins(incompatibleCordovaPlugins, platform, 'incompatible');\n}\n"
  },
  {
    "path": "cli/src/tasks/migrate-spm.ts",
    "content": "import { check } from '../common';\nimport type { Config, Writable } from '../definitions';\nimport { fatal } from '../errors';\nimport { getCommonChecks } from '../ios/common';\nimport { logger } from '../log';\nimport {\n  extractSPMPackageDirectory,\n  removeCocoapodsFiles,\n  runCocoapodsDeintegrate,\n  addInfoPlistDebugIfNeeded,\n} from '../util/spm';\n\nimport { update } from './update';\n\nexport async function migrateToSPM(config: Config): Promise<void> {\n  if ((await config.ios.packageManager) == 'SPM') {\n    fatal('Capacitor project is already using SPM, exiting.');\n  }\n  await check(await getCommonChecks(config));\n  await extractSPMPackageDirectory(config);\n  await runCocoapodsDeintegrate(config);\n  await removeCocoapodsFiles(config);\n  await addInfoPlistDebugIfNeeded(config);\n  const configWritable: Writable<Config> = config as Writable<Config>;\n  configWritable.ios.packageManager = Promise.resolve('SPM');\n  await update(configWritable as Config, 'ios', false);\n  logger.info(\n    'To complete migration follow the manual steps at https://capacitorjs.com/docs/ios/spm#using-our-migration-tool',\n  );\n}\n"
  },
  {
    "path": "cli/src/tasks/migrate.ts",
    "content": "import { writeFileSync, readFileSync, existsSync } from 'fs-extra';\nimport { join } from 'path';\nimport { rimraf } from 'rimraf';\nimport { coerce, gte, lt } from 'semver';\n\nimport c from '../colors';\nimport { getCoreVersion, runTask, checkJDKMajorVersion } from '../common';\nimport type { Config } from '../definitions';\nimport { fatal } from '../errors';\nimport { getMajoriOSVersion } from '../ios/common';\nimport { logger, logPrompt, logSuccess } from '../log';\nimport { deleteFolderRecursive } from '../util/fs';\nimport { runCommand } from '../util/subprocess';\nimport { extractTemplate } from '../util/template';\n\n// eslint-disable-next-line prefer-const\nlet allDependencies: { [key: string]: any } = {};\nconst libs = ['@capacitor/core', '@capacitor/cli', '@capacitor/ios', '@capacitor/android'];\nconst plugins = [\n  '@capacitor/action-sheet',\n  '@capacitor/app',\n  '@capacitor/app-launcher',\n  '@capacitor/browser',\n  '@capacitor/camera',\n  '@capacitor/clipboard',\n  '@capacitor/device',\n  '@capacitor/dialog',\n  '@capacitor/filesystem',\n  '@capacitor/geolocation',\n  '@capacitor/google-maps',\n  '@capacitor/haptics',\n  '@capacitor/keyboard',\n  '@capacitor/local-notifications',\n  '@capacitor/motion',\n  '@capacitor/network',\n  '@capacitor/preferences',\n  '@capacitor/push-notifications',\n  '@capacitor/screen-orientation',\n  '@capacitor/screen-reader',\n  '@capacitor/share',\n  '@capacitor/splash-screen',\n  '@capacitor/status-bar',\n  '@capacitor/text-zoom',\n  '@capacitor/toast',\n];\nconst coreVersion = '^8.0.0';\nconst pluginVersion = '^8.0.0';\nconst gradleVersion = '8.14.3';\nconst iOSVersion = '15';\nconst kotlinVersion = '2.2.20';\nlet installFailed = false;\n\nexport async function migrateCommand(config: Config, noprompt: boolean, packagemanager: string): Promise<void> {\n  if (config === null) {\n    fatal('Config data missing');\n  }\n\n  const capMajor = await checkCapacitorMajorVersion(config);\n  if (capMajor < 7) {\n    fatal('Migrate can only be used on Capacitor 7, please use the CLI in Capacitor 7 to upgrade to 7 first');\n  }\n\n  const jdkMajor = await checkJDKMajorVersion();\n\n  if (jdkMajor < 21) {\n    logger.warn('Capacitor 8 requires JDK 21 or higher. Some steps may fail.');\n  }\n\n  const variablesAndClasspaths:\n    | {\n        variables: any;\n        'com.android.tools.build:gradle': string;\n        'com.google.gms:google-services': string;\n      }\n    | undefined = await getAndroidVariablesAndClasspaths(config);\n\n  if (!variablesAndClasspaths) {\n    fatal('Variable and Classpath info could not be read.');\n  }\n\n  allDependencies = {\n    ...config.app.package.dependencies,\n    ...config.app.package.devDependencies,\n  };\n\n  const monorepoWarning =\n    'Please note this tool is not intended for use in a mono-repo environment, you should migrate manually instead. Refer to https://capacitorjs.com/docs/next/updating/8-0';\n\n  logger.info(monorepoWarning);\n\n  const { migrateconfirm } = noprompt\n    ? { migrateconfirm: 'y' }\n    : await logPrompt(`Capacitor 8 sets a deployment target of iOS ${iOSVersion} and Android 16 (SDK 36). \\n`, {\n        type: 'text',\n        name: 'migrateconfirm',\n        message: `Are you sure you want to migrate? (Y/n)`,\n        initial: 'y',\n      });\n\n  if (typeof migrateconfirm === 'string' && migrateconfirm.toLowerCase() === 'y') {\n    try {\n      const { depInstallConfirm } = noprompt\n        ? { depInstallConfirm: 'y' }\n        : await logPrompt(\n            `Would you like the migrator to run npm, yarn, pnpm, or bun install to install the latest versions of capacitor packages? (Those using other package managers should answer N)`,\n            {\n              type: 'text',\n              name: 'depInstallConfirm',\n              message: `Run Dependency Install? (Y/n)`,\n              initial: 'y',\n            },\n          );\n\n      const runNpmInstall = typeof depInstallConfirm === 'string' && depInstallConfirm.toLowerCase() === 'y';\n\n      let installerType = 'npm';\n      if (runNpmInstall) {\n        const { manager } = packagemanager\n          ? { manager: packagemanager }\n          : await logPrompt('What dependency manager do you use?', {\n              type: 'select',\n              name: 'manager',\n              message: `Dependency Management Tool`,\n              choices: [\n                { title: 'NPM', value: 'npm' },\n                { title: 'Yarn', value: 'yarn' },\n                { title: 'PNPM', value: 'pnpm' },\n                { title: 'Bun', value: 'bun' },\n              ],\n              initial: 0,\n            });\n        installerType = manager;\n      }\n\n      try {\n        await runTask(`Installing Latest Modules using ${installerType}.`, () => {\n          return installLatestLibs(installerType, runNpmInstall, config);\n        });\n      } catch (ex) {\n        logger.error(\n          `${installerType} install failed. Try deleting node_modules folder and running ${c.input(\n            `${installerType} install --force`,\n          )} manually.`,\n        );\n        installFailed = true;\n      }\n\n      // Update iOS Projects\n      if (allDependencies['@capacitor/ios'] && existsSync(config.ios.platformDirAbs)) {\n        const currentiOSVersion = getMajoriOSVersion(config);\n        if (parseInt(currentiOSVersion) < parseInt(iOSVersion)) {\n          // ios template changes\n          await runTask(`Migrating deployment target to ${iOSVersion}.0.`, () => {\n            return updateFile(\n              config,\n              join(config.ios.nativeXcodeProjDirAbs, 'project.pbxproj'),\n              'IPHONEOS_DEPLOYMENT_TARGET = ',\n              ';',\n              `${iOSVersion}.0`,\n            );\n          });\n\n          if ((await config.ios.packageManager) !== 'SPM') {\n            // Update Podfile\n            await runTask(`Migrating Podfile to ${iOSVersion}.0.`, () => {\n              return updateFile(\n                config,\n                join(config.ios.nativeProjectDirAbs, 'Podfile'),\n                `platform :ios, '`,\n                `'`,\n                `${iOSVersion}.0`,\n              );\n            });\n          }\n        } else {\n          logger.warn('Skipped updating deployment target');\n        }\n      }\n\n      if (!installFailed) {\n        await runTask(`Running cap sync.`, () => {\n          return runCommand('npx', ['cap', 'sync']);\n        });\n      } else {\n        logger.warn('Skipped Running cap sync.');\n      }\n\n      if (allDependencies['@capacitor/android'] && existsSync(config.android.platformDirAbs)) {\n        // AndroidManifest.xml add \"density\"\n        await runTask(`Migrating AndroidManifest.xml by adding density to Activity configChanges.`, () => {\n          return updateAndroidManifest(join(config.android.srcMainDirAbs, 'AndroidManifest.xml'));\n        });\n\n        const gradleWrapperVersion = getGradleWrapperVersion(\n          join(config.android.platformDirAbs, 'gradle', 'wrapper', 'gradle-wrapper.properties'),\n        );\n\n        if (!installFailed && gte(gradleVersion, gradleWrapperVersion)) {\n          try {\n            await runTask(`Upgrading gradle wrapper`, () => {\n              return updateGradleWrapperFiles(config.android.platformDirAbs);\n            });\n            // Run twice as first time it only updates the wrapper properties file\n            await runTask(`Upgrading gradle wrapper files`, () => {\n              return updateGradleWrapperFiles(config.android.platformDirAbs);\n            });\n          } catch (e: any) {\n            if (e.includes('EACCES')) {\n              logger.error(\n                `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${c.input(\n                  `chmod +x ./${config.android.platformDir}/gradlew`,\n                )} and ${c.input(\n                  `cd ${config.android.platformDir} && ./gradlew wrapper --distribution-type all --gradle-version ${gradleVersion} --warning-mode all`,\n                )} to update the files manually`,\n              );\n            } else {\n              logger.error(`gradle wrapper files were not updated`);\n            }\n          }\n        } else {\n          logger.warn('Skipped upgrading gradle wrapper files');\n        }\n        await runTask(`Migrating root build.gradle file.`, () => {\n          return updateBuildGradle(join(config.android.platformDirAbs, 'build.gradle'), variablesAndClasspaths);\n        });\n\n        await runTask(`Migrating app build.gradle file.`, () => {\n          return updateAppBuildGradle(join(config.android.appDirAbs, 'build.gradle'));\n        });\n\n        // Variables gradle\n        await runTask(`Migrating variables.gradle file.`, () => {\n          return (async (): Promise<void> => {\n            const variablesPath = join(config.android.platformDirAbs, 'variables.gradle');\n            let txt = readFile(variablesPath);\n            if (!txt) {\n              return;\n            }\n            txt = txt.replace(/= {2}'/g, `= '`);\n            writeFileSync(variablesPath, txt, { encoding: 'utf-8' });\n            for (const variable of Object.keys(variablesAndClasspaths.variables)) {\n              let replaceStart = `${variable} = '`;\n              let replaceEnd = `'\\n`;\n              if (typeof variablesAndClasspaths.variables[variable] === 'number') {\n                replaceStart = `${variable} = `;\n                replaceEnd = `\\n`;\n              }\n\n              if (txt.includes(replaceStart)) {\n                const first = txt.indexOf(replaceStart) + replaceStart.length;\n                const value = txt.substring(first, txt.indexOf(replaceEnd, first));\n                if (\n                  (typeof variablesAndClasspaths.variables[variable] === 'number' &&\n                    value <= variablesAndClasspaths.variables[variable]) ||\n                  (typeof variablesAndClasspaths.variables[variable] === 'string' &&\n                    lt(value, variablesAndClasspaths.variables[variable]))\n                ) {\n                  await updateFile(\n                    config,\n                    variablesPath,\n                    replaceStart,\n                    replaceEnd,\n                    variablesAndClasspaths.variables[variable].toString(),\n                    true,\n                  );\n                }\n              } else {\n                let file = readFile(variablesPath);\n                if (file) {\n                  file = file.replace(\n                    '}',\n                    `    ${replaceStart}${variablesAndClasspaths.variables[variable].toString()}${replaceEnd}}`,\n                  );\n                  writeFileSync(variablesPath, file);\n                }\n              }\n            }\n            const pluginVariables: { [key: string]: string } = {\n              firebaseMessagingVersion: '25.0.1',\n              playServicesLocationVersion: '21.3.0',\n              androidxBrowserVersion: '1.9.0',\n              androidxMaterialVersion: '1.13.0',\n              androidxExifInterfaceVersion: '1.4.1',\n              androidxCoreKTXVersion: '1.17.0',\n              googleMapsPlayServicesVersion: '19.2.0',\n              googleMapsUtilsVersion: '3.19.1',\n              googleMapsKtxVersion: '5.2.1',\n              googleMapsUtilsKtxVersion: '5.2.1',\n              kotlinxCoroutinesVersion: '1.10.2',\n              coreSplashScreenVersion: '1.2.0',\n            };\n            for (const variable of Object.keys(pluginVariables)) {\n              await updateFile(config, variablesPath, `${variable} = '`, `'`, pluginVariables[variable], true);\n            }\n          })();\n        });\n\n        rimraf.sync(join(config.android.appDirAbs, 'build'));\n      }\n\n      // Write all breaking changes\n      await runTask(`Writing breaking changes.`, () => {\n        return writeBreakingChanges();\n      });\n\n      if (!installFailed) {\n        logSuccess(`Migration to Capacitor ${coreVersion} is complete. Run and test your app!`);\n      } else {\n        logger.warn(\n          `Migration to Capacitor ${coreVersion} is incomplete. Check the log messages for more information.`,\n        );\n      }\n    } catch (err) {\n      fatal(`Failed to migrate: ${err}`);\n    }\n  } else {\n    fatal(`User canceled migration.`);\n  }\n}\n\nasync function checkCapacitorMajorVersion(config: Config): Promise<number> {\n  const capacitorVersion = await getCoreVersion(config);\n  const versionArray = capacitorVersion.match(/([0-9]+)\\.([0-9]+)\\.([0-9]+)/) ?? [];\n  const majorVersion = parseInt(versionArray[1]);\n  return majorVersion;\n}\n\nasync function installLatestLibs(dependencyManager: string, runInstall: boolean, config: Config) {\n  const pkgJsonPath = join(config.app.rootDir, 'package.json');\n  const pkgJsonFile = readFile(pkgJsonPath);\n  if (!pkgJsonFile) {\n    return;\n  }\n  const pkgJson: any = JSON.parse(pkgJsonFile);\n\n  for (const devDepKey of Object.keys(pkgJson['devDependencies'] || {})) {\n    if (libs.includes(devDepKey)) {\n      pkgJson['devDependencies'][devDepKey] = coreVersion;\n    } else if (plugins.includes(devDepKey)) {\n      pkgJson['devDependencies'][devDepKey] = pluginVersion;\n    }\n  }\n  for (const depKey of Object.keys(pkgJson['dependencies'] || {})) {\n    if (libs.includes(depKey)) {\n      pkgJson['dependencies'][depKey] = coreVersion;\n    } else if (plugins.includes(depKey)) {\n      pkgJson['dependencies'][depKey] = pluginVersion;\n    }\n  }\n\n  writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2), {\n    encoding: 'utf-8',\n  });\n\n  if (runInstall) {\n    rimraf.sync(join(config.app.rootDir, 'node_modules/@capacitor/!(cli)'));\n    await runCommand(dependencyManager, ['install']);\n    if (dependencyManager == 'yarn') {\n      await runCommand(dependencyManager, ['upgrade']);\n    } else {\n      await runCommand(dependencyManager, ['update']);\n    }\n  } else {\n    logger.info(`Please run an install command with your package manager of choice. (ex: yarn install)`);\n  }\n}\n\nasync function writeBreakingChanges() {\n  const breaking = [\n    '@capacitor/action-sheet',\n    '@capacitor/barcode-scanner',\n    '@capacitor/browser',\n    '@capacitor/camera',\n    '@capacitor/geolocation',\n    '@capacitor/google-maps',\n    '@capacitor/push-notifications',\n    '@capacitor/screen-orientation',\n    '@capacitor/splash-screen',\n    '@capacitor/status-bar',\n  ];\n  const broken = [];\n  for (const lib of breaking) {\n    if (allDependencies[lib]) {\n      broken.push(lib);\n    }\n  }\n  if (broken.length > 0) {\n    logger.info(\n      `IMPORTANT: Review https://capacitorjs.com/docs/next/updating/8-0#plugins for breaking changes in these plugins that you use: ${broken.join(\n        ', ',\n      )}.`,\n    );\n  }\n}\n\nasync function getAndroidVariablesAndClasspaths(config: Config) {\n  const tempAndroidTemplateFolder = join(config.cli.assetsDirAbs, 'tempAndroidTemplate');\n  await extractTemplate(config.cli.assets.android.platformTemplateArchiveAbs, tempAndroidTemplateFolder);\n  const variablesGradleFile = readFile(join(tempAndroidTemplateFolder, 'variables.gradle'));\n  const buildGradleFile = readFile(join(tempAndroidTemplateFolder, 'build.gradle'));\n  if (!variablesGradleFile || !buildGradleFile) {\n    return;\n  }\n  deleteFolderRecursive(tempAndroidTemplateFolder);\n\n  const firstIndxOfCATBGV = buildGradleFile.indexOf(`classpath 'com.android.tools.build:gradle:`) + 42;\n  const firstIndxOfCGGGS = buildGradleFile.indexOf(`com.google.gms:google-services:`) + 31;\n  const comAndroidToolsBuildGradleVersion =\n    '' + buildGradleFile.substring(firstIndxOfCATBGV, buildGradleFile.indexOf(\"'\", firstIndxOfCATBGV));\n  const comGoogleGmsGoogleServices =\n    '' + buildGradleFile.substring(firstIndxOfCGGGS, buildGradleFile.indexOf(\"'\", firstIndxOfCGGGS));\n\n  const variablesGradleAsJSON = JSON.parse(\n    variablesGradleFile\n      .replace('ext ', '')\n      .replace(/=/g, ':')\n      .replace(/\\n/g, ',')\n      .replace(/,([^:]+):/g, function (_k, p1) {\n        return `,\"${p1}\":`;\n      })\n      .replace('{,', '{')\n      .replace(',}', '}')\n      .replace(/\\s/g, '')\n      .replace(/'/g, '\"'),\n  );\n\n  return {\n    variables: variablesGradleAsJSON,\n    'com.android.tools.build:gradle': comAndroidToolsBuildGradleVersion,\n    'com.google.gms:google-services': comGoogleGmsGoogleServices,\n  };\n}\n\nfunction readFile(filename: string): string | undefined {\n  try {\n    if (!existsSync(filename)) {\n      logger.error(`Unable to find ${filename}. Try updating it manually`);\n      return;\n    }\n    return readFileSync(filename, 'utf-8');\n  } catch (err) {\n    logger.error(`Unable to read ${filename}. Verify it is not already open. ${err}`);\n  }\n}\n\nfunction getGradleWrapperVersion(filename: string): string {\n  const txt = readFile(filename);\n  if (!txt) {\n    return '0.0.0';\n  }\n  const version = txt.substring(txt.indexOf('gradle-') + 7, txt.indexOf('-all.zip'));\n  const semverVersion = coerce(version)?.version;\n  return semverVersion ? semverVersion : '0.0.0';\n}\n\nasync function updateGradleWrapperFiles(platformDir: string) {\n  await runCommand(\n    `./gradlew`,\n    ['wrapper', '--distribution-type', 'all', '--gradle-version', gradleVersion, '--warning-mode', 'all'],\n    {\n      cwd: platformDir,\n    },\n  );\n}\n\nasync function updateBuildGradle(\n  filename: string,\n  variablesAndClasspaths: {\n    variables: any;\n    'com.android.tools.build:gradle': string;\n    'com.google.gms:google-services': string;\n  },\n) {\n  const txt = readFile(filename);\n  if (!txt) {\n    return;\n  }\n  const neededDeps: { [key: string]: string } = {\n    'com.android.tools.build:gradle': variablesAndClasspaths['com.android.tools.build:gradle'],\n    'com.google.gms:google-services': variablesAndClasspaths['com.google.gms:google-services'],\n  };\n  let replaced = txt;\n\n  for (const dep of Object.keys(neededDeps)) {\n    if (replaced.includes(`classpath '${dep}`)) {\n      const firstIndex = replaced.indexOf(dep) + dep.length + 1;\n      const existingVersion = '' + replaced.substring(firstIndex, replaced.indexOf(\"'\", firstIndex));\n      if (gte(neededDeps[dep], existingVersion)) {\n        replaced = setAllStringIn(replaced, `classpath '${dep}:`, `'`, neededDeps[dep]);\n        logger.info(`Set ${dep} = ${neededDeps[dep]}.`);\n      }\n    }\n  }\n\n  const beforeKotlinVersionUpdate = replaced;\n  replaced = replaceVersion(replaced, /(ext\\.kotlin_version\\s*=\\s*['\"])([^'\"]+)(['\"])/, kotlinVersion);\n  replaced = replaceVersion(replaced, /(org\\.jetbrains\\.kotlin:kotlin[^:]*:)([\\d.]+)(['\"])/, kotlinVersion);\n  if (beforeKotlinVersionUpdate !== replaced) {\n    logger.info(`Set Kotlin version to ${kotlinVersion}`);\n  }\n  writeFileSync(filename, replaced, 'utf-8');\n}\n\nfunction replaceVersion(text: string, regex: RegExp, newVersion: string): string {\n  return text.replace(regex, (match, prefix, currentVersion, suffix) => {\n    const semVer = coerce(currentVersion)?.version;\n    if (gte(newVersion, semVer ? semVer : '0.0.0')) {\n      return `${prefix || ''}${newVersion}${suffix || ''}`;\n    }\n    return match;\n  });\n}\n\nasync function updateAppBuildGradle(filename: string) {\n  const txt = readFile(filename);\n  if (!txt) {\n    return;\n  }\n  let replaced = txt;\n\n  const gradlePproperties = ['compileSdk', 'namespace', 'ignoreAssetsPattern'];\n  for (const prop of gradlePproperties) {\n    // Use updated Groovy DSL syntax with \" = \" assignment\n    const regex = new RegExp(`(^\\\\s*${prop})\\\\s+(?!=)(.+)$`, 'gm');\n    replaced = replaced.replace(regex, (_match, key, value) => {\n      return `${key} = ${value.trim()}`;\n    });\n  }\n  writeFileSync(filename, replaced, 'utf-8');\n}\n\nasync function updateFile(\n  config: Config,\n  filename: string,\n  textStart: string,\n  textEnd: string,\n  replacement?: string,\n  skipIfNotFound?: boolean,\n): Promise<boolean> {\n  if (config === null) {\n    return false;\n  }\n  const path = filename;\n  let txt = readFile(path);\n  if (!txt) {\n    return false;\n  }\n  if (txt.includes(textStart)) {\n    if (replacement) {\n      txt = setAllStringIn(txt, textStart, textEnd, replacement);\n      writeFileSync(path, txt, { encoding: 'utf-8' });\n    } else {\n      // Replacing in code so we need to count the number of brackets to find the end of the function in swift\n      const lines = txt.split('\\n');\n      let replaced = '';\n      let keep = true;\n      let brackets = 0;\n      for (const line of lines) {\n        if (line.includes(textStart)) {\n          keep = false;\n        }\n        if (!keep) {\n          brackets += (line.match(/{/g) || []).length;\n          brackets -= (line.match(/}/g) || []).length;\n          if (brackets == 0) {\n            keep = true;\n          }\n        } else {\n          replaced += line + '\\n';\n        }\n      }\n      writeFileSync(path, replaced, { encoding: 'utf-8' });\n    }\n    return true;\n  } else if (!skipIfNotFound) {\n    logger.error(`Unable to find \"${textStart}\" in ${filename}. Try updating it manually`);\n  }\n\n  return false;\n}\n\nfunction setAllStringIn(data: string, start: string, end: string, replacement: string): string {\n  let position = 0;\n  let result = data;\n  let replaced = true;\n  while (replaced) {\n    const foundIdx = result.indexOf(start, position);\n    if (foundIdx == -1) {\n      replaced = false;\n    } else {\n      const idx = foundIdx + start.length;\n      position = idx + replacement.length;\n      result = result.substring(0, idx) + replacement + result.substring(result.indexOf(end, idx));\n    }\n  }\n  return result;\n}\n\nasync function updateAndroidManifest(filename: string) {\n  const txt = readFile(filename);\n  if (!txt) {\n    return;\n  }\n\n  if (txt.includes('|density') || txt.includes('density|')) {\n    return; // Probably already updated\n  }\n  // Since navigation was an optional change in Capacitor 7, attempting to add density and/or navigation\n  const replaced = txt\n    .replace(\n      'android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation\"',\n      'android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density\"',\n    )\n    .replace(\n      'android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode\"',\n      'android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density\"',\n    );\n\n  if (!replaced.includes('|density')) {\n    logger.error(`Unable to add 'density' to 'android:configChanges' in ${filename}. Try adding it manually`);\n  } else {\n    writeFileSync(filename, replaced, 'utf-8');\n  }\n}\n"
  },
  {
    "path": "cli/src/tasks/new-plugin.ts",
    "content": "import c from '../colors';\nimport { fatal } from '../errors';\n\nexport async function newPluginCommand(): Promise<void> {\n  fatal(`The plugin:generate command has been removed.\\n` + `Use ${c.input('npm init @capacitor/plugin')}`);\n}\n"
  },
  {
    "path": "cli/src/tasks/open.ts",
    "content": "import { openAndroid } from '../android/open';\nimport c from '../colors';\nimport {\n  resolvePlatform,\n  runPlatformHook,\n  runTask,\n  isValidPlatform,\n  selectPlatforms,\n  promptForPlatform,\n} from '../common';\nimport type { Config } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { openIOS } from '../ios/open';\nimport { logger } from '../log';\n\nexport async function openCommand(config: Config, selectedPlatformName: string): Promise<void> {\n  if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) {\n    const platformDir = resolvePlatform(config, selectedPlatformName);\n    if (platformDir) {\n      await runPlatformHook(config, selectedPlatformName, platformDir, 'capacitor:open');\n    } else {\n      logger.error(`Platform ${c.input(selectedPlatformName)} not found.`);\n    }\n  } else {\n    const platforms = await selectPlatforms(config, selectedPlatformName);\n    let platformName: string;\n    if (platforms.length === 1) {\n      platformName = platforms[0];\n    } else {\n      platformName = await promptForPlatform(\n        platforms.filter(createOpenablePlatformFilter(config)),\n        `Please choose a platform to open:`,\n      );\n    }\n\n    try {\n      await open(config, platformName);\n    } catch (e: any) {\n      if (!isFatal(e)) {\n        fatal(e.stack ?? e);\n      }\n\n      throw e;\n    }\n  }\n}\n\nfunction createOpenablePlatformFilter(config: Config): (platform: string) => boolean {\n  return (platform) => platform === config.ios.name || platform === config.android.name;\n}\n\nexport async function open(config: Config, platformName: string): Promise<void> {\n  if (platformName === config.ios.name) {\n    await runTask('Opening the Xcode workspace...', () => {\n      return openIOS(config);\n    });\n  } else if (platformName === config.android.name) {\n    return openAndroid(config);\n  } else if (platformName === config.web.name) {\n    return Promise.resolve();\n  } else {\n    throw `Platform ${platformName} is not valid.`;\n  }\n}\n"
  },
  {
    "path": "cli/src/tasks/run.ts",
    "content": "import { columnar } from '@ionic/utils-terminal';\n\nimport { runAndroid } from '../android/run';\nimport c from '../colors';\nimport {\n  isValidPlatform,\n  resolvePlatform,\n  runPlatformHook,\n  selectPlatforms,\n  promptForPlatform,\n  getPlatformTargetName,\n} from '../common';\nimport { getCordovaPlugins, writeCordovaAndroidManifest } from '../cordova';\nimport type { Config } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { runIOS } from '../ios/run';\nimport { logger, output } from '../log';\nimport { CapLiveReloadHelper } from '../util/livereload';\nimport { getPlatformTargets } from '../util/native-run';\n\nimport { sync } from './sync';\n\nexport interface RunCommandOptions {\n  scheme?: string;\n  flavor?: string;\n  list?: boolean;\n  json?: boolean;\n  target?: string;\n  targetName?: string;\n  targetNameSdkVersion?: string;\n  sync?: boolean;\n  forwardPorts?: string;\n  liveReload?: boolean;\n  host?: string;\n  port?: string;\n  configuration?: string;\n  https?: boolean;\n}\n\nexport async function runCommand(\n  config: Config,\n  selectedPlatformName: string,\n  options: RunCommandOptions,\n): Promise<void> {\n  options.host = options.host ?? CapLiveReloadHelper.getIpAddress() ?? 'localhost';\n  if (!options.https && !options.port) {\n    options.port = '3000';\n  }\n  if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) {\n    const platformDir = resolvePlatform(config, selectedPlatformName);\n    if (platformDir) {\n      await runPlatformHook(config, selectedPlatformName, platformDir, 'capacitor:run');\n    } else {\n      logger.error(`Platform ${c.input(selectedPlatformName)} not found.`);\n    }\n  } else {\n    const platforms = await selectPlatforms(config, selectedPlatformName);\n    let platformName: string;\n    if (platforms.length === 1) {\n      platformName = platforms[0];\n    } else {\n      platformName = await promptForPlatform(\n        platforms.filter(createRunnablePlatformFilter(config)),\n        `Please choose a platform to run:`,\n      );\n    }\n\n    if (options.list) {\n      const targets = await getPlatformTargets(platformName);\n      const outputTargets = targets.map((t) => ({\n        name: getPlatformTargetName(t),\n        api: `${t.platform === 'ios' ? 'iOS' : 'API'} ${t.sdkVersion}`,\n        id: t.id ?? '?',\n      }));\n\n      if (options.json) {\n        process.stdout.write(`${JSON.stringify(outputTargets)}\\n`);\n      } else {\n        const rows = outputTargets.map((t) => [t.name, t.api, t.id]);\n\n        output.write(\n          `${columnar(rows, {\n            headers: ['Name', 'API', 'Target ID'],\n            vsep: ' ',\n          })}\\n`,\n        );\n      }\n\n      return;\n    }\n\n    try {\n      if (options.sync) {\n        await sync(config, platformName, false, true);\n      }\n      const cordovaPlugins = await getCordovaPlugins(config, platformName);\n      if (options.liveReload) {\n        await CapLiveReloadHelper.editCapConfigForLiveReload(config, platformName, options);\n        if (platformName === config.android.name) {\n          await await writeCordovaAndroidManifest(cordovaPlugins, config, platformName, true);\n        }\n      }\n      await run(config, platformName, options);\n      if (options.liveReload) {\n        new Promise((resolve) => process.on('SIGINT', resolve))\n          .then(async () => {\n            await CapLiveReloadHelper.revertCapConfigForLiveReload();\n            if (platformName === config.android.name) {\n              await writeCordovaAndroidManifest(cordovaPlugins, config, platformName, false);\n            }\n          })\n          .then(() => process.exit());\n        logger.info(\n          `App running with live reload listing for: ${options.https ? 'https' : 'http'}://${options.host}${options.port ? `:${options.port}` : ''}. Press Ctrl+C to quit.`,\n        );\n        await sleepForever();\n      }\n    } catch (e: any) {\n      if (!isFatal(e)) {\n        fatal(e.stack ?? e);\n      }\n\n      throw e;\n    }\n  }\n}\n\nexport async function run(config: Config, platformName: string, options: RunCommandOptions): Promise<void> {\n  if (platformName == config.ios.name) {\n    await runIOS(config, options);\n  } else if (platformName === config.android.name) {\n    await runAndroid(config, options);\n  } else if (platformName === config.web.name) {\n    return;\n  } else {\n    throw `Platform ${platformName} is not valid.`;\n  }\n}\n\nfunction createRunnablePlatformFilter(config: Config): (platform: string) => boolean {\n  return (platform) => platform === config.ios.name || platform === config.android.name;\n}\n\nasync function sleepForever(): Promise<never> {\n  return new Promise<never>(() => {\n    setInterval(() => {\n      /* do nothing */\n    }, 1000);\n  });\n}\n"
  },
  {
    "path": "cli/src/tasks/serve.ts",
    "content": "import c from '../colors';\nimport { fatal } from '../errors';\n\nexport async function serveCommand(): Promise<void> {\n  fatal(\n    `The serve command has been removed.\\n` +\n      `Use a third-party tool for serving single page apps, such as ${c.strong(\n        'serve',\n      )}: ${c.strong('https://www.npmjs.com/package/serve')}`,\n  );\n}\n"
  },
  {
    "path": "cli/src/tasks/sourcemaps.ts",
    "content": "import { readdirSync, existsSync, readFileSync, writeFileSync, unlinkSync, lstatSync } from 'fs-extra';\nimport { join, extname } from 'path';\n\nimport type { Config } from '../definitions';\nimport { logger } from '../log';\n\nfunction walkDirectory(dirPath: string) {\n  const files = readdirSync(dirPath);\n  files.forEach((file) => {\n    const targetFile = join(dirPath, file);\n    if (existsSync(targetFile) && lstatSync(targetFile).isDirectory()) {\n      walkDirectory(targetFile);\n    } else {\n      const mapFile = join(dirPath, `${file}.map`);\n      if (extname(file) === '.js' && existsSync(mapFile)) {\n        const bufMap = readFileSync(mapFile).toString('base64');\n        const bufFile = readFileSync(targetFile, 'utf8');\n        const result = bufFile.replace(\n          `sourceMappingURL=${file}.map`,\n          'sourceMappingURL=data:application/json;charset=utf-8;base64,' + bufMap,\n        );\n        writeFileSync(targetFile, result);\n        unlinkSync(mapFile);\n      }\n    }\n  });\n}\n\nexport async function inlineSourceMaps(config: Config, platformName: string): Promise<void> {\n  let buildDir = '';\n\n  if (platformName == config.ios.name) {\n    buildDir = await config.ios.webDirAbs;\n  }\n\n  if (platformName == config.android.name) {\n    buildDir = await config.android.webDirAbs;\n  }\n\n  if (buildDir) {\n    logger.info('Inlining sourcemaps');\n    walkDirectory(buildDir);\n  }\n}\n"
  },
  {
    "path": "cli/src/tasks/sync.ts",
    "content": "import { check, checkPackage, checkWebDir, selectPlatforms, isValidPlatform, runHooks } from '../common';\nimport type { Config } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { logger } from '../log';\nimport { allSerial } from '../util/promise';\n\nimport { copy, copyCommand } from './copy';\nimport { addUpdateChecks, update, updateCommand } from './update';\n\n/**\n * Sync is a copy and an update in one.\n */\nexport async function syncCommand(\n  config: Config,\n  selectedPlatformName: string,\n  deployment: boolean,\n  inline = false,\n): Promise<void> {\n  if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) {\n    try {\n      await copyCommand(config, selectedPlatformName, inline);\n    } catch (e: any) {\n      logger.error(e.stack ?? e);\n    }\n    await updateCommand(config, selectedPlatformName, deployment);\n  } else {\n    const then = +new Date();\n    const platforms = await selectPlatforms(config, selectedPlatformName);\n    try {\n      await check([() => checkPackage(), () => checkWebDir(config), ...(await addUpdateChecks(config, platforms))]);\n      await allSerial(platforms.map((platformName) => () => sync(config, platformName, deployment, inline)));\n      const now = +new Date();\n      const diff = (now - then) / 1000;\n      logger.info(`Sync finished in ${diff}s`);\n    } catch (e: any) {\n      if (!isFatal(e)) {\n        fatal(e.stack ?? e);\n      }\n\n      throw e;\n    }\n  }\n}\n\nexport async function sync(config: Config, platformName: string, deployment: boolean, inline = false): Promise<void> {\n  await runHooks(config, platformName, config.app.rootDir, 'capacitor:sync:before');\n\n  try {\n    await copy(config, platformName, inline);\n  } catch (e: any) {\n    logger.error(e.stack ?? e);\n  }\n  await update(config, platformName, deployment);\n\n  await runHooks(config, platformName, config.app.rootDir, 'capacitor:sync:after');\n}\n"
  },
  {
    "path": "cli/src/tasks/telemetry.ts",
    "content": "import c from '../colors';\nimport { fatal } from '../errors';\nimport { logger, logSuccess, output } from '../log';\nimport { readConfig, writeConfig } from '../sysconfig';\n\nconst THANK_YOU =\n  `\\nThank you for helping to make Capacitor better! 💖` +\n  `\\nInformation about the data we collect is available on our website: ${c.strong('https://capacitorjs.com/docs/next/cli/telemetry')}\\n`;\n\nexport async function telemetryCommand(onOrOff?: string): Promise<void> {\n  const sysconfig = await readConfig();\n  const enabled = interpretEnabled(onOrOff);\n\n  if (typeof enabled === 'boolean') {\n    if (sysconfig.telemetry === enabled) {\n      logger.info(`Telemetry is already ${c.strong(enabled ? 'on' : 'off')}`);\n    } else {\n      await writeConfig({ ...sysconfig, telemetry: enabled });\n      logSuccess(\n        `You have ${c.strong(`opted ${enabled ? 'in' : 'out'}`)} ${enabled ? 'for' : 'of'} telemetry on this machine.`,\n      );\n\n      if (enabled) {\n        output.write(THANK_YOU);\n      }\n    }\n  } else {\n    logger.info(`Telemetry is ${c.strong(sysconfig.telemetry ? 'on' : 'off')}`);\n  }\n}\n\nfunction interpretEnabled(onOrOff?: string): boolean | undefined {\n  switch (onOrOff) {\n    case 'on':\n      return true;\n    case 'off':\n      return false;\n    case undefined:\n      return undefined;\n  }\n\n  fatal(`Argument must be ${c.strong('on')} or ${c.strong('off')} (or left unspecified)`);\n}\n"
  },
  {
    "path": "cli/src/tasks/update.ts",
    "content": "import { updateAndroid } from '../android/update';\nimport c from '../colors';\nimport {\n  check,\n  checkPackage,\n  resolvePlatform,\n  runHooks,\n  runPlatformHook,\n  runTask,\n  selectPlatforms,\n  isValidPlatform,\n} from '../common';\nimport type { CheckFunction } from '../common';\nimport type { Config } from '../definitions';\nimport { fatal, isFatal } from '../errors';\nimport { checkBundler, checkCocoaPods, getCommonChecks } from '../ios/common';\nimport { updateIOS } from '../ios/update';\nimport { logger } from '../log';\nimport { allSerial } from '../util/promise';\n\nexport async function updateCommand(config: Config, selectedPlatformName: string, deployment: boolean): Promise<void> {\n  if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) {\n    const platformDir = resolvePlatform(config, selectedPlatformName);\n    if (platformDir) {\n      await runPlatformHook(config, selectedPlatformName, platformDir, 'capacitor:update');\n    } else {\n      logger.error(`Platform ${c.input(selectedPlatformName)} not found.`);\n    }\n  } else {\n    const then = +new Date();\n    const platforms = await selectPlatforms(config, selectedPlatformName);\n    try {\n      await check([() => checkPackage(), ...(await addUpdateChecks(config, platforms))]);\n      await allSerial(platforms.map((platformName) => async () => await update(config, platformName, deployment)));\n      const now = +new Date();\n      const diff = (now - then) / 1000;\n      logger.info(`Update finished in ${diff}s`);\n    } catch (e: any) {\n      if (!isFatal(e)) {\n        fatal(e.stack ?? e);\n      }\n\n      throw e;\n    }\n  }\n}\n\nexport async function addUpdateChecks(config: Config, platforms: string[]): Promise<CheckFunction[]> {\n  let checks: CheckFunction[] = [];\n  for (const platformName of platforms) {\n    if (platformName === config.ios.name) {\n      checks = await getCommonChecks(config);\n    } else if (platformName === config.android.name) {\n      continue;\n    } else if (platformName === config.web.name) {\n      continue;\n    } else {\n      throw `Platform ${platformName} is not valid.`;\n    }\n  }\n  return checks;\n}\n\n/**\n * @deprecated use addUpdateChecks\n * @param config\n * @param platforms\n * @returns\n */\nexport function updateChecks(config: Config, platforms: string[]): CheckFunction[] {\n  const checks: CheckFunction[] = [];\n  for (const platformName of platforms) {\n    if (platformName === config.ios.name) {\n      checks.push(() => checkBundler(config) || checkCocoaPods(config));\n    } else if (platformName === config.android.name) {\n      continue;\n    } else if (platformName === config.web.name) {\n      continue;\n    } else {\n      throw `Platform ${platformName} is not valid.`;\n    }\n  }\n  return checks;\n}\n\nexport async function update(config: Config, platformName: string, deployment: boolean): Promise<void> {\n  await runTask(c.success(c.strong(`update ${platformName}`)), async () => {\n    await runHooks(config, platformName, config.app.rootDir, 'capacitor:update:before');\n\n    if (platformName === config.ios.name) {\n      await updateIOS(config, deployment);\n    } else if (platformName === config.android.name) {\n      await updateAndroid(config);\n    }\n\n    await runHooks(config, platformName, config.app.rootDir, 'capacitor:update:after');\n  });\n}\n"
  },
  {
    "path": "cli/src/telemetry.ts",
    "content": "import { Command } from 'commander';\nimport Debug from 'debug';\n\nimport c from './colors';\nimport type { Config } from './definitions';\nimport { send } from './ipc';\nimport { output } from './log';\nimport { readConfig, writeConfig } from './sysconfig';\nimport type { SystemConfig } from './sysconfig';\nimport { getCommandOutput } from './util/subprocess';\nimport { isInteractive } from './util/term';\n\nconst debug = Debug('capacitor:telemetry');\n\nconst THANK_YOU =\n  `\\nThank you for helping improve Capacitor by sharing anonymous usage data! 💖` +\n  `\\nInformation about the data we collect is available on our website: ${c.strong('https://capacitorjs.com/docs/next/cli/telemetry')}` +\n  `\\nYou can disable telemetry at any time by using the ${c.input('npx cap telemetry off')} command.`;\n\nexport interface CommandMetricData {\n  app_id: string;\n  command: string;\n  arguments: string;\n  options: string;\n  duration: number;\n  error: string | null;\n  node_version: string;\n  os: string;\n}\n\nexport interface Metric<N extends string, D> {\n  name: N;\n  timestamp: string;\n  session_id: string;\n  source: 'capacitor_cli';\n  value: D;\n}\n\ntype CommanderAction = (...args: any[]) => void | Promise<void>;\n\nexport function telemetryAction(config: Config, action: CommanderAction): CommanderAction {\n  return async (...actionArgs: any[]): Promise<void> => {\n    const start = new Date();\n    // This is how commanderjs works--the command object is either the last\n    // element or second to last if there are additional options (via `.allowUnknownOption()`)\n    const lastArg = actionArgs[actionArgs.length - 1];\n    const cmd: Command = lastArg instanceof Command ? lastArg : actionArgs[actionArgs.length - 2];\n    const command = getFullCommandName(cmd);\n    let error: any;\n\n    try {\n      await action(...actionArgs);\n    } catch (e) {\n      error = e;\n    }\n\n    const end = new Date();\n    const duration = end.getTime() - start.getTime();\n\n    const packages = Object.entries({\n      ...config.app.package.devDependencies,\n      ...config.app.package.dependencies,\n    });\n\n    // Only collect packages in the capacitor org:\n    // https://www.npmjs.com/org/capacitor\n    const capacitorPackages = packages.filter(([k]) => k.startsWith('@capacitor/'));\n\n    const versions = capacitorPackages.map(([k, v]) => [\n      `${k.replace(/^@capacitor\\//, '').replace(/-/g, '_')}_version`,\n      v,\n    ]);\n\n    const data: CommandMetricData = {\n      app_id: await getAppIdentifier(config),\n      command,\n      arguments: cmd.args.join(' '),\n      options: JSON.stringify(cmd.opts()),\n      duration,\n      error: error ? (error.message ? error.message : String(error)) : null,\n      node_version: process.version,\n      os: config.cli.os,\n      ...Object.fromEntries(versions),\n    };\n\n    if (isInteractive()) {\n      let sysconfig = await readConfig();\n\n      if (!error && typeof sysconfig.telemetry === 'undefined') {\n        // Telemetry is opt-out; turn telemetry on then inform the user how to opt-out.\n        sysconfig = { ...sysconfig, telemetry: true };\n        await writeConfig(sysconfig);\n\n        output.write(THANK_YOU);\n      }\n\n      await sendMetric(sysconfig, 'capacitor_cli_command', data);\n    }\n\n    if (error) {\n      throw error;\n    }\n  };\n}\n\n/**\n * If telemetry is enabled, send a metric via IPC to a forked process for uploading.\n */\nexport async function sendMetric<D>(\n  sysconfig: Pick<SystemConfig, 'machine' | 'telemetry'>,\n  name: string,\n  data: D,\n): Promise<void> {\n  if (sysconfig.telemetry && isInteractive()) {\n    const message: Metric<string, D> = {\n      name,\n      timestamp: new Date().toISOString(),\n      session_id: sysconfig.machine,\n      source: 'capacitor_cli',\n      value: data,\n    };\n\n    await send({ type: 'telemetry', data: message });\n  } else {\n    debug('Telemetry is off (user choice, non-interactive terminal, or CI)--not sending metric');\n  }\n}\n\n/**\n * Get a unique anonymous identifier for this app.\n */\nasync function getAppIdentifier(config: Config): Promise<string | null> {\n  const { createHash } = await import('crypto');\n\n  // get the first commit hash, which should be universally unique\n  const output = await getCommandOutput('git', ['rev-list', '--max-parents=0', 'HEAD'], { cwd: config.app.rootDir });\n\n  const firstLine = output?.split('\\n')[0];\n\n  if (!firstLine) {\n    debug('Could not obtain unique app identifier');\n    return null;\n  }\n\n  // use sha1 to create a one-way hash to anonymize\n  const id = createHash('sha1').update(firstLine).digest('hex');\n\n  return id;\n}\n\n/**\n * Walk through the command's parent tree and construct a space-separated name.\n *\n * Probably overkill because we don't have nested commands, but whatever.\n */\nfunction getFullCommandName(cmd: Command): string {\n  const names: string[] = [];\n\n  while (cmd.parent !== null) {\n    names.push(cmd.name());\n    cmd = cmd.parent;\n  }\n\n  return names.reverse().join(' ');\n}\n"
  },
  {
    "path": "cli/src/util/cli.ts",
    "content": "import envPaths from 'env-paths';\n\nimport { isFatal } from '../errors';\nimport { logger } from '../log';\n\nexport const ENV_PATHS = envPaths('capacitor', { suffix: '' });\n\nexport type CommanderAction = (...args: any[]) => void | Promise<void>;\n\nexport function wrapAction(action: CommanderAction): CommanderAction {\n  return async (...args: any[]) => {\n    try {\n      await action(...args);\n    } catch (e) {\n      if (isFatal(e)) {\n        process.exitCode = e.exitCode;\n        logger.error(e.message);\n      } else {\n        throw e;\n      }\n    }\n  };\n}\n"
  },
  {
    "path": "cli/src/util/emoji.ts",
    "content": "// Emoji falback, right now just uses fallback on windows,\n// but could expand to be more sophisticated to allow emoji\n// on Hyper term on windows, for example.\nexport const emoji = (x: string, fallback: string): string => {\n  if (process.platform === 'win32') {\n    return fallback;\n  }\n  return x;\n};\n"
  },
  {
    "path": "cli/src/util/fn.ts",
    "content": "export const tryFn = async <T extends (...args: any[]) => Promise<R>, R>(fn: T, ...args: any[]): Promise<R | null> => {\n  try {\n    return await fn(...args);\n  } catch {\n    // ignore\n  }\n\n  return null;\n};\n"
  },
  {
    "path": "cli/src/util/fs.ts",
    "content": "import { statSync, type Stats } from 'fs';\nimport { readdir } from 'fs/promises';\nimport { existsSync, lstatSync, readdirSync, rmdirSync, unlinkSync } from 'fs-extra';\nimport { join } from 'path';\n\nexport const convertToUnixPath = (path: string): string => {\n  return path.replace(/\\\\/g, '/');\n};\n\nexport const deleteFolderRecursive = (directoryPath: any): void => {\n  if (existsSync(directoryPath)) {\n    readdirSync(directoryPath).forEach((file) => {\n      const curPath = join(directoryPath, file);\n      if (lstatSync(curPath).isDirectory()) {\n        deleteFolderRecursive(curPath);\n      } else {\n        unlinkSync(curPath);\n      }\n    });\n    rmdirSync(directoryPath);\n  }\n};\n\nexport interface ReaddirPOptions {\n  /**\n   * Filter out items from the walk process from the final result.\n   *\n   * @return `true` to keep, otherwise the item is filtered out\n   */\n  readonly filter: (item: WalkerItem) => boolean;\n}\n\nexport interface WalkerItem {\n  path: string;\n  stats: Stats;\n}\n\nexport async function readdirp(dir: string, { filter }: ReaddirPOptions): Promise<string[]> {\n  const dirContent = await readdir(dir, { recursive: true });\n  const dirContentWalker: WalkerItem[] = [];\n  const filteredContent: string[] = [];\n  dirContent.forEach((element) => {\n    const path = join(dir, element);\n    const stats = statSync(path);\n    dirContentWalker.push({ path, stats });\n  });\n  dirContentWalker.forEach((element) => {\n    if (filter(element)) {\n      filteredContent.push(element.path);\n    }\n  });\n  return filteredContent;\n}\n"
  },
  {
    "path": "cli/src/util/iosplugin.ts",
    "content": "import { readFileSync, readJSONSync, writeJSONSync } from 'fs-extra';\nimport { resolve } from 'path';\n\nimport { getCordovaPlugins } from '../cordova';\nimport type { Config } from '../definitions';\nimport type { Plugin } from '../plugin';\nimport { getPluginType, PluginType } from '../plugin';\n\nimport type { ReaddirPOptions } from './fs';\nimport { readdirp } from './fs';\n\nexport async function getPluginFiles(plugins: Plugin[]): Promise<string[]> {\n  let filenameList: string[] = [];\n\n  const options: ReaddirPOptions = {\n    filter: (item) => {\n      if (item.stats.isFile() && (item.path.endsWith('.swift') || item.path.endsWith('.m'))) {\n        return true;\n      } else {\n        return false;\n      }\n    },\n  };\n\n  for (const plugin of plugins) {\n    if (plugin.ios && getPluginType(plugin, 'ios') === PluginType.Core) {\n      const pluginPath = resolve(plugin.rootPath, plugin.ios?.path);\n      const filenames = await readdirp(pluginPath, options);\n      filenameList = filenameList.concat(filenames);\n    }\n  }\n\n  return filenameList;\n}\n\nexport async function findPluginClasses(files: string[]): Promise<string[]> {\n  const classList: string[] = [];\n\n  for (const file of files) {\n    const fileData = readFileSync(file, 'utf-8');\n    const swiftPluginRegex = RegExp(/@objc\\(([A-Za-z0-9_-]+)\\)/);\n    const objcPluginRegex = RegExp(/CAP_PLUGIN\\(([A-Za-z0-9_-]+)/);\n\n    const swiftMatches = swiftPluginRegex.exec(fileData);\n    if (swiftMatches?.[1] && !classList.includes(swiftMatches[1])) {\n      classList.push(swiftMatches[1]);\n    }\n\n    const objcMatches = objcPluginRegex.exec(fileData);\n    if (objcMatches?.[1] && !classList.includes(objcMatches[1])) {\n      classList.push(objcMatches[1]);\n    }\n  }\n\n  return classList;\n}\n\nexport async function writePluginJSON(config: Config, classList: string[]): Promise<void> {\n  const capJSONFile = resolve(config.ios.nativeTargetDirAbs, 'capacitor.config.json');\n  const capJSON = readJSONSync(capJSONFile);\n  capJSON['packageClassList'] = classList;\n  writeJSONSync(capJSONFile, capJSON, { spaces: '\\t' });\n}\n\nexport async function generateIOSPackageJSON(config: Config, plugins: Plugin[]): Promise<void> {\n  const fileList = await getPluginFiles(plugins);\n  const classList = await findPluginClasses(fileList);\n  const cordovaPlugins = await getCordovaPlugins(config, 'ios');\n  if (cordovaPlugins.length > 0) {\n    classList.push('CDVPlugin');\n  }\n  writePluginJSON(config, classList);\n}\n"
  },
  {
    "path": "cli/src/util/js.ts",
    "content": "import util from 'util';\n\nexport function formatJSObject(o: { [key: string]: any }): string {\n  try {\n    o = JSON.parse(JSON.stringify(o));\n  } catch (e: any) {\n    throw new Error(`Cannot parse object as JSON: ${e.stack ? e.stack : e}`);\n  }\n\n  return util.inspect(o, {\n    compact: false,\n    breakLength: Infinity,\n    depth: Infinity,\n    maxArrayLength: Infinity,\n    maxStringLength: Infinity,\n  });\n}\n"
  },
  {
    "path": "cli/src/util/livereload.ts",
    "content": "import { readJSONSync, writeJSONSync } from 'fs-extra';\nimport { networkInterfaces } from 'os';\nimport { join } from 'path';\n\nimport type { Config } from '../definitions';\nimport type { RunCommandOptions } from '../tasks/run';\n\nclass CapLiveReload {\n  configJsonToRevertTo: {\n    json: string | null;\n    platformPath: string | null;\n  } = {\n    json: null,\n    platformPath: null,\n  };\n\n  constructor() {\n    // nothing to do\n  }\n\n  getIpAddress(name?: string, family?: any) {\n    const interfaces: any = networkInterfaces() ?? {};\n\n    const _normalizeFamily = (family?: any) => {\n      if (family === 4) {\n        return 'ipv4';\n      }\n      if (family === 6) {\n        return 'ipv6';\n      }\n      return family ? family.toLowerCase() : 'ipv4';\n    };\n    const isLoopback = (addr: string) => {\n      return (\n        /^(::f{4}:)?127\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})/.test(addr) ||\n        /^fe80::1$/.test(addr) ||\n        /^::1$/.test(addr) ||\n        /^::$/.test(addr)\n      );\n    };\n    const isPrivate = (addr: string) => {\n      return (\n        /^(::f{4}:)?10\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$/i.test(addr) ||\n        /^(::f{4}:)?192\\.168\\.([0-9]{1,3})\\.([0-9]{1,3})$/i.test(addr) ||\n        /^(::f{4}:)?172\\.(1[6-9]|2\\d|30|31)\\.([0-9]{1,3})\\.([0-9]{1,3})$/i.test(addr) ||\n        /^(::f{4}:)?127\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$/i.test(addr) ||\n        /^(::f{4}:)?169\\.254\\.([0-9]{1,3})\\.([0-9]{1,3})$/i.test(addr) ||\n        /^f[cd][0-9a-f]{2}:/i.test(addr) ||\n        /^fe80:/i.test(addr) ||\n        /^::1$/.test(addr) ||\n        /^::$/.test(addr)\n      );\n    };\n    const isPublic = (addr: string) => {\n      return !isPrivate(addr);\n    };\n    const loopback = (family?: any) => {\n      //\n      // Default to `ipv4`\n      //\n      family = _normalizeFamily(family);\n\n      if (family !== 'ipv4' && family !== 'ipv6') {\n        throw new Error('family must be ipv4 or ipv6');\n      }\n\n      return family === 'ipv4' ? '127.0.0.1' : 'fe80::1';\n    };\n\n    //\n    // Default to `ipv4`\n    //\n    family = _normalizeFamily(family);\n\n    //\n    // If a specific network interface has been named,\n    // return the address.\n    //\n    if (name && name !== 'private' && name !== 'public') {\n      const res = interfaces[name].filter((details: any) => {\n        const itemFamily = _normalizeFamily(details.family);\n        return itemFamily === family;\n      });\n      if (res.length === 0) {\n        return undefined;\n      }\n      return res[0].address;\n    }\n\n    const all = Object.keys(interfaces)\n      .map((nic) => {\n        //\n        // Note: name will only be `public` or `private`\n        // when this is called.\n        //\n        const addresses = interfaces[nic].filter((details: any) => {\n          details.family = _normalizeFamily(details.family);\n          if (details.family !== family || isLoopback(details.address)) {\n            return false;\n          }\n          if (!name) {\n            return true;\n          }\n\n          return name === 'public' ? isPrivate(details.address) : isPublic(details.address);\n        });\n\n        return addresses.length ? addresses[0].address : undefined;\n      })\n      .filter(Boolean);\n\n    return !all.length ? loopback(family) : all[0];\n  }\n\n  // TODO remove on next major as it's unused\n  async editExtConfigForLiveReload(\n    config: Config,\n    platformName: string,\n    options: RunCommandOptions,\n    rootConfigChange = false,\n  ): Promise<any> {\n    const platformAbsPath =\n      platformName == config.ios.name\n        ? config.ios.nativeTargetDirAbs\n        : platformName == config.android.name\n          ? config.android.assetsDirAbs\n          : null;\n    if (platformAbsPath == null) throw new Error('Platform not found.');\n    const capConfigPath = rootConfigChange\n      ? config.app.extConfigFilePath\n      : join(platformAbsPath, 'capacitor.config.json');\n\n    const configJson = { ...config.app.extConfig };\n    this.configJsonToRevertTo.json = JSON.stringify(configJson, null, 2);\n    this.configJsonToRevertTo.platformPath = capConfigPath;\n    const url = `http://${options.host}:${options.port}`;\n    configJson.server = {\n      url,\n    };\n    return configJson;\n  }\n\n  // TODO remove rootConfigChange param on next major as it's unused\n  async editCapConfigForLiveReload(\n    config: Config,\n    platformName: string,\n    options: RunCommandOptions,\n    rootConfigChange = false,\n  ): Promise<void> {\n    const platformAbsPath =\n      platformName == config.ios.name\n        ? config.ios.nativeTargetDirAbs\n        : platformName == config.android.name\n          ? config.android.assetsDirAbs\n          : null;\n    if (platformAbsPath == null) throw new Error('Platform not found.');\n    const capConfigPath = rootConfigChange\n      ? config.app.extConfigFilePath\n      : join(platformAbsPath, 'capacitor.config.json');\n\n    const configJson = readJSONSync(capConfigPath);\n    this.configJsonToRevertTo.json = JSON.stringify(configJson, null, 2);\n    this.configJsonToRevertTo.platformPath = capConfigPath;\n    const url = `${options.https ? 'https' : 'http'}://${options.host}${options.port ? `:${options.port}` : ''}`;\n    configJson.server = {\n      ...configJson.server,\n      url,\n    };\n    writeJSONSync(capConfigPath, configJson, { spaces: '\\t' });\n  }\n\n  async revertCapConfigForLiveReload(): Promise<void> {\n    if (this.configJsonToRevertTo.json == null || this.configJsonToRevertTo.platformPath == null) return;\n    const capConfigPath = this.configJsonToRevertTo.platformPath;\n    const configJson = this.configJsonToRevertTo.json;\n    writeJSONSync(capConfigPath, JSON.parse(configJson), { spaces: '\\t' });\n    this.configJsonToRevertTo.json = null;\n    this.configJsonToRevertTo.platformPath = null;\n  }\n}\n\nexport const CapLiveReloadHelper = new CapLiveReload();\n"
  },
  {
    "path": "cli/src/util/monorepotools.ts",
    "content": "import { existsSync, readFileSync } from 'node:fs';\nimport { join, dirname, relative } from 'node:path';\n\n/**\n * Finds the monorepo root from the given path.\n * @param currentPath - The current path to start searching from.\n * @returns The path to the monorepo root.\n * @throws An error if the monorepo root is not found.\n */\nexport function findMonorepoRoot(currentPath: string): string {\n  const packageJsonPath = join(currentPath, 'package.json');\n  const pnpmWorkspacePath = join(currentPath, 'pnpm-workspace.yaml');\n  if (\n    existsSync(pnpmWorkspacePath) ||\n    (existsSync(packageJsonPath) && JSON.parse(readFileSync(packageJsonPath, 'utf-8')).workspaces)\n  ) {\n    return currentPath;\n  }\n  const parentPath = dirname(currentPath);\n  if (parentPath === currentPath) {\n    throw new Error('Monorepo root not found');\n  }\n  return findMonorepoRoot(parentPath);\n}\n\n/**\n * Finds the NX monorepo root from the given path.\n * @param currentPath - The current path to start searching from.\n * @returns The path to the monorepo root.\n * @throws An error if the monorepo root is not found.\n */\nexport function findNXMonorepoRoot(currentPath: string): string {\n  const nxJsonPath = join(currentPath, 'nx.json');\n  if (existsSync(nxJsonPath)) {\n    return currentPath;\n  }\n  const parentPath = dirname(currentPath);\n  if (parentPath === currentPath) {\n    throw new Error('Monorepo root not found');\n  }\n  return findNXMonorepoRoot(parentPath);\n}\n\n/**\n * Finds the path to a package within the node_modules folder,\n * searching up the directory hierarchy until the last possible directory is reached.\n * @param packageName - The name of the package to find.\n * @param currentPath - The current path to start searching from.\n * @param lastPossibleDirectory - The last possible directory to search for the package.\n * @returns The path to the package, or null if not found.\n */\nexport function findPackagePath(\n  packageName: string,\n  currentPath: string,\n  lastPossibleDirectory: string,\n): string | null {\n  const nodeModulesPath = join(currentPath, 'node_modules', packageName);\n  if (existsSync(nodeModulesPath)) {\n    return nodeModulesPath;\n  }\n  if (currentPath === lastPossibleDirectory) {\n    return null;\n  }\n  const parentPath = dirname(currentPath);\n  return findPackagePath(packageName, parentPath, lastPossibleDirectory);\n}\n\n/**\n * Finds the relative path to a package from the current directory,\n * using the monorepo root as the last possible directory.\n * @param packageName - The name of the package to find.\n * @param currentPath - The current path to start searching from.\n * @returns The relative path to the package, or null if not found.\n */\nexport function findPackageRelativePathInMonorepo(packageName: string, currentPath: string): string | null {\n  const monorepoRoot = findMonorepoRoot(currentPath);\n  const packagePath = findPackagePath(packageName, currentPath, monorepoRoot);\n  if (packagePath) {\n    return relative(currentPath, packagePath);\n  }\n  return null;\n}\n\n/**\n * Detects if the current directory is part of a monorepo (npm, yarn, pnpm).\n * @param currentPath - The current path to start searching from.\n * @returns True if the current directory is part of a monorepo, false otherwise.\n */\nexport function isMonorepo(currentPath: string): boolean {\n  try {\n    findMonorepoRoot(currentPath);\n    return true;\n  } catch (error) {\n    return false;\n  }\n}\n\n/**\n * Detects if the current directory is part of a nx integrated monorepo.\n * @param currentPath - The current path to start searching from.\n * @returns True if the current directory is part of a monorepo, false otherwise.\n */\nexport function isNXMonorepo(currentPath: string): boolean {\n  try {\n    findNXMonorepoRoot(currentPath);\n    return true;\n  } catch (error) {\n    return false;\n  }\n}\n"
  },
  {
    "path": "cli/src/util/native-run.ts",
    "content": "import { dirname } from 'path';\n\nimport c from '../colors';\nimport type { PlatformTarget } from '../common';\nimport { fatal } from '../errors';\nimport { logger } from '../log';\n\nimport { resolveNode } from './node';\nimport { runCommand } from './subprocess';\nimport type { RunCommandOptions } from './subprocess';\n\nexport async function runNativeRun(args: readonly string[], options: RunCommandOptions = {}): Promise<string> {\n  const p = resolveNode(__dirname, dirname('native-run/package'), 'bin/native-run');\n\n  if (!p) {\n    fatal(`${c.input('native-run')} not found.`);\n  }\n\n  if (process.versions.pnp) {\n    return await runCommand('yarn', ['node', p, ...args], options);\n  } else {\n    return await runCommand(p, args, options);\n  }\n}\n\nexport async function getPlatformTargets(platformName: string): Promise<PlatformTarget[]> {\n  const errors = [];\n  try {\n    const output = await runNativeRun([platformName, '--list', '--json']);\n    const parsedOutput = JSON.parse(output);\n    if (parsedOutput.devices.length || parsedOutput.virtualDevices.length) {\n      return [\n        ...parsedOutput.devices.map((t: any) => ({ ...t, virtual: false })),\n        ...parsedOutput.virtualDevices.map((t: any) => ({\n          ...t,\n          virtual: true,\n        })),\n      ];\n    } else {\n      parsedOutput.errors.map((e: any) => {\n        errors.push(e);\n      });\n    }\n  } catch (e: any) {\n    const err = JSON.parse(e);\n    errors.push(err);\n  }\n\n  if (errors.length === 0) {\n    logger.info('No devices found.');\n    return [];\n  }\n\n  const plural = errors.length > 1 ? 's' : '';\n  const errMsg = `${c.strong('native-run')} failed with error${plural}\\n\n  ${errors\n    .map((e: any) => {\n      return `\\t${c.strong(e.code)}: ${e.error}`;\n    })\n    .join('\\n')}\n  \\n\\tMore details for this error${plural} may be available online: ${c.strong(\n    'https://github.com/ionic-team/native-run/wiki/Android-Errors',\n  )}\n  `;\n  throw errMsg;\n}\n"
  },
  {
    "path": "cli/src/util/node.ts",
    "content": "import { existsSync } from 'fs';\nimport { readFileSync } from 'fs-extra';\nimport { resolve } from 'path';\nimport type typescript from 'typescript';\n\ninterface NodeModuleWithCompile extends NodeJS.Module {\n  _compile?(code: string, filename: string): any;\n}\n\n/**\n * @see https://github.com/ionic-team/stencil/blob/HEAD/src/compiler/sys/node-require.ts\n */\nexport const requireTS = (ts: typeof typescript, p: string): unknown => {\n  const id = resolve(p);\n\n  delete require.cache[id];\n\n  require.extensions['.ts'] = (module: NodeModuleWithCompile, fileName: string) => {\n    let sourceText = readFileSync(fileName, 'utf8');\n\n    if (fileName.endsWith('.ts')) {\n      const tsResults = ts.transpileModule(sourceText, {\n        fileName,\n        compilerOptions: {\n          module: ts.ModuleKind.CommonJS,\n          moduleResolution: ts.ModuleResolutionKind.NodeJs,\n          esModuleInterop: true,\n          strict: true,\n          target: ts.ScriptTarget.ES2017,\n        },\n        reportDiagnostics: true,\n      });\n      sourceText = tsResults.outputText;\n    } else {\n      // quick hack to turn a modern es module\n      // into and old school commonjs module\n      sourceText = sourceText.replace(/export\\s+\\w+\\s+(\\w+)/gm, 'exports.$1');\n    }\n\n    module._compile?.(sourceText, fileName);\n  };\n\n  const m = require(id); // eslint-disable-line @typescript-eslint/no-var-requires\n\n  delete require.extensions['.ts'];\n\n  return m;\n};\n\nexport function resolveNode(root: string, ...pathSegments: string[]): string | null {\n  try {\n    return require.resolve(pathSegments.join('/'), { paths: [root] });\n  } catch (e) {\n    const path = [root, 'node_modules', ...pathSegments].join('/');\n    if (existsSync(path)) {\n      return path;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "cli/src/util/promise.ts",
    "content": "export function allSerial<T>(funcs: (() => Promise<T>)[]): Promise<T[]> {\n  return funcs.reduce(\n    (promise, func) => promise.then((result) => func().then((x) => result.concat(x))),\n    Promise.resolve<T[]>([]),\n  );\n}\n\nexport type PromiseExecutor<T> = (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void;\n\nexport type PromiseOnFulfilled<T, TResult> = ((value: T) => TResult | PromiseLike<TResult>) | undefined | null;\n\nexport type PromiseOnRejected<TResult> = ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null;\n\nexport class LazyPromise<T> extends Promise<T> {\n  private _executor: PromiseExecutor<T>;\n  private _promise?: Promise<T>;\n\n  constructor(executor: PromiseExecutor<T>) {\n    super(() => {\n      /* ignore */\n    });\n\n    this._executor = executor;\n  }\n\n  then<TResult1 = T, TResult2 = never>(\n    onfulfilled?: PromiseOnFulfilled<T, TResult1>,\n    onrejected?: PromiseOnRejected<TResult2>,\n  ): Promise<TResult1 | TResult2> {\n    this._promise = this._promise || new Promise(this._executor);\n    return this._promise.then(onfulfilled, onrejected);\n  }\n\n  catch<TResult = never>(onrejected?: PromiseOnRejected<TResult>): Promise<T | TResult> {\n    this._promise = this._promise || new Promise(this._executor);\n    return this._promise.catch(onrejected);\n  }\n}\n\nexport function lazy<T>(fn: () => T | Promise<T>): LazyPromise<T> {\n  return new LazyPromise<T>(async (resolve, reject) => {\n    try {\n      resolve(await fn());\n    } catch (e) {\n      reject(e);\n    }\n  });\n}\n"
  },
  {
    "path": "cli/src/util/spm.ts",
    "content": "import { pathExists, existsSync, readFileSync, writeFileSync, remove, move, mkdtemp } from 'fs-extra';\nimport { tmpdir } from 'os';\nimport { join, relative, resolve } from 'path';\nimport type { PlistObject } from 'plist';\nimport { build, parse } from 'plist';\nimport { extract } from 'tar';\n\nimport { getCapacitorPackageVersion } from '../common';\nimport type { Config } from '../definitions';\nimport { fatal } from '../errors';\nimport { getMajoriOSVersion } from '../ios/common';\nimport { logger } from '../log';\nimport type { Plugin } from '../plugin';\nimport { getPluginType, PluginType } from '../plugin';\nimport { runCommand } from '../util/subprocess';\n\nexport interface SwiftPlugin {\n  name: string;\n  path: string;\n}\n\n/**\n * @deprecated use config.ios.packageManager\n * @param config\n * @returns 'Cocoapods' | 'SPM'\n */\nexport async function checkPackageManager(config: Config): Promise<'Cocoapods' | 'SPM'> {\n  const iosDirectory = config.ios.nativeProjectDirAbs;\n  if (existsSync(resolve(iosDirectory, 'CapApp-SPM'))) {\n    return 'SPM';\n  }\n\n  return 'Cocoapods';\n}\n\nexport async function findPackageSwiftFile(config: Config): Promise<string> {\n  const packageDirectory = resolve(config.ios.nativeProjectDirAbs, 'CapApp-SPM');\n  return resolve(packageDirectory, 'Package.swift');\n}\n\nexport async function generatePackageFile(config: Config, plugins: Plugin[]): Promise<void> {\n  const packageSwiftFile = await findPackageSwiftFile(config);\n  try {\n    logger.info('Writing Package.swift');\n\n    const textToWrite = await generatePackageText(config, plugins);\n    writeFileSync(packageSwiftFile, textToWrite);\n  } catch (err) {\n    logger.error(`Unable to write to ${packageSwiftFile}. Verify it is not already open. \\n Error: ${err}`);\n  }\n}\n\nexport async function checkPluginsForPackageSwift(config: Config, plugins: Plugin[]): Promise<Plugin[]> {\n  const iOSCapacitorPlugins = plugins.filter((p) => getPluginType(p, 'ios') === PluginType.Core);\n\n  const packageSwiftPluginList = await pluginsWithPackageSwift(iOSCapacitorPlugins);\n\n  if (plugins.length == packageSwiftPluginList.length) {\n    logger.debug(`Found ${plugins.length} iOS plugins, ${packageSwiftPluginList.length} have a Package.swift file`);\n    logger.info('All plugins have a Package.swift file and will be included in Package.swift');\n  } else {\n    logger.warn('Some installed packages are not compatable with SPM');\n  }\n\n  return packageSwiftPluginList;\n}\n\nexport async function extractSPMPackageDirectory(config: Config): Promise<void> {\n  const spmDirectory = join(config.ios.nativeProjectDirAbs, 'CapApp-SPM');\n  const spmTemplate = join(config.cli.assetsDirAbs, 'ios-spm-template.tar.gz');\n  const debugConfig = join(config.ios.platformDirAbs, 'debug.xcconfig');\n\n  logger.info('Extracting ' + spmTemplate + ' to ' + spmDirectory);\n\n  try {\n    const tempCapDir = await mkdtemp(join(tmpdir(), 'cap-'));\n    const tempCapSPM = join(tempCapDir, 'App', 'CapApp-SPM');\n    const tempDebugXCConfig = join(tempCapDir, 'debug.xcconfig');\n    await extract({ file: spmTemplate, cwd: tempCapDir });\n    await move(tempCapSPM, spmDirectory);\n    await move(tempDebugXCConfig, debugConfig);\n  } catch (err) {\n    fatal('Failed to create ' + spmDirectory + ' with error: ' + err);\n  }\n}\n\nexport async function removeCocoapodsFiles(config: Config): Promise<void> {\n  const iosDirectory = config.ios.nativeProjectDirAbs;\n  const podFile = resolve(iosDirectory, 'Podfile');\n  const podlockFile = resolve(iosDirectory, 'Podfile.lock');\n  const xcworkspaceFile = resolve(iosDirectory, 'App.xcworkspace');\n\n  await remove(podFile);\n  await remove(podlockFile);\n  await remove(xcworkspaceFile);\n}\n\nexport async function generatePackageText(config: Config, plugins: Plugin[]): Promise<string> {\n  const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name);\n  const iosVersion = getMajoriOSVersion(config);\n\n  let packageSwiftText = `// swift-tools-version: 5.9\nimport PackageDescription\n\n// DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands\nlet package = Package(\n    name: \"CapApp-SPM\",\n    platforms: [.iOS(.v${iosVersion})],\n    products: [\n        .library(\n            name: \"CapApp-SPM\",\n            targets: [\"CapApp-SPM\"])\n    ],\n    dependencies: [\n        .package(url: \"https://github.com/ionic-team/capacitor-swift-pm.git\", exact: \"${iosPlatformVersion}\")`;\n\n  for (const plugin of plugins) {\n    if (getPluginType(plugin, config.ios.name) === PluginType.Cordova) {\n      packageSwiftText += `,\\n        .package(name: \"${plugin.name}\", path: \"../../capacitor-cordova-ios-plugins/sources/${plugin.name}\")`;\n    } else {\n      const relPath = relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath);\n      packageSwiftText += `,\\n        .package(name: \"${plugin.ios?.name}\", path: \"${relPath}\")`;\n    }\n  }\n\n  packageSwiftText += `\n    ],\n    targets: [\n        .target(\n            name: \"CapApp-SPM\",\n            dependencies: [\n                .product(name: \"Capacitor\", package: \"capacitor-swift-pm\"),\n                .product(name: \"Cordova\", package: \"capacitor-swift-pm\")`;\n\n  for (const plugin of plugins) {\n    packageSwiftText += `,\\n                .product(name: \"${plugin.ios?.name}\", package: \"${plugin.ios?.name}\")`;\n  }\n\n  packageSwiftText += `\n            ]\n        )\n    ]\n)\n`;\n\n  return packageSwiftText;\n}\n\nexport async function runCocoapodsDeintegrate(config: Config): Promise<void> {\n  const podPath = await config.ios.podPath;\n  const projectFileName = config.ios.nativeXcodeProjDirAbs;\n  const useBundler = (await config.ios.packageManager) === 'bundler';\n\n  logger.info('Running pod deintegrate on project ' + projectFileName);\n\n  if (useBundler) {\n    logger.info('Found bundler, using it to run CocoaPods.');\n    await runCommand('bundle', ['exec', 'pod', 'deintegrate', projectFileName], {\n      cwd: config.ios.nativeProjectDirAbs,\n    });\n  } else {\n    await runCommand(podPath, ['deintegrate', projectFileName], {\n      cwd: config.ios.nativeProjectDirAbs,\n    });\n  }\n}\n\nexport async function addInfoPlistDebugIfNeeded(config: Config): Promise<void> {\n  type Mutable<T> = { -readonly [P in keyof T]: T[P] };\n\n  const infoPlist = resolve(config.ios.nativeTargetDirAbs, 'Info.plist');\n  logger.info('Checking ' + infoPlist + ' for CAPACITOR_DEBUG');\n\n  if (existsSync(infoPlist)) {\n    const infoPlistContents = readFileSync(infoPlist, 'utf-8');\n    const plistEntries = parse(infoPlistContents) as Mutable<PlistObject>;\n\n    if (plistEntries['CAPACITOR_DEBUG'] === undefined) {\n      logger.info('Writing CAPACITOR_DEBUG to ' + infoPlist);\n      plistEntries['CAPACITOR_DEBUG'] = '$(CAPACITOR_DEBUG)';\n      const plistToWrite = build(plistEntries);\n      writeFileSync(infoPlist, plistToWrite);\n    } else {\n      logger.warn('Found CAPACITOR_DEBUG set to ' + plistEntries['CAPACITOR_DEBUG'] + ', skipping.');\n    }\n  } else {\n    logger.warn(infoPlist + ' not found.');\n  }\n}\n\n// Private Functions\n\nasync function pluginsWithPackageSwift(plugins: Plugin[]): Promise<Plugin[]> {\n  const pluginList: Plugin[] = [];\n  for (const plugin of plugins) {\n    const packageSwiftFound = await pathExists(join(plugin.rootPath, 'Package.swift'));\n    if (packageSwiftFound) {\n      pluginList.push(plugin);\n    } else {\n      logger.warn(plugin.id + ' does not have a Package.swift');\n    }\n  }\n\n  return pluginList;\n}\n"
  },
  {
    "path": "cli/src/util/subprocess.ts",
    "content": "import { Subprocess, SubprocessError, which } from '@ionic/utils-subprocess';\n\nexport interface RunCommandOptions {\n  cwd?: string;\n}\n\nexport async function runCommand(\n  command: string,\n  args: readonly string[],\n  options: RunCommandOptions = {},\n): Promise<string> {\n  const p = new Subprocess(command, args, options);\n\n  try {\n    return await p.output();\n  } catch (e) {\n    if (e instanceof SubprocessError) {\n      // old behavior of just throwing the stdout/stderr strings\n      throw e.output ? e.output : e.cause ? `${e.message} ${e.cause.toString()}` : e.code ? e.code : 'Unknown error';\n    }\n\n    throw e;\n  }\n}\n\nexport async function getCommandOutput(\n  command: string,\n  args: readonly string[],\n  options: RunCommandOptions = {},\n): Promise<string | null> {\n  try {\n    return (await runCommand(command, args, options)).trim();\n  } catch (e) {\n    return null;\n  }\n}\n\nexport async function isInstalled(command: string): Promise<boolean> {\n  try {\n    await which(command);\n  } catch (e) {\n    return false;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "cli/src/util/template.ts",
    "content": "import { mkdirp } from 'fs-extra';\nimport { extract } from 'tar';\n\nexport async function extractTemplate(src: string, dir: string): Promise<void> {\n  await mkdirp(dir);\n  await extract({ file: src, cwd: dir });\n}\n"
  },
  {
    "path": "cli/src/util/term.ts",
    "content": "import { TERMINAL_INFO } from '@ionic/utils-terminal';\n\nimport c from '../colors';\nimport { logger } from '../log';\n\n// Given input variables to a command, make sure all are provided if the terminal\n// is not interactive (because we won't be able to prompt the user)\nexport const checkInteractive = (...args: string[]): boolean => {\n  if (isInteractive()) {\n    return true;\n  }\n\n  // Fail if no args are provided, treat this as just a check of whether the term is\n  // interactive or not.\n  if (!args.length) {\n    return false;\n  }\n\n  // Make sure none of the provided args are empty, otherwise print the interactive\n  // warning and return false\n  if (args.filter((arg) => !arg).length) {\n    logger.error(\n      `Non-interactive shell detected.\\n` +\n        `Run the command with ${c.input('--help')} to see a list of arguments that must be provided.`,\n    );\n    return false;\n  }\n  return true;\n};\n\nexport const isInteractive = (): boolean => TERMINAL_INFO.tty && !TERMINAL_INFO.ci;\n"
  },
  {
    "path": "cli/src/util/uuid.ts",
    "content": "export function uuidv4(): string {\n  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n    const r = (Math.random() * 16) | 0;\n    const v = c == 'x' ? r : (r & 0x3) | 0x8;\n\n    return v.toString(16);\n  });\n}\n"
  },
  {
    "path": "cli/src/util/xml.ts",
    "content": "import { readFile } from 'fs-extra';\nimport xml2js from 'xml2js';\n\nexport async function readXML(path: string): Promise<any> {\n  try {\n    const xmlStr = await readFile(path, { encoding: 'utf-8' });\n    try {\n      return await xml2js.parseStringPromise(xmlStr);\n    } catch (e: any) {\n      throw `Error parsing: ${path}, ${e.stack ?? e}`;\n    }\n  } catch (e) {\n    throw `Unable to read: ${path}`;\n  }\n}\n\nexport function parseXML(xmlStr: string, options?: xml2js.OptionsV2): any {\n  const parser = options !== undefined ? new xml2js.Parser({ ...options }) : new xml2js.Parser();\n  let xmlObj;\n  parser.parseString(xmlStr, (err: any, result: any) => {\n    if (!err) {\n      xmlObj = result;\n    }\n  });\n  return xmlObj;\n}\n\nexport async function writeXML(object: any): Promise<any> {\n  return new Promise((resolve) => {\n    const builder = new xml2js.Builder({\n      headless: true,\n      explicitRoot: false,\n      rootName: 'deleteme',\n    });\n    let xml = builder.buildObject(object);\n    xml = xml.replace('<deleteme>', '').replace('</deleteme>', '');\n    resolve(xml);\n  });\n}\n\nexport function buildXmlElement(configElement: any, rootName: string): string {\n  const builder = new xml2js.Builder({\n    headless: true,\n    explicitRoot: false,\n    rootName: rootName,\n  });\n\n  return builder.buildObject(configElement);\n}\n"
  },
  {
    "path": "cli/test/add.android.spec.ts",
    "content": "import { APP_ID, APP_NAME, run, makeAppDir, MappedFS, installPlatform } from './util';\n\ndescribe.each([false, true])('Add: Android (monoRepoLike: %p)', (monoRepoLike) => {\n  let appDirObj: any;\n  let FS: MappedFS;\n\n  beforeAll(async () => {\n    // These commands are slowww...\n    jest.setTimeout(150000);\n    appDirObj = await makeAppDir(monoRepoLike);\n    const appDir = appDirObj.appDir;\n    // Init in this directory so we can test add\n    await run(appDir, `init \"${APP_NAME}\" \"${APP_ID}\"`);\n    await installPlatform(appDir, 'android');\n    await run(appDir, `add android`);\n    FS = new MappedFS(appDir);\n  });\n\n  afterAll(() => {\n    appDirObj.cleanupCallback();\n  });\n\n  it('Should add', async () => {\n    expect(await FS.exists('android/')).toBe(true);\n  });\n\n  it('Should have Cordova JS copied', async () => {\n    expect(await FS.exists('android/app/src/main/assets/public/cordova.js')).toBe(true);\n    expect(await FS.exists('android/app/src/main/assets/public/cordova_plugins.js')).toBe(true);\n  });\n\n  it('Should rename package', async () => {\n    expect(await FS.exists('android/app/src/main/java/com/getcapacitor/cli/test/MainActivity.java')).toBe(true);\n  });\n\n  it('Should rename package in main activity', async () => {\n    const activityContent = await FS.read('android/app/src/main/java/com/getcapacitor/cli/test/MainActivity.java');\n    const regex = new RegExp(`package ${APP_ID};`);\n    expect(regex.test(activityContent)).toBe(true);\n  });\n\n  it('Should rename app id in build.gradle', async () => {\n    const gradleContent = await FS.read('android/app/build.gradle');\n    const regex = new RegExp(`applicationId \"${APP_ID}\"`);\n    expect(regex.test(gradleContent)).toBe(true);\n  });\n\n  it('Should update strings.xml', async () => {\n    const stringsContent = await FS.read('android/app/src/main/res/values/strings.xml');\n    let regex = new RegExp(`<string name=\"app_name\">${APP_NAME}</string>`);\n    expect(regex.test(stringsContent)).toBe(true);\n    regex = new RegExp(`<string name=\"title_activity_main\">${APP_NAME}</string>`);\n    expect(regex.test(stringsContent)).toBe(true);\n    regex = new RegExp(`<string name=\"custom_url_scheme\">${APP_ID}</string>`);\n    expect(regex.test(stringsContent)).toBe(true);\n  });\n\n  // Other test ideas:\n  // should install/copy pre-existing cordova/capacitor plugins in package.json\n});\n"
  },
  {
    "path": "cli/test/add.ios.spec.ts",
    "content": "import { APP_ID, APP_NAME, run, makeAppDir, MappedFS, installPlatform } from './util';\n\ndescribe.each([false, true])('Add: iOS (monoRepoLike: %p)', (monoRepoLike) => {\n  let appDirObj: any;\n  let FS: MappedFS;\n\n  beforeAll(async () => {\n    // These commands are slowww...\n    jest.setTimeout(150000);\n    appDirObj = await makeAppDir(monoRepoLike);\n    const appDir = appDirObj.appDir;\n    // Init in this directory so we can test add\n    await run(appDir, `init \"${APP_NAME}\" \"${APP_ID}\"`);\n    await installPlatform(appDir, 'ios');\n    await run(appDir, `add ios`);\n    FS = new MappedFS(appDir);\n  });\n\n  afterAll(() => {\n    appDirObj.cleanupCallback();\n  });\n\n  it('Should add', async () => {\n    expect(await FS.exists('ios/')).toBe(true);\n  });\n\n  it('Should update Info.plist', async () => {\n    const infoContent = await FS.read('ios/App/App/Info.plist');\n    const regex = new RegExp(`<key>CFBundleDisplayName</key>[^<]*<string>${APP_NAME}</string>`);\n    expect(regex.test(infoContent)).toBe(true);\n  });\n\n  it('Should update project.pbxproj', async () => {\n    const pbxContent = await FS.read('ios/App/App.xcodeproj/project.pbxproj');\n    const regex = new RegExp(`PRODUCT_BUNDLE_IDENTIFIER = ${APP_ID}`);\n    expect(regex.test(pbxContent)).toBe(true);\n  });\n\n  // Other test ideas:\n  // should install/copy pre-existing cordova/capacitor plugins in package.json\n});\n"
  },
  {
    "path": "cli/test/framework-detection.spec.ts",
    "content": "import { resolve } from 'path';\n\nimport { detectFramework } from '../../cli/src/framework-configs';\nimport type { Config } from '../src/definitions';\n\ndescribe('framework detection', () => {\n  let config: Config;\n\n  beforeEach(() => {\n    config = {\n      cli: null as any,\n      app: {\n        rootDir: resolve('/'),\n        appId: 'appId',\n        appName: 'appName',\n        webDir: '',\n        webDirAbs: '',\n        package: {\n          name: 'package-name',\n          version: '0.0.0',\n        },\n        extConfigType: 'json',\n        extConfigName: '',\n        extConfigFilePath: '',\n        extConfig: null as any,\n      },\n      android: null as any,\n      ios: null as any,\n      web: null as any,\n    };\n  });\n\n  it('Angular', () => {\n    addDep(config, '@angular/cli');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Angular');\n    expect(f?.webDir).toBe('dist');\n  });\n\n  it('Create React App', () => {\n    addDep(config, 'react-scripts');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Create React App');\n    expect(f?.webDir).toBe('build');\n  });\n\n  it('Ember', () => {\n    addDep(config, 'ember-cli');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Ember');\n    expect(f?.webDir).toBe('dist');\n  });\n\n  it('Gatsby', () => {\n    addDep(config, 'react-scripts');\n    addDep(config, 'gatsby');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Gatsby');\n    expect(f?.webDir).toBe('public');\n  });\n\n  it('Ionic Angular', () => {\n    addDep(config, '@ionic/angular');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Ionic Angular');\n    expect(f?.webDir).toBe('www');\n  });\n\n  it('Ionic Angular and not just Angular', () => {\n    addDep(config, '@angular/cli');\n    addDep(config, '@ionic/angular');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Ionic Angular');\n    expect(f?.webDir).toBe('www');\n  });\n\n  it('Ionic React', () => {\n    addDep(config, '@ionic/react');\n    addDep(config, 'react-scripts');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Ionic React');\n    expect(f?.webDir).toBe('build');\n  });\n\n  it('Ionic React over Create React App', () => {\n    addDep(config, '@ionic/react');\n    addDep(config, 'react-scripts');\n    addDep(config, 'react-dev-utils');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Ionic React');\n    expect(f?.webDir).toBe('build');\n  });\n\n  it('Ionic Vue', () => {\n    addDep(config, '@ionic/vue');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Ionic Vue');\n    expect(f?.webDir).toBe('public');\n  });\n\n  it('Ionic Vue and not just Vue', () => {\n    addDep(config, '@ionic/vue');\n    addDep(config, '@vue/cli-service');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Ionic Vue');\n    expect(f?.webDir).toBe('public');\n  });\n\n  it('Next', () => {\n    addDep(config, 'next');\n    addDep(config, 'react-scripts');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Next');\n    expect(f?.webDir).toBe('public');\n  });\n\n  it('Preact', () => {\n    addDep(config, 'preact-cli');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Preact');\n    expect(f?.webDir).toBe('build');\n  });\n\n  it('Stencil', () => {\n    addDep(config, '@stencil/core');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Stencil');\n    expect(f?.webDir).toBe('www');\n  });\n\n  it('Svelte', () => {\n    addDep(config, 'svelte');\n    addDep(config, 'sirv-cli');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Svelte');\n    expect(f?.webDir).toBe('public');\n  });\n\n  it('not Svelte w/out sirv-cli', () => {\n    addDep(config, 'svelte');\n    const f = detectFramework(config);\n    expect(f).toBeUndefined();\n  });\n\n  it('Vue', () => {\n    addDep(config, '@vue/cli-service');\n    const f = detectFramework(config);\n    expect(f?.name).toBe('Vue');\n    expect(f?.webDir).toBe('dist');\n  });\n\n  it('nothing detected', () => {\n    const f = detectFramework(config);\n    expect(f).toBeUndefined();\n  });\n});\n\nfunction addDep(config: Config, depName: string) {\n  (config.app.package as any).dependencies = config.app.package.dependencies || {};\n  (config.app.package.dependencies as any)[depName] = '0.0.0';\n}\n"
  },
  {
    "path": "cli/test/init.spec.ts",
    "content": "import { mkdir } from 'fs-extra';\nimport { join } from 'path';\n\nimport { APP_ID, APP_NAME, run, mktmp, MappedFS } from './util';\n\ndescribe('Init', () => {\n  let appDirObj: any;\n  let tmpDir: string;\n  let appDir: string;\n  let FS: MappedFS;\n\n  beforeAll(async () => {\n    // These commands are slowww...\n    jest.setTimeout(150000);\n    appDirObj = await mktmp();\n    tmpDir = appDirObj.path;\n    appDir = join(tmpDir, 'test-app');\n    await mkdir(appDir);\n    FS = new MappedFS(appDir);\n  });\n\n  afterAll(() => {\n    appDirObj.cleanupCallback();\n  });\n\n  it('Should init a project', async () => {\n    await run(appDir, `init \"${APP_NAME}\" \"${APP_ID}\"`);\n    expect(await FS.exists('capacitor.config.json')).toBe(true);\n\n    const fileContents = await FS.read('capacitor.config.json');\n    const jsonContents = JSON.parse(fileContents);\n    expect(jsonContents.appId).toEqual(APP_ID);\n    expect(jsonContents.appName).toEqual(APP_NAME);\n    expect(jsonContents.webDir).toEqual('www');\n  });\n\n  it('Should init a project with webDir set', async () => {\n    await run(appDir, `init \"${APP_NAME}\" \"${APP_ID}\" --web-dir=\"build\"`);\n    expect(await FS.exists('capacitor.config.json')).toBe(true);\n\n    const fileContents = await FS.read('capacitor.config.json');\n    const jsonContents = JSON.parse(fileContents);\n    expect(jsonContents.appId).toEqual(APP_ID);\n    expect(jsonContents.appName).toEqual(APP_NAME);\n    expect(jsonContents.webDir).toEqual('build');\n  });\n});\n"
  },
  {
    "path": "cli/test/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n      \"strict\": false\n  }\n}\n"
  },
  {
    "path": "cli/test/update.android.spec.ts",
    "content": "import { APP_ID, APP_NAME, CORDOVA_PLUGIN_ID, MappedFS, makeAppDir, run, installPlatform } from './util';\n\ndescribe.each([false, true])('Update: Android (monoRepoLike: %p)', (monoRepoLike) => {\n  let appDirObj: any;\n  let appDir: string;\n  let FS: MappedFS;\n\n  beforeAll(async () => {\n    // These commands are slowww...\n    jest.setTimeout(150000);\n    appDirObj = await makeAppDir(monoRepoLike);\n    appDir = appDirObj.appDir;\n    // Init in this directory so we can test add\n    await run(appDir, `init \"${APP_NAME}\" \"${APP_ID}\"`);\n    await installPlatform(appDir, 'android');\n    await run(appDir, `add android`);\n    FS = new MappedFS(appDir);\n  });\n\n  afterAll(() => {\n    //appDirObj.cleanupCallback();\n  });\n\n  it('Should install Cordova plugin JS', async () => {\n    const cordovaPluginJSContent = await FS.read('android/app/src/main/assets/public/cordova_plugins.js');\n    const regex = new RegExp(CORDOVA_PLUGIN_ID);\n    expect(regex.test(cordovaPluginJSContent)).toBe(true);\n  });\n\n  // Other test ideas:\n  // should install/copy pre-existing cordova/capacitor plugins in package.json\n});\n"
  },
  {
    "path": "cli/test/update.ios.spec.ts",
    "content": "import { APP_ID, APP_NAME, CORDOVA_PLUGIN_ID, MappedFS, makeAppDir, run, installPlatform } from './util';\n\ndescribe.each([false, true])('Update: iOS (monoRepoLike: %p)', (monoRepoLike) => {\n  let appDirObj: any;\n  let appDir: string;\n  let FS: MappedFS;\n\n  beforeAll(async () => {\n    // These commands are slowww...\n    jest.setTimeout(150000);\n    appDirObj = await makeAppDir(monoRepoLike);\n    appDir = appDirObj.appDir;\n    // Init in this directory so we can test add\n    await run(appDir, `init \"${APP_NAME}\" \"${APP_ID}\"`);\n    await installPlatform(appDir, 'ios');\n    await run(appDir, `add ios`);\n    FS = new MappedFS(appDir);\n  });\n\n  afterAll(() => {\n    //appDirObj.cleanupCallback();\n  });\n\n  it('Should install Cordova plugin JS', async () => {\n    const cordovaPluginJSContent = await FS.read('ios/App/App/public/cordova_plugins.js');\n    const regex = new RegExp(CORDOVA_PLUGIN_ID);\n    expect(regex.test(cordovaPluginJSContent)).toBe(true);\n  });\n\n  // Other test ideas:\n  // should install/copy pre-existing cordova/capacitor plugins in package.json\n});\n"
  },
  {
    "path": "cli/test/util.ts",
    "content": "import { exec } from 'child_process';\nimport { mkdir, mkdirp, readFile, pathExists, writeFile } from 'fs-extra';\nimport { join, resolve } from 'path';\nimport tmp from 'tmp';\nimport type { DirCallback } from 'tmp';\n\nimport { loadConfig } from '../src/config';\nimport type { Config } from '../src/definitions';\nimport { runCommand } from '../src/util/subprocess';\n\nconst cwd = process.cwd();\n\nexport const CORDOVA_PLUGIN_ID = 'cool-cordova-plugin';\nexport const APP_ID = 'com.getcapacitor.cli.test';\nexport const APP_NAME = 'Capacitor CLI Test';\n\nexport async function makeConfig(appRoot: string): Promise<Config> {\n  process.chdir(appRoot);\n  const config = await loadConfig();\n  process.chdir(cwd);\n  return config;\n}\n\nexport async function run(appRoot: string, capCommand: string): Promise<string> {\n  return new Promise((resolve, reject) => {\n    exec(`cd \"${appRoot}\" && \"${cwd}/bin/capacitor\" ${capCommand}`, (error, stdout, stderr) => {\n      if (error) {\n        reject(stdout + stderr);\n      } else {\n        resolve(stdout);\n      }\n    });\n  });\n}\n\nexport function mktmp(): Promise<{\n  cleanupCallback: DirCallback;\n  path: string;\n}> {\n  return new Promise((resolve) => {\n    tmp.dir((err, path, cleanupCallback) => {\n      if (err) {\n        throw err;\n      }\n\n      resolve({\n        cleanupCallback,\n        path,\n      });\n    });\n  });\n}\n\nconst APP_INDEX = `\n<!DOCTYPE html>\n<html lang=\"en\" dir=\"ltr\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Test Capacitor App</title>\n</head>\n<body>\n  <capacitor-welcome></capacitor-welcome>\n</body>\n</html>\n`;\n\nexport async function installPlatform(appDir: string, platform: string): Promise<void> {\n  const platformPath = resolve(cwd, '..', platform);\n  await runCommand('npm', ['install', platformPath], { cwd: appDir });\n}\n\nexport async function makeAppDir(monoRepoLike = false): Promise<void> {\n  const appDirObj: any = await mktmp();\n  const tmpDir = appDirObj.path;\n  const rootDir = monoRepoLike ? join(tmpDir, 'test-root') : join(tmpDir, 'test-app');\n  if (monoRepoLike) {\n    await mkdir(rootDir);\n  }\n  const cordovaPluginPath = join(tmpDir, CORDOVA_PLUGIN_ID);\n  const APP_PACKAGE_JSON = `\n{\n  \"name\": \"test-app\",\n  \"dependencies\": {\n    \"${CORDOVA_PLUGIN_ID}\": \"file:${cordovaPluginPath}\"\n  }\n}\n`;\n  const appDir = monoRepoLike ? join(rootDir, 'test-app') : rootDir;\n  await mkdir(appDir);\n  // Make the web dir\n  await mkdir(join(appDir, 'www'));\n  // Make a fake index.html\n  await writeFile(join(appDir, 'www', 'index.html'), APP_INDEX);\n  // Make a fake package.json\n  await writeFile(join(appDir, 'package.json'), APP_PACKAGE_JSON);\n\n  // We use 'npm install' to install @capacitor/core and @capacitor/cli\n  // Otherwise later use of 'npm install --save @capacitor/android|ios' will wipe 'node_modules/@capacitor/'\n  const corePath = resolve(cwd, '../core');\n  const cliPath = resolve(cwd, '../cli');\n  await runCommand('npm', ['install', '--save', corePath, cliPath], {\n    cwd: rootDir,\n  });\n\n  // Make a fake cordova plugin\n  await makeCordovaPlugin(cordovaPluginPath);\n\n  await runCommand('npm', ['install', '--save', cordovaPluginPath], {\n    cwd: rootDir,\n  });\n\n  return {\n    ...appDirObj,\n    appDir,\n  };\n}\n\nconst CODOVA_PLUGIN_JS = `\nvar exec = require('cordova/exec');\nvar CoolPlugin = {\n    doSomethingCool: function (doOverlay) {\n        exec(null, null, \"CoolPlugin\", \"doSomethingCool\", []);\n    }\n};\nmodule.exports = CoolPlugin;\n`;\n\nconst CORDOVA_PLUGIN_XML = `\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\n    id=\"${CORDOVA_PLUGIN_ID}\"\n    version=\"1.0.20\">\n    <name>Cool Cordova Plugin</name>\n    <js-module src=\"plugin.js\" name=\"coolplugin\">\n        <clobbers target=\"window.CoolPlugin\" />\n    </js-module>\n    <platform name=\"android\">\n        <config-file target=\"res/xml/config.xml\" parent=\"/*\">\n            <feature name=\"CoolPlugin\">\n                <param name=\"android-package\" value=\"com.getcapacitor.cordova.CoolPlugin\"/>\n            </feature>\n        </config-file>\n        <source-file src=\"android/com/getcapacitor/CoolPlugin.java\" target-dir=\"src/com/getcapacitor/cordova\" />\n    </platform>\n    <platform name=\"ios\">\n         <config-file target=\"config.xml\" parent=\"/*\">\n             <feature name=\"CoolPlugin\">\n                 <param name=\"ios-package\" value=\"CoolPlugin\" />\n             </feature>\n         </config-file>\n         <source-file src=\"src/ios/CoolPlugin.m\" />\n    </platform>\n</plugin>\n`;\n\nconst CORDOVA_PLUGIN_PACKAGE = `\n{\n  \"name\": \"${CORDOVA_PLUGIN_ID}\",\n  \"version\": \"0.0.1\",\n  \"description\": \"Cool Cordova plugin\",\n  \"cordova\": {\n    \"id\": \"${CORDOVA_PLUGIN_ID}\",\n    \"platforms\": [\n      \"android\",\n      \"ios\"\n    ]\n  },\n  \"author\": \"Cap tester\",\n  \"license\": \"MIT\"\n}\n`;\n\nasync function makeCordovaPlugin(cordovaPluginPath: string) {\n  const iosPath = join(cordovaPluginPath, 'src', 'ios');\n  const androidPath = join(cordovaPluginPath, 'android/com/getcapacitor');\n  await mkdirp(cordovaPluginPath);\n  await writeFile(join(cordovaPluginPath, 'plugin.js'), CODOVA_PLUGIN_JS);\n  await writeFile(join(cordovaPluginPath, 'plugin.xml'), CORDOVA_PLUGIN_XML);\n  await writeFile(join(cordovaPluginPath, 'package.json'), CORDOVA_PLUGIN_PACKAGE);\n  await mkdirp(iosPath);\n  await mkdirp(androidPath);\n  await writeFile(join(iosPath, 'CoolPlugin.m'), '');\n  await writeFile(join(androidPath, 'CoolPlugin.java'), '');\n}\n\nclass MappedFS {\n  private rootDir: string;\n  constructor(rootDir: string) {\n    this.rootDir = rootDir;\n  }\n  async read(path: string): Promise<string> {\n    return await readFile(resolve(this.rootDir, path), { encoding: 'utf-8' });\n  }\n  async exists(path: string): Promise<boolean> {\n    return await pathExists(resolve(this.rootDir, path));\n  }\n}\n\nexport { MappedFS };\n"
  },
  {
    "path": "cli/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"importHelpers\": true,\n    \"lib\": [\n      \"ES2021\",\n      \"ES2022.Error\"\n    ],\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"noEmitHelpers\": true,\n    \"outDir\": \"dist\",\n    \"pretty\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es2019\"\n  },\n  \"files\": [\"src/index.ts\"]\n}\n"
  },
  {
    "path": "core/.npmrc",
    "content": "package-lock=false"
  },
  {
    "path": "core/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n# [8.2.0](https://github.com/ionic-team/capacitor/compare/8.1.0...8.2.0) (2026-03-06)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [8.1.0](https://github.com/ionic-team/capacitor/compare/8.0.2...8.1.0) (2026-02-11)\n\n### Bug Fixes\n\n- **cookies:** only send expires param on web if a date is set ([b10cd7f](https://github.com/ionic-team/capacitor/commit/b10cd7ff15b010a76802374214f4e0cbd04abdab))\n\n### Reverts\n\n- revert version bump from [#8319](https://github.com/ionic-team/capacitor/issues/8319) and [#8320](https://github.com/ionic-team/capacitor/issues/8320) ([a48ebb6](https://github.com/ionic-team/capacitor/commit/a48ebb622ea4ebe92927bf1756a4d8ac6012884b))\n\n## [8.0.2](https://github.com/ionic-team/capacitor/compare/8.0.1...8.0.2) (2026-01-27)\n\n### Bug Fixes\n\n- **core:** make SystemBars hide and show options optional ([#8305](https://github.com/ionic-team/capacitor/issues/8305)) ([95dc7d8](https://github.com/ionic-team/capacitor/commit/95dc7d8ace3aabdda7e325c4a8ef7d1432ad37e9))\n\n## [8.0.1](https://github.com/ionic-team/capacitor/compare/8.0.0...8.0.1) (2026-01-13)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [8.0.0](https://github.com/ionic-team/capacitor/compare/8.0.0-beta.0...8.0.0) (2025-12-08)\n\n### Features\n\n- **android:** Improving SystemBars inset handling ([#8268](https://github.com/ionic-team/capacitor/issues/8268)) ([81ae30a](https://github.com/ionic-team/capacitor/commit/81ae30a503797e417dd125b06262dabc4696c88a))\n\n# [8.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.3...8.0.0-beta.0) (2025-11-14)\n\n### Bug Fixes\n\n- make Plugin.resolve act consistently ([#8225](https://github.com/ionic-team/capacitor/issues/8225)) ([06aeb9e](https://github.com/ionic-team/capacitor/commit/06aeb9e85d162d6be9d96820edcb2008cd74da84))\n\n### Features\n\n- System Bars Plugin ([#8180](https://github.com/ionic-team/capacitor/issues/8180)) ([a32216a](https://github.com/ionic-team/capacitor/commit/a32216ac0607172a3a9c7ae5cdbfc598769294a6))\n\n# [8.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.2...8.0.0-alpha.3) (2025-10-22)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [8.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.1...8.0.0-alpha.2) (2025-08-20)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [8.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/7.4.2...8.0.0-alpha.1) (2025-08-20)\n\n### Bug Fixes\n\n- http content headers not sent when using axios ([#8039](https://github.com/ionic-team/capacitor/issues/8039)) ([67cac40](https://github.com/ionic-team/capacitor/commit/67cac40660b3e8cc78d1d228b7c6915450948ef1))\n\n## [7.4.2](https://github.com/ionic-team/capacitor/compare/7.4.1...7.4.2) (2025-07-10)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [7.4.1](https://github.com/ionic-team/capacitor/compare/7.4.0...7.4.1) (2025-07-03)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.4.0](https://github.com/ionic-team/capacitor/compare/7.3.0...7.4.0) (2025-06-18)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.3.0](https://github.com/ionic-team/capacitor/compare/7.2.0...7.3.0) (2025-06-05)\n\n### Bug Fixes\n\n- **core:** Prevent error when hasListeners is empty ([#7975](https://github.com/ionic-team/capacitor/issues/7975)) ([a4a0942](https://github.com/ionic-team/capacitor/commit/a4a0942eddba068e078bd782bb093ed1ecff9e00))\n\n# [7.2.0](https://github.com/ionic-team/capacitor/compare/7.1.0...7.2.0) (2025-03-31)\n\n### Features\n\n- **http:** Apply overrideUserAgent to requests ([#7906](https://github.com/ionic-team/capacitor/issues/7906)) ([52482c9](https://github.com/ionic-team/capacitor/commit/52482c9d3c575b737054b41f9d1730c70cc5f471))\n\n# [7.1.0](https://github.com/ionic-team/capacitor/compare/7.0.1...7.1.0) (2025-03-12)\n\n### Bug Fixes\n\n- **core:** use getPlatform instead of platform in cordova.js ([#7902](https://github.com/ionic-team/capacitor/issues/7902)) ([277db7b](https://github.com/ionic-team/capacitor/commit/277db7b48caaf870eefdf701ea99332c4338d7ed))\n- **http:** boundary not added for Request objects ([#7897](https://github.com/ionic-team/capacitor/issues/7897)) ([bdaa6f3](https://github.com/ionic-team/capacitor/commit/bdaa6f3c38c33f3a021ac61f2de89101a5b66cff))\n\n## [7.0.1](https://github.com/ionic-team/capacitor/compare/7.0.0...7.0.1) (2025-01-21)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.0.0](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.1...7.0.0) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.0...7.0.0-rc.1) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/7.0.0-beta.0...7.0.0-rc.0) (2024-12-20)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.2...7.0.0-beta.0) (2024-12-20)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.1...7.0.0-alpha.2) (2024-11-19)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [7.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/6.1.2...7.0.0-alpha.1) (2024-10-14)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [6.1.2](https://github.com/ionic-team/capacitor/compare/6.1.1...6.1.2) (2024-08-07)\n\n### Bug Fixes\n\n- **http:** pass original url as query param on the proxy url ([#7527](https://github.com/ionic-team/capacitor/issues/7527)) ([1da06e6](https://github.com/ionic-team/capacitor/commit/1da06e66cb9cfbf5a5cc48ba6c23cdbe18bc8fc0))\n\n## [6.1.1](https://github.com/ionic-team/capacitor/compare/6.1.0...6.1.1) (2024-07-17)\n\n### Bug Fixes\n\n- **http:** handle UInt8Array on body ([#7546](https://github.com/ionic-team/capacitor/issues/7546)) ([cfb9ce1](https://github.com/ionic-team/capacitor/commit/cfb9ce175615f69fe86b61af6d51ec2044d147cd))\n\n# [6.1.0](https://github.com/ionic-team/capacitor/compare/6.0.0...6.1.0) (2024-06-11)\n\n### Bug Fixes\n\n- **http:** don't override readyState for non POST requests ([#7488](https://github.com/ionic-team/capacitor/issues/7488)) ([30c13a8](https://github.com/ionic-team/capacitor/commit/30c13a865e7710e6dc5f0ee014e951d52d030795))\n\n# [6.0.0](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.2...6.0.0) (2024-04-15)\n\n### Bug Fixes\n\n- **http:** Add URLSearchParams support ([#7374](https://github.com/ionic-team/capacitor/issues/7374)) ([9367ecc](https://github.com/ionic-team/capacitor/commit/9367ecc56a0c78249dccdf95dca5006422144289))\n- **http:** prevent POST request from being proxied ([#7395](https://github.com/ionic-team/capacitor/issues/7395)) ([7b8c352](https://github.com/ionic-team/capacitor/commit/7b8c3523decd5610dcf09e926640bf35b382d61d))\n\n# [6.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.1...6.0.0-rc.2) (2024-03-25)\n\n### Bug Fixes\n\n- **http:** change proxy url generation ([#7354](https://github.com/ionic-team/capacitor/issues/7354)) ([318c316](https://github.com/ionic-team/capacitor/commit/318c316847c5b059fb88b46d4acd31e1ced477e5))\n- vue 3 log warning causes error on iOS ([#6993](https://github.com/ionic-team/capacitor/issues/6993)) ([87271e2](https://github.com/ionic-team/capacitor/commit/87271e2671013ad35d13b22f2e96d4fe8f4eeaf0))\n\n# [6.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.0...6.0.0-rc.1) (2024-03-15)\n\n### Bug Fixes\n\n- **core:** make 'content-type' header count for XMLHttpRequest patch ([#7161](https://github.com/ionic-team/capacitor/issues/7161)) ([26d7f68](https://github.com/ionic-team/capacitor/commit/26d7f688284914c6ef795564ba424119efc32a1c))\n- **http:** handle proxy urls with port ([#7273](https://github.com/ionic-team/capacitor/issues/7273)) ([514409a](https://github.com/ionic-team/capacitor/commit/514409aeb93ad65be105bbe2da8d2cd86ff159b0))\n- **http:** keep original URL properties on proxy ([#7329](https://github.com/ionic-team/capacitor/issues/7329)) ([cbb6407](https://github.com/ionic-team/capacitor/commit/cbb6407225b42a0d9db4f335a9766f119501021d))\n- **http:** Make proxy work with Request objects ([#7348](https://github.com/ionic-team/capacitor/issues/7348)) ([7e68725](https://github.com/ionic-team/capacitor/commit/7e6872573df03ab5cdc10a1a27db3e9fe81a141d))\n- **http:** route get requests through custom handler ([#6818](https://github.com/ionic-team/capacitor/issues/6818)) ([b853d06](https://github.com/ionic-team/capacitor/commit/b853d065055b5a819949551be58b62d40b52e37c))\n- **http:** set port for proxy url ([#7341](https://github.com/ionic-team/capacitor/issues/7341)) ([a3059dc](https://github.com/ionic-team/capacitor/commit/a3059dca4a7746d9fb7102a7d41f4da80e2f48b4))\n\n### Features\n\n- **webview:** add setServerAssetPath method ([881235b](https://github.com/ionic-team/capacitor/commit/881235b14de23ef988746bfb89a5a0fc3c8d8466))\n\n# [6.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.2...6.0.0-rc.0) (2024-01-23)\n\n### Bug Fixes\n\n- **web:** Implement `retainUntilConsumed` on notifyListeners ([#7127](https://github.com/ionic-team/capacitor/issues/7127)) ([526292e](https://github.com/ionic-team/capacitor/commit/526292eb273ee7d8fa9a9912ce3b59e9a104c19e))\n\n# [6.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.1...6.0.0-beta.2) (2023-12-14)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [6.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.0...6.0.0-beta.1) (2023-12-14)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [6.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.2...6.0.0-beta.0) (2023-12-13)\n\n### Bug Fixes\n\n- **http:** parse readablestream data on fetch request objects ([#6919](https://github.com/ionic-team/capacitor/issues/6919)) ([80ec3b7](https://github.com/ionic-team/capacitor/commit/80ec3b73db18b7b6841bf90ed50a67389946ab87))\n- **http:** set formdata boundary and body when content-type not explicitly set ([0c2ccd9](https://github.com/ionic-team/capacitor/commit/0c2ccd910a92ce3deaa67eb1819a4faa39c6af6e))\n\n# [6.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.1...6.0.0-alpha.2) (2023-11-15)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [6.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/5.2.3...6.0.0-alpha.1) (2023-11-08)\n\n### Bug Fixes\n\n- **android:** make local urls use unpatched fetch ([#6953](https://github.com/ionic-team/capacitor/issues/6953)) ([e50e56c](https://github.com/ionic-team/capacitor/commit/e50e56c5231f230497d1bd420e02e2e065c38f86))\n- **http:** add support for Request objects in fetch ([24b3cc1](https://github.com/ionic-team/capacitor/commit/24b3cc113e3d8aae5d85dbf2d25bec0c35136477))\n- **http:** inherit object properties on window.XMLHttpRequest ([91c11d0](https://github.com/ionic-team/capacitor/commit/91c11d06f773c45a10f6f2d52f672ae6f189b162))\n\n# [5.6.0](https://github.com/ionic-team/capacitor/compare/5.5.1...5.6.0) (2023-12-14)\n\n### Bug Fixes\n\n- **http:** set formdata boundary and body when content-type not explicitly set ([#7133](https://github.com/ionic-team/capacitor/issues/7133)) ([3862d6e](https://github.com/ionic-team/capacitor/commit/3862d6e6721793d78add9acf5b14fd9a8f7a5b60))\n\n## [5.5.1](https://github.com/ionic-team/capacitor/compare/5.5.0...5.5.1) (2023-10-25)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.5.0](https://github.com/ionic-team/capacitor/compare/5.4.2...5.5.0) (2023-10-11)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [5.4.2](https://github.com/ionic-team/capacitor/compare/5.4.1...5.4.2) (2023-10-04)\n\n### Bug Fixes\n\n- **android:** make local urls use unpatched fetch ([#6954](https://github.com/ionic-team/capacitor/issues/6954)) ([56fb853](https://github.com/ionic-team/capacitor/commit/56fb8536af53f4f4ee49b9394fd966ad514b9458))\n\n## [5.4.1](https://github.com/ionic-team/capacitor/compare/5.4.0...5.4.1) (2023-09-21)\n\n### Bug Fixes\n\n- **http:** parse readablestream data on fetch request objects ([3fe0642](https://github.com/ionic-team/capacitor/commit/3fe06426bd20713e2322780b70bc5d97ad371fae))\n- **http:** return xhr response headers case insensitive ([687b6b1](https://github.com/ionic-team/capacitor/commit/687b6b1780506c17fb73ed1d9cbf50c1d1e40ef1))\n\n# [5.4.0](https://github.com/ionic-team/capacitor/compare/5.3.0...5.4.0) (2023-09-14)\n\n### Bug Fixes\n\n- **http:** add support for defining xhr and angular http response types ([09bd040](https://github.com/ionic-team/capacitor/commit/09bd040dfe4b8808d7499b6ee592005420406cac))\n- **http:** add support for Request objects in fetch ([2fe4535](https://github.com/ionic-team/capacitor/commit/2fe4535e781b1a5cfa0f3359c1afa5c360073b6a))\n- **http:** inherit object properties on window.XMLHttpRequest ([5cd3b2f](https://github.com/ionic-team/capacitor/commit/5cd3b2fa6d6936864e1aab2e98963df2d4da3b95))\n\n# [5.3.0](https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0) (2023-08-23)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [5.2.3](https://github.com/ionic-team/capacitor/compare/5.2.2...5.2.3) (2023-08-10)\n\n### Bug Fixes\n\n- **cli:** signing type option issue ([#6716](https://github.com/ionic-team/capacitor/issues/6716)) ([ee0f745](https://github.com/ionic-team/capacitor/commit/ee0f7457e458ca4bb4eb74f67552ac2ace76016b))\n- **cookies:** hide httpOnly cookies from client ([0cc927e](https://github.com/ionic-team/capacitor/commit/0cc927ef5f0f7076a6d486d666d78483f1d71c54))\n- **http:** return valid response for relative url xhr requests ([bde6569](https://github.com/ionic-team/capacitor/commit/bde65696218f97a8328041f137457f46e5eb766a))\n\n## [5.2.2](https://github.com/ionic-team/capacitor/compare/5.2.1...5.2.2) (2023-07-19)\n\n### Bug Fixes\n\n- add http method to prototype.open ([#6740](https://github.com/ionic-team/capacitor/issues/6740)) ([1fd2d87](https://github.com/ionic-team/capacitor/commit/1fd2d8762ff2341a8fe20eec9e774c6a29576e88))\n\n## [5.2.1](https://github.com/ionic-team/capacitor/compare/5.2.0...5.2.1) (2023-07-13)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.2.0](https://github.com/ionic-team/capacitor/compare/5.1.1...5.2.0) (2023-07-12)\n\n### Bug Fixes\n\n- **http:** fire events in correct order when using xhr ([5ed3617](https://github.com/ionic-team/capacitor/commit/5ed361787596bb5949f6ae5e366495f296352bf3))\n\n### Features\n\n- **http:** support for FormData requests ([#6708](https://github.com/ionic-team/capacitor/issues/6708)) ([849c564](https://github.com/ionic-team/capacitor/commit/849c56458205bea3b078b1ee19807d7fd84c47b1))\n\n## [5.1.1](https://github.com/ionic-team/capacitor/compare/5.1.0...5.1.1) (2023-07-05)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.1.0](https://github.com/ionic-team/capacitor/compare/5.0.5...5.1.0) (2023-06-29)\n\n### Features\n\n- export buildRequestInit function so we can use for downloadFile ([95b0575](https://github.com/ionic-team/capacitor/commit/95b0575e3fbc1b1408aa69b61c58e18bf8882cea)\n\n  )\n\n## [5.0.5](https://github.com/ionic-team/capacitor/compare/5.0.4...5.0.5) (2023-06-09)\n\n### Bug Fixes\n\n- **http:** don't throw errors when content-type is null on response ([#6627](https://github.com/ionic-team/capacitor/issues/6627)) ([538821f](https://github.com/ionic-team/capacitor/commit/538821f267aa3b79548fed6aaea8880ff949ffdd))\n\n## [5.0.4](https://github.com/ionic-team/capacitor/compare/5.0.3...5.0.4) (2023-05-23)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [5.0.3](https://github.com/ionic-team/capacitor/compare/5.0.2...5.0.3) (2023-05-10)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [5.0.2](https://github.com/ionic-team/capacitor/compare/5.0.1...5.0.2) (2023-05-09)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [5.0.1](https://github.com/ionic-team/capacitor/compare/5.0.0...5.0.1) (2023-05-05)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.3...5.0.0) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.2...5.0.0-rc.3) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.1...5.0.0-rc.2) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.0...5.0.0-rc.1) (2023-05-02)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.3...5.0.0-rc.0) (2023-05-01)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.2...5.0.0-beta.3) (2023-04-21)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.1...5.0.0-beta.2) (2023-04-13)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [5.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.0...5.0.0-beta.1) (2023-04-03)\n\n### Bug Fixes\n\n- copy url from nativeResponse to response in native-bridge.ts ([#6397](https://github.com/ionic-team/capacitor/issues/6397)) ([e81a2ff](https://github.com/ionic-team/capacitor/commit/e81a2ff42ddd446f3004ab5af6e74209f7ff076a))\n\n# [5.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/5.0.0-alpha.1...5.0.0-beta.0) (2023-03-31)\n\n### Bug Fixes\n\n- 204 http response ([#6266](https://github.com/ionic-team/capacitor/issues/6266)) ([771f6ce](https://github.com/ionic-team/capacitor/commit/771f6ce1f35159848db218a42dc4f56b5106f750))\n\n### Features\n\n- Upgrade to Typescript 5.x ([#6433](https://github.com/ionic-team/capacitor/issues/6433)) ([88d0ded](https://github.com/ionic-team/capacitor/commit/88d0ded9e7356531ffc4563b9b81a0f3f069484b))\n\n# [5.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/4.7.0...5.0.0-alpha.1) (2023-03-16)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.7.0](https://github.com/ionic-team/capacitor/compare/4.6.3...4.7.0) (2023-02-22)\n\n### Bug Fixes\n\n- handle fetch headers that are Headers objects ([#6320](https://github.com/ionic-team/capacitor/issues/6320)) ([cb00e49](https://github.com/ionic-team/capacitor/commit/cb00e4952acca8e877555f30b2190f6685d25934))\n- **ios:** Correctly Attach Headers to Request ([#6303](https://github.com/ionic-team/capacitor/issues/6303)) ([a3f875c](https://github.com/ionic-team/capacitor/commit/a3f875cf42e111fde07d6e87643264b19ed77573))\n\n## [4.6.3](https://github.com/ionic-team/capacitor/compare/4.6.2...4.6.3) (2023-02-03)\n\n### Bug Fixes\n\n- **ios:** crash when http headers contain numbers ([#6251](https://github.com/ionic-team/capacitor/issues/6251)) ([028c556](https://github.com/ionic-team/capacitor/commit/028c556a50b41ee99fe8f4f1aa2f42d3fd57f92d))\n\n## [4.6.2](https://github.com/ionic-team/capacitor/compare/4.6.1...4.6.2) (2023-01-17)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [4.6.1](https://github.com/ionic-team/capacitor/compare/4.6.0...4.6.1) (2022-12-05)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.6.0](https://github.com/ionic-team/capacitor/compare/4.5.0...4.6.0) (2022-12-01)\n\n### Bug Fixes\n\n- **cookies:** Use Set-Cookie headers to persist cookies ([57f8b39](https://github.com/ionic-team/capacitor/commit/57f8b39d7f4c5ee0e5e5cb316913e9450a81d22b))\n\n# [4.5.0](https://github.com/ionic-team/capacitor/compare/4.4.0...4.5.0) (2022-11-16)\n\n### Features\n\n- **cookies:** add get cookies plugin method ([ba1e770](https://github.com/ionic-team/capacitor/commit/ba1e7702a3338714aee24388c0afea39706c9341))\n\n# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21)\n\n### Bug Fixes\n\n- **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc))\n- **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f))\n\n# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21)\n\n### Bug Fixes\n\n- **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e))\n\n### Features\n\n- Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45))\n\n# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27)\n\n### Features\n\n- **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625))\n- **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264))\n\n# [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.0...4.0.0-beta.1) (2022-06-27)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.6.0...4.0.0-beta.0) (2022-06-17)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.6.0](https://github.com/ionic-team/capacitor/compare/3.5.1...3.6.0) (2022-06-17)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04)\n\n### Bug Fixes\n\n- **core:** Make cordova bridge use Promise instead of setTimeout ([#5586](https://github.com/ionic-team/capacitor/issues/5586)) ([f35d96b](https://github.com/ionic-team/capacitor/commit/f35d96b185f5890600a64b78e6bf939c336cbb2d))\n\n# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.4.0](https://github.com/ionic-team/capacitor/compare/3.3.4...3.4.0) (2022-01-19)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.3.4](https://github.com/ionic-team/capacitor/compare/3.3.3...3.3.4) (2022-01-05)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.3.3](https://github.com/ionic-team/capacitor/compare/3.3.2...3.3.3) (2021-12-08)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.3.2](https://github.com/ionic-team/capacitor/compare/3.3.1...3.3.2) (2021-11-17)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.3.1](https://github.com/ionic-team/capacitor/compare/3.3.0...3.3.1) (2021-11-05)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.3.0](https://github.com/ionic-team/capacitor/compare/3.2.5...3.3.0) (2021-11-03)\n\n### Bug Fixes\n\n- **core:** avoid crash on logging circular objects ([#5186](https://github.com/ionic-team/capacitor/issues/5186)) ([1451ec8](https://github.com/ionic-team/capacitor/commit/1451ec850a9ef73267a032638e73f1fc440647b9))\n\n## [3.2.5](https://github.com/ionic-team/capacitor/compare/3.2.4...3.2.5) (2021-10-13)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.2.4](https://github.com/ionic-team/capacitor/compare/3.2.3...3.2.4) (2021-09-27)\n\n### Bug Fixes\n\n- **ios:** show correct line number on console logs ([#5073](https://github.com/ionic-team/capacitor/issues/5073)) ([ec41e74](https://github.com/ionic-team/capacitor/commit/ec41e743aa4ba81e791ad446fac461b7f43b46ed))\n\n## [3.2.3](https://github.com/ionic-team/capacitor/compare/3.2.2...3.2.3) (2021-09-15)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.2.2](https://github.com/ionic-team/capacitor/compare/3.2.1...3.2.2) (2021-09-02)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.2.1](https://github.com/ionic-team/capacitor/compare/3.2.0...3.2.1) (2021-09-01)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.2.0](https://github.com/ionic-team/capacitor/compare/3.1.2...3.2.0) (2021-08-18)\n\n### Features\n\n- **core:** implement CapacitorCustomPlatform for 3rd party platforms ([#4771](https://github.com/ionic-team/capacitor/issues/4771)) ([12c6294](https://github.com/ionic-team/capacitor/commit/12c6294b9eb82976b1322f00da9ba5a6004f7977))\n\n## [3.1.2](https://github.com/ionic-team/capacitor/compare/3.1.1...3.1.2) (2021-07-21)\n\n### Bug Fixes\n\n- **core:** handle toJSON() in plugin objects ([#4823](https://github.com/ionic-team/capacitor/issues/4823)) ([0479822](https://github.com/ionic-team/capacitor/commit/04798221666437408f22947253a18ccb4f9e409e))\n- **core:** Modify safeStringify to allow multiple null values ([#4853](https://github.com/ionic-team/capacitor/issues/4853)) ([854539b](https://github.com/ionic-team/capacitor/commit/854539b62a658e484954edbe38b25eea1b0b6f10))\n\n## [3.1.1](https://github.com/ionic-team/capacitor/compare/3.1.0...3.1.1) (2021-07-07)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.1.0](https://github.com/ionic-team/capacitor/compare/3.0.2...3.1.0) (2021-07-07)\n\n**Note:** Version bump only for package @capacitor/core\n\n## [3.0.2](https://github.com/ionic-team/capacitor/compare/3.0.1...3.0.2) (2021-06-23)\n\n### Bug Fixes\n\n- **core:** cordova events not firing ([#4712](https://github.com/ionic-team/capacitor/issues/4712)) ([ca4e3b6](https://github.com/ionic-team/capacitor/commit/ca4e3b62dba6a40e593a1404ba2fe2b416a4ac14))\n\n## [3.0.1](https://github.com/ionic-team/capacitor/compare/3.0.0...3.0.1) (2021-06-09)\n\n### Bug Fixes\n\n- Make isPluginAvailable available on bridge ([#4589](https://github.com/ionic-team/capacitor/issues/4589)) ([151e7a8](https://github.com/ionic-team/capacitor/commit/151e7a899d9646dbd5625a2539fd3f2297349bc5))\n\n# [3.0.0](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.4...3.0.0) (2021-05-18)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-rc.4](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.3...3.0.0-rc.4) (2021-05-18)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.2...3.0.0-rc.3) (2021-05-11)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.1...3.0.0-rc.2) (2021-05-07)\n\n### Bug Fixes\n\n- **bridge:** Fix type errors with new Platforms API ([#4524](https://github.com/ionic-team/capacitor/issues/4524)) ([7bbaea8](https://github.com/ionic-team/capacitor/commit/7bbaea85494c53a950abab40bb77f37087e22abe))\n- **bridge:** Safely JSON.Stringify circular json on log ([#4507](https://github.com/ionic-team/capacitor/issues/4507)) ([e4c8fe4](https://github.com/ionic-team/capacitor/commit/e4c8fe41ec3992df5c20e4d0d3b69240ce672e44)), closes [#4506](https://github.com/ionic-team/capacitor/issues/4506)\n\n### Features\n\n- **core:** platforms api ([#4255](https://github.com/ionic-team/capacitor/issues/4255)) ([7d62713](https://github.com/ionic-team/capacitor/commit/7d6271369cb15eeab07c0bc7f606de6447a17cd4))\n\n# [3.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.0...3.0.0-rc.1) (2021-04-29)\n\n### Bug Fixes\n\n- **core:** Call native implementation before web implementation ([#4493](https://github.com/ionic-team/capacitor/issues/4493)) ([febd606](https://github.com/ionic-team/capacitor/commit/febd60617ab60a3b34132f68f212e9a867d1b434))\n- **core:** Use web listener if there is no native implementation ([#4488](https://github.com/ionic-team/capacitor/issues/4488)) ([196d843](https://github.com/ionic-team/capacitor/commit/196d843a3c9442c5dc6cf61bfe3494fa399dec4f))\n\n### Features\n\n- Unify logging behavior across environments ([#4416](https://github.com/ionic-team/capacitor/issues/4416)) ([bae0f3d](https://github.com/ionic-team/capacitor/commit/bae0f3d2cee84978636d0f589bc7e2f745671baf))\n\n# [3.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.6...3.0.0-rc.0) (2021-03-10)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-beta.6](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.5...3.0.0-beta.6) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-beta.5](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.4...3.0.0-beta.5) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.2...3.0.0-beta.3) (2021-02-18)\n\n### Bug Fixes\n\n- **core:** do not add window.cordova on web apps ([#4214](https://github.com/ionic-team/capacitor/issues/4214)) ([6d673ef](https://github.com/ionic-team/capacitor/commit/6d673ef7076f00c37eac0f801c4c487415df6d4d))\n\n# [3.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.1...3.0.0-beta.2) (2021-02-08)\n\n### Bug Fixes\n\n- **core:** handle js.error messages to fix window error handler ([#4124](https://github.com/ionic-team/capacitor/issues/4124)) ([c0deb1d](https://github.com/ionic-team/capacitor/commit/c0deb1de349f5631af08eecbffc0ea4dea97c60d))\n- address bug in `isPluginAvailable()` for web and native ([#4114](https://github.com/ionic-team/capacitor/issues/4114)) ([2fbd954](https://github.com/ionic-team/capacitor/commit/2fbd95465a321b8f4c50d4daf22a63d8043cee9b))\n- **core:** fix another $$typeof issue ([#4113](https://github.com/ionic-team/capacitor/issues/4113)) ([4cbae41](https://github.com/ionic-team/capacitor/commit/4cbae41908670ab843bea5850da7a2cf1082afdb))\n\n# [3.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.0...3.0.0-beta.1) (2021-01-14)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.14...3.0.0-beta.0) (2021-01-13)\n\n### Features\n\n- **core:** add commonjs output format ([#4064](https://github.com/ionic-team/capacitor/issues/4064)) ([74b7be8](https://github.com/ionic-team/capacitor/commit/74b7be89ef1bbf13ccd103410037cfe81c8fc124))\n\n# [3.0.0-alpha.12](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.11...3.0.0-alpha.12) (2021-01-08)\n\n### Bug Fixes\n\n- **core:** fix $$typeof() not implemented error ([#4013](https://github.com/ionic-team/capacitor/issues/4013)) ([c7f80b5](https://github.com/ionic-team/capacitor/commit/c7f80b577c1de60cd0a105f3aaf0d1c314f3150d))\n\n# [3.0.0-alpha.9](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.8...3.0.0-alpha.9) (2020-12-15)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-alpha.8](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.7...3.0.0-alpha.8) (2020-12-15)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-alpha.7](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.6...3.0.0-alpha.7) (2020-12-02)\n\n### Bug Fixes\n\n- **core:** export PermissionState ([#3775](https://github.com/ionic-team/capacitor/issues/3775)) ([2d5ac96](https://github.com/ionic-team/capacitor/commit/2d5ac963d131a704628f8a421be8429b9f63cf61))\n\n# [3.0.0-alpha.6](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.5...3.0.0-alpha.6) (2020-10-30)\n\n### Features\n\n- improve permissions ([eec61a6](https://github.com/ionic-team/capacitor/commit/eec61a6d8d8edfe94aea1a361787d1e6c736e20d))\n- unified errors and error codes ([#3673](https://github.com/ionic-team/capacitor/issues/3673)) ([f9e0803](https://github.com/ionic-team/capacitor/commit/f9e08038aa88f7453e8235f380d2767a12a7a073))\n\n# [3.0.0-alpha.5](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.4...3.0.0-alpha.5) (2020-10-06)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.3...3.0.0-alpha.4) (2020-09-23)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.2...3.0.0-alpha.3) (2020-09-15)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.1...3.0.0-alpha.2) (2020-08-31)\n\n**Note:** Version bump only for package @capacitor/core\n\n# [3.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/2.4.0...3.0.0-alpha.1) (2020-08-21)\n\n### Bug Fixes\n\n- **core:** provide mock implementation for unimplemented platforms ([#3352](https://github.com/ionic-team/capacitor/issues/3352)) ([befe230](https://github.com/ionic-team/capacitor/commit/befe2300435dbd54b22882fb6586c722f5ef466d))\n- **core:** use more explicit result for Browser plugin events ([#3349](https://github.com/ionic-team/capacitor/issues/3349)) ([75f99d4](https://github.com/ionic-team/capacitor/commit/75f99d4de62a6afb2da0ff876ed3b0d351040184))\n- **core:** use own type for backButton event result ([#3348](https://github.com/ionic-team/capacitor/issues/3348)) ([05d0e45](https://github.com/ionic-team/capacitor/commit/05d0e457eb69d5d39c8bb1d0117bc3d31afdca93))\n\n### Features\n\n- **core:** add unsupported browser exception ([#3389](https://github.com/ionic-team/capacitor/issues/3389)) ([c51e8f8](https://github.com/ionic-team/capacitor/commit/c51e8f8960c795421b35ad1fdd1cd6afbd7a7dfc))\n\n# [3.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/2.3.0...3.0.0-alpha.0) (2020-07-23)\n\n### Features\n\n- **core:** add `registerPlugin` for importing from plugin packages ([#3305](https://github.com/ionic-team/capacitor/issues/3305)) ([95475cc](https://github.com/ionic-team/capacitor/commit/95475cceb4cbd5be2cc7e18f2cf3045eb6c6f7fd))\n"
  },
  {
    "path": "core/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017-present Drifty Co.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "core/README.md",
    "content": "# Capacitor Core JS\n\nSee the [Capacitor website](https://capacitorjs.com) for more information.\n"
  },
  {
    "path": "core/cookies.md",
    "content": "# CapacitorCookies\n\nThe Capacitor Cookies API provides native cookie support via patching `document.cookie` to use native libraries. It also provides methods for modifying cookies at a specific url. This plugin is bundled with `@capacitor/core`.\n\n## Configuration\n\nBy default, the patching of `document.cookie` to use native libraries is disabled.\nIf you would like to enable this feature, modify the configuration below in the `capacitor.config` file.\n\n| Prop          | Type                 | Description                                                               | Default            |\n| ------------- | -------------------- | ------------------------------------------------------------------------- | ------------------ |\n| **`enabled`** | <code>boolean</code> | Enable the patching of `document.cookie` to use native libraries instead. | <code>false</code> |\n\n### Example Configuration\n\nIn `capacitor.config.json`:\n\n```json\n{\n  \"plugins\": {\n    \"CapacitorCookies\": {\n      \"enabled\": true\n    }\n  }\n}\n```\n\nIn `capacitor.config.ts`:\n\n```ts\nimport { CapacitorConfig } from '@capacitor/cli';\n\nconst config: CapacitorConfig = {\n  plugins: {\n    CapacitorCookies: {\n      enabled: true,\n    },\n  },\n};\n\nexport default config;\n```\n\n## Example\n\n```typescript\nimport { CapacitorCookies } from '@capacitor/core';\n\nconst getCookies = () => {\n  return document.cookie;\n};\n\nconst setCookie = () => {\n  document.cookie = key + '=' + value;\n};\n\nconst setCapacitorCookie = async () => {\n  await CapacitorCookies.setCookie({\n    url: 'http://example.com',\n    key: 'language',\n    value: 'en',\n  });\n};\n\nconst deleteCookie = async () => {\n  await CapacitorCookies.deleteCookie({\n    url: 'https://example.com',\n    key: 'language',\n  });\n};\n\nconst clearCookiesOnUrl = async () => {\n  await CapacitorCookies.clearCookies({\n    url: 'https://example.com',\n  });\n};\n\nconst clearAllCookies = async () => {\n  await CapacitorCookies.clearAllCookies();\n};\n```\n\n## Third Party Cookies on iOS\n\nAs of iOS 14, you cannot use 3rd party cookies by default. Add the following lines to your Info.plist file to get better support for cookies on iOS. You can add up to 10 domains.\n\n```xml\n<key>WKAppBoundDomains</key>\n<array>\n  <string>www.mydomain.com</string>\n  <string>api.mydomain.com</string>\n  <string>www.myothercooldomain.com</string>\n</array>\n```\n\n## API\n\n<docgen-index>\n\n* [`getCookies(...)`](#getcookies)\n* [`setCookie(...)`](#setcookie)\n* [`deleteCookie(...)`](#deletecookie)\n* [`clearCookies(...)`](#clearcookies)\n* [`clearAllCookies()`](#clearallcookies)\n* [Interfaces](#interfaces)\n* [Type Aliases](#type-aliases)\n\n</docgen-index>\n\n<docgen-api>\n<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->\n\n### getCookies(...)\n\n```typescript\ngetCookies(options?: GetCookieOptions) => Promise<HttpCookieMap>\n```\n\n| Param         | Type                                                          |\n| ------------- | ------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#getcookieoptions\">GetCookieOptions</a></code> |\n\n**Returns:** <code>Promise&lt;<a href=\"#httpcookiemap\">HttpCookieMap</a>&gt;</code>\n\n--------------------\n\n\n### setCookie(...)\n\n```typescript\nsetCookie(options: SetCookieOptions) => Promise<void>\n```\n\nWrite a cookie to the device.\n\n| Param         | Type                                                          |\n| ------------- | ------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#setcookieoptions\">SetCookieOptions</a></code> |\n\n--------------------\n\n\n### deleteCookie(...)\n\n```typescript\ndeleteCookie(options: DeleteCookieOptions) => Promise<void>\n```\n\nDelete a cookie from the device.\n\n| Param         | Type                                                                |\n| ------------- | ------------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#deletecookieoptions\">DeleteCookieOptions</a></code> |\n\n--------------------\n\n\n### clearCookies(...)\n\n```typescript\nclearCookies(options: ClearCookieOptions) => Promise<void>\n```\n\nClear cookies from the device at a given URL.\n\n| Param         | Type                                                              |\n| ------------- | ----------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#clearcookieoptions\">ClearCookieOptions</a></code> |\n\n--------------------\n\n\n### clearAllCookies()\n\n```typescript\nclearAllCookies() => Promise<void>\n```\n\nClear all cookies on the device.\n\n--------------------\n\n\n### Interfaces\n\n\n#### HttpCookieMap\n\n\n#### HttpCookie\n\n| Prop        | Type                | Description              |\n| ----------- | ------------------- | ------------------------ |\n| **`url`**   | <code>string</code> | The URL of the cookie.   |\n| **`key`**   | <code>string</code> | The key of the cookie.   |\n| **`value`** | <code>string</code> | The value of the cookie. |\n\n\n#### HttpCookieExtras\n\n| Prop          | Type                | Description                      |\n| ------------- | ------------------- | -------------------------------- |\n| **`path`**    | <code>string</code> | The path to write the cookie to. |\n| **`expires`** | <code>string</code> | The date to expire the cookie.   |\n\n\n### Type Aliases\n\n\n#### GetCookieOptions\n\n<code><a href=\"#omit\">Omit</a>&lt;<a href=\"#httpcookie\">HttpCookie</a>, 'key' | 'value'&gt;</code>\n\n\n#### Omit\n\nConstruct a type with the properties of T except for those in type K.\n\n<code><a href=\"#pick\">Pick</a>&lt;T, <a href=\"#exclude\">Exclude</a>&lt;keyof T, K&gt;&gt;</code>\n\n\n#### Pick\n\nFrom T, pick a set of properties whose keys are in the union K\n\n<code>{\r [P in K]: T[P];\r }</code>\n\n\n#### Exclude\n\n<a href=\"#exclude\">Exclude</a> from T those types that are assignable to U\n\n<code>T extends U ? never : T</code>\n\n\n#### SetCookieOptions\n\n<code><a href=\"#httpcookie\">HttpCookie</a> & <a href=\"#httpcookieextras\">HttpCookieExtras</a></code>\n\n\n#### DeleteCookieOptions\n\n<code><a href=\"#omit\">Omit</a>&lt;<a href=\"#httpcookie\">HttpCookie</a>, 'value'&gt;</code>\n\n\n#### ClearCookieOptions\n\n<code><a href=\"#omit\">Omit</a>&lt;<a href=\"#httpcookie\">HttpCookie</a>, 'key' | 'value'&gt;</code>\n\n</docgen-api>"
  },
  {
    "path": "core/cordova.js",
    "content": "// Platform: Capacitor\n/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n \n     http://www.apache.org/licenses/LICENSE-2.0\n \n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n*/\n(function () {\n  var PLATFORM_VERSION_BUILD_LABEL = '1.0.0';\n  // file: src/scripts/require.js\n\n  /* jshint -W079 */\n  /* jshint -W020 */\n\n  var require;\n  var define;\n\n  (function () {\n    var modules = {};\n    // Stack of moduleIds currently being built.\n    var requireStack = [];\n    // Map of module ID -> index into requireStack of modules currently being built.\n    var inProgressModules = {};\n    var SEPARATOR = '.';\n\n    function build(module) {\n      var factory = module.factory;\n      var localRequire = function (id) {\n        var resultantId = id;\n        // Its a relative path, so lop off the last portion and add the id (minus \"./\")\n        if (id.charAt(0) === '.') {\n          resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2);\n        }\n        return require(resultantId);\n      };\n      module.exports = {};\n      delete module.factory;\n      factory(localRequire, module.exports, module);\n      return module.exports;\n    }\n\n    require = function (id) {\n      if (!modules[id]) {\n        throw 'module ' + id + ' not found';\n      } else if (id in inProgressModules) {\n        var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;\n        throw 'Cycle in require graph: ' + cycle;\n      }\n      if (modules[id].factory) {\n        try {\n          inProgressModules[id] = requireStack.length;\n          requireStack.push(id);\n          return build(modules[id]);\n        } finally {\n          delete inProgressModules[id];\n          requireStack.pop();\n        }\n      }\n      return modules[id].exports;\n    };\n\n    define = function (id, factory) {\n      if (modules[id]) {\n        throw 'module ' + id + ' already defined';\n      }\n\n      modules[id] = {\n        id: id,\n        factory: factory,\n      };\n    };\n\n    define.remove = function (id) {\n      delete modules[id];\n    };\n\n    define.moduleMap = modules;\n  })();\n\n  // Export for use in node\n  if (typeof module === 'object' && typeof require === 'function') {\n    module.exports.require = require;\n    module.exports.define = define;\n  }\n\n  // file: src/cordova.js\n  define('cordova', function (require, exports, module) {\n    var channel = require('cordova/channel');\n    var platform = require('cordova/platform');\n\n    /**\n     * Intercept calls to addEventListener + removeEventListener and handle deviceready,\n     * resume, and pause events.\n     */\n    var m_document_addEventListener = document.addEventListener;\n    var m_document_removeEventListener = document.removeEventListener;\n    var m_window_addEventListener = window.addEventListener;\n    var m_window_removeEventListener = window.removeEventListener;\n\n    /**\n     * Houses custom event handlers to intercept on document + window event listeners.\n     */\n    var documentEventHandlers = {};\n    var windowEventHandlers = {};\n\n    document.addEventListener = function (evt, handler, capture) {\n      var e = evt.toLowerCase();\n      if (typeof documentEventHandlers[e] !== 'undefined') {\n        documentEventHandlers[e].subscribe(handler);\n      } else {\n        m_document_addEventListener.call(document, evt, handler, capture);\n      }\n    };\n\n    window.addEventListener = function (evt, handler, capture) {\n      var e = evt.toLowerCase();\n      if (typeof windowEventHandlers[e] !== 'undefined') {\n        windowEventHandlers[e].subscribe(handler);\n      } else {\n        m_window_addEventListener.call(window, evt, handler, capture);\n      }\n    };\n\n    document.removeEventListener = function (evt, handler, capture) {\n      var e = evt.toLowerCase();\n      // If unsubscribing from an event that is handled by a plugin\n      if (typeof documentEventHandlers[e] !== 'undefined') {\n        documentEventHandlers[e].unsubscribe(handler);\n      } else {\n        m_document_removeEventListener.call(document, evt, handler, capture);\n      }\n    };\n\n    window.removeEventListener = function (evt, handler, capture) {\n      var e = evt.toLowerCase();\n      // If unsubscribing from an event that is handled by a plugin\n      if (typeof windowEventHandlers[e] !== 'undefined') {\n        windowEventHandlers[e].unsubscribe(handler);\n      } else {\n        m_window_removeEventListener.call(window, evt, handler, capture);\n      }\n    };\n\n    /* eslint-disable no-undef */\n    var cordova = {\n      define: define,\n      require: require,\n      version: PLATFORM_VERSION_BUILD_LABEL,\n      platformVersion: PLATFORM_VERSION_BUILD_LABEL,\n      platformId: platform.id,\n\n      /* eslint-enable no-undef */\n\n      /**\n       * Methods to add/remove your own addEventListener hijacking on document + window.\n       */\n      addWindowEventHandler: function (event) {\n        return (windowEventHandlers[event] = channel.create(event));\n      },\n      addStickyDocumentEventHandler: function (event) {\n        return (documentEventHandlers[event] = channel.createSticky(event));\n      },\n      addDocumentEventHandler: function (event) {\n        return (documentEventHandlers[event] = channel.create(event));\n      },\n      removeWindowEventHandler: function (event) {\n        delete windowEventHandlers[event];\n      },\n      removeDocumentEventHandler: function (event) {\n        delete documentEventHandlers[event];\n      },\n      /**\n       * Retrieve original event handlers that were replaced by Cordova\n       *\n       * @return object\n       */\n      getOriginalHandlers: function () {\n        return {\n          document: {\n            addEventListener: m_document_addEventListener,\n            removeEventListener: m_document_removeEventListener,\n          },\n          window: {\n            addEventListener: m_window_addEventListener,\n            removeEventListener: m_window_removeEventListener,\n          },\n        };\n      },\n      /**\n       * Method to fire event from native code\n       * bNoDetach is required for events which cause an exception which needs to be caught in native code\n       */\n      fireDocumentEvent: function (type, data, bNoDetach) {\n        var evt = window.Capacitor.createEvent(type, data);\n        if (typeof documentEventHandlers[type] !== 'undefined') {\n          if (bNoDetach) {\n            documentEventHandlers[type].fire(evt);\n          } else {\n            setTimeout(function () {\n              // Fire deviceready on listeners that were registered before cordova.js was loaded.\n              if (type === 'deviceready') {\n                document.dispatchEvent(evt);\n              }\n              documentEventHandlers[type].fire(evt);\n            }, 0);\n          }\n        } else {\n          document.dispatchEvent(evt);\n        }\n      },\n      fireWindowEvent: function (type, data) {\n        var evt = window.Capacitor.createEvent(type, data);\n        if (typeof windowEventHandlers[type] !== 'undefined') {\n          setTimeout(function () {\n            windowEventHandlers[type].fire(evt);\n          }, 0);\n        } else {\n          window.dispatchEvent(evt);\n        }\n      },\n\n      /**\n       * Plugin callback mechanism.\n       */\n      // Randomize the starting callbackId to avoid collisions after refreshing or navigating.\n      // This way, it's very unlikely that any new callback would get the same callbackId as an old callback.\n      callbackId: Math.floor(Math.random() * 2000000000),\n      callbacks: {},\n      callbackStatus: {\n        NO_RESULT: 0,\n        OK: 1,\n        CLASS_NOT_FOUND_EXCEPTION: 2,\n        ILLEGAL_ACCESS_EXCEPTION: 3,\n        INSTANTIATION_EXCEPTION: 4,\n        MALFORMED_URL_EXCEPTION: 5,\n        IO_EXCEPTION: 6,\n        INVALID_ACTION: 7,\n        JSON_EXCEPTION: 8,\n        ERROR: 9,\n      },\n\n      /**\n       * Called by native code when returning successful result from an action.\n       */\n      callbackSuccess: function (callbackId, args) {\n        cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);\n      },\n\n      /**\n       * Called by native code when returning error result from an action.\n       */\n      callbackError: function (callbackId, args) {\n        // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.\n        // Derive success from status.\n        cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);\n      },\n\n      /**\n       * Called by native code when returning the result from an action.\n       */\n      callbackFromNative: function (callbackId, isSuccess, status, args, keepCallback) {\n        try {\n          var callback = cordova.callbacks[callbackId];\n          if (callback) {\n            if (isSuccess && status === cordova.callbackStatus.OK) {\n              callback.success && callback.success.apply(null, args);\n            } else if (!isSuccess) {\n              callback.fail && callback.fail.apply(null, args);\n            }\n            /*\n                        else\n                            Note, this case is intentionally not caught.\n                            this can happen if isSuccess is true, but callbackStatus is NO_RESULT\n                            which is used to remove a callback from the list without calling the callbacks\n                            typically keepCallback is false in this case\n                        */\n            // Clear callback if not expecting any more results\n            if (!keepCallback) {\n              delete cordova.callbacks[callbackId];\n            }\n          }\n        } catch (err) {\n          var msg = 'Error in ' + (isSuccess ? 'Success' : 'Error') + ' callbackId: ' + callbackId + ' : ' + err;\n          console && console.log && console.log(msg);\n          cordova.fireWindowEvent('cordovacallbackerror', { message: msg });\n          throw err;\n        }\n      },\n      addConstructor: function (func) {\n        channel.onCordovaReady.subscribe(function () {\n          try {\n            func();\n          } catch (e) {\n            console.log('Failed to run constructor: ' + e);\n          }\n        });\n      },\n    };\n\n    module.exports = cordova;\n  });\n\n  // file: src/common/argscheck.js\n  define('cordova/argscheck', function (require, exports, module) {\n    var utils = require('cordova/utils');\n\n    var moduleExports = module.exports;\n\n    var typeMap = {\n      A: 'Array',\n      D: 'Date',\n      N: 'Number',\n      S: 'String',\n      F: 'Function',\n      O: 'Object',\n    };\n\n    function extractParamName(callee, argIndex) {\n      return /.*?\\((.*?)\\)/.exec(callee)[1].split(', ')[argIndex];\n    }\n\n    function checkArgs(spec, functionName, args, opt_callee) {\n      if (!moduleExports.enableChecks) {\n        return;\n      }\n      var errMsg = null;\n      var typeName;\n      for (var i = 0; i < spec.length; ++i) {\n        var c = spec.charAt(i);\n        var cUpper = c.toUpperCase();\n        var arg = args[i];\n        // Asterix means allow anything.\n        if (c === '*') {\n          continue;\n        }\n        typeName = utils.typeName(arg);\n        if ((arg === null || arg === undefined) && c === cUpper) {\n          continue;\n        }\n        if (typeName !== typeMap[cUpper]) {\n          errMsg = 'Expected ' + typeMap[cUpper];\n          break;\n        }\n      }\n      if (errMsg) {\n        errMsg += ', but got ' + typeName + '.';\n        errMsg =\n          'Wrong type for parameter \"' +\n          extractParamName(opt_callee || args.callee, i) +\n          '\" of ' +\n          functionName +\n          ': ' +\n          errMsg;\n        // Don't log when running unit tests.\n        if (typeof jasmine === 'undefined') {\n          console.error(errMsg);\n        }\n        throw TypeError(errMsg);\n      }\n    }\n\n    function getValue(value, defaultValue) {\n      return value === undefined ? defaultValue : value;\n    }\n\n    moduleExports.checkArgs = checkArgs;\n    moduleExports.getValue = getValue;\n    moduleExports.enableChecks = true;\n  });\n\n  // file: src/common/base64.js\n  define('cordova/base64', function (require, exports, module) {\n    var base64 = exports;\n\n    base64.fromArrayBuffer = function (arrayBuffer) {\n      var array = new Uint8Array(arrayBuffer);\n      return uint8ToBase64(array);\n    };\n\n    base64.toArrayBuffer = function (str) {\n      var decodedStr = typeof atob !== 'undefined' ? atob(str) : Buffer.from(str, 'base64').toString('binary'); // eslint-disable-line no-undef\n      var arrayBuffer = new ArrayBuffer(decodedStr.length);\n      var array = new Uint8Array(arrayBuffer);\n      for (var i = 0, len = decodedStr.length; i < len; i++) {\n        array[i] = decodedStr.charCodeAt(i);\n      }\n      return arrayBuffer;\n    };\n\n    // ------------------------------------------------------------------------------\n\n    /* This code is based on the performance tests at http://jsperf.com/b64tests\n     * This 12-bit-at-a-time algorithm was the best performing version on all\n     * platforms tested.\n     */\n\n    var b64_6bit = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n    var b64_12bit;\n\n    var b64_12bitTable = function () {\n      b64_12bit = [];\n      for (var i = 0; i < 64; i++) {\n        for (var j = 0; j < 64; j++) {\n          b64_12bit[i * 64 + j] = b64_6bit[i] + b64_6bit[j];\n        }\n      }\n      b64_12bitTable = function () {\n        return b64_12bit;\n      };\n      return b64_12bit;\n    };\n\n    function uint8ToBase64(rawData) {\n      var numBytes = rawData.byteLength;\n      var output = '';\n      var segment;\n      var table = b64_12bitTable();\n      for (var i = 0; i < numBytes - 2; i += 3) {\n        segment = (rawData[i] << 16) + (rawData[i + 1] << 8) + rawData[i + 2];\n        output += table[segment >> 12];\n        output += table[segment & 0xfff];\n      }\n      if (numBytes - i === 2) {\n        segment = (rawData[i] << 16) + (rawData[i + 1] << 8);\n        output += table[segment >> 12];\n        output += b64_6bit[(segment & 0xfff) >> 6];\n        output += '=';\n      } else if (numBytes - i === 1) {\n        segment = rawData[i] << 16;\n        output += table[segment >> 12];\n        output += '==';\n      }\n      return output;\n    }\n  });\n\n  // file: src/common/builder.js\n  define('cordova/builder', function (require, exports, module) {\n    var utils = require('cordova/utils');\n\n    function each(objects, func, context) {\n      for (var prop in objects) {\n        if (objects.hasOwnProperty(prop)) {\n          func.apply(context, [objects[prop], prop]);\n        }\n      }\n    }\n\n    function clobber(obj, key, value) {\n      exports.replaceHookForTesting(obj, key);\n      var needsProperty = false;\n      try {\n        obj[key] = value;\n      } catch (e) {\n        needsProperty = true;\n      }\n      // Getters can only be overridden by getters.\n      if (needsProperty || obj[key] !== value) {\n        utils.defineGetter(obj, key, function () {\n          return value;\n        });\n      }\n    }\n\n    function assignOrWrapInDeprecateGetter(obj, key, value, message) {\n      if (message) {\n        utils.defineGetter(obj, key, function () {\n          console.log(message);\n          delete obj[key];\n          clobber(obj, key, value);\n          return value;\n        });\n      } else {\n        clobber(obj, key, value);\n      }\n    }\n\n    function include(parent, objects, clobber, merge) {\n      each(objects, function (obj, key) {\n        try {\n          var result = obj.path ? require(obj.path) : {};\n\n          if (clobber) {\n            // Clobber if it doesn't exist.\n            if (typeof parent[key] === 'undefined') {\n              assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);\n            } else if (typeof obj.path !== 'undefined') {\n              // If merging, merge properties onto parent, otherwise, clobber.\n              if (merge) {\n                recursiveMerge(parent[key], result);\n              } else {\n                assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);\n              }\n            }\n            result = parent[key];\n          } else {\n            // Overwrite if not currently defined.\n            if (typeof parent[key] === 'undefined') {\n              assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);\n            } else {\n              // Set result to what already exists, so we can build children into it if they exist.\n              result = parent[key];\n            }\n          }\n\n          if (obj.children) {\n            include(result, obj.children, clobber, merge);\n          }\n        } catch (e) {\n          utils.alert('Exception building Cordova JS globals: ' + e + ' for key \"' + key + '\"');\n        }\n      });\n    }\n\n    /**\n     * Merge properties from one object onto another recursively.  Properties from\n     * the src object will overwrite existing target property.\n     *\n     * @param target Object to merge properties into.\n     * @param src Object to merge properties from.\n     */\n    function recursiveMerge(target, src) {\n      for (var prop in src) {\n        if (src.hasOwnProperty(prop)) {\n          if (target.prototype && target.prototype.constructor === target) {\n            // If the target object is a constructor override off prototype.\n            clobber(target.prototype, prop, src[prop]);\n          } else {\n            if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {\n              recursiveMerge(target[prop], src[prop]);\n            } else {\n              clobber(target, prop, src[prop]);\n            }\n          }\n        }\n      }\n    }\n\n    exports.buildIntoButDoNotClobber = function (objects, target) {\n      include(target, objects, false, false);\n    };\n    exports.buildIntoAndClobber = function (objects, target) {\n      include(target, objects, true, false);\n    };\n    exports.buildIntoAndMerge = function (objects, target) {\n      include(target, objects, true, true);\n    };\n    exports.recursiveMerge = recursiveMerge;\n    exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;\n    exports.replaceHookForTesting = function () {};\n  });\n\n  // file: src/common/channel.js\n  define('cordova/channel', function (require, exports, module) {\n    var utils = require('cordova/utils');\n    var nextGuid = 1;\n\n    /**\n     * Custom pub-sub \"channel\" that can have functions subscribed to it\n     * This object is used to define and control firing of events for\n     * cordova initialization, as well as for custom events thereafter.\n     *\n     * The order of events during page load and Cordova startup is as follows:\n     *\n     * onDOMContentLoaded*         Internal event that is received when the web page is loaded and parsed.\n     * onNativeReady*              Internal event that indicates the Cordova native side is ready.\n     * onCordovaReady*             Internal event fired when all Cordova JavaScript objects have been created.\n     * onDeviceReady*              User event fired to indicate that Cordova is ready\n     * onResume                    User event fired to indicate a start/resume lifecycle event\n     * onPause                     User event fired to indicate a pause lifecycle event\n     *\n     * The events marked with an * are sticky. Once they have fired, they will stay in the fired state.\n     * All listeners that subscribe after the event is fired will be executed right away.\n     *\n     * The only Cordova events that user code should register for are:\n     *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript\n     *      pause                 App has moved to background\n     *      resume                App has returned to foreground\n     *\n     * Listeners can be registered as:\n     *      document.addEventListener(\"deviceready\", myDeviceReadyListener, false);\n     *      document.addEventListener(\"resume\", myResumeListener, false);\n     *      document.addEventListener(\"pause\", myPauseListener, false);\n     *\n     * The DOM lifecycle events should be used for saving and restoring state\n     *      window.onload\n     *      window.onunload\n     *\n     */\n\n    /**\n     * Channel\n     * @constructor\n     * @param type  String the channel name\n     */\n    var Channel = function (type, sticky) {\n      this.type = type;\n      // Map of guid -> function.\n      this.handlers = {};\n      // 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.\n      this.state = sticky ? 1 : 0;\n      // Used in sticky mode to remember args passed to fire().\n      this.fireArgs = null;\n      // Used by onHasSubscribersChange to know if there are any listeners.\n      this.numHandlers = 0;\n      // Function that is called when the first listener is subscribed, or when\n      // the last listener is unsubscribed.\n      this.onHasSubscribersChange = null;\n    };\n    var channel = {\n      /**\n       * Calls the provided function only after all of the channels specified\n       * have been fired. All channels must be sticky channels.\n       */\n      join: function (h, c) {\n        var len = c.length;\n        var i = len;\n        var f = function () {\n          if (!--i) h();\n        };\n        for (var j = 0; j < len; j++) {\n          if (c[j].state === 0) {\n            throw Error('Can only use join with sticky channels.');\n          }\n          c[j].subscribe(f);\n        }\n        if (!len) h();\n      },\n      /* eslint-disable no-return-assign */\n      create: function (type) {\n        return (channel[type] = new Channel(type, false));\n      },\n      createSticky: function (type) {\n        return (channel[type] = new Channel(type, true));\n      },\n      /* eslint-enable no-return-assign */\n      /**\n       * cordova Channels that must fire before \"deviceready\" is fired.\n       */\n      deviceReadyChannelsArray: [],\n      deviceReadyChannelsMap: {},\n\n      /**\n       * Indicate that a feature needs to be initialized before it is ready to be used.\n       * This holds up Cordova's \"deviceready\" event until the feature has been initialized\n       * and Cordova.initComplete(feature) is called.\n       *\n       * @param feature {String}     The unique feature name\n       */\n      waitForInitialization: function (feature) {\n        if (feature) {\n          var c = channel[feature] || this.createSticky(feature);\n          this.deviceReadyChannelsMap[feature] = c;\n          this.deviceReadyChannelsArray.push(c);\n        }\n      },\n\n      /**\n       * Indicate that initialization code has completed and the feature is ready to be used.\n       *\n       * @param feature {String}     The unique feature name\n       */\n      initializationComplete: function (feature) {\n        var c = this.deviceReadyChannelsMap[feature];\n        if (c) {\n          c.fire();\n        }\n      },\n    };\n\n    function checkSubscriptionArgument(argument) {\n      if (typeof argument !== 'function' && typeof argument.handleEvent !== 'function') {\n        throw new Error(\n          'Must provide a function or an EventListener object ' + 'implementing the handleEvent interface.',\n        );\n      }\n    }\n\n    /**\n     * Subscribes the given function to the channel. Any time that\n     * Channel.fire is called so too will the function.\n     * Optionally specify an execution context for the function\n     * and a guid that can be used to stop subscribing to the channel.\n     * Returns the guid.\n     */\n    Channel.prototype.subscribe = function (eventListenerOrFunction, eventListener) {\n      checkSubscriptionArgument(eventListenerOrFunction);\n      var handleEvent, guid;\n\n      if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {\n        // Received an EventListener object implementing the handleEvent interface\n        handleEvent = eventListenerOrFunction.handleEvent;\n        eventListener = eventListenerOrFunction;\n      } else {\n        // Received a function to handle event\n        handleEvent = eventListenerOrFunction;\n      }\n\n      if (this.state === 2) {\n        handleEvent.apply(eventListener || this, this.fireArgs);\n        return;\n      }\n\n      guid = eventListenerOrFunction.observer_guid;\n      if (typeof eventListener === 'object') {\n        handleEvent = utils.close(eventListener, handleEvent);\n      }\n\n      if (!guid) {\n        // First time any channel has seen this subscriber\n        guid = '' + nextGuid++;\n      }\n      handleEvent.observer_guid = guid;\n      eventListenerOrFunction.observer_guid = guid;\n\n      // Don't add the same handler more than once.\n      if (!this.handlers[guid]) {\n        this.handlers[guid] = handleEvent;\n        this.numHandlers++;\n        if (this.numHandlers === 1) {\n          this.onHasSubscribersChange && this.onHasSubscribersChange();\n        }\n      }\n    };\n\n    /**\n     * Unsubscribes the function with the given guid from the channel.\n     */\n    Channel.prototype.unsubscribe = function (eventListenerOrFunction) {\n      checkSubscriptionArgument(eventListenerOrFunction);\n      var handleEvent, guid, handler;\n\n      if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {\n        // Received an EventListener object implementing the handleEvent interface\n        handleEvent = eventListenerOrFunction.handleEvent;\n      } else {\n        // Received a function to handle event\n        handleEvent = eventListenerOrFunction;\n      }\n\n      guid = handleEvent.observer_guid;\n      handler = this.handlers[guid];\n      if (handler) {\n        delete this.handlers[guid];\n        this.numHandlers--;\n        if (this.numHandlers === 0) {\n          this.onHasSubscribersChange && this.onHasSubscribersChange();\n        }\n      }\n    };\n\n    /**\n     * Calls all functions subscribed to this channel.\n     */\n    Channel.prototype.fire = function (e) {\n      var fail = false; // eslint-disable-line no-unused-vars\n      var fireArgs = Array.prototype.slice.call(arguments);\n      // Apply stickiness.\n      if (this.state === 1) {\n        this.state = 2;\n        this.fireArgs = fireArgs;\n      }\n      if (this.numHandlers) {\n        // Copy the values first so that it is safe to modify it from within\n        // callbacks.\n        var toCall = [];\n        for (var item in this.handlers) {\n          toCall.push(this.handlers[item]);\n        }\n        for (var i = 0; i < toCall.length; ++i) {\n          toCall[i].apply(this, fireArgs);\n        }\n        if (this.state === 2 && this.numHandlers) {\n          this.numHandlers = 0;\n          this.handlers = {};\n          this.onHasSubscribersChange && this.onHasSubscribersChange();\n        }\n      }\n    };\n\n    // defining them here so they are ready super fast!\n    // DOM event that is received when the web page is loaded and parsed.\n    channel.createSticky('onDOMContentLoaded');\n\n    // Event to indicate the Cordova native side is ready.\n    channel.createSticky('onNativeReady');\n\n    // Event to indicate that all Cordova JavaScript objects have been created\n    // and it's time to run plugin constructors.\n    channel.createSticky('onCordovaReady');\n\n    // Event to indicate that all automatically loaded JS plugins are loaded and ready.\n    // FIXME remove this\n    channel.createSticky('onPluginsReady');\n\n    // Event to indicate that Cordova is ready\n    channel.createSticky('onDeviceReady');\n\n    // Event to indicate a resume lifecycle event\n    channel.create('onResume');\n\n    // Event to indicate a pause lifecycle event\n    channel.create('onPause');\n\n    // Channels that must fire before \"deviceready\" is fired.\n    channel.waitForInitialization('onCordovaReady');\n    channel.waitForInitialization('onDOMContentLoaded');\n\n    module.exports = channel;\n  });\n\n  define('cordova/exec', function (require, exports, module) {\n    /*global require, module, atob, document */\n\n    /**\n     * Creates a gap bridge iframe used to notify the native code about queued\n     * commands.\n     */\n    var cordova = require('cordova'),\n      utils = require('cordova/utils'),\n      base64 = require('cordova/base64'),\n      execIframe,\n      commandQueue = [], // Contains pending JS->Native messages.\n      isInContextOfEvalJs = 0,\n      failSafeTimerId = 0;\n\n    function massageArgsJsToNative(args) {\n      if (window.androidBridge) {\n        for (var i = 0; i < args.length; i++) {\n          if (utils.typeName(args[i]) == 'ArrayBuffer') {\n            args[i] = base64.fromArrayBuffer(args[i]);\n          }\n        }\n        return args;\n      } else {\n        if (!args || utils.typeName(args) !== 'Array') {\n          return args;\n        }\n        var ret = [];\n        args.forEach(function (arg, i) {\n          if (utils.typeName(arg) === 'ArrayBuffer') {\n            ret.push({\n              CDVType: 'ArrayBuffer',\n              data: base64.fromArrayBuffer(arg),\n            });\n          } else {\n            ret.push(arg);\n          }\n        });\n        return ret;\n      }\n    }\n\n    function massageMessageNativeToJs(message) {\n      if (message.CDVType === 'ArrayBuffer') {\n        var stringToArrayBuffer = function (str) {\n          var ret = new Uint8Array(str.length);\n          for (var i = 0; i < str.length; i++) {\n            ret[i] = str.charCodeAt(i);\n          }\n          return ret.buffer;\n        };\n        var base64ToArrayBuffer = function (b64) {\n          return stringToArrayBuffer(atob(b64)); // eslint-disable-line no-undef\n        };\n        message = base64ToArrayBuffer(message.data);\n      }\n      return message;\n    }\n\n    function convertMessageToArgsNativeToJs(message) {\n      var args = [];\n      if (!message || !message.hasOwnProperty('CDVType')) {\n        args.push(message);\n      } else if (message.CDVType === 'MultiPart') {\n        message.messages.forEach(function (e) {\n          args.push(massageMessageNativeToJs(e));\n        });\n      } else {\n        args.push(massageMessageNativeToJs(message));\n      }\n      return args;\n    }\n\n    var capacitorExec = function () {\n      // detect change in bridge, if there is a change, we forward to new bridge\n\n      var successCallback, failCallback, service, action, actionArgs;\n      var callbackId = null;\n      if (typeof arguments[0] !== 'string') {\n        // FORMAT ONE\n        successCallback = arguments[0];\n        failCallback = arguments[1];\n        service = arguments[2];\n        action = arguments[3];\n        actionArgs = arguments[4];\n\n        // Since we need to maintain backwards compatibility, we have to pass\n        // an invalid callbackId even if no callback was provided since plugins\n        // will be expecting it. The Cordova.exec() implementation allocates\n        // an invalid callbackId and passes it even if no callbacks were given.\n        callbackId = 'INVALID';\n      } else {\n        throw new Error(\n          'The old format of this exec call has been removed (deprecated since 2.1). Change to: ' + // eslint-disable-line\n            \"cordova.exec(null, null, 'Service', 'action', [ arg1, arg2 ]);\",\n        );\n      }\n\n      // If actionArgs is not provided, default to an empty array\n      actionArgs = actionArgs || [];\n\n      // Register the callbacks and add the callbackId to the positional\n      // arguments if given.\n      if (successCallback || failCallback) {\n        callbackId = service + cordova.callbackId++;\n        cordova.callbacks[callbackId] = {\n          success: successCallback,\n          fail: failCallback,\n        };\n      }\n\n      // Properly encode ArrayBuffer action arguments\n      actionArgs = massageArgsJsToNative(actionArgs);\n      actionArgs = JSON.parse(JSON.stringify(actionArgs));\n      var command = {\n        type: 'cordova',\n        callbackId: callbackId,\n        service: service,\n        action: action,\n        actionArgs: actionArgs,\n      };\n      if (window.androidBridge) {\n        window.androidBridge.postMessage(JSON.stringify(command));\n      } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.bridge) {\n        window.webkit.messageHandlers.bridge.postMessage(command);\n      }\n    };\n\n    // CB-10530\n    function proxyChanged() {\n      var cexec = cordovaExec();\n\n      return (\n        execProxy !== cexec && capacitorExec !== cexec // proxy objects are different // proxy object is not the current capacitorExec\n      );\n    }\n\n    // CB-10106\n    function handleBridgeChange() {\n      if (proxyChanged()) {\n        var commandString = commandQueue.shift();\n        while (commandString) {\n          var command = JSON.parse(commandString);\n          var callbackId = command[0];\n          var service = command[1];\n          var action = command[2];\n          var actionArgs = command[3];\n          var callbacks = cordova.callbacks[callbackId] || {};\n\n          execProxy(callbacks.success, callbacks.fail, service, action, actionArgs);\n\n          commandString = commandQueue.shift();\n        }\n        return true;\n      }\n\n      return false;\n    }\n\n    function pokeNative() {\n      // CB-5488 - Don't attempt to create iframe before document.body is available.\n      if (!document.body) {\n        setTimeout(pokeNative);\n        return;\n      }\n\n      // Check if they've removed it from the DOM, and put it back if so.\n      if (execIframe && execIframe.contentWindow) {\n        execIframe.contentWindow.location = 'gap://ready';\n      } else {\n        execIframe = document.createElement('iframe');\n        execIframe.style.display = 'none';\n        execIframe.src = 'gap://ready';\n        document.body.appendChild(execIframe);\n      }\n      // Use a timer to protect against iframe being unloaded during the poke (CB-7735).\n      // This makes the bridge ~ 7% slower, but works around the poke getting lost\n      // when the iframe is removed from the DOM.\n      // An onunload listener could be used in the case where the iframe has just been\n      // created, but since unload events fire only once, it doesn't work in the normal\n      // case of iframe reuse (where unload will have already fired due to the attempted\n      // navigation of the page).\n      failSafeTimerId = setTimeout(function () {\n        if (commandQueue.length) {\n          // CB-10106 - flush the queue on bridge change\n          if (!handleBridgeChange()) {\n            pokeNative();\n          }\n        }\n      }, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).\n    }\n\n    capacitorExec.nativeFetchMessages = function () {\n      // Stop listing for window detatch once native side confirms poke.\n      if (failSafeTimerId) {\n        clearTimeout(failSafeTimerId);\n        failSafeTimerId = 0;\n      }\n      // Each entry in commandQueue is a JSON string already.\n      if (!commandQueue.length) {\n        return '';\n      }\n      var json = '[' + commandQueue.join(',') + ']';\n      commandQueue.length = 0;\n      return json;\n    };\n\n    capacitorExec.nativeCallback = function (callbackId, status, message, keepCallback, debug) {\n      var success = status === 0 || status === 1;\n      var args = convertMessageToArgsNativeToJs(message);\n      Promise.resolve().then(function () {\n        cordova.callbackFromNative(callbackId, success, status, args, keepCallback); // eslint-disable-line\n      });\n    };\n\n    // for backwards compatibility\n    capacitorExec.nativeEvalAndFetch = function (func) {\n      try {\n        func();\n      } catch (e) {\n        console.log(e);\n      }\n    };\n\n    // Proxy the exec for bridge changes. See CB-10106\n    function cordovaExec() {\n      var cexec = require('cordova/exec');\n      var cexec_valid =\n        typeof cexec.nativeFetchMessages === 'function' &&\n        typeof cexec.nativeEvalAndFetch === 'function' &&\n        typeof cexec.nativeCallback === 'function';\n      return cexec_valid && execProxy !== cexec ? cexec : capacitorExec;\n    }\n    function execProxy() {\n      cordovaExec().apply(null, arguments);\n    }\n\n    execProxy.nativeFetchMessages = function () {\n      return cordovaExec().nativeFetchMessages.apply(null, arguments);\n    };\n\n    execProxy.nativeEvalAndFetch = function () {\n      return cordovaExec().nativeEvalAndFetch.apply(null, arguments);\n    };\n\n    execProxy.nativeCallback = function () {\n      return cordovaExec().nativeCallback.apply(null, arguments);\n    };\n\n    module.exports = execProxy;\n  });\n\n  // file: src/common/exec/proxy.js\n  define('cordova/exec/proxy', function (require, exports, module) {\n    // internal map of proxy function\n    var CommandProxyMap = {};\n\n    module.exports = {\n      // example: cordova.commandProxy.add(\"Accelerometer\",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...);\n      add: function (id, proxyObj) {\n        console.log('adding proxy for ' + id);\n        CommandProxyMap[id] = proxyObj;\n        return proxyObj;\n      },\n\n      // cordova.commandProxy.remove(\"Accelerometer\");\n      remove: function (id) {\n        var proxy = CommandProxyMap[id];\n        delete CommandProxyMap[id];\n        CommandProxyMap[id] = null;\n        return proxy;\n      },\n\n      get: function (service, action) {\n        return CommandProxyMap[service] ? CommandProxyMap[service][action] : null;\n      },\n    };\n  });\n\n  // file: src/common/init.js\n  define('cordova/init', function (require, exports, module) {\n    var channel = require('cordova/channel');\n    var cordova = require('cordova');\n    var modulemapper = require('cordova/modulemapper');\n    var platform = require('cordova/platform');\n    var pluginloader = require('cordova/pluginloader');\n    var utils = require('cordova/utils');\n\n    var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];\n\n    function logUnfiredChannels(arr) {\n      for (var i = 0; i < arr.length; ++i) {\n        if (arr[i].state !== 2) {\n          console.log('Channel not fired: ' + arr[i].type);\n        }\n      }\n    }\n\n    window.setTimeout(function () {\n      if (channel.onDeviceReady.state !== 2) {\n        console.log('deviceready has not fired after 5 seconds.');\n        logUnfiredChannels(platformInitChannelsArray);\n        logUnfiredChannels(channel.deviceReadyChannelsArray);\n      }\n    }, 5000);\n\n    // Replace navigator before any modules are required(), to ensure it happens as soon as possible.\n    // We replace it so that properties that can't be clobbered can instead be overridden.\n    function replaceNavigator(origNavigator) {\n      var CordovaNavigator = function () {};\n      CordovaNavigator.prototype = origNavigator;\n      var newNavigator = new CordovaNavigator();\n      // This work-around really only applies to new APIs that are newer than Function.bind.\n      // Without it, APIs such as getGamepads() break.\n      if (CordovaNavigator.bind) {\n        for (var key in origNavigator) {\n          if (typeof origNavigator[key] === 'function') {\n            newNavigator[key] = origNavigator[key].bind(origNavigator);\n          } else {\n            (function (k) {\n              utils.defineGetterSetter(newNavigator, key, function () {\n                return origNavigator[k];\n              });\n            })(key);\n          }\n        }\n      }\n      return newNavigator;\n    }\n\n    if (window.navigator) {\n      window.navigator = replaceNavigator(window.navigator);\n    }\n\n    // Register pause, resume and deviceready channels as events on document.\n    channel.onPause = cordova.addDocumentEventHandler('pause');\n    channel.onResume = cordova.addDocumentEventHandler('resume');\n    channel.onActivated = cordova.addDocumentEventHandler('activated');\n    channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');\n\n    // Listen for DOMContentLoaded and notify our channel subscribers.\n    if (document.readyState === 'complete' || document.readyState === 'interactive') {\n      channel.onDOMContentLoaded.fire();\n    } else {\n      document.addEventListener(\n        'DOMContentLoaded',\n        function () {\n          channel.onDOMContentLoaded.fire();\n        },\n        false,\n      );\n    }\n\n    // _nativeReady is global variable that the native side can set\n    // to signify that the native code is ready. It is a global since\n    // it may be called before any cordova JS is ready.\n    if (window._nativeReady) {\n      channel.onNativeReady.fire();\n    }\n\n    modulemapper.clobbers('cordova', 'cordova');\n    modulemapper.clobbers('cordova/exec', 'cordova.exec');\n    modulemapper.clobbers('cordova/exec', 'Cordova.exec');\n\n    // Call the platform-specific initialization.\n    platform.bootstrap && platform.bootstrap();\n\n    // Wrap in a setTimeout to support the use-case of having plugin JS appended to cordova.js.\n    // The delay allows the attached modules to be defined before the plugin loader looks for them.\n    setTimeout(function () {\n      pluginloader.load(function () {\n        channel.onPluginsReady.fire();\n      });\n    }, 0);\n\n    /**\n     * Create all cordova objects once native side is ready.\n     */\n    channel.join(function () {\n      modulemapper.mapModules(window);\n\n      platform.initialize && platform.initialize();\n\n      // Fire event to notify that all objects are created\n      channel.onCordovaReady.fire();\n\n      // Fire onDeviceReady event once page has fully loaded, all\n      // constructors have run and cordova info has been received from native\n      // side.\n      channel.join(function () {\n        require('cordova').fireDocumentEvent('deviceready');\n      }, channel.deviceReadyChannelsArray);\n    }, platformInitChannelsArray);\n  });\n\n  // file: src/common/modulemapper.js\n  define('cordova/modulemapper', function (require, exports, module) {\n    var builder = require('cordova/builder');\n    var moduleMap = define.moduleMap; // eslint-disable-line no-undef\n    var symbolList;\n    var deprecationMap;\n\n    exports.reset = function () {\n      symbolList = [];\n      deprecationMap = {};\n    };\n\n    function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) {\n      if (!(moduleName in moduleMap)) {\n        throw new Error('Module ' + moduleName + ' does not exist.');\n      }\n      symbolList.push(strategy, moduleName, symbolPath);\n      if (opt_deprecationMessage) {\n        deprecationMap[symbolPath] = opt_deprecationMessage;\n      }\n    }\n\n    // Note: Android 2.3 does have Function.bind().\n    exports.clobbers = function (moduleName, symbolPath, opt_deprecationMessage) {\n      addEntry('c', moduleName, symbolPath, opt_deprecationMessage);\n    };\n\n    exports.merges = function (moduleName, symbolPath, opt_deprecationMessage) {\n      addEntry('m', moduleName, symbolPath, opt_deprecationMessage);\n    };\n\n    exports.defaults = function (moduleName, symbolPath, opt_deprecationMessage) {\n      addEntry('d', moduleName, symbolPath, opt_deprecationMessage);\n    };\n\n    exports.runs = function (moduleName) {\n      addEntry('r', moduleName, null);\n    };\n\n    function prepareNamespace(symbolPath, context) {\n      if (!symbolPath) {\n        return context;\n      }\n      var parts = symbolPath.split('.');\n      var cur = context;\n      for (var i = 0, part; (part = parts[i]); ++i) {\n        // eslint-disable-line no-cond-assign\n        cur = cur[part] = cur[part] || {};\n      }\n      return cur;\n    }\n\n    exports.mapModules = function (context) {\n      var origSymbols = {};\n      context.CDV_origSymbols = origSymbols;\n      for (var i = 0, len = symbolList.length; i < len; i += 3) {\n        var strategy = symbolList[i];\n        var moduleName = symbolList[i + 1];\n        var module = require(moduleName);\n        // <runs/>\n        if (strategy === 'r') {\n          continue;\n        }\n        var symbolPath = symbolList[i + 2];\n        var lastDot = symbolPath.lastIndexOf('.');\n        var namespace = symbolPath.substr(0, lastDot);\n        var lastName = symbolPath.substr(lastDot + 1);\n\n        var deprecationMsg =\n          symbolPath in deprecationMap\n            ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg\n            : null;\n        var parentObj = prepareNamespace(namespace, context);\n        var target = parentObj[lastName];\n\n        if (strategy === 'm' && target) {\n          builder.recursiveMerge(target, module);\n        } else if ((strategy === 'd' && !target) || strategy !== 'd') {\n          if (!(symbolPath in origSymbols)) {\n            origSymbols[symbolPath] = target;\n          }\n          builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg);\n        }\n      }\n    };\n\n    exports.getOriginalSymbol = function (context, symbolPath) {\n      var origSymbols = context.CDV_origSymbols;\n      if (origSymbols && symbolPath in origSymbols) {\n        return origSymbols[symbolPath];\n      }\n      var parts = symbolPath.split('.');\n      var obj = context;\n      for (var i = 0; i < parts.length; ++i) {\n        obj = obj && obj[parts[i]];\n      }\n      return obj;\n    };\n\n    exports.reset();\n  });\n\n  define('cordova/platform', function (require, exports, module) {\n    module.exports = {\n      id: window.Capacitor.getPlatform(),\n      bootstrap: function () {\n        require('cordova/channel').onNativeReady.fire();\n      },\n    };\n  });\n\n  // file: src/common/pluginloader.js\n  define('cordova/pluginloader', function (require, exports, module) {\n    var modulemapper = require('cordova/modulemapper');\n\n    function onScriptLoadingComplete(moduleList, finishPluginLoading) {\n      console.log('onscript loading complete');\n      // Loop through all the plugins and then through their clobbers and merges.\n      for (var i = 0, module; (module = moduleList[i]); i++) {\n        // eslint-disable-line no-cond-assign\n        if (module.clobbers && module.clobbers.length) {\n          for (var j = 0; j < module.clobbers.length; j++) {\n            modulemapper.clobbers(module.id, module.clobbers[j]);\n          }\n        }\n\n        if (module.merges && module.merges.length) {\n          for (var k = 0; k < module.merges.length; k++) {\n            modulemapper.merges(module.id, module.merges[k]);\n          }\n        }\n\n        // Finally, if runs is truthy we want to simply require() the module.\n        if (module.runs) {\n          modulemapper.runs(module.id);\n        }\n      }\n\n      finishPluginLoading();\n    }\n\n    // Tries to load all plugins' js-modules.\n    // This is an async process, but onDeviceReady is blocked on onPluginsReady.\n    // onPluginsReady is fired when there are no plugins to load, or they are all done.\n    exports.load = function (callback) {\n      var moduleList = require('cordova/plugin_list');\n      onScriptLoadingComplete(moduleList, callback);\n    };\n  });\n\n  // file: src/common/urlutil.js\n  define('cordova/urlutil', function (require, exports, module) {\n    /**\n     * For already absolute URLs, returns what is passed in.\n     * For relative URLs, converts them to absolute ones.\n     */\n    exports.makeAbsolute = function makeAbsolute(url) {\n      var anchorEl = document.createElement('a');\n      anchorEl.href = url;\n      return anchorEl.href;\n    };\n  });\n\n  // file: src/common/utils.js\n  define('cordova/utils', function (require, exports, module) {\n    var utils = exports;\n\n    /**\n     * Defines a property getter / setter for obj[key].\n     */\n    utils.defineGetterSetter = function (obj, key, getFunc, opt_setFunc) {\n      if (Object.defineProperty) {\n        var desc = {\n          get: getFunc,\n          configurable: true,\n        };\n        if (opt_setFunc) {\n          desc.set = opt_setFunc;\n        }\n        Object.defineProperty(obj, key, desc);\n      } else {\n        obj.__defineGetter__(key, getFunc);\n        if (opt_setFunc) {\n          obj.__defineSetter__(key, opt_setFunc);\n        }\n      }\n    };\n\n    /**\n     * Defines a property getter for obj[key].\n     */\n    utils.defineGetter = utils.defineGetterSetter;\n\n    utils.arrayIndexOf = function (a, item) {\n      if (a.indexOf) {\n        return a.indexOf(item);\n      }\n      var len = a.length;\n      for (var i = 0; i < len; ++i) {\n        if (a[i] === item) {\n          return i;\n        }\n      }\n      return -1;\n    };\n\n    /**\n     * Returns whether the item was found in the array.\n     */\n    utils.arrayRemove = function (a, item) {\n      var index = utils.arrayIndexOf(a, item);\n      if (index !== -1) {\n        a.splice(index, 1);\n      }\n      return index !== -1;\n    };\n\n    utils.typeName = function (val) {\n      return Object.prototype.toString.call(val).slice(8, -1);\n    };\n\n    /**\n     * Returns an indication of whether the argument is an array or not\n     */\n    utils.isArray =\n      Array.isArray ||\n      function (a) {\n        return utils.typeName(a) === 'Array';\n      };\n\n    /**\n     * Returns an indication of whether the argument is a Date or not\n     */\n    utils.isDate = function (d) {\n      return d instanceof Date;\n    };\n\n    /**\n     * Does a deep clone of the object.\n     */\n    utils.clone = function (obj) {\n      if (!obj || typeof obj === 'function' || utils.isDate(obj) || typeof obj !== 'object') {\n        return obj;\n      }\n\n      var retVal, i;\n\n      if (utils.isArray(obj)) {\n        retVal = [];\n        for (i = 0; i < obj.length; ++i) {\n          retVal.push(utils.clone(obj[i]));\n        }\n        return retVal;\n      }\n\n      retVal = {};\n      for (i in obj) {\n        // https://issues.apache.org/jira/browse/CB-11522 'unknown' type may be returned in\n        // custom protocol activation case on Windows Phone 8.1 causing \"No such interface supported\" exception\n        // on cloning.\n        if ((!(i in retVal) || retVal[i] !== obj[i]) && typeof obj[i] !== 'undefined' && typeof obj[i] !== 'unknown') {\n          // eslint-disable-line valid-typeof\n          retVal[i] = utils.clone(obj[i]);\n        }\n      }\n      return retVal;\n    };\n\n    /**\n     * Returns a wrapped version of the function\n     */\n    utils.close = function (context, func, params) {\n      return function () {\n        var args = params || arguments;\n        return func.apply(context, args);\n      };\n    };\n\n    // ------------------------------------------------------------------------------\n    function UUIDcreatePart(length) {\n      var uuidpart = '';\n      for (var i = 0; i < length; i++) {\n        var uuidchar = parseInt(Math.random() * 256, 10).toString(16);\n        if (uuidchar.length === 1) {\n          uuidchar = '0' + uuidchar;\n        }\n        uuidpart += uuidchar;\n      }\n      return uuidpart;\n    }\n\n    /**\n     * Create a UUID\n     */\n    utils.createUUID = function () {\n      return (\n        UUIDcreatePart(4) +\n        '-' +\n        UUIDcreatePart(2) +\n        '-' +\n        UUIDcreatePart(2) +\n        '-' +\n        UUIDcreatePart(2) +\n        '-' +\n        UUIDcreatePart(6)\n      );\n    };\n\n    /**\n     * Extends a child object from a parent object using classical inheritance\n     * pattern.\n     */\n    utils.extend = (function () {\n      // proxy used to establish prototype chain\n      var F = function () {};\n      // extend Child from Parent\n      return function (Child, Parent) {\n        F.prototype = Parent.prototype;\n        Child.prototype = new F();\n        Child.__super__ = Parent.prototype;\n        Child.prototype.constructor = Child;\n      };\n    })();\n\n    /**\n     * Alerts a message in any available way: alert or console.log.\n     */\n    utils.alert = function (msg) {\n      if (window.alert) {\n        window.alert(msg);\n      } else if (console && console.log) {\n        console.log(msg);\n      }\n    };\n  });\n\n  window.cordova = require('cordova');\n  // file: src/scripts/bootstrap.js\n\n  require('cordova/init');\n})();\n"
  },
  {
    "path": "core/http.md",
    "content": "# CapacitorHttp\n\nThe Capacitor Http API provides native http support via patching `fetch` and `XMLHttpRequest` to use native libraries. It also provides helper methods for native http requests without the use of `fetch` and `XMLHttpRequest`. This plugin is bundled with `@capacitor/core`.\n\n## Configuration\n\nBy default, the patching of `window.fetch` and `XMLHttpRequest` to use native libraries is disabled.\nIf you would like to enable this feature, modify the configuration below in the `capacitor.config` file.\n\n| Prop          | Type                 | Description                                                                          | Default            |\n| ------------- | -------------------- | ------------------------------------------------------------------------------------ | ------------------ |\n| **`enabled`** | <code>boolean</code> | Enable the patching of `fetch` and `XMLHttpRequest` to use native libraries instead. | <code>false</code> |\n\n### Example Configuration\n\nIn `capacitor.config.json`:\n\n```json\n{\n  \"plugins\": {\n    \"CapacitorHttp\": {\n      \"enabled\": true\n    }\n  }\n}\n```\n\nIn `capacitor.config.ts`:\n\n```ts\nimport { CapacitorConfig } from '@capacitor/cli';\n\nconst config: CapacitorConfig = {\n  plugins: {\n    CapacitorHttp: {\n      enabled: true,\n    },\n  },\n};\n\nexport default config;\n```\n\n## Example\n\n```typescript\nimport { CapacitorHttp } from '@capacitor/core';\n\n// Example of a GET request\nconst doGet = () => {\n  const options = {\n    url: 'https://example.com/my/api',\n    headers: { 'X-Fake-Header': 'Fake-Value' },\n    params: { size: 'XL' },\n  };\n\n  const response: HttpResponse = await CapacitorHttp.get(options);\n\n  // or...\n  // const response = await CapacitorHttp.request({ ...options, method: 'GET' })\n};\n\n// Example of a POST request. Note: data\n// can be passed as a raw JS Object (must be JSON serializable)\nconst doPost = () => {\n  const options = {\n    url: 'https://example.com/my/api',\n    headers: { 'X-Fake-Header': 'Fake-Value' },\n    data: { foo: 'bar' },\n  };\n\n  const response: HttpResponse = await CapacitorHttp.post(options);\n\n  // or...\n  // const response = await CapacitorHttp.request({ ...options, method: 'POST' })\n};\n```\n\n## Large File Support\n\nDue to the nature of the bridge, parsing and transferring large amount of data from native to the web can cause issues. Support for downloading and uploading files has been added to the [`@capacitor/file-transfer`](https://capacitorjs.com/docs/apis/file-transfer) plugin. In many cases, you may also need [`@capacitor/filesystem`](https://capacitorjs.com/docs/apis/filesystem) to generate a valid [file URI](https://capacitorjs.com/docs/apis/filesystem#geturi).\n\n## API\n\n<docgen-index>\n\n* [`request(...)`](#request)\n* [`get(...)`](#get)\n* [`post(...)`](#post)\n* [`put(...)`](#put)\n* [`patch(...)`](#patch)\n* [`delete(...)`](#delete)\n* [Interfaces](#interfaces)\n* [Type Aliases](#type-aliases)\n\n</docgen-index>\n\n<docgen-api>\n<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->\n\n****** HTTP PLUGIN *******\n\n### request(...)\n\n```typescript\nrequest(options: HttpOptions) => Promise<HttpResponse>\n```\n\nMake a Http Request to a server using native libraries.\n\n| Param         | Type                                                |\n| ------------- | --------------------------------------------------- |\n| **`options`** | <code><a href=\"#httpoptions\">HttpOptions</a></code> |\n\n**Returns:** <code>Promise&lt;<a href=\"#httpresponse\">HttpResponse</a>&gt;</code>\n\n--------------------\n\n\n### get(...)\n\n```typescript\nget(options: HttpOptions) => Promise<HttpResponse>\n```\n\nMake a Http GET Request to a server using native libraries.\n\n| Param         | Type                                                |\n| ------------- | --------------------------------------------------- |\n| **`options`** | <code><a href=\"#httpoptions\">HttpOptions</a></code> |\n\n**Returns:** <code>Promise&lt;<a href=\"#httpresponse\">HttpResponse</a>&gt;</code>\n\n--------------------\n\n\n### post(...)\n\n```typescript\npost(options: HttpOptions) => Promise<HttpResponse>\n```\n\nMake a Http POST Request to a server using native libraries.\n\n| Param         | Type                                                |\n| ------------- | --------------------------------------------------- |\n| **`options`** | <code><a href=\"#httpoptions\">HttpOptions</a></code> |\n\n**Returns:** <code>Promise&lt;<a href=\"#httpresponse\">HttpResponse</a>&gt;</code>\n\n--------------------\n\n\n### put(...)\n\n```typescript\nput(options: HttpOptions) => Promise<HttpResponse>\n```\n\nMake a Http PUT Request to a server using native libraries.\n\n| Param         | Type                                                |\n| ------------- | --------------------------------------------------- |\n| **`options`** | <code><a href=\"#httpoptions\">HttpOptions</a></code> |\n\n**Returns:** <code>Promise&lt;<a href=\"#httpresponse\">HttpResponse</a>&gt;</code>\n\n--------------------\n\n\n### patch(...)\n\n```typescript\npatch(options: HttpOptions) => Promise<HttpResponse>\n```\n\nMake a Http PATCH Request to a server using native libraries.\n\n| Param         | Type                                                |\n| ------------- | --------------------------------------------------- |\n| **`options`** | <code><a href=\"#httpoptions\">HttpOptions</a></code> |\n\n**Returns:** <code>Promise&lt;<a href=\"#httpresponse\">HttpResponse</a>&gt;</code>\n\n--------------------\n\n\n### delete(...)\n\n```typescript\ndelete(options: HttpOptions) => Promise<HttpResponse>\n```\n\nMake a Http DELETE Request to a server using native libraries.\n\n| Param         | Type                                                |\n| ------------- | --------------------------------------------------- |\n| **`options`** | <code><a href=\"#httpoptions\">HttpOptions</a></code> |\n\n**Returns:** <code>Promise&lt;<a href=\"#httpresponse\">HttpResponse</a>&gt;</code>\n\n--------------------\n\n\n### Interfaces\n\n\n#### HttpResponse\n\n| Prop          | Type                                                | Description                                       |\n| ------------- | --------------------------------------------------- | ------------------------------------------------- |\n| **`data`**    | <code>any</code>                                    | Additional data received with the Http response.  |\n| **`status`**  | <code>number</code>                                 | The status code received from the Http response.  |\n| **`headers`** | <code><a href=\"#httpheaders\">HttpHeaders</a></code> | The headers received from the Http response.      |\n| **`url`**     | <code>string</code>                                 | The response URL received from the Http response. |\n\n\n#### HttpHeaders\n\n\n#### HttpOptions\n\n| Prop                        | Type                                                          | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                |\n| --------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`url`**                   | <code>string</code>                                           | The URL to send the request to.                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n| **`method`**                | <code>string</code>                                           | The Http Request method to run. (Default is GET)                                                                                                                                                                                                                                                                                                                                                                                                                           |\n| **`params`**                | <code><a href=\"#httpparams\">HttpParams</a></code>             | URL parameters to append to the request.                                                                                                                                                                                                                                                                                                                                                                                                                                   |\n| **`data`**                  | <code>any</code>                                              | Note: On Android and iOS, data can only be a string or a JSON. FormData, <a href=\"#blob\">Blob</a>, <a href=\"#arraybuffer\">ArrayBuffer</a>, and other complex types are only directly supported on web or through enabling `CapacitorHttp` in the config and using the patched `window.fetch` or `XMLHttpRequest`. If you need to send a complex type, you should serialize the data to base64 and set the `headers[\"Content-Type\"]` and `dataType` attributes accordingly. |\n| **`headers`**               | <code><a href=\"#httpheaders\">HttpHeaders</a></code>           | Http Request headers to send with the request.                                                                                                                                                                                                                                                                                                                                                                                                                             |\n| **`readTimeout`**           | <code>number</code>                                           | How long to wait to read additional data in milliseconds. Resets each time new data is received.                                                                                                                                                                                                                                                                                                                                                                           |\n| **`connectTimeout`**        | <code>number</code>                                           | How long to wait for the initial connection in milliseconds.                                                                                                                                                                                                                                                                                                                                                                                                               |\n| **`disableRedirects`**      | <code>boolean</code>                                          | Sets whether automatic HTTP redirects should be disabled                                                                                                                                                                                                                                                                                                                                                                                                                   |\n| **`webFetchExtra`**         | <code><a href=\"#requestinit\">RequestInit</a></code>           | Extra arguments for fetch when running on the web                                                                                                                                                                                                                                                                                                                                                                                                                          |\n| **`responseType`**          | <code><a href=\"#httpresponsetype\">HttpResponseType</a></code> | This is used to parse the response appropriately before returning it to the requestee. If the response content-type is \"json\", this value is ignored.                                                                                                                                                                                                                                                                                                                      |\n| **`shouldEncodeUrlParams`** | <code>boolean</code>                                          | Use this option if you need to keep the URL unencoded in certain cases (already encoded, azure/firebase testing, etc.). The default is _true_.                                                                                                                                                                                                                                                                                                                             |\n| **`dataType`**              | <code>'file' \\| 'formData'</code>                             | This is used if we've had to convert the data from a JS type that needs special handling in the native layer                                                                                                                                                                                                                                                                                                                                                               |\n\n\n#### HttpParams\n\n\n#### RequestInit\n\n| Prop                 | Type                                                              | Description                                                                                                                                                                       |\n| -------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`body`**           | <code><a href=\"#bodyinit\">BodyInit</a></code>                     | A <a href=\"#bodyinit\">BodyInit</a> object or null to set request's body.                                                                                                          |\n| **`cache`**          | <code><a href=\"#requestcache\">RequestCache</a></code>             | A string indicating how the request will interact with the browser's cache to set request's cache.                                                                                |\n| **`credentials`**    | <code><a href=\"#requestcredentials\">RequestCredentials</a></code> | A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials.                          |\n| **`headers`**        | <code><a href=\"#headersinit\">HeadersInit</a></code>               | A <a href=\"#headers\">Headers</a> object, an object literal, or an array of two-item arrays to set request's headers.                                                              |\n| **`integrity`**      | <code>string</code>                                               | A cryptographic hash of the resource to be fetched by request. Sets request's integrity.                                                                                          |\n| **`keepalive`**      | <code>boolean</code>                                              | A boolean to set request's keepalive.                                                                                                                                             |\n| **`method`**         | <code>string</code>                                               | A string to set request's method.                                                                                                                                                 |\n| **`mode`**           | <code><a href=\"#requestmode\">RequestMode</a></code>               | A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode.                                                           |\n| **`redirect`**       | <code><a href=\"#requestredirect\">RequestRedirect</a></code>       | A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. |\n| **`referrer`**       | <code>string</code>                                               | A string whose value is a same-origin URL, \"about:client\", or the empty string, to set request's referrer.                                                                        |\n| **`referrerPolicy`** | <code><a href=\"#referrerpolicy\">ReferrerPolicy</a></code>         | A referrer policy to set request's referrerPolicy.                                                                                                                                |\n| **`signal`**         | <code><a href=\"#abortsignal\">AbortSignal</a></code>               | An <a href=\"#abortsignal\">AbortSignal</a> to set request's signal.                                                                                                                |\n| **`window`**         | <code>any</code>                                                  | Can only be null. Used to disassociate request from any Window.                                                                                                                   |\n\n\n#### Blob\n\nA file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The <a href=\"#file\">File</a> interface is based on <a href=\"#blob\">Blob</a>, inheriting blob functionality and expanding it to support files on the user's system.\n`Blob` class is a global reference for `require('node:buffer').Blob`\nhttps://nodejs.org/api/buffer.html#class-blob\n\n| Prop       | Type                |\n| ---------- | ------------------- |\n| **`size`** | <code>number</code> |\n| **`type`** | <code>string</code> |\n\n| Method          | Signature                                                                           |\n| --------------- | ----------------------------------------------------------------------------------- |\n| **arrayBuffer** | () =&gt; Promise&lt;<a href=\"#arraybuffer\">ArrayBuffer</a>&gt;                      |\n| **slice**       | (start?: number, end?: number, contentType?: string) =&gt; <a href=\"#blob\">Blob</a> |\n| **stream**      | () =&gt; <a href=\"#readablestream\">ReadableStream</a>                               |\n| **text**        | () =&gt; Promise&lt;string&gt;                                                      |\n\n\n#### ArrayBuffer\n\nRepresents a raw buffer of binary data, which is used to store data for the\r\ndifferent typed arrays. ArrayBuffers cannot be read from or written to directly,\r\nbut can be passed to a typed array or DataView Object to interpret the raw\r\nbuffer as needed.\n\n| Prop             | Type                | Description                                                                     |\n| ---------------- | ------------------- | ------------------------------------------------------------------------------- |\n| **`byteLength`** | <code>number</code> | Read-only. The length of the <a href=\"#arraybuffer\">ArrayBuffer</a> (in bytes). |\n\n| Method    | Signature                                                                  | Description                                                     |\n| --------- | -------------------------------------------------------------------------- | --------------------------------------------------------------- |\n| **slice** | (begin: number, end?: number) =&gt; <a href=\"#arraybuffer\">ArrayBuffer</a> | Returns a section of an <a href=\"#arraybuffer\">ArrayBuffer</a>. |\n\n\n#### ReadableStream\n\nThis Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a <a href=\"#readablestream\">ReadableStream</a> through the body property of a Response object.\n\n| Prop         | Type                 |\n| ------------ | -------------------- |\n| **`locked`** | <code>boolean</code> |\n\n| Method          | Signature                                                                                                                                                                                                            |\n| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **cancel**      | (reason?: any) =&gt; Promise&lt;void&gt;                                                                                                                                                                             |\n| **getReader**   | () =&gt; <a href=\"#readablestreamdefaultreader\">ReadableStreamDefaultReader</a>&lt;R&gt;                                                                                                                             |\n| **pipeThrough** | &lt;T&gt;(transform: <a href=\"#readablewritablepair\">ReadableWritablePair</a>&lt;T, R&gt;, options?: <a href=\"#streampipeoptions\">StreamPipeOptions</a>) =&gt; <a href=\"#readablestream\">ReadableStream</a>&lt;T&gt; |\n| **pipeTo**      | (dest: <a href=\"#writablestream\">WritableStream</a>&lt;R&gt;, options?: <a href=\"#streampipeoptions\">StreamPipeOptions</a>) =&gt; Promise&lt;void&gt;                                                                |\n| **tee**         | () =&gt; [ReadableStream&lt;R&gt;, <a href=\"#readablestream\">ReadableStream</a>&lt;R&gt;]                                                                                                                            |\n\n\n#### ReadableStreamDefaultReader\n\n| Method          | Signature                                                                                                       |\n| --------------- | --------------------------------------------------------------------------------------------------------------- |\n| **read**        | () =&gt; Promise&lt;<a href=\"#readablestreamdefaultreadresult\">ReadableStreamDefaultReadResult</a>&lt;R&gt;&gt; |\n| **releaseLock** | () =&gt; void                                                                                                   |\n\n\n#### ReadableStreamDefaultReadValueResult\n\n| Prop        | Type               |\n| ----------- | ------------------ |\n| **`done`**  | <code>false</code> |\n| **`value`** | <code>T</code>     |\n\n\n#### ReadableStreamDefaultReadDoneResult\n\n| Prop        | Type              |\n| ----------- | ----------------- |\n| **`done`**  | <code>true</code> |\n| **`value`** |                   |\n\n\n#### ReadableWritablePair\n\n| Prop           | Type                                                               | Description                                                                                                                                                                                                                                                                                                                                                                         |\n| -------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`readable`** | <code><a href=\"#readablestream\">ReadableStream</a>&lt;R&gt;</code> |                                                                                                                                                                                                                                                                                                                                                                                     |\n| **`writable`** | <code><a href=\"#writablestream\">WritableStream</a>&lt;W&gt;</code> | Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. |\n\n\n#### WritableStream\n\nThis Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing.\n\n| Prop         | Type                 |\n| ------------ | -------------------- |\n| **`locked`** | <code>boolean</code> |\n\n| Method        | Signature                                                                                |\n| ------------- | ---------------------------------------------------------------------------------------- |\n| **abort**     | (reason?: any) =&gt; Promise&lt;void&gt;                                                 |\n| **getWriter** | () =&gt; <a href=\"#writablestreamdefaultwriter\">WritableStreamDefaultWriter</a>&lt;W&gt; |\n\n\n#### WritableStreamDefaultWriter\n\nThis Streams API interface is the object returned by <a href=\"#writablestream\">WritableStream.getWriter</a>() and once created locks the &lt; writer to the <a href=\"#writablestream\">WritableStream</a> ensuring that no other streams can write to the underlying sink.\n\n| Prop              | Type                                  |\n| ----------------- | ------------------------------------- |\n| **`closed`**      | <code>Promise&lt;undefined&gt;</code> |\n| **`desiredSize`** | <code>number</code>                   |\n| **`ready`**       | <code>Promise&lt;undefined&gt;</code> |\n\n| Method          | Signature                                |\n| --------------- | ---------------------------------------- |\n| **abort**       | (reason?: any) =&gt; Promise&lt;void&gt; |\n| **close**       | () =&gt; Promise&lt;void&gt;             |\n| **releaseLock** | () =&gt; void                            |\n| **write**       | (chunk: W) =&gt; Promise&lt;void&gt;     |\n\n\n#### StreamPipeOptions\n\n| Prop                | Type                                                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n| ------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`preventAbort`**  | <code>boolean</code>                                |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| **`preventCancel`** | <code>boolean</code>                                |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| **`preventClose`**  | <code>boolean</code>                                | Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. Errors and closures of the source and destination streams propagate as follows: An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. The signal option can be set to an <a href=\"#abortsignal\">AbortSignal</a> to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. |\n| **`signal`**        | <code><a href=\"#abortsignal\">AbortSignal</a></code> |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n\n\n#### AbortSignal\n\nA signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object.\n\n| Prop          | Type                                                                                                  | Description                                                                                                               |\n| ------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| **`aborted`** | <code>boolean</code>                                                                                  | Returns true if this <a href=\"#abortsignal\">AbortSignal</a>'s AbortController has signaled to abort, and false otherwise. |\n| **`onabort`** | <code>(this: <a href=\"#abortsignal\">AbortSignal</a>, ev: <a href=\"#event\">Event</a>) =&gt; any</code> |                                                                                                                           |\n\n| Method                  | Signature                                                                                                                                                                                                                          | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **addEventListener**    | &lt;K extends \"abort\"&gt;(type: K, listener: (this: <a href=\"#abortsignal\">AbortSignal</a>, ev: AbortSignalEventMap[K]) =&gt; any, options?: boolean \\| <a href=\"#addeventlisteneroptions\">AddEventListenerOptions</a>) =&gt; void | Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. |\n| **addEventListener**    | (type: string, listener: <a href=\"#eventlisteneroreventlistenerobject\">EventListenerOrEventListenerObject</a>, options?: boolean \\| <a href=\"#addeventlisteneroptions\">AddEventListenerOptions</a>) =&gt; void                     | Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. |\n| **removeEventListener** | &lt;K extends \"abort\"&gt;(type: K, listener: (this: <a href=\"#abortsignal\">AbortSignal</a>, ev: AbortSignalEventMap[K]) =&gt; any, options?: boolean \\| <a href=\"#eventlisteneroptions\">EventListenerOptions</a>) =&gt; void       | Removes the event listener in target's event listener list with the same type, callback, and options.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| **removeEventListener** | (type: string, listener: <a href=\"#eventlisteneroreventlistenerobject\">EventListenerOrEventListenerObject</a>, options?: boolean \\| <a href=\"#eventlisteneroptions\">EventListenerOptions</a>) =&gt; void                           | Removes the event listener in target's event listener list with the same type, callback, and options.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n\n\n#### AbortSignalEventMap\n\n| Prop          | Type                                    |\n| ------------- | --------------------------------------- |\n| **`\"abort\"`** | <code><a href=\"#event\">Event</a></code> |\n\n\n#### Event\n\nAn event which takes place in the DOM.\n\n| Prop                   | Type                                                | Description                                                                                                                                                                                                                                                |\n| ---------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`bubbles`**          | <code>boolean</code>                                | Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise.                                                                                                |\n| **`cancelBubble`**     | <code>boolean</code>                                |                                                                                                                                                                                                                                                            |\n| **`cancelable`**       | <code>boolean</code>                                | Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method. |\n| **`composed`**         | <code>boolean</code>                                | Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise.                                                                                  |\n| **`currentTarget`**    | <code><a href=\"#eventtarget\">EventTarget</a></code> | Returns the object whose event listener's callback is currently being invoked.                                                                                                                                                                             |\n| **`defaultPrevented`** | <code>boolean</code>                                | Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise.                                                                                                                                                    |\n| **`eventPhase`**       | <code>number</code>                                 | Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE.                                                                                                                                                           |\n| **`isTrusted`**        | <code>boolean</code>                                | Returns true if event was dispatched by the user agent, and false otherwise.                                                                                                                                                                               |\n| **`returnValue`**      | <code>boolean</code>                                |                                                                                                                                                                                                                                                            |\n| **`srcElement`**       | <code><a href=\"#eventtarget\">EventTarget</a></code> |                                                                                                                                                                                                                                                            |\n| **`target`**           | <code><a href=\"#eventtarget\">EventTarget</a></code> | Returns the object to which event is dispatched (its target).                                                                                                                                                                                              |\n| **`timeStamp`**        | <code>number</code>                                 | Returns the event's timestamp as the number of milliseconds measured relative to the time origin.                                                                                                                                                          |\n| **`type`**             | <code>string</code>                                 | Returns the type of event, e.g. \"click\", \"hashchange\", or \"submit\".                                                                                                                                                                                        |\n| **`AT_TARGET`**        | <code>number</code>                                 |                                                                                                                                                                                                                                                            |\n| **`BUBBLING_PHASE`**   | <code>number</code>                                 |                                                                                                                                                                                                                                                            |\n| **`CAPTURING_PHASE`**  | <code>number</code>                                 |                                                                                                                                                                                                                                                            |\n| **`NONE`**             | <code>number</code>                                 |                                                                                                                                                                                                                                                            |\n\n| Method                       | Signature                                                          | Description                                                                                                                                                                                                                             |\n| ---------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **composedPath**             | () =&gt; EventTarget[]                                             | Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is \"closed\" that are not reachable from event's currentTarget. |\n| **initEvent**                | (type: string, bubbles?: boolean, cancelable?: boolean) =&gt; void |                                                                                                                                                                                                                                         |\n| **preventDefault**           | () =&gt; void                                                      | If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled.               |\n| **stopImmediatePropagation** | () =&gt; void                                                      | Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects.                            |\n| **stopPropagation**          | () =&gt; void                                                      | When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object.                                                                                                                 |\n\n\n#### EventTarget\n\n<a href=\"#eventtarget\">EventTarget</a> is a DOM interface implemented by objects that can receive events and may have listeners for them.\nEventTarget is a DOM interface implemented by objects that can\nreceive events and may have listeners for them.\n\n| Method                  | Signature                                                                                                                                                                                                              | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **addEventListener**    | (type: string, listener: <a href=\"#eventlisteneroreventlistenerobject\">EventListenerOrEventListenerObject</a> \\| null, options?: boolean \\| <a href=\"#addeventlisteneroptions\">AddEventListenerOptions</a>) =&gt; void | Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. |\n| **dispatchEvent**       | (event: <a href=\"#event\">Event</a>) =&gt; boolean                                                                                                                                                                      | Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\n| **removeEventListener** | (type: string, callback: <a href=\"#eventlisteneroreventlistenerobject\">EventListenerOrEventListenerObject</a> \\| null, options?: <a href=\"#eventlisteneroptions\">EventListenerOptions</a> \\| boolean) =&gt; void       | Removes the event listener in target's event listener list with the same type, callback, and options.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n\n\n#### EventListener\n\n\n#### EventListenerObject\n\n| Method          | Signature                                    |\n| --------------- | -------------------------------------------- |\n| **handleEvent** | (evt: <a href=\"#event\">Event</a>) =&gt; void |\n\n\n#### AddEventListenerOptions\n\n| Prop          | Type                 |\n| ------------- | -------------------- |\n| **`once`**    | <code>boolean</code> |\n| **`passive`** | <code>boolean</code> |\n\n\n#### EventListenerOptions\n\n| Prop          | Type                 |\n| ------------- | -------------------- |\n| **`capture`** | <code>boolean</code> |\n\n\n#### ArrayBufferView\n\n| Prop             | Type                                                        | Description                                                                  |\n| ---------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------- |\n| **`buffer`**     | <code><a href=\"#arraybufferlike\">ArrayBufferLike</a></code> | The <a href=\"#arraybuffer\">ArrayBuffer</a> instance referenced by the array. |\n| **`byteLength`** | <code>number</code>                                         | The length in bytes of the array.                                            |\n| **`byteOffset`** | <code>number</code>                                         | The offset in bytes of the array.                                            |\n\n\n#### ArrayBufferTypes\n\nAllowed <a href=\"#arraybuffer\">ArrayBuffer</a> types for the buffer of an <a href=\"#arraybufferview\">ArrayBufferView</a> and related Typed Arrays.\n\n| Prop              | Type                                                |\n| ----------------- | --------------------------------------------------- |\n| **`ArrayBuffer`** | <code><a href=\"#arraybuffer\">ArrayBuffer</a></code> |\n\n\n#### FormData\n\nProvides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to \"multipart/form-data\".\n\n| Method      | Signature                                                                                                                                                               |\n| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **append**  | (name: string, value: string \\| <a href=\"#blob\">Blob</a>, fileName?: string) =&gt; void                                                                                 |\n| **delete**  | (name: string) =&gt; void                                                                                                                                               |\n| **get**     | (name: string) =&gt; <a href=\"#formdataentryvalue\">FormDataEntryValue</a> \\| null                                                                                       |\n| **getAll**  | (name: string) =&gt; FormDataEntryValue[]                                                                                                                               |\n| **has**     | (name: string) =&gt; boolean                                                                                                                                            |\n| **set**     | (name: string, value: string \\| <a href=\"#blob\">Blob</a>, fileName?: string) =&gt; void                                                                                 |\n| **forEach** | (callbackfn: (value: <a href=\"#formdataentryvalue\">FormDataEntryValue</a>, key: string, parent: <a href=\"#formdata\">FormData</a>) =&gt; void, thisArg?: any) =&gt; void |\n\n\n#### File\n\nProvides information about files and allows JavaScript in a web page to access their content.\n\n| Prop               | Type                |\n| ------------------ | ------------------- |\n| **`lastModified`** | <code>number</code> |\n| **`name`**         | <code>string</code> |\n\n\n#### URLSearchParams\n\n<a href=\"#urlsearchparams\">`URLSearchParams`</a> class is a global reference for `require('url').URLSearchParams`\nhttps://nodejs.org/api/url.html#class-urlsearchparams\n\n| Method       | Signature                                                                                                                               | Description                                                                                                                |\n| ------------ | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |\n| **append**   | (name: string, value: string) =&gt; void                                                                                                | Appends a specified key/value pair as a new search parameter.                                                              |\n| **delete**   | (name: string) =&gt; void                                                                                                               | Deletes the given search parameter, and its associated value, from the list of all search parameters.                      |\n| **get**      | (name: string) =&gt; string \\| null                                                                                                     | Returns the first value associated to the given search parameter.                                                          |\n| **getAll**   | (name: string) =&gt; string[]                                                                                                           | Returns all the values association with a given search parameter.                                                          |\n| **has**      | (name: string) =&gt; boolean                                                                                                            | Returns a Boolean indicating if such a search parameter exists.                                                            |\n| **set**      | (name: string, value: string) =&gt; void                                                                                                | Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. |\n| **sort**     | () =&gt; void                                                                                                                           |                                                                                                                            |\n| **toString** | () =&gt; string                                                                                                                         | Returns a string containing a query string suitable for use in a URL. Does not include the question mark.                  |\n| **forEach**  | (callbackfn: (value: string, key: string, parent: <a href=\"#urlsearchparams\">URLSearchParams</a>) =&gt; void, thisArg?: any) =&gt; void |                                                                                                                            |\n\n\n#### Uint8Array\n\nA typed array of 8-bit unsigned integer values. The contents are initialized to 0. If the\r\nrequested number of bytes could not be allocated an exception is raised.\n\n| Prop                    | Type                                                        | Description                                                                  |\n| ----------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------- |\n| **`BYTES_PER_ELEMENT`** | <code>number</code>                                         | The size in bytes of each element in the array.                              |\n| **`buffer`**            | <code><a href=\"#arraybufferlike\">ArrayBufferLike</a></code> | The <a href=\"#arraybuffer\">ArrayBuffer</a> instance referenced by the array. |\n| **`byteLength`**        | <code>number</code>                                         | The length in bytes of the array.                                            |\n| **`byteOffset`**        | <code>number</code>                                         | The offset in bytes of the array.                                            |\n| **`length`**            | <code>number</code>                                         | The length of the array.                                                     |\n\n| Method             | Signature                                                                                                                                                                      | Description                                                                                                                                                                                                                                 |\n| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **copyWithin**     | (target: number, start: number, end?: number) =&gt; this                                                                                                                       | Returns the this object after copying a section of the array identified by start and end to the same array starting at position target                                                                                                      |\n| **every**          | (predicate: (value: number, index: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; unknown, thisArg?: any) =&gt; boolean                                            | Determines whether all the members of an array satisfy the specified test.                                                                                                                                                                  |\n| **fill**           | (value: number, start?: number, end?: number) =&gt; this                                                                                                                       | Returns the this object after filling the section identified by start and end with value                                                                                                                                                    |\n| **filter**         | (predicate: (value: number, index: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; any, thisArg?: any) =&gt; <a href=\"#uint8array\">Uint8Array</a>                   | Returns the elements of an array that meet the condition specified in a callback function.                                                                                                                                                  |\n| **find**           | (predicate: (value: number, index: number, obj: <a href=\"#uint8array\">Uint8Array</a>) =&gt; boolean, thisArg?: any) =&gt; number \\| undefined                                  | Returns the value of the first element in the array where predicate is true, and undefined otherwise.                                                                                                                                       |\n| **findIndex**      | (predicate: (value: number, index: number, obj: <a href=\"#uint8array\">Uint8Array</a>) =&gt; boolean, thisArg?: any) =&gt; number                                               | Returns the index of the first element in the array where predicate is true, and -1 otherwise.                                                                                                                                              |\n| **forEach**        | (callbackfn: (value: number, index: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; void, thisArg?: any) =&gt; void                                                 | Performs the specified action for each element in an array.                                                                                                                                                                                 |\n| **indexOf**        | (searchElement: number, fromIndex?: number) =&gt; number                                                                                                                       | Returns the index of the first occurrence of a value in an array.                                                                                                                                                                           |\n| **join**           | (separator?: string) =&gt; string                                                                                                                                              | Adds all the elements of an array separated by the specified separator string.                                                                                                                                                              |\n| **lastIndexOf**    | (searchElement: number, fromIndex?: number) =&gt; number                                                                                                                       | Returns the index of the last occurrence of a value in an array.                                                                                                                                                                            |\n| **map**            | (callbackfn: (value: number, index: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; number, thisArg?: any) =&gt; <a href=\"#uint8array\">Uint8Array</a>               | Calls a defined callback function on each element of an array, and returns an array that contains the results.                                                                                                                              |\n| **reduce**         | (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; number) =&gt; number                       | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.                      |\n| **reduce**         | (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; number, initialValue: number) =&gt; number |                                                                                                                                                                                                                                             |\n| **reduce**         | &lt;U&gt;(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; U, initialValue: U) =&gt; U            | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.                      |\n| **reduceRight**    | (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; number) =&gt; number                       | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. |\n| **reduceRight**    | (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; number, initialValue: number) =&gt; number |                                                                                                                                                                                                                                             |\n| **reduceRight**    | &lt;U&gt;(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; U, initialValue: U) =&gt; U            | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. |\n| **reverse**        | () =&gt; <a href=\"#uint8array\">Uint8Array</a>                                                                                                                                  | Reverses the elements in an Array.                                                                                                                                                                                                          |\n| **set**            | (array: <a href=\"#arraylike\">ArrayLike</a>&lt;number&gt;, offset?: number) =&gt; void                                                                                          | Sets a value or an array of values.                                                                                                                                                                                                         |\n| **slice**          | (start?: number, end?: number) =&gt; <a href=\"#uint8array\">Uint8Array</a>                                                                                                      | Returns a section of an array.                                                                                                                                                                                                              |\n| **some**           | (predicate: (value: number, index: number, array: <a href=\"#uint8array\">Uint8Array</a>) =&gt; unknown, thisArg?: any) =&gt; boolean                                            | Determines whether the specified callback function returns true for any element of an array.                                                                                                                                                |\n| **sort**           | (compareFn?: (a: number, b: number) =&gt; number) =&gt; this                                                                                                                   | Sorts an array.                                                                                                                                                                                                                             |\n| **subarray**       | (begin?: number, end?: number) =&gt; <a href=\"#uint8array\">Uint8Array</a>                                                                                                      | Gets a new <a href=\"#uint8array\">Uint8Array</a> view of the <a href=\"#arraybuffer\">ArrayBuffer</a> store for this array, referencing the elements at begin, inclusive, up to end, exclusive.                                                |\n| **toLocaleString** | () =&gt; string                                                                                                                                                                | Converts a number to a string by using the current locale.                                                                                                                                                                                  |\n| **toString**       | () =&gt; string                                                                                                                                                                | Returns a string representation of an array.                                                                                                                                                                                                |\n| **valueOf**        | () =&gt; <a href=\"#uint8array\">Uint8Array</a>                                                                                                                                  | Returns the primitive value of the specified object.                                                                                                                                                                                        |\n\n\n#### ArrayLike\n\n| Prop         | Type                |\n| ------------ | ------------------- |\n| **`length`** | <code>number</code> |\n\n\n#### Headers\n\nThis Fetch API interface allows you to perform various actions on HTTP request and response headers. These actions include retrieving, setting, adding to, and removing. A <a href=\"#headers\">Headers</a> object has an associated header list, which is initially empty and consists of zero or more name and value pairs.  You can add to this using methods like append() (see Examples.) In all methods of this interface, header names are matched by case-insensitive byte sequence.\n\n| Method      | Signature                                                                                                               |\n| ----------- | ----------------------------------------------------------------------------------------------------------------------- |\n| **append**  | (name: string, value: string) =&gt; void                                                                                |\n| **delete**  | (name: string) =&gt; void                                                                                               |\n| **get**     | (name: string) =&gt; string \\| null                                                                                     |\n| **has**     | (name: string) =&gt; boolean                                                                                            |\n| **set**     | (name: string, value: string) =&gt; void                                                                                |\n| **forEach** | (callbackfn: (value: string, key: string, parent: <a href=\"#headers\">Headers</a>) =&gt; void, thisArg?: any) =&gt; void |\n\n\n### Type Aliases\n\n\n#### BodyInit\n\n<code><a href=\"#blob\">Blob</a> | <a href=\"#buffersource\">BufferSource</a> | <a href=\"#formdata\">FormData</a> | <a href=\"#urlsearchparams\">URLSearchParams</a> | <a href=\"#readablestream\">ReadableStream</a>&lt;<a href=\"#uint8array\">Uint8Array</a>&gt; | string</code>\n\n\n#### ReadableStreamDefaultReadResult\n\n<code><a href=\"#readablestreamdefaultreadvalueresult\">ReadableStreamDefaultReadValueResult</a>&lt;T&gt; | <a href=\"#readablestreamdefaultreaddoneresult\">ReadableStreamDefaultReadDoneResult</a></code>\n\n\n#### EventListenerOrEventListenerObject\n\n<code><a href=\"#eventlistener\">EventListener</a> | <a href=\"#eventlistenerobject\">EventListenerObject</a></code>\n\n\n#### BufferSource\n\n<code><a href=\"#arraybufferview\">ArrayBufferView</a> | <a href=\"#arraybuffer\">ArrayBuffer</a></code>\n\n\n#### ArrayBufferLike\n\n<code>ArrayBufferTypes[keyof ArrayBufferTypes]</code>\n\n\n#### FormDataEntryValue\n\n<code><a href=\"#file\">File</a> | string</code>\n\n\n#### RequestCache\n\n<code>\"default\" | \"force-cache\" | \"no-cache\" | \"no-store\" | \"only-if-cached\" | \"reload\"</code>\n\n\n#### RequestCredentials\n\n<code>\"include\" | \"omit\" | \"same-origin\"</code>\n\n\n#### HeadersInit\n\n<code><a href=\"#headers\">Headers</a> | string[][] | <a href=\"#record\">Record</a>&lt;string, string&gt;</code>\n\n\n#### Record\n\nConstruct a type with a set of properties K of type T\n\n<code>{\r [P in K]: T;\r }</code>\n\n\n#### RequestMode\n\n<code>\"cors\" | \"navigate\" | \"no-cors\" | \"same-origin\"</code>\n\n\n#### RequestRedirect\n\n<code>\"error\" | \"follow\" | \"manual\"</code>\n\n\n#### ReferrerPolicy\n\n<code>\"\" | \"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\"</code>\n\n\n#### HttpResponseType\n\nHow to parse the Http response before returning it to the client.\n\n<code>'arraybuffer' | 'blob' | 'json' | 'text' | 'document'</code>\n\n</docgen-api>"
  },
  {
    "path": "core/native-bridge.ts",
    "content": "/**\n * Note: When making changes to this file, run `npm run build:nativebridge`\n * afterwards to build the nativebridge.js files to the android and iOS projects.\n */\nimport type { HttpResponse } from './src/core-plugins';\nimport type {\n  CallData,\n  CapacitorInstance,\n  ErrorCallData,\n  MessageCallData,\n  PluginResult,\n  WindowCapacitor,\n  CapFormDataEntry,\n} from './src/definitions-internal';\nimport { CapacitorException } from './src/util';\n\n// For removing exports for iOS/Android, keep let for reassignment\n// eslint-disable-next-line\nlet dummy = {};\n\nconst readFileAsBase64 = (file: File): Promise<string> =>\n  new Promise((resolve, reject) => {\n    const reader = new FileReader();\n    reader.onloadend = () => {\n      const data = reader.result as string;\n      resolve(btoa(data));\n    };\n    reader.onerror = reject;\n\n    reader.readAsBinaryString(file);\n  });\n\nconst convertFormData = async (formData: FormData): Promise<any> => {\n  const newFormData: CapFormDataEntry[] = [];\n  for (const pair of formData.entries()) {\n    const [key, value] = pair;\n    if (value instanceof File) {\n      const base64File = await readFileAsBase64(value);\n      newFormData.push({\n        key,\n        value: base64File,\n        type: 'base64File',\n        contentType: value.type,\n        fileName: value.name,\n      });\n    } else {\n      newFormData.push({ key, value, type: 'string' });\n    }\n  }\n\n  return newFormData;\n};\n\nconst convertBody = async (\n  body: Document | XMLHttpRequestBodyInit | ReadableStream<any> | undefined,\n  contentType?: string,\n): Promise<any> => {\n  if (body instanceof ReadableStream || body instanceof Uint8Array) {\n    let encodedData;\n    if (body instanceof ReadableStream) {\n      const reader = body.getReader();\n      const chunks: any[] = [];\n      while (true) {\n        const { done, value } = await reader.read();\n        if (done) break;\n        chunks.push(value);\n      }\n      const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));\n      let position = 0;\n      for (const chunk of chunks) {\n        concatenated.set(chunk, position);\n        position += chunk.length;\n      }\n      encodedData = concatenated;\n    } else {\n      encodedData = body;\n    }\n\n    let data = new TextDecoder().decode(encodedData);\n    let type;\n    if (contentType === 'application/json') {\n      try {\n        data = JSON.parse(data);\n      } catch (ignored) {\n        // ignore\n      }\n      type = 'json';\n    } else if (contentType === 'multipart/form-data') {\n      type = 'formData';\n    } else if (contentType?.startsWith('image')) {\n      type = 'image';\n    } else if (contentType === 'application/octet-stream') {\n      type = 'binary';\n    } else {\n      type = 'text';\n    }\n\n    return {\n      data,\n      type,\n      headers: { 'Content-Type': contentType || 'application/octet-stream' },\n    };\n  } else if (body instanceof URLSearchParams) {\n    return {\n      data: body.toString(),\n      type: 'text',\n    };\n  } else if (body instanceof FormData) {\n    return {\n      data: await convertFormData(body),\n      type: 'formData',\n    };\n  } else if (body instanceof File) {\n    const fileData = await readFileAsBase64(body);\n    return {\n      data: fileData,\n      type: 'file',\n      headers: { 'Content-Type': body.type },\n    };\n  }\n\n  return { data: body, type: 'json' };\n};\n\nconst CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_';\nconst CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u';\n\n// TODO: export as Cap function\nconst isRelativeOrProxyUrl = (url: string | undefined): boolean =>\n  !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1;\n\n// TODO: export as Cap function\nconst createProxyUrl = (url: string, win: WindowCapacitor): string => {\n  if (isRelativeOrProxyUrl(url)) return url;\n  const bridgeUrl = new URL(win.Capacitor?.getServerUrl() ?? '');\n  bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR;\n  bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url);\n\n  return bridgeUrl.toString();\n};\n\nconst initBridge = (w: any): void => {\n  const getPlatformId = (win: WindowCapacitor): 'android' | 'ios' | 'web' => {\n    if (win?.androidBridge) {\n      return 'android';\n    } else if (win?.webkit?.messageHandlers?.bridge) {\n      return 'ios';\n    } else {\n      return 'web';\n    }\n  };\n\n  const convertFileSrcServerUrl = (webviewServerUrl: string, filePath: string): string => {\n    if (typeof filePath === 'string') {\n      if (filePath.startsWith('/')) {\n        return webviewServerUrl + '/_capacitor_file_' + filePath;\n      } else if (filePath.startsWith('file://')) {\n        return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_');\n      } else if (filePath.startsWith('content://')) {\n        return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_');\n      }\n    }\n    return filePath;\n  };\n\n  const initEvents = (win: WindowCapacitor, cap: CapacitorInstance) => {\n    cap.addListener = (pluginName, eventName, callback) => {\n      const callbackId = cap.nativeCallback(\n        pluginName,\n        'addListener',\n        {\n          eventName: eventName,\n        },\n        callback,\n      );\n      return {\n        remove: async () => {\n          win?.console?.debug('Removing listener', pluginName, eventName);\n          cap.removeListener(pluginName, callbackId, eventName, callback);\n        },\n      };\n    };\n\n    cap.removeListener = (pluginName, callbackId, eventName, callback) => {\n      cap.nativeCallback(\n        pluginName,\n        'removeListener',\n        {\n          callbackId: callbackId,\n          eventName: eventName,\n        },\n        callback,\n      );\n    };\n\n    cap.createEvent = (eventName, eventData) => {\n      const doc = win.document;\n      if (doc) {\n        const ev = doc.createEvent('Events');\n        ev.initEvent(eventName, false, false);\n        if (eventData && typeof eventData === 'object') {\n          for (const i in eventData) {\n            // eslint-disable-next-line no-prototype-builtins\n            if (eventData.hasOwnProperty(i)) {\n              ev[i] = eventData[i];\n            }\n          }\n        }\n        return ev;\n      }\n      return null;\n    };\n\n    cap.triggerEvent = (eventName, target, eventData) => {\n      const doc = win.document;\n      const cordova = win.cordova;\n      eventData = eventData || {};\n      const ev = cap.createEvent(eventName, eventData);\n\n      if (ev) {\n        if (target === 'document') {\n          if (cordova?.fireDocumentEvent) {\n            cordova.fireDocumentEvent(eventName, eventData);\n            return true;\n          } else if (doc?.dispatchEvent) {\n            return doc.dispatchEvent(ev);\n          }\n        } else if (target === 'window' && win.dispatchEvent) {\n          return win.dispatchEvent(ev);\n        } else if (doc?.querySelector) {\n          const targetEl = doc.querySelector(target);\n          if (targetEl) {\n            return targetEl.dispatchEvent(ev);\n          }\n        }\n      }\n      return false;\n    };\n\n    win.Capacitor = cap;\n  };\n\n  const initLegacyHandlers = (win: WindowCapacitor, cap: CapacitorInstance) => {\n    // define cordova if it's not there already\n    win.cordova = win.cordova || {};\n\n    const doc = win.document;\n    const nav = win.navigator;\n\n    if (nav) {\n      nav.app = nav.app || {};\n      nav.app.exitApp = () => {\n        if (!cap.Plugins?.App) {\n          win.console.warn('App plugin not installed');\n        } else {\n          cap.nativeCallback('App', 'exitApp', {});\n        }\n      };\n    }\n\n    if (doc) {\n      const docAddEventListener = doc.addEventListener;\n      doc.addEventListener = (...args: any[]) => {\n        const eventName = args[0];\n        const handler = args[1];\n        if (eventName === 'deviceready' && handler) {\n          Promise.resolve().then(handler);\n        } else if (eventName === 'backbutton' && cap.Plugins.App) {\n          // Add a dummy listener so Capacitor doesn't do the default\n          // back button action\n          if (!cap.Plugins?.App) {\n            win.console.warn('App plugin not installed');\n          } else {\n            cap.Plugins.App.addListener('backButton', () => {\n              // ignore\n            });\n          }\n        }\n        return docAddEventListener.apply(doc, args);\n      };\n    }\n\n    win.Capacitor = cap;\n  };\n\n  const initVendor = (win: WindowCapacitor, cap: CapacitorInstance) => {\n    const Ionic = (win.Ionic = win.Ionic || {});\n    const IonicWebView = (Ionic.WebView = Ionic.WebView || {});\n    const Plugins = cap.Plugins;\n\n    IonicWebView.getServerBasePath = (callback: (path: string) => void) => {\n      Plugins?.WebView?.getServerBasePath().then((result: any) => {\n        callback(result.path);\n      });\n    };\n\n    IonicWebView.setServerAssetPath = (path: any) => {\n      Plugins?.WebView?.setServerAssetPath({ path });\n    };\n\n    IonicWebView.setServerBasePath = (path: any) => {\n      Plugins?.WebView?.setServerBasePath({ path });\n    };\n\n    IonicWebView.persistServerBasePath = () => {\n      Plugins?.WebView?.persistServerBasePath();\n    };\n\n    IonicWebView.convertFileSrc = (url: string) => cap.convertFileSrc(url);\n\n    win.Capacitor = cap;\n    win.Ionic.WebView = IonicWebView;\n  };\n\n  const initLogger = (win: WindowCapacitor, cap: CapacitorInstance) => {\n    const BRIDGED_CONSOLE_METHODS: (keyof Console)[] = ['debug', 'error', 'info', 'log', 'trace', 'warn'];\n\n    const createLogFromNative = (c: Partial<Console>) => (result: PluginResult) => {\n      if (isFullConsole(c)) {\n        const success = result.success === true;\n\n        const tagStyles = success\n          ? 'font-style: italic; font-weight: lighter; color: gray'\n          : 'font-style: italic; font-weight: lighter; color: red';\n        c.groupCollapsed(\n          '%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')',\n          tagStyles,\n          'font-style: italic; font-weight: bold; color: #444',\n        );\n        if (result.success === false) {\n          c.error(result.error);\n        } else {\n          c.dir(JSON.stringify(result.data));\n        }\n        c.groupEnd();\n      } else {\n        if (result.success === false) {\n          c.error('LOG FROM NATIVE', result.error);\n        } else {\n          c.log('LOG FROM NATIVE', result.data);\n        }\n      }\n    };\n\n    const createLogToNative = (c: Partial<Console>) => (call: MessageCallData) => {\n      if (isFullConsole(c)) {\n        c.groupCollapsed(\n          '%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')',\n          'font-weight: lighter; color: gray',\n          'font-weight: bold; color: #000',\n        );\n        c.dir(call);\n        c.groupEnd();\n      } else {\n        c.log('LOG TO NATIVE: ', call);\n      }\n    };\n\n    const isFullConsole = (c: Partial<Console>): c is Console => {\n      if (!c) {\n        return false;\n      }\n\n      return typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function';\n    };\n\n    const serializeConsoleMessage = (msg: any): string => {\n      try {\n        if (typeof msg === 'object') {\n          msg = JSON.stringify(msg);\n        }\n        return String(msg);\n      } catch (e) {\n        return '';\n      }\n    };\n\n    const platform = getPlatformId(win);\n\n    if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') {\n      // add DOM ready listener for System Bars\n      document.addEventListener('DOMContentLoaded', function () {\n        win.CapacitorSystemBarsAndroidInterface.onDOMReady();\n      });\n    }\n\n    if (platform == 'android' || platform == 'ios') {\n      // patch document.cookie on Android/iOS\n      win.CapacitorCookiesDescriptor =\n        Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||\n        Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');\n\n      let doPatchCookies = false;\n\n      // check if capacitor cookies is disabled before patching\n      if (platform === 'ios') {\n        // Use prompt to synchronously get capacitor cookies config.\n        // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n\n        const payload = {\n          type: 'CapacitorCookies.isEnabled',\n        };\n\n        const isCookiesEnabled = prompt(JSON.stringify(payload));\n        if (isCookiesEnabled === 'true') {\n          doPatchCookies = true;\n        }\n      } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n        const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled();\n        if (isCookiesEnabled === true) {\n          doPatchCookies = true;\n        }\n      }\n\n      if (doPatchCookies) {\n        Object.defineProperty(document, 'cookie', {\n          get: function () {\n            if (platform === 'ios') {\n              // Use prompt to synchronously get cookies.\n              // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n\n              const payload = {\n                type: 'CapacitorCookies.get',\n              };\n\n              const res = prompt(JSON.stringify(payload));\n              return res;\n            } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n              // return original document.cookie since Android does not support filtering of `httpOnly` cookies\n              return win.CapacitorCookiesDescriptor?.get?.call(document) ?? '';\n            }\n          },\n          set: function (val) {\n            const cookiePairs = val.split(';');\n            const domainSection = val.toLowerCase().split('domain=')[1];\n            const domain =\n              cookiePairs.length > 1 && domainSection != null && domainSection.length > 0\n                ? domainSection.split(';')[0].trim()\n                : '';\n\n            if (platform === 'ios') {\n              // Use prompt to synchronously set cookies.\n              // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n\n              const payload = {\n                type: 'CapacitorCookies.set',\n                action: val,\n                domain,\n              };\n\n              prompt(JSON.stringify(payload));\n            } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n              win.CapacitorCookiesAndroidInterface.setCookie(domain, val);\n            }\n          },\n        });\n      }\n\n      // patch fetch / XHR on Android/iOS\n      // store original fetch & XHR functions\n      win.CapacitorWebFetch = window.fetch;\n      win.CapacitorWebXMLHttpRequest = {\n        abort: window.XMLHttpRequest.prototype.abort,\n        constructor: window.XMLHttpRequest.prototype.constructor,\n        fullObject: window.XMLHttpRequest,\n        getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders,\n        getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader,\n        open: window.XMLHttpRequest.prototype.open,\n        prototype: window.XMLHttpRequest.prototype,\n        send: window.XMLHttpRequest.prototype.send,\n        setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader,\n      };\n\n      let doPatchHttp = false;\n\n      // check if capacitor http is disabled before patching\n      if (platform === 'ios') {\n        // Use prompt to synchronously get capacitor http config.\n        // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n\n        const payload = {\n          type: 'CapacitorHttp',\n        };\n\n        const isHttpEnabled = prompt(JSON.stringify(payload));\n        if (isHttpEnabled === 'true') {\n          doPatchHttp = true;\n        }\n      } else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') {\n        const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled();\n        if (isHttpEnabled === true) {\n          doPatchHttp = true;\n        }\n      }\n\n      if (doPatchHttp) {\n        // fetch patch\n        window.fetch = async (resource: RequestInfo | URL, options?: RequestInit) => {\n          const headers = new Headers(options?.headers);\n          const contentType = headers.get('Content-Type') || headers.get('content-type');\n          if (\n            options?.body instanceof FormData &&\n            contentType?.includes('multipart/form-data') &&\n            !contentType.includes('boundary')\n          ) {\n            headers.delete('Content-Type');\n            headers.delete('content-type');\n            options.headers = headers;\n          }\n          const request = new Request(resource, options);\n          if (request.url.startsWith(`${cap.getServerUrl()}/`)) {\n            return win.CapacitorWebFetch(resource, options);\n          }\n          const { method } = request;\n          if (\n            method.toLocaleUpperCase() === 'GET' ||\n            method.toLocaleUpperCase() === 'HEAD' ||\n            method.toLocaleUpperCase() === 'OPTIONS' ||\n            method.toLocaleUpperCase() === 'TRACE'\n          ) {\n            // a workaround for following android webview issue:\n            // https://issues.chromium.org/issues/40450316\n            // Sets the user-agent header to a custom value so that its not stripped\n            // on its way to the native layer\n            if (platform === 'android' && options?.headers) {\n              const userAgent = headers.get('User-Agent') || headers.get('user-agent');\n              if (userAgent !== null) {\n                headers.set('x-cap-user-agent', userAgent);\n                options.headers = headers;\n              }\n            }\n\n            if (typeof resource === 'string') {\n              return await win.CapacitorWebFetch(createProxyUrl(resource, win), options);\n            } else if (resource instanceof URL) {\n              const modifiedURL = new URL(createProxyUrl(resource.toString(), win));\n              return await win.CapacitorWebFetch(modifiedURL, options);\n            } else if (resource instanceof Request) {\n              const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource);\n              return await win.CapacitorWebFetch(modifiedRequest, options);\n            }\n          }\n\n          const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`;\n          console.time(tag);\n\n          try {\n            const { body } = request;\n            const optionHeaders = Object.fromEntries(request.headers.entries());\n            const {\n              data: requestData,\n              type,\n              headers: requestHeaders,\n            } = await convertBody(\n              options?.body || body || undefined,\n              optionHeaders['Content-Type'] || optionHeaders['content-type'],\n            );\n\n            const nativeHeaders = {\n              ...requestHeaders,\n              ...optionHeaders,\n            };\n\n            if (platform === 'android') {\n              if (headers.has('User-Agent')) {\n                nativeHeaders['User-Agent'] = headers.get('User-Agent');\n              }\n\n              if (headers.has('user-agent')) {\n                nativeHeaders['user-agent'] = headers.get('user-agent');\n              }\n            }\n\n            const nativeResponse: HttpResponse = await cap.nativePromise('CapacitorHttp', 'request', {\n              url: request.url,\n              method: method,\n              data: requestData,\n              dataType: type,\n              headers: nativeHeaders,\n            });\n\n            const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'];\n            let data = contentType?.startsWith('application/json')\n              ? JSON.stringify(nativeResponse.data)\n              : nativeResponse.data;\n\n            // use null data for 204 No Content HTTP response\n            if (nativeResponse.status === 204) {\n              data = null;\n            }\n\n            // intercept & parse response before returning\n            const response = new Response(data, {\n              headers: nativeResponse.headers,\n              status: nativeResponse.status,\n            });\n\n            /*\n             * copy url to response, `cordova-plugin-ionic` uses this url from the response\n             * we need `Object.defineProperty` because url is an inherited getter on the Response\n             * see: https://stackoverflow.com/a/57382543\n             * */\n            Object.defineProperty(response, 'url', {\n              value: nativeResponse.url,\n            });\n\n            console.timeEnd(tag);\n            return response;\n          } catch (error) {\n            console.timeEnd(tag);\n            return Promise.reject(error);\n          }\n        };\n\n        // XHR patch\n        interface PatchedXMLHttpRequestConstructor extends XMLHttpRequest {\n          new (): XMLHttpRequest;\n        }\n\n        window.XMLHttpRequest = function () {\n          const xhr = new win.CapacitorWebXMLHttpRequest.constructor();\n          Object.defineProperties(xhr, {\n            _headers: {\n              value: {},\n              writable: true,\n            },\n            _method: {\n              value: xhr.method,\n              writable: true,\n            },\n          });\n          const prototype = win.CapacitorWebXMLHttpRequest.prototype;\n\n          const isProgressEventAvailable = () =>\n            typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event;\n\n          // XHR patch abort\n          prototype.abort = function () {\n            if (isRelativeOrProxyUrl(this._url)) {\n              return win.CapacitorWebXMLHttpRequest.abort.call(this);\n            }\n            this.readyState = 0;\n            setTimeout(() => {\n              this.dispatchEvent(new Event('abort'));\n              this.dispatchEvent(new Event('loadend'));\n            });\n          };\n\n          // XHR patch open\n          prototype.open = function (method: string, url: string) {\n            this._method = method.toLocaleUpperCase();\n            this._url = url;\n\n            if (\n              !this._method ||\n              this._method === 'GET' ||\n              this._method === 'HEAD' ||\n              this._method === 'OPTIONS' ||\n              this._method === 'TRACE'\n            ) {\n              if (isRelativeOrProxyUrl(url)) {\n                return win.CapacitorWebXMLHttpRequest.open.call(this, method, url);\n              }\n\n              this._url = createProxyUrl(this._url, win);\n\n              return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url);\n            }\n            Object.defineProperties(this, {\n              readyState: {\n                get: function () {\n                  return this._readyState ?? 0;\n                },\n                set: function (val: number) {\n                  this._readyState = val;\n                  setTimeout(() => {\n                    this.dispatchEvent(new Event('readystatechange'));\n                  });\n                },\n              },\n            });\n            setTimeout(() => {\n              this.dispatchEvent(new Event('loadstart'));\n            });\n            this.readyState = 1;\n          };\n\n          // XHR patch set request header\n          prototype.setRequestHeader = function (header: string, value: string) {\n            // a workaround for the following android web view issue:\n            // https://issues.chromium.org/issues/40450316\n            // Sets the user-agent header to a custom value so that its not stripped\n            // on its way to the native layer\n            if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) {\n              header = 'x-cap-user-agent';\n            }\n\n            if (isRelativeOrProxyUrl(this._url)) {\n              return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value);\n            }\n            this._headers[header] = value;\n          };\n\n          // XHR patch send\n          prototype.send = function (body?: Document | XMLHttpRequestBodyInit) {\n            if (isRelativeOrProxyUrl(this._url)) {\n              return win.CapacitorWebXMLHttpRequest.send.call(this, body);\n            }\n\n            const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`;\n            console.time(tag);\n\n            try {\n              this.readyState = 2;\n\n              Object.defineProperties(this, {\n                response: {\n                  value: '',\n                  writable: true,\n                },\n                responseText: {\n                  value: '',\n                  writable: true,\n                },\n                responseURL: {\n                  value: '',\n                  writable: true,\n                },\n                status: {\n                  value: 0,\n                  writable: true,\n                },\n              });\n\n              convertBody(body).then(({ data, type, headers }) => {\n                let otherHeaders =\n                  this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined;\n\n                if (body instanceof FormData) {\n                  if (!this._headers['Content-Type'] && !this._headers['content-type']) {\n                    otherHeaders = {\n                      ...otherHeaders,\n                      'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}`,\n                    };\n                  }\n                }\n\n                // intercept request & pass to the bridge\n                cap\n                  .nativePromise('CapacitorHttp', 'request', {\n                    url: this._url,\n                    method: this._method,\n                    data: data !== null ? data : undefined,\n                    headers: {\n                      ...headers,\n                      ...otherHeaders,\n                    },\n                    dataType: type,\n                  })\n                  .then((nativeResponse: any) => {\n                    // intercept & parse response before returning\n                    if (this.readyState == 2) {\n                      //TODO: Add progress event emission on native side\n                      if (isProgressEventAvailable()) {\n                        this.dispatchEvent(\n                          new ProgressEvent('progress', {\n                            lengthComputable: true,\n                            loaded: nativeResponse.data.length,\n                            total: nativeResponse.data.length,\n                          }),\n                        );\n                      }\n                      this._headers = nativeResponse.headers;\n                      this.status = nativeResponse.status;\n                      if (this.responseType === '' || this.responseType === 'text') {\n                        this.response =\n                          typeof nativeResponse.data !== 'string'\n                            ? JSON.stringify(nativeResponse.data)\n                            : nativeResponse.data;\n                      } else {\n                        this.response = nativeResponse.data;\n                      }\n                      this.responseText = (\n                        nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']\n                      )?.startsWith('application/json')\n                        ? JSON.stringify(nativeResponse.data)\n                        : nativeResponse.data;\n                      this.responseURL = nativeResponse.url;\n                      this.readyState = 4;\n                      setTimeout(() => {\n                        this.dispatchEvent(new Event('load'));\n                        this.dispatchEvent(new Event('loadend'));\n                      });\n                    }\n                    console.timeEnd(tag);\n                  })\n                  .catch((error: any) => {\n                    this.status = error.status;\n                    this._headers = error.headers;\n                    this.response = error.data;\n                    this.responseText = JSON.stringify(error.data);\n                    this.responseURL = error.url;\n                    this.readyState = 4;\n                    if (isProgressEventAvailable()) {\n                      this.dispatchEvent(\n                        new ProgressEvent('progress', {\n                          lengthComputable: false,\n                          loaded: 0,\n                          total: 0,\n                        }),\n                      );\n                    }\n                    setTimeout(() => {\n                      this.dispatchEvent(new Event('error'));\n                      this.dispatchEvent(new Event('loadend'));\n                    });\n                    console.timeEnd(tag);\n                  });\n              });\n            } catch (error) {\n              this.status = 500;\n              this._headers = {};\n              this.response = error;\n              this.responseText = error.toString();\n              this.responseURL = this._url;\n              this.readyState = 4;\n              if (isProgressEventAvailable()) {\n                this.dispatchEvent(\n                  new ProgressEvent('progress', {\n                    lengthComputable: false,\n                    loaded: 0,\n                    total: 0,\n                  }),\n                );\n              }\n              setTimeout(() => {\n                this.dispatchEvent(new Event('error'));\n                this.dispatchEvent(new Event('loadend'));\n              });\n              console.timeEnd(tag);\n            }\n          };\n\n          // XHR patch getAllResponseHeaders\n          prototype.getAllResponseHeaders = function () {\n            if (isRelativeOrProxyUrl(this._url)) {\n              return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this);\n            }\n\n            let returnString = '';\n            for (const key in this._headers) {\n              if (key != 'Set-Cookie') {\n                returnString += key + ': ' + this._headers[key] + '\\r\\n';\n              }\n            }\n            return returnString;\n          };\n\n          // XHR patch getResponseHeader\n          prototype.getResponseHeader = function (name: string) {\n            if (isRelativeOrProxyUrl(this._url)) {\n              return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name);\n            }\n            return this._headers[name];\n          };\n\n          Object.setPrototypeOf(xhr, prototype);\n          return xhr;\n        } as unknown as PatchedXMLHttpRequestConstructor;\n\n        Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject);\n      }\n    }\n\n    // patch window.console on iOS and store original console fns\n    const isIos = getPlatformId(win) === 'ios';\n    if (win.console && isIos) {\n      Object.defineProperties(\n        win.console,\n        BRIDGED_CONSOLE_METHODS.reduce((props: any, method) => {\n          const consoleMethod = win.console[method].bind(win.console);\n          props[method] = {\n            value: (...args: any[]) => {\n              const msgs = [...args];\n              cap.toNative('Console', 'log', {\n                level: method,\n                message: msgs.map(serializeConsoleMessage).join(' '),\n              });\n              return consoleMethod(...args);\n            },\n          };\n          return props;\n        }, {}),\n      );\n    }\n\n    cap.logJs = (msg, level) => {\n      switch (level) {\n        case 'error':\n          win.console.error(msg);\n          break;\n        case 'warn':\n          win.console.warn(msg);\n          break;\n        case 'info':\n          win.console.info(msg);\n          break;\n        default:\n          win.console.log(msg);\n      }\n    };\n\n    cap.logToNative = createLogToNative(win.console);\n    cap.logFromNative = createLogFromNative(win.console);\n\n    cap.handleError = (err) => win.console.error(err);\n\n    win.Capacitor = cap;\n  };\n\n  function initNativeBridge(win: WindowCapacitor) {\n    const cap = win.Capacitor || ({} as CapacitorInstance);\n\n    // keep a collection of callbacks for native response data\n    const callbacks = new Map();\n\n    const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : '';\n    cap.getServerUrl = () => webviewServerUrl;\n    cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath);\n\n    // Counter of callback ids, randomized to avoid\n    // any issues during reloads if a call comes back with\n    // an existing callback id from an old session\n    let callbackIdCount = Math.floor(Math.random() * 134217728);\n\n    let postToNative: (data: CallData) => void | null = null;\n\n    const isNativePlatform = () => true;\n    const getPlatform = () => getPlatformId(win);\n\n    cap.getPlatform = getPlatform;\n    cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name);\n    cap.isNativePlatform = isNativePlatform;\n\n    // create the postToNative() fn if needed\n    if (getPlatformId(win) === 'android') {\n      // android platform\n      postToNative = (data) => {\n        try {\n          win.androidBridge.postMessage(JSON.stringify(data));\n        } catch (e) {\n          win?.console?.error(e);\n        }\n      };\n    } else if (getPlatformId(win) === 'ios') {\n      // ios platform\n      postToNative = (data) => {\n        try {\n          data.type = data.type ? data.type : 'message';\n          win.webkit.messageHandlers.bridge.postMessage(data);\n        } catch (e) {\n          win?.console?.error(e);\n        }\n      };\n    }\n\n    cap.handleWindowError = (msg, url, lineNo, columnNo, err) => {\n      const str = (msg as string).toLowerCase();\n\n      if (str.indexOf('script error') > -1) {\n        // Some IE issue?\n      } else {\n        const errObj: ErrorCallData = {\n          type: 'js.error',\n          error: {\n            message: msg as string,\n            url: url,\n            line: lineNo,\n            col: columnNo,\n            errorObject: JSON.stringify(err),\n          },\n        };\n\n        if (err !== null) {\n          cap.handleError(err);\n        }\n\n        postToNative(errObj);\n      }\n\n      return false;\n    };\n\n    if (cap.DEBUG) {\n      window.onerror = cap.handleWindowError;\n    }\n\n    initLogger(win, cap);\n\n    /**\n     * Send a plugin method call to the native layer\n     */\n    cap.toNative = (pluginName, methodName, options, storedCallback) => {\n      try {\n        if (typeof postToNative === 'function') {\n          let callbackId = '-1';\n\n          if (\n            storedCallback &&\n            (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function')\n          ) {\n            // store the call for later lookup\n            callbackId = String(++callbackIdCount);\n            callbacks.set(callbackId, storedCallback);\n          }\n\n          const callData = {\n            callbackId: callbackId,\n            pluginId: pluginName,\n            methodName: methodName,\n            options: options || {},\n          };\n\n          if (cap.isLoggingEnabled && pluginName !== 'Console') {\n            cap.logToNative(callData);\n          }\n\n          // post the call data to native\n          postToNative(callData);\n\n          return callbackId;\n        } else {\n          win?.console?.warn(`implementation unavailable for: ${pluginName}`);\n        }\n      } catch (e) {\n        win?.console?.error(e);\n      }\n\n      return null;\n    };\n\n    if (win?.androidBridge) {\n      win.androidBridge.onmessage = function (event) {\n        returnResult(JSON.parse(event.data));\n      };\n    }\n\n    /**\n     * Process a response from the native layer.\n     */\n    cap.fromNative = (result) => {\n      returnResult(result);\n    };\n\n    const returnResult = (result: any) => {\n      if (cap.isLoggingEnabled && result.pluginId !== 'Console') {\n        cap.logFromNative(result);\n      }\n\n      // get the stored call, if it exists\n      try {\n        const storedCall = callbacks.get(result.callbackId);\n\n        if (storedCall) {\n          // looks like we've got a stored call\n\n          if (result.error) {\n            // ensure stacktraces by copying error properties to an Error\n            result.error = Object.keys(result.error).reduce((err, key) => {\n              // use any type to avoid importing util and compiling most of .ts files\n              (err as any)[key] = (result as any).error[key];\n              return err;\n            }, new cap.Exception(''));\n          }\n\n          if (typeof storedCall.callback === 'function') {\n            // callback\n            if (result.success) {\n              storedCall.callback(result.data);\n            } else {\n              storedCall.callback(null, result.error);\n            }\n          } else if (typeof storedCall.resolve === 'function') {\n            // promise\n            if (result.success) {\n              storedCall.resolve(result.data);\n            } else {\n              storedCall.reject(result.error);\n            }\n\n            // no need to keep this stored callback\n            // around for a one time resolve promise\n            callbacks.delete(result.callbackId);\n          }\n        } else if (!result.success && result.error) {\n          // no stored callback, but if there was an error let's log it\n          win?.console?.warn(result.error);\n        }\n\n        if (result.save === false) {\n          callbacks.delete(result.callbackId);\n        }\n      } catch (e) {\n        win?.console?.error(e);\n      }\n\n      // always delete to prevent memory leaks\n      // overkill but we're not sure what apps will do with this data\n      delete result.data;\n      delete result.error;\n    };\n\n    cap.nativeCallback = (pluginName, methodName, options, callback) => {\n      if (typeof options === 'function') {\n        console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`);\n\n        callback = options as any;\n        options = null;\n      }\n\n      return cap.toNative(pluginName, methodName, options, { callback });\n    };\n\n    cap.nativePromise = (pluginName, methodName, options) => {\n      return new Promise((resolve, reject) => {\n        cap.toNative(pluginName, methodName, options, {\n          resolve: resolve,\n          reject: reject,\n        });\n      });\n    };\n\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    cap.withPlugin = (_pluginId, _fn) => dummy;\n\n    cap.Exception = CapacitorException;\n\n    initEvents(win, cap);\n    initLegacyHandlers(win, cap);\n    initVendor(win, cap);\n\n    win.Capacitor = cap;\n  }\n\n  initNativeBridge(w);\n};\n\ninitBridge(\n  typeof globalThis !== 'undefined'\n    ? (globalThis as WindowCapacitor)\n    : typeof self !== 'undefined'\n      ? (self as WindowCapacitor)\n      : typeof window !== 'undefined'\n        ? (window as WindowCapacitor)\n        : typeof global !== 'undefined'\n          ? (global as WindowCapacitor)\n          : ({} as WindowCapacitor),\n);\n\n// Export only for tests\nexport { initBridge };\n"
  },
  {
    "path": "core/package.json",
    "content": "{\n  \"name\": \"@capacitor/core\",\n  \"version\": \"8.2.0\",\n  \"description\": \"Capacitor: Cross-platform apps with JavaScript and the web\",\n  \"homepage\": \"https://capacitorjs.com\",\n  \"author\": \"Ionic Team <hi@ionic.io> (https://ionic.io)\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/ionic-team/capacitor.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/ionic-team/capacitor/issues\"\n  },\n  \"files\": [\n    \"dist/\",\n    \"types/\",\n    \"cookies.md\",\n    \"cordova.js\",\n    \"http.md\",\n    \"system-bars.md\"\n  ],\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.js\",\n  \"types\": \"types/index.d.ts\",\n  \"unpkg\": \"dist/capacitor.js\",\n  \"scripts\": {\n    \"build\": \"npm run clean && npm run docgen && npm run transpile && npm run rollup\",\n    \"build:nativebridge\": \"tsc native-bridge.ts --target es2017 --moduleResolution node --outDir build && rollup --config rollup.bridge.config.js\",\n    \"clean\": \"rimraf dist\",\n    \"docgen\": \"docgen --api CapacitorCookiesPlugin --output-readme cookies.md && docgen --api CapacitorHttpPlugin --output-readme http.md && docgen --api SystemBarsPlugin --output-readme system-bars.md\",\n    \"prepublishOnly\": \"npm run build\",\n    \"rollup\": \"rollup --config rollup.config.js\",\n    \"transpile\": \"tsc\",\n    \"test\": \"jest\",\n    \"test.watch\": \"jest --watchAll\",\n    \"test.treeshaking\": \"node src/tests/build-treeshaking.js\"\n  },\n  \"dependencies\": {\n    \"tslib\": \"^2.1.0\"\n  },\n  \"devDependencies\": {\n    \"@capacitor/docgen\": \"^0.2.2\",\n    \"@rollup/plugin-node-resolve\": \"^10.0.0\",\n    \"@rollup/plugin-replace\": \"^2.4.2\",\n    \"@types/jest\": \"^29.5.0\",\n    \"jest\": \"^29.5.0\",\n    \"jest-environment-jsdom\": \"^29.5.0\",\n    \"jest-jasmine2\": \"^29.5.0\",\n    \"rimraf\": \"^4.4.1\",\n    \"rollup\": \"^2.21.0\",\n    \"rollup-plugin-terser\": \"^7.0.2\",\n    \"typescript\": \"~5.0.2\"\n  },\n  \"jest\": {\n    \"preset\": \"ts-jest\",\n    \"testRunner\": \"jest-jasmine2\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  }\n}\n"
  },
  {
    "path": "core/rollup.bridge.config.js",
    "content": "import replace from '@rollup/plugin-replace';\n\nconst banner = `\n/*! Capacitor: https://capacitorjs.com/ - MIT License */\n/* Generated File. Do not edit. */\n`;\n\nexport default {\n  input: 'build/native-bridge.js',\n  output: [\n    {\n      file: '../android/capacitor/src/main/assets/native-bridge.js',\n      format: 'iife',\n      name: 'nativeBridge',\n      preferConst: true,\n      banner,\n      sourcemap: false,\n    },\n    {\n      file: '../ios/Capacitor/Capacitor/assets/native-bridge.js',\n      format: 'iife',\n      name: 'nativeBridge',\n      preferConst: true,\n      banner,\n      sourcemap: false,\n    },\n  ],\n  // Remove any references to module.exports or exports.__esModule by replacing them with unused variables\n  plugins: [\n    replace({\n      'module.exports': 'dummy',\n      'exports.__esModule': 'dummy',\n      'exports.initBridge': 'dummy',\n      preventAssignment: false,\n    }),\n  ],\n};\n"
  },
  {
    "path": "core/rollup.config.js",
    "content": "import nodeResolve from '@rollup/plugin-node-resolve';\nimport { terser } from 'rollup-plugin-terser';\n\nconst banner = '/*! Capacitor: https://capacitorjs.com/ - MIT License */';\n\nexport default {\n  input: 'build/index.js',\n  output: [\n    {\n      file: 'dist/capacitor.js',\n      format: 'iife',\n      name: 'capacitorExports',\n      preferConst: true,\n      banner,\n      sourcemap: true,\n      plugins: [terser()],\n    },\n    {\n      file: 'dist/index.js',\n      format: 'esm',\n      preferConst: true,\n      banner,\n      sourcemap: true,\n    },\n    {\n      file: 'dist/index.cjs.js',\n      format: 'cjs',\n      banner,\n      sourcemap: true,\n      inlineDynamicImports: true,\n    },\n  ],\n  plugins: [nodeResolve()],\n};\n"
  },
  {
    "path": "core/src/core-plugins.ts",
    "content": "import type { Plugin } from './definitions';\nimport { registerPlugin } from './global';\nimport { WebPlugin } from './web-plugin';\n\n/******** WEB VIEW PLUGIN ********/\nexport interface WebViewPlugin extends Plugin {\n  setServerAssetPath(options: WebViewPath): Promise<void>;\n  setServerBasePath(options: WebViewPath): Promise<void>;\n  getServerBasePath(): Promise<WebViewPath>;\n  persistServerBasePath(): Promise<void>;\n}\n\nexport interface WebViewPath {\n  path: string;\n}\n\nexport const WebView = /*#__PURE__*/ registerPlugin<WebViewPlugin>('WebView');\n/******** END WEB VIEW PLUGIN ********/\n\n/******** COOKIES PLUGIN ********/\n/**\n * Safely web encode a string value (inspired by js-cookie)\n * @param str The string value to encode\n */\nconst encode = (str: string) =>\n  encodeURIComponent(str)\n    .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)\n    .replace(/[()]/g, escape);\n\n/**\n * Safely web decode a string value (inspired by js-cookie)\n * @param str The string value to decode\n */\nconst decode = (str: string): string => str.replace(/(%[\\dA-F]{2})+/gi, decodeURIComponent);\n\nexport interface CapacitorCookiesPlugin {\n  getCookies(options?: GetCookieOptions): Promise<HttpCookieMap>;\n  /**\n   * Write a cookie to the device.\n   */\n  setCookie(options: SetCookieOptions): Promise<void>;\n  /**\n   * Delete a cookie from the device.\n   */\n  deleteCookie(options: DeleteCookieOptions): Promise<void>;\n  /**\n   * Clear cookies from the device at a given URL.\n   */\n  clearCookies(options: ClearCookieOptions): Promise<void>;\n  /**\n   * Clear all cookies on the device.\n   */\n  clearAllCookies(): Promise<void>;\n}\n\ninterface HttpCookie {\n  /**\n   * The URL of the cookie.\n   */\n  url?: string;\n  /**\n   * The key of the cookie.\n   */\n  key: string;\n  /**\n   * The value of the cookie.\n   */\n  value: string;\n}\n\ninterface HttpCookieMap {\n  [key: string]: string;\n}\n\ninterface HttpCookieExtras {\n  /**\n   * The path to write the cookie to.\n   */\n  path?: string;\n  /**\n   * The date to expire the cookie.\n   */\n  expires?: string;\n}\n\nexport type GetCookieOptions = Omit<HttpCookie, 'key' | 'value'>;\nexport type SetCookieOptions = HttpCookie & HttpCookieExtras;\nexport type DeleteCookieOptions = Omit<HttpCookie, 'value'>;\nexport type ClearCookieOptions = Omit<HttpCookie, 'key' | 'value'>;\n\nexport class CapacitorCookiesPluginWeb extends WebPlugin implements CapacitorCookiesPlugin {\n  async getCookies(): Promise<HttpCookieMap> {\n    const cookies = document.cookie;\n    const cookieMap: HttpCookieMap = {};\n    cookies.split(';').forEach((cookie) => {\n      if (cookie.length <= 0) return;\n      // Replace first \"=\" with CAP_COOKIE to prevent splitting on additional \"=\"\n      let [key, value] = cookie.replace(/=/, 'CAP_COOKIE').split('CAP_COOKIE');\n      key = decode(key).trim();\n      value = decode(value).trim();\n      cookieMap[key] = value;\n    });\n    return cookieMap;\n  }\n\n  async setCookie(options: SetCookieOptions): Promise<void> {\n    try {\n      // Safely Encoded Key/Value\n      const encodedKey = encode(options.key);\n      const encodedValue = encode(options.value);\n\n      // Clean & sanitize options\n      const expires = options.expires ? `; expires=${options.expires.replace('expires=', '')}` : '';\n\n      const path = (options.path || '/').replace('path=', ''); // Default is \"path=/\"\n      const domain = options.url != null && options.url.length > 0 ? `domain=${options.url}` : '';\n\n      document.cookie = `${encodedKey}=${encodedValue || ''}${expires}; path=${path}; ${domain};`;\n    } catch (error) {\n      return Promise.reject(error);\n    }\n  }\n\n  async deleteCookie(options: DeleteCookieOptions): Promise<void> {\n    try {\n      document.cookie = `${options.key}=; Max-Age=0`;\n    } catch (error) {\n      return Promise.reject(error);\n    }\n  }\n\n  async clearCookies(): Promise<void> {\n    try {\n      const cookies = document.cookie.split(';') || [];\n      for (const cookie of cookies) {\n        document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`);\n      }\n    } catch (error) {\n      return Promise.reject(error);\n    }\n  }\n\n  async clearAllCookies(): Promise<void> {\n    try {\n      await this.clearCookies();\n    } catch (error) {\n      return Promise.reject(error);\n    }\n  }\n}\n\nexport const CapacitorCookies = registerPlugin<CapacitorCookiesPlugin>('CapacitorCookies', {\n  web: () => new CapacitorCookiesPluginWeb(),\n});\n\n/******** END COOKIES PLUGIN ********/\n\n/******** HTTP PLUGIN ********/\nexport interface CapacitorHttpPlugin {\n  /**\n   * Make a Http Request to a server using native libraries.\n   */\n  request(options: HttpOptions): Promise<HttpResponse>;\n  /**\n   * Make a Http GET Request to a server using native libraries.\n   */\n  get(options: HttpOptions): Promise<HttpResponse>;\n  /**\n   * Make a Http POST Request to a server using native libraries.\n   */\n  post(options: HttpOptions): Promise<HttpResponse>;\n  /**\n   * Make a Http PUT Request to a server using native libraries.\n   */\n  put(options: HttpOptions): Promise<HttpResponse>;\n  /**\n   * Make a Http PATCH Request to a server using native libraries.\n   */\n  patch(options: HttpOptions): Promise<HttpResponse>;\n  /**\n   * Make a Http DELETE Request to a server using native libraries.\n   */\n  delete(options: HttpOptions): Promise<HttpResponse>;\n}\n\n/**\n * How to parse the Http response before returning it to the client.\n */\nexport type HttpResponseType = 'arraybuffer' | 'blob' | 'json' | 'text' | 'document';\n\nexport interface HttpOptions {\n  /**\n   * The URL to send the request to.\n   */\n  url: string;\n  /**\n   * The Http Request method to run. (Default is GET)\n   */\n  method?: string;\n  /**\n   * URL parameters to append to the request.\n   */\n  params?: HttpParams;\n  /**\n   * Note: On Android and iOS, data can only be a string or a JSON.\n   * FormData, Blob, ArrayBuffer, and other complex types are only directly supported on web\n   * or through enabling `CapacitorHttp` in the config and using the patched `window.fetch` or `XMLHttpRequest`.\n   *\n   * If you need to send a complex type, you should serialize the data to base64\n   * and set the `headers[\"Content-Type\"]` and `dataType` attributes accordingly.\n   */\n  data?: any;\n  /**\n   * Http Request headers to send with the request.\n   */\n  headers?: HttpHeaders;\n  /**\n   * How long to wait to read additional data in milliseconds.\n   * Resets each time new data is received.\n   */\n  readTimeout?: number;\n  /**\n   * How long to wait for the initial connection in milliseconds.\n   */\n  connectTimeout?: number;\n  /**\n   * Sets whether automatic HTTP redirects should be disabled\n   */\n  disableRedirects?: boolean;\n  /**\n   * Extra arguments for fetch when running on the web\n   */\n  webFetchExtra?: RequestInit;\n  /**\n   * This is used to parse the response appropriately before returning it to\n   * the requestee. If the response content-type is \"json\", this value is ignored.\n   */\n  responseType?: HttpResponseType;\n  /**\n   * Use this option if you need to keep the URL unencoded in certain cases\n   * (already encoded, azure/firebase testing, etc.). The default is _true_.\n   */\n  shouldEncodeUrlParams?: boolean;\n  /**\n   * This is used if we've had to convert the data from a JS type that needs\n   * special handling in the native layer\n   */\n  dataType?: 'file' | 'formData';\n}\n\nexport interface HttpParams {\n  /**\n   * A key/value dictionary of URL parameters to set.\n   */\n  [key: string]: string | string[];\n}\n\nexport interface HttpHeaders {\n  /**\n   * A key/value dictionary of Http headers.\n   */\n  [key: string]: string;\n}\n\nexport interface HttpResponse {\n  /**\n   * Additional data received with the Http response.\n   */\n  data: any;\n  /**\n   * The status code received from the Http response.\n   */\n  status: number;\n  /**\n   * The headers received from the Http response.\n   */\n  headers: HttpHeaders;\n  /**\n   * The response URL received from the Http response.\n   */\n  url: string;\n}\n\n// UTILITY FUNCTIONS\n\n/**\n * Read in a Blob value and return it as a base64 string\n * @param blob The blob value to convert to a base64 string\n */\nexport const readBlobAsBase64 = async (blob: Blob): Promise<string> =>\n  new Promise((resolve, reject) => {\n    const reader = new FileReader();\n    reader.onload = () => {\n      const base64String = reader.result as string;\n      // remove prefix \"data:application/pdf;base64,\"\n      resolve(base64String.indexOf(',') >= 0 ? base64String.split(',')[1] : base64String);\n    };\n    reader.onerror = (error: any) => reject(error);\n    reader.readAsDataURL(blob);\n  });\n\n/**\n * Normalize an HttpHeaders map by lowercasing all of the values\n * @param headers The HttpHeaders object to normalize\n */\nconst normalizeHttpHeaders = (headers: HttpHeaders = {}): HttpHeaders => {\n  const originalKeys = Object.keys(headers);\n  const loweredKeys = Object.keys(headers).map((k) => k.toLocaleLowerCase());\n  const normalized = loweredKeys.reduce<HttpHeaders>((acc, key, index) => {\n    acc[key] = headers[originalKeys[index]];\n    return acc;\n  }, {});\n  return normalized;\n};\n\n/**\n * Builds a string of url parameters that\n * @param params A map of url parameters\n * @param shouldEncode true if you should encodeURIComponent() the values (true by default)\n */\nconst buildUrlParams = (params?: HttpParams, shouldEncode = true): string | null => {\n  if (!params) return null;\n\n  const output = Object.entries(params).reduce((accumulator, entry) => {\n    const [key, value] = entry;\n\n    let encodedValue: string;\n    let item: string;\n    if (Array.isArray(value)) {\n      item = '';\n      value.forEach((str) => {\n        encodedValue = shouldEncode ? encodeURIComponent(str) : str;\n        item += `${key}=${encodedValue}&`;\n      });\n      // last character will always be \"&\" so slice it off\n      item.slice(0, -1);\n    } else {\n      encodedValue = shouldEncode ? encodeURIComponent(value) : value;\n      item = `${key}=${encodedValue}`;\n    }\n\n    return `${accumulator}&${item}`;\n  }, '');\n\n  // Remove initial \"&\" from the reduce\n  return output.substr(1);\n};\n\n/**\n * Build the RequestInit object based on the options passed into the initial request\n * @param options The Http plugin options\n * @param extra Any extra RequestInit values\n */\nexport const buildRequestInit = (options: HttpOptions, extra: RequestInit = {}): RequestInit => {\n  const output: RequestInit = {\n    method: options.method || 'GET',\n    headers: options.headers,\n    ...extra,\n  };\n\n  // Get the content-type\n  const headers = normalizeHttpHeaders(options.headers);\n  const type = headers['content-type'] || '';\n\n  // If body is already a string, then pass it through as-is.\n  if (typeof options.data === 'string') {\n    output.body = options.data;\n  }\n  // Build request initializers based off of content-type\n  else if (type.includes('application/x-www-form-urlencoded')) {\n    const params = new URLSearchParams();\n    for (const [key, value] of Object.entries(options.data || {})) {\n      params.set(key, value as any);\n    }\n    output.body = params.toString();\n  } else if (type.includes('multipart/form-data') || options.data instanceof FormData) {\n    const form = new FormData();\n    if (options.data instanceof FormData) {\n      options.data.forEach((value, key) => {\n        form.append(key, value);\n      });\n    } else {\n      for (const key of Object.keys(options.data)) {\n        form.append(key, options.data[key]);\n      }\n    }\n    output.body = form;\n    const headers = new Headers(output.headers);\n    headers.delete('content-type'); // content-type will be set by `window.fetch` to includy boundary\n    output.headers = headers;\n  } else if (type.includes('application/json') || typeof options.data === 'object') {\n    output.body = JSON.stringify(options.data);\n  }\n\n  return output;\n};\n\n// WEB IMPLEMENTATION\nexport class CapacitorHttpPluginWeb extends WebPlugin implements CapacitorHttpPlugin {\n  /**\n   * Perform an Http request given a set of options\n   * @param options Options to build the HTTP request\n   */\n  async request(options: HttpOptions): Promise<HttpResponse> {\n    const requestInit = buildRequestInit(options, options.webFetchExtra);\n    const urlParams = buildUrlParams(options.params, options.shouldEncodeUrlParams);\n    const url = urlParams ? `${options.url}?${urlParams}` : options.url;\n\n    const response = await fetch(url, requestInit);\n    const contentType = response.headers.get('content-type') || '';\n\n    // Default to 'text' responseType so no parsing happens\n    let { responseType = 'text' } = response.ok ? options : {};\n\n    // If the response content-type is json, force the response to be json\n    if (contentType.includes('application/json')) {\n      responseType = 'json';\n    }\n\n    let data: any;\n    let blob: any;\n    switch (responseType) {\n      case 'arraybuffer':\n      case 'blob':\n        blob = await response.blob();\n        data = await readBlobAsBase64(blob);\n        break;\n      case 'json':\n        data = await response.json();\n        break;\n      case 'document':\n      case 'text':\n      default:\n        data = await response.text();\n    }\n\n    // Convert fetch headers to Capacitor HttpHeaders\n    const headers = {} as HttpHeaders;\n    response.headers.forEach((value: string, key: string) => {\n      headers[key] = value;\n    });\n\n    return {\n      data,\n      headers,\n      status: response.status,\n      url: response.url,\n    };\n  }\n\n  /**\n   * Perform an Http GET request given a set of options\n   * @param options Options to build the HTTP request\n   */\n  async get(options: HttpOptions): Promise<HttpResponse> {\n    return this.request({ ...options, method: 'GET' });\n  }\n\n  /**\n   * Perform an Http POST request given a set of options\n   * @param options Options to build the HTTP request\n   */\n  async post(options: HttpOptions): Promise<HttpResponse> {\n    return this.request({ ...options, method: 'POST' });\n  }\n\n  /**\n   * Perform an Http PUT request given a set of options\n   * @param options Options to build the HTTP request\n   */\n  async put(options: HttpOptions): Promise<HttpResponse> {\n    return this.request({ ...options, method: 'PUT' });\n  }\n\n  /**\n   * Perform an Http PATCH request given a set of options\n   * @param options Options to build the HTTP request\n   */\n  async patch(options: HttpOptions): Promise<HttpResponse> {\n    return this.request({ ...options, method: 'PATCH' });\n  }\n\n  /**\n   * Perform an Http DELETE request given a set of options\n   * @param options Options to build the HTTP request\n   */\n  async delete(options: HttpOptions): Promise<HttpResponse> {\n    return this.request({ ...options, method: 'DELETE' });\n  }\n}\n\nexport const CapacitorHttp = registerPlugin<CapacitorHttpPlugin>('CapacitorHttp', {\n  web: () => new CapacitorHttpPluginWeb(),\n});\n\n/******** END HTTP PLUGIN ********/\n\n/******** SYSTEM BARS PLUGIN ********/\n\n/**\n * Available status bar styles.\n */\nexport enum SystemBarsStyle {\n  /**\n   * Light system bar content on a dark background.\n   *\n   * @since 8.0.0\n   */\n  Dark = 'DARK',\n\n  /**\n   * For dark system bar content on a light background.\n   *\n   * @since 8.0.0\n   */\n  Light = 'LIGHT',\n\n  /**\n   * The style is based on the device appearance or the underlying content.\n   * If the device is using Dark mode, the system bars content will be light.\n   * If the device is using Light mode, the system bars content will be dark.\n   *\n   * @since 8.0.0\n   */\n  Default = 'DEFAULT',\n}\n\n/**\n * Available status bar animations.  iOS only.\n */\nexport type SystemBarsAnimation = 'FADE' | 'NONE';\n\n/**\n * Available system bar types.\n */\nexport enum SystemBarType {\n  /**\n   * The top status bar on both Android and iOS.\n   *\n   * @since 8.0.0\n   */\n  StatusBar = 'StatusBar',\n  /**\n   * The navigation bar (or gesture bar on iOS) on both Android and iOS.\n   *\n   * @since 8.0.0\n   */\n  NavigationBar = 'NavigationBar',\n}\n\nexport interface SystemBarsStyleOptions {\n  /**\n   * Style of the text and icons of the system bars.\n   *\n   * @since 8.0.0\n   * @default 'DEFAULT'\n   * @example \"DARK\"\n   */\n  style: SystemBarsStyle;\n\n  /**\n   * The system bar to which to apply the style.\n   *\n   *\n   * @since 8.0.0\n   * @default null\n   * @example SystemBarType.StatusBar\n   */\n  bar?: SystemBarType;\n}\n\nexport interface SystemBarsVisibilityOptions {\n  /**\n   * The system bar to hide or show.\n   *\n   * @since 8.0.0\n   * @default null\n   * @example SystemBarType.StatusBar\n   */\n  bar?: SystemBarType;\n\n  /**\n   * The type of status bar animation used when showing or hiding.\n   *\n   * This option is only supported on iOS.\n   *\n   * @default 'FADE'\n   *\n   * @since 8.0.0\n   */\n  animation?: SystemBarsAnimation;\n}\n\nexport interface SystemBarsAnimationOptions {\n  /**\n   * The type of status bar animation used when showing or hiding.\n   *\n   * This option is only supported on iOS.\n   *\n   * @default 'FADE'\n   *\n   * @since 8.0.0\n   */\n  animation: SystemBarsAnimation;\n}\n\nexport interface SystemBarsPlugin {\n  /**\n   * Set the current style of the system bars.\n   *\n   * @since 8.0.0\n   */\n  setStyle(options: SystemBarsStyleOptions): Promise<void>;\n\n  /**\n   * Show the system bars.\n   *\n   * @since 8.0.0\n   */\n  show(options?: SystemBarsVisibilityOptions): Promise<void>;\n\n  /**\n   * Hide the system bars.\n   *\n   * @since 8.0.0\n   */\n  hide(options?: SystemBarsVisibilityOptions): Promise<void>;\n\n  /**\n   * Set the animation to use when showing / hiding the status bar.\n   *\n   * Only available on iOS.\n   *\n   * @since 8.0.0\n   */\n  setAnimation(options: SystemBarsAnimationOptions): Promise<void>;\n}\n\nexport class SystemBarsPluginWeb extends WebPlugin implements SystemBarsPlugin {\n  async setStyle(): Promise<void> {\n    this.unavailable('not available for web');\n  }\n\n  async setAnimation(): Promise<void> {\n    this.unavailable('not available for web');\n  }\n\n  async show(): Promise<void> {\n    this.unavailable('not available for web');\n  }\n\n  async hide(): Promise<void> {\n    this.unavailable('not available for web');\n  }\n}\n\nexport const SystemBars = registerPlugin<SystemBarsPlugin>('SystemBars', {\n  web: () => new SystemBarsPluginWeb(),\n});\n\n/******** END SYSTEM BARS PLUGIN ********/\n"
  },
  {
    "path": "core/src/definitions-internal.ts",
    "content": "import type { CapacitorGlobal, PluginCallback, PluginResultData, PluginResultError } from './definitions';\n\nexport interface PluginHeaderMethod {\n  readonly name: string;\n  readonly rtype?: 'promise' | 'callback';\n}\n\nexport interface PluginHeader {\n  readonly name: string;\n  readonly methods: readonly PluginHeaderMethod[];\n}\n\n/**\n * Has all instance properties that are available and used\n * by the native layer. The \"Capacitor\" interface it extends\n * is the public one.\n */\nexport interface CapacitorInstance extends CapacitorGlobal {\n  /**\n   * Internal registry for all plugins assigned to the Capacitor global.\n   * Legacy Capacitor referenced this property directly, but as of v3\n   * it should be an internal API. Still exporting on the Capacitor\n   * type, but with the deprecated JSDoc tag.\n   */\n  Plugins: {\n    [pluginName: string]: {\n      [prop: string]: any;\n    };\n  };\n\n  PluginHeaders?: readonly PluginHeader[];\n\n  /**\n   * Gets the WebView server urls set by the native web view. Defaults\n   * to \"\" if not running from a native platform.\n   */\n  getServerUrl: () => string;\n\n  /**\n   * Low-level API to send data to the native layer.\n   * Prefer using `nativeCallback()` or `nativePromise()` instead.\n   * Returns the Callback Id.\n   */\n  toNative?: (pluginName: string, methodName: string, options: any, storedCallback?: StoredCallback) => string;\n\n  /**\n   * Sends data over the bridge to the native layer.\n   * Returns the Callback Id.\n   */\n  nativeCallback: <O>(pluginName: string, methodName: string, options?: O, callback?: PluginCallback) => string;\n\n  /**\n   * Sends data over the bridge to the native layer and\n   * resolves the promise when it receives the data from\n   * the native implementation.\n   */\n  nativePromise: <O, R>(pluginName: string, methodName: string, options?: O) => Promise<R>;\n\n  /**\n   * Low-level API used by the native layers to send\n   * data back to the webview runtime.\n   */\n  fromNative?: (result: PluginResult) => void;\n\n  /**\n   * Low-level API for backwards compatibility.\n   */\n  createEvent?: (eventName: string, eventData?: any) => Event;\n\n  /**\n   * Low-level API triggered from native implementations.\n   */\n  triggerEvent?: (eventName: string, target: string, eventData?: any) => boolean;\n\n  handleError: (err: Error) => void;\n\n  handleWindowError: (msg: string | Event, url: string, lineNo: number, columnNo: number, err: Error) => void;\n\n  /**\n   * Low-level API used by the native bridge to log messages.\n   */\n  logJs: (message: string, level: 'error' | 'warn' | 'info' | 'log') => void;\n\n  logToNative: (data: MessageCallData) => void;\n\n  logFromNative: (results: PluginResult) => void;\n\n  /**\n   * Low-level API used by the native bridge.\n   */\n  withPlugin?: (pluginName: string, fn: (...args: any[]) => any) => void;\n}\n\nexport interface MessageCallData {\n  type?: 'message';\n  callbackId: string;\n  pluginId: string;\n  methodName: string;\n  options: any;\n}\n\nexport interface ErrorCallData {\n  type: 'js.error';\n  error: {\n    message: string;\n    url: string;\n    line: number;\n    col: number;\n    errorObject: string;\n  };\n}\n\nexport type CallData = MessageCallData | ErrorCallData;\n\n/**\n * A resulting call back from the native layer.\n */\nexport interface PluginResult {\n  callbackId?: string;\n  methodName?: string;\n  data: PluginResultData;\n  success: boolean;\n  error?: PluginResultError;\n  pluginId?: string;\n  save?: boolean;\n}\n\n/**\n * Callback data kept on the client\n * to be called after native response\n */\nexport interface StoredCallback {\n  callback?: PluginCallback;\n  resolve?: (...args: any[]) => any;\n  reject?: (...args: any[]) => any;\n}\n\nexport interface CapacitorCustomPlatformInstance {\n  name: string;\n  plugins: { [pluginName: string]: any };\n}\n\nexport interface WindowCapacitor {\n  Capacitor?: CapacitorInstance;\n  CapacitorSystemBarsAndroidInterface?: any;\n  CapacitorCookiesAndroidInterface?: any;\n  CapacitorCookiesDescriptor?: PropertyDescriptor;\n  CapacitorHttpAndroidInterface?: any;\n  CapacitorWebFetch?: any;\n  CapacitorWebXMLHttpRequest?: any;\n  CapacitorCustomPlatform?: CapacitorCustomPlatformInstance;\n  Ionic?: {\n    WebView?: {\n      getServerBasePath?: any;\n      setServerBasePath?: any;\n      setServerAssetPath?: any;\n      persistServerBasePath?: any;\n      convertFileSrc?: any;\n    };\n  };\n  WEBVIEW_SERVER_URL?: string;\n  androidBridge?: {\n    postMessage(data: string): void;\n    onmessage?: (event: { data: string }) => void;\n  };\n  webkit?: {\n    messageHandlers?: {\n      bridge: {\n        postMessage(data: any): void;\n      };\n    };\n  };\n  console?: Console;\n  cordova?: {\n    fireDocumentEvent?: (eventName: string, eventData: any) => void;\n  };\n  dispatchEvent?: any;\n  document?: any;\n  navigator?: {\n    app?: {\n      exitApp?: () => void;\n    };\n  };\n}\n\nexport interface CapFormDataEntry {\n  key: string;\n  value: string;\n  type: 'base64File' | 'string';\n  contentType?: string;\n  fileName?: string;\n}\n"
  },
  {
    "path": "core/src/definitions.ts",
    "content": "import type { CapacitorException } from './util';\n\nexport interface CapacitorGlobal {\n  /**\n   * The Exception class used when generating plugin Exceptions\n   * from bridge calls.\n   */\n  Exception: typeof CapacitorException;\n\n  /**\n   * Utility function to convert a file path into a usable src depending\n   * on the native WebView implementation value and environment.\n   */\n  convertFileSrc: (filePath: string) => string;\n\n  /**\n   * Gets the name of the platform, such as `android`, `ios`, or `web`.\n   */\n  getPlatform: () => string;\n\n  /**\n   * Boolean if the platform is native or not. `android` and `ios`\n   * would return `true`, otherwise `false`.\n   */\n  isNativePlatform: () => boolean;\n\n  /**\n   * Used to check if a platform is registered and available.\n   */\n  isPluginAvailable: (name: string) => boolean;\n\n  registerPlugin: RegisterPlugin;\n\n  /**\n   * Add a listener for a plugin event.\n   */\n  addListener?: (pluginName: string, eventName: string, callback: PluginCallback) => PluginListenerHandle;\n\n  /**\n   * Remove a listener to a plugin event.\n   */\n  removeListener?: (pluginName: string, callbackId: string, eventName: string, callback: PluginCallback) => void;\n\n  DEBUG?: boolean;\n  isLoggingEnabled?: boolean;\n}\n\n/**\n * Register plugin implementations with Capacitor.\n *\n * This function will create and register an instance that contains the\n * implementations of the plugin.\n *\n * Each plugin has multiple implementations, one per platform. Each\n * implementation must adhere to a common interface to ensure client code\n * behaves consistently across each platform.\n *\n * @param pluginName The unique CamelCase name of this plugin.\n * @param implementations The map of plugin implementations.\n */\nexport type RegisterPlugin = <T>(pluginName: string, implementations?: Readonly<PluginImplementations>) => T;\n\n/**\n * A map of plugin implementations.\n *\n * Each key should be the lowercased platform name as recognized by Capacitor,\n * e.g. 'android', 'ios', and 'web'. Each value must be an instance of a plugin\n * implementation for the respective platform.\n */\nexport type PluginImplementations = {\n  [platform: string]: (() => Promise<any>) | any;\n};\n\nexport interface Plugin {\n  addListener(eventName: string, listenerFunc: (...args: any[]) => any): Promise<PluginListenerHandle>;\n  removeAllListeners(): Promise<void>;\n}\n\nexport type PermissionState = 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied';\n\nexport interface PluginListenerHandle {\n  remove: () => Promise<void>;\n}\n\nexport interface PluginResultData {\n  [key: string]: any;\n}\n\nexport interface PluginResultError {\n  message: string;\n}\n\nexport type PluginCallback = (data: PluginResultData, error?: PluginResultError) => void;\n"
  },
  {
    "path": "core/src/global.ts",
    "content": "import { initCapacitorGlobal } from './runtime';\n\nexport const Capacitor = /*#__PURE__*/ initCapacitorGlobal(\n  typeof globalThis !== 'undefined'\n    ? globalThis\n    : typeof self !== 'undefined'\n      ? self\n      : typeof window !== 'undefined'\n        ? window\n        : typeof global !== 'undefined'\n          ? global\n          : {},\n);\n\nexport const registerPlugin = Capacitor.registerPlugin;\n"
  },
  {
    "path": "core/src/index.ts",
    "content": "// Type Definitions\nexport type {\n  CapacitorGlobal,\n  PermissionState,\n  Plugin,\n  PluginCallback,\n  PluginImplementations,\n  PluginListenerHandle,\n  PluginResultData,\n  PluginResultError,\n} from './definitions';\n\n// Global APIs\nexport { Capacitor, registerPlugin } from './global';\n\n// Base WebPlugin\nexport { WebPlugin, ListenerCallback } from './web-plugin';\n\n// Core Plugins APIs\nexport {\n  SystemBars,\n  SystemBarType,\n  SystemBarsStyle,\n  SystemBarsAnimation,\n  CapacitorCookies,\n  CapacitorHttp,\n  WebView,\n  buildRequestInit,\n} from './core-plugins';\n\n// Core Plugin definitions\nexport type {\n  ClearCookieOptions,\n  DeleteCookieOptions,\n  SetCookieOptions,\n  HttpHeaders,\n  HttpOptions,\n  HttpParams,\n  HttpResponse,\n  HttpResponseType,\n  WebViewPath,\n  WebViewPlugin,\n  SystemBarsVisibilityOptions,\n  SystemBarsStyleOptions,\n} from './core-plugins';\n\n// Constants\nexport { CapacitorException, ExceptionCode } from './util';\n"
  },
  {
    "path": "core/src/runtime.ts",
    "content": "import type { CapacitorGlobal, PluginImplementations } from './definitions';\nimport type {\n  CapacitorCustomPlatformInstance,\n  CapacitorInstance,\n  PluginHeader,\n  WindowCapacitor,\n} from './definitions-internal';\nimport { CapacitorException, getPlatformId, ExceptionCode } from './util';\n\nexport interface RegisteredPlugin {\n  readonly name: string;\n  readonly proxy: any;\n  readonly platforms: ReadonlySet<string>;\n}\n\nexport const createCapacitor = (win: WindowCapacitor): CapacitorInstance => {\n  const capCustomPlatform: CapacitorCustomPlatformInstance = win.CapacitorCustomPlatform || null;\n  const cap: CapacitorInstance = win.Capacitor || ({} as any);\n  const Plugins = (cap.Plugins = cap.Plugins || ({} as any));\n\n  const getPlatform = () => {\n    return capCustomPlatform !== null ? capCustomPlatform.name : getPlatformId(win);\n  };\n\n  const isNativePlatform = () => getPlatform() !== 'web';\n\n  const isPluginAvailable = (pluginName: string): boolean => {\n    const plugin = registeredPlugins.get(pluginName);\n\n    if (plugin?.platforms.has(getPlatform())) {\n      // JS implementation available for the current platform.\n      return true;\n    }\n\n    if (getPluginHeader(pluginName)) {\n      // Native implementation available.\n      return true;\n    }\n\n    return false;\n  };\n\n  const getPluginHeader = (pluginName: string): PluginHeader | undefined =>\n    cap.PluginHeaders?.find((h) => h.name === pluginName);\n\n  const handleError = (err: Error) => win.console.error(err);\n\n  const registeredPlugins = new Map<string, RegisteredPlugin>();\n\n  const registerPlugin = (pluginName: string, jsImplementations: PluginImplementations = {}): any => {\n    const registeredPlugin = registeredPlugins.get(pluginName);\n    if (registeredPlugin) {\n      console.warn(`Capacitor plugin \"${pluginName}\" already registered. Cannot register plugins twice.`);\n\n      return registeredPlugin.proxy;\n    }\n\n    const platform = getPlatform();\n    const pluginHeader = getPluginHeader(pluginName);\n    let jsImplementation: any;\n\n    const loadPluginImplementation = async (): Promise<any> => {\n      if (!jsImplementation && platform in jsImplementations) {\n        jsImplementation =\n          typeof jsImplementations[platform] === 'function'\n            ? (jsImplementation = await jsImplementations[platform]())\n            : (jsImplementation = jsImplementations[platform]);\n      } else if (capCustomPlatform !== null && !jsImplementation && 'web' in jsImplementations) {\n        jsImplementation =\n          typeof jsImplementations['web'] === 'function'\n            ? (jsImplementation = await jsImplementations['web']())\n            : (jsImplementation = jsImplementations['web']);\n      }\n\n      return jsImplementation;\n    };\n\n    const createPluginMethod = (impl: any, prop: PropertyKey): ((...args: any[]) => any) => {\n      if (pluginHeader) {\n        const methodHeader = pluginHeader?.methods.find((m) => prop === m.name);\n        if (methodHeader) {\n          if (methodHeader.rtype === 'promise') {\n            return (options: any) => cap.nativePromise(pluginName, prop.toString(), options);\n          } else {\n            return (options: any, callback: any) => cap.nativeCallback(pluginName, prop.toString(), options, callback);\n          }\n        } else if (impl) {\n          return impl[prop]?.bind(impl);\n        }\n      } else if (impl) {\n        return impl[prop]?.bind(impl);\n      } else {\n        throw new CapacitorException(\n          `\"${pluginName}\" plugin is not implemented on ${platform}`,\n          ExceptionCode.Unimplemented,\n        );\n      }\n    };\n\n    const createPluginMethodWrapper = (prop: PropertyKey) => {\n      let remove: (() => void) | undefined;\n      const wrapper = (...args: any[]) => {\n        const p = loadPluginImplementation().then((impl) => {\n          const fn = createPluginMethod(impl, prop);\n\n          if (fn) {\n            const p = fn(...args);\n            remove = p?.remove;\n            return p;\n          } else {\n            throw new CapacitorException(\n              `\"${pluginName}.${prop as any}()\" is not implemented on ${platform}`,\n              ExceptionCode.Unimplemented,\n            );\n          }\n        });\n\n        if (prop === 'addListener') {\n          (p as any).remove = async () => remove();\n        }\n\n        return p;\n      };\n\n      // Some flair ✨\n      wrapper.toString = () => `${prop.toString()}() { [capacitor code] }`;\n      Object.defineProperty(wrapper, 'name', {\n        value: prop,\n        writable: false,\n        configurable: false,\n      });\n\n      return wrapper;\n    };\n\n    const addListener = createPluginMethodWrapper('addListener');\n    const removeListener = createPluginMethodWrapper('removeListener');\n    const addListenerNative = (eventName: string, callback: any) => {\n      const call = addListener({ eventName }, callback);\n      const remove = async () => {\n        const callbackId = await call;\n\n        removeListener(\n          {\n            eventName,\n            callbackId,\n          },\n          callback,\n        );\n      };\n\n      const p = new Promise((resolve) => call.then(() => resolve({ remove })));\n\n      (p as any).remove = async () => {\n        console.warn(`Using addListener() without 'await' is deprecated.`);\n        await remove();\n      };\n\n      return p;\n    };\n\n    const proxy = new Proxy(\n      {},\n      {\n        get(_, prop) {\n          switch (prop) {\n            // https://github.com/facebook/react/issues/20030\n            case '$$typeof':\n              return undefined;\n            case 'toJSON':\n              return () => ({});\n            case 'addListener':\n              return pluginHeader ? addListenerNative : addListener;\n            case 'removeListener':\n              return removeListener;\n            default:\n              return createPluginMethodWrapper(prop);\n          }\n        },\n      },\n    );\n\n    Plugins[pluginName] = proxy;\n\n    registeredPlugins.set(pluginName, {\n      name: pluginName,\n      proxy,\n      platforms: new Set([...Object.keys(jsImplementations), ...(pluginHeader ? [platform] : [])]),\n    });\n\n    return proxy;\n  };\n\n  // Add in convertFileSrc for web, it will already be available in native context\n  if (!cap.convertFileSrc) {\n    cap.convertFileSrc = (filePath) => filePath;\n  }\n\n  cap.getPlatform = getPlatform;\n  cap.handleError = handleError;\n  cap.isNativePlatform = isNativePlatform;\n  cap.isPluginAvailable = isPluginAvailable;\n  cap.registerPlugin = registerPlugin;\n  cap.Exception = CapacitorException;\n  cap.DEBUG = !!cap.DEBUG;\n  cap.isLoggingEnabled = !!cap.isLoggingEnabled;\n\n  return cap;\n};\n\nexport const initCapacitorGlobal = (win: any): CapacitorGlobal => (win.Capacitor = createCapacitor(win));\n"
  },
  {
    "path": "core/src/tests/bridge.spec.ts",
    "content": "/**\n * @jest-environment jsdom\n */\n\nimport { initBridge } from '../../native-bridge';\nimport type { CapacitorInstance, PluginResult, WindowCapacitor } from '../definitions-internal';\nimport { createCapacitor } from '../runtime';\n\ndescribe('bridge', () => {\n  let win: WindowCapacitor;\n  let cap: CapacitorInstance;\n\n  beforeEach(() => {\n    win = {};\n    initBridge(win);\n    // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n    // @ts-ignore\n    // eslint-disable-next-line @typescript-eslint/no-empty-function\n    window.prompt = () => {};\n  });\n\n  it('android nativePromise error', (done) => {\n    mockAndroidPluginResult({\n      success: false,\n      data: null,\n      error: { message: 'darn it' },\n    });\n    initBridge(win);\n\n    cap = createCapacitor(win);\n    expect(cap.getPlatform()).toBe('android');\n    expect(cap.isNativePlatform()).toBe(true);\n\n    cap\n      .nativePromise('id', 'method')\n      .then(() => {\n        done('should throw error');\n      })\n      .catch((err) => {\n        try {\n          expect(err.message).toBe('darn it');\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n  });\n\n  it('android nativePromise success', (done) => {\n    mockAndroidPluginResult({\n      success: true,\n      data: { mph: 88 },\n    });\n    initBridge(win);\n\n    cap = createCapacitor(win);\n    expect(cap.getPlatform()).toBe('android');\n    expect(cap.isNativePlatform()).toBe(true);\n\n    cap\n      .nativePromise('id', 'method')\n      .then((data) => {\n        try {\n          expect(data).toEqual({ mph: 88 });\n          done();\n        } catch (e) {\n          done(e);\n        }\n      })\n      .catch(done);\n  });\n\n  it('ios nativeCallback w/ callback error', (done) => {\n    mockIosPluginResult({\n      data: null,\n      success: false,\n      error: { message: 'darn it' },\n    });\n    initBridge(win);\n\n    cap = createCapacitor(win);\n    expect(cap.getPlatform()).toBe('ios');\n    expect(cap.isNativePlatform()).toBe(true);\n\n    cap.nativeCallback('pluginName', 'methodName', {}, (data, err) => {\n      try {\n        expect(data).toEqual(null);\n        expect(err.message).toBe('darn it');\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n\n  it('ios nativeCallback w/ options and callback, success', (done) => {\n    mockIosPluginResult({\n      data: { mph: 88 },\n      success: true,\n    });\n    initBridge(win);\n\n    cap = createCapacitor(win);\n    expect(cap.getPlatform()).toBe('ios');\n    expect(cap.isNativePlatform()).toBe(true);\n\n    cap.nativeCallback('pluginName', 'methodName', {}, (data, err) => {\n      try {\n        expect(data).toEqual({ mph: 88 });\n        expect(err).toBe(undefined);\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n\n  const mockAndroidPluginResult = (pluginResult: PluginResult) => {\n    win.androidBridge = {\n      postMessage: (m) => {\n        const d = JSON.parse(m);\n        Promise.resolve().then(() => {\n          pluginResult.callbackId = d.callbackId;\n          pluginResult.methodName = d.methodName;\n          cap.fromNative(pluginResult);\n        });\n      },\n    };\n  };\n\n  const mockIosPluginResult = (pluginResult: PluginResult) => {\n    win.webkit = {\n      messageHandlers: {\n        bridge: {\n          postMessage: (m) => {\n            Promise.resolve().then(() => {\n              pluginResult.callbackId = m.callbackId;\n              pluginResult.methodName = m.methodName;\n              cap.fromNative(pluginResult);\n            });\n          },\n        },\n      },\n    };\n  };\n});\n"
  },
  {
    "path": "core/src/tests/build-treeshaking.js",
    "content": "const rollup = require('rollup');\nconst path = require('path');\nconst fs = require('fs');\n\nasync function validateBuildTreeshaking() {\n  const rootDir = path.join(__dirname, '..', '..');\n  const pkgJsonPath = path.join(rootDir, 'package.json');\n  const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));\n  const entryModulePath = path.join(rootDir, pkgJson.main);\n  const outputFile = path.join(rootDir, 'build', `treeshake-output.js`);\n\n  if (!fs.existsSync(entryModulePath)) {\n    throw new Error(`Run a full build before testing treeshaking`);\n  }\n\n  const virtualInputId = `@g@doo`;\n  const entryId = `@entry-module`;\n\n  const bundle = await rollup.rollup({\n    input: virtualInputId,\n    treeshake: true,\n    plugins: [\n      {\n        name: 'testResolver',\n        resolveId(id) {\n          if (id === virtualInputId) {\n            return id;\n          }\n          if (id === entryId) {\n            return entryModulePath;\n          }\n        },\n        load(id) {\n          if (id === virtualInputId) {\n            return `import \"${entryId}\"; // side-effect import that \"should\" do nothing`;\n          }\n        },\n      },\n    ],\n    onwarn(warning) {\n      if (warning.code !== 'EMPTY_BUNDLE') {\n        throw warning;\n      }\n    },\n  });\n\n  const o = await bundle.generate({\n    format: 'es',\n  });\n\n  const output = o.output[0];\n  const outputCode = output.code.trim();\n\n  fs.writeFileSync(outputFile, outputCode);\n\n  if (outputCode !== '') {\n    console.error(`\\nTreeshake output: ${outputFile}\\n`);\n\n    throw new Error(`🧨  Not all code was not treeshaken (treeshooken? treeshaked?) Review the ${outputFile} file.`);\n  }\n\n  console.log(`🌳  validated treeshaking: ${entryModulePath}`);\n}\n\nvalidateBuildTreeshaking().catch((err) => {\n  console.error(err);\n  process.exit(1);\n});\n"
  },
  {
    "path": "core/src/tests/convert-file-src.spec.ts",
    "content": "import { initBridge } from '../../native-bridge';\nimport type { CapacitorInstance, WindowCapacitor } from '../definitions-internal';\nimport { createCapacitor } from '../runtime';\n\ndescribe('convertFileSrc', () => {\n  let win: WindowCapacitor;\n  let cap: CapacitorInstance;\n\n  beforeEach(() => {\n    win = {\n      WEBVIEW_SERVER_URL: 'webviewSeverUrl',\n    };\n    initBridge(win);\n    cap = createCapacitor(win);\n  });\n\n  it('starts with content://', () => {\n    expect(cap.convertFileSrc('content://myfile')).toBe('webviewSeverUrl/_capacitor_content_/myfile');\n  });\n\n  it('starts with file://', () => {\n    expect(cap.convertFileSrc('file://myfile')).toBe('webviewSeverUrl/_capacitor_file_myfile');\n  });\n\n  it('starts with /', () => {\n    expect(cap.convertFileSrc('/myfile')).toBe('webviewSeverUrl/_capacitor_file_/myfile');\n  });\n\n  it('non string', () => {\n    expect(cap.convertFileSrc(null)).toBe(null);\n    expect(cap.convertFileSrc(undefined)).toBe(undefined);\n    expect(cap.convertFileSrc(88 as any)).toBe(88);\n    expect(cap.convertFileSrc('')).toBe('');\n  });\n});\n"
  },
  {
    "path": "core/src/tests/events.spec.ts",
    "content": "import { initBridge } from '../../native-bridge';\nimport type { CapacitorInstance, WindowCapacitor } from '../definitions-internal';\nimport { createCapacitor } from '../runtime';\n\ndescribe('plugin', () => {\n  let win: WindowCapacitor;\n  let cap: CapacitorInstance;\n\n  beforeEach(() => {\n    win = {\n      document: {\n        createEvent() {\n          return {\n            initEvent() {\n              return true;\n            },\n          };\n        },\n        dispatchEvent() {\n          return true;\n        },\n      },\n    };\n    initBridge(win);\n  });\n\n  it('createEvent from document api', () => {\n    cap = createCapacitor(win);\n    const ev = cap.createEvent('eventName', { mph: 88 });\n    expect(typeof ev.initEvent).toBe('function');\n    expect((ev as any).mph).toBe(88);\n  });\n\n  it('createEvent, null when no document', () => {\n    win = {};\n    initBridge(win);\n    cap = createCapacitor(win);\n    const ev = cap.createEvent('eventName', { mph: 88 });\n    expect(ev).toBe(null);\n  });\n\n  it('triggerEvent, window true', () => {\n    let windowTrigger = false;\n    win.dispatchEvent = () => {\n      windowTrigger = true;\n      return true;\n    };\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'window');\n    expect(ev).toBe(true);\n    expect(windowTrigger).toBe(true);\n  });\n\n  it('triggerEvent, window false', () => {\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'window');\n    expect(ev).toBe(false);\n  });\n\n  it('triggerEvent, document, cordova fireDocumentEvent', () => {\n    let cordovaTrigger = false;\n    win.cordova = {\n      fireDocumentEvent() {\n        cordovaTrigger = true;\n      },\n    };\n    initBridge(win);\n\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'document');\n    expect(ev).toBe(true);\n    expect(ev).toBe(cordovaTrigger);\n  });\n\n  it('triggerEvent, document true', () => {\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'document');\n    expect(ev).toBe(true);\n  });\n\n  it('triggerEvent, document false', () => {\n    delete win.document;\n    initBridge(win);\n\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'document');\n    expect(ev).toBe(false);\n  });\n\n  it('triggerEvent, querySelector true', () => {\n    let querySelectorTriggered = false;\n    win.document.querySelector = () => {\n      return {\n        dispatchEvent() {\n          querySelectorTriggered = true;\n          return true;\n        },\n      };\n    };\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'some-id');\n    expect(ev).toBe(true);\n    expect(querySelectorTriggered).toBe(true);\n  });\n\n  it('triggerEvent, querySelector false', () => {\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'some-id');\n    expect(ev).toBe(false);\n  });\n\n  it('triggerEvent, document querySelector false', () => {\n    delete win.document;\n    cap = createCapacitor(win);\n    const ev = cap.triggerEvent('eventName', 'some-id');\n    expect(ev).toBe(false);\n  });\n});\n"
  },
  {
    "path": "core/src/tests/plugin.spec.ts",
    "content": "/**\n * @jest-environment jsdom\n */\n\nimport { initBridge } from '../../native-bridge';\nimport type { CapacitorGlobal, Plugin } from '../definitions';\nimport type { WindowCapacitor, CapacitorInstance } from '../definitions-internal';\nimport { initCapacitorGlobal } from '../runtime';\nimport { ExceptionCode } from '../util';\nimport { WebPlugin } from '../web-plugin';\n\ndescribe('plugin', () => {\n  let win: WindowCapacitor;\n  let cap: CapacitorGlobal;\n\n  beforeEach(() => {\n    win = {};\n    // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n    // @ts-ignore\n    global.setImmediate = global.setTimeout;\n  });\n\n  it('error from missing method from native implementation', async (done) => {\n    // mock the global with the android bridge\n    mockAndroidBridge();\n    initBridge(win);\n\n    // simulate native adding the plugin header with a garbage method before\n    // the core runtime\n    mockAndroidPlugin('Awesome', 'whatever');\n\n    // core runtime creates the actual Capacitor instance\n    cap = initCapacitorGlobal(win);\n\n    try {\n      const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome');\n      await Awesome.mph();\n      done('did not throw');\n    } catch (e) {\n      expect(e.message).toBe(`\"Awesome.mph()\" is not implemented on android`);\n      expect(e.code).toBe(ExceptionCode.Unimplemented);\n      done();\n    }\n  });\n\n  it('native implementation', async () => {\n    // mock the global with the android bridge\n    mockAndroidBridge();\n    initBridge(win);\n\n    // simulate native adding the plugin header before the core runtime\n    mockAndroidPlugin('Awesome', 'mph');\n\n    // core runtime creates the actual Capacitor instance\n    cap = initCapacitorGlobal(win);\n    (cap as CapacitorInstance).nativePromise = async () => 88 as any;\n\n    // user runtime registers the plugin\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome');\n\n    const results1 = await Awesome.mph();\n    expect(results1).toBe(88);\n  });\n\n  it('error from missing native implementation', async (done) => {\n    // mock the global with the android bridge\n    mockAndroidBridge();\n    initBridge(win);\n\n    // do not simulate native adding the bridge before the core runtime\n\n    // core runtime creates the actual Capacitor instance\n    cap = initCapacitorGlobal(win);\n\n    try {\n      const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome');\n      await Awesome.mph();\n      done('did not throw');\n    } catch (e) {\n      expect(e.message).toBe(`\"Awesome\" plugin is not implemented on android`);\n      expect(e.code).toBe(ExceptionCode.Unimplemented);\n      done();\n    }\n  });\n\n  it('error lazy loading implementation', async (done) => {\n    mockAndroidBridge();\n    initBridge(win);\n\n    cap = initCapacitorGlobal(win);\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      android: async () => {\n        throw 'unable to load module';\n      },\n    });\n\n    try {\n      await Awesome.mph();\n      done('did not throw');\n    } catch (e) {\n      expect(e).toBe('unable to load module');\n      done();\n    }\n  });\n\n  it('call method on lazy loaded implementation', async () => {\n    mockAndroidBridge();\n    initBridge(win);\n\n    cap = initCapacitorGlobal(win);\n\n    const AwesomePlugin = class {\n      val = 88;\n      mph() {\n        return this.val;\n      }\n    };\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      android: async () => new AwesomePlugin(),\n    });\n\n    const p1 = Awesome.mph();\n    expect(await p1).toBe(88);\n\n    const rtn2 = await Awesome.mph();\n    expect(rtn2).toBe(88);\n  });\n\n  it('call method on already loaded implementation', async () => {\n    mockAndroidBridge();\n    initBridge(win);\n\n    cap = initCapacitorGlobal(win);\n\n    const AwesomePlugin = class {\n      val = 88;\n      mph() {\n        return this.val;\n      }\n    };\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      android: new AwesomePlugin(),\n    });\n\n    const rtn1 = await Awesome.mph();\n    expect(rtn1).toBe(88);\n\n    const rtn2 = await Awesome.mph();\n    expect(rtn2).toBe(88);\n  });\n\n  it('call method that had an error', async () => {\n    mockAndroidBridge();\n    initBridge(win);\n\n    cap = initCapacitorGlobal(win);\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      android: {\n        mph: async () => {\n          throw new Error('nope!');\n        },\n      },\n    });\n\n    expect(async () => Awesome.mph()).rejects.toThrowError('nope!');\n  });\n\n  it('missing method on lazy loaded implementation', async (done) => {\n    cap = initCapacitorGlobal(win);\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      web: async () => ({}),\n    });\n\n    try {\n      await Awesome.mph();\n      done('did not throw error');\n    } catch (e) {\n      expect(e.message).toBe(`\"Awesome.mph()\" is not implemented on web`);\n      expect(e.code).toBe(ExceptionCode.Unimplemented);\n      done();\n    }\n  });\n\n  it('missing method on already loaded implementation', async (done) => {\n    mockAndroidBridge();\n    initBridge(win);\n\n    cap = initCapacitorGlobal(win);\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      android: {},\n    });\n\n    try {\n      await Awesome.mph();\n      done('should throw error');\n    } catch (e) {\n      expect(e.message).toBe(`\"Awesome.mph()\" is not implemented on android`);\n      expect(e.code).toBe(ExceptionCode.Unimplemented);\n      done();\n    }\n  });\n\n  it('no web platform implementation', async (done) => {\n    cap = initCapacitorGlobal(win);\n    expect(cap.getPlatform()).toBe('web');\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome');\n\n    try {\n      await Awesome.mph();\n      done('should throw error');\n    } catch (e) {\n      expect(e.message).toBe(`\"Awesome\" plugin is not implemented on web`);\n      expect(e.code).toBe(ExceptionCode.Unimplemented);\n      done();\n    }\n  });\n\n  it('no native platform implementation', async (done) => {\n    mockAndroidBridge();\n    initBridge(win);\n\n    cap = initCapacitorGlobal(win);\n    expect(cap.getPlatform()).toBe('android');\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      web: { mph: async () => 88 },\n    });\n\n    try {\n      await Awesome.mph();\n      done('should throw error');\n    } catch (e) {\n      expect(e.message).toBe(`\"Awesome\" plugin is not implemented on android`);\n      expect(e.code).toBe(ExceptionCode.Unimplemented);\n      done();\n    }\n  });\n\n  it('do not double register a plugin', () => {\n    mockAndroidBridge();\n    initBridge(win);\n    mockAndroidPlugin('Awesome', 'mph');\n\n    cap = initCapacitorGlobal(win);\n    expect(cap.getPlatform()).toBe('android');\n\n    const Awesome1 = cap.registerPlugin<AwesomePlugin>('Awesome');\n    const Awesome2 = cap.registerPlugin<AwesomePlugin>('Awesome');\n\n    expect(Awesome1).toBe(Awesome2);\n  });\n\n  it('addListener, w/out addListener on implementation', async (done) => {\n    const LazyWeb = class {\n      val = 88;\n      async addListener(_eventName: string, fn: any) {\n        fn(this.val);\n      }\n    };\n\n    cap = initCapacitorGlobal(win);\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      web: async () => new LazyWeb(),\n    });\n\n    Awesome.addListener('eventName', (data) => {\n      try {\n        expect(data).toEqual(88);\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n\n  it('sync addListener on android', async () => {\n    mockAndroidBridge();\n    initBridge(win);\n    mockAndroidPlugin('Awesome', 'mph');\n\n    cap = initCapacitorGlobal(win);\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome');\n\n    const rtn = Awesome.addListener('eventName', () => {\n      // ignore\n    });\n\n    expect(rtn).toBeDefined();\n    expect(typeof (rtn as any).remove === 'function').toBe(true);\n  });\n\n  it('async addListener on android', async () => {\n    mockAndroidBridge();\n    initBridge(win);\n    mockAndroidPlugin('Awesome', 'mph');\n\n    cap = initCapacitorGlobal(win);\n\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome');\n\n    const rtn = await Awesome.addListener('eventName', () => {\n      // ignore\n    });\n\n    expect(rtn).toBeDefined();\n    expect(typeof (rtn as any).remove === 'function').toBe(true);\n  });\n\n  it('async removeAllListeners on web, lazy load and call implementation', async (done) => {\n    const AwesomeWeb = class extends WebPlugin {\n      async removeAllListeners() {\n        setImmediate(() => done());\n      }\n    };\n    cap = initCapacitorGlobal(win);\n    const Awesome = cap.registerPlugin<AwesomePlugin>('Awesome', {\n      web: async () => new AwesomeWeb(),\n    });\n\n    const rtn = await Awesome.removeAllListeners();\n    expect(rtn).toBeUndefined();\n  });\n\n  const mockAndroidBridge = () => {\n    win.androidBridge = {\n      postMessage: () => {\n        // ignore\n      },\n    };\n  };\n\n  const mockAndroidPlugin = (pluginClassName: string, methodName: string) => {\n    const w: any = win;\n    w.Capacitor = w.Capacitor ?? {};\n    w.Capacitor.PluginHeaders = w.Capacitor.PluginHeaders ?? [];\n    w.Capacitor.PluginHeaders.push({\n      name: pluginClassName,\n      methods: [\n        { name: methodName, rtype: 'promise' },\n        { name: 'addListener' },\n        { name: 'removeListener' },\n        { name: 'removeAllListeners' },\n      ],\n    });\n  };\n});\n\ninterface AwesomePlugin extends Plugin {\n  mph(): Promise<number>;\n}\n"
  },
  {
    "path": "core/src/tests/runtime.spec.ts",
    "content": "import { initBridge } from '../../native-bridge';\nimport type { CapacitorInstance, WindowCapacitor } from '../definitions-internal';\nimport { createCapacitor } from '../runtime';\n\ndescribe('runtime', () => {\n  let win: WindowCapacitor;\n  let cap: CapacitorInstance;\n\n  beforeEach(() => {\n    win = {};\n    initBridge(win);\n    createCapacitor(win);\n  });\n\n  it('default methods/props', () => {\n    cap = createCapacitor(win);\n    expect(cap.getPlatform()).toBe('web');\n    expect(cap.isNativePlatform()).toBe(false);\n    expect(cap.isPluginAvailable('Nope')).toBe(false);\n  });\n\n  it('used existing window.Capacitor.Plugins', () => {\n    win.Capacitor = {\n      Plugins: { Awesome: {} },\n      PluginHeaders: [{ name: 'Awesome', methods: [] }],\n    } as any;\n    cap = createCapacitor(win);\n    expect(cap.isPluginAvailable('Awesome')).toBe(true);\n    expect(cap.isPluginAvailable('Nope')).toBe(false);\n  });\n\n  it('DEBUG false default', () => {\n    cap = createCapacitor(win);\n    expect(cap.DEBUG).toBe(false);\n  });\n\n  it('DEBUG set from window.Capacitor.DEBUG', () => {\n    (win as any).Capacitor = {\n      DEBUG: true,\n    };\n    cap = createCapacitor(win);\n    expect(cap.DEBUG).toBe(true);\n  });\n\n  it('isLoggingEnabled false default', () => {\n    cap = createCapacitor(win);\n    expect(cap.isLoggingEnabled).toBe(false);\n  });\n\n  it('isLoggingEnabled set from window.Capacitor.isLoggingEnabled', () => {\n    (win as any).Capacitor = {\n      isLoggingEnabled: true,\n    };\n    cap = createCapacitor(win);\n    expect(cap.isLoggingEnabled).toBe(true);\n  });\n\n  it('cannot reset server url after initializing capacitor', () => {\n    win.WEBVIEW_SERVER_URL = 'whatever://home';\n    initBridge(win);\n    cap = createCapacitor(win);\n    win.WEBVIEW_SERVER_URL = 'CHANGED!!!';\n    expect(cap.getServerUrl()).toBe('whatever://home');\n  });\n\n  it('server url set from window.WEBVIEW_SERVER_URL', () => {\n    win.WEBVIEW_SERVER_URL = 'whatever://home';\n    initBridge(win);\n    cap = createCapacitor(win);\n    expect(cap.getServerUrl()).toBe('whatever://home');\n  });\n\n  it('server url default w/out window.WEBVIEW_SERVER_URL set', () => {\n    cap = createCapacitor(win);\n    expect(cap.getServerUrl()).toBe('');\n  });\n});\n"
  },
  {
    "path": "core/src/tests/web-plugin.spec.ts",
    "content": "/**\n * @jest-environment jsdom\n */\n\nimport { initBridge } from '../../native-bridge';\nimport type { WindowCapacitor } from '../definitions-internal';\nimport { createCapacitor } from '../runtime';\nimport { WebPlugin } from '../web-plugin';\n\nconst win = {};\ninitBridge(win);\n\nclass MockPlugin extends WebPlugin {\n  trigger() {\n    this.notifyListeners('test', {\n      value: 'Capacitors on top of toast!',\n    });\n  }\n\n  triggerRetained() {\n    this.notifyListeners(\n      'testRetained',\n      {\n        value: 'Test Retained Value 1',\n      },\n      true,\n    );\n\n    this.notifyListeners(\n      'testRetained',\n      {\n        value: 'Test Retained Value 2',\n      },\n      true,\n    );\n  }\n\n  getListeners() {\n    return this.listeners;\n  }\n\n  getWindowListeners() {\n    return this.windowListeners;\n  }\n\n  registerFakeWindowListener() {\n    this.registerWindowListener('fake', 'test');\n  }\n}\n\ndescribe('Web Plugin', () => {\n  let plugin: MockPlugin;\n  let win: WindowCapacitor;\n\n  beforeEach(() => {\n    win = {};\n    createCapacitor(win);\n    plugin = new MockPlugin();\n  });\n\n  it('Should add event listeners', async () => {\n    const lf = (event: any) => {\n      console.log(event);\n    };\n\n    const handle = await plugin.addListener('test', lf);\n\n    const listener = plugin.getListeners()['test'];\n    expect(listener).not.toBe(undefined);\n    expect(listener.length).toEqual(1);\n    handle.remove();\n  });\n\n  it('Should manage multiple event listeners', async () => {\n    const lf1 = (event: any) => {\n      console.log(event);\n    };\n    const lf2 = (event: any) => {\n      console.log(event);\n    };\n    const lf3 = (event: any) => {\n      console.log(event);\n    };\n    const handle1 = await plugin.addListener('test', lf1);\n    const handle2 = await plugin.addListener('test', lf2);\n    const handle3 = await plugin.addListener('test', lf3);\n\n    const listener = plugin.getListeners()['test'];\n    expect(listener.length).toEqual(3);\n    handle1.remove();\n    expect(listener.length).toEqual(2);\n    handle2.remove();\n    expect(listener.length).toEqual(1);\n    handle3.remove();\n    expect(listener.length).toEqual(0);\n  });\n\n  it('Should remove event listeners', async () => {\n    const lf = (event: any) => {\n      console.log(event);\n    };\n    const handle = await plugin.addListener('test', lf);\n    handle.remove();\n\n    const listener = plugin.getListeners()['test'];\n    expect(listener).toEqual([]);\n  });\n\n  it('Should notify listeners', async () => {\n    const lf = jest.fn();\n    const handle = await plugin.addListener('test', lf);\n\n    plugin.trigger();\n\n    expect(lf.mock.calls.length).toEqual(1);\n    expect(lf.mock.calls[0][0]).toEqual({\n      value: 'Capacitors on top of toast!',\n    });\n    handle.remove();\n  });\n\n  it('Should submit retained events on event registration', async () => {\n    const lf = jest.fn();\n    plugin.triggerRetained();\n\n    const handle = await plugin.addListener('testRetained', lf);\n\n    expect(lf.mock.calls.length).toEqual(2);\n    expect(lf.mock.calls[0][0]).toEqual({\n      value: 'Test Retained Value 1',\n    });\n    expect(lf.mock.calls[1][0]).toEqual({\n      value: 'Test Retained Value 2',\n    });\n\n    handle.remove();\n  });\n\n  it('Should register and remove window listeners', async () => {\n    const pluginAddWindowListener = jest.spyOn(MockPlugin.prototype as any, 'addWindowListener');\n    plugin.registerFakeWindowListener();\n\n    const lf = jest.fn();\n    const handle = await plugin.addListener('test', lf);\n\n    // Make sure the window listener was added\n    let windowListener = plugin.getWindowListeners()['test'];\n    expect(windowListener.registered).toEqual(true);\n    expect(pluginAddWindowListener.mock.calls.length).toEqual(1);\n\n    // Trigger a custom window event\n    const event = new CustomEvent('fake', {\n      detail: { value: 'Capacitors on top of toast!' },\n    });\n    window.dispatchEvent(event);\n\n    expect(lf.mock.calls.length).toEqual(1);\n\n    const eventArg = lf.mock.calls[0][0];\n    expect(eventArg.detail.value).toEqual('Capacitors on top of toast!');\n\n    handle.remove();\n    windowListener = plugin.getWindowListeners()['test'];\n    expect(windowListener.registered).toEqual(false);\n  });\n\n  it('Should only call window event if listeners bound', () => {\n    plugin.registerFakeWindowListener();\n\n    // Make sure the window listener was added\n    const windowListener = plugin.getWindowListeners()['test'];\n\n    expect(windowListener.registered).toEqual(false);\n\n    const handlerFunction = jest.spyOn(windowListener, 'handler');\n    // Trigger a custom window event\n    const event = new CustomEvent('fake', {\n      detail: { value: 'Capacitors on top of toast!' },\n    });\n    window.dispatchEvent(event);\n\n    expect(handlerFunction).not.toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "core/src/util.ts",
    "content": "import type { WindowCapacitor } from './definitions-internal';\n\nexport enum ExceptionCode {\n  /**\n   * API is not implemented.\n   *\n   * This usually means the API can't be used because it is not implemented for\n   * the current platform.\n   */\n  Unimplemented = 'UNIMPLEMENTED',\n\n  /**\n   * API is not available.\n   *\n   * This means the API can't be used right now because:\n   *   - it is currently missing a prerequisite, such as network connectivity\n   *   - it requires a particular platform or browser version\n   */\n  Unavailable = 'UNAVAILABLE',\n}\n\nexport interface ExceptionData {\n  [key: string]: any;\n}\n\nexport class CapacitorException extends Error {\n  constructor(\n    readonly message: string,\n    readonly code?: ExceptionCode,\n    readonly data?: ExceptionData,\n  ) {\n    super(message);\n  }\n}\n\nexport const getPlatformId = (win: WindowCapacitor): 'android' | 'ios' | 'web' => {\n  if (win?.androidBridge) {\n    return 'android';\n  } else if (win?.webkit?.messageHandlers?.bridge) {\n    return 'ios';\n  } else {\n    return 'web';\n  }\n};\n"
  },
  {
    "path": "core/src/web-plugin.ts",
    "content": "import type { PluginListenerHandle, Plugin } from './definitions';\nimport { Capacitor } from './global';\nimport { ExceptionCode } from './util';\nimport type { CapacitorException } from './util';\n\n/**\n * Base class web plugins should extend.\n */\nexport class WebPlugin implements Plugin {\n  protected listeners: { [eventName: string]: ListenerCallback[] } = {};\n  protected retainedEventArguments: { [eventName: string]: any[] } = {};\n  protected windowListeners: { [eventName: string]: WindowListenerHandle } = {};\n\n  addListener(eventName: string, listenerFunc: ListenerCallback): Promise<PluginListenerHandle> {\n    let firstListener = false;\n\n    const listeners = this.listeners[eventName];\n    if (!listeners) {\n      this.listeners[eventName] = [];\n      firstListener = true;\n    }\n\n    this.listeners[eventName].push(listenerFunc);\n\n    // If we haven't added a window listener for this event and it requires one,\n    // go ahead and add it\n    const windowListener = this.windowListeners[eventName];\n    if (windowListener && !windowListener.registered) {\n      this.addWindowListener(windowListener);\n    }\n\n    if (firstListener) {\n      this.sendRetainedArgumentsForEvent(eventName);\n    }\n\n    const remove = async () => this.removeListener(eventName, listenerFunc);\n\n    const p: any = Promise.resolve({ remove });\n\n    return p;\n  }\n\n  async removeAllListeners(): Promise<void> {\n    this.listeners = {};\n    for (const listener in this.windowListeners) {\n      this.removeWindowListener(this.windowListeners[listener]);\n    }\n    this.windowListeners = {};\n  }\n\n  protected notifyListeners(eventName: string, data: any, retainUntilConsumed?: boolean): void {\n    const listeners = this.listeners[eventName];\n    if (!listeners) {\n      if (retainUntilConsumed) {\n        let args = this.retainedEventArguments[eventName];\n        if (!args) {\n          args = [];\n        }\n\n        args.push(data);\n\n        this.retainedEventArguments[eventName] = args;\n      }\n\n      return;\n    }\n\n    listeners.forEach((listener) => listener(data));\n  }\n\n  protected hasListeners(eventName: string): boolean {\n    return !!this.listeners[eventName]?.length;\n  }\n\n  protected registerWindowListener(windowEventName: string, pluginEventName: string): void {\n    this.windowListeners[pluginEventName] = {\n      registered: false,\n      windowEventName,\n      pluginEventName,\n      handler: (event) => {\n        this.notifyListeners(pluginEventName, event);\n      },\n    };\n  }\n\n  protected unimplemented(msg = 'not implemented'): CapacitorException {\n    return new Capacitor.Exception(msg, ExceptionCode.Unimplemented);\n  }\n\n  protected unavailable(msg = 'not available'): CapacitorException {\n    return new Capacitor.Exception(msg, ExceptionCode.Unavailable);\n  }\n\n  private async removeListener(eventName: string, listenerFunc: ListenerCallback): Promise<void> {\n    const listeners = this.listeners[eventName];\n    if (!listeners) {\n      return;\n    }\n\n    const index = listeners.indexOf(listenerFunc);\n    this.listeners[eventName].splice(index, 1);\n\n    // If there are no more listeners for this type of event,\n    // remove the window listener\n    if (!this.listeners[eventName].length) {\n      this.removeWindowListener(this.windowListeners[eventName]);\n    }\n  }\n\n  private addWindowListener(handle: WindowListenerHandle): void {\n    window.addEventListener(handle.windowEventName, handle.handler);\n    handle.registered = true;\n  }\n\n  private removeWindowListener(handle: WindowListenerHandle): void {\n    if (!handle) {\n      return;\n    }\n\n    window.removeEventListener(handle.windowEventName, handle.handler);\n    handle.registered = false;\n  }\n\n  private sendRetainedArgumentsForEvent(eventName: string): void {\n    const args = this.retainedEventArguments[eventName];\n    if (!args) {\n      return;\n    }\n\n    delete this.retainedEventArguments[eventName];\n\n    args.forEach((arg) => {\n      this.notifyListeners(eventName, arg);\n    });\n  }\n}\n\nexport type ListenerCallback = (err: any, ...args: any[]) => void;\n\nexport interface WindowListenerHandle {\n  registered: boolean;\n  windowEventName: string;\n  pluginEventName: string;\n  handler: (event: any) => void;\n}\n"
  },
  {
    "path": "core/system-bars.md",
    "content": "# SystemBars\n\nThe SystemBars API provides methods for configuring the style and visibility of the device System Bars / Status Bar. This plugin is bundled with `@capacitor/core`.\n\nThis API differs from the [Status Bar](https://capacitorjs.com/docs/apis/status-bar) plugin in that it is only intended to support modern edge to edge use cases moving forward.  For legacy functionality, use the [Status Bar](https://capacitorjs.com/docs/apis/status-bar) plugin.\n\n| Feature | System Bars | Status Bar |\n| ------- | ----------- | ---------- |\n| `setOverlaysWebView()` | Unsupported | Supported on iOS and Android <= 14 (or 15 if edge to edge opt-out is enabled) |\n| `setBackgroundColor()` | Unsupported | Supported |\n| `setStyle()` | Supported | Supported - top Status Bar only |\n| `hide()/show()` | Supported | Supported - top Status Bar only |\n\n## iOS Note\n\nThis plugin requires \"View controller-based status bar appearance\"\n(`UIViewControllerBasedStatusBarAppearance`) set to `YES` in `Info.plist`. Read\nabout [Configuring iOS](https://capacitorjs.com/docs/ios/configuration) for\nhelp.\n\nThe status bar visibility defaults to visible and the style defaults to\n`Style.Default`. You can change these defaults by adding\n`UIStatusBarHidden` and/or `UIStatusBarStyle` in `Info.plist`.\n\n## Android Note\n\nDue to a [bug](https://issues.chromium.org/issues/40699457) in some older versions of Android WebView (< 140), correct safe area values are not available via the `safe-area-inset-x` CSS `env` variables.  This plugin will inject the correct inset values into a new CSS variable(s) named `--safe-area-inset-x` that you can use as a fallback in your frontend styles:\n\n```css\nhtml {\n  padding-top: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));\n  padding-bottom: var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));\n  padding-left: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));\n  padding-right: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));\n}\n```\nTo control this behavior, use the `insetsHandling` configuration setting.\n\n## Example\n\n```typescript\nimport { SystemBars, SystemBarsStyle, SystemBarType } from '@capacitor/core';\n\nconst setSystemBarStyleDark = async () => {\n  await SystemBars.setStyle({ style: SystemBarsStyle.Dark });\n};\n\nconst setSystemBarStyleLight = async () => {\n  await SystemBars.setStyle({ style: SystemBarsStyle.Light });\n};\n\nconst hideSystemBars = async () => {\n  await SystemBars.hide();\n};\n\nconst showSystemBars = async () => {\n  await SystemBars.show();\n};\n\nconst hideNavigationBar = async () => {\n  await SystemBars.hide({\n    bar: SystemBarType.NavigationBar\n  })\n}\n\n// Set the Status Bar animation, only on iOS\nconst setStatusBarAnimation = async () => {\n  await SystemBars.setAnimation({ animation: \"NONE\" });\n}\n\n````\n\n## Configuration\n| Prop          | Type                 | Description                                                               | Default            |\n| ------------- | -------------------- | ------------------------------------------------------------------------- | ------------------ |\n| **`insetsHandling`** | <code>string</code> | Specifies how to handle problematic insets on Android.  This option is only supported on Android.<br>`css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.<br>`disable` = Disable CSS variables injection. | <code>css</code> |\n| **`style`** | <code>string</code> | The style of the text and icons of the system bars. | <code>DEFAULT</code> |\n| **`hidden`** | <code>boolean</code> | Hide the system bars on start. | <code>false</code> |\n| **`animation`** | <code>string</code> | The type of status bar animation used when showing or hiding.  This option is only supported on iOS. | <code>FADE</code> |\n\n\n### Example Configuration\n\nIn `capacitor.config.json`:\n\n```json\n{\n  \"plugins\": {\n    \"SystemBars\": {\n      \"insetsHandling\": \"css\",\n      \"style\": \"DARK\",\n      \"hidden\": false,\n      \"animation\": \"NONE\"\n    }\n  }\n}\n```\n\nIn `capacitor.config.ts`:\n\n```ts\nimport { CapacitorConfig } from '@capacitor/cli';\n\nconst config: CapacitorConfig = {\n  plugins: {\n    SystemBars: {\n      insetsHandling: \"css\",\n      style: \"DARK\",\n      hidden: false,\n      animation: \"NONE\"\n    },\n  },\n};\n\nexport default config;\n```\n\n## API\n\n<docgen-index>\n\n* [`setStyle(...)`](#setstyle)\n* [`show(...)`](#show)\n* [`hide(...)`](#hide)\n* [`setAnimation(...)`](#setanimation)\n* [Interfaces](#interfaces)\n* [Type Aliases](#type-aliases)\n* [Enums](#enums)\n\n</docgen-index>\n\n<docgen-api>\n<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->\n\n### setStyle(...)\n\n```typescript\nsetStyle(options: SystemBarsStyleOptions) => Promise<void>\n```\n\nSet the current style of the system bars.\n\n| Param         | Type                                                                      |\n| ------------- | ------------------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#systembarsstyleoptions\">SystemBarsStyleOptions</a></code> |\n\n**Since:** 8.0.0\n\n--------------------\n\n\n### show(...)\n\n```typescript\nshow(options?: SystemBarsVisibilityOptions) => Promise<void>\n```\n\nShow the system bars.\n\n| Param         | Type                                                                                |\n| ------------- | ----------------------------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#systembarsvisibilityoptions\">SystemBarsVisibilityOptions</a></code> |\n\n**Since:** 8.0.0\n\n--------------------\n\n\n### hide(...)\n\n```typescript\nhide(options?: SystemBarsVisibilityOptions) => Promise<void>\n```\n\nHide the system bars.\n\n| Param         | Type                                                                                |\n| ------------- | ----------------------------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#systembarsvisibilityoptions\">SystemBarsVisibilityOptions</a></code> |\n\n**Since:** 8.0.0\n\n--------------------\n\n\n### setAnimation(...)\n\n```typescript\nsetAnimation(options: SystemBarsAnimationOptions) => Promise<void>\n```\n\nSet the animation to use when showing / hiding the status bar.\n\nOnly available on iOS.\n\n| Param         | Type                                                                              |\n| ------------- | --------------------------------------------------------------------------------- |\n| **`options`** | <code><a href=\"#systembarsanimationoptions\">SystemBarsAnimationOptions</a></code> |\n\n**Since:** 8.0.0\n\n--------------------\n\n\n### Interfaces\n\n\n#### SystemBarsStyleOptions\n\n| Prop        | Type                                                        | Description                                     | Default                | Since |\n| ----------- | ----------------------------------------------------------- | ----------------------------------------------- | ---------------------- | ----- |\n| **`style`** | <code><a href=\"#systembarsstyle\">SystemBarsStyle</a></code> | Style of the text and icons of the system bars. | <code>'DEFAULT'</code> | 8.0.0 |\n| **`bar`**   | <code><a href=\"#systembartype\">SystemBarType</a></code>     | The system bar to which to apply the style.     | <code>null</code>      | 8.0.0 |\n\n\n#### SystemBarsVisibilityOptions\n\n| Prop            | Type                                                                | Description                                                                                         | Default             | Since |\n| --------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------- | ----- |\n| **`bar`**       | <code><a href=\"#systembartype\">SystemBarType</a></code>             | The system bar to hide or show.                                                                     | <code>null</code>   | 8.0.0 |\n| **`animation`** | <code><a href=\"#systembarsanimation\">SystemBarsAnimation</a></code> | The type of status bar animation used when showing or hiding. This option is only supported on iOS. | <code>'FADE'</code> | 8.0.0 |\n\n\n#### SystemBarsAnimationOptions\n\n| Prop            | Type                                                                | Description                                                                                         | Default             | Since |\n| --------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------- | ----- |\n| **`animation`** | <code><a href=\"#systembarsanimation\">SystemBarsAnimation</a></code> | The type of status bar animation used when showing or hiding. This option is only supported on iOS. | <code>'FADE'</code> | 8.0.0 |\n\n\n### Type Aliases\n\n\n#### SystemBarsAnimation\n\nAvailable status bar animations.  iOS only.\n\n<code>'FADE' | 'NONE'</code>\n\n\n### Enums\n\n\n#### SystemBarsStyle\n\n| Members       | Value                  | Description                                                                                                                                                                                                              | Since |\n| ------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |\n| **`Dark`**    | <code>'DARK'</code>    | Light system bar content on a dark background.                                                                                                                                                                           | 8.0.0 |\n| **`Light`**   | <code>'LIGHT'</code>   | For dark system bar content on a light background.                                                                                                                                                                       | 8.0.0 |\n| **`Default`** | <code>'DEFAULT'</code> | The style is based on the device appearance or the underlying content. If the device is using Dark mode, the system bars content will be light. If the device is using Light mode, the system bars content will be dark. | 8.0.0 |\n\n\n#### SystemBarType\n\n| Members             | Value                        | Description                                                         | Since |\n| ------------------- | ---------------------------- | ------------------------------------------------------------------- | ----- |\n| **`StatusBar`**     | <code>'StatusBar'</code>     | The top status bar on both Android and iOS.                         | 8.0.0 |\n| **`NavigationBar`** | <code>'NavigationBar'</code> | The navigation bar (or gesture bar on iOS) on both Android and iOS. | 8.0.0 |\n\n</docgen-api>\n"
  },
  {
    "path": "core/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"importHelpers\": true,\n    \"lib\": [\"dom\", \"dom.iterable\"],\n    \"module\": \"es2015\",\n    \"moduleResolution\": \"node\",\n    \"noEmitHelpers\": true,\n    \"noImplicitAny\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"outDir\": \"build\",\n    \"declarationDir\": \"types\",\n    \"skipLibCheck\": true,\n    \"sourceMap\": true,\n    \"target\": \"es2017\"\n  },\n  \"files\": [\"src/index.ts\"]\n}\n"
  },
  {
    "path": "ios/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n# [8.2.0](https://github.com/ionic-team/capacitor/compare/8.1.0...8.2.0) (2026-03-06)\n\n### Bug Fixes\n\n- **ios:** remove tmpWindow usages on presentVC/dismissVC ([#8338](https://github.com/ionic-team/capacitor/issues/8338)) ([fc9647f](https://github.com/ionic-team/capacitor/commit/fc9647f26f08ff64f53b32c79fb19f153e3b0a24))\n\n# [8.1.0](https://github.com/ionic-team/capacitor/compare/8.0.2...8.1.0) (2026-02-11)\n\n### Reverts\n\n- revert version bump from [#8319](https://github.com/ionic-team/capacitor/issues/8319) and [#8320](https://github.com/ionic-team/capacitor/issues/8320) ([a48ebb6](https://github.com/ionic-team/capacitor/commit/a48ebb622ea4ebe92927bf1756a4d8ac6012884b))\n\n## [8.0.2](https://github.com/ionic-team/capacitor/compare/8.0.1...8.0.2) (2026-01-27)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [8.0.1](https://github.com/ionic-team/capacitor/compare/8.0.0...8.0.1) (2026-01-13)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [8.0.0](https://github.com/ionic-team/capacitor/compare/8.0.0-beta.0...8.0.0) (2025-12-08)\n\n### Bug Fixes\n\n- **ios:** move PrivacyInfo.xcprivacy to resource_bundles to fix build… ([#8264](https://github.com/ionic-team/capacitor/issues/8264)) ([e6f50b8](https://github.com/ionic-team/capacitor/commit/e6f50b8c0c41acaaa21af373e195751508c37e9d))\n\n### Features\n\n- **android:** Improving SystemBars inset handling ([#8268](https://github.com/ionic-team/capacitor/issues/8268)) ([81ae30a](https://github.com/ionic-team/capacitor/commit/81ae30a503797e417dd125b06262dabc4696c88a))\n\n# [8.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.3...8.0.0-beta.0) (2025-11-14)\n\n### Bug Fixes\n\n- **ios:** use ReturnPromise for SystemBars returnType ([#8239](https://github.com/ionic-team/capacitor/issues/8239)) ([eb51288](https://github.com/ionic-team/capacitor/commit/eb5128866953281dc7bed88bd734fc3804b3a702))\n- make Plugin.resolve act consistently ([#8225](https://github.com/ionic-team/capacitor/issues/8225)) ([06aeb9e](https://github.com/ionic-team/capacitor/commit/06aeb9e85d162d6be9d96820edcb2008cd74da84))\n\n### Features\n\n- **iOS:** Allow plugins to hook into handling WebView URL authentication challenges ([#8216](https://github.com/ionic-team/capacitor/issues/8216)) ([e8507cf](https://github.com/ionic-team/capacitor/commit/e8507cfe4d93337ddee8ab6597aab5209ebb4858))\n- System Bars Plugin ([#8180](https://github.com/ionic-team/capacitor/issues/8180)) ([a32216a](https://github.com/ionic-team/capacitor/commit/a32216ac0607172a3a9c7ae5cdbfc598769294a6))\n\n# [8.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.2...8.0.0-alpha.3) (2025-10-22)\n\n### Bug Fixes\n\n- **ios:** also call bridge.reset() on webViewWebContentProcessDidTerminate ([#8143](https://github.com/ionic-team/capacitor/issues/8143)) ([1de1f15](https://github.com/ionic-team/capacitor/commit/1de1f157169bc0e23060ffabf044c6d391b98efe))\n- **ios:** Remove Cordova UIView extension ([#8189](https://github.com/ionic-team/capacitor/issues/8189)) ([0c7bcd3](https://github.com/ionic-team/capacitor/commit/0c7bcd3d10f3e3d5a1259f5096879771f8e26436))\n- **ios:** replace deprecation warnings ([#8156](https://github.com/ionic-team/capacitor/issues/8156)) ([e76b29b](https://github.com/ionic-team/capacitor/commit/e76b29b77a7d71ef2341dd3aa530c5d9f291a941))\n- **ios:** Silence WKProcessPool warning ([#8184](https://github.com/ionic-team/capacitor/issues/8184)) ([b6abcb7](https://github.com/ionic-team/capacitor/commit/b6abcb7d656ee24e48a5d0dc7b68888b0318fe5d))\n\n# [8.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.0.0-alpha.1...8.0.0-alpha.2) (2025-08-20)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [8.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/7.4.2...8.0.0-alpha.1) (2025-08-20)\n\n### Bug Fixes\n\n- http content headers not sent when using axios ([#8039](https://github.com/ionic-team/capacitor/issues/8039)) ([67cac40](https://github.com/ionic-team/capacitor/commit/67cac40660b3e8cc78d1d228b7c6915450948ef1))\n\n## [7.4.2](https://github.com/ionic-team/capacitor/compare/7.4.1...7.4.2) (2025-07-10)\n\n### Bug Fixes\n\n- **http:** Properly URL-encode key and values during `x-www-form-urlencoded` POSTs ([#8037](https://github.com/ionic-team/capacitor/issues/8037)) ([87b4641](https://github.com/ionic-team/capacitor/commit/87b4641d1fa32b78e6fc2e87ee7b2c49b625b213))\n\n## [7.4.1](https://github.com/ionic-team/capacitor/compare/7.4.0...7.4.1) (2025-07-03)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [7.4.0](https://github.com/ionic-team/capacitor/compare/7.3.0...7.4.0) (2025-06-18)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [7.3.0](https://github.com/ionic-team/capacitor/compare/7.2.0...7.3.0) (2025-06-05)\n\n### Bug Fixes\n\n- Remove all plugin listeners in bridge reset ([#7962](https://github.com/ionic-team/capacitor/issues/7962)) ([06aeea9](https://github.com/ionic-team/capacitor/commit/06aeea973aa56ac2fb791f893ebd899253ee65f9))\n\n### Features\n\n- expose `appStartPath` on cap config server configuration ([#8019](https://github.com/ionic-team/capacitor/issues/8019)) ([a274fef](https://github.com/ionic-team/capacitor/commit/a274fef069176a8e528a22f4734d1e29a539709c))\n- **ios:** Alternative debug config for SPM ([#7982](https://github.com/ionic-team/capacitor/issues/7982)) ([a054aa5](https://github.com/ionic-team/capacitor/commit/a054aa5bb4d5eabe5da5d42a380bd51b8785958c))\n\n# [7.2.0](https://github.com/ionic-team/capacitor/compare/7.1.0...7.2.0) (2025-03-31)\n\n### Bug Fixes\n\n- **ios:** respect shouldEncodeUrlParams value ([#7931](https://github.com/ionic-team/capacitor/issues/7931)) ([4fb12a0](https://github.com/ionic-team/capacitor/commit/4fb12a03a92c0f38b0d48b114252342f4bdc17bf))\n\n### Features\n\n- **http:** Apply overrideUserAgent to requests ([#7906](https://github.com/ionic-team/capacitor/issues/7906)) ([52482c9](https://github.com/ionic-team/capacitor/commit/52482c9d3c575b737054b41f9d1730c70cc5f471))\n- **ios:** Enable Fullscreen API on WebView ([#7909](https://github.com/ionic-team/capacitor/issues/7909)) ([172638e](https://github.com/ionic-team/capacitor/commit/172638ec7b6eb67bf25f8dac2818122ba31c2c91))\n\n# [7.1.0](https://github.com/ionic-team/capacitor/compare/7.0.1...7.1.0) (2025-03-12)\n\n### Bug Fixes\n\n- **http:** boundary not added for Request objects ([#7897](https://github.com/ionic-team/capacitor/issues/7897)) ([bdaa6f3](https://github.com/ionic-team/capacitor/commit/bdaa6f3c38c33f3a021ac61f2de89101a5b66cff))\n- **ios:** don't check isMediaExtension on range requests ([#7868](https://github.com/ionic-team/capacitor/issues/7868)) ([028caa5](https://github.com/ionic-team/capacitor/commit/028caa5378d359fb1004098aa93a24ad0f49a4ae))\n- **ios:** listen for CapacitorViewDidAppear ([#7850](https://github.com/ionic-team/capacitor/issues/7850)) ([e24ffb7](https://github.com/ionic-team/capacitor/commit/e24ffb7d4de0bf3d53e92537f21c864f121c1fad))\n- **ios:** Reset plugin listeners when WebView process is terminated ([#7905](https://github.com/ionic-team/capacitor/issues/7905)) ([d039157](https://github.com/ionic-team/capacitor/commit/d0391576726955b2c1b484f1ca9a03465b9ef67e))\n\n### Features\n\n- Add function to inject external JS into WebView before document load ([#7864](https://github.com/ionic-team/capacitor/issues/7864)) ([ec0954c](https://github.com/ionic-team/capacitor/commit/ec0954c197543e913939f3ab9c4bcb172bfa3530))\n\n## [7.0.1](https://github.com/ionic-team/capacitor/compare/7.0.0...7.0.1) (2025-01-21)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [7.0.0](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.1...7.0.0) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [7.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/7.0.0-rc.0...7.0.0-rc.1) (2025-01-20)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [7.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/7.0.0-beta.0...7.0.0-rc.0) (2024-12-20)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [7.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.2...7.0.0-beta.0) (2024-12-20)\n\n### Bug Fixes\n\n- **ios:** Make Bridge webView first responder ([#7753](https://github.com/ionic-team/capacitor/issues/7753)) ([77e4668](https://github.com/ionic-team/capacitor/commit/77e4668fa8dbb24b4561387e101547f74e37538e))\n\n### Features\n\n- Add global initialFocus configuration ([#7775](https://github.com/ionic-team/capacitor/issues/7775)) ([61d0165](https://github.com/ionic-team/capacitor/commit/61d01653685d8e3594d2d8a6bd870fa9643ba95c))\n\n# [7.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/7.0.0-alpha.1...7.0.0-alpha.2) (2024-11-19)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [7.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/6.1.2...7.0.0-alpha.1) (2024-10-14)\n\n### Bug Fixes\n\n- **ios:** fix retain cycle caused by CDVPluginManager ([#7692](https://github.com/ionic-team/capacitor/issues/7692)) ([02bdb3d](https://github.com/ionic-team/capacitor/commit/02bdb3d1d15907dcc577f16b7f2e22050e54ffef))\n\n### Features\n\n- **core:** expose `methodName` via `CAPPluginCall` ([#7641](https://github.com/ionic-team/capacitor/issues/7641)) ([df109aa](https://github.com/ionic-team/capacitor/commit/df109aa1cfa1ea824e22feecbd2b7183a57fc693))\n- **ios:** JSValueEncoder/Decoder feature parity with JSONEncoder/Decoder ([#7647](https://github.com/ionic-team/capacitor/issues/7647)) ([410249b](https://github.com/ionic-team/capacitor/commit/410249b6c626e67235f25b466ed4969d52148bd1)), closes [#7576](https://github.com/ionic-team/capacitor/issues/7576)\n\n## [6.1.2](https://github.com/ionic-team/capacitor/compare/6.1.1...6.1.2) (2024-08-07)\n\n### Bug Fixes\n\n- **http:** pass original url as query param on the proxy url ([#7527](https://github.com/ionic-team/capacitor/issues/7527)) ([1da06e6](https://github.com/ionic-team/capacitor/commit/1da06e66cb9cfbf5a5cc48ba6c23cdbe18bc8fc0))\n\n## [6.1.1](https://github.com/ionic-team/capacitor/compare/6.1.0...6.1.1) (2024-07-17)\n\n### Bug Fixes\n\n- **http:** handle UInt8Array on body ([#7546](https://github.com/ionic-team/capacitor/issues/7546)) ([cfb9ce1](https://github.com/ionic-team/capacitor/commit/cfb9ce175615f69fe86b61af6d51ec2044d147cd))\n- **ios:** make removeAllListeners accessible from javascript ([#7566](https://github.com/ionic-team/capacitor/issues/7566)) ([388a822](https://github.com/ionic-team/capacitor/commit/388a8223c60e8eac8437ca4cb26a93ab359f53ad))\n\n# [6.1.0](https://github.com/ionic-team/capacitor/compare/6.0.0...6.1.0) (2024-06-11)\n\n### Bug Fixes\n\n- **http:** don't override readyState for non POST requests ([#7488](https://github.com/ionic-team/capacitor/issues/7488)) ([30c13a8](https://github.com/ionic-team/capacitor/commit/30c13a865e7710e6dc5f0ee014e951d52d030795))\n- **ios:** check if urlSchemeTask is stopped before calling its methods ([#7482](https://github.com/ionic-team/capacitor/issues/7482)) ([b32b5b1](https://github.com/ionic-team/capacitor/commit/b32b5b17ed14bd43c846b3fcb930bfd88e245e40))\n\n### Features\n\n- **ios:** CAPPluginMethod selector-based initializer ([#7412](https://github.com/ionic-team/capacitor/issues/7412)) ([44c5b55](https://github.com/ionic-team/capacitor/commit/44c5b55e36b85471a85ee5a1af47bdb1a5d83a8b))\n\n# [6.0.0](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.2...6.0.0) (2024-04-15)\n\n### Bug Fixes\n\n- **http:** Add URLSearchParams support ([#7374](https://github.com/ionic-team/capacitor/issues/7374)) ([9367ecc](https://github.com/ionic-team/capacitor/commit/9367ecc56a0c78249dccdf95dca5006422144289))\n- **http:** prevent POST request from being proxied ([#7395](https://github.com/ionic-team/capacitor/issues/7395)) ([7b8c352](https://github.com/ionic-team/capacitor/commit/7b8c3523decd5610dcf09e926640bf35b382d61d))\n\n# [6.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.1...6.0.0-rc.2) (2024-03-25)\n\n### Bug Fixes\n\n- **http:** change proxy url generation ([#7354](https://github.com/ionic-team/capacitor/issues/7354)) ([318c316](https://github.com/ionic-team/capacitor/commit/318c316847c5b059fb88b46d4acd31e1ced477e5))\n- inject cordova files if a cordova plugin is present ([#7363](https://github.com/ionic-team/capacitor/issues/7363)) ([ce9d505](https://github.com/ionic-team/capacitor/commit/ce9d50585b1cab183245197878bf625cf0289275))\n- vue 3 log warning causes error on iOS ([#6993](https://github.com/ionic-team/capacitor/issues/6993)) ([87271e2](https://github.com/ionic-team/capacitor/commit/87271e2671013ad35d13b22f2e96d4fe8f4eeaf0))\n\n# [6.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/6.0.0-rc.0...6.0.0-rc.1) (2024-03-15)\n\n### Bug Fixes\n\n- **core:** make 'content-type' header count for XMLHttpRequest patch ([#7161](https://github.com/ionic-team/capacitor/issues/7161)) ([26d7f68](https://github.com/ionic-team/capacitor/commit/26d7f688284914c6ef795564ba424119efc32a1c))\n- **http:** handle proxy urls with port ([#7273](https://github.com/ionic-team/capacitor/issues/7273)) ([514409a](https://github.com/ionic-team/capacitor/commit/514409aeb93ad65be105bbe2da8d2cd86ff159b0))\n- **http:** keep original URL properties on proxy ([#7329](https://github.com/ionic-team/capacitor/issues/7329)) ([cbb6407](https://github.com/ionic-team/capacitor/commit/cbb6407225b42a0d9db4f335a9766f119501021d))\n- **http:** Make proxy work with Request objects ([#7348](https://github.com/ionic-team/capacitor/issues/7348)) ([7e68725](https://github.com/ionic-team/capacitor/commit/7e6872573df03ab5cdc10a1a27db3e9fe81a141d))\n- **http:** route get requests through custom handler ([#6818](https://github.com/ionic-team/capacitor/issues/6818)) ([b853d06](https://github.com/ionic-team/capacitor/commit/b853d065055b5a819949551be58b62d40b52e37c))\n- **http:** set port for proxy url ([#7341](https://github.com/ionic-team/capacitor/issues/7341)) ([a3059dc](https://github.com/ionic-team/capacitor/commit/a3059dca4a7746d9fb7102a7d41f4da80e2f48b4))\n- **ios:** Moves `updateBinaryVersion` call to the end of `loadView`. ([#7226](https://github.com/ionic-team/capacitor/issues/7226)) ([7724760](https://github.com/ionic-team/capacitor/commit/77247602ac150797d2375118f702cf9ba39ea957))\n- **ios:** overwrite CORS headers on livereload ([#7339](https://github.com/ionic-team/capacitor/issues/7339)) ([0c8d69b](https://github.com/ionic-team/capacitor/commit/0c8d69b7f9c163730e7d74bb0147b61742c61676))\n\n### Features\n\n- **webview:** add setServerAssetPath method ([881235b](https://github.com/ionic-team/capacitor/commit/881235b14de23ef988746bfb89a5a0fc3c8d8466))\n\n# [6.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.2...6.0.0-rc.0) (2024-01-23)\n\n### Bug Fixes\n\n- remove duplicates from auto registration array using set ([#7192](https://github.com/ionic-team/capacitor/issues/7192)) ([c4984ae](https://github.com/ionic-team/capacitor/commit/c4984ae4433602dbe028f72736cb6e56e8f92bf9))\n- stop crashing from `objc_getClassList` ([#7187](https://github.com/ionic-team/capacitor/issues/7187)) ([e148db7](https://github.com/ionic-team/capacitor/commit/e148db7758e4186ad45fd74185a10fb757ff9b29))\n\n### Features\n\n- **ios:** Replace usage of UserDefaults with KeyValueStore. ([#7191](https://github.com/ionic-team/capacitor/issues/7191)) ([cd58ba2](https://github.com/ionic-team/capacitor/commit/cd58ba2a654f40bf72616f430d7b9604b283e23d))\n\n# [6.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.1...6.0.0-beta.2) (2023-12-14)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [6.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/6.0.0-beta.0...6.0.0-beta.1) (2023-12-14)\n\n### Bug Fixes\n\n- **ios:** Add Codable folder to podspec source_files ([#7131](https://github.com/ionic-team/capacitor/issues/7131)) ([04d1d55](https://github.com/ionic-team/capacitor/commit/04d1d557b51fcac31281a3f547300f06c6dacfb2))\n\n# [6.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.2...6.0.0-beta.0) (2023-12-13)\n\n### Bug Fixes\n\n- **http:** parse readablestream data on fetch request objects ([#6919](https://github.com/ionic-team/capacitor/issues/6919)) ([80ec3b7](https://github.com/ionic-team/capacitor/commit/80ec3b73db18b7b6841bf90ed50a67389946ab87))\n- **ios:** add some new cordova-ios classes used by Cordova plugins ([#7096](https://github.com/ionic-team/capacitor/issues/7096)) ([3db9051](https://github.com/ionic-team/capacitor/commit/3db9051eb015cf5f402f81b4cbaa7b27a5c9477a))\n\n### Features\n\n- **ios:** Add Codable support for CAPPluginCall and JSValueContainer ([#7119](https://github.com/ionic-team/capacitor/issues/7119)) ([af417e0](https://github.com/ionic-team/capacitor/commit/af417e0cbbb1a3a7b3b62756eebb8d1dc0952cc4))\n\n# [6.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/6.0.0-alpha.1...6.0.0-alpha.2) (2023-11-15)\n\n### Bug Fixes\n\n- **ios:** Remove CocoaPods Xcode 15 workaround that causes issues ([#7059](https://github.com/ionic-team/capacitor/issues/7059)) ([043a8db](https://github.com/ionic-team/capacitor/commit/043a8dba4059e33c7445696c186110bef1130e16))\n\n# [6.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/5.2.3...6.0.0-alpha.1) (2023-11-08)\n\n### Bug Fixes\n\n- **android:** make local urls use unpatched fetch ([#6953](https://github.com/ionic-team/capacitor/issues/6953)) ([e50e56c](https://github.com/ionic-team/capacitor/commit/e50e56c5231f230497d1bd420e02e2e065c38f86))\n- **http:** add support for Request objects in fetch ([24b3cc1](https://github.com/ionic-team/capacitor/commit/24b3cc113e3d8aae5d85dbf2d25bec0c35136477))\n- **http:** inherit object properties on window.XMLHttpRequest ([91c11d0](https://github.com/ionic-team/capacitor/commit/91c11d06f773c45a10f6f2d52f672ae6f189b162))\n\n### Features\n\n- Add XCFrameworks ([#7020](https://github.com/ionic-team/capacitor/issues/7020)) ([5306095](https://github.com/ionic-team/capacitor/commit/53060955dc83cdbfda66bed60c2efcba395a9ca8))\n- **ios:** Makes CapacitorBridge, WebViewAssetHandler, and WebViewDelegationHandler open classes, along with several of their methods ([#7009](https://github.com/ionic-team/capacitor/issues/7009)) ([40d62cb](https://github.com/ionic-team/capacitor/commit/40d62cbce950c2f3972764fe134cc37f2343f33d))\n- modify package.swift on update and sync ([#7042](https://github.com/ionic-team/capacitor/issues/7042)) ([24573fb](https://github.com/ionic-team/capacitor/commit/24573fb864c43551e2ce42721b45ff901155627d))\n\n# [5.6.0](https://github.com/ionic-team/capacitor/compare/5.5.1...5.6.0) (2023-12-14)\n\n### Bug Fixes\n\n- **ios:** add some new cordova-ios classes used by Cordova plugins ([#7115](https://github.com/ionic-team/capacitor/issues/7115)) ([5fb902b](https://github.com/ionic-team/capacitor/commit/5fb902b232d9afded2edc865c8d3c0c0e7efe5e7))\n\n## [5.5.1](https://github.com/ionic-team/capacitor/compare/5.5.0...5.5.1) (2023-10-25)\n\n### Bug Fixes\n\n- **ios:** CAPWebView config update ([#7004](https://github.com/ionic-team/capacitor/issues/7004)) ([f3e8be0](https://github.com/ionic-team/capacitor/commit/f3e8be0453c31f74a2fdf4c9a6d8d7967a6b5c20))\n\n# [5.5.0](https://github.com/ionic-team/capacitor/compare/5.4.2...5.5.0) (2023-10-11)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [5.4.2](https://github.com/ionic-team/capacitor/compare/5.4.1...5.4.2) (2023-10-04)\n\n### Bug Fixes\n\n- **android:** make local urls use unpatched fetch ([#6954](https://github.com/ionic-team/capacitor/issues/6954)) ([56fb853](https://github.com/ionic-team/capacitor/commit/56fb8536af53f4f4ee49b9394fd966ad514b9458))\n\n## [5.4.1](https://github.com/ionic-team/capacitor/compare/5.4.0...5.4.1) (2023-09-21)\n\n### Bug Fixes\n\n- **http:** parse readablestream data on fetch request objects ([3fe0642](https://github.com/ionic-team/capacitor/commit/3fe06426bd20713e2322780b70bc5d97ad371fae))\n- **http:** return xhr response headers case insensitive ([687b6b1](https://github.com/ionic-team/capacitor/commit/687b6b1780506c17fb73ed1d9cbf50c1d1e40ef1))\n- **ios:** Add workaround for CocoaPods problem on Xcode 15 ([#6921](https://github.com/ionic-team/capacitor/issues/6921)) ([1ffa244](https://github.com/ionic-team/capacitor/commit/1ffa2441fc8a04e4bf1712d0afb868a83e7f1951))\n\n# [5.4.0](https://github.com/ionic-team/capacitor/compare/5.3.0...5.4.0) (2023-09-14)\n\n### Bug Fixes\n\n- **http:** add support for defining xhr and angular http response types ([09bd040](https://github.com/ionic-team/capacitor/commit/09bd040dfe4b8808d7499b6ee592005420406cac))\n- **http:** add support for Request objects in fetch ([2fe4535](https://github.com/ionic-team/capacitor/commit/2fe4535e781b1a5cfa0f3359c1afa5c360073b6a))\n- **http:** inherit object properties on window.XMLHttpRequest ([5cd3b2f](https://github.com/ionic-team/capacitor/commit/5cd3b2fa6d6936864e1aab2e98963df2d4da3b95))\n\n# [5.3.0](https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0) (2023-08-23)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [5.2.3](https://github.com/ionic-team/capacitor/compare/5.2.2...5.2.3) (2023-08-10)\n\n### Bug Fixes\n\n- **cli:** signing type option issue ([#6716](https://github.com/ionic-team/capacitor/issues/6716)) ([ee0f745](https://github.com/ionic-team/capacitor/commit/ee0f7457e458ca4bb4eb74f67552ac2ace76016b))\n- **cookies:** hide httpOnly cookies from client ([0cc927e](https://github.com/ionic-team/capacitor/commit/0cc927ef5f0f7076a6d486d666d78483f1d71c54))\n- **http:** return valid response for relative url xhr requests ([bde6569](https://github.com/ionic-team/capacitor/commit/bde65696218f97a8328041f137457f46e5eb766a))\n\n## [5.2.2](https://github.com/ionic-team/capacitor/compare/5.2.1...5.2.2) (2023-07-19)\n\n### Bug Fixes\n\n- add http method to prototype.open ([#6740](https://github.com/ionic-team/capacitor/issues/6740)) ([1fd2d87](https://github.com/ionic-team/capacitor/commit/1fd2d8762ff2341a8fe20eec9e774c6a29576e88))\n\n## [5.2.1](https://github.com/ionic-team/capacitor/compare/5.2.0...5.2.1) (2023-07-13)\n\n### Bug Fixes\n\n- allow single parameter on setRequestBody ([#6728](https://github.com/ionic-team/capacitor/issues/6728)) ([5343bdb](https://github.com/ionic-team/capacitor/commit/5343bdb60d26849cd8f9c8ff28ba7d9ddbd05b26))\n\n# [5.2.0](https://github.com/ionic-team/capacitor/compare/5.1.1...5.2.0) (2023-07-12)\n\n### Bug Fixes\n\n- **cookies:** sanitize url before retrieving/setting cookies ([ca40634](https://github.com/ionic-team/capacitor/commit/ca4063471f215d3f7525e51592d9c72138a52855))\n- **http:** fire events in correct order when using xhr ([5ed3617](https://github.com/ionic-team/capacitor/commit/5ed361787596bb5949f6ae5e366495f296352bf3))\n\n### Features\n\n- **http:** support for FormData requests ([#6708](https://github.com/ionic-team/capacitor/issues/6708)) ([849c564](https://github.com/ionic-team/capacitor/commit/849c56458205bea3b078b1ee19807d7fd84c47b1))\n\n## [5.1.1](https://github.com/ionic-team/capacitor/compare/5.1.0...5.1.1) (2023-07-05)\n\n### Bug Fixes\n\n- **ios:** Revert server url addition for CAPWebView. ([#6705](https://github.com/ionic-team/capacitor/issues/6705)) ([1b8352d](https://github.com/ionic-team/capacitor/commit/1b8352dc5124dc3f57d7881d619537cbf8c3674b))\n\n# [5.1.0](https://github.com/ionic-team/capacitor/compare/5.0.5...5.1.0) (2023-06-29)\n\n### Bug Fixes\n\n- **ios:** Return proper MIME Type for local WASM files ([#6675](https://github.com/ionic-team/capacitor/issues/6675)) ([d7856de](https://github.com/ionic-team/capacitor/commit/d7856de62a4c058ac474ae91a5fd221dabf99c0a))\n- **ios:** set cors headers in asset handler for live reload ([e5a1c81](https://github.com/ionic-team/capacitor/commit/e5a1c81fe81904dfd7e3f5100a04088173effc1c))\n\n## [5.0.5](https://github.com/ionic-team/capacitor/compare/5.0.4...5.0.5) (2023-06-09)\n\n### Bug Fixes\n\n- **http:** don't throw errors when content-type is null on response ([#6627](https://github.com/ionic-team/capacitor/issues/6627)) ([538821f](https://github.com/ionic-team/capacitor/commit/538821f267aa3b79548fed6aaea8880ff949ffdd))\n\n## [5.0.4](https://github.com/ionic-team/capacitor/compare/5.0.3...5.0.4) (2023-05-23)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [5.0.3](https://github.com/ionic-team/capacitor/compare/5.0.2...5.0.3) (2023-05-10)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [5.0.2](https://github.com/ionic-team/capacitor/compare/5.0.1...5.0.2) (2023-05-09)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [5.0.1](https://github.com/ionic-team/capacitor/compare/5.0.0...5.0.1) (2023-05-05)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.3...5.0.0) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.2...5.0.0-rc.3) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.1...5.0.0-rc.2) (2023-05-03)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/5.0.0-rc.0...5.0.0-rc.1) (2023-05-02)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.3...5.0.0-rc.0) (2023-05-01)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.2...5.0.0-beta.3) (2023-04-21)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.1...5.0.0-beta.2) (2023-04-13)\n\n### Bug Fixes\n\n- **ios/android:** copy url from nativeResponse to response ([#6482](https://github.com/ionic-team/capacitor/issues/6482)) ([828fb71](https://github.com/ionic-team/capacitor/commit/828fb71ebb52c0655d5879ad0edaac7368ab2b96))\n\n### Features\n\n- **ios:** add webContentsDebuggingEnabled configuration ([#6495](https://github.com/ionic-team/capacitor/issues/6495)) ([c691e4a](https://github.com/ionic-team/capacitor/commit/c691e4aecbfb7a45ce0465d1fe9020ab715815d3))\n\n# [5.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/5.0.0-beta.0...5.0.0-beta.1) (2023-04-03)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [5.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/5.0.0-alpha.1...5.0.0-beta.0) (2023-03-31)\n\n### Bug Fixes\n\n- 204 http response ([#6266](https://github.com/ionic-team/capacitor/issues/6266)) ([771f6ce](https://github.com/ionic-team/capacitor/commit/771f6ce1f35159848db218a42dc4f56b5106f750))\n- **ios:** Event listeners were unexpectedly nil ([#6445](https://github.com/ionic-team/capacitor/issues/6445)) ([209d4ed](https://github.com/ionic-team/capacitor/commit/209d4edace610b00e689440a5c08e72f5da60cc2))\n\n### Features\n\n- retain multiple calls per event until consumed ([#6419](https://github.com/ionic-team/capacitor/issues/6419)) ([5aba2cb](https://github.com/ionic-team/capacitor/commit/5aba2cbe29bdbab2a7af861c65d8323acf9c54a6))\n\n# [5.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/4.7.0...5.0.0-alpha.1) (2023-03-16)\n\n### Bug Fixes\n\n- **iOS:** Separate cookies by `; ` rather than `;` when accessing through `document.cookie` ([#6313](https://github.com/ionic-team/capacitor/issues/6313)) ([beade60](https://github.com/ionic-team/capacitor/commit/beade6020e25dc405e796e1b06bf6dd90b217693))\n\n# [4.7.0](https://github.com/ionic-team/capacitor/compare/4.6.3...4.7.0) (2023-02-22)\n\n### Bug Fixes\n\n- handle fetch headers that are Headers objects ([#6320](https://github.com/ionic-team/capacitor/issues/6320)) ([cb00e49](https://github.com/ionic-team/capacitor/commit/cb00e4952acca8e877555f30b2190f6685d25934))\n- **ios:** Avoid double encoding on http urls ([#6288](https://github.com/ionic-team/capacitor/issues/6288)) ([4768085](https://github.com/ionic-team/capacitor/commit/4768085414768bb2c013afcc6c645664893cd297))\n- **ios:** Correctly Attach Headers to Request ([#6303](https://github.com/ionic-team/capacitor/issues/6303)) ([a3f875c](https://github.com/ionic-team/capacitor/commit/a3f875cf42e111fde07d6e87643264b19ed77573))\n\n## [4.6.3](https://github.com/ionic-team/capacitor/compare/4.6.2...4.6.3) (2023-02-03)\n\n### Bug Fixes\n\n- **ios:** crash when http headers contain numbers ([#6251](https://github.com/ionic-team/capacitor/issues/6251)) ([028c556](https://github.com/ionic-team/capacitor/commit/028c556a50b41ee99fe8f4f1aa2f42d3fd57f92d))\n\n## [4.6.2](https://github.com/ionic-team/capacitor/compare/4.6.1...4.6.2) (2023-01-17)\n\n### Bug Fixes\n\n- **ios:** CapacitorHttp cannot use delete method ([#6220](https://github.com/ionic-team/capacitor/issues/6220)) ([4d238a9](https://github.com/ionic-team/capacitor/commit/4d238a9e0dcf1e3e8c105c3aa4c7361abf16398e))\n- **ios:** encode whitespace in http plugin urls ([#6169](https://github.com/ionic-team/capacitor/issues/6169)) ([dccb0a9](https://github.com/ionic-team/capacitor/commit/dccb0a99850c7c878906156a509ecd675836ef1a))\n- **ios/android:** better http error handling ([#6208](https://github.com/ionic-team/capacitor/issues/6208)) ([7d4d70a](https://github.com/ionic-team/capacitor/commit/7d4d70a0500b7996c710c0762907f44bdf27c92b))\n\n## [4.6.1](https://github.com/ionic-team/capacitor/compare/4.6.0...4.6.1) (2022-12-05)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [4.6.0](https://github.com/ionic-team/capacitor/compare/4.5.0...4.6.0) (2022-12-01)\n\n### Bug Fixes\n\n- **cookies:** Use Set-Cookie headers to persist cookies ([57f8b39](https://github.com/ionic-team/capacitor/commit/57f8b39d7f4c5ee0e5e5cb316913e9450a81d22b))\n\n### Features\n\n- **ios:** Plugin Registration and Plugin Instance Support ([#6072](https://github.com/ionic-team/capacitor/issues/6072)) ([9f1d863](https://github.com/ionic-team/capacitor/commit/9f1d863c1222096334a0dd05f39ce7f984a2763a))\n\n# [4.5.0](https://github.com/ionic-team/capacitor/compare/4.4.0...4.5.0) (2022-11-16)\n\n### Bug Fixes\n\n- **cli/ios:** Read handleApplicationNotifications configuration option ([#6030](https://github.com/ionic-team/capacitor/issues/6030)) ([99ccf18](https://github.com/ionic-team/capacitor/commit/99ccf181f6ee8a00ed97bdbf9076e2b2ea27cd57))\n\n### Features\n\n- **cookies:** add get cookies plugin method ([ba1e770](https://github.com/ionic-team/capacitor/commit/ba1e7702a3338714aee24388c0afea39706c9341))\n\n# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21)\n\n### Bug Fixes\n\n- **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc))\n- **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f))\n\n# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21)\n\n### Bug Fixes\n\n- **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e))\n\n### Features\n\n- Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45))\n\n# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18)\n\n### Bug Fixes\n\n- **ios:** Prevent Xcode 14 warning on CAPWebView ([#5821](https://github.com/ionic-team/capacitor/issues/5821)) ([66954ef](https://github.com/ionic-team/capacitor/commit/66954ef6bc93f2038d85a386ef2f8b582af11bc3))\n- **ios:** return proper mimeType on M1 x86_64 simulators ([#5853](https://github.com/ionic-team/capacitor/issues/5853)) ([325b6fe](https://github.com/ionic-team/capacitor/commit/325b6fe83939efaaef44c7e8624e33de742a57e2)), closes [#5793](https://github.com/ionic-team/capacitor/issues/5793)\n\n### Features\n\n- **ios:** Add `setServerBasePath(_:)` to CAPBridgeProtocol ([#5860](https://github.com/ionic-team/capacitor/issues/5860)) ([76f28e7](https://github.com/ionic-team/capacitor/commit/76f28e70a5c0a03e4c6b9a93a0c068666a2c38ff))\n\n## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28)\n\n### Bug Fixes\n\n- **ios:** publish Podfile script ([#5799](https://github.com/ionic-team/capacitor/issues/5799)) ([604f03a](https://github.com/ionic-team/capacitor/commit/604f03a29bc500d2841987d0a0f1b20d34fba7d6))\n\n# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27)\n\n### Bug Fixes\n\n- **ios:** error data is optional ([#5782](https://github.com/ionic-team/capacitor/issues/5782)) ([da48d79](https://github.com/ionic-team/capacitor/commit/da48d798c3463de9de188ae6a6475fd6afba6091))\n\n### Features\n\n- **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625))\n- **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264))\n- **ios:** Add overrideable router var for CAPWebView. ([#5743](https://github.com/ionic-team/capacitor/issues/5743)) ([c1de1c0](https://github.com/ionic-team/capacitor/commit/c1de1c0138aad188a760118e35983d10d257f8e7))\n- **iOS:** post install script for deployment target ([#5783](https://github.com/ionic-team/capacitor/issues/5783)) ([f5afa94](https://github.com/ionic-team/capacitor/commit/f5afa94b3b9c246d87b2af03359840f503bace90))\n- Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3))\n\n# [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08)\n\n### Bug Fixes\n\n- **ios:** Add check for both serverURL and localURL in navigation ([#5736](https://github.com/ionic-team/capacitor/issues/5736)) ([8e824f3](https://github.com/ionic-team/capacitor/commit/8e824f33ad4df898fb8c0936a8f5e9041832a5c5))\n- **ios:** properly deliver retained events after listener re-add [#5732](https://github.com/ionic-team/capacitor/issues/5732) ([c5d6328](https://github.com/ionic-team/capacitor/commit/c5d632831924a1bcc868bc46b42f7ff619408752))\n\n### Features\n\n- **ios:** Add `setServerBasePath(path:)` to CAPWebView ([#5742](https://github.com/ionic-team/capacitor/issues/5742)) ([1afbf8a](https://github.com/ionic-team/capacitor/commit/1afbf8a9dd0b8f7b1ac439d24e5d8ba26f786318))\n- Add CapWebView ([#5715](https://github.com/ionic-team/capacitor/issues/5715)) ([143d266](https://github.com/ionic-team/capacitor/commit/143d266ef0a818bac59dbbdaeda3b5c382ebfa1d))\n\n# [4.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.0...4.0.0-beta.1) (2022-06-27)\n\n### Bug Fixes\n\n- **ios:** Remove Cordova as an embedded framework ([#5709](https://github.com/ionic-team/capacitor/issues/5709)) ([bbf6d24](https://github.com/ionic-team/capacitor/commit/bbf6d248bf9217a5c5c6c15c7bcfeda209aba5b1))\n\n### Features\n\n- **ios:** Allow to configure popover size ([#5717](https://github.com/ionic-team/capacitor/issues/5717)) ([ca1a125](https://github.com/ionic-team/capacitor/commit/ca1a125e5ab05d6066dd303bc75e99dfe21f210a))\n\n# [4.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.6.0...4.0.0-beta.0) (2022-06-17)\n\n### Bug Fixes\n\n- **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc))\n\n### Features\n\n- **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))\n- **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))\n- **ios:** Support of range requests on WebViewAssetHandler ([#5659](https://github.com/ionic-team/capacitor/issues/5659)) ([348c08d](https://github.com/ionic-team/capacitor/commit/348c08d511e9d57a1b2ecedc3290c65fa9ba3924))\n\n# [3.6.0](https://github.com/ionic-team/capacitor/compare/3.5.1...3.6.0) (2022-06-17)\n\n### Bug Fixes\n\n- **ios:** Use `URL(fileURLWithPath:)` instead of `URL(string:)` ([#5603](https://github.com/ionic-team/capacitor/issues/5603)) ([5fac1b2](https://github.com/ionic-team/capacitor/commit/5fac1b2da5aa5882087716cb2aa862d89173f4a1))\n\n### Features\n\n- **iOS, Android:** add AppUUID Lib for plugins ([#5690](https://github.com/ionic-team/capacitor/issues/5690)) ([05e76cf](https://github.com/ionic-team/capacitor/commit/05e76cf526a44e07fa75f9482fa2223a13918638))\n\n# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12)\n\n### Bug Fixes\n\n- **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc))\n\n### Features\n\n- **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))\n- **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))\n\n## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22)\n\n### Features\n\n- **ios:** Add overrideable routing for CAPBridgeViewController subclasses ([#5546](https://github.com/ionic-team/capacitor/issues/5546)) ([8875d5e](https://github.com/ionic-team/capacitor/commit/8875d5e2721e8a8ee763ce70cb672db383f36efa))\n\n# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25)\n\n### Features\n\n- **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))\n\n## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09)\n\n### Bug Fixes\n\n- **ios:** Reload webView on webViewWebContentProcessDidTerminate ([#5391](https://github.com/ionic-team/capacitor/issues/5391)) ([beebff4](https://github.com/ionic-team/capacitor/commit/beebff4550575c28c233937a11a8eacf5a76411c))\n\n# [3.4.0](https://github.com/ionic-team/capacitor/compare/3.3.4...3.4.0) (2022-01-19)\n\n### Features\n\n- **ios:** Add new iOS 15 Motion permission delegate ([#5317](https://github.com/ionic-team/capacitor/issues/5317)) ([c05a3cb](https://github.com/ionic-team/capacitor/commit/c05a3cbbf02217e3972d5e067970cae18bff3faa))\n- **ios:** Add new iOS15 media capture permission delegate ([#5196](https://github.com/ionic-team/capacitor/issues/5196)) ([d8b54ac](https://github.com/ionic-team/capacitor/commit/d8b54ac23414bfe56e50e1254066630a6f87eb0e))\n\n## [3.3.4](https://github.com/ionic-team/capacitor/compare/3.3.3...3.3.4) (2022-01-05)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [3.3.3](https://github.com/ionic-team/capacitor/compare/3.3.2...3.3.3) (2021-12-08)\n\n### Bug Fixes\n\n- **ios:** Present js alert on top of the presented VC ([#5282](https://github.com/ionic-team/capacitor/issues/5282)) ([a53d236](https://github.com/ionic-team/capacitor/commit/a53d236452e99d1e6151e19e313b3d1545957419))\n\n## [3.3.2](https://github.com/ionic-team/capacitor/compare/3.3.1...3.3.2) (2021-11-17)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [3.3.1](https://github.com/ionic-team/capacitor/compare/3.3.0...3.3.1) (2021-11-05)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.3.0](https://github.com/ionic-team/capacitor/compare/3.2.5...3.3.0) (2021-11-03)\n\n### Bug Fixes\n\n- **core:** avoid crash on logging circular objects ([#5186](https://github.com/ionic-team/capacitor/issues/5186)) ([1451ec8](https://github.com/ionic-team/capacitor/commit/1451ec850a9ef73267a032638e73f1fc440647b9))\n- **ios:** Avoid CDVScreenOrientationDelegate umbrella header warning ([#5156](https://github.com/ionic-team/capacitor/issues/5156)) ([31ec30d](https://github.com/ionic-team/capacitor/commit/31ec30de193aa3117dbb7eda928ef3a365d5667c))\n\n## [3.2.5](https://github.com/ionic-team/capacitor/compare/3.2.4...3.2.5) (2021-10-13)\n\n### Bug Fixes\n\n- **ios:** proper handling of allowNavigation with multiple wildcard ([#5096](https://github.com/ionic-team/capacitor/issues/5096)) ([cda17a6](https://github.com/ionic-team/capacitor/commit/cda17a6c1504235c1c1e4826830f1d0e2ef2d35c))\n\n## [3.2.4](https://github.com/ionic-team/capacitor/compare/3.2.3...3.2.4) (2021-09-27)\n\n### Bug Fixes\n\n- **ios:** Add CDVScreenOrientationDelegate protocol on CAPBridgeViewController ([#5070](https://github.com/ionic-team/capacitor/issues/5070)) ([530477d](https://github.com/ionic-team/capacitor/commit/530477d05e1364931f83a30d61d4f9b5cb687b19))\n- **ios:** show correct line number on console logs ([#5073](https://github.com/ionic-team/capacitor/issues/5073)) ([ec41e74](https://github.com/ionic-team/capacitor/commit/ec41e743aa4ba81e791ad446fac461b7f43b46ed))\n\n## [3.2.3](https://github.com/ionic-team/capacitor/compare/3.2.2...3.2.3) (2021-09-15)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [3.2.2](https://github.com/ionic-team/capacitor/compare/3.2.1...3.2.2) (2021-09-02)\n\n### Bug Fixes\n\n- **ios:** fixing podspec source paths ([#5002](https://github.com/ionic-team/capacitor/issues/5002)) ([6004a43](https://github.com/ionic-team/capacitor/commit/6004a43c608a4c967e3444c83954ad2095c3dcfd))\n\n## [3.2.1](https://github.com/ionic-team/capacitor/compare/3.2.0...3.2.1) (2021-09-01)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.2.0](https://github.com/ionic-team/capacitor/compare/3.1.2...3.2.0) (2021-08-18)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [3.1.2](https://github.com/ionic-team/capacitor/compare/3.1.1...3.1.2) (2021-07-21)\n\n**Note:** Version bump only for package @capacitor/ios\n\n## [3.1.1](https://github.com/ionic-team/capacitor/compare/3.1.0...3.1.1) (2021-07-07)\n\n### Bug Fixes\n\n- fixing peer deps issues in android and ios libs ([310d9f4](https://github.com/ionic-team/capacitor/commit/310d9f486db976cb258fcda5ac893f019667617f))\n\n# [3.1.0](https://github.com/ionic-team/capacitor/compare/3.0.2...3.1.0) (2021-07-07)\n\n### Bug Fixes\n\n- **ios:** isNewBinary is true if defaults have no stored versions ([#4779](https://github.com/ionic-team/capacitor/issues/4779)) ([bd86dbe](https://github.com/ionic-team/capacitor/commit/bd86dbeb74771ed201d0100773babf49e6764818))\n\n### Features\n\n- **ios:** Add limitsNavigationsToAppBoundDomains configuration option ([#4789](https://github.com/ionic-team/capacitor/issues/4789)) ([2b7016f](https://github.com/ionic-team/capacitor/commit/2b7016f3b4d62fd8c9d03fde2745b3d515bf08b2))\n\n## [3.0.2](https://github.com/ionic-team/capacitor/compare/3.0.1...3.0.2) (2021-06-23)\n\n### Bug Fixes\n\n- **core:** cordova events not firing ([#4712](https://github.com/ionic-team/capacitor/issues/4712)) ([ca4e3b6](https://github.com/ionic-team/capacitor/commit/ca4e3b62dba6a40e593a1404ba2fe2b416a4ac14))\n- **ios:** Use proper native events for cordova events ([#4720](https://github.com/ionic-team/capacitor/issues/4720)) ([99c21dc](https://github.com/ionic-team/capacitor/commit/99c21dcf98f1418d992e845492c730160611783a))\n\n## [3.0.1](https://github.com/ionic-team/capacitor/compare/3.0.0...3.0.1) (2021-06-09)\n\n### Bug Fixes\n\n- Make isPluginAvailable available on bridge ([#4589](https://github.com/ionic-team/capacitor/issues/4589)) ([151e7a8](https://github.com/ionic-team/capacitor/commit/151e7a899d9646dbd5625a2539fd3f2297349bc5))\n\n# [3.0.0](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.4...3.0.0) (2021-05-18)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-rc.4](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.3...3.0.0-rc.4) (2021-05-18)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-rc.3](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.2...3.0.0-rc.3) (2021-05-11)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-rc.2](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.1...3.0.0-rc.2) (2021-05-07)\n\n### Bug Fixes\n\n- **ios:** Don't auto release saved calls ([#4535](https://github.com/ionic-team/capacitor/issues/4535)) ([4f76933](https://github.com/ionic-team/capacitor/commit/4f76933b98d0461564d3dca9b36d4ea1eba8ed49))\n\n# [3.0.0-rc.1](https://github.com/ionic-team/capacitor/compare/3.0.0-rc.0...3.0.0-rc.1) (2021-04-29)\n\n### Bug Fixes\n\n- generate Capacitor.Plugins object ([#4496](https://github.com/ionic-team/capacitor/issues/4496)) ([1c71b7a](https://github.com/ionic-team/capacitor/commit/1c71b7adb2c325e34d980dbf578dc22afb2c332b))\n- **ios:** cordova-plugin-screen-orientation compatibility ([#4367](https://github.com/ionic-team/capacitor/issues/4367)) ([b893a57](https://github.com/ionic-team/capacitor/commit/b893a57aaaf3a16e13db9c33037a12f1a5ac92e0))\n- **ios:** fire resume/pause events if no cordova plugins installed ([#4467](https://github.com/ionic-team/capacitor/issues/4467)) ([0105f7a](https://github.com/ionic-team/capacitor/commit/0105f7a2c68f2e7bec16ca23384b6acbb2f3057b))\n- **ios:** put cancel button of confirm/prompt on the left ([#4464](https://github.com/ionic-team/capacitor/issues/4464)) ([e5b53aa](https://github.com/ionic-team/capacitor/commit/e5b53aa687a70938994802c7b1367cfcbb1e3811))\n\n### Features\n\n- Unify logging behavior across environments ([#4416](https://github.com/ionic-team/capacitor/issues/4416)) ([bae0f3d](https://github.com/ionic-team/capacitor/commit/bae0f3d2cee84978636d0f589bc7e2f745671baf))\n- **iOS:** Include native-bridge.js as a resource in both Cocoapods and direct build ([#4505](https://github.com/ionic-team/capacitor/issues/4505)) ([c16ccc0](https://github.com/ionic-team/capacitor/commit/c16ccc0118aec57dc23649894bc3bcd83827f89f))\n\n# [3.0.0-rc.0](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.6...3.0.0-rc.0) (2021-03-10)\n\n### Features\n\n- **iOS:** Obj-C convenience accessors on CAPPluginCall ([#4309](https://github.com/ionic-team/capacitor/issues/4309)) ([e3657d7](https://github.com/ionic-team/capacitor/commit/e3657d77647187946ffcd4c4791f4a47c768db7f))\n- **iOS:** Unifying saving plugin calls ([#4253](https://github.com/ionic-team/capacitor/issues/4253)) ([de71da5](https://github.com/ionic-team/capacitor/commit/de71da52b80ff52d0234a5301fc6cae675640a33))\n\n# [3.0.0-beta.6](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.5...3.0.0-beta.6) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-beta.5](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.4...3.0.0-beta.5) (2021-02-27)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-beta.4](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.3...3.0.0-beta.4) (2021-02-26)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-beta.3](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.2...3.0.0-beta.3) (2021-02-18)\n\n### Features\n\n- **iOS:** Add automatic Date serialization to bridge communication ([#4177](https://github.com/ionic-team/capacitor/issues/4177)) ([3dabc69](https://github.com/ionic-team/capacitor/commit/3dabc69eab1c8ce0b7734acb641b67d349ec3093))\n\n# [3.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.1...3.0.0-beta.2) (2021-02-08)\n\n### Bug Fixes\n\n- address bug in `isPluginAvailable()` for web and native ([#4114](https://github.com/ionic-team/capacitor/issues/4114)) ([2fbd954](https://github.com/ionic-team/capacitor/commit/2fbd95465a321b8f4c50d4daf22a63d8043cee9b))\n- **iOS:** preserve null values in bridged types ([#4072](https://github.com/ionic-team/capacitor/issues/4072)) ([6dc691e](https://github.com/ionic-team/capacitor/commit/6dc691e66a07a421d5d4b08028ea05a65b3ddd84))\n\n# [3.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/3.0.0-beta.0...3.0.0-beta.1) (2021-01-14)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.14...3.0.0-beta.0) (2021-01-13)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-alpha.14](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.13...3.0.0-alpha.14) (2021-01-13)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-alpha.13](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.12...3.0.0-alpha.13) (2021-01-13)\n\n### Bug Fixes\n\n- **iOS:** properly handle date types during JSValue coercion ([#4043](https://github.com/ionic-team/capacitor/issues/4043)) ([1affae7](https://github.com/ionic-team/capacitor/commit/1affae7cf8d2f49681bf25be48633ab985bbd12f))\n- **iOS:** skip Swift type coercion on Cordova plugin calls ([#4048](https://github.com/ionic-team/capacitor/issues/4048)) ([7bb9e0f](https://github.com/ionic-team/capacitor/commit/7bb9e0f22fdea369a6522c2d63a5b56baab9f5ca))\n\n# [3.0.0-alpha.12](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.11...3.0.0-alpha.12) (2021-01-08)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-alpha.11](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.10...3.0.0-alpha.11) (2020-12-26)\n\n### Features\n\n- **iOS:** Open CAPBridgeViewController for subclassing ([#3973](https://github.com/ionic-team/capacitor/issues/3973)) ([a601705](https://github.com/ionic-team/capacitor/commit/a601705f8116ac10d1a0b5942511952c07cf474e))\n\n# [3.0.0-alpha.9](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.8...3.0.0-alpha.9) (2020-12-15)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-alpha.8](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.7...3.0.0-alpha.8) (2020-12-15)\n\n### Bug Fixes\n\n- **ios:** expose lastURL getter ([#3898](https://github.com/ionic-team/capacitor/issues/3898)) ([90b7fe3](https://github.com/ionic-team/capacitor/commit/90b7fe39f5a7cb9d584618a6fba66338f2bbf5fe))\n\n# [3.0.0-alpha.7](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.6...3.0.0-alpha.7) (2020-12-02)\n\n### Bug Fixes\n\n- **ios:** share message handler between webview and bridge ([#3875](https://github.com/ionic-team/capacitor/issues/3875)) ([f7dff2e](https://github.com/ionic-team/capacitor/commit/f7dff2e661a54bee770940ee1ebd9eab6456ba2e))\n\n### Features\n\n- **ios:** add local and remote notification router ([#3796](https://github.com/ionic-team/capacitor/issues/3796)) ([f3edaf9](https://github.com/ionic-team/capacitor/commit/f3edaf93d4328ea3ee90df573bf14ef0efc7553b))\n- **ios:** add path utilities to bridge ([#3842](https://github.com/ionic-team/capacitor/issues/3842)) ([c31eb35](https://github.com/ionic-team/capacitor/commit/c31eb35f83a33626a9d88731c0fff18966c71b0b))\n- **iOS:** Add base implementation of permissions calls ([#3856](https://github.com/ionic-team/capacitor/issues/3856)) ([d733236](https://github.com/ionic-team/capacitor/commit/d7332364212794a5005229defd05c129921d9c5d))\n- **iOS:** Refactoring configuration ([#3759](https://github.com/ionic-team/capacitor/issues/3759)) ([e2e64c2](https://github.com/ionic-team/capacitor/commit/e2e64c23b88d93a1c594df51dddd0c55d5f37770))\n\n# [3.0.0-alpha.6](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.5...3.0.0-alpha.6) (2020-10-30)\n\n### Features\n\n- unified errors and error codes ([#3673](https://github.com/ionic-team/capacitor/issues/3673)) ([f9e0803](https://github.com/ionic-team/capacitor/commit/f9e08038aa88f7453e8235f380d2767a12a7a073))\n\n# [3.0.0-alpha.5](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.4...3.0.0-alpha.5) (2020-10-06)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.3...3.0.0-alpha.4) (2020-09-23)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.2...3.0.0-alpha.3) (2020-09-15)\n\n**Note:** Version bump only for package @capacitor/ios\n\n# [3.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.0.0-alpha.1...3.0.0-alpha.2) (2020-08-31)\n\n### Features\n\n- Add extension for creating data from data url ([#3474](https://github.com/ionic-team/capacitor/issues/3474)) ([2909fd0](https://github.com/ionic-team/capacitor/commit/2909fd0ac0d9fdb2cdb7fd25e38742451aa05fb1))\n\n# [3.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/2.4.0...3.0.0-alpha.1) (2020-08-21)\n\n### Bug Fixes\n\n- **ios:** config bug from swiftlint refactor ([ace879f](https://github.com/ionic-team/capacitor/commit/ace879f42b19aa064efa80142c3783f736745344))\n\n# [3.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/2.3.0...3.0.0-alpha.0) (2020-07-23)\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/AppUUID.swift",
    "content": "import CommonCrypto\nimport Foundation\n\nprivate func hexString(_ iterator: Array<UInt8>.Iterator) -> String {\n    return iterator.map { String(format: \"%02x\", $0) }.joined()\n}\n\nextension Data {\n    public var sha256: String {\n        var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))\n        self.withUnsafeBytes { bytes in\n            _ = CC_SHA256(bytes.baseAddress, CC_LONG(self.count), &digest)\n        }\n        return hexString(digest.makeIterator())\n    }\n}\n\npublic class AppUUID {\n    private static let key: String = \"CapacitorAppUUID\"\n\n    public static func getAppUUID() -> String {\n        assertAppUUID()\n        return readUUID()\n    }\n\n    public static func regenerateAppUUID() {\n        let uuid = generateUUID()\n        writeUUID(uuid)\n    }\n\n    private static func assertAppUUID() {\n        let uuid = readUUID()\n        if uuid == \"\" {\n            regenerateAppUUID()\n        }\n    }\n\n    private static func generateUUID() -> String {\n        let uuid: String = UUID.init().uuidString\n        return uuid.data(using: .utf8)!.sha256\n    }\n\n    private static func readUUID() -> String {\n        KeyValueStore.standard[key] ?? \"\"\n    }\n\n    private static func writeUUID(_ uuid: String) {\n        KeyValueStore.standard[key] = uuid\n    }\n\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Array+Capacitor.swift",
    "content": "// convenience wrappers to transform Arrays between NSNull and Optional values, for interoperability with Obj-C\nextension Array: CapacitorExtension {}\nextension CapacitorExtensionTypeWrapper where T == [JSValue] {\n    public func replacingNullValues() -> [JSValue?] {\n        return baseType.map({ (value) -> JSValue? in\n            if value is NSNull {\n                return nil\n            }\n            return value\n        })\n    }\n\n    public func replacingOptionalValues() -> [JSValue] {\n        return baseType\n    }\n}\n\nextension CapacitorExtensionTypeWrapper where T == [JSValue?] {\n    public func replacingNullValues() -> [JSValue?] {\n        return baseType\n    }\n\n    public func replacingOptionalValues() -> [JSValue] {\n        return baseType.map({ (value) -> JSValue in\n            if let value = value {\n                return value\n            }\n            return NSNull()\n        })\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift",
    "content": "import Foundation\n\n@objc(CAPApplicationDelegateProxy)\npublic class ApplicationDelegateProxy: NSObject, UIApplicationDelegate {\n    public static let shared = ApplicationDelegateProxy()\n\n    public private(set) var lastURL: URL?\n\n    public func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {\n        NotificationCenter.default.post(name: .capacitorOpenURL, object: [\n            \"url\": url,\n            \"options\": options\n        ])\n        NotificationCenter.default.post(name: NSNotification.Name.CDVPluginHandleOpenURL, object: url)\n        lastURL = url\n        return true\n    }\n\n    public func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {\n        // TODO: Support other types, emit to rest of plugins\n        if userActivity.activityType != NSUserActivityTypeBrowsingWeb || userActivity.webpageURL == nil {\n            return false\n        }\n\n        let url = userActivity.webpageURL\n        lastURL = url\n        NotificationCenter.default.post(name: .capacitorOpenUniversalLink, object: [\n            \"url\": url\n        ])\n        return true\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridge.swift",
    "content": "import Foundation\n\n// the @available compiler directive does not provide an easy way to split apart string literals, so ignore the line length\n// swiftlint:disable line_length\n@available(*, deprecated, message: \"'statusBarTappedNotification' has been moved to Notification.Name.capacitorStatusBarTapped. 'getLastUrl' and application delegate methods have been moved to ApplicationDelegateProxy.\")\n// swiftlint:enable line_length\n@objc public class CAPBridge: NSObject {\n    @objc public static let statusBarTappedNotification = Notification(name: .capacitorStatusBarTapped)\n\n    public static func getLastUrl() -> URL? {\n        return ApplicationDelegateProxy.shared.lastURL\n    }\n\n    public static func handleOpenUrl(_ url: URL, _ options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {\n        return ApplicationDelegateProxy.shared.application(UIApplication.shared, open: url, options: options)\n    }\n\n    public static func handleContinueActivity(_ userActivity: NSUserActivity, _ restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {\n        return ApplicationDelegateProxy.shared.application(UIApplication.shared, continue: userActivity, restorationHandler: restorationHandler)\n    }\n\n    public static func handleAppBecameActive(_ application: UIApplication) {\n        // no-op for now\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgeDelegate.swift",
    "content": "import Foundation\n\npublic protocol CAPBridgeDelegate: AnyObject {\n    var bridgedWebView: WKWebView? { get }\n    var bridgedViewController: UIViewController? { get }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgeProtocol.swift",
    "content": "import Foundation\nimport WebKit\n\n@objc public protocol CAPBridgeProtocol: NSObjectProtocol {\n    // MARK: - Environment Properties\n    var viewController: UIViewController? { get }\n    var config: InstanceConfiguration { get }\n    var webView: WKWebView? { get }\n    var notificationRouter: NotificationRouter { get }\n    var isSimEnvironment: Bool { get }\n    var isDevEnvironment: Bool { get }\n    var userInterfaceStyle: UIUserInterfaceStyle { get }\n    var autoRegisterPlugins: Bool { get }\n    var statusBarVisible: Bool { get set }\n    var statusBarStyle: UIStatusBarStyle { get set }\n    var statusBarAnimation: UIStatusBarAnimation { get set }\n\n    // MARK: - Deprecated\n    @available(*, deprecated, renamed: \"webView\")\n    func getWebView() -> WKWebView?\n\n    @available(*, deprecated, renamed: \"isSimEnvironment\")\n    func isSimulator() -> Bool\n\n    @available(*, deprecated, renamed: \"isDevEnvironment\")\n    func isDevMode() -> Bool\n\n    @available(*, deprecated, renamed: \"statusBarVisible\")\n    func getStatusBarVisible() -> Bool\n\n    @available(*, deprecated, renamed: \"statusBarStyle\")\n    func getStatusBarStyle() -> UIStatusBarStyle\n\n    @available(*, deprecated, renamed: \"userInterfaceStyle\")\n    func getUserInterfaceStyle() -> UIUserInterfaceStyle\n\n    @available(*, deprecated, message: \"Moved - equivalent is found on config.localURL\")\n    func getLocalUrl() -> String\n\n    @available(*, deprecated, renamed: \"savedCall(withID:)\")\n    func getSavedCall(_ callbackId: String) -> CAPPluginCall?\n\n    @available(*, deprecated, renamed: \"releaseCall(withID:)\")\n    func releaseCall(callbackId: String)\n\n    // MARK: - Plugin Access\n    func plugin(withName: String) -> CAPPlugin?\n\n    // MARK: - Call Management\n    func saveCall(_ call: CAPPluginCall)\n    func savedCall(withID: String) -> CAPPluginCall?\n    func releaseCall(_ call: CAPPluginCall)\n    func releaseCall(withID: String)\n\n    // MARK: - JavaScript Handling\n    // `js` is a short name but needs to be preserved for backwards compatibility.\n    // swiftlint:disable identifier_name\n    func evalWithPlugin(_ plugin: CAPPlugin, js: String)\n    func eval(js: String)\n    // swiftlint:enable identifier_name\n\n    @objc optional func injectScriptBeforeLoad(path: String)\n\n    func triggerJSEvent(eventName: String, target: String)\n    func triggerJSEvent(eventName: String, target: String, data: String)\n\n    func triggerWindowJSEvent(eventName: String)\n    func triggerWindowJSEvent(eventName: String, data: String)\n\n    func triggerDocumentJSEvent(eventName: String)\n    func triggerDocumentJSEvent(eventName: String, data: String)\n\n    // MARK: - Paths, Files, Assets\n    func localURL(fromWebURL webURL: URL?) -> URL?\n    func portablePath(fromLocalURL localURL: URL?) -> URL?\n    func setServerBasePath(_ path: String)\n\n    // MARK: - Plugins\n    func registerPluginType(_ pluginType: CAPPlugin.Type)\n    func registerPluginInstance(_ pluginInstance: CAPPlugin)\n\n    // MARK: - View Presentation\n    func showAlertWith(title: String, message: String, buttonTitle: String)\n    @available(*, deprecated, message: \"Use self?.bridge?.viewController?.present\")\n    func presentVC(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?)\n    @available(*, deprecated, message: \"Use self?.bridge?.viewController?.dismiss\")\n    func dismissVC(animated flag: Bool, completion: (() -> Void)?)\n}\n\n/*\n Extensions to Obj-C protocols are not exposed to Obj-C code because of limitations in the runtime.\n Therefore these methods are implicitly Swift-only.\n\n The deprecated methods are declared here because they can be defined without colliding with the synthesized Obj-C setters\n for the respective properties (e.g. `setStatusBarVisible:` for 'statusBarVisible`).\n */\nextension CAPBridgeProtocol {\n    // variadic parameters cannot be exposed to Obj-C\n    @available(*, deprecated, message: \"Use CAPLog directly\")\n    public func modulePrint(_ plugin: CAPPlugin, _ items: Any...) {\n        let output = items.map { String(describing: $0) }.joined(separator: \" \")\n        CAPLog.print(\"⚡️ \", plugin.pluginId, \"-\", output)\n    }\n\n    // default arguments are not permitted in protocol declarations\n    public func alert(_ title: String, _ message: String, _ buttonTitle: String = \"OK\") {\n        showAlertWith(title: title, message: message, buttonTitle: buttonTitle)\n    }\n\n    @available(*, deprecated, renamed: \"statusBarVisible\")\n    public func setStatusBarVisible(_ visible: Bool) {\n        statusBarVisible = visible\n    }\n\n    @available(*, deprecated, renamed: \"statusBarStyle\")\n    public func setStatusBarStyle(_ style: UIStatusBarStyle) {\n        statusBarStyle = style\n    }\n\n    @available(*, deprecated, renamed: \"statusBarAnimation\")\n    public func setStatusBarAnimation(_ animation: UIStatusBarAnimation) {\n        statusBarAnimation = animation\n    }\n}\n\n/*\n Error(s) potentially exported by the bridge.\n */\npublic enum CapacitorBridgeError: Error {\n    case errorExportingCoreJS\n}\n\nextension CapacitorBridgeError: CustomNSError {\n    public static var errorDomain: String { \"CapacitorBridge\" }\n    public var errorCode: Int {\n        switch self {\n        case .errorExportingCoreJS:\n            return 0\n        }\n    }\n    public var errorUserInfo: [String: Any] {\n        return [\"info\": String(describing: self)]\n    }\n}\n\nextension CapacitorBridgeError: LocalizedError {\n    public var errorDescription: String? {\n        return NSLocalizedString(\"Unable to export JavaScript bridge code to webview\", comment: \"Capacitor bridge initialization error\")\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h",
    "content": "#import <Capacitor/Capacitor-Swift.h>\n\n@interface CAPBridgeViewController (CDVScreenOrientationDelegate) <CDVScreenOrientationDelegate>\n\n@end\n\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m",
    "content": "#import \"CAPBridgeViewController+CDVScreenOrientationDelegate.h\"\n\n@implementation CAPBridgeViewController (CDVScreenOrientationDelegate)\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgeViewController.swift",
    "content": "import UIKit\nimport WebKit\nimport Cordova\n\n@objc open class CAPBridgeViewController: UIViewController {\n    private var capacitorBridge: CapacitorBridge?\n    public final var bridge: CAPBridgeProtocol? {\n        return capacitorBridge\n    }\n\n    public fileprivate(set) var webView: WKWebView?\n\n    public var isStatusBarVisible = true\n    public var statusBarStyle: UIStatusBarStyle = .default\n    public var statusBarAnimation: UIStatusBarAnimation = .fade\n    @objc public var supportedOrientations: [Int] = []\n\n    public lazy final var isNewBinary: Bool = {\n        if let curVersionCode = Bundle.main.infoDictionary?[\"CFBundleVersion\"] as? String,\n           let curVersionName = Bundle.main.infoDictionary?[\"CFBundleShortVersionString\"] as? String {\n            if let lastVersionCode = KeyValueStore.standard[\"lastBinaryVersionCode\", as: String.self],\n               let lastVersionName = KeyValueStore.standard[\"lastBinaryVersionName\", as: String.self] {\n                return curVersionCode != lastVersionCode || curVersionName != lastVersionName\n            }\n            return true\n        }\n        return false\n    }()\n\n    override public final func loadView() {\n        // load the configuration and set the logging flag\n        let configDescriptor = instanceDescriptor()\n        let configuration = InstanceConfiguration(with: configDescriptor, isDebug: CapacitorBridge.isDevEnvironment)\n        CAPLog.enableLogging = configuration.loggingEnabled\n        logWarnings(for: configDescriptor)\n\n        setStatusBarDefaults()\n        setScreenOrientationDefaults()\n\n        // get the web view\n        let assetHandler = WebViewAssetHandler(router: router())\n        assetHandler.setAssetPath(configuration.appLocation.path)\n        assetHandler.setServerUrl(configuration.serverURL)\n        let delegationHandler = WebViewDelegationHandler()\n        prepareWebView(with: configuration, assetHandler: assetHandler, delegationHandler: delegationHandler)\n        view = webView\n        // create the bridge\n        capacitorBridge = CapacitorBridge(with: configuration,\n                                          delegate: self,\n                                          cordovaConfiguration: configDescriptor.cordovaConfiguration,\n                                          assetHandler: assetHandler,\n                                          delegationHandler: delegationHandler)\n        capacitorDidLoad()\n\n        if configDescriptor.instanceType == .fixed {\n            updateBinaryVersion()\n        }\n    }\n\n    override open func viewDidLoad() {\n        super.viewDidLoad()\n        loadWebView()\n    }\n\n    override open func viewDidAppear(_ animated: Bool) {\n        super.viewDidAppear(animated)\n        NotificationCenter.default.post(Notification(name: .capacitorViewDidAppear))\n        if bridge?.config.hasInitialFocus ?? true {\n            self.webView?.becomeFirstResponder()\n        }\n    }\n\n    override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {\n        super.viewWillTransition(to: size, with: coordinator)\n        NotificationCenter.default.post(Notification(name: .capacitorViewWillTransition))\n    }\n\n    override open func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, withSender sender: Any) -> Bool {\n        return false\n    }\n\n    // MARK: - Initialization\n\n    /**\n     The InstanceDescriptor that should be used for the Capacitor environment.\n\n     - Returns: `InstanceDescriptor`\n\n     - Note: This is called early in the View Controller's lifecycle. Not all properties will be set at invocation.\n     */\n    open func instanceDescriptor() -> InstanceDescriptor {\n        let descriptor = InstanceDescriptor.init()\n        if !isNewBinary && !descriptor.cordovaDeployDisabled {\n            if let persistedPath = KeyValueStore.standard[\"serverBasePath\", as: String.self], !persistedPath.isEmpty {\n                if let libPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first {\n                    descriptor.appLocation = URL(fileURLWithPath: libPath, isDirectory: true)\n                        .appendingPathComponent(\"NoCloud\")\n                        .appendingPathComponent(\"ionic_built_snapshots\")\n                        .appendingPathComponent(URL(fileURLWithPath: persistedPath, isDirectory: true).lastPathComponent)\n                }\n            }\n        }\n        return descriptor\n    }\n\n    open func router() -> Router {\n        return CapacitorRouter()\n    }\n\n    /**\n     The WKWebViewConfiguration to use for the webview.\n\n     - Parameter instanceConfiguration: the configuration that will define the capacitor environment.\n\n     - Returns: `WKWebViewConfiguration`\n\n     It is recommended to call super's implementation and modify the result, rather than creating a new object.\n     */\n    open func webViewConfiguration(for instanceConfiguration: InstanceConfiguration) -> WKWebViewConfiguration {\n        let webViewConfiguration = WKWebViewConfiguration()\n        webViewConfiguration.websiteDataStore.httpCookieStore.add(CapacitorWKCookieObserver())\n        webViewConfiguration.allowsInlineMediaPlayback = true\n        webViewConfiguration.suppressesIncrementalRendering = false\n        webViewConfiguration.allowsAirPlayForMediaPlayback = true\n        webViewConfiguration.mediaTypesRequiringUserActionForPlayback = []\n        webViewConfiguration.limitsNavigationsToAppBoundDomains = instanceConfiguration.limitsNavigationsToAppBoundDomains\n        if #available(iOS 15.4, *) {\n            webViewConfiguration.preferences.isElementFullscreenEnabled = true\n        }\n        if let appendUserAgent = instanceConfiguration.appendedUserAgentString {\n            if let appName = webViewConfiguration.applicationNameForUserAgent {\n                webViewConfiguration.applicationNameForUserAgent = \"\\(appName) \\(appendUserAgent)\"\n            } else {\n                webViewConfiguration.applicationNameForUserAgent = appendUserAgent\n            }\n        }\n        if let preferredContentMode = instanceConfiguration.preferredContentMode {\n            var mode = WKWebpagePreferences.ContentMode.recommended\n            if preferredContentMode == \"mobile\" {\n                mode = WKWebpagePreferences.ContentMode.mobile\n            } else if preferredContentMode == \"desktop\" {\n                mode = WKWebpagePreferences.ContentMode.desktop\n            }\n            webViewConfiguration.defaultWebpagePreferences.preferredContentMode = mode\n        }\n        return webViewConfiguration\n    }\n\n    /**\n     Returns a WKWebView initialized with the frame and configuration.\n\n     Subclasses can override this method to return a subclass of WKWebView if needed.\n     */\n    open func webView(with frame: CGRect, configuration: WKWebViewConfiguration) -> WKWebView {\n        return WKWebView(frame: frame, configuration: configuration)\n    }\n\n    /**\n     Allows any additional configuration to be performed. The `webView` and `bridge` properties will be set by this point.\n\n     - Note: This is called before the webview has been added to the view hierarchy. Not all operations may be possible at\n     this time.\n     */\n    open func capacitorDidLoad() {\n    }\n\n    public final func loadWebView() {\n        guard let bridge = capacitorBridge else {\n            return\n        }\n\n        guard FileManager.default.fileExists(atPath: bridge.config.appStartFileURL.path) else {\n            fatalLoadError()\n        }\n\n        let url = bridge.config.appStartServerURL\n        CAPLog.print(\"⚡️  Loading app at \\(url.absoluteString)...\")\n        bridge.webViewDelegationHandler.willLoadWebview(webView)\n        _ = webView?.load(URLRequest(url: url))\n    }\n\n    // MARK: - System Integration\n\n    open func setStatusBarDefaults() {\n        if let plist = Bundle.main.infoDictionary {\n            if let statusBarHidden = plist[\"UIStatusBarHidden\"] as? Bool {\n                if statusBarHidden {\n                    self.isStatusBarVisible = false\n                }\n            }\n            if let statusBarStyle = plist[\"UIStatusBarStyle\"] as? String {\n                if statusBarStyle == \"UIStatusBarStyleDarkContent\" {\n                    self.statusBarStyle = .darkContent\n                } else if statusBarStyle != \"UIStatusBarStyleDefault\" {\n                    self.statusBarStyle = .lightContent\n                }\n            }\n        }\n    }\n\n    open func setScreenOrientationDefaults() {\n        if let plist = Bundle.main.infoDictionary {\n            if let orientations = plist[\"UISupportedInterfaceOrientations\"] as? [String] {\n                for orientation in orientations {\n                    if orientation == \"UIInterfaceOrientationPortrait\" {\n                        self.supportedOrientations.append(UIInterfaceOrientation.portrait.rawValue)\n                    }\n                    if orientation == \"UIInterfaceOrientationPortraitUpsideDown\" {\n                        self.supportedOrientations.append(UIInterfaceOrientation.portraitUpsideDown.rawValue)\n                    }\n                    if orientation == \"UIInterfaceOrientationLandscapeLeft\" {\n                        self.supportedOrientations.append(UIInterfaceOrientation.landscapeLeft.rawValue)\n                    }\n                    if orientation == \"UIInterfaceOrientationLandscapeRight\" {\n                        self.supportedOrientations.append(UIInterfaceOrientation.landscapeRight.rawValue)\n                    }\n                }\n                if self.supportedOrientations.count == 0 {\n                    self.supportedOrientations.append(UIInterfaceOrientation.portrait.rawValue)\n                }\n            }\n        }\n    }\n\n    override open var prefersStatusBarHidden: Bool {\n        return !isStatusBarVisible\n    }\n\n    override open var preferredStatusBarStyle: UIStatusBarStyle {\n        return statusBarStyle\n    }\n\n    override open var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {\n        return statusBarAnimation\n    }\n\n    open func setStatusBarVisible(_ isStatusBarVisible: Bool) {\n        self.isStatusBarVisible = isStatusBarVisible\n        UIView.animate(withDuration: 0.2, animations: {\n            self.setNeedsStatusBarAppearanceUpdate()\n        })\n    }\n\n    open func setStatusBarStyle(_ statusBarStyle: UIStatusBarStyle) {\n        self.statusBarStyle = statusBarStyle\n        UIView.animate(withDuration: 0.2, animations: {\n            self.setNeedsStatusBarAppearanceUpdate()\n        })\n    }\n\n    open func setStatusBarAnimation(_ statusBarAnimation: UIStatusBarAnimation) {\n        self.statusBarAnimation = statusBarAnimation\n    }\n\n    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {\n        var ret = 0\n        if self.supportedOrientations.contains(UIInterfaceOrientation.portrait.rawValue) {\n            ret = ret | (1 << UIInterfaceOrientation.portrait.rawValue)\n        }\n        if self.supportedOrientations.contains(UIInterfaceOrientation.portraitUpsideDown.rawValue) {\n            ret = ret | (1 << UIInterfaceOrientation.portraitUpsideDown.rawValue)\n        }\n        if self.supportedOrientations.contains(UIInterfaceOrientation.landscapeRight.rawValue) {\n            ret = ret | (1 << UIInterfaceOrientation.landscapeRight.rawValue)\n        }\n        if self.supportedOrientations.contains(UIInterfaceOrientation.landscapeLeft.rawValue) {\n            ret = ret | (1 << UIInterfaceOrientation.landscapeLeft.rawValue)\n        }\n        return UIInterfaceOrientationMask.init(rawValue: UInt(ret))\n    }\n}\n\n// MARK: - Application Path\n\nextension CAPBridgeViewController {\n    @objc public func getServerBasePath() -> String {\n        return bridge?.config.appLocation.path ?? \"\"\n    }\n\n    @objc public func setServerBasePath(path: String) {\n        guard let capBridge = capacitorBridge else { return }\n        capBridge.setServerBasePath(path)\n        DispatchQueue.main.async { [weak self] in\n            _ = self?.webView?.load(URLRequest(url: capBridge.config.serverURL))\n        }\n    }\n}\n\n// MARK: - Private\n\nextension CAPBridgeViewController {\n    private func prepareWebView(with configuration: InstanceConfiguration, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler) {\n        // set the cookie policy\n        HTTPCookieStorage.shared.cookieAcceptPolicy = HTTPCookie.AcceptPolicy.always\n        // setup the web view configuration\n        let webConfig = webViewConfiguration(for: configuration)\n        webConfig.setURLSchemeHandler(assetHandler, forURLScheme: configuration.localURL.scheme ?? InstanceDescriptorDefaults.scheme)\n        webConfig.userContentController = delegationHandler.contentController\n        // create the web view and set its properties\n        let aWebView = webView(with: .zero, configuration: webConfig)\n        aWebView.scrollView.bounces = false\n        aWebView.scrollView.contentInsetAdjustmentBehavior = configuration.contentInsetAdjustmentBehavior\n        aWebView.allowsLinkPreview = configuration.allowLinkPreviews\n        aWebView.scrollView.isScrollEnabled = configuration.scrollingEnabled\n        if let overrideUserAgent = configuration.overridenUserAgentString {\n            aWebView.customUserAgent = overrideUserAgent\n        }\n        if let backgroundColor = configuration.backgroundColor {\n            aWebView.backgroundColor = backgroundColor\n            aWebView.scrollView.backgroundColor = backgroundColor\n        } else {\n            // Use the system background colors if background is not set by user\n            aWebView.backgroundColor = UIColor.systemBackground\n            aWebView.scrollView.backgroundColor = UIColor.systemBackground\n        }\n        aWebView.capacitor.setKeyboardShouldRequireUserInteraction(false)\n        // set our ivar\n        webView = aWebView\n        // set our delegates\n        aWebView.uiDelegate = delegationHandler\n        aWebView.navigationDelegate = delegationHandler\n        if !configuration.zoomingEnabled {\n            aWebView.scrollView.delegate = delegationHandler\n        }\n    }\n\n    private func updateBinaryVersion() {\n        guard isNewBinary else {\n            return\n        }\n        guard let versionCode = Bundle.main.infoDictionary?[\"CFBundleVersion\"] as? String,\n              let versionName = Bundle.main.infoDictionary?[\"CFBundleShortVersionString\"] as? String else {\n            return\n        }\n        let store = KeyValueStore.standard\n        store[\"lastBinaryVersionCode\"] = versionCode\n        store[\"lastBinaryVersionName\"] = versionName\n        store[\"serverBasePath\"] = nil as String?\n    }\n\n    private func logWarnings(for descriptor: InstanceDescriptor) {\n        if descriptor.warnings.contains(.missingAppDir) {\n            CAPLog.print(\"⚡️  ERROR: Unable to find application directory at: \\\"\\(descriptor.appLocation.absoluteString)\\\"!\")\n        }\n        if descriptor.instanceType == .fixed {\n            if descriptor.warnings.contains(.missingFile) {\n                CAPLog.print(\"Unable to find capacitor.config.json, make sure it exists and run npx cap copy.\")\n            }\n            if descriptor.warnings.contains(.invalidFile) {\n                CAPLog.print(\"Unable to parse capacitor.config.json. Make sure it's valid JSON.\")\n            }\n            if descriptor.warnings.contains(.missingCordovaFile) {\n                CAPLog.print(\"Unable to find config.xml, make sure it exists and run npx cap copy.\")\n            }\n            if descriptor.warnings.contains(.invalidCordovaFile) {\n                CAPLog.print(\"Unable to parse config.xml. Make sure it's valid XML.\")\n            }\n        }\n    }\n\n    private func printLoadError() {\n        let fullStartPath = bridge?.config.appStartFileURL.path ?? \"\"\n\n        CAPLog.print(\"⚡️  ERROR: Unable to load \\(fullStartPath)\")\n        CAPLog.print(\"⚡️  This file is the root of your web app and must exist before\")\n        CAPLog.print(\"⚡️  Capacitor can run. Ensure you've run capacitor copy at least\")\n        CAPLog.print(\"⚡️  or, if embedding, that this directory exists as a resource directory.\")\n    }\n\n    private func fatalLoadError() -> Never {\n        printLoadError()\n        exit(1)\n    }\n}\n\nextension CAPBridgeViewController: CAPBridgeDelegate {\n    public var bridgedWebView: WKWebView? {\n        return webView\n    }\n\n    public var bridgedViewController: UIViewController? {\n        return self\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgedJSTypes.h",
    "content": "// Convenience methods for bridging to/from JavaScript types. Deliberately hidden from\n// Swift by omission (to avoid collisions with Swift protocols), use\n// `#import <Capacitor/CAPBridgedJSTypes.h>` if working in Objective-C.\n\n#import <Foundation/Foundation.h>\n#import <Capacitor/Capacitor-Swift.h>\n\n@protocol BridgedJSValueContainerImplementation <NSObject>\n@required\n- (NSString * _Nullable)getString:(NSString * _Nonnull)key defaultValue:(NSString * _Nullable)defaultValue;\n- (NSDate * _Nullable)getDate:(NSString * _Nonnull)key defaultValue:(NSDate * _Nullable)defaultValue;\n- (NSDictionary * _Nullable)getObject:(NSString * _Nonnull)key defaultValue:(NSDictionary * _Nullable)defaultValue;\n- (NSArray * _Nullable)getArray:(NSString * _Nonnull)key defaultValue:(NSArray * _Nullable)defaultValue;\n- (NSNumber * _Nullable)getNumber:(NSString * _Nonnull)key defaultValue:(NSNumber * _Nullable)defaultValue;\n- (BOOL)getBool:(NSString * _Nonnull)key defaultValue:(BOOL)defaultValue;\n@end\n\n@interface CAPPluginCall (BridgedJSProtocol) <BridgedJSValueContainerImplementation>\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgedJSTypes.m",
    "content": "#import <Foundation/Foundation.h>\n#import \"CAPBridgedJSTypes.h\"\n\n@implementation CAPPluginCall (BridgedJSProtocol)\n- (NSString * _Nullable)getString:(NSString * _Nonnull)key defaultValue:(NSString * _Nullable)defaultValue {\n    id value = [[self dictionaryRepresentation] objectForKey:key];\n    if (value != nil && [value isKindOfClass:[NSString class]]) {\n        return value;\n    }\n    return defaultValue;\n}\n\n- (NSDate * _Nullable)getDate:(NSString * _Nonnull)key defaultValue:(NSDate * _Nullable)defaultValue {\n    id value = [[self dictionaryRepresentation] objectForKey:key];\n    if (value != nil && [value isKindOfClass:[NSDate class]]) {\n        return value;\n    }\n    else if (value != nil && [value isKindOfClass:[NSString class]]) {\n        return [[[self class] jsDateFormatter] dateFromString:value];\n    }\n    return defaultValue;\n}\n\n- (NSDictionary * _Nullable)getObject:(NSString * _Nonnull)key defaultValue:(NSDictionary * _Nullable)defaultValue {\n    id value = [[self dictionaryRepresentation] objectForKey:key];\n    if (value != nil && [value isKindOfClass:[NSDictionary class]]) {\n        return value;\n    }\n    return defaultValue;\n}\n\n- (NSArray * _Nullable)getArray:(NSString * _Nonnull)key defaultValue:(NSArray * _Nullable)defaultValue; {\n    id value = [[self dictionaryRepresentation] objectForKey:key];\n    if (value != nil && [value isKindOfClass:[NSArray class]]) {\n        return value;\n    }\n    return defaultValue;\n}\n\n- (NSNumber * _Nullable)getNumber:(NSString * _Nonnull)key defaultValue:(NSNumber * _Nullable)defaultValue {\n    id value = [[self dictionaryRepresentation] objectForKey:key];\n    if (value != nil && [value isKindOfClass:[NSNumber class]]) {\n        return value;\n    }\n    return defaultValue;\n}\n\n- (BOOL)getBool:(NSString * _Nonnull)key defaultValue:(BOOL)defaultValue {\n    return [[self getNumber:key defaultValue:[NSNumber numberWithBool:defaultValue]] boolValue];\n}\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgedPlugin+getMethod.swift",
    "content": "//\n//  CAPBridgedPlugin+getMethod.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 3/1/23.\n//  Copyright © 2023 Drifty Co. All rights reserved.\n//\n\nextension CAPBridgedPlugin {\n    func getMethod(named name: String) -> CAPPluginMethod? {\n        pluginMethods.first { $0.name == name }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPBridgedPlugin.h",
    "content": "#import \"CAPPluginMethod.h\"\n\n#if defined(__cplusplus)\n#define CAP_EXTERN extern \"C\" __attribute__((visibility(\"default\")))\n#else\n#define CAP_EXTERN extern __attribute__((visibility(\"default\")))\n#endif\n\n#define CAPPluginReturnNone @\"none\"\n#define CAPPluginReturnCallback @\"callback\"\n#define CAPPluginReturnPromise @\"promise\"\n\n@class CAPPluginCall;\n@class CAPPlugin;\n\n@protocol CAPBridgedPlugin <NSObject>\n@property (nonnull, readonly) NSString *identifier;\n@property (nonnull, readonly) NSString *jsName;\n@property (nonnull, readonly) NSArray<CAPPluginMethod *> *pluginMethods;\n@end\n\n#define CAP_PLUGIN_CONFIG(plugin_id, js_name) \\\n- (NSString *)identifier { return @#plugin_id; } \\\n- (NSString *)jsName { return @js_name; }\n#define CAP_PLUGIN_METHOD(method_name, method_return_type) \\\n[methods addObject:[[CAPPluginMethod alloc] initWithName:@#method_name returnType:method_return_type]]\n\n#define CAP_PLUGIN(objc_name, js_name, methods_body) \\\n@interface objc_name : NSObject \\\n@end \\\n@interface objc_name (CAPPluginCategory) <CAPBridgedPlugin> \\\n@end \\\n@implementation objc_name (CAPPluginCategory) \\\n- (NSArray *)pluginMethods { \\\n  NSMutableArray *methods = [NSMutableArray new]; \\\n  methods_body \\\n  return methods; \\\n} \\\nCAP_PLUGIN_CONFIG(objc_name, js_name) \\\n@end\n\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPFile.swift",
    "content": "/**\n * CAPFileManager helps map file schemes to physical files, whether they are on\n * disk, in a bundle, or in another location.\n */\n@objc public class CAPFileManager: NSObject {\n    @available(*, deprecated, message: \"Use portablePath(fromLocalURL:) on the Bridge\")\n    public static func getPortablePath(host: String, uri: URL?) -> String? {\n        if let uri = uri {\n            let uriWithoutFile = uri.absoluteString.replacingOccurrences(of: \"file://\", with: \"\")\n            return host + CapacitorBridge.fileStartIdentifier + uriWithoutFile\n        }\n        return nil\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPInstanceConfiguration.h",
    "content": "#ifndef CAPInstanceConfiguration_h\n#define CAPInstanceConfiguration_h\n\n@import UIKit;\n\n@class CAPInstanceDescriptor;\n\nNS_SWIFT_NAME(InstanceConfiguration)\n@interface CAPInstanceConfiguration: NSObject\n@property (nonatomic, readonly, nullable) NSString *appendedUserAgentString;\n@property (nonatomic, readonly, nullable) NSString *overridenUserAgentString;\n@property (nonatomic, readonly, nullable) UIColor *backgroundColor;\n@property (nonatomic, readonly, nonnull) NSArray<NSString*> *allowedNavigationHostnames;\n@property (nonatomic, readonly, nonnull) NSURL *localURL;\n@property (nonatomic, readonly, nonnull) NSURL *serverURL;\n@property (nonatomic, readonly, nullable) NSString *errorPath;\n@property (nonatomic, readonly, nonnull) NSDictionary *pluginConfigurations;\n@property (nonatomic, readonly) BOOL loggingEnabled;\n@property (nonatomic, readonly) BOOL scrollingEnabled;\n@property (nonatomic, readonly) BOOL zoomingEnabled;\n@property (nonatomic, readonly) BOOL allowLinkPreviews;\n@property (nonatomic, readonly) BOOL handleApplicationNotifications;\n@property (nonatomic, readonly) BOOL isWebDebuggable;\n@property (nonatomic, readonly) BOOL hasInitialFocus;\n@property (nonatomic, readonly) BOOL cordovaDeployDisabled;\n@property (nonatomic, readonly) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;\n@property (nonatomic, readonly, nonnull) NSURL *appLocation;\n@property (nonatomic, readonly, nullable) NSString *appStartPath;\n@property (nonatomic, readonly) BOOL limitsNavigationsToAppBoundDomains;\n@property (nonatomic, readonly, nullable) NSString *preferredContentMode;\n\n@property (nonatomic, readonly, nonnull) NSDictionary *legacyConfig DEPRECATED_MSG_ATTRIBUTE(\"Use direct properties instead\");\n\n- (instancetype _Nonnull)initWithDescriptor:(CAPInstanceDescriptor* _Nonnull)descriptor isDebug:(BOOL)debug NS_SWIFT_NAME(init(with:isDebug:));\n- (instancetype _Nonnull)updatingAppLocation:(NSURL* _Nonnull)location NS_SWIFT_NAME(updatingAppLocation(_:));\n@end\n\n#endif /* CAPInstanceConfiguration_h */\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPInstanceConfiguration.m",
    "content": "#import \"CAPInstanceConfiguration.h\"\n#import <Capacitor/Capacitor-Swift.h>\n\n@interface CAPInstanceConfiguration (Internal)\n- (instancetype)initWithConfiguration:(CAPInstanceConfiguration*)configuration andLocation:(NSURL*)location;\n@end\n\n\n@implementation CAPInstanceConfiguration\n\n- (instancetype)initWithDescriptor:(CAPInstanceDescriptor *)descriptor isDebug:(BOOL)debug {\n    if (self = [super init]) {\n        // first, give the descriptor a chance to make itself internally consistent\n        [descriptor normalize];\n        // now copy the simple properties\n        _appendedUserAgentString = descriptor.appendedUserAgentString;\n        _overridenUserAgentString = descriptor.overridenUserAgentString;\n        _backgroundColor = descriptor.backgroundColor;\n        _allowedNavigationHostnames = descriptor.allowedNavigationHostnames;\n        switch (descriptor.loggingBehavior) {\n            case CAPInstanceLoggingBehaviorProduction:\n                _loggingEnabled = true;\n                break;\n            case CAPInstanceLoggingBehaviorDebug:\n                _loggingEnabled = debug;\n                break;\n            default:\n                _loggingEnabled = false;\n                break;\n        }\n        _scrollingEnabled = descriptor.scrollingEnabled;\n        _zoomingEnabled =  descriptor.zoomingEnabled;\n        _allowLinkPreviews = descriptor.allowLinkPreviews;\n        _handleApplicationNotifications = descriptor.handleApplicationNotifications;\n        _contentInsetAdjustmentBehavior = descriptor.contentInsetAdjustmentBehavior;\n        _appLocation = descriptor.appLocation;\n        _appStartPath = descriptor.appStartPath;\n        _limitsNavigationsToAppBoundDomains = descriptor.limitsNavigationsToAppBoundDomains;\n        _preferredContentMode = descriptor.preferredContentMode;\n        _pluginConfigurations = descriptor.pluginConfigurations;\n        _isWebDebuggable = descriptor.isWebDebuggable;\n        _hasInitialFocus = descriptor.hasInitialFocus;\n        _legacyConfig = descriptor.legacyConfig;\n        // construct the necessary URLs\n        _localURL = [[NSURL alloc] initWithString:[NSString stringWithFormat:@\"%@://%@\", descriptor.urlScheme, descriptor.urlHostname]];\n        if (descriptor.serverURL != nil) {\n            _serverURL = [[NSURL alloc] initWithString:(descriptor.serverURL)];\n        }\n        else {\n            _serverURL = _localURL;\n        }\n        _errorPath = descriptor.errorPath;\n        // extract the one value we care about from the cordova configuration\n        _cordovaDeployDisabled = [descriptor cordovaDeployDisabled];\n    }\n    return self;\n}\n\n- (instancetype)initWithConfiguration:(CAPInstanceConfiguration*)configuration andLocation:(NSURL*)location {\n    if (self = [super init]) {\n        _appendedUserAgentString = [[configuration appendedUserAgentString] copy];\n        _overridenUserAgentString = [[configuration overridenUserAgentString] copy];\n        _backgroundColor = configuration.backgroundColor;\n        _allowedNavigationHostnames = [[configuration allowedNavigationHostnames] copy];\n        _localURL = [[configuration localURL] copy];\n        _serverURL = [[configuration serverURL] copy];\n        _errorPath = [[configuration errorPath] copy];\n        _pluginConfigurations = [[configuration pluginConfigurations] copy];\n        _loggingEnabled = configuration.loggingEnabled;\n        _scrollingEnabled = configuration.scrollingEnabled;\n        _zoomingEnabled = configuration.zoomingEnabled;\n        _allowLinkPreviews = configuration.allowLinkPreviews;\n        _handleApplicationNotifications = configuration.handleApplicationNotifications;\n        _isWebDebuggable = configuration.isWebDebuggable;\n        _hasInitialFocus = configuration.hasInitialFocus;\n        _cordovaDeployDisabled = configuration.cordovaDeployDisabled;\n        _contentInsetAdjustmentBehavior = configuration.contentInsetAdjustmentBehavior;\n        // we don't care about internal usage of deprecated APIs and the framework should build cleanly\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n        _legacyConfig = [[configuration legacyConfig] copy];\n#pragma clang diagnostic pop\n        _appStartPath = configuration.appStartPath;\n        _appLocation = [location copy];\n    }\n    return self;\n}\n\n- (instancetype)updatingAppLocation:(NSURL*)location {\n    return [[CAPInstanceConfiguration alloc] initWithConfiguration:self andLocation:location];\n}\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift",
    "content": "import Foundation\n\nextension InstanceConfiguration {\n    @objc public var appStartFileURL: URL {\n        if let path = appStartPath {\n            return appLocation.appendingPathComponent(path)\n        }\n        return appLocation\n    }\n\n    @objc public var appStartServerURL: URL {\n        if let path = appStartPath {\n            return serverURL.appendingPathComponent(path)\n        }\n        return serverURL\n    }\n\n    @objc public var errorPathURL: URL? {\n        guard let errorPath = errorPath else {\n            return nil\n        }\n\n        return localURL.appendingPathComponent(errorPath)\n    }\n\n    @available(*, deprecated, message: \"Use getPluginConfig\")\n    @objc public func getPluginConfigValue(_ pluginId: String, _ configKey: String) -> Any? {\n        return (pluginConfigurations as? JSObject)?[keyPath: KeyPath(\"\\(pluginId).\\(configKey)\")]\n    }\n\n    @objc public func getPluginConfig(_ pluginId: String) -> PluginConfig {\n        if let cfg = (pluginConfigurations as? JSObject)?[keyPath: KeyPath(\"\\(pluginId)\")] as? JSObject {\n            return PluginConfig(config: cfg)\n        }\n        return PluginConfig(config: JSObject())\n    }\n\n    @objc public func shouldAllowNavigation(to host: String) -> Bool {\n        for hostname in allowedNavigationHostnames {\n            if doesHost(host, match: hostname) {\n                return true\n            }\n        }\n        return false\n    }\n\n    @available(*, deprecated, message: \"Use direct property accessors\")\n    @objc public func getValue(_ key: String) -> Any? {\n        return (legacyConfig as? JSObject)?[keyPath: KeyPath(key)]\n    }\n\n    @available(*, deprecated, message: \"Use direct property accessors\")\n    @objc public func getString(_ key: String) -> String? {\n        return (legacyConfig as? JSObject)?[keyPath: KeyPath(key)] as? String\n    }\n\n    // MARK: - Private\n\n    private func doesHost(_ host: String, match pattern: String) -> Bool {\n        // bail early in the simple case\n        if pattern == \"*\" {\n            return true\n        }\n        // break apart the pieces\n        var hostComponents = host.lowercased().split(separator: \".\")\n        var patternComponents = pattern.lowercased().split(separator: \".\")\n        guard hostComponents.count == patternComponents.count else {\n            return false\n        }\n        // remove any wildcard segments\n        for wildcard in patternComponents.enumerated().reversed().filter({ $0.element == \"*\" }) {\n            hostComponents.remove(at: wildcard.offset)\n            patternComponents.remove(at: wildcard.offset)\n        }\n        // match with what's left\n        return hostComponents == patternComponents\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPInstanceDescriptor.h",
    "content": "#ifndef CAPInstanceDescriptor_h\n#define CAPInstanceDescriptor_h\n\n@import UIKit;\n@import Cordova;\n\ntypedef NS_ENUM(NSInteger, CAPInstanceType) {\n    CAPInstanceTypeFixed NS_SWIFT_NAME(fixed),\n    CAPInstanceTypeVariable NS_SWIFT_NAME(variable)\n} NS_SWIFT_NAME(InstanceType);\n\ntypedef NS_OPTIONS(NSUInteger, CAPInstanceWarning) {\n    CAPInstanceWarningMissingAppDir       NS_SWIFT_NAME(missingAppDir)      = 1 << 0,\n    CAPInstanceWarningMissingFile         NS_SWIFT_NAME(missingFile)        = 1 << 1,\n    CAPInstanceWarningInvalidFile         NS_SWIFT_NAME(invalidFile)        = 1 << 2,\n    CAPInstanceWarningMissingCordovaFile  NS_SWIFT_NAME(missingCordovaFile) = 1 << 3,\n    CAPInstanceWarningInvalidCordovaFile  NS_SWIFT_NAME(invalidCordovaFile) = 1 << 4\n} NS_SWIFT_NAME(InstanceWarning);\n\ntypedef NS_OPTIONS(NSUInteger, CAPInstanceLoggingBehavior) {\n    CAPInstanceLoggingBehaviorNone          NS_SWIFT_NAME(none)         = 1 << 0,\n    CAPInstanceLoggingBehaviorDebug         NS_SWIFT_NAME(debug)        = 1 << 1,\n    CAPInstanceLoggingBehaviorProduction    NS_SWIFT_NAME(production)   = 1 << 2,\n} NS_SWIFT_NAME(InstanceLoggingBehavior);\n\nextern NSString * _Nonnull const CAPInstanceDescriptorDefaultScheme NS_SWIFT_UNAVAILABLE(\"Use InstanceDescriptorDefaults\");\nextern NSString * _Nonnull const CAPInstanceDescriptorDefaultHostname NS_SWIFT_UNAVAILABLE(\"Use InstanceDescriptorDefaults\");\n\nNS_SWIFT_NAME(InstanceDescriptor)\n@interface CAPInstanceDescriptor : NSObject\n/**\n @brief A value to append to the @c User-Agent string. Ignored if @c overridenUserAgentString is set.\n @discussion Set by @c appendUserAgent in the configuration file.\n */\n@property (nonatomic, copy, nullable) NSString *appendedUserAgentString;\n/**\n @brief A value that will completely replace the @c User-Agent string. Overrides @c appendedUserAgentString.\n @discussion Set by @c overrideUserAgent in the configuration file.\n */\n@property (nonatomic, copy, nullable) NSString *overridenUserAgentString;\n/**\n @brief The background color to set on the web view where content is not visible.\n @discussion Set by @c backgroundColor in the configuration file.\n */\n@property (nonatomic, retain, nullable) UIColor *backgroundColor;\n/**\n @brief Hostnames to which the web view is allowed to navigate without opening an external browser.\n @discussion Set by @c allowNavigation in the configuration file.\n */\n@property (nonatomic, copy, nonnull) NSArray<NSString*> *allowedNavigationHostnames;\n/**\n @brief The scheme that will be used for the server URL.\n @discussion Defaults to @c capacitor. Set by @c server.iosScheme in the configuration file.\n */\n@property (nonatomic, copy, nullable) NSString *urlScheme;\n/**\n @brief The path to a local html page to display in case of errors.\n @discussion Defaults to nil.\n */\n@property (nonatomic, copy, nullable) NSString *errorPath;\n/**\n @brief The hostname that will be used for the server URL.\n @discussion Defaults to @c localhost. Set by @c server.hostname in the configuration file.\n */\n@property (nonatomic, copy, nullable) NSString *urlHostname;\n/**\n @brief The fully formed URL that will be used as the server URL.\n @discussion Defaults to nil, in which case the server URL will be constructed from @c urlScheme and @c urlHostname. If set, it will override the other properties. Set by @c server.url in the configuration file.\n */\n@property (nonatomic, copy, nullable) NSString *serverURL;\n/**\n @brief The JSON dictionary that contains the plugin-specific configuration information.\n @discussion Set by @c plugins in the configuration file.\n */\n@property (nonatomic, retain, nonnull) NSDictionary *pluginConfigurations;\n/**\n @brief The build configurations under which logging should be enabled.\n @discussion Defaults to @c debug. Set by @c loggingBehavior in the configuration file.\n */\n@property (nonatomic, assign) CAPInstanceLoggingBehavior loggingBehavior;\n/**\n @brief Whether or not the web view can scroll.\n @discussion Set by @c ios.scrollEnabled in the configuration file. Corresponds to @c isScrollEnabled on WKWebView.\n */\n@property (nonatomic, assign) BOOL scrollingEnabled;\n/**\n @brief Whether or not the web view can zoom.\n @discussion Set by @c zoomEnabled in the configuration file.\n */\n@property (nonatomic, assign) BOOL zoomingEnabled;\n/**\n @brief Whether or not the web view will preview links.\n @discussion Set by @c ios.allowsLinkPreview in the configuration file. Corresponds to @c allowsLinkPreview on WKWebView.\n */\n@property (nonatomic, assign) BOOL allowLinkPreviews;\n/**\n @brief Whether or not the Capacitor runtime will set itself as the @c UNUserNotificationCenter delegate.\n @discussion Defaults to @c true. Required to be @c true for notification plugins to work correctly. Set to @c false if your application will handle notifications independently.\n */\n@property (nonatomic, assign) BOOL handleApplicationNotifications;\n/**\n @brief Enables web debugging by setting isInspectable of  @c WKWebView to @c true on iOS 16.4 and greater\n @discussion Defaults to true in debug mode and false in production\n */\n@property (nonatomic, assign) BOOL isWebDebuggable;\n/**\n @brief Whether or not the webview will have focus.\n @discussion Defaults to @c true.  Set by @c ios.initialFocus in the configuration file.\n */\n@property (nonatomic, assign) BOOL hasInitialFocus;\n\n/**\n @brief How the web view will inset its content\n @discussion Set by @c ios.contentInset in the configuration file. Corresponds to @c contentInsetAdjustmentBehavior on WKWebView.\n */\n@property (nonatomic, assign) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;\n/**\n @brief The base file URL from which Capacitor will load resources\n @discussion Defaults to @c public/ located at the root of the application bundle.\n */\n@property (nonatomic, copy, nonnull) NSURL *appLocation;\n/**\n @brief The path (relative to @c appLocation) which Capacitor will use for the inital URL at launch.\n @discussion Defaults to nil, in which case Capacitor will attempt to load @c index.html.\n */\n@property (nonatomic, copy, nullable) NSString *appStartPath;\n/**\n @brief Whether or not the Capacitor WebView will limit the navigation to @c WKAppBoundDomains listed in the Info.plist.\n @discussion Defaults to @c false. Set by @c ios.limitsNavigationsToAppBoundDomains in the configuration file.  Required to be @c true for plugins to work if the app includes @c WKAppBoundDomains in the Info.plist.\n */\n@property (nonatomic, assign) BOOL limitsNavigationsToAppBoundDomains;\n/**\n @brief The content mode for the web view to use when it loads and renders web content.\n @discussion Defaults to  @c recommended. Set by @c ios.preferredContentMode in the configuration file.\n */\n@property (nonatomic, copy, nullable) NSString *preferredContentMode;\n/**\n @brief The parser used to load the cofiguration for Cordova plugins.\n */\n@property (nonatomic, copy, nonnull) CDVConfigParser *cordovaConfiguration;\n/**\n @brief Warnings generated during initialization.\n */\n@property (nonatomic, assign) CAPInstanceWarning warnings;\n/**\n @brief The type of instance.\n */\n@property (nonatomic, readonly) CAPInstanceType instanceType;\n/**\n @brief The JSON dictionary representing the contents of the configuration file.\n @warning Deprecated. Do not use.\n */\n@property (nonatomic, retain, nonnull) NSDictionary *legacyConfig;\n/**\n @brief Initialize the descriptor with the default environment. This assumes that the application was built with the help of the Capacitor CLI and that that the web app is located inside the application bundle at @c public/.\n */\n- (instancetype _Nonnull)initAsDefault NS_SWIFT_NAME(init());\n/**\n @brief Initialize the descriptor for use in other contexts. The app location is the one required parameter.\n @param appURL The location of the folder containing the web app.\n @param configURL The location of the Capacitor configuration file.\n @param cordovaURL The location of the Cordova configuration file.\n */\n- (instancetype _Nonnull)initAtLocation:(NSURL* _Nonnull)appURL configuration:(NSURL* _Nullable)configURL cordovaConfiguration:(NSURL* _Nullable)cordovaURL NS_SWIFT_NAME(init(at:configuration:cordovaConfiguration:));\n@end\n\n#endif /* CAPInstanceDescriptor_h */\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPInstanceDescriptor.m",
    "content": "#import \"CAPInstanceDescriptor.h\"\n#import <Capacitor/Capacitor-Swift.h>\n\n// Swift extensions marked as @objc and internal are available to the Obj-C runtime but are not available at compile time.\n// so we need this declaration to avoid compiler complaints\n@interface CAPInstanceDescriptor (InternalSwiftExtension)\n- (void)_parseConfigurationAt:(NSURL *)configURL cordovaConfiguration:(NSURL *)cordovaURL;\n@end\n\nNSString* const CAPInstanceDescriptorDefaultScheme = @\"capacitor\";\nNSString* const CAPInstanceDescriptorDefaultHostname = @\"localhost\";\n\n@implementation CAPInstanceDescriptor\n- (instancetype)initAsDefault {\n    if (self = [super init]) {\n        _instanceType = CAPInstanceTypeFixed;\n        [self _setDefaultsWithAppLocation:[[NSBundle mainBundle] URLForResource:@\"public\" withExtension:nil]];\n        [self _parseConfigurationAt:[[NSBundle mainBundle] URLForResource:@\"capacitor.config\" withExtension:@\"json\"] cordovaConfiguration:[[NSBundle mainBundle] URLForResource:@\"config\" withExtension:@\"xml\"]];\n    }\n    return self;\n}\n\n- (instancetype)initAtLocation:(NSURL*)appURL configuration:(NSURL*)configURL cordovaConfiguration:(NSURL*)cordovaURL {\n    if (self = [super init]) {\n        _instanceType = CAPInstanceTypeVariable;\n        [self _setDefaultsWithAppLocation:appURL];\n        [self _parseConfigurationAt:configURL cordovaConfiguration:cordovaURL];\n    }\n    return self;\n}\n\n- (void)_setDefaultsWithAppLocation:(NSURL*)location {\n    _allowedNavigationHostnames = @[];\n    _urlScheme = CAPInstanceDescriptorDefaultScheme;\n    _urlHostname = CAPInstanceDescriptorDefaultHostname;\n    _pluginConfigurations = @{};\n    _legacyConfig = @{};\n    _loggingBehavior = CAPInstanceLoggingBehaviorDebug;\n    _scrollingEnabled = YES;\n    _zoomingEnabled = NO;\n    _allowLinkPreviews = YES;\n    _handleApplicationNotifications = YES;\n    _isWebDebuggable = NO;\n    _hasInitialFocus = YES;\n    _contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;\n    _appLocation = location;\n    _limitsNavigationsToAppBoundDomains = FALSE;\n    _cordovaConfiguration = [[CDVConfigParser alloc] init];\n    _warnings = 0;\n    if (location == nil) {\n        _warnings |= CAPInstanceWarningMissingAppDir;\n        // location is nil so assume it was supposed to be the default\n        _appLocation = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:@\"public\"];\n    }\n}\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift",
    "content": "import Foundation\n\npublic enum InstanceDescriptorDefaults {\n    public static let scheme = \"capacitor\"\n    public static let hostname = \"localhost\"\n}\n\nprivate extension InstanceLoggingBehavior {\n    static func behavior(from: String) -> InstanceLoggingBehavior? {\n        switch from.lowercased() {\n        case \"none\":\n            return InstanceLoggingBehavior.none\n        case \"debug\":\n            return InstanceLoggingBehavior.debug\n        case \"production\":\n            return InstanceLoggingBehavior.production\n        default:\n            return nil\n        }\n    }\n}\n\n/**\n The purpose of this function is to hide the messy details of parsing the configuration(s) so\n the complexity is worth it. And the name starts with an underscore to match the convention of\n private APIs in Obj-C (from which it is called).\n */\ninternal extension InstanceDescriptor {\n    // swiftlint:disable cyclomatic_complexity\n    // swiftlint:disable function_body_length\n    // swiftlint:disable:next identifier_name\n    @objc func _parseConfiguration(at capacitorURL: URL?, cordovaConfiguration cordovaURL: URL?) {\n        // sanity check that the app directory is valid\n        var isDirectory: ObjCBool = ObjCBool(false)\n        if warnings.contains(.missingAppDir) == false,\n           FileManager.default.fileExists(atPath: appLocation.path, isDirectory: &isDirectory) == false || isDirectory.boolValue == false {\n            warnings.update(with: .missingAppDir)\n        }\n\n        // parse the capacitor configuration\n        var config: JSObject?\n        if let capacitorURL = capacitorURL,\n           FileManager.default.fileExists(atPath: capacitorURL.path, isDirectory: &isDirectory),\n           isDirectory.boolValue == false {\n            do {\n                let contents = try Data(contentsOf: capacitorURL)\n                config = JSTypes.coerceDictionaryToJSObject(try JSONSerialization.jsonObject(with: contents) as? [String: Any])\n            } catch {\n                warnings.update(with: .invalidFile)\n            }\n        } else {\n            warnings.update(with: .missingFile)\n        }\n\n        // parse the cordova configuration\n        var configParser: XMLParser?\n        if let cordovaURL = cordovaURL,\n           FileManager.default.fileExists(atPath: cordovaURL.path, isDirectory: &isDirectory),\n           isDirectory.boolValue == false {\n            configParser = XMLParser(contentsOf: cordovaURL)\n        } else {\n            warnings.update(with: .missingCordovaFile)\n            // we don't want to break up string literals\n            // swiftlint:disable:next line_length\n            if let cordovaXML = \"<?xml version='1.0' encoding='utf-8'?><widget version=\\\"1.0.0\\\" xmlns=\\\"http://www.w3.org/ns/widgets\\\" xmlns:cdv=\\\"http://cordova.apache.org/ns/1.0\\\"><access origin=\\\"*\\\" /></widget>\".data(using: .utf8) {\n                configParser = XMLParser(data: cordovaXML)\n            }\n        }\n        configParser?.delegate = cordovaConfiguration\n        configParser?.parse()\n\n        // extract our configuration values\n        if let config = config {\n            // to be removed\n            legacyConfig = config\n\n            if let agentString = (config[keyPath: \"ios.appendUserAgent\"] as? String) ?? (config[keyPath: \"appendUserAgent\"] as? String) {\n                appendedUserAgentString = agentString\n            }\n            if let agentString = (config[keyPath: \"ios.overrideUserAgent\"] as? String) ?? (config[keyPath: \"overrideUserAgent\"] as? String) {\n                overridenUserAgentString = agentString\n            }\n            if let colorString = (config[keyPath: \"ios.backgroundColor\"] as? String) ?? (config[keyPath: \"backgroundColor\"] as? String),\n               let color = UIColor.capacitor.color(fromHex: colorString) {\n                backgroundColor = color\n            }\n            if let allowNav = config[keyPath: \"server.allowNavigation\"] as? [String] {\n                allowedNavigationHostnames = allowNav\n            }\n            if let scheme = (config[keyPath: \"server.iosScheme\"] as? String)?.lowercased() {\n                urlScheme = scheme\n            }\n            if let host = config[keyPath: \"server.hostname\"] as? String {\n                urlHostname = host\n            }\n            if let urlString = config[keyPath: \"server.url\"] as? String {\n                serverURL = urlString\n            }\n            if let errorPathString = (config[keyPath: \"server.errorPath\"] as? String) {\n                errorPath = errorPathString\n            }\n            if let insetBehavior = config[keyPath: \"ios.contentInset\"] as? String {\n                let availableInsets: [String: UIScrollView.ContentInsetAdjustmentBehavior] = [\"automatic\": .automatic,\n                                                                                              \"scrollableAxes\": .scrollableAxes,\n                                                                                              \"never\": .never,\n                                                                                              \"always\": .always]\n                if let option = availableInsets[insetBehavior] {\n                    contentInsetAdjustmentBehavior = option\n                }\n            }\n            if let allowPreviews = config[keyPath: \"ios.allowsLinkPreview\"] as? Bool {\n                allowLinkPreviews = allowPreviews\n            }\n            if let scrollEnabled = config[keyPath: \"ios.scrollEnabled\"] as? Bool {\n                scrollingEnabled = scrollEnabled\n            }\n            if let zoomEnabled = (config[keyPath: \"ios.zoomEnabled\"] as? Bool) ?? (config[keyPath: \"zoomEnabled\"] as? Bool) {\n                zoomingEnabled = zoomEnabled\n            }\n            if let pluginConfig = config[keyPath: \"plugins\"] as? JSObject {\n                pluginConfigurations = pluginConfig\n            }\n            if let value = (config[keyPath: \"ios.loggingBehavior\"] as? String) ?? (config[keyPath: \"loggingBehavior\"] as? String) {\n                if let behavior = InstanceLoggingBehavior.behavior(from: value) {\n                    loggingBehavior = behavior\n                }\n            }\n            if let limitsNavigations = config[keyPath: \"ios.limitsNavigationsToAppBoundDomains\"] as? Bool {\n                limitsNavigationsToAppBoundDomains = limitsNavigations\n            }\n            if let preferredMode = (config[keyPath: \"ios.preferredContentMode\"] as? String) {\n                preferredContentMode = preferredMode\n            }\n            if let handleNotifications = config[keyPath: \"ios.handleApplicationNotifications\"] as? Bool {\n                handleApplicationNotifications = handleNotifications\n            }\n            if let webContentsDebuggingEnabled = config[keyPath: \"ios.webContentsDebuggingEnabled\"] as? Bool {\n                isWebDebuggable = webContentsDebuggingEnabled\n            } else {\n                #if DEBUG\n                isWebDebuggable = true\n                #else\n                // this is needed for SPM xcframework Capacitor.  Can eventually be removed when the SPM package moves to being source-based.\n                if let debugValue = Bundle.main.object(forInfoDictionaryKey: \"CAPACITOR_DEBUG\") as? String, debugValue == \"true\" {\n                    isWebDebuggable = true\n                }\n                #endif\n            }\n            if let initialFocus = (config[keyPath: \"ios.initialFocus\"] as? Bool) ?? (config[keyPath: \"initialFocus\"] as? Bool) {\n                hasInitialFocus = initialFocus\n            }\n            if let startPath = (config[keyPath: \"server.appStartPath\"] as? String) {\n                appStartPath = startPath\n            }\n        }\n    }\n    // swiftlint:enable cyclomatic_complexity\n    // swiftlint:enable function_body_length\n}\n\nextension InstanceDescriptor {\n    @objc public var cordovaDeployDisabled: Bool {\n        return (cordovaConfiguration.settings?[\"DisableDeploy\".lowercased()] as? NSString)?.boolValue ?? false\n    }\n\n    @objc public func normalize() {\n        // first, make sure the scheme is valid\n        var schemeValid = false\n        if let scheme = urlScheme, WKWebView.handlesURLScheme(scheme) == false,\n           scheme.range(of: \"^[a-z][a-z0-9.+-]*$\", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil {\n            schemeValid = true\n        }\n        if !schemeValid {\n            // reset to the default\n            urlScheme = InstanceDescriptorDefaults.scheme\n        }\n        // make sure we have a hostname\n        if urlHostname == nil {\n            urlHostname = InstanceDescriptorDefaults.hostname\n        }\n        // now validate the server.url\n        var urlValid = false\n        if let server = serverURL, URL(string: server) != nil {\n            urlValid = true\n        }\n        if !urlValid {\n            serverURL = nil\n        }\n        // reset the path if it's not valid\n        if let path = appStartPath?.trimmingCharacters(in: .whitespacesAndNewlines), path.isEmpty {\n            appStartPath = nil\n        }\n        // if the plugin configuration was programmatically modified, the necessary type information may have been lost.\n        // so perform a coercion here to make sure that casting will work as expected\n        pluginConfigurations = JSTypes.coerceDictionaryToJSObject(pluginConfigurations) ?? [:]\n        legacyConfig = JSTypes.coerceDictionaryToJSObject(legacyConfig) ?? [:]\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPInstancePlugin.swift",
    "content": "//\n//  CAPInstancePlugin.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 11/21/22.\n//  Copyright © 2022 Drifty Co. All rights reserved.\n//\n\n/// A CAPPlugin subclass meant to be explicitly initialized by the caller and not the bridge.\n@objc open class CAPInstancePlugin: CAPPlugin {}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPLog.swift",
    "content": "public class CAPLog {\n    public static var enableLogging: Bool = true\n\n    public static func print(_ items: Any..., separator: String = \" \", terminator: String = \"\\n\") {\n        if enableLogging {\n            for (itemIndex, item) in items.enumerated() {\n                Swift.print(\"\\(item)\".prefix(4068), terminator: itemIndex == items.count - 1 ? terminator : separator)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPNotifications.swift",
    "content": "/**\n Notificaton types for NotificationCenter and NSNotificationCenter\n\n We want to include `capacitor` in the name(s) to uniquely identify these even though it can make the names long\n and the deprecated notifications are only here for backwards compatibility.\n */\n// swiftlint:disable identifier_name\nextension Notification.Name {\n    public static let capacitorOpenURL = Notification.Name(rawValue: \"CapacitorOpenURLNotification\")\n    public static let capacitorOpenUniversalLink = Notification.Name(rawValue: \"CapacitorOpenUniversalLinkNotification\")\n    public static let capacitorContinueActivity = Notification.Name(rawValue: \"CapacitorContinueActivityNotification\")\n    public static let capacitorDidRegisterForRemoteNotifications =\n        Notification.Name(rawValue: \"CapacitorDidRegisterForRemoteNotificationsNotification\")\n    public static let capacitorDidFailToRegisterForRemoteNotifications =\n        Notification.Name(rawValue: \"CapacitorDidFailToRegisterForRemoteNotificationsNotification\")\n    public static let capacitorDecidePolicyForNavigationAction =\n        Notification.Name(rawValue: \"CapacitorDecidePolicyForNavigationActionNotification\")\n    public static let capacitorStatusBarTapped = Notification.Name(rawValue: \"CapacitorStatusBarTappedNotification\")\n    public static let capacitorViewDidAppear = Notification.Name(rawValue: \"CapacitorViewDidAppear\")\n    public static let capacitorViewWillTransition = Notification.Name(rawValue: \"CapacitorViewWillTransition\")\n}\n\n@objc extension NSNotification {\n    public static let capacitorOpenURL = Notification.Name.capacitorOpenURL\n    public static let capacitorOpenUniversalLink = Notification.Name.capacitorOpenUniversalLink\n    public static let capacitorContinueActivity = Notification.Name.capacitorContinueActivity\n    public static let capacitorDidRegisterForRemoteNotifications = Notification.Name.capacitorDidRegisterForRemoteNotifications\n    public static let capacitorDidFailToRegisterForRemoteNotifications = Notification.Name.capacitorDidFailToRegisterForRemoteNotifications\n    public static let capacitorDecidePolicyForNavigationAction = Notification.Name.capacitorDecidePolicyForNavigationAction\n    public static let capacitorStatusBarTapped = Notification.Name.capacitorStatusBarTapped\n    public static let capacitorViewDidAppear = Notification.Name.capacitorViewDidAppear\n    public static let capacitorViewWillTransition = Notification.Name.capacitorViewWillTransition\n}\n\n/**\n Deprecated, will be removed\n */\n@objc public enum CAPNotifications: Int {\n    @available(*, deprecated, message: \"renamed to 'Notification.Name.capacitorOpenURL'\")\n    case URLOpen\n    @available(*, deprecated, message: \"renamed to 'Notification.Name.capacitorOpenUniversalLink'\")\n    case UniversalLinkOpen\n    @available(*, deprecated, message: \"Notification.Name.capacitorContinueActivity'\")\n    case ContinueActivity\n    @available(*, deprecated, message: \"renamed to 'Notification.Name.capacitorDidRegisterForRemoteNotifications'\")\n    case DidRegisterForRemoteNotificationsWithDeviceToken\n    @available(*, deprecated, message: \"renamed to 'Notification.Name.capacitorDidFailToRegisterForRemoteNotifications'\")\n    case DidFailToRegisterForRemoteNotificationsWithError\n    @available(*, deprecated, message: \"renamed to 'Notification.Name.capacitorDecidePolicyForNavigationAction'\")\n    case DecidePolicyForNavigationAction\n\n    public func name() -> String {\n        switch self {\n        case .URLOpen:\n            return Notification.Name.capacitorOpenURL.rawValue\n        case .UniversalLinkOpen:\n            return Notification.Name.capacitorOpenUniversalLink.rawValue\n        case .ContinueActivity:\n            return Notification.Name.capacitorContinueActivity.rawValue\n        case .DidRegisterForRemoteNotificationsWithDeviceToken:\n            return Notification.Name.capacitorDidRegisterForRemoteNotifications.rawValue\n        case .DidFailToRegisterForRemoteNotificationsWithError:\n            return Notification.Name.capacitorDidFailToRegisterForRemoteNotifications.rawValue\n        case .DecidePolicyForNavigationAction:\n            return Notification.Name.capacitorDecidePolicyForNavigationAction.rawValue\n        }\n    }\n}\n// swiftlint:enable identifier_name\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPlugin+LoadInstance.swift",
    "content": "//\n//  CAPPlugin+LoadInstance.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 11/9/22.\n//  Copyright © 2022 Drifty Co. All rights reserved.\n//\n\nextension CAPBridgedPlugin where Self: CAPPlugin {\n    func load(on bridge: CAPBridgeProtocol) {\n        self.bridge = bridge\n        webView = bridge.webView\n        shouldStringifyDatesInCalls = true\n        retainedEventArguments = [:]\n        eventListeners = [:]\n        pluginId = identifier\n        pluginName = jsName\n        load()\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPlugin.h",
    "content": "#import <Foundation/Foundation.h>\n#import <WebKit/WebKit.h>\n\n@protocol CAPBridgeProtocol;\n@class CAPPluginCall;\n\n@class PluginConfig;\n\n@interface CAPPlugin : NSObject\n\n@property (nonatomic, weak, nullable) WKWebView *webView;\n@property (nonatomic, weak, nullable) id<CAPBridgeProtocol> bridge;\n@property (nonatomic, strong, nonnull) NSString *pluginId;\n@property (nonatomic, strong, nonnull) NSString *pluginName;\n@property (nonatomic, strong, nullable) NSMutableDictionary<NSString *, NSMutableArray<CAPPluginCall *>*> *eventListeners;\n@property (nonatomic, strong, nullable) NSMutableDictionary<NSString *, NSMutableArray<id> *> *retainedEventArguments;\n@property (nonatomic, assign) BOOL shouldStringifyDatesInCalls;\n\n- (instancetype _Nonnull) initWithBridge:(id<CAPBridgeProtocol> _Nonnull) bridge pluginId:(NSString* _Nonnull) pluginId pluginName:(NSString* _Nonnull) pluginName DEPRECATED_MSG_ATTRIBUTE(\"This initializer is deprecated and is not suggested for use. Any data set through this init method will be overridden when it is loaded on the bridge.\");\n- (void)addEventListener:(NSString* _Nonnull)eventName listener:(CAPPluginCall* _Nonnull)listener;\n- (void)removeEventListener:(NSString* _Nonnull)eventName listener:(CAPPluginCall* _Nonnull)listener;\n- (void)notifyListeners:(NSString* _Nonnull)eventName data:(NSDictionary<NSString *, id>* _Nullable)data;\n- (void)notifyListeners:(NSString* _Nonnull)eventName data:(NSDictionary<NSString *, id>* _Nullable)data retainUntilConsumed:(BOOL)retain;\n- (NSArray<CAPPluginCall *>* _Nullable)getListeners:(NSString* _Nonnull)eventName;\n- (BOOL)hasListeners:(NSString* _Nonnull)eventName;\n- (void)addListener:(CAPPluginCall* _Nonnull)call;\n- (void)removeListener:(CAPPluginCall* _Nonnull)call;\n- (void)removeAllListeners:(CAPPluginCall* _Nonnull)call;\n/**\n * Default implementation of the capacitor 3.0 permission pattern\n */\n- (void)checkPermissions:(CAPPluginCall* _Nonnull)call;\n- (void)requestPermissions:(CAPPluginCall* _Nonnull)call;\n/**\n * Give the plugins a chance to take control when a URL is about to be loaded in the WebView.\n * Returning true causes the WebView to abort loading the URL.\n * Returning false causes the WebView to continue loading the URL.\n * Returning nil will defer to the default Capacitor policy\n */\n- (NSNumber* _Nullable)shouldOverrideLoad:(WKNavigationAction* _Nonnull)navigationAction;\n/**\n * Allows plugins to hook into and respond to the WebView's URL authentication challenge.\n * Returning false will defer to the default response of [.rejectProtectionSpace](https://developer.apple.com/documentation/Foundation/URLSession/AuthChallengeDisposition/rejectProtectionSpace).\n */\n- (BOOL)handleWKWebViewURLAuthenticationChallenge:(NSURLAuthenticationChallenge* _Nonnull)challenge completionHandler:(void (^_Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;\n\n// Called after init if the plugin wants to do\n// some loading so the plugin author doesn't\n// need to override init()\n-(void)load;\n-(NSString* _Nonnull)getId;\n-(BOOL)getBool:(CAPPluginCall* _Nonnull) call field:(NSString* _Nonnull)field defaultValue:(BOOL)defaultValue DEPRECATED_MSG_ATTRIBUTE(\"Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations.\");\n-(NSString* _Nullable)getString:(CAPPluginCall* _Nonnull)call field:(NSString* _Nonnull)field defaultValue:(NSString* _Nonnull)defaultValue DEPRECATED_MSG_ATTRIBUTE(\"Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations.\");\n-(id _Nullable)getConfigValue:(NSString* _Nonnull)key __deprecated_msg(\"use getConfig() and access config values using the methods available depending on the type.\");\n-(PluginConfig* _Nonnull)getConfig;\n-(void)setCenteredPopover:(UIViewController* _Nonnull) vc;\n-(void)setCenteredPopover:(UIViewController* _Nonnull) vc size:(CGSize) size;\n-(BOOL)supportsPopover DEPRECATED_MSG_ATTRIBUTE(\"All iOS 13+ devices support popover\");\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPlugin.m",
    "content": "#import \"CAPPlugin.h\"\n#import \"CAPBridgedJSTypes.h\"\n#import <Capacitor/Capacitor-Swift.h>\n#import <Foundation/Foundation.h>\n\n@implementation CAPPlugin\n\n-(instancetype) initWithBridge:(id<CAPBridgeProtocol>)bridge pluginId:(NSString *)pluginId pluginName:(NSString *)pluginName {\n  self.bridge = bridge;\n  self.webView = bridge.webView;\n  self.pluginId = pluginId;\n  self.pluginName = pluginName;\n  self.eventListeners = [[NSMutableDictionary alloc] init];\n  self.retainedEventArguments = [[NSMutableDictionary alloc] init];\n  self.shouldStringifyDatesInCalls = true;\n  return self;\n}\n\n-(NSString *) getId {\n  return self.pluginName;\n}\n\n- (BOOL)getBool:(CAPPluginCall *)call field:(NSString *)field defaultValue:(BOOL)defaultValue {\n  NSNumber* value = [call getNumber:field defaultValue:[NSNumber numberWithBool:defaultValue]];\n  return [value boolValue];\n}\n\n- (NSString *) getString:(CAPPluginCall *)call field:(NSString *)field defaultValue:(NSString *)defaultValue {\n  return [call getString:field defaultValue:defaultValue];\n}\n\n-(id)getConfigValue:(NSString *)key __deprecated {\n  return [self.bridge.config getPluginConfigValue:self.pluginName :key];\n}\n\n-(PluginConfig*)getConfig {\n    return [self.bridge.config getPluginConfig:self.pluginName];\n}\n\n-(void)load {}\n\n- (void)addEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener {\n  NSMutableArray *listenersForEvent = [self.eventListeners objectForKey:eventName];\n  if(listenersForEvent == nil || [listenersForEvent count] == 0) {\n    listenersForEvent = [[NSMutableArray alloc] initWithObjects:listener, nil];\n    [self.eventListeners setValue:listenersForEvent forKey:eventName];\n    \n    [self sendRetainedArgumentsForEvent:eventName];\n  } else {\n    [listenersForEvent addObject:listener];\n  }\n}\n\n- (void)sendRetainedArgumentsForEvent:(NSString *)eventName {\n    // copy retained args and null source to prevent potential race conditions\n    NSMutableArray *retained = [self.retainedEventArguments objectForKey:eventName];\n    if (retained == nil) {\n        return;\n    }\n    \n    [self.retainedEventArguments removeObjectForKey:eventName];\n    \n    for(id data in retained) {\n        [self notifyListeners:eventName data:data];\n    }\n}\n\n- (void)removeEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener {\n  NSMutableArray *listenersForEvent = [self.eventListeners objectForKey:eventName];\n  if(!listenersForEvent) { return; }\n  NSUInteger listenerIndex = [listenersForEvent indexOfObject:listener];\n  if(listenerIndex == NSNotFound) {\n    return;\n  }\n  [listenersForEvent removeObjectAtIndex:listenerIndex];\n}\n\n- (void)notifyListeners:(NSString *)eventName data:(NSDictionary<NSString *,id> *)data {\n  [self notifyListeners:eventName data:data retainUntilConsumed:NO];\n}\n\n- (void)notifyListeners:(NSString *)eventName data:(NSDictionary<NSString *,id> *)data retainUntilConsumed:(BOOL)retain {\n  NSArray<CAPPluginCall *> *listenersForEvent = [self.eventListeners objectForKey:eventName];\n  if(listenersForEvent == nil || [listenersForEvent count] == 0) {\n    if (retain == YES) {\n        \n        if ([self.retainedEventArguments objectForKey:eventName] == nil) {\n            [self.retainedEventArguments setObject:[[NSMutableArray alloc] init] forKey:eventName];\n        }\n        \n        [[self.retainedEventArguments objectForKey:eventName] addObject:data];\n    }\n    return;\n  }\n\n  for (int i=0; i < listenersForEvent.count; i++) {\n    CAPPluginCall *call = listenersForEvent[i];\n    if (call != nil) {\n      CAPPluginCallResult *result = [[CAPPluginCallResult alloc] init:data];\n      call.successHandler(result, call);\n    }\n  }\n}\n\n- (void)addListener:(CAPPluginCall *)call {\n  NSString *eventName = [call.options objectForKey:@\"eventName\"];\n  [call setKeepAlive:TRUE];\n  [self addEventListener:eventName listener:call];\n}\n\n- (void)removeListener:(CAPPluginCall *)call {\n  NSString *eventName = [call.options objectForKey:@\"eventName\"];\n  NSString *callbackId = [call.options objectForKey:@\"callbackId\"];\n  CAPPluginCall *storedCall = [self.bridge savedCallWithID:callbackId];\n  [self removeEventListener:eventName listener:storedCall];\n  [self.bridge releaseCallWithID:callbackId];\n}\n\n- (void)removeAllListeners:(CAPPluginCall *)call {\n  [self.eventListeners removeAllObjects];\n  [call resolve];\n}\n\n- (NSArray<CAPPluginCall *>*)getListeners:(NSString *)eventName {\n  NSArray<CAPPluginCall *>* listeners = [self.eventListeners objectForKey:eventName];\n  return listeners;\n}\n\n- (BOOL)hasListeners:(NSString *)eventName {\n  NSArray<CAPPluginCall *>* listeners = [self.eventListeners objectForKey:eventName];\n  \n  if (listeners == nil) {\n    return false;\n  }\n  return [listeners count] > 0;\n}\n\n- (void)checkPermissions:(CAPPluginCall *)call {\n  [call resolve];\n}\n\n- (void)requestPermissions:(CAPPluginCall *)call {\n  [call resolve];\n}\n\n/**\n * Configure popover sourceRect, sourceView and permittedArrowDirections to show it centered\n */\n-(void)setCenteredPopover:(UIViewController *) vc {\n  if (self.bridge.viewController != nil) {\n    vc.popoverPresentationController.sourceRect = CGRectMake(self.bridge.viewController.view.center.x, self.bridge.viewController.view.center.y, 0, 0);\n    vc.popoverPresentationController.sourceView = self.bridge.viewController.view;\n    vc.popoverPresentationController.permittedArrowDirections = 0;\n  }\n}\n\n-(void)setCenteredPopover:(UIViewController* _Nonnull) vc size:(CGSize) size {\n    if (self.bridge.viewController != nil) {\n      vc.popoverPresentationController.sourceRect = CGRectMake(self.bridge.viewController.view.center.x, self.bridge.viewController.view.center.y, 0, 0);\n      vc.preferredContentSize = size;\n      vc.popoverPresentationController.sourceView = self.bridge.viewController.view;\n      vc.popoverPresentationController.permittedArrowDirections = 0;\n    }\n}\n\n-(BOOL)supportsPopover {\n    return YES;\n}\n\n- (NSNumber*)shouldOverrideLoad:(WKNavigationAction*)navigationAction {\n    return nil;\n}\n\n- (BOOL)handleWKWebViewURLAuthenticationChallenge:(NSURLAuthenticationChallenge* _Nonnull)challenge completionHandler:(void (^_Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {\n    return NO;\n}\n\n\n@end\n\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPluginCall.h",
    "content": "#import <Foundation/Foundation.h>\n\n@class CAPPluginCall;\n@class CAPPluginCallResult;\n@class CAPPluginCallError;\n\ntypedef void(^CAPPluginCallSuccessHandler)(CAPPluginCallResult *result, CAPPluginCall* call);\ntypedef void(^CAPPluginCallErrorHandler)(CAPPluginCallError *error);\n\n@interface CAPPluginCall : NSObject\n\n@property (nonatomic, assign) BOOL isSaved DEPRECATED_MSG_ATTRIBUTE(\"Use 'keepAlive' instead.\");\n@property (nonatomic, assign) BOOL keepAlive;\n@property (nonatomic, strong) NSString *callbackId;\n@property (nonatomic, strong) NSString *methodName;\n@property (nonatomic, strong) NSDictionary *options;\n@property (nonatomic, copy) CAPPluginCallSuccessHandler successHandler;\n@property (nonatomic, copy) CAPPluginCallErrorHandler errorHandler;\n\n- (instancetype)initWithCallbackId:(NSString *)callbackId options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler)success error:(CAPPluginCallErrorHandler)error DEPRECATED_MSG_ATTRIBUTE(\"Specify the method name as well.\");\n\n- (instancetype)initWithCallbackId:(NSString *)callbackId methodName:(NSString *)methodName options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler)success error:(CAPPluginCallErrorHandler)error;\n\n- (void)save DEPRECATED_MSG_ATTRIBUTE(\"Use the 'keepAlive' property instead.\");\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPluginCall.m",
    "content": "#import <Foundation/Foundation.h>\n#import \"CAPPluginCall.h\"\n\n@implementation CAPPluginCall\n\n- (instancetype)initWithCallbackId:(NSString *)callbackId options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler) success error:(CAPPluginCallErrorHandler) error __deprecated {\n  self.callbackId = callbackId;\n  self.methodName = @\"\";\n  self.options = options;\n  self.successHandler = success;\n  self.errorHandler = error;\n  return self;\n}\n\n- (instancetype)initWithCallbackId:(NSString *)callbackId methodName:(NSString *)methodName options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler) success error:(CAPPluginCallErrorHandler) error {\n  self.callbackId = callbackId;\n  self.methodName = methodName;\n  self.options = options;\n  self.successHandler = success;\n  self.errorHandler = error;\n  return self;\n}\n\n- (BOOL)isSaved {\n    return self.keepAlive;\n}\n\n- (void)setIsSaved:(BOOL)saved {\n    self.keepAlive = saved;\n}\n\n- (void)save {\n  self.keepAlive = true;\n}\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPluginCall.swift",
    "content": "import Foundation\n\n@available(*, deprecated, renamed: \"PluginCallResultData\")\npublic typealias PluginCallErrorData = [String: Any]\n@available(*, deprecated, renamed: \"PluginCallResultData\")\npublic typealias PluginResultData = [String: Any]\n\n/**\n * Swift niceties for CAPPluginCall\n */\n\nextension CAPPluginCall: JSValueContainer {\n    public var jsObjectRepresentation: JSObject {\n        return options as? JSObject ?? [:]\n    }\n}\n\n@objc extension CAPPluginCall: BridgedJSValueContainer {\n    public var dictionaryRepresentation: NSDictionary {\n        return options as NSDictionary\n    }\n\n    public static var jsDateFormatter: ISO8601DateFormatter = {\n        return ISO8601DateFormatter()\n    }()\n}\n\n@objc public extension CAPPluginCall {\n    @available(*, deprecated, message: \"Presence of a key should not be considered significant. Use typed accessors to check the value instead.\")\n    func hasOption(_ key: String) -> Bool {\n        guard let value = options[key] else {\n            return false\n        }\n        return !(value is NSNull)\n    }\n\n    func resolve() {\n        successHandler(CAPPluginCallResult(nil), self)\n    }\n\n    func resolve(_ data: PluginCallResultData = [:]) {\n        successHandler(CAPPluginCallResult(data), self)\n    }\n\n    func reject(_ message: String, _ code: String? = nil, _ error: Error? = nil, _ data: PluginCallResultData? = nil) {\n        errorHandler(CAPPluginCallError(message: message, code: code, error: error, data: data))\n    }\n\n    func unimplemented() {\n        unimplemented(\"not implemented\")\n    }\n\n    func unimplemented(_ message: String) {\n        errorHandler(CAPPluginCallError(message: message, code: \"UNIMPLEMENTED\", error: nil, data: [:]))\n    }\n\n    func unavailable() {\n        unavailable(\"not available\")\n    }\n\n    func unavailable(_ message: String) {\n        errorHandler(CAPPluginCallError(message: message, code: \"UNAVAILABLE\", error: nil, data: [:]))\n    }\n}\n\n// MARK: Codable Support\npublic extension CAPPluginCall {\n    /// Encodes the given value to a ``JSObject`` and resolves the call. If an error is thrown during encoding, ``reject(_:_:_:_:)`` is called.\n    /// - Parameters:\n    ///   - data: The value to encode\n    ///   - encoder: The encoder to use. Defaults to `JSValueEncoder()`\n    ///   - messageForRejectionFromError: A closure that takes the error thrown from ``JSValueEncoder/encodeJSObject(_:)``\n    ///   and returns a string to be provided to ``reject(_:_:_:_:)``. Defaults to a function that returns \"Failed encoding response\".\n    func resolve<T: Encodable>(\n        with data: T,\n        encoder: JSValueEncoder = JSValueEncoder(),\n        messageForRejectionFromError: (Error) -> String = { _ in \"Failed encoding response\" }\n    ) {\n        do {\n            let encoded = try encoder.encodeJSObject(data)\n            resolve(encoded)\n        } catch {\n            let message = messageForRejectionFromError(error)\n            reject(message, nil, error)\n        }\n    }\n\n    /// Decodes the options to the given type.\n    /// - Parameters:\n    ///   - type: The type to decode to.\n    ///   - decoder: The decoder to use. Defaults to `JSValueDecoder()`.\n    /// - Throws: If the options cannot be decoded.\n    /// - Returns: The decoded value.\n    func decode<T: Decodable>(_ type: T.Type, decoder: JSValueDecoder = JSValueDecoder()) throws -> T {\n        try decoder.decode(type, from: options as? JSObject ?? [:])\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPluginMethod.h",
    "content": "#import \"CAPPluginCall.h\"\n#import \"CAPPlugin.h\"\n\ntypedef enum {\n  CAPPluginMethodArgumentNotNullable,\n  CAPPluginMethodArgumentNullable\n} CAPPluginMethodArgumentNullability;\n\ntypedef NSString CAPPluginReturnType;\n\n/**\n * Represents a single argument to a plugin method.\n */\n@interface CAPPluginMethodArgument : NSObject\n\n@property (nonatomic, copy) NSString *name;\n@property (nonatomic, assign) CAPPluginMethodArgumentNullability nullability;\n\n- (instancetype)initWithName:(NSString *)name nullability:(CAPPluginMethodArgumentNullability)nullability type:(NSString *)type;\n\n@end\n\n/**\n * Represents a method that a plugin supports, with the ability\n * to compute selectors and invoke the method.\n */\n@interface CAPPluginMethod : NSObject\n\n@property (nonatomic, assign) SEL selector;\n@property (nonatomic, strong) NSString *name; // Raw method name\n@property (nonatomic, strong) CAPPluginReturnType *returnType; // Return type of method (i.e. callback/promise/sync)\n\n- (instancetype)initWithName:(NSString *)name returnType:(CAPPluginReturnType *)returnType;\n- (instancetype)initWithSelector:(SEL)selector returnType:(CAPPluginReturnType *)returnType;\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPluginMethod.m",
    "content": "#import <Capacitor/Capacitor-Swift.h>\n#import \"CAPPluginMethod.h\"\n\ntypedef void(^CAPCallback)(id _arg, NSInteger index);\n\n@implementation CAPPluginMethodArgument\n\n- (instancetype)initWithName:(NSString *)name nullability:(CAPPluginMethodArgumentNullability)nullability type:(NSString *)type {\n  self.name = name;\n  self.nullability = nullability;\n  return self;\n}\n\n@end\n\n@implementation CAPPluginMethod {\n  // NSInvocation's retainArguments doesn't work with our arguments\n  // so we have to retain args manually\n  NSMutableArray *_manualRetainArgs;\n  // Retain invocation instance\n  NSInvocation *_invocation;\n  NSMutableArray *_methodArgumentCallbacks;\n  CAPPluginCall *_call;\n  SEL _selector;\n}\n\n-(instancetype)initWithName:(NSString *)name returnType:(CAPPluginReturnType *)returnType {\n  self.name = name;\n  self.selector = NSSelectorFromString([name stringByAppendingString:@\":\"]);\n  self.returnType = returnType;\n  return self;\n}\n\n-(instancetype)initWithSelector:(SEL) selector returnType:(CAPPluginReturnType *)returnType {\n    // need to drop the : from the selector string\n    NSString* rawSelString = NSStringFromSelector(selector);\n    self.name = [rawSelString substringToIndex:[rawSelString length] - 1];\n    self.selector = selector;\n    self.returnType = returnType;\n    return self;\n}\n\n@end\n\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CAPPluginMethod.swift",
    "content": "//\n//  CAPPluginMethod.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 4/18/24.\n//  Copyright © 2024 Drifty Co. All rights reserved.\n//\n\nextension CAPPluginMethod {\n    public enum ReturnType: String {\n        case promise, callback, none\n    }\n\n    public convenience init(_ selector: Selector, returnType: ReturnType = .promise) {\n        self.init(selector: selector, returnType: returnType.rawValue)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Capacitor.h",
    "content": "#import <UIKit/UIKit.h>\n\n//! Project version number for bridge.\nFOUNDATION_EXPORT double CapacitorVersionNumber;\n\n//! Project version string for bridge.\nFOUNDATION_EXPORT const unsigned char CapacitorVersionString[];\n\n#import <Capacitor/CAPPlugin.h>\n#import <Capacitor/CAPPluginCall.h>\n#import <Capacitor/CAPBridgedPlugin.h>\n#import <Capacitor/CAPPluginMethod.h>\n#import <Capacitor/CAPInstanceDescriptor.h>\n#import <Capacitor/CAPInstanceConfiguration.h>\n\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Capacitor.modulemap",
    "content": "framework module Capacitor {\n  umbrella header \"Capacitor.h\"\n  exclude header \"CAPBridgedJSTypes.h\"\n  exclude header \"CAPBridgeViewController+CDVScreenOrientationDelegate.h\"\n\n  export *\n  module * { export * }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CapacitorBridge.swift",
    "content": "import Foundation\nimport Dispatch\nimport WebKit\nimport Cordova\n\ninternal typealias CapacitorPlugin = CAPPlugin & CAPBridgedPlugin\n\nstruct RegistrationList: Codable {\n    let packageClassList: Set<String>\n}\n\n/**\n An internal class adopting a public protocol means that we have a lot of `public` methods\n but that is by design not a mistake. And since the bridge is the center of the whole project\n its size/complexity is unavoidable.\n\n Quiet these warnings for the whole file.\n */\n// swiftlint:disable lower_acl_than_parent\n// swiftlint:disable file_length\n// swiftlint:disable type_body_length\nopen class CapacitorBridge: NSObject, CAPBridgeProtocol {\n\n    // this decision is needed before the bridge is instantiated,\n    // so we need a class property to avoid duplication\n    public static var isDevEnvironment: Bool {\n        #if DEBUG\n        return true\n        #else\n        // this is needed for SPM xcframework Capacitor.  Can eventually be removed when the SPM package moves to being source-based.\n        if let debugValue = Bundle.main.object(forInfoDictionaryKey: \"CAPACITOR_DEBUG\") as? String, debugValue == \"true\" {\n            return true\n        }\n        return false\n        #endif\n    }\n\n    // MARK: - CAPBridgeProtocol: Properties\n\n    public var webView: WKWebView? {\n        return bridgeDelegate?.bridgedWebView\n    }\n\n    public let autoRegisterPlugins: Bool\n\n    public var notificationRouter: NotificationRouter\n\n    public var isSimEnvironment: Bool {\n        #if targetEnvironment(simulator)\n        return true\n        #else\n        return false\n        #endif\n    }\n\n    public var isDevEnvironment: Bool {\n        return CapacitorBridge.isDevEnvironment\n    }\n\n    public var userInterfaceStyle: UIUserInterfaceStyle {\n        return viewController?.traitCollection.userInterfaceStyle ?? .unspecified\n    }\n\n    public var statusBarVisible: Bool {\n        get {\n            return !(viewController?.prefersStatusBarHidden ?? true)\n        }\n        set {\n            DispatchQueue.main.async { [weak self] in\n                (self?.viewController as? CAPBridgeViewController)?.setStatusBarVisible(newValue)\n            }\n        }\n    }\n\n    public var statusBarStyle: UIStatusBarStyle {\n        get {\n            return viewController?.preferredStatusBarStyle ?? .default\n        }\n        set {\n            DispatchQueue.main.async { [weak self] in\n                (self?.viewController as? CAPBridgeViewController)?.setStatusBarStyle(newValue)\n            }\n        }\n    }\n\n    public var statusBarAnimation: UIStatusBarAnimation {\n        get {\n            return (viewController as? CAPBridgeViewController)?.statusBarAnimation ?? .slide\n        }\n        set {\n            DispatchQueue.main.async { [weak self] in\n                (self?.viewController as? CAPBridgeViewController)?.setStatusBarAnimation(newValue)\n            }\n        }\n    }\n    @available(*, deprecated, message: \"obsolete\")\n    var tmpWindow: UIWindow?\n    @available(*, deprecated, message: \"obsolete\")\n    static let tmpVCAppeared = Notification(name: Notification.Name(rawValue: \"tmpViewControllerAppeared\"))\n    public static let capacitorSite = \"https://capacitorjs.com/\"\n    public static let fileStartIdentifier = \"/_capacitor_file_\"\n    public static let httpInterceptorStartIdentifier = \"/_capacitor_http_interceptor_\"\n    @available(*, deprecated, message: \"`httpsInterceptorStartIdentifier` is no longer required. All proxied requests are handled via `httpInterceptorStartIdentifier` instead\")\n    public static let httpsInterceptorStartIdentifier = \"/_capacitor_https_interceptor_\"\n    public static let httpInterceptorUrlParam = \"u\"\n    public static let defaultScheme = \"capacitor\"\n\n    public private(set) var webViewAssetHandler: WebViewAssetHandler\n    public private(set) var webViewDelegationHandler: WebViewDelegationHandler\n    public private(set) weak var bridgeDelegate: CAPBridgeDelegate?\n    @objc public var viewController: UIViewController? {\n        return bridgeDelegate?.bridgedViewController\n    }\n\n    var lastPlugin: CAPPlugin?\n\n    @objc public var config: InstanceConfiguration\n    // Map of all loaded and instantiated plugins by pluginId -> instance\n    var plugins =  [String: CapacitorPlugin]()\n    // Manager for getting Cordova plugins\n    var cordovaPluginManager: CDVPluginManager?\n    // Calls we are storing to resolve later\n    var storedCalls = ConcurrentDictionary<CAPPluginCall>()\n    // Whether to inject the Cordova files\n    private var injectCordovaFiles = false\n    private var cordovaParser: CDVConfigParser?\n    private var injectMiscFiles: [String] = []\n    private var canInjectJS: Bool = true\n\n    // Background dispatch queue for plugin calls\n    open private(set) var dispatchQueue = DispatchQueue(label: \"bridge\")\n    // Array of block based observers\n    var observers: [NSObjectProtocol] = []\n\n    // MARK: - CAPBridgeProtocol: Deprecated\n\n    public func getWebView() -> WKWebView? {\n        return webView\n    }\n\n    public func isSimulator() -> Bool {\n        return isSimEnvironment\n    }\n\n    public func isDevMode() -> Bool {\n        return isDevEnvironment\n    }\n\n    public func getStatusBarVisible() -> Bool {\n        return statusBarVisible\n    }\n\n    @nonobjc public func setStatusBarVisible(_ visible: Bool) {\n        statusBarVisible = visible\n    }\n\n    public func getStatusBarStyle() -> UIStatusBarStyle {\n        return statusBarStyle\n    }\n    @nonobjc public func setStatusBarStyle(_ style: UIStatusBarStyle) {\n        statusBarStyle = style\n    }\n\n    public func getUserInterfaceStyle() -> UIUserInterfaceStyle {\n        return userInterfaceStyle\n    }\n\n    public func getLocalUrl() -> String {\n        return config.localURL.absoluteString\n    }\n\n    @nonobjc public func setStatusBarAnimation(_ animation: UIStatusBarAnimation) {\n        statusBarAnimation = animation\n    }\n\n    public func setServerBasePath(_ path: String) {\n        let url = URL(fileURLWithPath: path, isDirectory: true)\n        guard FileManager.default.fileExists(atPath: url.path) else { return }\n        config = config.updatingAppLocation(url)\n        webViewAssetHandler.setAssetPath(url.path)\n    }\n\n    // MARK: - Static Methods\n\n    /**\n     Print a hopefully informative error message to the log when something\n     particularly dreadful happens.\n     */\n    static func fatalError(_ error: Error, _ originalError: Error) {\n        CAPLog.print(\"⚡️ ❌  Capacitor: FATAL ERROR\")\n        CAPLog.print(\"⚡️ ❌  Error was: \", originalError.localizedDescription)\n        switch error {\n        case CapacitorBridgeError.errorExportingCoreJS:\n            CAPLog.print(\"⚡️ ❌  Unable to export required Bridge JavaScript. Bridge will not function.\")\n            CAPLog.print(\"⚡️ ❌  You should run \\\"npx capacitor copy\\\" to ensure the Bridge JS is added to your project.\")\n            if let wke = originalError as? WKError {\n                CAPLog.print(\"⚡️ ❌ \", wke.userInfo)\n            }\n        default:\n            CAPLog.print(\"⚡️ ❌  Unknown error\")\n        }\n\n        CAPLog.print(\"⚡️ ❌  Please verify your installation or file an issue\")\n    }\n\n    // MARK: - Initialization\n\n    public init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: CDVConfigParser, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) {\n        self.bridgeDelegate = bridgeDelegate\n        self.webViewAssetHandler = assetHandler\n        self.webViewDelegationHandler = delegationHandler\n        self.config = configuration\n        self.cordovaParser = cordovaConfiguration\n        self.notificationRouter = NotificationRouter()\n        self.notificationRouter.handleApplicationNotifications = configuration.handleApplicationNotifications\n        self.autoRegisterPlugins = autoRegisterPlugins\n        super.init()\n\n        self.webViewDelegationHandler.bridge = self\n\n        exportCoreJS(localUrl: configuration.localURL.absoluteString)\n        registerPlugins()\n        setupCordovaCompatibility()\n        exportMiscJS()\n        canInjectJS = false\n        observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in\n            self?.tmpWindow = nil\n        })\n\n        self.setupWebDebugging(configuration: configuration)\n    }\n\n    deinit {\n        // the message handler needs to removed to avoid any retain cycles\n        webViewDelegationHandler.cleanUp()\n        for observer in observers {\n            NotificationCenter.default.removeObserver(observer)\n        }\n    }\n\n    // MARK: - Plugins\n    /**\n     Export core JavaScript to the webview\n     */\n    func exportCoreJS(localUrl: String) {\n        do {\n            try JSExport.exportCapacitorGlobalJS(userContentController: webViewDelegationHandler.contentController,\n                                                 isDebug: isDevEnvironment,\n                                                 loggingEnabled: config.loggingEnabled,\n                                                 localUrl: localUrl)\n            try JSExport.exportBridgeJS(userContentController: webViewDelegationHandler.contentController)\n        } catch {\n            type(of: self).fatalError(error, error)\n        }\n    }\n\n    /**\n     Export misc JavaScript to the webview\n     */\n    func exportMiscJS() {\n        JSExport.exportMiscFileJS(paths: injectMiscFiles, userContentController: webViewDelegationHandler.contentController)\n        injectMiscFiles.removeAll()\n    }\n\n    /**\n     Set up our Cordova compat by loading all known Cordova plugins and injecting their JS.\n     */\n    func setupCordovaCompatibility() {\n        if injectCordovaFiles {\n            exportCordovaJS()\n            registerCordovaPlugins()\n        } else {\n            observers.append(NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in\n                self?.triggerDocumentJSEvent(eventName: \"resume\")\n            })\n            observers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in\n                self?.triggerDocumentJSEvent(eventName: \"pause\")\n            })\n        }\n    }\n\n    /**\n     Export the core Cordova JS runtime\n     */\n    func exportCordovaJS() {\n        do {\n            try JSExport.exportCordovaJS(userContentController: webViewDelegationHandler.contentController)\n        } catch {\n            type(of: self).fatalError(error, error)\n        }\n    }\n\n    /**\n     Reset the state of the bridge between navigations to avoid\n     sending data back to the page from a previous page.\n     */\n    func reset() {\n        storedCalls.withLock { $0.removeAll() }\n        removeAllPluginListeners()\n    }\n\n    /**\n     Register all plugins that have been declared\n     */\n    func registerPlugins() {\n        var pluginList: [AnyClass] = [CAPHttpPlugin.self, CAPConsolePlugin.self, CAPWebViewPlugin.self, CAPCookiesPlugin.self, CAPSystemBarsPlugin.self]\n\n        if autoRegisterPlugins {\n            do {\n                if let pluginJSON = Bundle.main.url(forResource: \"capacitor.config\", withExtension: \"json\") {\n                    let pluginData = try Data(contentsOf: pluginJSON)\n                    let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData)\n\n                    for plugin in registrationList.packageClassList {\n                        if let pluginClass = NSClassFromString(plugin) {\n                            if pluginClass == CDVPlugin.self {\n                                injectCordovaFiles = true\n                            } else {\n                                pluginList.append(pluginClass)\n                            }\n                        }\n                    }\n                }\n            } catch {\n                CAPLog.print(\"Error registering plugins: \\(error)\")\n            }\n        }\n\n        for plugin in pluginList {\n            if plugin is CAPInstancePlugin.Type { continue }\n            if let capPlugin = plugin as? CapacitorPlugin.Type {\n                registerPlugin(capPlugin)\n            }\n        }\n    }\n\n    public func registerPluginType(_ pluginType: CAPPlugin.Type) {\n        if autoRegisterPlugins { return }\n        if pluginType is CAPInstancePlugin.Type {\n            Swift.fatalError(\"\"\"\n\n            ⚡️ ❌  Cannot register class \\(pluginType): CAPInstancePlugin through registerPluginType(_:).\n            ⚡️ ❌  Use `registerPluginInstance(_:)` to register subclasses of CAPInstancePlugin.\n            \"\"\")\n        }\n        guard let bridgedType = pluginType as? CapacitorPlugin.Type else { return }\n        registerPlugin(bridgedType)\n    }\n\n    public func registerPluginInstance(_ pluginInstance: CAPPlugin) {\n        guard let pluginInstance = pluginInstance as? CapacitorPlugin else {\n            CAPLog.print(\"\"\"\n\n            ⚡️  Plugin \\(pluginInstance.classForCoder) must conform to CAPBridgedPlugin.\n            ⚡️  Not loading plugin \\(pluginInstance.classForCoder)\n            \"\"\")\n            return\n        }\n\n        if plugins[pluginInstance.jsName] != nil {\n            CAPLog.print(\"⚡️  Overriding existing registered plugin \\(pluginInstance.classForCoder)\")\n        }\n        plugins[pluginInstance.jsName] = pluginInstance\n        pluginInstance.load(on: self)\n\n        JSExport.exportJS(for: pluginInstance, in: webViewDelegationHandler.contentController)\n    }\n\n    /**\n     Register a single plugin.\n     */\n    func registerPlugin(_ pluginType: CapacitorPlugin.Type) {\n        if let plugin = loadPlugin(type: pluginType) {\n            JSExport.exportJS(for: plugin, in: webViewDelegationHandler.contentController)\n        }\n    }\n\n    func loadPlugin(type: CAPPlugin.Type) -> CapacitorPlugin? {\n        guard let plugin = type.init() as? CapacitorPlugin else {\n            CAPLog.print(\"⚡️  Unable to load plugin \\(type.classForCoder()). No such module found.\")\n            return nil\n        }\n        plugin.load(on: self)\n        plugins[plugin.jsName] = plugin\n        return plugin\n    }\n\n    // MARK: - CAPBridgeProtocol: Plugin Access\n\n    @objc public func plugin(withName: String) -> CAPPlugin? {\n        return self.plugins[withName]\n    }\n\n    // MARK: - CAPBridgeProtocol: Call Management\n\n    @objc public func saveCall(_ call: CAPPluginCall) {\n        storedCalls[call.callbackId] = call\n    }\n\n    @objc public func savedCall(withID: String) -> CAPPluginCall? {\n        return storedCalls[withID]\n    }\n\n    @objc public func releaseCall(_ call: CAPPluginCall) {\n        releaseCall(withID: call.callbackId)\n    }\n\n    @objc public func releaseCall(withID: String) {\n        _ = storedCalls.withLock { $0.removeValue(forKey: withID) }\n    }\n\n    // MARK: - Deprecated Versions\n\n    @objc public func getSavedCall(_ callbackId: String) -> CAPPluginCall? {\n        return savedCall(withID: callbackId)\n    }\n\n    @objc public func releaseCall(callbackId: String) {\n        releaseCall(withID: callbackId)\n    }\n\n    // MARK: - Internal\n\n    func getDispatchQueue() -> DispatchQueue {\n        return self.dispatchQueue\n    }\n\n    func registerCordovaPlugins() {\n        guard let cordovaParser = cordovaParser else {\n            return\n        }\n        cordovaPluginManager = CDVPluginManager.init(parser: cordovaParser, viewController: self.viewController, webView: self.getWebView())\n        if cordovaParser.startupPluginNames.count > 0 {\n            for pluginName in cordovaParser.startupPluginNames {\n                _ = cordovaPluginManager?.getCommandInstance(pluginName as? String)\n            }\n        }\n        do {\n            try JSExport.exportCordovaPluginsJS(userContentController: webViewDelegationHandler.contentController)\n        } catch {\n            type(of: self).fatalError(error, error)\n        }\n    }\n\n    func reload() {\n        self.getWebView()?.reload()\n    }\n\n    func docLink(_ url: String) -> String {\n        return \"\\(type(of: self).capacitorSite)docs/\\(url)\"\n    }\n\n    private func setupWebDebugging(configuration: InstanceConfiguration) {\n        let isWebDebuggable = configuration.isWebDebuggable\n        if isWebDebuggable, #unavailable(iOS 16.4) {\n            CAPLog.print(\"⚡️ Warning: isWebDebuggable only functions as intended on iOS 16.4 and above.\")\n        }\n\n        if #available(iOS 16.4, *) {\n            self.webView?.isInspectable = isWebDebuggable\n        }\n    }\n\n    /**\n     Handle a call from JavaScript. First, find the corresponding plugin, construct a selector,\n     and perform that selector on the plugin instance.\n\n     Quiet the length warning because we don't want to refactor the function at this time.\n     */\n    // swiftlint:disable:next function_body_length\n    func handleJSCall(call: JSCall) {\n        let load = {\n            NSClassFromString(call.pluginId)\n                .flatMap { $0 as? CAPPlugin.Type }\n                .flatMap(self.loadPlugin(type:))\n        }\n\n        guard let plugin = plugins[call.pluginId] ?? load() else {\n            CAPLog.print(\"⚡️  Error loading plugin \\(call.pluginId) for call. Check that the pluginId is correct\")\n            return\n        }\n\n        let selector: Selector\n        if call.method == \"addListener\" || call.method == \"removeListener\" || call.method == \"removeAllListeners\" {\n            selector = NSSelectorFromString(call.method + \":\")\n        } else {\n            guard let method = plugin.getMethod(named: call.method) else {\n                CAPLog.print(\"⚡️  Error calling method \\(call.method) on plugin \\(call.pluginId): No method found.\")\n                CAPLog.print(\"⚡️  Ensure plugin method exists and uses @objc in its declaration, and has been defined\")\n                return\n            }\n\n            selector = method.selector\n        }\n\n        if !plugin.responds(to: selector) {\n            // we don't want to break up string literals\n            // swiftlint:disable line_length\n            CAPLog.print(\"⚡️  Error: Plugin \\(plugin.getId()) does not respond to method call \\\"\\(call.method)\\\" using selector \\\"\\(selector)\\\".\")\n            CAPLog.print(\"⚡️  Ensure plugin method exists, uses @objc in its declaration, and arguments match selector without callbacks in CAP_PLUGIN_METHOD.\")\n            CAPLog.print(\"⚡️  Learn more: \\(docLink(DocLinks.CAPPluginMethodSelector.rawValue))\")\n            // swiftlint:enable line_length\n            return\n        }\n\n        // Create a plugin call object and handle the success/error callbacks\n        dispatchQueue.async { [weak self] in\n            // let startTime = CFAbsoluteTimeGetCurrent()\n\n            let pluginCall = CAPPluginCall(callbackId: call.callbackId, methodName: call.method,\n                                           options: JSTypes.coerceDictionaryToJSObject(call.options,\n                                                                                       formattingDatesAsStrings: plugin.shouldStringifyDatesInCalls) ?? [:],\n                                           success: {(result: CAPPluginCallResult?, pluginCall: CAPPluginCall?) in\n                                            if let result = result {\n                                                self?.toJs(result: JSResult(call: call, callResult: result), save: pluginCall?.keepAlive ?? false)\n                                            } else {\n                                                self?.toJs(result: JSResult(call: call, result: .dictionary([:])), save: pluginCall?.keepAlive ?? false)\n                                            }\n                                           }, error: {(error: CAPPluginCallError?) in\n                                            if let error = error {\n                                                self?.toJsError(error: JSResultError(call: call, callError: error))\n                                            } else {\n                                                self?.toJsError(error: JSResultError(call: call,\n                                                                                     errorMessage: \"\",\n                                                                                     errorDescription: \"\",\n                                                                                     errorCode: nil,\n                                                                                     result: .dictionary([:])))\n                                            }\n                                           })\n\n            if let pluginCall = pluginCall {\n                plugin.perform(selector, with: pluginCall)\n                if pluginCall.keepAlive {\n                    self?.saveCall(pluginCall)\n                }\n            }\n\n            // let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime\n            // CAPLog.print(\"Native call took\", timeElapsed)\n        }\n    }\n\n    /**\n     Handle a Cordova call from JavaScript. First, find the corresponding plugin,\n     construct a selector, and perform that selector on the plugin instance.\n     */\n    func handleCordovaJSCall(call: JSCall) {\n        // Create a selector to send to the plugin\n\n        if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) {\n            let selector = NSSelectorFromString(\"\\(call.method):\")\n            if !plugin.responds(to: selector) {\n                CAPLog.print(\"Error: Plugin \\(plugin.className ?? \"\") does not respond to method call \\(selector).\")\n                CAPLog.print(\"Ensure plugin method exists and uses @objc in its declaration\")\n                return\n            }\n\n            let arguments: [Any] = call.options[\"options\"] as? [Any] ?? []\n            let pluginCall = CDVInvokedUrlCommand(arguments: arguments,\n                                                  callbackId: call.callbackId,\n                                                  className: plugin.className,\n                                                  methodName: call.method)\n            plugin.perform(selector, with: pluginCall)\n\n        } else {\n            CAPLog.print(\"Error: Cordova Plugin mapping not found\")\n            return\n        }\n    }\n\n    func removeAllPluginListeners() {\n        for plugin in plugins.values {\n            plugin.perform(#selector(CAPPlugin.removeAllListeners(_:)), with: nil)\n        }\n    }\n\n    /**\n     Send a successful result to the JavaScript layer.\n     */\n    func toJs(result: JSResultProtocol, save: Bool) {\n        let resultJson = result.jsonPayload()\n        CAPLog.print(\"⚡️  TO JS\", resultJson.prefix(256))\n\n        DispatchQueue.main.async {\n            self.webView?.evaluateJavaScript(\"\"\"\n             window.Capacitor.fromNative({\n             callbackId: '\\(result.callbackID)',\n             pluginId: '\\(result.pluginID)',\n             methodName: '\\(result.methodName)',\n             save: \\(save),\n             success: true,\n             data: \\(resultJson)\n             })\n            \"\"\") { (_, error) in\n                if let error = error {\n                    CAPLog.print(error)\n                }\n            }\n        }\n    }\n\n    /**\n     Send an error result to the JavaScript layer.\n     */\n    func toJsError(error: JSResultProtocol) {\n        DispatchQueue.main.async {\n            self.webView?.evaluateJavaScript(\"window.Capacitor.fromNative({ callbackId: '\\(error.callbackID)', pluginId: '\\(error.pluginID)', methodName: '\\(error.methodName)', success: false, error: \\(error.jsonPayload())})\") { (_, error) in\n                if let error = error {\n                    CAPLog.print(error)\n                }\n            }\n        }\n    }\n\n    // MARK: - CAPBridgeProtocol: JavaScript Handling\n\n    /**\n     Inject JavaScript from an external file before the WebView loads.\n\n     `path` is relative to the public folder\n     */\n    public func injectScriptBeforeLoad(path: String) {\n        if canInjectJS {\n            injectMiscFiles.append(path)\n        }\n    }\n\n    /**\n     Eval JS for a specific plugin.\n\n     `js` is a short name but needs to be preserved for backwards compatibility.\n     */\n    // swiftlint:disable:next identifier_name\n    @objc public func evalWithPlugin(_ plugin: CAPPlugin, js: String) {\n        let wrappedJs = \"\"\"\n        window.Capacitor.withPlugin('\\(plugin.getId())', function(plugin) {\n        if(!plugin) { console.error('Unable to execute JS in plugin, no such plugin found for id \\(plugin.getId())'); }\n        \\(js)\n        });\n        \"\"\"\n\n        DispatchQueue.main.async {\n            self.getWebView()?.evaluateJavaScript(wrappedJs, completionHandler: { (_, error) in\n                if let error = error {\n                    CAPLog.print(\"⚡️  JS Eval error\", error.localizedDescription)\n                }\n            })\n        }\n    }\n\n    /**\n     Eval JS in the web view\n\n     `js` is a short name but needs to be preserved for backwards compatibility.\n     */\n    // swiftlint:disable:next identifier_name\n    @objc public func eval(js: String) {\n        DispatchQueue.main.async {\n            self.getWebView()?.evaluateJavaScript(js, completionHandler: { (_, error) in\n                if let error = error {\n                    CAPLog.print(\"⚡️  JS Eval error\", error.localizedDescription)\n                }\n            })\n        }\n    }\n\n    @objc public func triggerJSEvent(eventName: String, target: String) {\n        self.eval(js: \"window.Capacitor.triggerEvent('\\(eventName)', '\\(target)')\")\n    }\n\n    @objc public func triggerJSEvent(eventName: String, target: String, data: String) {\n        self.eval(js: \"window.Capacitor.triggerEvent('\\(eventName)', '\\(target)', \\(data))\")\n    }\n\n    @objc public func triggerWindowJSEvent(eventName: String) {\n        self.triggerJSEvent(eventName: eventName, target: \"window\")\n    }\n\n    @objc public func triggerWindowJSEvent(eventName: String, data: String) {\n        self.triggerJSEvent(eventName: eventName, target: \"window\", data: data)\n    }\n\n    @objc public func triggerDocumentJSEvent(eventName: String) {\n        self.triggerJSEvent(eventName: eventName, target: \"document\")\n    }\n\n    @objc public func triggerDocumentJSEvent(eventName: String, data: String) {\n        self.triggerJSEvent(eventName: eventName, target: \"document\", data: data)\n    }\n\n    public func logToJs(_ message: String, _ level: String = \"log\") {\n        DispatchQueue.main.async {\n            self.getWebView()?.evaluateJavaScript(\"window.Capacitor.logJs('\\(message)', '\\(level)')\") { (result, error) in\n                if error != nil, let result = result {\n                    CAPLog.print(result)\n                }\n            }\n        }\n    }\n\n    // MARK: - CAPBridgeProtocol: Paths, Files, Assets\n\n    /**\n     Translate a URL from the web view into a file URL for native iOS.\n\n     The web view may be handling several different types of URLs:\n     - res:// (shortcut scheme to web assets)\n     - file:// (fully qualified URL to file on the local device)\n     - base64:// (to be implemented)\n     - [web view scheme]:// (already converted once to load in the web view, to be implemented)\n     */\n    public func localURL(fromWebURL webURL: URL?) -> URL? {\n        guard let inputURL = webURL else {\n            return nil\n        }\n\n        let url: URL\n\n        switch inputURL.scheme {\n        case \"res\":\n            url = config.appLocation.appendingPathComponent(inputURL.path)\n        case \"file\":\n            url = inputURL\n        default:\n            return nil\n        }\n\n        return url\n    }\n\n    /**\n     Translate a file URL for native iOS into a URL to load in the web view.\n     */\n    public func portablePath(fromLocalURL localURL: URL?) -> URL? {\n        guard let inputURL = localURL else {\n            return nil\n        }\n\n        return self.config.localURL.appendingPathComponent(CapacitorBridge.fileStartIdentifier).appendingPathComponent(inputURL.path)\n    }\n\n    // MARK: - CAPBridgeProtocol: View Presentation\n\n    @objc open func showAlertWith(title: String, message: String, buttonTitle: String) {\n        let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)\n        alert.addAction(UIAlertAction(title: buttonTitle, style: UIAlertAction.Style.default, handler: nil))\n        self.viewController?.present(alert, animated: true, completion: nil)\n    }\n\n    @objc open func presentVC(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {\n        self.viewController?.present(viewControllerToPresent, animated: flag, completion: completion)\n    }\n\n    @objc open func dismissVC(animated flag: Bool, completion: (() -> Void)? = nil) {\n        self.viewController?.dismiss(animated: flag, completion: completion)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/CapacitorExtension.swift",
    "content": "import Foundation\n\npublic protocol CapacitorExtension {\n    associatedtype CapacitorType\n    var capacitor: CapacitorType { get }\n    static var capacitor: CapacitorType.Type { get }\n}\n\npublic extension CapacitorExtension {\n    var capacitor: CapacitorExtensionTypeWrapper<Self> {\n        return CapacitorExtensionTypeWrapper(self)\n    }\n\n    static var capacitor: CapacitorExtensionTypeWrapper<Self>.Type {\n        return CapacitorExtensionTypeWrapper.self\n    }\n}\n\npublic struct CapacitorExtensionTypeWrapper<T> {\n    var baseType: T\n    init(_ baseType: T) {\n        self.baseType = baseType\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Codable/JSValueDecoder.swift",
    "content": "//\n//  JSValueDecoder.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 12/8/23.\n//  Copyright © 2023 Drifty Co. All rights reserved.\n//\n\nimport Foundation\nimport Combine\n\n/// A decoder that can decode ``JSValue`` objects into `Decodable` types.\npublic final class JSValueDecoder: TopLevelDecoder {\n    /// The strategies available for formatting dates when decoding from a ``JSValue``\n    public typealias DateDecodingStrategy = JSONDecoder.DateDecodingStrategy\n    /// The strategies available for decoding raw data.\n    public typealias DataDecodingStrategy = JSONDecoder.DataDecodingStrategy\n\n    /// The strategies availble for decoding NaN, Infinity, and -Infinity\n    public enum NonConformingFloatDecodingStrategy {\n        /// Decodes directly into the floating point type as .infinity, -.infinity, or .nan\n        case deferred\n        /// Throw an error when a non-conforming float is encountered\n        case `throw`\n        /// Converts from the provided strings into .infinity, -.infinity, or .nan\n        case convertFromString(positiveInfinity: String, negativeInfinity: String, nan: String)\n    }\n\n    fileprivate struct Options {\n        var dataStrategy: DataDecodingStrategy\n        var dateStrategy: DateDecodingStrategy\n        var nonConformingStrategy: NonConformingFloatDecodingStrategy\n    }\n\n    private var options: Options\n\n    /// Creates a new JSValueDecoder with the provided decoding and formatting strategies\n    /// - Parameters:\n    ///   - dateDecodingStrategy: Defaults to `DateDecodingStrategy.deferredToDate`\n    ///   - dataDecodingStrategy: Defaults to `DataDecodingStrategy.deferredToData`\n    ///   - nonConformingFloatDecodingStrategy: Defaults to ``NonConformingFloatDecodingStrategy/deferred``\n    public init(\n        dateDecodingStrategy: DateDecodingStrategy = .deferredToDate,\n        dataDecodingStrategy: DataDecodingStrategy = .deferredToData,\n        nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy = .deferred\n    ) {\n        self.options = .init(dataStrategy: dataDecodingStrategy, dateStrategy: dateDecodingStrategy, nonConformingStrategy: nonConformingFloatDecodingStrategy)\n    }\n\n    fileprivate init(options: Options) {\n        self.options = options\n    }\n\n    /// The strategy to use when decoding dates from a ``JSValue``\n    public var dateDecodingStrategy: DateDecodingStrategy {\n        get { options.dateStrategy }\n        set { options.dateStrategy = newValue }\n    }\n\n    /// The strategy to use when decoding raw data from a ``JSValue``\n    public var dataDecodingStrategy: DataDecodingStrategy {\n        get { options.dataStrategy }\n        set { options.dataStrategy = newValue }\n    }\n\n    /// The strategy used by a decoder when it encounters exceptional floating-point values\n    public var nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy {\n        get { options.nonConformingStrategy }\n        set { options.nonConformingStrategy = newValue }\n    }\n\n    /// Decodes a ``JSValue`` into the provided `Decodable` type\n    /// - Parameters:\n    ///   - type: The type of the value to decode from the provided ``JSValue`` object\n    ///   - data: The ``JSValue`` to decode\n    /// - Returns: A value of the specified type.\n    ///\n    /// An error will be thrown from this method for two possible reasons:\n    /// 1. A type mismatch was found.\n    /// 2. A key was not found in the `data` field that is required in the `type` provided.\n    public func decode<T>(_ type: T.Type, from data: JSValue) throws -> T where T: Decodable {\n        let decoder = _JSValueDecoder(data: data, options: options)\n        return try decoder.decodeData(as: T.self)\n    }\n}\n\ntypealias CodingUserInfo = [CodingUserInfoKey: Any]\nprivate typealias Options = JSValueDecoder.Options\n\nprivate final class _JSValueDecoder {\n    var codingPath: [CodingKey] = []\n    var userInfo: CodingUserInfo = [:]\n    var options: Options\n    fileprivate var data: JSValue\n\n    init(data: JSValue, options: Options) {\n        self.data = data\n        self.options = options\n    }\n}\n\nextension _JSValueDecoder: Decoder {\n    func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {\n        guard let data = data as? JSObject else {\n            throw DecodingError.typeMismatch(JSObject.self, on: data, codingPath: codingPath)\n        }\n\n        return KeyedDecodingContainer(\n            KeyedContainer(\n                data: data,\n                codingPath: codingPath,\n                userInfo: userInfo,\n                options: options\n            )\n        )\n    }\n\n    func unkeyedContainer() throws -> UnkeyedDecodingContainer {\n        guard let data = data as? JSArray else {\n            throw DecodingError.typeMismatch(JSArray.self, on: data, codingPath: codingPath)\n        }\n\n        return UnkeyedContainer(data: data, codingPath: codingPath, userInfo: userInfo, options: options)\n    }\n\n    func singleValueContainer() throws -> SingleValueDecodingContainer {\n        SingleValueContainer(data: data, codingPath: codingPath, userInfo: userInfo, options: options)\n    }\n\n    // force casting is fine becasue we've already determined that T is the type in the case\n    // the swift standard library also force casts in their similar functions\n    // https://github.com/swiftlang/swift-foundation/blob/da80d51fa3e77f3e7ed57c4300a870689e755713/Sources/FoundationEssentials/JSON/JSONEncoder.swift#L1140\n    // swiftlint:disable force_cast\n    fileprivate func decodeData<T>(as type: T.Type) throws -> T where T: Decodable {\n        switch type {\n        case is Date.Type:\n            return try decodeDate() as! T\n        case is URL.Type:\n            return try decodeUrl() as! T\n        case is Data.Type:\n            return try decodeData() as! T\n        default:\n            return try T(from: self)\n        }\n    }\n    // swiftlint:enable force_cast\n\n    private func decodeDate() throws -> Date {\n        switch options.dateStrategy {\n        case .deferredToDate:\n            return try Date(from: self)\n        case .secondsSince1970:\n            guard let value = data as? NSNumber else { throw DecodingError.dataCorrupted(data, target: Double.self, codingPath: codingPath) }\n            return Date(timeIntervalSince1970: value.doubleValue)\n        case .millisecondsSince1970:\n            guard let value = data as? NSNumber else { throw DecodingError.dataCorrupted(data, target: Double.self, codingPath: codingPath) }\n            return Date(timeIntervalSince1970: value.doubleValue / Double(MSEC_PER_SEC))\n        case .iso8601:\n            guard let value = data as? String else { throw DecodingError.dataCorrupted(data, target: String.self, codingPath: codingPath) }\n            let formatter = ISO8601DateFormatter()\n            guard let date = formatter.date(from: value) else { throw DecodingError.dataCorrupted(value, target: Date.self, codingPath: codingPath) }\n            return date\n        case .formatted(let formatter):\n            guard let value = data as? String else { throw DecodingError.dataCorrupted(data, target: String.self, codingPath: codingPath) }\n            guard let date = formatter.date(from: value) else { throw DecodingError.dataCorrupted(value, target: Date.self, codingPath: codingPath) }\n            return date\n        case .custom(let decode):\n            return try decode(self)\n        @unknown default:\n            return try Date(from: self)\n        }\n    }\n\n    private func decodeUrl() throws -> URL {\n        guard let str = data as? String,\n              let url = URL(string: str)\n        else { throw DecodingError.dataCorrupted(data, target: URL.self, codingPath: codingPath) }\n\n        return url\n    }\n\n    private func decodeData() throws -> Data {\n        switch options.dataStrategy {\n        case .deferredToData:\n            return try Data(from: self)\n        case .base64:\n            guard let value = data as? String else { throw DecodingError.dataCorrupted(data, target: String.self, codingPath: codingPath) }\n            guard let data = Data(base64Encoded: value) else { throw DecodingError.dataCorrupted(value, target: Data.self, codingPath: codingPath) }\n            return data\n        case .custom(let decode):\n            return try decode(self)\n        @unknown default:\n            return try Data(from: self)\n        }\n    }\n}\n\nprivate final class KeyedContainer<Key> where Key: CodingKey {\n    var data: JSObject\n    var codingPath: [CodingKey]\n    var userInfo: CodingUserInfo\n    var allKeys: [Key]\n    var options: Options\n\n    init(data: JSObject, codingPath: [CodingKey], userInfo: CodingUserInfo, options: Options) {\n        self.data = data\n        self.codingPath = codingPath\n        self.userInfo = userInfo\n        self.allKeys = data.keys.compactMap(Key.init(stringValue:))\n        self.options = options\n    }\n}\n\nextension KeyedContainer: KeyedDecodingContainerProtocol {\n    func contains(_ key: Key) -> Bool {\n        allKeys.contains { $0.stringValue == key.stringValue }\n    }\n\n    func decodeNil(forKey key: Key) throws -> Bool {\n        data[key.stringValue] == nil || data[key.stringValue] is NSNull\n    }\n\n    func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Decodable {\n        guard let rawValue = data[key.stringValue] else {\n            throw DecodingError.keyNotFound(key, on: data, codingPath: codingPath)\n        }\n\n        var newPath = codingPath\n        newPath.append(key)\n        let decoder = _JSValueDecoder(data: rawValue, options: options)\n        return try decoder.decodeData(as: T.self)\n    }\n\n    func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {\n        var newPath = codingPath\n        newPath.append(key)\n        guard let data = data[key.stringValue] as? JSArray else {\n            throw DecodingError.typeMismatch(\n                JSArray.self,\n                on: data[key.stringValue] ?? \"null value\",\n                codingPath: newPath\n            )\n        }\n\n        return UnkeyedContainer(data: data, codingPath: newPath, userInfo: userInfo, options: options)\n    }\n\n    func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {\n        var newPath = codingPath\n        newPath.append(key)\n        guard let data = data[key.stringValue] as? JSObject else {\n            throw DecodingError.typeMismatch(\n                JSObject.self,\n                on: data[key.stringValue] ?? \"null value\",\n                codingPath: newPath\n            )\n        }\n\n        return KeyedDecodingContainer(KeyedContainer<NestedKey>(data: data, codingPath: newPath, userInfo: userInfo, options: options))\n    }\n\n    enum SuperKey: String, CodingKey { case `super` }\n\n    func superDecoder() throws -> Decoder {\n        var newPath = codingPath\n        newPath.append(SuperKey.super)\n        guard let data = data[SuperKey.super.stringValue] else {\n            throw DecodingError.keyNotFound(SuperKey.super, on: data, codingPath: newPath)\n        }\n\n        return _JSValueDecoder(data: data, options: options)\n    }\n\n    func superDecoder(forKey key: Key) throws -> Decoder {\n        var newPath = codingPath\n        newPath.append(key)\n        guard let data = data[key.stringValue] else {\n            throw DecodingError.keyNotFound(key, on: data, codingPath: newPath)\n        }\n\n        return _JSValueDecoder(data: data, options: options)\n    }\n}\n\nprivate final class UnkeyedContainer {\n    var data: JSArray\n    var codingPath: [CodingKey]\n    var userInfo: CodingUserInfo\n    private(set) var currentIndex = 0\n    var options: Options\n\n    init(data: JSArray, codingPath: [CodingKey], userInfo: CodingUserInfo, options: Options) {\n        self.data = data\n        self.codingPath = codingPath\n        self.userInfo = userInfo\n        self.options = options\n    }\n}\n\nextension UnkeyedContainer: UnkeyedDecodingContainer {\n    var count: Int? {\n        data.count\n    }\n\n    var isAtEnd: Bool {\n        currentIndex == data.endIndex\n    }\n\n    func decodeNil() throws -> Bool {\n        defer { currentIndex += 1 }\n        return data[currentIndex] is NSNull\n    }\n\n    func decode<T>(_ type: T.Type) throws -> T where T: Decodable {\n        defer { currentIndex += 1 }\n        let decoder = _JSValueDecoder(data: data[currentIndex], options: options)\n        return try decoder.decodeData(as: T.self)\n    }\n\n    func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {\n        defer { currentIndex += 1 }\n        guard let data = data[currentIndex] as? JSArray else {\n            throw DecodingError.typeMismatch(JSArray.self, on: data[currentIndex], codingPath: codingPath)\n        }\n\n        return UnkeyedContainer(data: data, codingPath: codingPath, userInfo: userInfo, options: options)\n    }\n\n    func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {\n        defer { currentIndex += 1 }\n        guard let data = data[currentIndex] as? JSObject else {\n            throw DecodingError.typeMismatch(JSObject.self, on: data[currentIndex], codingPath: codingPath)\n        }\n\n        return KeyedDecodingContainer(KeyedContainer(data: data, codingPath: codingPath, userInfo: userInfo, options: options))\n    }\n\n    func superDecoder() throws -> Decoder {\n        defer { currentIndex += 1 }\n        let data = data[currentIndex]\n        return _JSValueDecoder(data: data, options: options)\n    }\n}\n\nprivate final class SingleValueContainer {\n    var data: JSValue\n    var codingPath: [CodingKey]\n    var userInfo: CodingUserInfo\n    var options: Options\n\n    init(data: JSValue, codingPath: [CodingKey], userInfo: CodingUserInfo, options: Options) {\n        self.data = data\n        self.codingPath = codingPath\n        self.userInfo = userInfo\n        self.options = options\n    }\n}\n\nextension SingleValueContainer: SingleValueDecodingContainer {\n    func decodeNil() -> Bool {\n        return data is NSNull\n    }\n\n    private func cast<T>(to type: T.Type) throws -> T {\n        guard let data = data as? T else {\n            throw DecodingError.typeMismatch(type, on: data, codingPath: codingPath)\n        }\n\n        return data\n    }\n\n    private func castFloat<N>(to type: N.Type) throws -> N where N: FloatingPoint {\n        if let data = data as? String,\n           case let .convertFromString(positiveInfinity: pos, negativeInfinity: neg, nan: nan) = options.nonConformingStrategy {\n            switch data {\n            case pos:\n                return N.infinity\n            case neg:\n                return -N.infinity\n            case nan:\n                return N.nan\n            default:\n                throw DecodingError.typeMismatch(type, on: data, codingPath: codingPath)\n            }\n        }\n\n        let data = try cast(to: N.self)\n        if !data.isFinite, case .throw = options.nonConformingStrategy {\n            throw DecodingError.dataCorrupted(.init(codingPath: codingPath, debugDescription: \"\\(data) is a non-conforming floating point number\"))\n        }\n        return data\n    }\n\n    func decode(_ type: Bool.Type) throws -> Bool {\n        try cast(to: type)\n    }\n\n    func decode(_ type: String.Type) throws -> String {\n        try cast(to: type)\n    }\n\n    func decode(_ type: Double.Type) throws -> Double {\n        try castFloat(to: type)\n    }\n\n    func decode(_ type: Float.Type) throws -> Float {\n        try castFloat(to: type)\n    }\n\n    func decode(_ type: Int.Type) throws -> Int {\n        try cast(to: type)\n    }\n\n    func decode(_ type: Int8.Type) throws -> Int8 {\n        try cast(to: type)\n    }\n\n    func decode(_ type: Int16.Type) throws -> Int16 {\n        try cast(to: type)\n    }\n\n    func decode(_ type: Int32.Type) throws -> Int32 {\n        try cast(to: type)\n    }\n\n    func decode(_ type: Int64.Type) throws -> Int64 {\n        try cast(to: type)\n    }\n\n    func decode(_ type: UInt.Type) throws -> UInt {\n        try cast(to: type)\n    }\n\n    func decode(_ type: UInt8.Type) throws -> UInt8 {\n        try cast(to: type)\n    }\n\n    func decode(_ type: UInt16.Type) throws -> UInt16 {\n        try cast(to: type)\n    }\n\n    func decode(_ type: UInt32.Type) throws -> UInt32 {\n        try cast(to: type)\n    }\n\n    func decode(_ type: UInt64.Type) throws -> UInt64 {\n        try cast(to: type)\n    }\n\n    func decode<T>(_ type: T.Type) throws -> T where T: Decodable {\n        let decoder = _JSValueDecoder(data: data, options: options)\n        return try decoder.decodeData(as: T.self)\n    }\n}\n\nextension DecodingError {\n    static func typeMismatch(_ type: Any.Type, on data: any JSValue, codingPath: [CodingKey]) -> DecodingError {\n        return .typeMismatch(\n            type,\n            .init(\n                codingPath: codingPath,\n                debugDescription: \"\\(data) was unable to be cast to \\(type).\"\n            )\n        )\n    }\n\n    static func keyNotFound(_ key: any CodingKey, on data: any JSValue, codingPath: [CodingKey]) -> DecodingError {\n        return .keyNotFound(\n            key,\n            .init(\n                codingPath: codingPath,\n                debugDescription: \"Key \\(key.stringValue) not found in \\(data)\")\n        )\n    }\n\n    static func dataCorrupted<T>(_ value: any JSValue, target type: T.Type, codingPath: [CodingKey]) -> DecodingError where T: Decodable {\n        return .dataCorrupted(.init(codingPath: codingPath, debugDescription: \"\\(value) was not in the format expected for \\(T.self)\"))\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Codable/JSValueEncoder.swift",
    "content": "//\n//  JSValueEncoder.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 12/8/23.\n//  Copyright © 2023 Drifty Co. All rights reserved.\n//\n\nimport Foundation\nimport Combine\n\n/// An encoder than can encode ``JSValue`` objects from `Encodable` types\npublic final class JSValueEncoder: TopLevelEncoder {\n    /// The strategy to use when encoding `nil` values\n    public enum OptionalEncodingStrategy {\n        /// Encode `nil` values as `NSNull`\n        case explicitNulls\n        /// Excludes the value from the encoded object altogether\n        case undefined\n    }\n\n    /// The strategies available for encoding .nan, .infinity, and -.infinity\n    public enum NonConformingFloatEncodingStrategy: Equatable {\n        /// Throws an error when encountering an exceptional floating-point value\n        case `throw`\n        /// Converts to the provided strings\n        case convertToString(positiveInfinity: String, negativeInfinity: String, nan: String)\n        /// Encodes directly into an NSNumber\n        case deferred\n    }\n\n    /// The strategy to use when encoding `Date` values\n    public typealias DateEncodingStrategy = JSONEncoder.DateEncodingStrategy\n\n    /// The strategy to use when encoding `Data` values\n    public typealias DataEncodingStrategy = JSONEncoder.DataEncodingStrategy\n\n    fileprivate struct Options {\n        var optionalStrategy: OptionalEncodingStrategy\n        var dateStrategy: DateEncodingStrategy\n        var dataStrategy: DataEncodingStrategy\n        var nonConformingFloatStrategy: NonConformingFloatEncodingStrategy\n    }\n\n    private var options: Options\n\n    /// The strategy to use when encoding `nil` values\n    public var optionalEncodingStrategy: OptionalEncodingStrategy {\n        get { options.optionalStrategy }\n        set { options.optionalStrategy = newValue }\n    }\n\n    /// The strategy to use when encoding dates\n    public var dateEncodingStrategy: DateEncodingStrategy {\n        get { options.dateStrategy }\n        set { options.dateStrategy = newValue }\n    }\n\n    /// The encoding strategy to use when encoding raw data\n    public var dataEncodingStrategy: DataEncodingStrategy {\n        get { options.dataStrategy }\n        set { options.dataStrategy = newValue }\n    }\n\n    /// The encoding strategy to use when the encoder encounters exceptional floating-point values\n    public var nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy {\n        get { options.nonConformingFloatStrategy }\n        set { options.nonConformingFloatStrategy = newValue }\n    }\n\n    /// Creates a new `JSValueEncoder`\n    /// - Parameter optionalEncodingStrategy: The strategy to use when encoding `nil` values. Defaults to ``OptionalEncodingStrategy-swift.enum/undefined``\n    /// - Parameter dateEncodingStrategy: Defaults to `DateEncodingStrategy.deferredToDate`\n    /// - Parameter dataEncodingStrategy: Defaults to `DataEncodingStrategy.deferredToData`\n    /// - Parameter nonConformingFloatEncodingStategy: Defaults to ``NonConformingFloatEncodingStrategy-swift.enum/deferred``\n    public init(\n        optionalEncodingStrategy: OptionalEncodingStrategy = .undefined,\n        dateEncodingStrategy: DateEncodingStrategy = .deferredToDate,\n        dataEncodingStrategy: DataEncodingStrategy = .deferredToData,\n        nonConformingFloatEncodingStategy: NonConformingFloatEncodingStrategy = .deferred\n    ) {\n        self.options = .init(\n            optionalStrategy: optionalEncodingStrategy,\n            dateStrategy: dateEncodingStrategy,\n            dataStrategy: dataEncodingStrategy,\n            nonConformingFloatStrategy: nonConformingFloatEncodingStategy\n        )\n    }\n\n    /// Encodes an `Encodable` value to a ``JSValue``\n    /// - Parameter value: The value to encode to ``JSValue``\n    /// - Returns: The encoded ``JSValue``\n    /// - Throws: An error if the value could not be encoded as a ``JSValue``\n    public func encode<T>(_ value: T) throws -> JSValue where T: Encodable {\n        let encoder = _JSValueEncoder(options: options)\n        try encoder.encodeGeneric(value)\n        guard let value = encoder.data else {\n            throw EncodingError.invalidValue(\n                value,\n                .init(\n                    codingPath: encoder.codingPath,\n                    debugDescription: \"\\(value) was unable to be encoded as a JSValue\"\n                )\n            )\n        }\n\n        return value\n    }\n\n    /// Encodes an `Encodable` value to a ``JSObject``\n    /// - Parameter value: The value to encode to a ``JSObject``\n    /// - Returns: The encoded ``JSObject``\n    /// - Throws: An error if the value could not be encoded as a ``JSObject``\n    ///\n    /// This method is a convenience method for encoding an `Encodable` value to a ``JSObject``.\n    /// It is equivalent to calling ``encode(_:)`` and casting the result to a ``JSObject`` and\n    /// throwing an error if the cast fails.\n    public func encodeJSObject<T>(_ value: T) throws -> JSObject where T: Encodable {\n        guard let object = try encode(value) as? JSObject else {\n            throw EncodingError.invalidValue(\n                value,\n                .init(\n                    codingPath: [],\n                    debugDescription: \"\\(value) was unable to be encoded as a JSObject\"\n                )\n            )\n        }\n\n        return object\n    }\n}\n\nprivate protocol JSValueEncodingContainer {\n    var data: JSValue? { get }\n}\n\nprivate enum EncodingContainer: JSValueEncodingContainer {\n    case singleValue(SingleValueContainer)\n    case unkeyed(UnkeyedContainer)\n    case keyed(AnyKeyedContainer)\n\n    var data: JSValue? {\n        switch self {\n        case let .singleValue(container):\n            return container.data\n        case let .unkeyed(container):\n            return container.data\n        case let .keyed(container):\n            return container.data\n        }\n    }\n\n    var type: String {\n        switch self {\n        case .singleValue:\n            \"SingleValueContainer\"\n        case .unkeyed:\n            \"UnkeyedContainer\"\n        case .keyed:\n            \"KeyedContainer\"\n        }\n    }\n}\n\nprivate typealias Options = JSValueEncoder.Options\n\nprivate final class _JSValueEncoder: JSValueEncodingContainer {\n    var codingPath: [CodingKey] = []\n    var data: JSValue? {\n        containers.data\n    }\n\n    var options: Options\n\n    var userInfo: CodingUserInfo = [:]\n    fileprivate var containers: [EncodingContainer] = []\n\n    init(options: Options) {\n        self.options = options\n    }\n}\n\nextension Array: JSValueEncodingContainer where Element == EncodingContainer {\n    var data: JSValue? {\n        guard count != 0 else { return nil }\n        guard count != 1 else { return self[0].data }\n        var data: (any JSValue)?\n\n        for container in self {\n            if data == nil {\n                data = container.data\n            } else {\n                // The top-level container is\n                switch container {\n                case let .keyed(container):\n                    guard let obj = data as? JSObject else { break }\n                    data = obj.merging(container.object() ?? [:]) { $1 }\n                case let .unkeyed(container):\n                    guard var copy = data as? JSArray else { break }\n                    copy.append(contentsOf: container.array ?? [])\n                    data = copy\n                default:\n                    break\n                }\n            }\n        }\n\n        return data\n    }\n}\n\nprivate enum EncodedValue {\n    case value(any JSValue)\n    case nestedContainer(any JSValueEncodingContainer)\n}\n\nextension _JSValueEncoder: Encoder {\n    func addContainer(_ container: EncodingContainer) {\n        guard !containers.isEmpty else {\n            containers.append(container)\n            return\n        }\n\n        for existingContainer in containers {\n            switch (existingContainer, container) {\n            case (.unkeyed, .unkeyed), (.keyed, .keyed):\n                containers.append(container)\n            default:\n                preconditionFailure(\"Sibling top-level containers must be of the same type. Attempted to add a \\(container)\")\n            }\n        }\n    }\n\n    func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {\n        let container = KeyedContainer<Key>(\n            codingPath: codingPath,\n            userInfo: userInfo,\n            options: options\n        )\n        addContainer(.keyed(.init(container)))\n        return KeyedEncodingContainer(container)\n    }\n\n    func unkeyedContainer() -> UnkeyedEncodingContainer {\n        let container = UnkeyedContainer(\n            codingPath: codingPath,\n            userInfo: userInfo,\n            options: options\n        )\n        addContainer(.unkeyed(container))\n        return container\n    }\n\n    func singleValueContainer() -> SingleValueEncodingContainer {\n        let container = SingleValueContainer(\n            codingPath: codingPath,\n            userInfo: userInfo,\n            options: options\n        )\n        addContainer(.singleValue(container))\n        return container\n    }\n\n    fileprivate func encodeGeneric<T>(_ value: T) throws where T: Encodable {\n        switch value {\n        case let value as Date:\n            switch options.dateStrategy {\n            case .deferredToDate:\n                try value.encode(to: self)\n            case .millisecondsSince1970:\n                try (value.timeIntervalSince1970 * Double(MSEC_PER_SEC)).encode(to: self)\n            case .secondsSince1970:\n                try value.timeIntervalSince1970.encode(to: self)\n            case .iso8601:\n                let formattedDate = ISO8601DateFormatter().string(from: value)\n                try formattedDate.encode(to: self)\n            case .formatted(let formatter):\n                let formattedDate = formatter.string(from: value)\n                try formattedDate.encode(to: self)\n            case .custom(let encode):\n                try encode(value, self)\n            @unknown default:\n                try value.encode(to: self)\n            }\n        case let value as URL:\n            try value.absoluteString.encode(to: self)\n        case let value as Data:\n            switch options.dataStrategy {\n            case .deferredToData:\n                try value.encode(to: self)\n            case .base64:\n                try value.base64EncodedString().encode(to: self)\n            case .custom(let encode):\n                try encode(value, self)\n            @unknown default:\n                try value.encode(to: self)\n            }\n        default:\n            try value.encode(to: self)\n        }\n    }\n}\n\nprivate final class KeyedContainer<Key> where Key: CodingKey {\n    var object: JSObject? {\n        encodedKeyedValue?.reduce(into: [:]) { obj, next in\n            let (key, value) = next\n            switch value {\n            case .value(let value):\n                obj[key] = value\n            case .nestedContainer(let container):\n                obj[key] = container.data\n            }\n        }\n    }\n\n    var codingPath: [CodingKey]\n    var userInfo: CodingUserInfo\n    var options: Options\n    private var encodedKeyedValue: [String: EncodedValue]?\n\n    init(codingPath: [CodingKey], userInfo: CodingUserInfo, options: Options) {\n        self.codingPath = codingPath\n        self.userInfo = userInfo\n        self.options = options\n    }\n}\n\nextension KeyedContainer: KeyedEncodingContainerProtocol {\n    func insert(_ value: JSValue, for key: Key) {\n        insert(.value(value), for: key)\n    }\n\n    func insert<K: CodingKey>(_ encodedValue: EncodedValue, for key: K) {\n        if encodedKeyedValue == nil {\n            encodedKeyedValue = [key.stringValue: encodedValue]\n        } else {\n            encodedKeyedValue![key.stringValue] = encodedValue\n        }\n    }\n\n    func encodeNil(forKey key: Key) throws {\n        insert(NSNull(), for: key)\n    }\n\n    func encode<T>(_ value: T, forKey key: Key) throws where T: Encodable {\n        let encoder = _JSValueEncoder(options: options)\n        try encoder.encodeGeneric(value)\n        insert(.nestedContainer(encoder), for: key)\n    }\n\n    // This is a perectly valid name for this method. The underscore is to avoid a conflict with the\n    // protocol requirement.\n    // swiftlint:disable:next identifier_name\n    func _encodeIfPresent<T>(_ value: T?, forKey key: Key) throws where T: Encodable {\n        switch options.optionalStrategy {\n        case .explicitNulls:\n            if let value = value {\n                try encode(value, forKey: key)\n            } else {\n                try encodeNil(forKey: key)\n            }\n        case .undefined:\n            guard let value = value else { return }\n            try encode(value, forKey: key)\n        }\n    }\n\n    func encodeIfPresent<T>(_ value: T?, forKey key: Key) throws where T: Encodable {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: String?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Double?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Float?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Int?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Int8?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Int16?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Int32?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: Int64?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: UInt?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws {\n        try _encodeIfPresent(value, forKey: key)\n    }\n\n    func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {\n        var newPath = codingPath\n        newPath.append(key)\n\n        let nestedContainer = KeyedContainer<NestedKey>(\n            codingPath: newPath,\n            userInfo: userInfo,\n            options: options\n        )\n\n        insert(.nestedContainer(nestedContainer), for: key)\n        return KeyedEncodingContainer(nestedContainer)\n    }\n\n    func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {\n        var newPath = codingPath\n        newPath.append(key)\n        let nestedContainer = UnkeyedContainer(\n            codingPath: codingPath,\n            userInfo: userInfo,\n            options: options\n        )\n        insert(.nestedContainer(nestedContainer), for: key)\n        return nestedContainer\n    }\n\n    enum SuperKey: String, CodingKey {\n        case `super`\n    }\n\n    func superEncoder() -> Encoder {\n        let encoder = _JSValueEncoder(options: options)\n        insert(.nestedContainer(encoder), for: SuperKey.super)\n        return encoder\n    }\n\n    func superEncoder(forKey key: Key) -> Encoder {\n        let encoder = _JSValueEncoder(options: options)\n        insert(.nestedContainer(encoder), for: key)\n        return encoder\n    }\n}\n\nprivate class AnyKeyedContainer: JSValueEncodingContainer {\n    var data: JSValue? { object() }\n    var object: () -> JSObject?\n\n    init<Key>(_ keyedContainer: KeyedContainer<Key>) where Key: CodingKey {\n        object = { keyedContainer.object }\n    }\n}\n\nextension KeyedContainer: JSValueEncodingContainer {\n    var data: JSValue? { object }\n}\n\nprivate final class UnkeyedContainer {\n    var array: JSArray? {\n        encodedUnkeyedValue?.reduce(into: []) { arr, next in\n            switch next {\n            case .value(let value):\n                arr.append(value)\n            case .nestedContainer(let container):\n                guard let data = container.data else { return }\n                arr.append(data)\n            }\n        }\n    }\n\n    var codingPath: [CodingKey]\n    var userInfo: CodingUserInfo\n    var options: Options\n    private var encodedUnkeyedValue: [EncodedValue]?\n\n    init(codingPath: [CodingKey], userInfo: CodingUserInfo, options: Options) {\n        self.codingPath = codingPath\n        self.userInfo = userInfo\n        self.options = options\n    }\n}\n\nextension UnkeyedContainer: UnkeyedEncodingContainer {\n    private func append(_ value: any JSValue) {\n        append(.value(value))\n    }\n\n    private func append(_ value: EncodedValue) {\n        if encodedUnkeyedValue == nil {\n            encodedUnkeyedValue = [value]\n        } else {\n            encodedUnkeyedValue!.append(value)\n        }\n    }\n\n    var count: Int {\n        array?.count ?? 0\n    }\n\n    func encodeNil() throws {\n        append(NSNull())\n    }\n\n    func encode<T>(_ value: T) throws where T: Encodable {\n        let encoder = _JSValueEncoder(options: options)\n        try encoder.encodeGeneric(value)\n        append(.nestedContainer(encoder))\n    }\n\n    func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {\n        let nestedContainer = UnkeyedContainer(\n            codingPath: codingPath,\n            userInfo: userInfo,\n            options: options\n        )\n        append(.nestedContainer(nestedContainer))\n        return nestedContainer\n    }\n\n    func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {\n        let nestedContainer = KeyedContainer<NestedKey>(\n            codingPath: codingPath,\n            userInfo: userInfo,\n            options: options\n        )\n        append(.nestedContainer(nestedContainer))\n        return KeyedEncodingContainer(nestedContainer)\n    }\n\n    func superEncoder() -> Encoder {\n        let encoder = _JSValueEncoder(options: options)\n        append(.nestedContainer(encoder))\n        return encoder\n    }\n}\n\nextension UnkeyedContainer: JSValueEncodingContainer {\n    var data: JSValue? { array }\n}\n\nprivate final class SingleValueContainer {\n    var data: JSValue?\n    var codingPath: [CodingKey]\n    var userInfo: CodingUserInfo\n    var options: Options\n\n    init(codingPath: [CodingKey], userInfo: CodingUserInfo, options: Options) {\n        self.codingPath = codingPath\n        self.userInfo = userInfo\n        self.options = options\n    }\n}\n\nextension SingleValueContainer: SingleValueEncodingContainer {\n    func encodeNil() throws {\n        data = NSNull()\n    }\n\n    func encode(_ value: Bool) throws {\n        data = value\n    }\n\n    func encode(_ value: String) throws {\n        data = value\n    }\n\n    func encode(_ value: Double) throws {\n        try encodeFloat(value)\n    }\n\n    // swiftlint:disable force_cast\n    private func encodeFloat<N>(_ value: N) throws where N: FloatingPoint {\n        if value.isFinite {\n            data = value as! NSNumber\n        } else {\n            switch options.nonConformingFloatStrategy {\n            case .deferred:\n                data = value as! NSNumber\n            case let .convertToString(positiveInfinity: pos, negativeInfinity: neg, nan: nan):\n                if value == .infinity { data = pos }\n                if value == -.infinity { data = neg }\n                if value.isNaN { data = nan }\n            case .throw:\n                throw EncodingError.invalidValue(\n                    value,\n                    .init(codingPath: codingPath, debugDescription: \"Unable to encode \\(value) to JSValue\")\n                )\n            }\n        }\n    }\n    // swiftlint:enable force_cast\n\n    func encode(_ value: Float) throws {\n        try encodeFloat(value)\n    }\n\n    func encode(_ value: Int) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: Int8) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: Int16) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: Int32) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: Int64) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: UInt) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: UInt8) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: UInt16) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: UInt32) throws {\n        data = value as NSNumber\n    }\n\n    func encode(_ value: UInt64) throws {\n        data = value as NSNumber\n    }\n\n    func encode<T>(_ value: T) throws where T: Encodable {\n        let encoder = _JSValueEncoder(options: options)\n        try encoder.encodeGeneric(value)\n        data = encoder.data\n    }\n}\n\nextension SingleValueContainer: JSValueEncodingContainer {}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Data+Capacitor.swift",
    "content": "extension Data: CapacitorExtension {}\npublic extension CapacitorExtensionTypeWrapper where T == Data {\n\n    static func data(base64EncodedOrDataUrl: String) -> Data? {\n        if isBase64DataUrl(base64EncodedOrDataUrl) {\n            if let url = URL(string: base64EncodedOrDataUrl) {\n                do {\n                    return try T(contentsOf: url)\n                } catch {\n                    return nil\n                }\n            }\n            return nil\n        } else {\n            return T(base64Encoded: base64EncodedOrDataUrl)\n        }\n    }\n\n    private static func isBase64DataUrl(_ base64EncodedOrDataUrl: String) -> Bool {\n        return  base64EncodedOrDataUrl.starts(with: \"data:\") && base64EncodedOrDataUrl.contains(\"base64,\")\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/DocLinks.swift",
    "content": "import Foundation\n\nenum DocLinks: String {\n    case CAPPluginMethodSelector = \"plugins/ios/#defining-methods\"\n    case NSPhotoLibraryAddUsageDescription = \"https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW73\"\n    case NSPhotoLibraryUsageDescription = \"https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW17\"\n    case NSCameraUsageDescription = \"https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW24\"\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>NSPrincipalClass</key>\n\t<string></string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/JS.swift",
    "content": "import Foundation\n\npublic class JSDate {\n    @available(*, deprecated, message: \"No longer needed. Dates will be mapped to strings during serialization.\")\n    static func toString(_ date: Date) -> String {\n        let formatter = ISO8601DateFormatter()\n        return formatter.string(from: date)\n    }\n}\n\n@available(*, deprecated, renamed: \"PluginCallResultData\")\npublic typealias JSResultBody = [String: Any]\n\n/**\n * A call originating from JavaScript land\n */\ninternal struct JSCall {\n    let options: [String: Any]\n    let pluginId: String\n    let method: String\n    let callbackId: String\n}\n\ninternal protocol JSResultProtocol {\n    var call: JSCall { get }\n    var callbackID: String { get }\n    var pluginID: String { get }\n    var methodName: String { get }\n    func jsonPayload() -> String\n}\n\ninternal extension JSResultProtocol {\n    var callbackID: String {\n        return call.callbackId\n    }\n\n    var pluginID: String {\n        return call.pluginId\n    }\n\n    var methodName: String {\n        return call.method\n    }\n}\n\nprivate enum SerializationResult: String {\n    case undefined = \"undefined\"\n    case empty = \"{}\"\n}\n\n/**\n * A result of processing a JSCall, contains\n * a reference to the original call and the new result.\n */\n\ninternal struct JSResult: JSResultProtocol {\n    let call: JSCall\n    let result: PluginCallResult?\n\n    func jsonPayload() -> String {\n        guard let result = result else {\n            return SerializationResult.undefined.rawValue\n        }\n        do {\n            if let payload = try result.jsonRepresentation() {\n                return payload\n            }\n        } catch PluginCallResult.SerializationError.invalidObject {\n            CAPLog.print(\"[Capacitor Plugin Error] - \\(call.pluginId) - \\(call.method) - Unable to serialize plugin response as JSON.\" +\n                            \"Ensure that all data passed to success callback from module method is JSON serializable!\")\n        } catch {\n            CAPLog.print(\"Unable to serialize plugin response as JSON: \\(error.localizedDescription)\")\n        }\n        return SerializationResult.empty.rawValue\n    }\n}\n\ninternal extension JSResult {\n    init(call: JSCall, callResult: CAPPluginCallResult) {\n        self.call = call\n        self.result = callResult.resultData\n    }\n}\n\ninternal struct JSResultError: JSResultProtocol {\n    let call: JSCall\n    let errorMessage: String\n    let errorDescription: String\n    let errorCode: String?\n    let result: PluginCallResult\n\n    func jsonPayload() -> String {\n        var errorDictionary: [String: Any] = [\n            \"message\": self.errorMessage,\n            \"errorMessage\": self.errorMessage\n        ]\n        errorDictionary[\"code\"] = self.errorCode\n\n        do {\n            if let payload = try result.jsonRepresentation(includingFields: errorDictionary) {\n                CAPLog.print(\"ERROR MESSAGE: \", payload.prefix(512))\n                return payload\n            }\n        } catch PluginCallResult.SerializationError.invalidObject {\n            CAPLog.print(\"[Capacitor Plugin Error] - \\(call.pluginId) - \\(call.method) - Unable to serialize plugin response as JSON.\" +\n                            \"Ensure that all data passed to success callback from module method is JSON serializable!\")\n        } catch {\n            CAPLog.print(\"Unable to serialize plugin response as JSON: \\(error.localizedDescription)\")\n        }\n        return SerializationResult.empty.rawValue\n    }\n}\n\ninternal extension JSResultError {\n    init(call: JSCall, callError: CAPPluginCallError) {\n        self.call = call\n        errorMessage = callError.message\n        errorDescription = callError.error?.localizedDescription ?? \"\"\n        errorCode = callError.code\n        result = callError.resultData ?? .dictionary([:])\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/JSExport.swift",
    "content": "internal struct PluginHeaderMethod: Codable {\n    let name: String\n    let rtype: String?\n}\n\ninternal struct PluginHeader: Codable {\n    let name: String\n    let methods: [PluginHeaderMethod]\n}\n\n/**\n JSExport handles defining JS APIs that map to registered plugins and are responsible for proxying calls to our bridge.\n */\ninternal class JSExport {\n    static let catchallOptionsParameter = \"_options\"\n    static let callbackParameter = \"_callback\"\n\n    static func exportCapacitorGlobalJS(userContentController: WKUserContentController, isDebug: Bool, loggingEnabled: Bool, localUrl: String) throws {\n        let data = \"window.Capacitor = { DEBUG: \\(isDebug), isLoggingEnabled: \\(loggingEnabled), Plugins: {} }; window.WEBVIEW_SERVER_URL = '\\(localUrl)';\"\n        let userScript = WKUserScript(source: data, injectionTime: .atDocumentStart, forMainFrameOnly: true)\n        userContentController.addUserScript(userScript)\n    }\n\n    static func exportBridgeJS(userContentController: WKUserContentController) throws {\n        let capBundle = Bundle(for: Self.self)\n        guard let jsUrl = capBundle.url(forResource: \"native-bridge\", withExtension: \"js\") else {\n            CAPLog.print(\"ERROR: Required native-bridge.js file in Capacitor not found. Bridge will not function!\")\n            throw CapacitorBridgeError.errorExportingCoreJS\n        }\n        do {\n            try self.injectFile(fileURL: jsUrl, userContentController: userContentController)\n        } catch {\n            CAPLog.print(\"ERROR: Unable to read required native-bridge.js file from the Capacitor framework. Bridge will not function!\")\n            throw CapacitorBridgeError.errorExportingCoreJS\n        }\n    }\n\n    static func exportCordovaJS(userContentController: WKUserContentController) throws {\n        guard let cordovaUrl = Bundle.main.url(forResource: \"public/cordova\", withExtension: \"js\") else {\n            CAPLog.print(\"ERROR: Required cordova.js file not found. Cordova plugins will not function!\")\n            throw CapacitorBridgeError.errorExportingCoreJS\n        }\n        guard let cordovaPluginsUrl = Bundle.main.url(forResource: \"public/cordova_plugins\", withExtension: \"js\") else {\n            CAPLog.print(\"ERROR: Required cordova_plugins.js file not found. Cordova plugins  will not function!\")\n            throw CapacitorBridgeError.errorExportingCoreJS\n        }\n        do {\n            try self.injectFile(fileURL: cordovaUrl, userContentController: userContentController)\n            try self.injectFile(fileURL: cordovaPluginsUrl, userContentController: userContentController)\n        } catch {\n            CAPLog.print(\"ERROR: Unable to read required cordova files. Cordova plugins will not function!\")\n            throw CapacitorBridgeError.errorExportingCoreJS\n        }\n    }\n\n    static func exportMiscFileJS(paths: [String], userContentController: WKUserContentController) {\n        for path in paths {\n            if let miscJSFilePath = Bundle.main.url(forResource: \"public/\\(path.replacingOccurrences(of: \".js\", with: \"\"))\", withExtension: \"js\") {\n                do {\n                    try self.injectFile(fileURL: miscJSFilePath, userContentController: userContentController)\n                } catch {\n                    CAPLog.print(\"WARNING: Unable to inject js from path \\(miscJSFilePath)\")\n                }\n            } else {\n                CAPLog.print(\"WARNING: Unable to inject js from path \\(path)\")\n            }\n        }\n    }\n\n    /**\n     Export the JS required to implement the given plugin.\n     */\n    static func exportJS(for plugin: CapacitorPlugin, in userContentController: WKUserContentController) {\n        var lines = [String]()\n\n        lines.append(\"\"\"\n                    (function(w) {\n                    var a = (w.Capacitor = w.Capacitor || {});\n                    var p = (a.Plugins = a.Plugins || {});\n                    var t = (p['\\(plugin.jsName)'] = {});\n                    t.addListener = function(eventName, callback) {\n                    return w.Capacitor.addListener('\\(plugin.jsName)', eventName, callback);\n                    }\n                    t.removeAllListeners = function() {\n                    return w.Capacitor.nativePromise('\\(plugin.jsName)', 'removeAllListeners');\n                    }\n                    \"\"\")\n\n        for method in plugin.pluginMethods {\n            lines.append(generateMethod(pluginClassName: plugin.jsName, method: method))\n        }\n\n        lines.append(\"\"\"\n            })(window);\n            \"\"\")\n        if let data = try? JSONEncoder().encode(createPluginHeader(for: plugin)),\n           let header = String(data: data, encoding: .utf8) {\n            lines.append(\"\"\"\n                (function(w) {\n                var a = (w.Capacitor = w.Capacitor || {});\n                var h = (a.PluginHeaders = a.PluginHeaders || []);\n                h.push(\\(header));\n                })(window);\n                \"\"\")\n        }\n        let js = lines.joined(separator: \"\\n\")\n        let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: true)\n        userContentController.addUserScript(userScript)\n    }\n\n    private static func createPluginHeader(for plugin: CapacitorPlugin) -> PluginHeader? {\n        let methods = [\n            PluginHeaderMethod(name: \"addListener\", rtype: nil),\n            PluginHeaderMethod(name: \"removeListener\", rtype: nil),\n            PluginHeaderMethod(name: \"removeAllListeners\", rtype: \"promise\"),\n            PluginHeaderMethod(name: \"checkPermissions\", rtype: \"promise\"),\n            PluginHeaderMethod(name: \"requestPermissions\", rtype: \"promise\")\n        ]\n\n        return PluginHeader(\n            name: plugin.jsName,\n            methods: methods + plugin.pluginMethods.map(createPluginHeaderMethod)\n        )\n\n    }\n\n    private static func createPluginHeaderMethod(method: CAPPluginMethod) -> PluginHeaderMethod {\n        var rtype = method.returnType\n        if rtype == \"none\" {\n            rtype = nil\n        }\n        return PluginHeaderMethod(name: method.name, rtype: rtype)\n    }\n\n    private static func generateMethod(pluginClassName: String, method: CAPPluginMethod) -> String {\n        let methodName = method.name!\n        let returnType = method.returnType!\n        var paramList = [String]()\n\n        // add the catch-all\n        // options argument which takes a full object and converts each\n        // key/value pair into an option for plugin call.\n        paramList.append(catchallOptionsParameter)\n\n        // Automatically add the _callback param if returning data through a callback\n        if returnType == CAPPluginReturnCallback {\n            paramList.append(callbackParameter)\n        }\n\n        // Create a param string of the form \"param1, param2, param3\"\n        let paramString = paramList.joined(separator: \", \")\n\n        // Generate the argument object that will be sent on each call\n        let argObjectString = catchallOptionsParameter\n\n        var lines = [String]()\n\n        // Create the function declaration\n        lines.append(\"t['\\(method.name!)'] = function(\\(paramString)) {\")\n\n        // Create the call to Capacitor ...\n        if returnType == CAPPluginReturnNone {\n            // ...using none\n            lines.append(\"\"\"\n                    return w.Capacitor.nativeCallback('\\(pluginClassName)', '\\(methodName)', \\(argObjectString));\n                    \"\"\")\n        } else if returnType == CAPPluginReturnPromise {\n\n            // ...using a promise\n            lines.append(\"\"\"\n                    return w.Capacitor.nativePromise('\\(pluginClassName)', '\\(methodName)', \\(argObjectString));\n                    \"\"\")\n        } else if returnType == CAPPluginReturnCallback {\n            // ...using a callback\n            lines.append(\"\"\"\n                    return w.Capacitor.nativeCallback('\\(pluginClassName)', '\\(methodName)', \\(argObjectString), \\(callbackParameter));\n                    \"\"\")\n        } else {\n            CAPLog.print(\"Error: plugin method return type \\(returnType) is not supported!\")\n        }\n\n        // Close the function\n        lines.append(\"}\")\n        return lines.joined(separator: \"\\n\")\n    }\n\n    static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws {\n        if let pluginsJSFolder = Bundle.main.url(forResource: \"public/plugins\", withExtension: nil) {\n            self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController)\n        }\n    }\n\n    static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) {\n        let fileManager = FileManager.default\n        do {\n            let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: [])\n            for fileURL in fileURLs {\n                if fileURL.hasDirectoryPath {\n                    injectFilesForFolder(folder: fileURL, userContentController: userContentController)\n                } else {\n                    try self.injectFile(fileURL: fileURL, userContentController: userContentController)\n                }\n            }\n        } catch {\n            CAPLog.print(\"Error while enumerating files\")\n        }\n    }\n\n    static func injectFile(fileURL: URL, userContentController: WKUserContentController) throws {\n        do {\n            let data = try String(contentsOf: fileURL, encoding: .utf8)\n            let userScript = WKUserScript(source: data, injectionTime: .atDocumentStart, forMainFrameOnly: true)\n            userContentController.addUserScript(userScript)\n        } catch {\n            CAPLog.print(\"Unable to inject js file\")\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/JSTypes.swift",
    "content": "import Foundation\n\n// declare our empty protocol, and conformance, for typing\npublic protocol JSValue {}\nextension String: JSValue {}\nextension Bool: JSValue {}\nextension Int: JSValue {}\nextension Float: JSValue {}\nextension Double: JSValue {}\nextension NSNumber: JSValue {}\nextension NSNull: JSValue {}\nextension Array: JSValue {}\nextension Date: JSValue {}\nextension Dictionary: JSValue where Key == String, Value == JSValue {}\n\n// convenience aliases\npublic typealias JSObject = [String: JSValue]\npublic typealias JSArray = [JSValue]\n\n// string types\npublic protocol JSStringContainer {\n    func getString(_ key: String, _ defaultValue: String) -> String\n    func getString(_ key: String) -> String?\n}\n\nextension JSStringContainer {\n    public func getString(_ key: String, _ defaultValue: String) -> String {\n        return getString(key) ?? defaultValue\n    }\n}\n\n// boolean types\npublic protocol JSBoolContainer {\n    func getBool(_ key: String, _ defaultValue: Bool) -> Bool\n    func getBool(_ key: String) -> Bool?\n}\n\nextension JSBoolContainer {\n    public func getBool(_ key: String, _ defaultValue: Bool) -> Bool {\n        return getBool(key) ?? defaultValue\n    }\n}\n\n// integer types\npublic protocol JSIntContainer {\n    func getInt(_ key: String, _ defaultValue: Int) -> Int\n    func getInt(_ key: String) -> Int?\n}\n\nextension JSIntContainer {\n    public func getInt(_ key: String, _ defaultValue: Int) -> Int {\n        return getInt(key) ?? defaultValue\n    }\n}\n\n// float types\npublic protocol JSFloatContainer {\n    func getFloat(_ key: String, _ defaultValue: Float) -> Float\n    func getFloat(_ key: String) -> Float?\n}\n\nextension JSFloatContainer {\n    public func getFloat(_ key: String, _ defaultValue: Float) -> Float {\n        return getFloat(key) ?? defaultValue\n    }\n}\n\n// double types\npublic protocol JSDoubleContainer {\n    func getDouble(_ key: String, _ defaultValue: Double) -> Double\n    func getDouble(_ key: String) -> Double?\n}\n\nextension JSDoubleContainer {\n    public func getDouble(_ key: String, _ defaultValue: Double) -> Double {\n        return getDouble(key) ?? defaultValue\n    }\n}\n\n// date types\npublic protocol JSDateContainer {\n    func getDate(_ key: String, _ defaultValue: Date) -> Date\n    func getDate(_ key: String) -> Date?\n}\n\nextension JSDateContainer {\n    public func getDate(_ key: String, _ defaultValue: Date) -> Date {\n        return getDate(key) ?? defaultValue\n    }\n}\n\n// array types\npublic protocol JSArrayContainer {\n    func getArray(_ key: String, _ defaultValue: JSArray) -> JSArray\n    func getArray<T>(_ key: String, _ ofType: T.Type) -> [T]?\n    func getArray(_ key: String) -> JSArray?\n}\n\nextension JSArrayContainer {\n    public func getArray(_ key: String, _ defaultValue: JSArray) -> JSArray {\n        return getArray(key) ?? defaultValue\n    }\n\n    public func getArray<T>(_ key: String, _ ofType: T.Type) -> [T]? {\n        return getArray(key) as? [T]\n    }\n}\n\n// dictionary types\npublic protocol JSObjectContainer {\n    func getObject(_ key: String, _ defaultValue: JSObject) -> JSObject\n    func getObject(_ key: String) -> JSObject?\n}\n\nextension JSObjectContainer {\n    public func getObject(_ key: String, _ defaultValue: JSObject) -> JSObject {\n        return getObject(key) ?? defaultValue\n    }\n}\n\npublic protocol JSValueContainer: JSStringContainer, JSBoolContainer, JSIntContainer, JSFloatContainer,\n                                  JSDoubleContainer, JSDateContainer, JSArrayContainer, JSObjectContainer {\n    static var jsDateFormatter: ISO8601DateFormatter { get }\n    var jsObjectRepresentation: JSObject { get }\n}\n\nextension JSValueContainer {\n    public func getValue(_ key: String) -> JSValue? {\n        return jsObjectRepresentation[key]\n    }\n\n    @available(*, message: \"All values returned conform to JSValue, use getValue(_:) instead.\", renamed: \"getValue(_:)\")\n    public func getAny(_ key: String) -> Any? {\n        return getValue(key)\n    }\n\n    public func getString(_ key: String) -> String? {\n        return jsObjectRepresentation[key] as? String\n    }\n\n    public func getBool(_ key: String) -> Bool? {\n        return jsObjectRepresentation[key] as? Bool\n    }\n\n    public func getInt(_ key: String) -> Int? {\n        return jsObjectRepresentation[key] as? Int\n    }\n\n    public func getFloat(_ key: String) -> Float? {\n        if let floatValue = jsObjectRepresentation[key] as? Float {\n            return floatValue\n        } else if let doubleValue = jsObjectRepresentation[key] as? Double {\n            return Float(doubleValue)\n        }\n        return nil\n    }\n\n    public func getDouble(_ key: String) -> Double? {\n        return jsObjectRepresentation[key] as? Double\n    }\n\n    public func getDate(_ key: String) -> Date? {\n        if let isoString = jsObjectRepresentation[key] as? String {\n            return Self.jsDateFormatter.date(from: isoString)\n        }\n        return jsObjectRepresentation[key] as? Date\n    }\n\n    public func getArray(_ key: String) -> JSArray? {\n        return jsObjectRepresentation[key] as? JSArray\n    }\n\n    public func getObject(_ key: String) -> JSObject? {\n        return jsObjectRepresentation[key] as? JSObject\n    }\n\n    /// Decodes a value of the given type for the given key.\n    /// - Parameters:\n    ///   - type: The type of the value to decode.\n    ///   - key: The key that the decoded value is associated with.\n    ///   - decoder: The decoder to use to decode the value. Defaults to `JSValueDecoder()`.\n    /// - Returns: A value of the requested type, if present for the given key and convertible to the requested type.\n    /// - Throws: `DecodingError` if the encountered encoded value is corrupted.\n    public func decode<T: Decodable>(_ type: T.Type, for key: String, with decoder: JSValueDecoder = JSValueDecoder()) throws -> T {\n        try decoder.decode(type, from: jsObjectRepresentation[key] ?? [:])\n    }\n}\n\n@objc protocol BridgedJSValueContainer: NSObjectProtocol {\n    static var jsDateFormatter: ISO8601DateFormatter { get }\n    var dictionaryRepresentation: NSDictionary { get }\n}\n\n/*\n Simply casting objects from foundation class clusters (such as __NSArrayM)\n doesn't work with the JSValue protocol and will always fail. So we need to\n recursively and explicitly convert each value in the dictionary.\n */\npublic enum JSTypes {}\nextension JSTypes {\n    public static func coerceDictionaryToJSObject(_ dictionary: NSDictionary?, formattingDatesAsStrings: Bool = false) -> JSObject? {\n        return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject\n    }\n\n    public static func coerceDictionaryToJSObject(_ dictionary: [AnyHashable: Any]?, formattingDatesAsStrings: Bool = false) -> JSObject? {\n        return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject\n    }\n\n    public static func coerceArrayToJSArray(_ array: [Any]?, formattingDatesAsStrings: Bool = false) -> JSArray? {\n        return array?.compactMap { coerceToJSValue($0, formattingDates: formattingDatesAsStrings) }\n    }\n}\n\nprivate let dateStringFormatter = ISO8601DateFormatter()\n\n// We need a large switch statement because we have a lot of types.\n// swiftlint:disable:next cyclomatic_complexity\nprivate func coerceToJSValue(_ value: Any?, formattingDates: Bool) -> JSValue? {\n    guard let value = value else {\n        return nil\n    }\n    switch value {\n    case let stringValue as String:\n        return stringValue\n    case let numberValue as NSNumber:\n        return numberValue\n    case let boolValue as Bool:\n        return boolValue\n    case let intValue as Int:\n        return intValue\n    case let floatValue as Float:\n        return floatValue\n    case let doubleValue as Double:\n        return doubleValue\n    case let dateValue as Date:\n        if formattingDates {\n            return dateStringFormatter.string(from: dateValue)\n        }\n        return dateValue\n    case let nullValue as NSNull:\n        return nullValue\n    case let arrayValue as NSArray:\n        return arrayValue.compactMap { coerceToJSValue($0, formattingDates: formattingDates) }\n    case let dictionaryValue as NSDictionary:\n        let keys = dictionaryValue.allKeys.compactMap { $0 as? String }\n        var result: JSObject = [:]\n        for key in keys {\n            result[key] = coerceToJSValue(dictionaryValue[key], formattingDates: formattingDates)\n        }\n        return result\n    default:\n        return nil\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/KeyPath.swift",
    "content": "import Foundation\n\npublic struct KeyPath {\n    var segments: [String]\n    var isEmpty: Bool { return segments.isEmpty }\n    var path: String {\n        return segments.joined(separator: \".\")\n    }\n\n    // initializers\n    init(_ string: String) {\n        self.segments = string.components(separatedBy: \".\")\n    }\n\n    init(segments: [String]) {\n        self.segments = segments\n    }\n\n    // returns a tuple of the first segment and the remaining key path. result is nil if the key path has no segments.\n    func headAndRemainder() -> (head: String, remainder: KeyPath)? {\n        guard !isEmpty else {\n            return nil\n        }\n        var paths = segments\n        let head = paths.removeFirst()\n        return (head, KeyPath(segments: paths))\n    }\n}\n\nextension KeyPath: ExpressibleByStringLiteral {\n    public init(stringLiteral value: String) {\n        self.init(value)\n    }\n\n    public init(unicodeScalarLiteral value: String) {\n        self.init(value)\n    }\n\n    public init(extendedGraphemeClusterLiteral value: String) {\n        self.init(value)\n    }\n}\n\nextension JSObject {\n    public subscript(keyPath keyPath: KeyPath) -> JSValue? {\n        switch keyPath.headAndRemainder() {\n        case nil: // path is empty\n            return nil\n        case let (head, remainder)? where remainder.isEmpty: // reached the end of the path\n            return self[head]\n        case let (head, remainder)?: // we have at least one level to traverse\n            switch self[head] {\n            case let childObject as JSObject: // iterate down the next level\n                return childObject[keyPath: remainder]\n            default: // not an object, can't go any deeper\n                return nil\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/KeyValueStore.swift",
    "content": "//\n//  KeyValueStore.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 1/5/24.\n//  Copyright © 2024 Drifty Co. All rights reserved.\n//\n\nimport Foundation\n\n/// A generic KeyValueStore that allows storing and retrieving values associated with string keys.\n/// The store supports both ephemeral (in-memory) storage and persistent (file-based) storage backends\n/// by default, however it can also take anything that conforms to ``KeyValueStoreBackend`` as\n/// a backend.\n///\n/// This class provides methods to get, set and delete key-value pairs for any type of value, provided the\n/// types conform to `Codable`. The default ``Backend/ephemeral`` and ``Backend/persistent(suiteName:)``\n/// backends are thread-safe.\n///\n/// ## Usage Examples\n///\n/// ### Non-throwing API\n/// ```swift\n/// let store = KeyValueStore.standard\n/// // Set\n/// store[\"key\"] = \"value\"\n///\n/// // Get\n/// if let value = store[\"key\", as: String.self] {\n///   // Do something with value\n/// }\n///\n/// // Delete\n/// // The type here is a required argument because\n/// // it is unable to be inferred\n/// store[\"key\", as: String.self] = nil\n/// // or\n/// store[\"key\"] = nil as String?\n/// ```\n///\n///\n/// ### Throwing API\n///\n/// ```swift\n/// let store = KeyValueStore.standard\n/// do {\n///     // Set\n///     try store.set(\"key\", value: \"value\")\n///\n///     // Get\n///     if let value = try store.get(\"key\", as: String.self) {\n///         // Do something with value\n///     }\n///\n///     // Delete\n///     try store.delete(\"key\")\n/// } catch {\n///     print(error.localizedDescription)\n/// }\n/// ```\n///\n/// ### Throwing vs Non-throwing\n///\n/// Of the built-in backends, both ``Backend/ephemeral`` and ``Backend/persistent(suiteName:)`` will throw in the following cases:\n/// * The data read from the file retrieved during ``get(_:as:)`` is unable to be decoded as the type provided.\n/// * The value provided to ``set(_:value:)`` encounters an error during encoding.\n///     * This is more likely to happen with types that have custom `Encodable` implementations\n///\n/// ``Backend/persistent(suiteName:)`` will throw for the following additional cases:\n/// * A file is unable to be read from disk during ``get(_:as:)``\n///     * The existence of the file on disk is checked before attempting to read the file, so out of the\n///       [possible file reading errors](https://developer.apple.com/documentation/foundation/1448136-nserror_codes#file-reading-errors),\n///       the only likely candidate would be\n///       [NSFileReadCorruptFileError](https://developer.apple.com/documentation/foundation/1448136-nserror_codes/nsfilereadcorruptfileerror).\n///       In practice, this should never happen since writes happen atomically.\n/// * The data from the value encoded in ``set(_:value:)`` is unable to be written to disk\n///     * Of the [possible file writing errors](https://developer.apple.com/documentation/foundation/1448136-nserror_codes#file-writing-errors),\n///       the only likely candidates are\n///       [NSFileWriteInvalidFileNameError](https://developer.apple.com/documentation/foundation/1448136-nserror_codes/nsfilewriteinvalidfilenameerror)\n///       if the key provided makes for an invalid file name and\n///       [NSFileWriteOutOfSpaceError](https://developer.apple.com/documentation/foundation/1448136-nserror_codes/nsfilewriteoutofspaceerror)\n///       if the user has no space left on disk\n///\n/// The throwing API should be used in cases where detailed error information is needed for logging or diagnostics. The non-throwing API should be used\n/// in cases where silent failure is preferred.\npublic class KeyValueStore {\n\n    /// The built-in storage backends\n    public enum Backend {\n        /// An in-memory backing store\n        case ephemeral\n        /// A persistent file-based backing store using the\n        /// `suiteName` as an identifier for the collection of files\n        case persistent(suiteName: String)\n    }\n\n    private let backend: any KeyValueStoreBackend\n\n    /// Creates an instance of ``KeyValueStore`` with a custom backend\n    /// - Parameter backend: The custom backend implementation\n    public init(backend: any KeyValueStoreBackend) {\n        self.backend = backend\n    }\n\n    /// Creates an instance of ``KeyValueStore`` with the provided built-in ``Backend``\n    /// - Parameter type: The type of ``Backend`` to use\n    public init(type: Backend) {\n        switch type {\n        case .ephemeral:\n            backend = InMemoryStore()\n        case .persistent(suiteName: let name):\n            backend = FileStore.with(name: name)\n        }\n    }\n\n    /// Creates an instance of ``KeyValueStore`` with ``Backend/persistent(suiteName:)``\n    /// - Parameter suiteName: The suite name to provide ``Backend/persistent(suiteName:)``\n    public convenience init(suiteName: String) {\n        self.init(type: .persistent(suiteName: suiteName))\n    }\n\n    /// Retrieves a value of the specified type and key\n    /// - Parameters:\n    ///   - key: The unique identifier for the value\n    ///   - type: The expected type of the value being retried\n    /// - Returns: A decoded value of the given type or `nil` if there is no such value\n    public func `get`<T>(_ key: String, as type: T.Type = T.self) throws -> T? where T: Decodable {\n        try backend.get(key, as: type)\n    }\n\n    /// Stores the value under the specified key\n    /// - Parameters:\n    ///   - key: The unique identifier\n    ///   - value: The value to be stored\n    public func `set`<T>(_ key: String, value: T) throws where T: Encodable {\n        try backend.set(key, value: value)\n    }\n\n    /// Deletes the value for the specified key\n    public func `delete`(_ key: String) throws {\n        try backend.delete(key)\n    }\n\n    /// Convenience for accessing and modifying values in the store without calling ``get(_:as:)``, ``set(_:value:)``, or ``delete(_:)``\n    /// - Parameters:\n    ///     - key: The unique identifier for the value to access or modify\n    ///     - type: The type the value is stored as\n    ///\n    /// This method is only really necessary when accessing a key and the type cannot be inferred from it's context.\n    /// ```swift\n    /// let store = KeyValueStore.standard\n    ///\n    /// // Get\n    /// let value = store[\"key\", as: String.self]\n    ///\n    /// // If the type can be inferred then it may be omitted\n    /// let value: String? = store[\"key\"]\n    /// let value = store[\"key\"] as String?\n    /// let value = store[\"key\"] ?? \"default\"\n    ///\n    /// // Delete\n    /// store[\"key\", as: String.self] = nil\n    /// store[\"key\"] = nil as String?\n    /// ```\n    public subscript<T> (_ key: String, as type: T.Type = T.self) -> T? where T: Codable {\n        get { try? self.get(key) }\n        set {\n            if let newValue {\n                try? self.set(key, value: newValue)\n            } else {\n                try? self.delete(key)\n            }\n        }\n    }\n\n    /// A shared persistent instance of ``KeyValueStore``\n    public static let standard = KeyValueStore(type: .persistent(suiteName: \"standard\"))\n}\n\npublic protocol KeyValueStoreBackend {\n    func `get`<T>(_ key: String, as type: T.Type) throws -> T? where T: Decodable\n    func `set`<T>(_ key: String, value: T) throws where T: Encodable\n    func `delete`(_ key: String) throws\n}\n\nprivate class FileStore: KeyValueStoreBackend {\n    private let cache = ConcurrentDictionary<Data>()\n    private let decoder = JSONDecoder()\n    private let encoder = JSONEncoder()\n    private let baseUrl: URL\n\n    private init(baseUrl: URL) {\n        self.baseUrl = baseUrl\n    }\n\n    func get<T>(_ key: String, as type: T.Type) throws -> T? where T: Decodable {\n        if let cached = cache[key],\n           let value = try? decoder.decode(type, from: cached) {\n            return value\n        }\n\n        let fileCacheLocation = baseUrl.appendingPathComponent(key)\n        guard FileManager.default.fileExists(atPath: fileCacheLocation.path) else { return nil }\n\n        let data = try Data(contentsOf: fileCacheLocation)\n        let decoded = try decoder.decode(type, from: data)\n\n        cache[key] = data\n        return decoded\n    }\n\n    func set<T>(_ key: String, value: T) throws where T: Encodable {\n        let encoded = try encoder.encode(value)\n        try encoded.write(to: url(for: key), options: .atomic)\n        cache[key] = encoded\n    }\n\n    func delete(_ key: String) throws {\n        cache[key] = nil\n        try FileManager.default.removeItem(at: url(for: key))\n    }\n\n    private func url(for key: String) -> URL {\n        baseUrl.appendingPathComponent(key)\n    }\n\n    private static let instances = ConcurrentDictionary<FileStore>()\n\n    // This ensures we essentially have singletons for accessing file based resources\n    // so we don't have a scenario where two separate instances may be writing to\n    // the same files.\n    static func with(name: String) -> FileStore {\n        if let existing = instances[name] { return existing }\n        guard let library = try? FileManager\n                .default\n                .url(\n                    for: .libraryDirectory,\n                    in: .userDomainMask,\n                    appropriateFor: nil,\n                    create: true\n                )\n        else { fatalError(\"⚡️ ❌ Library URL unable to be accessed or created by the current application. This is an impossible state.\") }\n\n        let url = library.appendingPathComponent(\"kvstore\").appendingPathComponent(name)\n\n        // Create the folder if it doesn't exist. This should never throw for the current base directory, so we ignore the exception.\n        try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)\n\n        let new = FileStore(baseUrl: url)\n        instances[name] = new\n        return new\n    }\n}\n\nprivate class InMemoryStore: KeyValueStoreBackend {\n    private let storage = ConcurrentDictionary<Data>()\n    private let decoder = JSONDecoder()\n    private let encoder = JSONEncoder()\n\n    func get<T>(_ key: String, as type: T.Type) throws -> T? where T: Decodable {\n        guard let data = storage[key] else { return nil }\n        return try decoder.decode(type, from: data)\n    }\n\n    func set<T>(_ key: String, value: T) throws where T: Encodable {\n        let data = try encoder.encode(value)\n        storage[key] = data\n    }\n\n    func delete(_ key: String) {\n        storage[key] = nil\n    }\n}\n\nclass ConcurrentDictionary<Value> {\n    typealias StorageType = [String: Value]\n    private var storage: StorageType\n    private let lock = NSLock()\n\n    init(_ initial: StorageType = [:]) {\n        storage = initial\n    }\n\n    subscript (_ key: String) -> Value? {\n        get {\n            lock.withLock {\n                storage[key]\n            }\n        }\n\n        set {\n            lock.withLock {\n                storage[key] = newValue\n            }\n        }\n    }\n\n    func withLock<T>(_ body: (_ storage: inout StorageType) -> T) -> T {\n        lock.withLock {\n            body(&storage)\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/NotificationHandlerProtocol.swift",
    "content": "import Foundation\n\n@objc(CAPNotificationHandlerProtocol) public protocol NotificationHandlerProtocol {\n    func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions\n    func didReceive(response: UNNotificationResponse)\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/NotificationRouter.swift",
    "content": "import Foundation\n\n@objc(CAPNotificationRouter) public class NotificationRouter: NSObject, UNUserNotificationCenterDelegate {\n    var handleApplicationNotifications: Bool {\n        get {\n            return UNUserNotificationCenter.current().delegate === self\n        }\n        set {\n            let center = UNUserNotificationCenter.current()\n\n            if newValue {\n                center.delegate = self\n            } else if center.delegate === self {\n                center.delegate = nil\n            }\n        }\n    }\n\n    public weak var pushNotificationHandler: NotificationHandlerProtocol? {\n        didSet {\n            if pushNotificationHandler != nil, oldValue != nil {\n                CAPLog.print(\"Push notification handler overriding previous instance: \\(String(describing: type(of: oldValue)))\")\n            }\n        }\n    }\n\n    public weak var localNotificationHandler: NotificationHandlerProtocol? {\n        didSet {\n            if localNotificationHandler != nil, oldValue != nil {\n                CAPLog.print(\"Local notification handler overriding previous instance: \\(String(describing: type(of: oldValue)))\")\n            }\n        }\n    }\n\n    public func userNotificationCenter(_ center: UNUserNotificationCenter,\n                                       willPresent notification: UNNotification,\n                                       withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {\n        let presentationOptions: UNNotificationPresentationOptions?\n\n        if notification.request.trigger?.isKind(of: UNPushNotificationTrigger.self) == true {\n            presentationOptions = pushNotificationHandler?.willPresent(notification: notification)\n        } else {\n            presentationOptions = localNotificationHandler?.willPresent(notification: notification)\n        }\n\n        completionHandler(presentationOptions ?? [])\n    }\n\n    public func userNotificationCenter(_ center: UNUserNotificationCenter,\n                                       didReceive response: UNNotificationResponse,\n                                       withCompletionHandler completionHandler: @escaping () -> Void) {\n        if response.notification.request.trigger?.isKind(of: UNPushNotificationTrigger.self) == true {\n            pushNotificationHandler?.didReceive(response: response)\n        } else {\n            localNotificationHandler?.didReceive(response: response)\n        }\n\n        completionHandler()\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/PluginCallResult.swift",
    "content": "import Foundation\n\npublic typealias PluginCallResultData = [String: Any]\n\npublic enum PluginCallResult {\n    case dictionary(PluginCallResultData)\n\n    enum SerializationError: Error {\n        case invalidObject\n    }\n\n    func jsonRepresentation(includingFields: PluginCallResultData? = nil) throws -> String? {\n        switch self {\n        case .dictionary(var dictionary):\n            if let fields = includingFields {\n                dictionary.merge(fields) { (current, _) in current }\n            }\n            dictionary = prepare(dictionary: dictionary)\n            guard JSONSerialization.isValidJSONObject(dictionary) else {\n                throw SerializationError.invalidObject\n            }\n            let data = try JSONSerialization.data(withJSONObject: dictionary, options: [])\n            return String(data: data, encoding: .utf8)\n        }\n    }\n\n    private static let formatter = ISO8601DateFormatter()\n\n    private func prepare(dictionary: PluginCallResultData) -> PluginCallResultData {\n        return dictionary.mapValues { (value) -> Any in\n            if let date = value as? Date {\n                return PluginCallResult.formatter.string(from: date)\n            } else if let aDictionary = value as? PluginCallResultData {\n                return prepare(dictionary: aDictionary)\n            } else if let anArray = value as? [Any] {\n                return prepare(array: anArray)\n            }\n            return value\n        }\n    }\n\n    private func prepare(array: [Any]) -> [Any] {\n        return array.map { (value) -> Any in\n            if let date = value as? Date {\n                return PluginCallResult.formatter.string(from: date)\n            } else if let aDictionary = value as? PluginCallResultData {\n                return prepare(dictionary: aDictionary)\n            } else if let anArray = value as? [Any] {\n                return prepare(array: anArray)\n            }\n            return value\n        }\n    }\n}\n\n@objc public class CAPPluginCallResult: NSObject {\n    public let resultData: PluginCallResult?\n\n    @objc public var data: PluginCallResultData? {\n        guard let result = resultData else {\n            return nil\n        }\n        switch result {\n        case .dictionary(let data):\n            return data\n        }\n    }\n\n    @objc(init:)\n    public init(_ data: PluginCallResultData?) {\n        if let data = data {\n            resultData = .dictionary(data)\n        } else {\n            resultData = nil\n        }\n    }\n}\n\n@objc public class CAPPluginCallError: NSObject {\n    @objc public let message: String\n    @objc public let code: String?\n    @objc public let error: Error?\n    public let resultData: PluginCallResult?\n\n    @objc public var data: PluginCallResultData? {\n        guard let result = resultData else {\n            return nil\n        }\n        switch result {\n        case .dictionary(let data):\n            return data\n        }\n    }\n\n    @objc(init:code:error:data:)\n    public init(message: String, code: String?, error: Error?, data: PluginCallResultData?) {\n        self.message = message\n        self.code = code\n        self.error = error\n        if let data = data {\n            resultData = .dictionary([\"data\": data])\n        } else {\n            resultData = nil\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/PluginConfig.swift",
    "content": "import Foundation\n\n@objc public class PluginConfig: NSObject {\n\n    // The object containing the plugin config values\n    private var config: JSObject\n\n    init(config: JSObject) {\n        self.config = config\n    }\n\n    @objc public func getString(_ configKey: String, _ defaultValue: String? = nil) -> String? {\n        if let val = (self.config)[keyPath: KeyPath(configKey)] as? String {\n            return val\n        }\n        return defaultValue\n    }\n\n    @objc public func getBoolean(_ configKey: String, _ defaultValue: Bool) -> Bool {\n        if let val = (self.config)[keyPath: KeyPath(configKey)] as? Bool {\n            return val\n        }\n        return defaultValue\n    }\n\n    @objc public func getInt(_ configKey: String, _ defaultValue: Int) -> Int {\n        if let val = (self.config)[keyPath: KeyPath(configKey)] as? Int {\n            return val\n        }\n        return defaultValue\n    }\n\n    public func getArray(_ configKey: String, _ defaultValue: JSArray? = nil) -> JSArray? {\n        if let val = (self.config)[keyPath: KeyPath(configKey)] as? JSArray {\n            return val\n        }\n        return defaultValue\n    }\n\n    public func getObject(_ configKey: String) -> JSObject? {\n        return (self.config)[keyPath: KeyPath(configKey)] as? JSObject\n    }\n\n    @objc public func isEmpty() -> Bool {\n        return self.config.isEmpty\n    }\n\n    /**\n     * Gets the JSObject containing the config of the the provided plugin ID.\n     *\n     * @return The config for that plugin\n     */\n    public func getConfigJSON() -> JSObject {\n        return self.config\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift",
    "content": "import Foundation\n\npublic class CapacitorWKCookieObserver: NSObject, WKHTTPCookieStoreObserver {\n    // Sync WKWebView Cookies to HTTPCookieStorage\n    public func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {\n        DispatchQueue.main.async {\n            cookieStore.getAllCookies { cookies in\n                cookies.forEach { cookie in\n                    HTTPCookieStorage.shared.setCookie(cookie)\n                }\n            }\n        }\n    }\n}\n\npublic class CapacitorCookieManager {\n    var config: InstanceConfiguration?\n\n    init(_ capConfig: InstanceConfiguration?) {\n        self.config = capConfig\n    }\n\n    public func getServerUrl() -> URL? {\n        return self.config?.serverURL ?? self.config?.localURL\n    }\n\n    private func isUrlSanitized(_ urlString: String) -> Bool {\n        return urlString.isEmpty || urlString == getServerUrl()?.absoluteString || urlString.hasPrefix(\"http://\") || urlString.hasPrefix(\"https://\")\n    }\n\n    public func getServerUrl(_ urlString: String?) -> URL? {\n        guard let urlString = urlString else {\n            return getServerUrl()\n        }\n\n        if urlString.isEmpty {\n            return getServerUrl()\n        }\n\n        let validUrlString = (isUrlSanitized(urlString)) ? urlString : \"http://\\(urlString)\"\n\n        guard let url = URL(string: validUrlString) else {\n            return getServerUrl()\n        }\n\n        return url\n    }\n\n    public func encode(_ value: String) -> String {\n        return value.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!\n    }\n\n    public func decode(_ value: String) -> String {\n        return value.removingPercentEncoding!\n    }\n\n    public func setCookie(_ domain: String, _ action: String) {\n        let url = getServerUrl(domain)!\n        let jar = HTTPCookieStorage.shared\n        let field = [\"Set-Cookie\": action]\n        let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url)\n        jar.setCookies(cookies, for: url, mainDocumentURL: nil)\n        syncCookiesToWebView()\n    }\n\n    public func setCookie(_ url: URL, _ key: String, _ value: String, _ expires: String?, _ path: String?) {\n        let jar = HTTPCookieStorage.shared\n        let field = [\"Set-Cookie\": \"\\(key)=\\(value); expires=\\(expires ?? \"\"); path=\\(path ?? \"/\")\"]\n        let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url)\n        jar.setCookies(cookies, for: url, mainDocumentURL: nil)\n        syncCookiesToWebView()\n    }\n\n    public func getCookiesAsMap(_ url: URL) -> [String: String] {\n        syncCookiesToWebView()\n        var cookiesMap: [String: String] = [:]\n        let jar = HTTPCookieStorage.shared\n        if let cookies = jar.cookies(for: url) {\n            for cookie in cookies {\n                if !cookie.isHTTPOnly {\n                    cookiesMap[cookie.name] = cookie.value\n                }\n            }\n        }\n        return cookiesMap\n    }\n\n    public func getCookies() -> String {\n        syncCookiesToWebView()\n        let jar = HTTPCookieStorage.shared\n        guard let url = self.getServerUrl() else { return \"\" }\n        guard let cookies = jar.cookies(for: url) else { return \"\" }\n        let filteredCookies = cookies.filter { !$0.isHTTPOnly }\n        return filteredCookies.map({\"\\($0.name)=\\($0.value)\"}).joined(separator: \"; \")\n    }\n\n    public func deleteCookie(_ url: URL, _ key: String) {\n        let jar = HTTPCookieStorage.shared\n        if let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in\n            return cookie.name == key\n        }) {\n            jar.deleteCookie(cookie)\n            DispatchQueue.main.async {\n                WKWebsiteDataStore.default().httpCookieStore.delete(cookie)\n            }\n        }\n    }\n\n    public func clearCookies(_ url: URL) {\n        let jar = HTTPCookieStorage.shared\n        jar.cookies(for: url)?.forEach({ (cookie) in\n            jar.deleteCookie(cookie)\n            DispatchQueue.main.async {\n                WKWebsiteDataStore.default().httpCookieStore.delete(cookie)\n            }\n        })\n    }\n\n    public func clearAllCookies() {\n        let jar = HTTPCookieStorage.shared\n        jar.cookies?.forEach({ (cookie) in\n            jar.deleteCookie(cookie)\n            DispatchQueue.main.async {\n                WKWebsiteDataStore.default().httpCookieStore.delete(cookie)\n            }\n        })\n    }\n\n    public func syncCookiesToWebView() {\n        if let cookies = HTTPCookieStorage.shared.cookies {\n            for cookie in cookies {\n                DispatchQueue.main.async {\n                    WKWebsiteDataStore.default()\n                        .httpCookieStore\n                        .setCookie(cookie, completionHandler: nil)\n                }\n\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/CapacitorCookies.swift",
    "content": "import Foundation\n\n@objc(CAPCookiesPlugin)\npublic class CAPCookiesPlugin: CAPPlugin, CAPBridgedPlugin {\n    public let identifier = \"CAPCookiesPlugin\"\n    public let jsName = \"CapacitorCookies\"\n    public let pluginMethods: [CAPPluginMethod] = [\n        CAPPluginMethod(name: \"getCookies\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"setCookie\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"deleteCookie\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"clearCookies\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"clearAllCookies\", returnType: CAPPluginReturnPromise)\n    ]\n\n    var cookieManager: CapacitorCookieManager?\n\n    @objc override public func load() {\n        cookieManager = CapacitorCookieManager(bridge?.config)\n    }\n\n    @objc func getCookies(_ call: CAPPluginCall) {\n        guard let url = cookieManager!.getServerUrl(call.getString(\"url\")) else { return call.reject(\"Invalid URL / Server URL\")}\n        call.resolve(cookieManager!.getCookiesAsMap(url))\n    }\n\n    @objc func setCookie(_ call: CAPPluginCall) {\n        guard let key = call.getString(\"key\") else { return call.reject(\"Must provide key\") }\n        guard let value = call.getString(\"value\") else { return call.reject(\"Must provide value\") }\n\n        guard let url = cookieManager!.getServerUrl(call.getString(\"url\")) else { return call.reject(\"Invalid domain\") }\n\n        let expires = call.getString(\"expires\", \"\")\n        let path = call.getString(\"path\", \"\")\n        cookieManager!.setCookie(url, key, cookieManager!.encode(value), expires, path)\n        call.resolve()\n    }\n\n    @objc func deleteCookie(_ call: CAPPluginCall) {\n        guard let key = call.getString(\"key\") else { return call.reject(\"Must provide key\") }\n        guard let url = cookieManager!.getServerUrl(call.getString(\"url\")) else { return call.reject(\"Invalid URL / Server URL\")}\n        cookieManager!.deleteCookie(url, key)\n        call.resolve()\n    }\n\n    @objc func clearCookies(_ call: CAPPluginCall) {\n        let url = cookieManager!.getServerUrl(call.getString(\"url\"))\n        if url != nil {\n            cookieManager!.clearCookies(url!)\n            call.resolve()\n        }\n    }\n\n    @objc func clearAllCookies(_ call: CAPPluginCall) {\n        cookieManager!.clearAllCookies()\n        call.resolve()\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/CapacitorHttp.swift",
    "content": "import Foundation\n\n@objc(CAPHttpPlugin)\npublic class CAPHttpPlugin: CAPPlugin, CAPBridgedPlugin {\n    public let identifier = \"CAPHttpPlugin\"\n    public let jsName = \"CapacitorHttp\"\n    public let pluginMethods: [CAPPluginMethod] = [\n        CAPPluginMethod(name: \"request\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"get\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"post\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"put\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"patch\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"delete\", returnType: CAPPluginReturnPromise)\n    ]\n\n    @objc func http(_ call: CAPPluginCall, _ httpMethod: String?) {\n        do {\n            if let clazz = NSClassFromString(\"SSLPinningHttpRequestHandlerClass\") {\n                // swiftlint:disable force_cast\n                (clazz as! NSObject.Type).perform(#selector(self.request(_:)), with: [\n                    \"call\": call,\n                    \"httpMethod\": httpMethod as Any,\n                    \"config\": self.bridge?.config as Any\n                ])\n                // swiftlint:enable force_cast\n            } else {\n                try HttpRequestHandler.request(call, httpMethod, self.bridge?.config)\n            }\n        } catch let error {\n            call.reject(error.localizedDescription)\n        }\n    }\n\n    @objc func request(_ call: CAPPluginCall) {\n        http(call, nil)\n    }\n\n    @objc func get(_ call: CAPPluginCall) {\n        http(call, \"GET\")\n    }\n\n    @objc func post(_ call: CAPPluginCall) {\n        http(call, \"POST\")\n    }\n\n    @objc func put(_ call: CAPPluginCall) {\n        http(call, \"PUT\")\n    }\n\n    @objc func patch(_ call: CAPPluginCall) {\n        http(call, \"PATCH\")\n    }\n\n    @objc func delete(_ call: CAPPluginCall) {\n        http(call, \"DELETE\")\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift",
    "content": "import Foundation\n\nopen class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate {\n    public var request: URLRequest\n    public var headers: [String: String]\n\n    public enum CapacitorUrlRequestError: Error {\n        case serializationError(String?)\n    }\n\n    public init(_ url: URL, method: String) {\n        request = URLRequest(url: url)\n        request.httpMethod = method\n        headers = [:]\n        if let lang = Locale.autoupdatingCurrent.languageCode {\n            if let country = Locale.autoupdatingCurrent.regionCode {\n                headers[\"Accept-Language\"] = \"\\(lang)-\\(country),\\(lang);q=0.5\"\n            } else {\n                headers[\"Accept-Language\"] = \"\\(lang);q=0.5\"\n            }\n            request.addValue(headers[\"Accept-Language\"]!, forHTTPHeaderField: \"Accept-Language\")\n        }\n    }\n\n    public func getRequestDataAsJson(_ data: JSValue) throws -> Data? {\n        // We need to check if the JSON is valid before attempting to serialize, as JSONSerialization.data will not throw an exception that can be caught, and will cause the application to crash if it fails.\n        if JSONSerialization.isValidJSONObject(data) {\n            return try JSONSerialization.data(withJSONObject: data)\n        } else {\n            throw CapacitorUrlRequest.CapacitorUrlRequestError.serializationError(\"[ data ] argument for request of content-type [ application/json ] must be serializable to JSON\")\n        }\n    }\n\n    public func getRequestDataAsFormUrlEncoded(_ data: JSValue) throws -> Data? {\n        guard var components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false) else { return nil }\n        components.queryItems = []\n\n        guard let obj = data as? JSObject else {\n            // Throw, other data types explicitly not supported\n            throw CapacitorUrlRequestError.serializationError(\"[ data ] argument for request with content-type [ multipart/form-data ] may only be a plain javascript object\")\n        }\n\n        let allowed = CharacterSet(charactersIn: \"-._*\").union(.alphanumerics)\n\n        obj.keys.forEach { (key: String) in\n            let value = obj[key] as? String ?? \"\"\n            components.queryItems?.append(URLQueryItem(name: key.addingPercentEncoding(withAllowedCharacters: allowed)?.replacingOccurrences(of: \"%20\", with: \"+\") ?? key, value: value.addingPercentEncoding(withAllowedCharacters: allowed)?.replacingOccurrences(of: \"%20\", with: \"+\")))\n        }\n\n        if components.query != nil {\n            return Data(components.query!.utf8)\n        }\n\n        return nil\n    }\n\n    public func getRequestDataAsMultipartFormData(_ data: JSValue, _ contentType: String) throws -> Data {\n        guard let obj = data as? JSObject else {\n            // Throw, other data types explicitly not supported.\n            throw CapacitorUrlRequestError.serializationError(\"[ data ] argument for request with content-type [ application/x-www-form-urlencoded ] may only be a plain javascript object\")\n        }\n\n        let strings: [String: String] = obj.compactMapValues { any in\n            any as? String\n        }\n\n        var data = Data()\n        var boundary = UUID().uuidString\n        if contentType.contains(\"=\"), let contentBoundary = contentType.components(separatedBy: \"=\").last {\n            boundary = contentBoundary\n        } else {\n            overrideContentType(boundary)\n        }\n        strings.forEach { key, value in\n            data.append(\"\\r\\n--\\(boundary)\\r\\n\".data(using: .utf8)!)\n            data.append(\"Content-Disposition: form-data; name=\\\"\\(key)\\\"\\r\\n\\r\\n\".data(using: .utf8)!)\n            data.append(value.data(using: .utf8)!)\n        }\n        data.append(\"\\r\\n--\\(boundary)--\\r\\n\".data(using: .utf8)!)\n\n        return data\n    }\n\n    private func overrideContentType(_ boundary: String) {\n        let contentType = \"multipart/form-data; boundary=\\(boundary)\"\n        request.setValue(contentType, forHTTPHeaderField: \"Content-Type\")\n        headers[\"Content-Type\"] = contentType\n    }\n\n    public func getRequestDataAsString(_ data: JSValue) throws -> Data {\n        guard let stringData = data as? String else {\n            throw CapacitorUrlRequestError.serializationError(\"[ data ] argument could not be parsed as string\")\n        }\n        return Data(stringData.utf8)\n    }\n\n    public func getRequestHeader(_ index: String) -> Any? {\n        var normalized = [:] as [String: Any]\n        self.headers.keys.forEach { (key: String) in\n            normalized[key.lowercased()] = self.headers[key]\n        }\n\n        return normalized[index.lowercased()]\n    }\n\n    public func getRequestDataFromFormData(_ data: JSValue, _ contentType: String) throws -> Data? {\n        guard let list = data as? JSArray else {\n            // Throw, other data types explicitly not supported.\n            throw CapacitorUrlRequestError.serializationError(\"Data must be an array for FormData\")\n        }\n        var data = Data()\n        var boundary = UUID().uuidString\n        if contentType.contains(\"=\"), let contentBoundary = contentType.components(separatedBy: \"=\").last {\n            boundary = contentBoundary\n        } else {\n            overrideContentType(boundary)\n        }\n        for entry in list {\n            guard let item = entry as? [String: String] else {\n                throw CapacitorUrlRequestError.serializationError(\"Data must be an array for FormData\")\n            }\n\n            let type = item[\"type\"]\n            let key = item[\"key\"]\n            let value = item[\"value\"]!\n\n            if type == \"base64File\" {\n                let fileName = item[\"fileName\"]\n                let fileContentType = item[\"contentType\"]\n\n                data.append(\"--\\(boundary)\\r\\n\".data(using: .utf8)!)\n                data.append(\"Content-Disposition: form-data; name=\\\"\\(key!)\\\"; filename=\\\"\\(fileName!)\\\"\\r\\n\".data(using: .utf8)!)\n                data.append(\"Content-Type: \\(fileContentType!)\\r\\n\".data(using: .utf8)!)\n                data.append(\"Content-Transfer-Encoding: binary\\r\\n\".data(using: .utf8)!)\n                data.append(\"\\r\\n\".data(using: .utf8)!)\n\n                data.append(Data(base64Encoded: value)!)\n\n                data.append(\"\\r\\n\".data(using: .utf8)!)\n            } else if type == \"string\" {\n                data.append(\"--\\(boundary)\\r\\n\".data(using: .utf8)!)\n                data.append(\"Content-Disposition: form-data; name=\\\"\\(key!)\\\"\\r\\n\".data(using: .utf8)!)\n                data.append(\"\\r\\n\".data(using: .utf8)!)\n                data.append(value.data(using: .utf8)!)\n                data.append(\"\\r\\n\".data(using: .utf8)!)\n            }\n        }\n        data.append(\"--\\(boundary)--\\r\\n\".data(using: .utf8)!)\n\n        return data\n    }\n\n    public func getRequestData(_ body: JSValue, _ contentType: String, _ dataType: String? = nil) throws -> Data? {\n        if dataType == \"file\" {\n            guard let stringData = body as? String else {\n                throw CapacitorUrlRequestError.serializationError(\"[ data ] argument could not be parsed as string\")\n            }\n            return Data(base64Encoded: stringData)\n        } else if dataType == \"formData\" {\n            return try getRequestDataFromFormData(body, contentType)\n        }\n\n        // If data can be parsed directly as a string, return that without processing.\n        if let strVal = try? getRequestDataAsString(body) {\n            return strVal\n        } else if contentType.contains(\"application/json\") {\n            return try getRequestDataAsJson(body)\n        } else if contentType.contains(\"application/x-www-form-urlencoded\") {\n            return try getRequestDataAsFormUrlEncoded(body)\n        } else if contentType.contains(\"multipart/form-data\") {\n            return try getRequestDataAsMultipartFormData(body, contentType)\n        } else {\n            throw CapacitorUrlRequestError.serializationError(\"[ data ] argument could not be parsed for content type [ \\(contentType) ]\")\n        }\n    }\n\n    @available(*, deprecated, message: \"Use newer function with passed headers of type [String: Any]\")\n    public func setRequestHeaders(_ headers: [String: String]) {\n        headers.keys.forEach { (key: String) in\n            let value = headers[key]\n            request.addValue(value!, forHTTPHeaderField: key)\n            self.headers[key] = value\n        }\n    }\n\n    public func setRequestHeaders(_ headers: [String: Any]) {\n        headers.keys.forEach { (key: String) in\n            let value = headers[key]\n            request.setValue(\"\\(value!)\", forHTTPHeaderField: key)\n            self.headers[key] = \"\\(value!)\"\n        }\n    }\n\n    public func setRequestBody(_ body: JSValue, _ dataType: String? = nil) throws {\n        let contentType = self.getRequestHeader(\"Content-Type\") as? String\n\n        if contentType != nil {\n            request.httpBody = try getRequestData(body, contentType!, dataType)\n        }\n    }\n\n    public func setContentType(_ data: String?) {\n        request.setValue(data, forHTTPHeaderField: \"Content-Type\")\n    }\n\n    public func setTimeout(_ timeout: TimeInterval) {\n        request.timeoutInterval = timeout\n    }\n\n    public func getUrlRequest() -> URLRequest {\n        return request\n    }\n\n    open func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {\n        completionHandler(nil)\n    }\n\n    open func getUrlSession(_ call: CAPPluginCall) -> URLSession {\n        let disableRedirects = call.getBool(\"disableRedirects\") ?? false\n        if !disableRedirects {\n            return URLSession.shared\n        }\n        return URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/Console.swift",
    "content": "import Foundation\n\n@objc(CAPConsolePlugin)\npublic class CAPConsolePlugin: CAPPlugin, CAPBridgedPlugin {\n    public let identifier = \"CAPConsolePlugin\"\n    public let jsName = \"Console\"\n    public let pluginMethods: [CAPPluginMethod] = [\n        CAPPluginMethod(name: \"log\", returnType: CAPPluginReturnNone)\n    ]\n\n    @objc public func log(_ call: CAPPluginCall) {\n        let message = call.getString(\"message\") ?? \"\"\n        let level = call.getString(\"level\") ?? \"log\"\n        CAPLog.print(\"⚡️  [\\(level)] - \\(message)\")\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift",
    "content": "import Foundation\n\n/// See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType\npublic enum ResponseType: String {\n    case arrayBuffer = \"arraybuffer\"\n    case blob = \"blob\"\n    case document = \"document\"\n    case json = \"json\"\n    case text = \"text\"\n\n    public static let `default`: ResponseType = .text\n\n    public init(string: String?) {\n        guard let string = string else {\n            self = .default\n            return\n        }\n\n        guard let responseType = ResponseType(rawValue: string.lowercased()) else {\n            self = .default\n            return\n        }\n\n        self = responseType\n    }\n}\n\n/// Helper that safely parses JSON Data. Otherwise returns an error (without throwing)\n/// - Parameters:\n///     - data: The JSON Data to parse\n/// - Returns: The parsed value or an error\nfunc tryParseJson(_ data: Data) -> Any {\n    do {\n        return try JSONSerialization.jsonObject(with: data, options: [.mutableContainers, .fragmentsAllowed])\n    } catch {\n        return error.localizedDescription\n    }\n}\n\n/// Helper to convert the headers dictionary to lower case keys. This allows case-insensitive querying in the bridge javascript.\n/// - Parameters:\n///     - headers: The headers as dictionary. The type is unspecific because the incoming headers are coming from the\n///       allHeaderFields property of the HttpResponse.\n/// - Returns: The modified headers dictionary with lowercase keys\nprivate func lowerCaseHeaderDictionary(_ headers: [AnyHashable: Any]) -> [String: Any] {\n    // Lowercases the key of the headers dictionary.\n    return Dictionary(uniqueKeysWithValues: headers.map({ (key: AnyHashable, value: Any) in\n        return (String(describing: key).lowercased(), value)\n    }))\n}\n\nopen class HttpRequestHandler {\n    open class CapacitorHttpRequestBuilder {\n        public var url: URL?\n        public var method: String?\n        public var params: [String: String]?\n        open var request: CapacitorUrlRequest?\n\n        public init() { }\n\n        /// Set the URL of the HttpRequest\n        /// - Throws: an error of URLError if the urlString cannot be parsed\n        /// - Parameters:\n        ///     - urlString: The URL value to parse\n        /// - Returns: self to continue chaining functions\n        public func setUrl(_ urlString: String) throws -> CapacitorHttpRequestBuilder {\n            guard let url = URL(string: urlString) else {\n                throw URLError(.badURL)\n            }\n            self.url = url\n            return self\n        }\n\n        public func setMethod(_ method: String) -> CapacitorHttpRequestBuilder {\n            self.method = method\n            return self\n        }\n\n        public func setUrlParams(_ params: [String: Any], _ shouldEncodeUrlParams: Bool = true) -> CapacitorHttpRequestBuilder {\n            if params.count != 0 {\n                // swiftlint:disable force_cast\n                var cmps = URLComponents(url: url!, resolvingAgainstBaseURL: true)\n                if cmps?.queryItems == nil {\n                    cmps?.queryItems = []\n                }\n\n                if shouldEncodeUrlParams {\n                    var urlSafeParams: [URLQueryItem] = []\n                    for (key, value) in params {\n                        if let arr = value as? [String] {\n                            arr.forEach { str in\n                                urlSafeParams.append(URLQueryItem(name: key, value: str))\n                            }\n                        } else {\n                            urlSafeParams.append(URLQueryItem(name: key, value: (value as! String)))\n                        }\n                    }\n                    cmps!.queryItems?.append(contentsOf: urlSafeParams)\n                } else {\n                    cmps?.query = params.flatMap { key, value -> [String] in\n                        if let arrayValue = value as? [String] {\n                            return arrayValue.map { \"\\(key)=\\($0)\" }\n                        } else {\n                            return [\"\\(key)=\\(value)\"]\n                        }\n                    }.joined(separator: \"&\")\n                }\n                url = cmps!.url!\n            }\n            return self\n        }\n\n        open func openConnection() -> CapacitorHttpRequestBuilder {\n            request = CapacitorUrlRequest(url!, method: method!)\n            return self\n        }\n\n        public func build() -> CapacitorUrlRequest {\n            return request!\n        }\n    }\n\n    public static func setCookiesFromResponse(_ response: HTTPURLResponse, _ config: InstanceConfiguration?) {\n        let headers = response.allHeaderFields\n        if let cookies = headers[\"Set-Cookie\"] as? String {\n            for cookie in cookies.components(separatedBy: \",\") {\n                let domainComponents = cookie.lowercased().components(separatedBy: \"domain=\")\n                if domainComponents.count > 1 {\n                    CapacitorCookieManager(config).setCookie(\n                        domainComponents[1].components(separatedBy: \";\")[0],\n                        cookie\n                    )\n                } else {\n                    CapacitorCookieManager(config).setCookie(\"\", cookie)\n                }\n            }\n        }\n        CapacitorCookieManager(config).syncCookiesToWebView()\n    }\n\n    public static func buildResponse(_ data: Data?, _ response: HTTPURLResponse, responseType: ResponseType = .default) -> [String: Any] {\n        var output = [:] as [String: Any]\n\n        output[\"status\"] = response.statusCode\n\n        // HTTP Headers are case insensitive. The allHeaderFields dictionary returned by Apple Foundation Code has its keys capitalized.\n        // According to the documentation at https://developer.apple.com/documentation/foundation/httpurlresponse/1417930-allheaderfields\n        // \"HTTP headers are case insensitive. To simplify your code, URL Loading System canonicalizes certain header field names into\n        // their standard form. For example, if the server sends a content-length header, it’s automatically adjusted to be Content-Length.\"\n        // To handle the case insevitivy, we are converting the header keys to lower case here. When querying for headers in the native bridge,\n        // we are lowercasing the key as well.\n        output[\"headers\"] = lowerCaseHeaderDictionary(response.allHeaderFields)\n        output[\"url\"] = response.url?.absoluteString\n\n        guard let data = data else {\n            output[\"data\"] = \"\"\n            return output\n        }\n\n        let contentType = (response.allHeaderFields[\"Content-Type\"] as? String ?? \"application/default\").lowercased()\n\n        if contentType.contains(\"application/json\") || responseType == .json {\n            output[\"data\"] = tryParseJson(data)\n        } else if responseType == .arrayBuffer || responseType == .blob {\n            output[\"data\"] = data.base64EncodedString()\n        } else if responseType == .document || responseType == .text || responseType == .default {\n            output[\"data\"] = String(data: data, encoding: .utf8)\n        }\n\n        return output\n    }\n\n    public static func request(_ call: CAPPluginCall, _ httpMethod: String?, _ config: InstanceConfiguration?) throws {\n        guard var urlString = call.getString(\"url\") else { throw URLError(.badURL) }\n        let method = httpMethod ?? call.getString(\"method\", \"GET\")\n\n        var headers = (call.getObject(\"headers\") ?? [:]) as [String: Any]\n        let params = (call.getObject(\"params\") ?? [:]) as [String: Any]\n        let responseType = call.getString(\"responseType\") ?? \"text\"\n        let connectTimeout = call.getDouble(\"connectTimeout\")\n        let shouldEncodeUrlParams = call.getBool(\"shouldEncodeUrlParams\", true)\n        let readTimeout = call.getDouble(\"readTimeout\")\n        let dataType = call.getString(\"dataType\") ?? \"any\"\n\n        if urlString == urlString.removingPercentEncoding {\n            guard let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)  else { throw URLError(.badURL) }\n            urlString = encodedUrlString\n        }\n\n        let request = try CapacitorHttpRequestBuilder()\n            .setUrl(urlString)\n            .setMethod(method)\n            .setUrlParams(params, shouldEncodeUrlParams)\n            .openConnection()\n            .build()\n\n        if let userAgentString = config?.overridenUserAgentString, headers[\"User-Agent\"] == nil, headers[\"user-agent\"] == nil {\n            headers[\"User-Agent\"] = userAgentString\n        }\n\n        request.setRequestHeaders(headers)\n\n        // Timeouts in iOS are in seconds. So read the value in millis and divide by 1000\n        let timeout = (connectTimeout ?? readTimeout ?? 600000.0) / 1000.0\n        request.setTimeout(timeout)\n\n        if let data = call.options[\"data\"] as? JSValue {\n            do {\n                try request.setRequestBody(data, dataType)\n            } catch {\n                // Explicitly reject if the http request body was not set successfully,\n                // so as to not send a known malformed request, and to provide the developer with additional context.\n                call.reject(error.localizedDescription, (error as NSError).domain, error, nil)\n                return\n            }\n        }\n\n        let urlRequest = request.getUrlRequest()\n        let urlSession = request.getUrlSession(call)\n        let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in\n            urlSession.invalidateAndCancel()\n\n            if let error = error {\n                call.reject(error.localizedDescription, (error as NSError).domain, error, nil)\n                return\n            }\n\n            setCookiesFromResponse(response as! HTTPURLResponse, config)\n\n            let type = ResponseType(rawValue: responseType) ?? .default\n            call.resolve(self.buildResponse(data, response as! HTTPURLResponse, responseType: type))\n        }\n\n        task.resume()\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/SystemBars.swift",
    "content": "import Foundation\n\n@objc(CAPSystemBarsPlugin)\npublic class CAPSystemBarsPlugin: CAPPlugin, CAPBridgedPlugin {\n    public let identifier = \"CAPSystemBarsPlugin\"\n    public let jsName = \"SystemBars\"\n    public let pluginMethods: [CAPPluginMethod] = [\n        CAPPluginMethod(name: \"setStyle\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"setAnimation\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"show\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"hide\", returnType: CAPPluginReturnPromise)\n    ]\n\n    public private(set) var hideHomeIndicator: Bool = false\n\n    enum Style: String {\n        case dark = \"DARK\"\n        case light = \"LIGHT\"\n        case defaultStyle = \"DEFAULT\"\n    }\n\n    @objc override public func load() {\n        let hidden = getConfig().getBoolean(\"hidden\", false)\n\n        if let style = getConfig().getString(\"style\", \"DEFAULT\") {\n            setStyle(style: style)\n        }\n\n        if let animation = getConfig().getString(\"animation\") {\n            setAnimation(animation: animation)\n        }\n\n        setHidden(hidden: hidden)\n    }\n\n    @objc func setStyle(_ call: CAPPluginCall) {\n        setStyle(style: call.getString(\"style\") ?? Style.defaultStyle.rawValue)\n        call.resolve()\n    }\n\n    @objc func show(_ call: CAPPluginCall) {\n        let bar = call.getString(\"bar\")\n\n        if let animation = call.getString(\"animation\") {\n            setAnimation(animation: animation)\n        }\n\n        DispatchQueue.main.async {\n            self.setHidden(hidden: false, bar: bar)\n            call.resolve()\n        }\n    }\n\n    @objc func hide(_ call: CAPPluginCall) {\n        let bar = call.getString(\"bar\")\n\n        if let animation = call.getString(\"animation\") {\n            setAnimation(animation: animation)\n        }\n\n        DispatchQueue.main.async {\n            self.setHidden(hidden: true, bar: bar)\n            call.resolve()\n        }\n    }\n\n    @objc func setAnimation(_ call: CAPPluginCall) {\n        let animation = call.getString(\"animation\", \"FADE\")\n        setAnimation(animation: animation)\n\n        call.resolve()\n    }\n\n    func setStyle(style: String) {\n        var newStyle: UIStatusBarStyle = .default\n\n        if let style = Style(rawValue: style.uppercased()) {\n            switch style {\n            case .dark:\n                newStyle = .lightContent\n            case .light:\n                newStyle = .darkContent\n            case .defaultStyle:\n                newStyle = .default\n            }\n        }\n\n        bridge?.statusBarStyle = newStyle\n    }\n\n    func setHidden(hidden: Bool, bar: String? = nil) {\n        if hidden {\n            if bar == nil || bar?.isEmpty ?? true || bar == \"StatusBar\" {\n                bridge?.statusBarVisible = false\n            }\n\n            if bar == nil || bar?.isEmpty ?? true || bar == \"NavigationBar\" {\n                hideHomeIndicator = true\n                bridge?.viewController?.setNeedsUpdateOfHomeIndicatorAutoHidden()\n            }\n\n            return\n        }\n\n        if bar == nil || bar?.isEmpty ?? true || bar == \"StatusBar\" {\n            bridge?.statusBarVisible = true\n        }\n\n        if bar == nil || bar?.isEmpty ?? true || bar == \"NavigationBar\" {\n            hideHomeIndicator = false\n            bridge?.viewController?.setNeedsUpdateOfHomeIndicatorAutoHidden()\n        }\n\n    }\n\n    func setAnimation(animation: String) {\n        if animation.uppercased() == \"NONE\" {\n            bridge?.statusBarAnimation = .none\n        } else {\n            bridge?.statusBarAnimation = .fade\n        }\n    }\n}\n\nextension CAPBridgeViewController {\n    override public var prefersHomeIndicatorAutoHidden: Bool {\n        if let systemBarPlugin = self.bridge?.plugin(withName: \"SystemBars\") as? CAPSystemBarsPlugin {\n            return systemBarPlugin.hideHomeIndicator\n        }\n\n        return false\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Plugins/WebView.swift",
    "content": "import Foundation\n\n@objc(CAPWebViewPlugin)\npublic class CAPWebViewPlugin: CAPPlugin, CAPBridgedPlugin {\n    public let identifier = \"CAPWebViewPlugin\"\n    public let jsName = \"WebView\"\n    public let pluginMethods: [CAPPluginMethod] = [\n        CAPPluginMethod(name: \"setServerAssetPath\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"setServerBasePath\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"getServerBasePath\", returnType: CAPPluginReturnPromise),\n        CAPPluginMethod(name: \"persistServerBasePath\", returnType: CAPPluginReturnPromise)\n    ]\n\n    @objc func setServerAssetPath(_ call: CAPPluginCall) {\n        if let path = call.getString(\"path\"), let viewController = bridge?.viewController as? CAPBridgeViewController {\n            viewController.setServerBasePath(path: Bundle.main.url(forResource: path, withExtension: nil)?.path ?? path)\n            call.resolve()\n        }\n    }\n\n    @objc func setServerBasePath(_ call: CAPPluginCall) {\n        if let path = call.getString(\"path\"), let viewController = bridge?.viewController as? CAPBridgeViewController {\n            viewController.setServerBasePath(path: path)\n            call.resolve()\n        }\n    }\n\n    @objc func getServerBasePath(_ call: CAPPluginCall) {\n        if let viewController = bridge?.viewController as? CAPBridgeViewController {\n            let path = viewController.getServerBasePath()\n            call.resolve([\n                \"path\": path\n            ])\n        }\n    }\n\n    @objc func persistServerBasePath(_ call: CAPPluginCall) {\n        if let viewController = bridge?.viewController as? CAPBridgeViewController {\n            let path = viewController.getServerBasePath()\n            KeyValueStore.standard[\"serverBasePath\"] = path\n            call.resolve()\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/PrivacyInfo.xcprivacy",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>NSPrivacyAccessedAPITypes</key>\n\t<array/>\n\t<key>NSPrivacyCollectedDataTypes</key>\n\t<array/>\n\t<key>NSPrivacyTrackingDomains</key>\n\t<array/>\n\t<key>NSPrivacyTracking</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/Router.swift",
    "content": "//\n//  Router.swift\n//  Capacitor\n//\n//  Created by Steven Sherry on 3/29/22.\n//  Copyright © 2022 Drifty Co. All rights reserved.\n//\n\nimport Foundation\n\npublic protocol Router {\n    func route(for path: String) -> String\n    var basePath: String { get set }\n}\n\npublic struct CapacitorRouter: Router {\n    public init() {}\n    public var basePath: String = \"\"\n    public func route(for path: String) -> String {\n        let pathUrl = URL(fileURLWithPath: path)\n\n        // If there's no path extension it also means the path is empty or a SPA route\n        if pathUrl.pathExtension.isEmpty {\n            return basePath + \"/index.html\"\n        }\n\n        return basePath + path\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/TmpViewController.swift",
    "content": "import UIKit\n\ninternal class TmpViewController: UIViewController {\n    override func viewDidAppear(_ animated: Bool) {\n        super.viewDidAppear(animated)\n        NotificationCenter.default.post(CapacitorBridge.tmpVCAppeared)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/UIColor.swift",
    "content": "extension UIColor: CapacitorExtension {}\npublic extension CapacitorExtensionTypeWrapper where T: UIColor {\n    // disable linting for the short variable names, since that's the point of the method\n    // swiftlint:disable:next identifier_name\n    static func color(r: Int, g: Int, b: Int, a: Int = 0xFF) -> UIColor {\n        return T(\n            red: CGFloat(r) / 255.0,\n            green: CGFloat(g) / 255.0,\n            blue: CGFloat(b) / 255.0,\n            alpha: CGFloat(a) / 255.0\n        )\n    }\n\n    static func color(argb: UInt32) -> UIColor {\n        return T(\n            red: CGFloat((argb >> 16) & 0xFF),\n            green: CGFloat((argb >> 8) & 0xFF),\n            blue: CGFloat(argb & 0xFF),\n            alpha: CGFloat((argb >> 24) & 0xFF)\n        )\n    }\n\n    static func color(fromHex: String) -> UIColor? {\n        let hexString = fromHex.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(\n            of: \"#\",\n            with: \"\"\n        )\n\n        var argb: UInt64 = 0\n\n        var red: CGFloat = 0.0\n        var green: CGFloat = 0.0\n        var blue: CGFloat = 0.0\n        var alpha: CGFloat = 1.0\n\n        guard Scanner(string: hexString).scanHexInt64(&argb) else { return nil }\n\n        if hexString.count == 6 {\n            red = CGFloat((argb & 0xFF0000) >> 16) / 255.0\n            green = CGFloat((argb & 0x00FF00) >> 8) / 255.0\n            blue = CGFloat(argb & 0x0000FF) / 255.0\n\n        } else if hexString.count == 8 {\n            red = CGFloat((argb & 0xFF00_0000) >> 24) / 255.0\n            green = CGFloat((argb & 0x00FF0000) >> 16) / 255.0\n            blue = CGFloat((argb & 0x0000FF00) >> 8) / 255.0\n            alpha = CGFloat(argb & 0x000000FF) / 255.0\n\n        } else {\n            return nil\n        }\n\n        return T(red: red, green: green, blue: blue, alpha: alpha)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/UIStatusBarManager+CAPHandleTapAction.m",
    "content": "#import <Capacitor/Capacitor-Swift.h>\n#import <objc/message.h>\n#import <objc/runtime.h>\n\n@implementation UIStatusBarManager (CAPHandleTapAction)\n\n+ (void)load {\n  static dispatch_once_t onceToken;\n  dispatch_once(&onceToken, ^{\n    Class class = [self class];\n    SEL originalSelector = NSSelectorFromString(@\"handleTapAction:\");\n    SEL swizzledSelector = @selector(nofity_handleTapAction:);\n\n    Method originalMethod = class_getInstanceMethod(class, originalSelector);\n    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);\n\n    BOOL didAddMethod = class_addMethod(class,\n                                        originalSelector,\n                                        method_getImplementation(swizzledMethod),\n                                        method_getTypeEncoding(swizzledMethod));\n    if (didAddMethod) {\n      class_replaceMethod(class,\n                          swizzledSelector,\n                          method_getImplementation(originalMethod),\n                          method_getTypeEncoding(originalMethod));\n    } else {\n      method_exchangeImplementations(originalMethod, swizzledMethod);\n    }\n  });\n}\n\n-(void)nofity_handleTapAction:(id)arg1 {\n  [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:NSNotification.capacitorStatusBarTapped object:nil]];\n  [self nofity_handleTapAction:arg1];\n}\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/WKWebView+Capacitor.m",
    "content": "#import <objc/runtime.h>\n#import <Capacitor/Capacitor-Swift.h>\n\n// Swift extensions marked as @objc and internal are available to the runtime but won't be found at compile time\n// so we need this declaration to avoid compiler complaints.\n@interface WKWebView (InternalSwiftExtension)\n+ (void)_swizzleKeyboardMethods;\n@end\n\n// +load is the safest place to swizzle methods but that won't work from a swift extension so we need this wrapper.\n@implementation WKWebView (CapacitorAutoFocus)\n+ (void)load {\n    [self _swizzleKeyboardMethods];\n}\n@end\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/WKWebView+Capacitor.swift",
    "content": "import Foundation\nimport WebKit\n\nextension WKWebView: CapacitorExtension {}\npublic extension CapacitorExtensionTypeWrapper where T == WKWebView {\n    var keyboardShouldRequireUserInteraction: Bool? {\n        return (self.baseType.associatedKeyboardFlagValue as? NSNumber)?.boolValue\n    }\n\n    // the readonly nature of the wrapper extension means we can't use a computed property with a setter\n    func setKeyboardShouldRequireUserInteraction(_ flag: Bool? = nil) {\n        if let flag = flag {\n            self.baseType.associatedKeyboardFlagValue = NSNumber(value: flag)\n        } else {\n            self.baseType.associatedKeyboardFlagValue = nil\n        }\n    }\n}\n\nprivate var associatedKeyboardFlagHandle: UInt8 = 0\n\ninternal extension WKWebView {\n    // Our lazy property can't be represented in Obj-C so we need this simple wrapper.\n    // swiftlint:disable identifier_name\n    @objc static func _swizzleKeyboardMethods() {\n        _ = oneTimeOnlySwizzle\n    }\n\n    typealias FiveArgClosureType =  @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void\n\n    // dispatch_once isn't available in Swift, but lazy properties use the same mechanism under the hood so\n    // we can safely assume that this block of code will only execute once.\n    static let oneTimeOnlySwizzle: () = {\n        let frameworkName = \"WK\"\n        let className = \"ContentView\"\n        guard let targetClass = NSClassFromString(frameworkName + className) else {\n            return\n        }\n\n        let containingWebView = { (object: Any?) -> WKWebView? in\n            var view = object as? UIView\n            while view != nil {\n                if let webview = view as? WKWebView {\n                    return webview\n                }\n                view = view?.superview\n            }\n            return nil\n        }\n\n        let swizzleFiveArgClosure = { (method: Method, selector: Selector) in\n            let originalImp: IMP = method_getImplementation(method)\n            let original: FiveArgClosureType = unsafeBitCast(originalImp, to: FiveArgClosureType.self)\n            let block: @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in\n                if let webview = containingWebView(me), let flag = webview.capacitor.keyboardShouldRequireUserInteraction {\n                    original(me, selector, arg0, !flag, arg2, arg3, arg4)\n                } else {\n                    original(me, selector, arg0, arg1, arg2, arg3, arg4)\n                }\n            }\n            let imp: IMP = imp_implementationWithBlock(block)\n            method_setImplementation(method, imp)\n        }\n\n        let selectorMkIV: Selector = sel_getUid(\"_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:\")\n\n        if let method = class_getInstanceMethod(targetClass, selectorMkIV) {\n            swizzleFiveArgClosure(method, selectorMkIV)\n        }\n    }()\n\n    var associatedKeyboardFlagValue: Any? {\n        get {\n            return objc_getAssociatedObject(self, &associatedKeyboardFlagHandle)\n        }\n        set {\n            objc_setAssociatedObject(self, &associatedKeyboardFlagHandle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/WebViewAssetHandler.swift",
    "content": "import Foundation\nimport UniformTypeIdentifiers\n\n@objc(CAPWebViewAssetHandler)\n// swiftlint:disable type_body_length\nopen class WebViewAssetHandler: NSObject, WKURLSchemeHandler {\n    private var router: Router\n    private var serverUrl: URL?\n\n    public init(router: Router) {\n        self.router = router\n        super.init()\n    }\n\n    open func setAssetPath(_ assetPath: String) {\n        router.basePath = assetPath\n    }\n\n    open func setServerUrl(_ serverUrl: URL?) {\n        self.serverUrl = serverUrl\n    }\n\n    private func isUsingLiveReload(_ localUrl: URL) -> Bool {\n        return self.serverUrl != nil && self.serverUrl?.scheme != localUrl.scheme\n    }\n\n    open func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {\n        let startPath: String\n        let url = urlSchemeTask.request.url!\n        let stringToLoad = url.path\n        let localUrl = URL.init(string: url.absoluteString)!\n\n        if url.path.starts(with: CapacitorBridge.httpInterceptorStartIdentifier) {\n            handleCapacitorHttpRequest(urlSchemeTask, localUrl, false)\n            return\n        }\n\n        if stringToLoad.starts(with: CapacitorBridge.fileStartIdentifier) {\n            startPath = stringToLoad.replacingOccurrences(of: CapacitorBridge.fileStartIdentifier, with: \"\")\n        } else {\n            startPath = router.route(for: stringToLoad)\n        }\n\n        let fileUrl = URL.init(fileURLWithPath: startPath)\n\n        do {\n            var data = Data()\n            let mimeType = mimeTypeForExtension(pathExtension: url.pathExtension)\n            var headers =  [\n                \"Content-Type\": mimeType,\n                \"Cache-Control\": \"no-cache\"\n            ]\n\n            // if using live reload, then set CORS headers\n            if isUsingLiveReload(localUrl) {\n                headers[\"Access-Control-Allow-Origin\"] = self.serverUrl?.absoluteString\n                headers[\"Access-Control-Allow-Methods\"] = \"GET, HEAD, OPTIONS, TRACE\"\n            }\n\n            if let rangeString = urlSchemeTask.request.value(forHTTPHeaderField: \"Range\"),\n               let totalSize = try fileUrl.resourceValues(forKeys: [.fileSizeKey]).fileSize {\n                let fileHandle = try FileHandle(forReadingFrom: fileUrl)\n                let parts = rangeString.components(separatedBy: \"=\")\n                let streamParts = parts[1].components(separatedBy: \"-\")\n                let fromRange = Int(streamParts[0]) ?? 0\n                var toRange = totalSize - 1\n                if streamParts.count > 1 {\n                    toRange = Int(streamParts[1]) ?? toRange\n                }\n                let rangeLength = toRange - fromRange + 1\n                try fileHandle.seek(toOffset: UInt64(fromRange))\n                data = fileHandle.readData(ofLength: rangeLength)\n                headers[\"Accept-Ranges\"] = \"bytes\"\n                headers[\"Content-Range\"] = \"bytes \\(fromRange)-\\(toRange)/\\(totalSize)\"\n                headers[\"Content-Length\"] = String(data.count)\n                let response = HTTPURLResponse(url: localUrl, statusCode: 206, httpVersion: nil, headerFields: headers)\n                urlSchemeTask.didReceive(response!)\n                try fileHandle.close()\n            } else {\n                if !stringToLoad.contains(\"cordova.js\") {\n                    if isMediaExtension(pathExtension: url.pathExtension) {\n                        data = try Data(contentsOf: fileUrl, options: Data.ReadingOptions.mappedIfSafe)\n                    } else {\n                        data = try Data(contentsOf: fileUrl)\n                    }\n                }\n                let urlResponse = URLResponse(url: localUrl, mimeType: mimeType, expectedContentLength: data.count, textEncodingName: nil)\n                let httpResponse = HTTPURLResponse(url: localUrl, statusCode: 200, httpVersion: nil, headerFields: headers)\n                if isMediaExtension(pathExtension: url.pathExtension) {\n                    urlSchemeTask.didReceive(urlResponse)\n                } else {\n                    urlSchemeTask.didReceive(httpResponse!)\n                }\n            }\n            urlSchemeTask.didReceive(data)\n        } catch let error as NSError {\n            urlSchemeTask.didFailWithError(error)\n            return\n        }\n        urlSchemeTask.didFinish()\n    }\n\n    open func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {\n        urlSchemeTask.stopped = true\n    }\n\n    open func mimeTypeForExtension(pathExtension: String) -> String {\n        if !pathExtension.isEmpty {\n            if let uti = UTType(filenameExtension: pathExtension) {\n                if let mimetype = uti.preferredMIMEType {\n                    return mimetype\n                }\n            }\n            // TODO: Remove when deployment target is set to iOS 17\n            if let mimeType = mimeTypes[pathExtension] {\n                return mimeType\n            }\n            return \"application/octet-stream\"\n        }\n        return \"text/html\"\n    }\n\n    open func isMediaExtension(pathExtension: String) -> Bool {\n        let mediaExtensions = [\"m4v\", \"mov\", \"mp4\",\n                               \"aac\", \"ac3\", \"aiff\", \"au\", \"flac\", \"m4a\", \"mp3\", \"wav\"]\n        if mediaExtensions.contains(pathExtension.lowercased()) {\n            return true\n        }\n        return false\n    }\n\n    func handleCapacitorHttpRequest(_ urlSchemeTask: WKURLSchemeTask, _ localUrl: URL, _ isHttpsRequest: Bool) {\n        var urlRequest = urlSchemeTask.request\n        guard let url = urlRequest.url else { return }\n\n        let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)\n        if let targetUrl = urlComponents?.queryItems?.first(where: { $0.name == CapacitorBridge.httpInterceptorUrlParam })?.value,\n           !targetUrl.isEmpty {\n            urlRequest.url = URL(string: targetUrl)\n        }\n\n        let urlSession = URLSession.shared\n        let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in\n            DispatchQueue.main.async {\n                guard !urlSchemeTask.stopped else { return }\n                if let error = error {\n                    urlSchemeTask.didFailWithError(error)\n                    return\n                }\n\n                if let response = response as? HTTPURLResponse {\n                    let existingHeaders = response.allHeaderFields\n                    var newHeaders: [AnyHashable: Any] = [:]\n\n                    // if using live reload, then set CORS headers\n                    if self.isUsingLiveReload(url) {\n                        newHeaders = [\n                            \"Access-Control-Allow-Origin\": self.serverUrl?.absoluteString ?? \"\",\n                            \"Access-Control-Allow-Methods\": \"GET, HEAD, OPTIONS, TRACE\"\n                        ]\n                    }\n\n                    if let mergedHeaders = existingHeaders.merging(newHeaders, uniquingKeysWith: { (_, newHeaders) in newHeaders }) as? [String: String] {\n\n                        if let responseUrl = response.url {\n                            if let modifiedResponse = HTTPURLResponse(\n                                url: responseUrl,\n                                statusCode: response.statusCode,\n                                httpVersion: nil,\n                                headerFields: mergedHeaders\n                            ) {\n                                urlSchemeTask.didReceive(modifiedResponse)\n                            }\n                        }\n\n                        if let data = data {\n                            urlSchemeTask.didReceive(data)\n                        }\n                    }\n                }\n                urlSchemeTask.didFinish()\n                return\n            }\n        }\n\n        task.resume()\n    }\n\n    public let mimeTypes = [\n        \"aaf\": \"application/octet-stream\",\n        \"aca\": \"application/octet-stream\",\n        \"accdb\": \"application/msaccess\",\n        \"accde\": \"application/msaccess\",\n        \"accdt\": \"application/msaccess\",\n        \"acx\": \"application/internet-property-stream\",\n        \"afm\": \"application/octet-stream\",\n        \"ai\": \"application/postscript\",\n        \"aif\": \"audio/x-aiff\",\n        \"aifc\": \"audio/aiff\",\n        \"aiff\": \"audio/aiff\",\n        \"application\": \"application/x-ms-application\",\n        \"art\": \"image/x-jg\",\n        \"asd\": \"application/octet-stream\",\n        \"asf\": \"video/x-ms-asf\",\n        \"asi\": \"application/octet-stream\",\n        \"asm\": \"text/plain\",\n        \"asr\": \"video/x-ms-asf\",\n        \"asx\": \"video/x-ms-asf\",\n        \"atom\": \"application/atom+xml\",\n        \"au\": \"audio/basic\",\n        \"avi\": \"video/x-msvideo\",\n        \"axs\": \"application/olescript\",\n        \"bas\": \"text/plain\",\n        \"bcpio\": \"application/x-bcpio\",\n        \"bin\": \"application/octet-stream\",\n        \"bmp\": \"image/bmp\",\n        \"c\": \"text/plain\",\n        \"cab\": \"application/octet-stream\",\n        \"calx\": \"application/vnd.ms-office.calx\",\n        \"cat\": \"application/vnd.ms-pki.seccat\",\n        \"cdf\": \"application/x-cdf\",\n        \"chm\": \"application/octet-stream\",\n        \"class\": \"application/x-java-applet\",\n        \"clp\": \"application/x-msclip\",\n        \"cmx\": \"image/x-cmx\",\n        \"cnf\": \"text/plain\",\n        \"cod\": \"image/cis-cod\",\n        \"cpio\": \"application/x-cpio\",\n        \"cpp\": \"text/plain\",\n        \"crd\": \"application/x-mscardfile\",\n        \"crl\": \"application/pkix-crl\",\n        \"crt\": \"application/x-x509-ca-cert\",\n        \"csh\": \"application/x-csh\",\n        \"css\": \"text/css\",\n        \"csv\": \"application/octet-stream\",\n        \"cur\": \"application/octet-stream\",\n        \"dcr\": \"application/x-director\",\n        \"deploy\": \"application/octet-stream\",\n        \"der\": \"application/x-x509-ca-cert\",\n        \"dib\": \"image/bmp\",\n        \"dir\": \"application/x-director\",\n        \"disco\": \"text/xml\",\n        \"dll\": \"application/x-msdownload\",\n        \"dll.config\": \"text/xml\",\n        \"dlm\": \"text/dlm\",\n        \"doc\": \"application/msword\",\n        \"docm\": \"application/vnd.ms-word.document.macroEnabled.12\",\n        \"docx\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n        \"dot\": \"application/msword\",\n        \"dotm\": \"application/vnd.ms-word.template.macroEnabled.12\",\n        \"dotx\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.template\",\n        \"dsp\": \"application/octet-stream\",\n        \"dtd\": \"text/xml\",\n        \"dvi\": \"application/x-dvi\",\n        \"dwf\": \"drawing/x-dwf\",\n        \"dwp\": \"application/octet-stream\",\n        \"dxr\": \"application/x-director\",\n        \"eml\": \"message/rfc822\",\n        \"emz\": \"application/octet-stream\",\n        \"eot\": \"application/octet-stream\",\n        \"eps\": \"application/postscript\",\n        \"etx\": \"text/x-setext\",\n        \"evy\": \"application/envoy\",\n        \"exe\": \"application/octet-stream\",\n        \"exe.config\": \"text/xml\",\n        \"fdf\": \"application/vnd.fdf\",\n        \"fif\": \"application/fractals\",\n        \"fla\": \"application/octet-stream\",\n        \"flr\": \"x-world/x-vrml\",\n        \"flv\": \"video/x-flv\",\n        \"gif\": \"image/gif\",\n        \"gtar\": \"application/x-gtar\",\n        \"gz\": \"application/x-gzip\",\n        \"h\": \"text/plain\",\n        \"hdf\": \"application/x-hdf\",\n        \"hdml\": \"text/x-hdml\",\n        \"hhc\": \"application/x-oleobject\",\n        \"hhk\": \"application/octet-stream\",\n        \"hhp\": \"application/octet-stream\",\n        \"hlp\": \"application/winhlp\",\n        \"hqx\": \"application/mac-binhex40\",\n        \"hta\": \"application/hta\",\n        \"htc\": \"text/x-component\",\n        \"htm\": \"text/html\",\n        \"html\": \"text/html\",\n        \"htt\": \"text/webviewhtml\",\n        \"hxt\": \"text/html\",\n        \"ico\": \"image/x-icon\",\n        \"ics\": \"application/octet-stream\",\n        \"ief\": \"image/ief\",\n        \"iii\": \"application/x-iphone\",\n        \"inf\": \"application/octet-stream\",\n        \"ins\": \"application/x-internet-signup\",\n        \"isp\": \"application/x-internet-signup\",\n        \"IVF\": \"video/x-ivf\",\n        \"jar\": \"application/java-archive\",\n        \"java\": \"application/octet-stream\",\n        \"jck\": \"application/liquidmotion\",\n        \"jcz\": \"application/liquidmotion\",\n        \"jfif\": \"image/pjpeg\",\n        \"jpb\": \"application/octet-stream\",\n        \"jpe\": \"image/jpeg\",\n        \"jpeg\": \"image/jpeg\",\n        \"jpg\": \"image/jpeg\",\n        \"js\": \"application/x-javascript\",\n        \"jsx\": \"text/jscript\",\n        \"latex\": \"application/x-latex\",\n        \"lit\": \"application/x-ms-reader\",\n        \"lpk\": \"application/octet-stream\",\n        \"lsf\": \"video/x-la-asf\",\n        \"lsx\": \"video/x-la-asf\",\n        \"lzh\": \"application/octet-stream\",\n        \"m13\": \"application/x-msmediaview\",\n        \"m14\": \"application/x-msmediaview\",\n        \"m1v\": \"video/mpeg\",\n        \"m3u\": \"audio/x-mpegurl\",\n        \"man\": \"application/x-troff-man\",\n        \"manifest\": \"application/x-ms-manifest\",\n        \"map\": \"text/plain\",\n        \"mdb\": \"application/x-msaccess\",\n        \"mdp\": \"application/octet-stream\",\n        \"me\": \"application/x-troff-me\",\n        \"mht\": \"message/rfc822\",\n        \"mhtml\": \"message/rfc822\",\n        \"mid\": \"audio/mid\",\n        \"midi\": \"audio/mid\",\n        \"mix\": \"application/octet-stream\",\n        \"mmf\": \"application/x-smaf\",\n        \"mno\": \"text/xml\",\n        \"mny\": \"application/x-msmoney\",\n        \"mov\": \"video/quicktime\",\n        \"movie\": \"video/x-sgi-movie\",\n        \"mp2\": \"video/mpeg\",\n        \"mp3\": \"audio/mpeg\",\n        \"mpa\": \"video/mpeg\",\n        \"mpe\": \"video/mpeg\",\n        \"mpeg\": \"video/mpeg\",\n        \"mpg\": \"video/mpeg\",\n        \"mpp\": \"application/vnd.ms-project\",\n        \"mpv2\": \"video/mpeg\",\n        \"ms\": \"application/x-troff-ms\",\n        \"msi\": \"application/octet-stream\",\n        \"mso\": \"application/octet-stream\",\n        \"mvb\": \"application/x-msmediaview\",\n        \"mvc\": \"application/x-miva-compiled\",\n        \"nc\": \"application/x-netcdf\",\n        \"nsc\": \"video/x-ms-asf\",\n        \"nws\": \"message/rfc822\",\n        \"ocx\": \"application/octet-stream\",\n        \"oda\": \"application/oda\",\n        \"odc\": \"text/x-ms-odc\",\n        \"ods\": \"application/oleobject\",\n        \"one\": \"application/onenote\",\n        \"onea\": \"application/onenote\",\n        \"onetoc\": \"application/onenote\",\n        \"onetoc2\": \"application/onenote\",\n        \"onetmp\": \"application/onenote\",\n        \"onepkg\": \"application/onenote\",\n        \"osdx\": \"application/opensearchdescription+xml\",\n        \"p10\": \"application/pkcs10\",\n        \"p12\": \"application/x-pkcs12\",\n        \"p7b\": \"application/x-pkcs7-certificates\",\n        \"p7c\": \"application/pkcs7-mime\",\n        \"p7m\": \"application/pkcs7-mime\",\n        \"p7r\": \"application/x-pkcs7-certreqresp\",\n        \"p7s\": \"application/pkcs7-signature\",\n        \"pbm\": \"image/x-portable-bitmap\",\n        \"pcx\": \"application/octet-stream\",\n        \"pcz\": \"application/octet-stream\",\n        \"pdf\": \"application/pdf\",\n        \"pfb\": \"application/octet-stream\",\n        \"pfm\": \"application/octet-stream\",\n        \"pfx\": \"application/x-pkcs12\",\n        \"pgm\": \"image/x-portable-graymap\",\n        \"pko\": \"application/vnd.ms-pki.pko\",\n        \"pma\": \"application/x-perfmon\",\n        \"pmc\": \"application/x-perfmon\",\n        \"pml\": \"application/x-perfmon\",\n        \"pmr\": \"application/x-perfmon\",\n        \"pmw\": \"application/x-perfmon\",\n        \"png\": \"image/png\",\n        \"pnm\": \"image/x-portable-anymap\",\n        \"pnz\": \"image/png\",\n        \"pot\": \"application/vnd.ms-powerpoint\",\n        \"potm\": \"application/vnd.ms-powerpoint.template.macroEnabled.12\",\n        \"potx\": \"application/vnd.openxmlformats-officedocument.presentationml.template\",\n        \"ppam\": \"application/vnd.ms-powerpoint.addin.macroEnabled.12\",\n        \"ppm\": \"image/x-portable-pixmap\",\n        \"pps\": \"application/vnd.ms-powerpoint\",\n        \"ppsm\": \"application/vnd.ms-powerpoint.slideshow.macroEnabled.12\",\n        \"ppsx\": \"application/vnd.openxmlformats-officedocument.presentationml.slideshow\",\n        \"ppt\": \"application/vnd.ms-powerpoint\",\n        \"pptm\": \"application/vnd.ms-powerpoint.presentation.macroEnabled.12\",\n        \"pptx\": \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n        \"prf\": \"application/pics-rules\",\n        \"prm\": \"application/octet-stream\",\n        \"prx\": \"application/octet-stream\",\n        \"ps\": \"application/postscript\",\n        \"psd\": \"application/octet-stream\",\n        \"psm\": \"application/octet-stream\",\n        \"psp\": \"application/octet-stream\",\n        \"pub\": \"application/x-mspublisher\",\n        \"qt\": \"video/quicktime\",\n        \"qtl\": \"application/x-quicktimeplayer\",\n        \"qxd\": \"application/octet-stream\",\n        \"ra\": \"audio/x-pn-realaudio\",\n        \"ram\": \"audio/x-pn-realaudio\",\n        \"rar\": \"application/octet-stream\",\n        \"ras\": \"image/x-cmu-raster\",\n        \"rf\": \"image/vnd.rn-realflash\",\n        \"rgb\": \"image/x-rgb\",\n        \"rm\": \"application/vnd.rn-realmedia\",\n        \"rmi\": \"audio/mid\",\n        \"roff\": \"application/x-troff\",\n        \"rpm\": \"audio/x-pn-realaudio-plugin\",\n        \"rtf\": \"application/rtf\",\n        \"rtx\": \"text/richtext\",\n        \"scd\": \"application/x-msschedule\",\n        \"sct\": \"text/scriptlet\",\n        \"sea\": \"application/octet-stream\",\n        \"setpay\": \"application/set-payment-initiation\",\n        \"setreg\": \"application/set-registration-initiation\",\n        \"sgml\": \"text/sgml\",\n        \"sh\": \"application/x-sh\",\n        \"shar\": \"application/x-shar\",\n        \"sit\": \"application/x-stuffit\",\n        \"sldm\": \"application/vnd.ms-powerpoint.slide.macroEnabled.12\",\n        \"sldx\": \"application/vnd.openxmlformats-officedocument.presentationml.slide\",\n        \"smd\": \"audio/x-smd\",\n        \"smi\": \"application/octet-stream\",\n        \"smx\": \"audio/x-smd\",\n        \"smz\": \"audio/x-smd\",\n        \"snd\": \"audio/basic\",\n        \"snp\": \"application/octet-stream\",\n        \"spc\": \"application/x-pkcs7-certificates\",\n        \"spl\": \"application/futuresplash\",\n        \"src\": \"application/x-wais-source\",\n        \"ssm\": \"application/streamingmedia\",\n        \"sst\": \"application/vnd.ms-pki.certstore\",\n        \"stl\": \"application/vnd.ms-pki.stl\",\n        \"sv4cpio\": \"application/x-sv4cpio\",\n        \"sv4crc\": \"application/x-sv4crc\",\n        \"svg\": \"image/svg+xml\",\n        \"swf\": \"application/x-shockwave-flash\",\n        \"t\": \"application/x-troff\",\n        \"tar\": \"application/x-tar\",\n        \"tcl\": \"application/x-tcl\",\n        \"tex\": \"application/x-tex\",\n        \"texi\": \"application/x-texinfo\",\n        \"texinfo\": \"application/x-texinfo\",\n        \"tgz\": \"application/x-compressed\",\n        \"thmx\": \"application/vnd.ms-officetheme\",\n        \"thn\": \"application/octet-stream\",\n        \"tif\": \"image/tiff\",\n        \"tiff\": \"image/tiff\",\n        \"toc\": \"application/octet-stream\",\n        \"tr\": \"application/x-troff\",\n        \"trm\": \"application/x-msterminal\",\n        \"tsv\": \"text/tab-separated-values\",\n        \"ttf\": \"application/octet-stream\",\n        \"txt\": \"text/plain\",\n        \"u32\": \"application/octet-stream\",\n        \"uls\": \"text/iuls\",\n        \"ustar\": \"application/x-ustar\",\n        \"vbs\": \"text/vbscript\",\n        \"vcf\": \"text/x-vcard\",\n        \"vcs\": \"text/plain\",\n        \"vdx\": \"application/vnd.ms-visio.viewer\",\n        \"vml\": \"text/xml\",\n        \"vsd\": \"application/vnd.visio\",\n        \"vss\": \"application/vnd.visio\",\n        \"vst\": \"application/vnd.visio\",\n        \"vsto\": \"application/x-ms-vsto\",\n        \"vsw\": \"application/vnd.visio\",\n        \"vsx\": \"application/vnd.visio\",\n        \"vtx\": \"application/vnd.visio\",\n        \"wasm\": \"application/wasm\",\n        \"wav\": \"audio/wav\",\n        \"wax\": \"audio/x-ms-wax\",\n        \"wbmp\": \"image/vnd.wap.wbmp\",\n        \"wcm\": \"application/vnd.ms-works\",\n        \"wdb\": \"application/vnd.ms-works\",\n        \"wks\": \"application/vnd.ms-works\",\n        \"wm\": \"video/x-ms-wm\",\n        \"wma\": \"audio/x-ms-wma\",\n        \"wmd\": \"application/x-ms-wmd\",\n        \"wmf\": \"application/x-msmetafile\",\n        \"wml\": \"text/vnd.wap.wml\",\n        \"wmlc\": \"application/vnd.wap.wmlc\",\n        \"wmls\": \"text/vnd.wap.wmlscript\",\n        \"wmlsc\": \"application/vnd.wap.wmlscriptc\",\n        \"wmp\": \"video/x-ms-wmp\",\n        \"wmv\": \"video/x-ms-wmv\",\n        \"wmx\": \"video/x-ms-wmx\",\n        \"wmz\": \"application/x-ms-wmz\",\n        \"wps\": \"application/vnd.ms-works\",\n        \"wri\": \"application/x-mswrite\",\n        \"wrl\": \"x-world/x-vrml\",\n        \"wrz\": \"x-world/x-vrml\",\n        \"wsdl\": \"text/xml\",\n        \"wvx\": \"video/x-ms-wvx\",\n        \"x\": \"application/directx\",\n        \"xaf\": \"x-world/x-vrml\",\n        \"xaml\": \"application/xaml+xml\",\n        \"xap\": \"application/x-silverlight-app\",\n        \"xbap\": \"application/x-ms-xbap\",\n        \"xbm\": \"image/x-xbitmap\",\n        \"xdr\": \"text/plain\",\n        \"xht\": \"application/xhtml+xml\",\n        \"xhtml\": \"application/xhtml+xml\",\n        \"xla\": \"application/vnd.ms-excel\",\n        \"xlam\": \"application/vnd.ms-excel.addin.macroEnabled.12\",\n        \"xlc\": \"application/vnd.ms-excel\",\n        \"xlm\": \"application/vnd.ms-excel\",\n        \"xls\": \"application/vnd.ms-excel\",\n        \"xlsb\": \"application/vnd.ms-excel.sheet.binary.macroEnabled.12\",\n        \"xlsm\": \"application/vnd.ms-excel.sheet.macroEnabled.12\",\n        \"xlsx\": \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n        \"xlt\": \"application/vnd.ms-excel\",\n        \"xltm\": \"application/vnd.ms-excel.template.macroEnabled.12\",\n        \"xltx\": \"application/vnd.openxmlformats-officedocument.spreadsheetml.template\",\n        \"xlw\": \"application/vnd.ms-excel\",\n        \"xml\": \"text/xml\",\n        \"xof\": \"x-world/x-vrml\",\n        \"xpm\": \"image/x-xpixmap\",\n        \"xps\": \"application/vnd.ms-xpsdocument\",\n        \"xsd\": \"text/xml\",\n        \"xsf\": \"text/xml\",\n        \"xsl\": \"text/xml\",\n        \"xslt\": \"text/xml\",\n        \"xsn\": \"application/octet-stream\",\n        \"xtp\": \"application/octet-stream\",\n        \"xwd\": \"image/x-xwindowdump\",\n        \"z\": \"application/x-compress\",\n        \"zip\": \"application/x-zip-compressed\"\n    ]\n}\n\nprivate var stoppedKey = malloc(1)\n\nprivate extension WKURLSchemeTask {\n    var stopped: Bool {\n        get {\n            return objc_getAssociatedObject(self, &stoppedKey) as? Bool ?? false\n        }\n        set {\n            objc_setAssociatedObject(self, &stoppedKey, newValue, .OBJC_ASSOCIATION_ASSIGN)\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/WebViewDelegationHandler.swift",
    "content": "import Foundation\nimport WebKit\n\n// adopting a public protocol in an internal class is by design\n// swiftlint:disable lower_acl_than_parent\n@objc(CAPWebViewDelegationHandler)\nopen class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler, UIScrollViewDelegate {\n    public internal(set) weak var bridge: CapacitorBridge?\n    open fileprivate(set) var contentController = WKUserContentController()\n    enum WebViewLoadingState {\n        case unloaded\n        case initialLoad(isOpaque: Bool)\n        case subsequentLoad\n    }\n\n    fileprivate(set) var webViewLoadingState = WebViewLoadingState.unloaded\n\n    private let handlerName = \"bridge\"\n\n    override public init() {\n        super.init()\n        contentController.add(self, name: handlerName)\n    }\n\n    open func cleanUp() {\n        contentController.removeScriptMessageHandler(forName: handlerName)\n    }\n\n    open func willLoadWebview(_ webView: WKWebView?) {\n        // Set the webview to be not opaque on the inital load. This prevents\n        // the webview from showing a white background, which is its default\n        // loading display, as that can appear as a screen flash. The opacity\n        // might have been set by something else, like a plugin, so we want\n        // to save the current value so it can be reset on success or failure.\n        if let webView = webView, case .unloaded = webViewLoadingState {\n            webViewLoadingState = .initialLoad(isOpaque: webView.isOpaque)\n            webView.isOpaque = false\n        }\n    }\n\n    // MARK: - WKNavigationDelegate\n\n    // The force unwrap is part of the protocol declaration, so we should keep it.\n    // swiftlint:disable:next implicitly_unwrapped_optional\n    open func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {\n        // Reset the bridge on each navigation\n        bridge?.reset()\n    }\n\n    open func webView(\n        _ webView: WKWebView,\n        requestMediaCapturePermissionFor origin: WKSecurityOrigin,\n        initiatedByFrame frame: WKFrameInfo,\n        type: WKMediaCaptureType,\n        decisionHandler: @escaping (WKPermissionDecision) -> Void\n    ) {\n        decisionHandler(.grant)\n    }\n\n    open func webView(_ webView: WKWebView,\n                      requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin,\n                      initiatedByFrame frame: WKFrameInfo,\n                      decisionHandler: @escaping (WKPermissionDecision) -> Void) {\n        decisionHandler(.grant)\n    }\n\n    open func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {\n        // post a notification for any listeners\n        NotificationCenter.default.post(name: .capacitorDecidePolicyForNavigationAction, object: navigationAction)\n\n        // sanity check, these shouldn't ever be nil in practice\n        guard let bridge = bridge, let navURL = navigationAction.request.url else {\n            decisionHandler(.allow)\n            return\n        }\n\n        // first, give plugins the chance to handle the decision\n        for pluginObject in bridge.plugins {\n            let plugin = pluginObject.value\n            let selector = NSSelectorFromString(\"shouldOverrideLoad:\")\n            if plugin.responds(to: selector) {\n                let shouldOverrideLoad = plugin.shouldOverrideLoad(navigationAction)\n                if shouldOverrideLoad != nil {\n                    if shouldOverrideLoad == true {\n                        decisionHandler(.cancel)\n                        return\n                    } else if shouldOverrideLoad == false {\n                        decisionHandler(.allow)\n                        return\n                    }\n                }\n            }\n        }\n\n        // next, check if this is covered by the allowedNavigation configuration\n        if let host = navURL.host, bridge.config.shouldAllowNavigation(to: host) {\n            decisionHandler(.allow)\n            return\n        }\n\n        // otherwise, is this a new window or a main frame navigation but to an outside source\n        let toplevelNavigation = (navigationAction.targetFrame == nil || navigationAction.targetFrame?.isMainFrame == true)\n\n        // Check if the url being navigated to is configured as an application url (whether local or remote)\n        let isApplicationNavigation = navURL.absoluteString.starts(with: bridge.config.serverURL.absoluteString) ||\n            navURL.absoluteString.starts(with: bridge.config.localURL.absoluteString)\n\n        if !isApplicationNavigation, toplevelNavigation {\n            // disallow and let the system handle it\n            if UIApplication.shared.applicationState == .active {\n                UIApplication.shared.open(navURL, options: [:], completionHandler: nil)\n            }\n            decisionHandler(.cancel)\n            return\n        }\n\n        // fallthrough to allowing it\n        decisionHandler(.allow)\n    }\n\n    // The force unwrap is part of the protocol declaration, so we should keep it.\n    // swiftlint:disable:next implicitly_unwrapped_optional\n    open func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {\n        if case .initialLoad(let isOpaque) = webViewLoadingState {\n            webView.isOpaque = isOpaque\n            webViewLoadingState = .subsequentLoad\n        }\n        CAPLog.print(\"⚡️  WebView loaded\")\n    }\n\n    // The force unwrap is part of the protocol declaration, so we should keep it.\n    // swiftlint:disable:next implicitly_unwrapped_optional\n    open func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {\n        if case .initialLoad(let isOpaque) = webViewLoadingState {\n            webView.isOpaque = isOpaque\n            webViewLoadingState = .subsequentLoad\n        }\n\n        if let errorURL = bridge?.config.errorPathURL {\n            webView.load(URLRequest(url: errorURL))\n        }\n\n        CAPLog.print(\"⚡️  WebView failed to load\")\n        CAPLog.print(\"⚡️  Error: \" + error.localizedDescription)\n    }\n\n    // The force unwrap is part of the protocol declaration, so we should keep it.\n    // swiftlint:disable:next implicitly_unwrapped_optional\n    open func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {\n        if let errorURL = bridge?.config.errorPathURL {\n            webView.load(URLRequest(url: errorURL))\n        }\n\n        CAPLog.print(\"⚡️  WebView failed provisional navigation\")\n        CAPLog.print(\"⚡️  Error: \" + error.localizedDescription)\n    }\n\n    open func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {\n        CAPLog.print(\"⚡️  WebView process terminated\")\n        bridge?.reset()\n        webView.reload()\n    }\n\n    open func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping @MainActor (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {\n        guard let bridge = bridge else {\n            completionHandler(.rejectProtectionSpace, nil)\n            return\n        }\n\n        for pluginObject in bridge.plugins {\n            let plugin = pluginObject.value\n            let selector = NSSelectorFromString(\"handleWKWebViewURLAuthenticationChallenge:completionHandler:\")\n            if plugin.responds(to: selector) {\n                if plugin.handleWKWebViewURLAuthenticationChallenge(challenge, completionHandler: completionHandler) {\n                    return\n                }\n            }\n        }\n\n        completionHandler(.rejectProtectionSpace, nil)\n        return\n    }\n\n    // MARK: - WKScriptMessageHandler\n\n    open func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {\n        guard let bridge = bridge else {\n            return\n        }\n\n        let body = message.body\n        if let dict = body as? [String: Any] {\n            let type = dict[\"type\"] as? String ?? \"\"\n\n            if type == \"js.error\" {\n                if let error = dict[\"error\"] as? [String: Any] {\n                    logJSError(error)\n                }\n            } else if type == \"message\" {\n                let pluginId = dict[\"pluginId\"] as? String ?? \"\"\n                let method = dict[\"methodName\"] as? String ?? \"\"\n                let callbackId = dict[\"callbackId\"] as? String ?? \"\"\n\n                let options = dict[\"options\"] as? [String: Any] ?? [:]\n\n                if pluginId != \"Console\" {\n                    CAPLog.print(\"⚡️  To Native -> \", pluginId, method, callbackId)\n                }\n\n                bridge.handleJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId))\n            } else if type == \"cordova\" {\n                let pluginId = dict[\"service\"] as? String ?? \"\"\n                let method = dict[\"action\"] as? String ?? \"\"\n                let callbackId = dict[\"callbackId\"] as? String ?? \"\"\n\n                let args = dict[\"actionArgs\"] as? Array ?? []\n                let options = [\"options\": args]\n\n                CAPLog.print(\"To Native Cordova -> \", pluginId, method, callbackId, options)\n\n                bridge.handleCordovaJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId))\n            }\n        }\n    }\n\n    // MARK: - WKUIDelegate\n\n    open func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {\n        guard var viewController = bridge?.viewController else {\n            completionHandler()\n            return\n        }\n\n        if let presentedVC = viewController.presentedViewController, !presentedVC.isBeingDismissed {\n            viewController = presentedVC\n        }\n\n        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)\n\n        alertController.addAction(UIAlertAction(title: \"Ok\", style: .default, handler: { (_) in\n            completionHandler()\n        }))\n\n        viewController.present(alertController, animated: true, completion: nil)\n    }\n\n    open func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {\n        guard let viewController = bridge?.viewController else {\n            return\n        }\n\n        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)\n\n        alertController.addAction(UIAlertAction(title: \"Cancel\", style: .default, handler: { (_) in\n            completionHandler(false)\n        }))\n\n        alertController.addAction(UIAlertAction(title: \"Ok\", style: .default, handler: { (_) in\n            completionHandler(true)\n        }))\n\n        viewController.present(alertController, animated: true, completion: nil)\n    }\n\n    open func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {\n\n        // Check if this is synchronous cookie or http call\n        do {\n            if let dataFromString = prompt.data(using: .utf8, allowLossyConversion: false) {\n                if let payload = try JSONSerialization.jsonObject(with: dataFromString, options: .fragmentsAllowed) as? [String: AnyObject] {\n                    let type = payload[\"type\"] as? String\n\n                    if type == \"CapacitorCookies.get\" {\n                        completionHandler(CapacitorCookieManager(bridge!.config).getCookies())\n                        // Don't present prompt\n                        return\n                    } else if type == \"CapacitorCookies.set\" {\n                        // swiftlint:disable force_cast\n                        let action = payload[\"action\"] as! String\n                        let domain = payload[\"domain\"] as! String\n                        CapacitorCookieManager(bridge!.config).setCookie(domain, action)\n                        completionHandler(\"\")\n                        // swiftlint:enable force_cast\n                        // Don't present prompt\n                        return\n                    } else if type == \"CapacitorCookies.isEnabled\" {\n                        let pluginConfig = bridge!.config.getPluginConfig(\"CapacitorCookies\")\n                        completionHandler(String(pluginConfig.getBoolean(\"enabled\", false)))\n                        // Don't present prompt\n                        return\n                    } else if type == \"CapacitorHttp\" {\n                        let pluginConfig = bridge!.config.getPluginConfig(\"CapacitorHttp\")\n                        completionHandler(String(pluginConfig.getBoolean(\"enabled\", false)))\n                        // Don't present prompt\n                        return\n                    }\n                }\n            }\n        } catch {\n            // Continue with regular prompt\n        }\n\n        guard let viewController = bridge?.viewController else {\n            return\n        }\n\n        let alertController = UIAlertController(title: nil, message: prompt, preferredStyle: .alert)\n\n        alertController.addTextField { (textField) in\n            textField.text = defaultText\n        }\n\n        alertController.addAction(UIAlertAction(title: \"Cancel\", style: .default, handler: { (_) in\n            completionHandler(nil)\n        }))\n\n        alertController.addAction(UIAlertAction(title: \"Ok\", style: .default, handler: { (_) in\n            if let text = alertController.textFields?.first?.text {\n                completionHandler(text)\n            } else {\n                completionHandler(defaultText)\n            }\n        }))\n\n        viewController.present(alertController, animated: true, completion: nil)\n    }\n\n    open func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {\n        if let url = navigationAction.request.url {\n            UIApplication.shared.open(url, options: [:], completionHandler: nil)\n        }\n        return nil\n    }\n\n    // MARK: - UIScrollViewDelegate\n\n    // disable zooming in WKWebView ScrollView\n    open func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {\n        scrollView.pinchGestureRecognizer?.isEnabled = false\n    }\n\n    // MARK: - Private\n\n    private func logJSError(_ error: [String: Any]) {\n        let message = error[\"message\"] ?? \"No message\"\n        let url = error[\"url\"] as? String ?? \"\"\n        let line = error[\"line\"] ?? \"\"\n        let col = error[\"col\"] ?? \"\"\n        var filename = \"\"\n        if let filenameIndex = url.range(of: \"/\", options: .backwards)?.lowerBound {\n            let index = url.index(after: filenameIndex)\n            filename = String(url[index...])\n        }\n\n        CAPLog.print(\"\\n⚡️  ------ STARTUP JS ERROR ------\\n\")\n        CAPLog.print(\"⚡️  \\(message)\")\n        CAPLog.print(\"⚡️  URL: \\(url)\")\n        CAPLog.print(\"⚡️  \\(filename):\\(line):\\(col)\")\n        CAPLog.print(\"\\n⚡️  See above for help with debugging blank-screen issues\")\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor/assets/native-bridge.js",
    "content": "\n/*! Capacitor: https://capacitorjs.com/ - MIT License */\n/* Generated File. Do not edit. */\n\nvar nativeBridge = (function (exports) {\n    'use strict';\n\n    var ExceptionCode;\n    (function (ExceptionCode) {\n        /**\n         * API is not implemented.\n         *\n         * This usually means the API can't be used because it is not implemented for\n         * the current platform.\n         */\n        ExceptionCode[\"Unimplemented\"] = \"UNIMPLEMENTED\";\n        /**\n         * API is not available.\n         *\n         * This means the API can't be used right now because:\n         *   - it is currently missing a prerequisite, such as network connectivity\n         *   - it requires a particular platform or browser version\n         */\n        ExceptionCode[\"Unavailable\"] = \"UNAVAILABLE\";\n    })(ExceptionCode || (ExceptionCode = {}));\n    class CapacitorException extends Error {\n        constructor(message, code, data) {\n            super(message);\n            this.message = message;\n            this.code = code;\n            this.data = data;\n        }\n    }\n\n    // For removing exports for iOS/Android, keep let for reassignment\n    // eslint-disable-next-line\n    let dummy = {};\n    const readFileAsBase64 = (file) => new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.onloadend = () => {\n            const data = reader.result;\n            resolve(btoa(data));\n        };\n        reader.onerror = reject;\n        reader.readAsBinaryString(file);\n    });\n    const convertFormData = async (formData) => {\n        const newFormData = [];\n        for (const pair of formData.entries()) {\n            const [key, value] = pair;\n            if (value instanceof File) {\n                const base64File = await readFileAsBase64(value);\n                newFormData.push({\n                    key,\n                    value: base64File,\n                    type: 'base64File',\n                    contentType: value.type,\n                    fileName: value.name,\n                });\n            }\n            else {\n                newFormData.push({ key, value, type: 'string' });\n            }\n        }\n        return newFormData;\n    };\n    const convertBody = async (body, contentType) => {\n        if (body instanceof ReadableStream || body instanceof Uint8Array) {\n            let encodedData;\n            if (body instanceof ReadableStream) {\n                const reader = body.getReader();\n                const chunks = [];\n                while (true) {\n                    const { done, value } = await reader.read();\n                    if (done)\n                        break;\n                    chunks.push(value);\n                }\n                const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));\n                let position = 0;\n                for (const chunk of chunks) {\n                    concatenated.set(chunk, position);\n                    position += chunk.length;\n                }\n                encodedData = concatenated;\n            }\n            else {\n                encodedData = body;\n            }\n            let data = new TextDecoder().decode(encodedData);\n            let type;\n            if (contentType === 'application/json') {\n                try {\n                    data = JSON.parse(data);\n                }\n                catch (ignored) {\n                    // ignore\n                }\n                type = 'json';\n            }\n            else if (contentType === 'multipart/form-data') {\n                type = 'formData';\n            }\n            else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) {\n                type = 'image';\n            }\n            else if (contentType === 'application/octet-stream') {\n                type = 'binary';\n            }\n            else {\n                type = 'text';\n            }\n            return {\n                data,\n                type,\n                headers: { 'Content-Type': contentType || 'application/octet-stream' },\n            };\n        }\n        else if (body instanceof URLSearchParams) {\n            return {\n                data: body.toString(),\n                type: 'text',\n            };\n        }\n        else if (body instanceof FormData) {\n            return {\n                data: await convertFormData(body),\n                type: 'formData',\n            };\n        }\n        else if (body instanceof File) {\n            const fileData = await readFileAsBase64(body);\n            return {\n                data: fileData,\n                type: 'file',\n                headers: { 'Content-Type': body.type },\n            };\n        }\n        return { data: body, type: 'json' };\n    };\n    const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_';\n    const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u';\n    // TODO: export as Cap function\n    const isRelativeOrProxyUrl = (url) => !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1;\n    // TODO: export as Cap function\n    const createProxyUrl = (url, win) => {\n        var _a, _b;\n        if (isRelativeOrProxyUrl(url))\n            return url;\n        const bridgeUrl = new URL((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 ? _b : '');\n        bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR;\n        bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url);\n        return bridgeUrl.toString();\n    };\n    const initBridge = (w) => {\n        const getPlatformId = (win) => {\n            var _a, _b;\n            if (win === null || win === void 0 ? void 0 : win.androidBridge) {\n                return 'android';\n            }\n            else if ((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) {\n                return 'ios';\n            }\n            else {\n                return 'web';\n            }\n        };\n        const convertFileSrcServerUrl = (webviewServerUrl, filePath) => {\n            if (typeof filePath === 'string') {\n                if (filePath.startsWith('/')) {\n                    return webviewServerUrl + '/_capacitor_file_' + filePath;\n                }\n                else if (filePath.startsWith('file://')) {\n                    return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_');\n                }\n                else if (filePath.startsWith('content://')) {\n                    return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_');\n                }\n            }\n            return filePath;\n        };\n        const initEvents = (win, cap) => {\n            cap.addListener = (pluginName, eventName, callback) => {\n                const callbackId = cap.nativeCallback(pluginName, 'addListener', {\n                    eventName: eventName,\n                }, callback);\n                return {\n                    remove: async () => {\n                        var _a;\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.debug('Removing listener', pluginName, eventName);\n                        cap.removeListener(pluginName, callbackId, eventName, callback);\n                    },\n                };\n            };\n            cap.removeListener = (pluginName, callbackId, eventName, callback) => {\n                cap.nativeCallback(pluginName, 'removeListener', {\n                    callbackId: callbackId,\n                    eventName: eventName,\n                }, callback);\n            };\n            cap.createEvent = (eventName, eventData) => {\n                const doc = win.document;\n                if (doc) {\n                    const ev = doc.createEvent('Events');\n                    ev.initEvent(eventName, false, false);\n                    if (eventData && typeof eventData === 'object') {\n                        for (const i in eventData) {\n                            // eslint-disable-next-line no-prototype-builtins\n                            if (eventData.hasOwnProperty(i)) {\n                                ev[i] = eventData[i];\n                            }\n                        }\n                    }\n                    return ev;\n                }\n                return null;\n            };\n            cap.triggerEvent = (eventName, target, eventData) => {\n                const doc = win.document;\n                const cordova = win.cordova;\n                eventData = eventData || {};\n                const ev = cap.createEvent(eventName, eventData);\n                if (ev) {\n                    if (target === 'document') {\n                        if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) {\n                            cordova.fireDocumentEvent(eventName, eventData);\n                            return true;\n                        }\n                        else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) {\n                            return doc.dispatchEvent(ev);\n                        }\n                    }\n                    else if (target === 'window' && win.dispatchEvent) {\n                        return win.dispatchEvent(ev);\n                    }\n                    else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) {\n                        const targetEl = doc.querySelector(target);\n                        if (targetEl) {\n                            return targetEl.dispatchEvent(ev);\n                        }\n                    }\n                }\n                return false;\n            };\n            win.Capacitor = cap;\n        };\n        const initLegacyHandlers = (win, cap) => {\n            // define cordova if it's not there already\n            win.cordova = win.cordova || {};\n            const doc = win.document;\n            const nav = win.navigator;\n            if (nav) {\n                nav.app = nav.app || {};\n                nav.app.exitApp = () => {\n                    var _a;\n                    if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) {\n                        win.console.warn('App plugin not installed');\n                    }\n                    else {\n                        cap.nativeCallback('App', 'exitApp', {});\n                    }\n                };\n            }\n            if (doc) {\n                const docAddEventListener = doc.addEventListener;\n                doc.addEventListener = (...args) => {\n                    var _a;\n                    const eventName = args[0];\n                    const handler = args[1];\n                    if (eventName === 'deviceready' && handler) {\n                        Promise.resolve().then(handler);\n                    }\n                    else if (eventName === 'backbutton' && cap.Plugins.App) {\n                        // Add a dummy listener so Capacitor doesn't do the default\n                        // back button action\n                        if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) {\n                            win.console.warn('App plugin not installed');\n                        }\n                        else {\n                            cap.Plugins.App.addListener('backButton', () => {\n                                // ignore\n                            });\n                        }\n                    }\n                    return docAddEventListener.apply(doc, args);\n                };\n            }\n            win.Capacitor = cap;\n        };\n        const initVendor = (win, cap) => {\n            const Ionic = (win.Ionic = win.Ionic || {});\n            const IonicWebView = (Ionic.WebView = Ionic.WebView || {});\n            const Plugins = cap.Plugins;\n            IonicWebView.getServerBasePath = (callback) => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.getServerBasePath().then((result) => {\n                    callback(result.path);\n                });\n            };\n            IonicWebView.setServerAssetPath = (path) => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerAssetPath({ path });\n            };\n            IonicWebView.setServerBasePath = (path) => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerBasePath({ path });\n            };\n            IonicWebView.persistServerBasePath = () => {\n                var _a;\n                (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.persistServerBasePath();\n            };\n            IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url);\n            win.Capacitor = cap;\n            win.Ionic.WebView = IonicWebView;\n        };\n        const initLogger = (win, cap) => {\n            const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn'];\n            const createLogFromNative = (c) => (result) => {\n                if (isFullConsole(c)) {\n                    const success = result.success === true;\n                    const tagStyles = success\n                        ? 'font-style: italic; font-weight: lighter; color: gray'\n                        : 'font-style: italic; font-weight: lighter; color: red';\n                    c.groupCollapsed('%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', tagStyles, 'font-style: italic; font-weight: bold; color: #444');\n                    if (result.success === false) {\n                        c.error(result.error);\n                    }\n                    else {\n                        c.dir(JSON.stringify(result.data));\n                    }\n                    c.groupEnd();\n                }\n                else {\n                    if (result.success === false) {\n                        c.error('LOG FROM NATIVE', result.error);\n                    }\n                    else {\n                        c.log('LOG FROM NATIVE', result.data);\n                    }\n                }\n            };\n            const createLogToNative = (c) => (call) => {\n                if (isFullConsole(c)) {\n                    c.groupCollapsed('%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', 'font-weight: lighter; color: gray', 'font-weight: bold; color: #000');\n                    c.dir(call);\n                    c.groupEnd();\n                }\n                else {\n                    c.log('LOG TO NATIVE: ', call);\n                }\n            };\n            const isFullConsole = (c) => {\n                if (!c) {\n                    return false;\n                }\n                return typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function';\n            };\n            const serializeConsoleMessage = (msg) => {\n                try {\n                    if (typeof msg === 'object') {\n                        msg = JSON.stringify(msg);\n                    }\n                    return String(msg);\n                }\n                catch (e) {\n                    return '';\n                }\n            };\n            const platform = getPlatformId(win);\n            if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') {\n                // add DOM ready listener for System Bars\n                document.addEventListener('DOMContentLoaded', function () {\n                    win.CapacitorSystemBarsAndroidInterface.onDOMReady();\n                });\n            }\n            if (platform == 'android' || platform == 'ios') {\n                // patch document.cookie on Android/iOS\n                win.CapacitorCookiesDescriptor =\n                    Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||\n                        Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');\n                let doPatchCookies = false;\n                // check if capacitor cookies is disabled before patching\n                if (platform === 'ios') {\n                    // Use prompt to synchronously get capacitor cookies config.\n                    // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                    const payload = {\n                        type: 'CapacitorCookies.isEnabled',\n                    };\n                    const isCookiesEnabled = prompt(JSON.stringify(payload));\n                    if (isCookiesEnabled === 'true') {\n                        doPatchCookies = true;\n                    }\n                }\n                else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n                    const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled();\n                    if (isCookiesEnabled === true) {\n                        doPatchCookies = true;\n                    }\n                }\n                if (doPatchCookies) {\n                    Object.defineProperty(document, 'cookie', {\n                        get: function () {\n                            var _a, _b, _c;\n                            if (platform === 'ios') {\n                                // Use prompt to synchronously get cookies.\n                                // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                                const payload = {\n                                    type: 'CapacitorCookies.get',\n                                };\n                                const res = prompt(JSON.stringify(payload));\n                                return res;\n                            }\n                            else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n                                // return original document.cookie since Android does not support filtering of `httpOnly` cookies\n                                return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : '';\n                            }\n                        },\n                        set: function (val) {\n                            const cookiePairs = val.split(';');\n                            const domainSection = val.toLowerCase().split('domain=')[1];\n                            const domain = cookiePairs.length > 1 && domainSection != null && domainSection.length > 0\n                                ? domainSection.split(';')[0].trim()\n                                : '';\n                            if (platform === 'ios') {\n                                // Use prompt to synchronously set cookies.\n                                // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                                const payload = {\n                                    type: 'CapacitorCookies.set',\n                                    action: val,\n                                    domain,\n                                };\n                                prompt(JSON.stringify(payload));\n                            }\n                            else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {\n                                win.CapacitorCookiesAndroidInterface.setCookie(domain, val);\n                            }\n                        },\n                    });\n                }\n                // patch fetch / XHR on Android/iOS\n                // store original fetch & XHR functions\n                win.CapacitorWebFetch = window.fetch;\n                win.CapacitorWebXMLHttpRequest = {\n                    abort: window.XMLHttpRequest.prototype.abort,\n                    constructor: window.XMLHttpRequest.prototype.constructor,\n                    fullObject: window.XMLHttpRequest,\n                    getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders,\n                    getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader,\n                    open: window.XMLHttpRequest.prototype.open,\n                    prototype: window.XMLHttpRequest.prototype,\n                    send: window.XMLHttpRequest.prototype.send,\n                    setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader,\n                };\n                let doPatchHttp = false;\n                // check if capacitor http is disabled before patching\n                if (platform === 'ios') {\n                    // Use prompt to synchronously get capacitor http config.\n                    // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323\n                    const payload = {\n                        type: 'CapacitorHttp',\n                    };\n                    const isHttpEnabled = prompt(JSON.stringify(payload));\n                    if (isHttpEnabled === 'true') {\n                        doPatchHttp = true;\n                    }\n                }\n                else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') {\n                    const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled();\n                    if (isHttpEnabled === true) {\n                        doPatchHttp = true;\n                    }\n                }\n                if (doPatchHttp) {\n                    // fetch patch\n                    window.fetch = async (resource, options) => {\n                        const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers);\n                        const contentType = headers.get('Content-Type') || headers.get('content-type');\n                        if ((options === null || options === void 0 ? void 0 : options.body) instanceof FormData &&\n                            (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) &&\n                            !contentType.includes('boundary')) {\n                            headers.delete('Content-Type');\n                            headers.delete('content-type');\n                            options.headers = headers;\n                        }\n                        const request = new Request(resource, options);\n                        if (request.url.startsWith(`${cap.getServerUrl()}/`)) {\n                            return win.CapacitorWebFetch(resource, options);\n                        }\n                        const { method } = request;\n                        if (method.toLocaleUpperCase() === 'GET' ||\n                            method.toLocaleUpperCase() === 'HEAD' ||\n                            method.toLocaleUpperCase() === 'OPTIONS' ||\n                            method.toLocaleUpperCase() === 'TRACE') {\n                            // a workaround for following android webview issue:\n                            // https://issues.chromium.org/issues/40450316\n                            // Sets the user-agent header to a custom value so that its not stripped\n                            // on its way to the native layer\n                            if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) {\n                                const userAgent = headers.get('User-Agent') || headers.get('user-agent');\n                                if (userAgent !== null) {\n                                    headers.set('x-cap-user-agent', userAgent);\n                                    options.headers = headers;\n                                }\n                            }\n                            if (typeof resource === 'string') {\n                                return await win.CapacitorWebFetch(createProxyUrl(resource, win), options);\n                            }\n                            else if (resource instanceof URL) {\n                                const modifiedURL = new URL(createProxyUrl(resource.toString(), win));\n                                return await win.CapacitorWebFetch(modifiedURL, options);\n                            }\n                            else if (resource instanceof Request) {\n                                const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource);\n                                return await win.CapacitorWebFetch(modifiedRequest, options);\n                            }\n                        }\n                        const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`;\n                        console.time(tag);\n                        try {\n                            const { body } = request;\n                            const optionHeaders = Object.fromEntries(request.headers.entries());\n                            const { data: requestData, type, headers: requestHeaders, } = await convertBody((options === null || options === void 0 ? void 0 : options.body) || body || undefined, optionHeaders['Content-Type'] || optionHeaders['content-type']);\n                            const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders);\n                            if (platform === 'android') {\n                                if (headers.has('User-Agent')) {\n                                    nativeHeaders['User-Agent'] = headers.get('User-Agent');\n                                }\n                                if (headers.has('user-agent')) {\n                                    nativeHeaders['user-agent'] = headers.get('user-agent');\n                                }\n                            }\n                            const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', {\n                                url: request.url,\n                                method: method,\n                                data: requestData,\n                                dataType: type,\n                                headers: nativeHeaders,\n                            });\n                            const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'];\n                            let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json'))\n                                ? JSON.stringify(nativeResponse.data)\n                                : nativeResponse.data;\n                            // use null data for 204 No Content HTTP response\n                            if (nativeResponse.status === 204) {\n                                data = null;\n                            }\n                            // intercept & parse response before returning\n                            const response = new Response(data, {\n                                headers: nativeResponse.headers,\n                                status: nativeResponse.status,\n                            });\n                            /*\n                             * copy url to response, `cordova-plugin-ionic` uses this url from the response\n                             * we need `Object.defineProperty` because url is an inherited getter on the Response\n                             * see: https://stackoverflow.com/a/57382543\n                             * */\n                            Object.defineProperty(response, 'url', {\n                                value: nativeResponse.url,\n                            });\n                            console.timeEnd(tag);\n                            return response;\n                        }\n                        catch (error) {\n                            console.timeEnd(tag);\n                            return Promise.reject(error);\n                        }\n                    };\n                    window.XMLHttpRequest = function () {\n                        const xhr = new win.CapacitorWebXMLHttpRequest.constructor();\n                        Object.defineProperties(xhr, {\n                            _headers: {\n                                value: {},\n                                writable: true,\n                            },\n                            _method: {\n                                value: xhr.method,\n                                writable: true,\n                            },\n                        });\n                        const prototype = win.CapacitorWebXMLHttpRequest.prototype;\n                        const isProgressEventAvailable = () => typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event;\n                        // XHR patch abort\n                        prototype.abort = function () {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.abort.call(this);\n                            }\n                            this.readyState = 0;\n                            setTimeout(() => {\n                                this.dispatchEvent(new Event('abort'));\n                                this.dispatchEvent(new Event('loadend'));\n                            });\n                        };\n                        // XHR patch open\n                        prototype.open = function (method, url) {\n                            this._method = method.toLocaleUpperCase();\n                            this._url = url;\n                            if (!this._method ||\n                                this._method === 'GET' ||\n                                this._method === 'HEAD' ||\n                                this._method === 'OPTIONS' ||\n                                this._method === 'TRACE') {\n                                if (isRelativeOrProxyUrl(url)) {\n                                    return win.CapacitorWebXMLHttpRequest.open.call(this, method, url);\n                                }\n                                this._url = createProxyUrl(this._url, win);\n                                return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url);\n                            }\n                            Object.defineProperties(this, {\n                                readyState: {\n                                    get: function () {\n                                        var _a;\n                                        return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0;\n                                    },\n                                    set: function (val) {\n                                        this._readyState = val;\n                                        setTimeout(() => {\n                                            this.dispatchEvent(new Event('readystatechange'));\n                                        });\n                                    },\n                                },\n                            });\n                            setTimeout(() => {\n                                this.dispatchEvent(new Event('loadstart'));\n                            });\n                            this.readyState = 1;\n                        };\n                        // XHR patch set request header\n                        prototype.setRequestHeader = function (header, value) {\n                            // a workaround for the following android web view issue:\n                            // https://issues.chromium.org/issues/40450316\n                            // Sets the user-agent header to a custom value so that its not stripped\n                            // on its way to the native layer\n                            if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) {\n                                header = 'x-cap-user-agent';\n                            }\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value);\n                            }\n                            this._headers[header] = value;\n                        };\n                        // XHR patch send\n                        prototype.send = function (body) {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.send.call(this, body);\n                            }\n                            const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`;\n                            console.time(tag);\n                            try {\n                                this.readyState = 2;\n                                Object.defineProperties(this, {\n                                    response: {\n                                        value: '',\n                                        writable: true,\n                                    },\n                                    responseText: {\n                                        value: '',\n                                        writable: true,\n                                    },\n                                    responseURL: {\n                                        value: '',\n                                        writable: true,\n                                    },\n                                    status: {\n                                        value: 0,\n                                        writable: true,\n                                    },\n                                });\n                                convertBody(body).then(({ data, type, headers }) => {\n                                    let otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined;\n                                    if (body instanceof FormData) {\n                                        if (!this._headers['Content-Type'] && !this._headers['content-type']) {\n                                            otherHeaders = Object.assign(Object.assign({}, otherHeaders), { 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}` });\n                                        }\n                                    }\n                                    // intercept request & pass to the bridge\n                                    cap\n                                        .nativePromise('CapacitorHttp', 'request', {\n                                        url: this._url,\n                                        method: this._method,\n                                        data: data !== null ? data : undefined,\n                                        headers: Object.assign(Object.assign({}, headers), otherHeaders),\n                                        dataType: type,\n                                    })\n                                        .then((nativeResponse) => {\n                                        var _a;\n                                        // intercept & parse response before returning\n                                        if (this.readyState == 2) {\n                                            //TODO: Add progress event emission on native side\n                                            if (isProgressEventAvailable()) {\n                                                this.dispatchEvent(new ProgressEvent('progress', {\n                                                    lengthComputable: true,\n                                                    loaded: nativeResponse.data.length,\n                                                    total: nativeResponse.data.length,\n                                                }));\n                                            }\n                                            this._headers = nativeResponse.headers;\n                                            this.status = nativeResponse.status;\n                                            if (this.responseType === '' || this.responseType === 'text') {\n                                                this.response =\n                                                    typeof nativeResponse.data !== 'string'\n                                                        ? JSON.stringify(nativeResponse.data)\n                                                        : nativeResponse.data;\n                                            }\n                                            else {\n                                                this.response = nativeResponse.data;\n                                            }\n                                            this.responseText = ((_a = (nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'])) === null || _a === void 0 ? void 0 : _a.startsWith('application/json'))\n                                                ? JSON.stringify(nativeResponse.data)\n                                                : nativeResponse.data;\n                                            this.responseURL = nativeResponse.url;\n                                            this.readyState = 4;\n                                            setTimeout(() => {\n                                                this.dispatchEvent(new Event('load'));\n                                                this.dispatchEvent(new Event('loadend'));\n                                            });\n                                        }\n                                        console.timeEnd(tag);\n                                    })\n                                        .catch((error) => {\n                                        this.status = error.status;\n                                        this._headers = error.headers;\n                                        this.response = error.data;\n                                        this.responseText = JSON.stringify(error.data);\n                                        this.responseURL = error.url;\n                                        this.readyState = 4;\n                                        if (isProgressEventAvailable()) {\n                                            this.dispatchEvent(new ProgressEvent('progress', {\n                                                lengthComputable: false,\n                                                loaded: 0,\n                                                total: 0,\n                                            }));\n                                        }\n                                        setTimeout(() => {\n                                            this.dispatchEvent(new Event('error'));\n                                            this.dispatchEvent(new Event('loadend'));\n                                        });\n                                        console.timeEnd(tag);\n                                    });\n                                });\n                            }\n                            catch (error) {\n                                this.status = 500;\n                                this._headers = {};\n                                this.response = error;\n                                this.responseText = error.toString();\n                                this.responseURL = this._url;\n                                this.readyState = 4;\n                                if (isProgressEventAvailable()) {\n                                    this.dispatchEvent(new ProgressEvent('progress', {\n                                        lengthComputable: false,\n                                        loaded: 0,\n                                        total: 0,\n                                    }));\n                                }\n                                setTimeout(() => {\n                                    this.dispatchEvent(new Event('error'));\n                                    this.dispatchEvent(new Event('loadend'));\n                                });\n                                console.timeEnd(tag);\n                            }\n                        };\n                        // XHR patch getAllResponseHeaders\n                        prototype.getAllResponseHeaders = function () {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this);\n                            }\n                            let returnString = '';\n                            for (const key in this._headers) {\n                                if (key != 'Set-Cookie') {\n                                    returnString += key + ': ' + this._headers[key] + '\\r\\n';\n                                }\n                            }\n                            return returnString;\n                        };\n                        // XHR patch getResponseHeader\n                        prototype.getResponseHeader = function (name) {\n                            if (isRelativeOrProxyUrl(this._url)) {\n                                return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name);\n                            }\n                            return this._headers[name];\n                        };\n                        Object.setPrototypeOf(xhr, prototype);\n                        return xhr;\n                    };\n                    Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject);\n                }\n            }\n            // patch window.console on iOS and store original console fns\n            const isIos = getPlatformId(win) === 'ios';\n            if (win.console && isIos) {\n                Object.defineProperties(win.console, BRIDGED_CONSOLE_METHODS.reduce((props, method) => {\n                    const consoleMethod = win.console[method].bind(win.console);\n                    props[method] = {\n                        value: (...args) => {\n                            const msgs = [...args];\n                            cap.toNative('Console', 'log', {\n                                level: method,\n                                message: msgs.map(serializeConsoleMessage).join(' '),\n                            });\n                            return consoleMethod(...args);\n                        },\n                    };\n                    return props;\n                }, {}));\n            }\n            cap.logJs = (msg, level) => {\n                switch (level) {\n                    case 'error':\n                        win.console.error(msg);\n                        break;\n                    case 'warn':\n                        win.console.warn(msg);\n                        break;\n                    case 'info':\n                        win.console.info(msg);\n                        break;\n                    default:\n                        win.console.log(msg);\n                }\n            };\n            cap.logToNative = createLogToNative(win.console);\n            cap.logFromNative = createLogFromNative(win.console);\n            cap.handleError = (err) => win.console.error(err);\n            win.Capacitor = cap;\n        };\n        function initNativeBridge(win) {\n            const cap = win.Capacitor || {};\n            // keep a collection of callbacks for native response data\n            const callbacks = new Map();\n            const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : '';\n            cap.getServerUrl = () => webviewServerUrl;\n            cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath);\n            // Counter of callback ids, randomized to avoid\n            // any issues during reloads if a call comes back with\n            // an existing callback id from an old session\n            let callbackIdCount = Math.floor(Math.random() * 134217728);\n            let postToNative = null;\n            const isNativePlatform = () => true;\n            const getPlatform = () => getPlatformId(win);\n            cap.getPlatform = getPlatform;\n            cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name);\n            cap.isNativePlatform = isNativePlatform;\n            // create the postToNative() fn if needed\n            if (getPlatformId(win) === 'android') {\n                // android platform\n                postToNative = (data) => {\n                    var _a;\n                    try {\n                        win.androidBridge.postMessage(JSON.stringify(data));\n                    }\n                    catch (e) {\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e);\n                    }\n                };\n            }\n            else if (getPlatformId(win) === 'ios') {\n                // ios platform\n                postToNative = (data) => {\n                    var _a;\n                    try {\n                        data.type = data.type ? data.type : 'message';\n                        win.webkit.messageHandlers.bridge.postMessage(data);\n                    }\n                    catch (e) {\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e);\n                    }\n                };\n            }\n            cap.handleWindowError = (msg, url, lineNo, columnNo, err) => {\n                const str = msg.toLowerCase();\n                if (str.indexOf('script error') > -1) ;\n                else {\n                    const errObj = {\n                        type: 'js.error',\n                        error: {\n                            message: msg,\n                            url: url,\n                            line: lineNo,\n                            col: columnNo,\n                            errorObject: JSON.stringify(err),\n                        },\n                    };\n                    if (err !== null) {\n                        cap.handleError(err);\n                    }\n                    postToNative(errObj);\n                }\n                return false;\n            };\n            if (cap.DEBUG) {\n                window.onerror = cap.handleWindowError;\n            }\n            initLogger(win, cap);\n            /**\n             * Send a plugin method call to the native layer\n             */\n            cap.toNative = (pluginName, methodName, options, storedCallback) => {\n                var _a, _b;\n                try {\n                    if (typeof postToNative === 'function') {\n                        let callbackId = '-1';\n                        if (storedCallback &&\n                            (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function')) {\n                            // store the call for later lookup\n                            callbackId = String(++callbackIdCount);\n                            callbacks.set(callbackId, storedCallback);\n                        }\n                        const callData = {\n                            callbackId: callbackId,\n                            pluginId: pluginName,\n                            methodName: methodName,\n                            options: options || {},\n                        };\n                        if (cap.isLoggingEnabled && pluginName !== 'Console') {\n                            cap.logToNative(callData);\n                        }\n                        // post the call data to native\n                        postToNative(callData);\n                        return callbackId;\n                    }\n                    else {\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(`implementation unavailable for: ${pluginName}`);\n                    }\n                }\n                catch (e) {\n                    (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e);\n                }\n                return null;\n            };\n            if (win === null || win === void 0 ? void 0 : win.androidBridge) {\n                win.androidBridge.onmessage = function (event) {\n                    returnResult(JSON.parse(event.data));\n                };\n            }\n            /**\n             * Process a response from the native layer.\n             */\n            cap.fromNative = (result) => {\n                returnResult(result);\n            };\n            const returnResult = (result) => {\n                var _a, _b;\n                if (cap.isLoggingEnabled && result.pluginId !== 'Console') {\n                    cap.logFromNative(result);\n                }\n                // get the stored call, if it exists\n                try {\n                    const storedCall = callbacks.get(result.callbackId);\n                    if (storedCall) {\n                        // looks like we've got a stored call\n                        if (result.error) {\n                            // ensure stacktraces by copying error properties to an Error\n                            result.error = Object.keys(result.error).reduce((err, key) => {\n                                // use any type to avoid importing util and compiling most of .ts files\n                                err[key] = result.error[key];\n                                return err;\n                            }, new cap.Exception(''));\n                        }\n                        if (typeof storedCall.callback === 'function') {\n                            // callback\n                            if (result.success) {\n                                storedCall.callback(result.data);\n                            }\n                            else {\n                                storedCall.callback(null, result.error);\n                            }\n                        }\n                        else if (typeof storedCall.resolve === 'function') {\n                            // promise\n                            if (result.success) {\n                                storedCall.resolve(result.data);\n                            }\n                            else {\n                                storedCall.reject(result.error);\n                            }\n                            // no need to keep this stored callback\n                            // around for a one time resolve promise\n                            callbacks.delete(result.callbackId);\n                        }\n                    }\n                    else if (!result.success && result.error) {\n                        // no stored callback, but if there was an error let's log it\n                        (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(result.error);\n                    }\n                    if (result.save === false) {\n                        callbacks.delete(result.callbackId);\n                    }\n                }\n                catch (e) {\n                    (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e);\n                }\n                // always delete to prevent memory leaks\n                // overkill but we're not sure what apps will do with this data\n                delete result.data;\n                delete result.error;\n            };\n            cap.nativeCallback = (pluginName, methodName, options, callback) => {\n                if (typeof options === 'function') {\n                    console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`);\n                    callback = options;\n                    options = null;\n                }\n                return cap.toNative(pluginName, methodName, options, { callback });\n            };\n            cap.nativePromise = (pluginName, methodName, options) => {\n                return new Promise((resolve, reject) => {\n                    cap.toNative(pluginName, methodName, options, {\n                        resolve: resolve,\n                        reject: reject,\n                    });\n                });\n            };\n            // eslint-disable-next-line @typescript-eslint/no-unused-vars\n            cap.withPlugin = (_pluginId, _fn) => dummy;\n            cap.Exception = CapacitorException;\n            initEvents(win, cap);\n            initLegacyHandlers(win, cap);\n            initVendor(win, cap);\n            win.Capacitor = cap;\n        }\n        initNativeBridge(w);\n    };\n    initBridge(typeof globalThis !== 'undefined'\n        ? globalThis\n        : typeof self !== 'undefined'\n            ? self\n            : typeof window !== 'undefined'\n                ? window\n                : typeof global !== 'undefined'\n                    ? global\n                    : {});\n\n    dummy = initBridge;\n\n    Object.defineProperty(exports, '__esModule', { value: true });\n\n    return exports;\n\n})({});\n"
  },
  {
    "path": "ios/Capacitor/Capacitor.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t0F83E885285A332E006C43CB /* AppUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F83E884285A332D006C43CB /* AppUUID.swift */; };\n\t\t0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */; };\n\t\t2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */; };\n\t\t2F81F5CA26FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */; };\n\t\t373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */; };\n\t\t373A69F2255C95D0000A6F44 /* NotificationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */; };\n\t\t501CBAA71FC0A723009B0D4D /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 501CBAA61FC0A723009B0D4D /* WebKit.framework */; };\n\t\t50503EE91FC08595003606DC /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50503EDF1FC08594003606DC /* Capacitor.framework */; };\n\t\t50503EEE1FC08595003606DC /* CapacitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50503EED1FC08595003606DC /* CapacitorTests.swift */; };\n\t\t6214934725509C3F006C36F9 /* CAPInstanceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6214934625509C3F006C36F9 /* CAPInstanceConfiguration.swift */; };\n\t\t621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCB42542045900D3D615 /* CAPBridgedJSTypes.m */; };\n\t\t621ECCB82542045900D3D615 /* CAPBridgedJSTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 621ECCB62542045900D3D615 /* CAPBridgedJSTypes.h */; settings = {ATTRIBUTES = (Private, ); }; };\n\t\t621ECCBC2542046400D3D615 /* JSTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCBB2542046400D3D615 /* JSTypes.swift */; };\n\t\t621ECCC3254204B700D3D615 /* BridgedTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCC2254204B700D3D615 /* BridgedTypesTests.swift */; };\n\t\t621ECCC8254204BE00D3D615 /* JSONSerializationWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCC6254204BE00D3D615 /* JSONSerializationWrapper.m */; };\n\t\t621ECCD6254205BD00D3D615 /* CAPBridgeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCD4254205BD00D3D615 /* CAPBridgeProtocol.swift */; };\n\t\t621ECCDA254205C400D3D615 /* CapacitorBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCD9254205C400D3D615 /* CapacitorBridge.swift */; };\n\t\t621ECCE3254206A600D3D615 /* CAPApplicationDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCE2254206A600D3D615 /* CAPApplicationDelegateProxy.swift */; };\n\t\t623D68FA254C5037002D01D1 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 623D68F9254C5037002D01D1 /* KeyPath.swift */; };\n\t\t623D6909254C6FDF002D01D1 /* CAPInstanceDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 623D6907254C6FDF002D01D1 /* CAPInstanceDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t623D690A254C6FDF002D01D1 /* CAPInstanceDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 623D6908254C6FDF002D01D1 /* CAPInstanceDescriptor.m */; };\n\t\t623D6914254C7030002D01D1 /* CAPInstanceDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 623D6913254C7030002D01D1 /* CAPInstanceDescriptor.swift */; };\n\t\t623D691D254C7462002D01D1 /* CAPInstanceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t623D691E254C7462002D01D1 /* CAPInstanceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */; };\n\t\t625AF1ED258963C700869675 /* WebViewAssetHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625AF1EC258963C700869675 /* WebViewAssetHandler.swift */; };\n\t\t6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */; };\n\t\t626D2D992613B61E0046CE81 /* hidinglogs.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 626D2D902613B4BB0046CE81 /* hidinglogs.json */; };\n\t\t62959B162524DA7800A3D7F1 /* CAPPluginCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE32524DA7700A3D7F1 /* JSExport.swift */; };\n\t\t62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t62959B1A2524DA7800A3D7F1 /* CAPPluginCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE62524DA7700A3D7F1 /* CAPPluginCall.swift */; };\n\t\t62959B1B2524DA7800A3D7F1 /* CAPFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE72524DA7700A3D7F1 /* CAPFile.swift */; };\n\t\t62959B1C2524DA7800A3D7F1 /* CAPPluginMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE82524DA7700A3D7F1 /* CAPPluginMethod.m */; };\n\t\t62959B1D2524DA7800A3D7F1 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE92524DA7700A3D7F1 /* UIColor.swift */; };\n\t\t62959B222524DA7800A3D7F1 /* Console.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AEF2524DA7700A3D7F1 /* Console.swift */; };\n\t\t62959B262524DA7800A3D7F1 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AF32524DA7700A3D7F1 /* WebView.swift */; };\n\t\t62959B302524DA7800A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */; };\n\t\t62959B312524DA7800A3D7F1 /* JS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AFF2524DA7700A3D7F1 /* JS.swift */; };\n\t\t62959B332524DA7800A3D7F1 /* CAPPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959B012524DA7700A3D7F1 /* CAPPlugin.m */; };\n\t\t62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B042524DA7700A3D7F1 /* CAPBridgeViewController.swift */; };\n\t\t62959B382524DA7800A3D7F1 /* CAPPluginCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959B062524DA7700A3D7F1 /* CAPPluginCall.m */; };\n\t\t62959B392524DA7800A3D7F1 /* CapacitorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B072524DA7700A3D7F1 /* CapacitorExtension.swift */; };\n\t\t62959B3A2524DA7800A3D7F1 /* CAPLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B082524DA7700A3D7F1 /* CAPLog.swift */; };\n\t\t62959B3B2524DA7800A3D7F1 /* CAPPluginMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B092524DA7700A3D7F1 /* CAPPluginMethod.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t62959B3C2524DA7800A3D7F1 /* CAPBridgeDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B0A2524DA7700A3D7F1 /* CAPBridgeDelegate.swift */; };\n\t\t62959B402524DA7800A3D7F1 /* TmpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B0E2524DA7700A3D7F1 /* TmpViewController.swift */; };\n\t\t62959B412524DA7800A3D7F1 /* Capacitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B0F2524DA7700A3D7F1 /* Capacitor.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t62959B422524DA7800A3D7F1 /* DocLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B102524DA7700A3D7F1 /* DocLinks.swift */; };\n\t\t62959B432524DA7800A3D7F1 /* Data+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B112524DA7700A3D7F1 /* Data+Capacitor.swift */; };\n\t\t62959B452524DA7800A3D7F1 /* CAPPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B132524DA7700A3D7F1 /* CAPPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t62959B462524DA7800A3D7F1 /* CAPBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B142524DA7700A3D7F1 /* CAPBridge.swift */; };\n\t\t62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B152524DA7700A3D7F1 /* CAPNotifications.swift */; };\n\t\t62959BA52526475A00A3D7F1 /* Cordova.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62959BA02526474300A3D7F1 /* Cordova.framework */; };\n\t\t6296A77E253A2E49005A202A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6296A77D253A2E49005A202A /* AppDelegate.swift */; };\n\t\t6296A782253A2E49005A202A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6296A781253A2E49005A202A /* ViewController.swift */; };\n\t\t6296A785253A2E49005A202A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6296A783253A2E49005A202A /* Main.storyboard */; };\n\t\t6296A787253A2E49005A202A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6296A786253A2E49005A202A /* Assets.xcassets */; };\n\t\t6296A78A253A2E49005A202A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6296A788253A2E49005A202A /* LaunchScreen.storyboard */; };\n\t\t62A91C3425535F5700861508 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A91C3325535F5700861508 /* ConfigurationTests.swift */; };\n\t\t62A91C3F2553710E00861508 /* nonjson.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62A91C392553710300861508 /* nonjson.json */; };\n\t\t62ADC0CA25CB678000E914DE /* PluginCallResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ADC0C925CB678000E914DE /* PluginCallResult.swift */; };\n\t\t62D43AF02581817500673C24 /* WKWebView+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */; };\n\t\t62D43B652582A13D00673C24 /* WKWebView+Capacitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */; };\n\t\t62E0736125535E8700BAAADB /* server.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735225535E6500BAAADB /* server.json */; };\n\t\t62E0736225535E8700BAAADB /* bad.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735325535E6500BAAADB /* bad.json */; };\n\t\t62E0736325535E8700BAAADB /* flat.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735425535E6500BAAADB /* flat.json */; };\n\t\t62E0736425535E8700BAAADB /* hierarchy.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735525535E6500BAAADB /* hierarchy.json */; };\n\t\t62E207AE2588234500A78983 /* WebViewDelegationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E207AD2588234500A78983 /* WebViewDelegationHandler.swift */; };\n\t\t62E79C722638B23300414164 /* JSExportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E79C712638B23300414164 /* JSExportTests.swift */; };\n\t\t62E79CD7263A178B00414164 /* native-bridge.js in Resources */ = {isa = PBXBuildFile; fileRef = 62E79C572638AF7500414164 /* native-bridge.js */; };\n\t\t62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */; };\n\t\t62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */; };\n\t\t62FABD2B25AE6182007B3814 /* BridgedTypesHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */; };\n\t\t957BD9402E78A4A50056874C /* SystemBars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957BD93E2E78A4A20056874C /* SystemBars.swift */; };\n\t\tA327E6B628DB8B2900CA8B0A /* HttpRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */; };\n\t\tA327E6B728DB8B2900CA8B0A /* CapacitorHttp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */; };\n\t\tA327E6B828DB8B2900CA8B0A /* CapacitorUrlRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */; };\n\t\tA38C3D7728484E76004B3680 /* CapacitorCookies.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38C3D7628484E76004B3680 /* CapacitorCookies.swift */; };\n\t\tA38C3D7B2848BE6F004B3680 /* CapacitorCookieManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */; };\n\t\tA71289E627F380A500DADDF3 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289E527F380A500DADDF3 /* Router.swift */; };\n\t\tA71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289EA27F380FD00DADDF3 /* RouterTests.swift */; };\n\t\tA7187FD22BD1CB7D00093C45 /* CAPPluginMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7187FD12BD1CB7D00093C45 /* CAPPluginMethod.swift */; };\n\t\tA76739792B98E09700795F7B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A76739782B98E09700795F7B /* PrivacyInfo.xcprivacy */; };\n\t\tA771ADEE2C8B845000AF234D /* DateCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A771ADED2C8B845000AF234D /* DateCodableTests.swift */; };\n\t\tA771ADF12C8B909100AF234D /* URLCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A771ADF02C8B909100AF234D /* URLCodableTests.swift */; };\n\t\tA7BE62CC2B486A5400165ACB /* KeyValueStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BE62CB2B486A5400165ACB /* KeyValueStore.swift */; };\n\t\tA7D474D52C8BA8E8005620A8 /* DataCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D474D42C8BA8E8005620A8 /* DataCodableTests.swift */; };\n\t\tA7D474D82C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D474D72C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift */; };\n\t\tA7D8B3522B238A840003FAD6 /* JSValueEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B3512B238A840003FAD6 /* JSValueEncoder.swift */; };\n\t\tA7D8B3632B263B8D0003FAD6 /* NestedCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B3622B263B8D0003FAD6 /* NestedCodableTests.swift */; };\n\t\tA7D8B3642B263B8D0003FAD6 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50503EDF1FC08594003606DC /* Capacitor.framework */; };\n\t\tA7D8B36A2B263B990003FAD6 /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B3562B23B2110003FAD6 /* CodableTests.swift */; };\n\t\tA7D8B36E2B2692300003FAD6 /* SuperCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B36D2B2692300003FAD6 /* SuperCodableTests.swift */; };\n\t\tA7D9312F2B23710300FF59A2 /* JSValueDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D9312E2B23710300FF59A2 /* JSValueDecoder.swift */; };\n\t\tA7DB03AC29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7DB03AB29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift */; };\n\t\tA7F7EDCD291EC75C0015B73B /* CAPPlugin+LoadInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F7EDCC291EC75C0015B73B /* CAPPlugin+LoadInstance.swift */; };\n\t\tA7F7EDD5292BE8520015B73B /* CAPInstancePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F7EDD4292BE8520015B73B /* CAPInstancePlugin.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t50503EEA1FC08595003606DC /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 50503ED61FC08594003606DC /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 50503EDE1FC08594003606DC;\n\t\t\tremoteInfo = Avocado;\n\t\t};\n\t\t62959B9F2526474300A3D7F1 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 62959B9B2526474300A3D7F1 /* CapacitorCordova.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2F5C86DC1FE94845004B09C7;\n\t\t\tremoteInfo = Cordova;\n\t\t};\n\t\t6296A796253A2EAE005A202A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 50503ED61FC08594003606DC /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 6296A77A253A2E49005A202A;\n\t\t\tremoteInfo = TestsHostApp;\n\t\t};\n\t\tA7D8B3652B263B8D0003FAD6 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 50503ED61FC08594003606DC /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 50503EDE1FC08594003606DC;\n\t\t\tremoteInfo = Capacitor;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t622BB9C32541FE1900A5DBCA /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = configurations;\n\t\t\tdstSubfolderSpec = 1;\n\t\t\tfiles = (\n\t\t\t\t626D2D992613B61E0046CE81 /* hidinglogs.json in CopyFiles */,\n\t\t\t\t62A91C3F2553710E00861508 /* nonjson.json in CopyFiles */,\n\t\t\t\t62E0736125535E8700BAAADB /* server.json in CopyFiles */,\n\t\t\t\t62E0736225535E8700BAAADB /* bad.json in CopyFiles */,\n\t\t\t\t62E0736325535E8700BAAADB /* flat.json in CopyFiles */,\n\t\t\t\t62E0736425535E8700BAAADB /* hierarchy.json in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0F83E884285A332D006C43CB /* AppUUID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUUID.swift; sourceTree = \"<group>\"; };\n\t\t0F8F33B127DA980A003F49D6 /* PluginConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PluginConfig.swift; sourceTree = \"<group>\"; };\n\t\t2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"CAPBridgeViewController+CDVScreenOrientationDelegate.h\"; sourceTree = \"<group>\"; };\n\t\t2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = \"CAPBridgeViewController+CDVScreenOrientationDelegate.m\"; sourceTree = \"<group>\"; };\n\t\t373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandlerProtocol.swift; sourceTree = \"<group>\"; };\n\t\t373A69F1255C95D0000A6F44 /* NotificationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRouter.swift; sourceTree = \"<group>\"; };\n\t\t501CBAA61FC0A723009B0D4D /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };\n\t\t50503EDF1FC08594003606DC /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t50503EE81FC08595003606DC /* CapacitorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CapacitorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t50503EED1FC08595003606DC /* CapacitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorTests.swift; sourceTree = \"<group>\"; };\n\t\t6214934625509C3F006C36F9 /* CAPInstanceConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPInstanceConfiguration.swift; sourceTree = \"<group>\"; };\n\t\t621ECCB42542045900D3D615 /* CAPBridgedJSTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPBridgedJSTypes.m; sourceTree = \"<group>\"; };\n\t\t621ECCB62542045900D3D615 /* CAPBridgedJSTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPBridgedJSTypes.h; sourceTree = \"<group>\"; };\n\t\t621ECCBB2542046400D3D615 /* JSTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSTypes.swift; sourceTree = \"<group>\"; };\n\t\t621ECCC2254204B700D3D615 /* BridgedTypesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BridgedTypesTests.swift; sourceTree = \"<group>\"; };\n\t\t621ECCC6254204BE00D3D615 /* JSONSerializationWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONSerializationWrapper.m; sourceTree = \"<group>\"; };\n\t\t621ECCC7254204BE00D3D615 /* JSONSerializationWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONSerializationWrapper.h; sourceTree = \"<group>\"; };\n\t\t621ECCCD254204C400D3D615 /* CapacitorTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"CapacitorTests-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t621ECCD4254205BD00D3D615 /* CAPBridgeProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPBridgeProtocol.swift; sourceTree = \"<group>\"; };\n\t\t621ECCD9254205C400D3D615 /* CapacitorBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorBridge.swift; sourceTree = \"<group>\"; };\n\t\t621ECCE2254206A600D3D615 /* CAPApplicationDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPApplicationDelegateProxy.swift; sourceTree = \"<group>\"; };\n\t\t623D68F9254C5037002D01D1 /* KeyPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPath.swift; sourceTree = \"<group>\"; };\n\t\t623D6907254C6FDF002D01D1 /* CAPInstanceDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAPInstanceDescriptor.h; sourceTree = \"<group>\"; };\n\t\t623D6908254C6FDF002D01D1 /* CAPInstanceDescriptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CAPInstanceDescriptor.m; sourceTree = \"<group>\"; };\n\t\t623D6913254C7030002D01D1 /* CAPInstanceDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPInstanceDescriptor.swift; sourceTree = \"<group>\"; };\n\t\t623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAPInstanceConfiguration.h; sourceTree = \"<group>\"; };\n\t\t623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CAPInstanceConfiguration.m; sourceTree = \"<group>\"; };\n\t\t625AF1EC258963C700869675 /* WebViewAssetHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewAssetHandler.swift; sourceTree = \"<group>\"; };\n\t\t6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PluginCallAccessorTests.m; sourceTree = \"<group>\"; };\n\t\t626D2D902613B4BB0046CE81 /* hidinglogs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hidinglogs.json; sourceTree = \"<group>\"; };\n\t\t62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPPluginCall.h; sourceTree = \"<group>\"; };\n\t\t62959AE32524DA7700A3D7F1 /* JSExport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSExport.swift; sourceTree = \"<group>\"; };\n\t\t62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPBridgedPlugin.h; sourceTree = \"<group>\"; };\n\t\t62959AE62524DA7700A3D7F1 /* CAPPluginCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPPluginCall.swift; sourceTree = \"<group>\"; };\n\t\t62959AE72524DA7700A3D7F1 /* CAPFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPFile.swift; sourceTree = \"<group>\"; };\n\t\t62959AE82524DA7700A3D7F1 /* CAPPluginMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPPluginMethod.m; sourceTree = \"<group>\"; };\n\t\t62959AE92524DA7700A3D7F1 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = \"<group>\"; };\n\t\t62959AEF2524DA7700A3D7F1 /* Console.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Console.swift; sourceTree = \"<group>\"; };\n\t\t62959AF32524DA7700A3D7F1 /* WebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = \"<group>\"; };\n\t\t62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"UIStatusBarManager+CAPHandleTapAction.m\"; sourceTree = \"<group>\"; };\n\t\t62959AFF2524DA7700A3D7F1 /* JS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JS.swift; sourceTree = \"<group>\"; };\n\t\t62959B012524DA7700A3D7F1 /* CAPPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPPlugin.m; sourceTree = \"<group>\"; };\n\t\t62959B042524DA7700A3D7F1 /* CAPBridgeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPBridgeViewController.swift; sourceTree = \"<group>\"; };\n\t\t62959B062524DA7700A3D7F1 /* CAPPluginCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPPluginCall.m; sourceTree = \"<group>\"; };\n\t\t62959B072524DA7700A3D7F1 /* CapacitorExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorExtension.swift; sourceTree = \"<group>\"; };\n\t\t62959B082524DA7700A3D7F1 /* CAPLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPLog.swift; sourceTree = \"<group>\"; };\n\t\t62959B092524DA7700A3D7F1 /* CAPPluginMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPPluginMethod.h; sourceTree = \"<group>\"; };\n\t\t62959B0A2524DA7700A3D7F1 /* CAPBridgeDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPBridgeDelegate.swift; sourceTree = \"<group>\"; };\n\t\t62959B0E2524DA7700A3D7F1 /* TmpViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TmpViewController.swift; sourceTree = \"<group>\"; };\n\t\t62959B0F2524DA7700A3D7F1 /* Capacitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Capacitor.h; sourceTree = \"<group>\"; };\n\t\t62959B102524DA7700A3D7F1 /* DocLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocLinks.swift; sourceTree = \"<group>\"; };\n\t\t62959B112524DA7700A3D7F1 /* Data+Capacitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"Data+Capacitor.swift\"; sourceTree = \"<group>\"; };\n\t\t62959B122524DA7700A3D7F1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t62959B132524DA7700A3D7F1 /* CAPPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPPlugin.h; sourceTree = \"<group>\"; };\n\t\t62959B142524DA7700A3D7F1 /* CAPBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPBridge.swift; sourceTree = \"<group>\"; };\n\t\t62959B152524DA7700A3D7F1 /* CAPNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPNotifications.swift; sourceTree = \"<group>\"; };\n\t\t62959B56252518FB00A3D7F1 /* Cordova.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Cordova.framework; sourceTree = \"<group>\"; };\n\t\t62959B8225253A9500A3D7F1 /* Capacitor.modulemap */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.module-map\"; path = Capacitor.modulemap; sourceTree = \"<group>\"; };\n\t\t62959B9B2526474300A3D7F1 /* CapacitorCordova.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = CapacitorCordova.xcodeproj; path = ../CapacitorCordova/CapacitorCordova.xcodeproj; sourceTree = \"<group>\"; };\n\t\t62959BBD2526510200A3D7F1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t6296A77B253A2E49005A202A /* TestsHostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestsHostApp.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t6296A77D253A2E49005A202A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t6296A781253A2E49005A202A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t6296A784253A2E49005A202A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t6296A786253A2E49005A202A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t6296A789253A2E49005A202A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t6296A78B253A2E49005A202A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t62A91C3325535F5700861508 /* ConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = \"<group>\"; };\n\t\t62A91C392553710300861508 /* nonjson.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = nonjson.json; sourceTree = \"<group>\"; };\n\t\t62ADC0C925CB678000E914DE /* PluginCallResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginCallResult.swift; sourceTree = \"<group>\"; };\n\t\t62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"WKWebView+Capacitor.swift\"; sourceTree = \"<group>\"; };\n\t\t62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"WKWebView+Capacitor.m\"; sourceTree = \"<group>\"; };\n\t\t62E0735225535E6500BAAADB /* server.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = server.json; sourceTree = \"<group>\"; };\n\t\t62E0735325535E6500BAAADB /* bad.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = bad.json; sourceTree = \"<group>\"; };\n\t\t62E0735425535E6500BAAADB /* flat.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = flat.json; sourceTree = \"<group>\"; };\n\t\t62E0735525535E6500BAAADB /* hierarchy.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hierarchy.json; sourceTree = \"<group>\"; };\n\t\t62E207AD2588234500A78983 /* WebViewDelegationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewDelegationHandler.swift; sourceTree = \"<group>\"; };\n\t\t62E79C572638AF7500414164 /* native-bridge.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = \"native-bridge.js\"; sourceTree = \"<group>\"; };\n\t\t62E79C712638B23300414164 /* JSExportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSExportTests.swift; sourceTree = \"<group>\"; };\n\t\t62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Array+Capacitor.swift\"; sourceTree = \"<group>\"; };\n\t\t62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgedTypesTests.m; sourceTree = \"<group>\"; };\n\t\t62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgedTypesHelper.swift; sourceTree = \"<group>\"; };\n\t\t957BD93E2E78A4A20056874C /* SystemBars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemBars.swift; sourceTree = \"<group>\"; };\n\t\tA327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpRequestHandler.swift; sourceTree = \"<group>\"; };\n\t\tA327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorHttp.swift; sourceTree = \"<group>\"; };\n\t\tA327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorUrlRequest.swift; sourceTree = \"<group>\"; };\n\t\tA38C3D7628484E76004B3680 /* CapacitorCookies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorCookies.swift; sourceTree = \"<group>\"; };\n\t\tA38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorCookieManager.swift; sourceTree = \"<group>\"; };\n\t\tA71289E527F380A500DADDF3 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = \"<group>\"; };\n\t\tA71289EA27F380FD00DADDF3 /* RouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = \"<group>\"; };\n\t\tA7187FD12BD1CB7D00093C45 /* CAPPluginMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPPluginMethod.swift; sourceTree = \"<group>\"; };\n\t\tA76739782B98E09700795F7B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = \"<group>\"; };\n\t\tA771ADED2C8B845000AF234D /* DateCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateCodableTests.swift; sourceTree = \"<group>\"; };\n\t\tA771ADF02C8B909100AF234D /* URLCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLCodableTests.swift; sourceTree = \"<group>\"; };\n\t\tA7BE62CB2B486A5400165ACB /* KeyValueStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueStore.swift; sourceTree = \"<group>\"; };\n\t\tA7D474D42C8BA8E8005620A8 /* DataCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataCodableTests.swift; sourceTree = \"<group>\"; };\n\t\tA7D474D72C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonconformingFloatCodableTests.swift; sourceTree = \"<group>\"; };\n\t\tA7D8B3512B238A840003FAD6 /* JSValueEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSValueEncoder.swift; sourceTree = \"<group>\"; };\n\t\tA7D8B3562B23B2110003FAD6 /* CodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableTests.swift; sourceTree = \"<group>\"; };\n\t\tA7D8B3602B263B8D0003FAD6 /* CodableTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CodableTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tA7D8B3622B263B8D0003FAD6 /* NestedCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedCodableTests.swift; sourceTree = \"<group>\"; };\n\t\tA7D8B36D2B2692300003FAD6 /* SuperCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperCodableTests.swift; sourceTree = \"<group>\"; };\n\t\tA7D9312E2B23710300FF59A2 /* JSValueDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSValueDecoder.swift; sourceTree = \"<group>\"; };\n\t\tA7DB03AB29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"CAPBridgedPlugin+getMethod.swift\"; sourceTree = \"<group>\"; };\n\t\tA7F7EDCC291EC75C0015B73B /* CAPPlugin+LoadInstance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"CAPPlugin+LoadInstance.swift\"; sourceTree = \"<group>\"; };\n\t\tA7F7EDD4292BE8520015B73B /* CAPInstancePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPInstancePlugin.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t50503EDB1FC08594003606DC /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t501CBAA71FC0A723009B0D4D /* WebKit.framework in Frameworks */,\n\t\t\t\t62959BA52526475A00A3D7F1 /* Cordova.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t50503EE51FC08595003606DC /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t50503EE91FC08595003606DC /* Capacitor.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t6296A778253A2E49005A202A /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tA7D8B35D2B263B8D0003FAD6 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tA7D8B3642B263B8D0003FAD6 /* Capacitor.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t501CBAA51FC0A723009B0D4D /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t62959B9B2526474300A3D7F1 /* CapacitorCordova.xcodeproj */,\n\t\t\t\t62959B56252518FB00A3D7F1 /* Cordova.framework */,\n\t\t\t\t501CBAA61FC0A723009B0D4D /* WebKit.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t50503ED51FC08594003606DC = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t62959AE12524DA7700A3D7F1 /* Capacitor */,\n\t\t\t\t50503EEC1FC08595003606DC /* CapacitorTests */,\n\t\t\t\t6296A77C253A2E49005A202A /* TestsHostApp */,\n\t\t\t\tA7D8B3612B263B8D0003FAD6 /* CodableTests */,\n\t\t\t\t50503EE01FC08594003606DC /* Products */,\n\t\t\t\t501CBAA51FC0A723009B0D4D /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t50503EE01FC08594003606DC /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t50503EDF1FC08594003606DC /* Capacitor.framework */,\n\t\t\t\t50503EE81FC08595003606DC /* CapacitorTests.xctest */,\n\t\t\t\t6296A77B253A2E49005A202A /* TestsHostApp.app */,\n\t\t\t\tA7D8B3602B263B8D0003FAD6 /* CodableTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t50503EEC1FC08595003606DC /* CapacitorTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t50503EED1FC08595003606DC /* CapacitorTests.swift */,\n\t\t\t\t621ECCC2254204B700D3D615 /* BridgedTypesTests.swift */,\n\t\t\t\t62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */,\n\t\t\t\t62A91C3325535F5700861508 /* ConfigurationTests.swift */,\n\t\t\t\t62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */,\n\t\t\t\t621ECCC7254204BE00D3D615 /* JSONSerializationWrapper.h */,\n\t\t\t\t621ECCC6254204BE00D3D615 /* JSONSerializationWrapper.m */,\n\t\t\t\t6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */,\n\t\t\t\t621ECCCD254204C400D3D615 /* CapacitorTests-Bridging-Header.h */,\n\t\t\t\t62E79C712638B23300414164 /* JSExportTests.swift */,\n\t\t\t\t62959BBD2526510200A3D7F1 /* Info.plist */,\n\t\t\t\tA71289EA27F380FD00DADDF3 /* RouterTests.swift */,\n\t\t\t);\n\t\t\tpath = CapacitorTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t62959AE12524DA7700A3D7F1 /* Capacitor */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tA7D9312C2B2370EF00FF59A2 /* Codable */,\n\t\t\t\t0F8F33B127DA980A003F49D6 /* PluginConfig.swift */,\n\t\t\t\tA76739782B98E09700795F7B /* PrivacyInfo.xcprivacy */,\n\t\t\t\t62959B0F2524DA7700A3D7F1 /* Capacitor.h */,\n\t\t\t\t62959B132524DA7700A3D7F1 /* CAPPlugin.h */,\n\t\t\t\t62959B012524DA7700A3D7F1 /* CAPPlugin.m */,\n\t\t\t\tA7F7EDCC291EC75C0015B73B /* CAPPlugin+LoadInstance.swift */,\n\t\t\t\t62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */,\n\t\t\t\tA7DB03AB29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift */,\n\t\t\t\t62959B092524DA7700A3D7F1 /* CAPPluginMethod.h */,\n\t\t\t\t62959AE82524DA7700A3D7F1 /* CAPPluginMethod.m */,\n\t\t\t\tA7187FD12BD1CB7D00093C45 /* CAPPluginMethod.swift */,\n\t\t\t\t62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */,\n\t\t\t\t62959B062524DA7700A3D7F1 /* CAPPluginCall.m */,\n\t\t\t\t62959AE62524DA7700A3D7F1 /* CAPPluginCall.swift */,\n\t\t\t\tA7F7EDD4292BE8520015B73B /* CAPInstancePlugin.swift */,\n\t\t\t\t62ADC0C925CB678000E914DE /* PluginCallResult.swift */,\n\t\t\t\t621ECCBB2542046400D3D615 /* JSTypes.swift */,\n\t\t\t\t621ECCB62542045900D3D615 /* CAPBridgedJSTypes.h */,\n\t\t\t\t621ECCB42542045900D3D615 /* CAPBridgedJSTypes.m */,\n\t\t\t\t623D68F9254C5037002D01D1 /* KeyPath.swift */,\n\t\t\t\t62959AFF2524DA7700A3D7F1 /* JS.swift */,\n\t\t\t\t62959AE32524DA7700A3D7F1 /* JSExport.swift */,\n\t\t\t\t621ECCD4254205BD00D3D615 /* CAPBridgeProtocol.swift */,\n\t\t\t\t621ECCD9254205C400D3D615 /* CapacitorBridge.swift */,\n\t\t\t\t62959B142524DA7700A3D7F1 /* CAPBridge.swift */,\n\t\t\t\t62959B042524DA7700A3D7F1 /* CAPBridgeViewController.swift */,\n\t\t\t\t621ECCE2254206A600D3D615 /* CAPApplicationDelegateProxy.swift */,\n\t\t\t\t62959B0A2524DA7700A3D7F1 /* CAPBridgeDelegate.swift */,\n\t\t\t\t62E207AD2588234500A78983 /* WebViewDelegationHandler.swift */,\n\t\t\t\t625AF1EC258963C700869675 /* WebViewAssetHandler.swift */,\n\t\t\t\t62959AEA2524DA7700A3D7F1 /* Plugins */,\n\t\t\t\t623D6907254C6FDF002D01D1 /* CAPInstanceDescriptor.h */,\n\t\t\t\t623D6908254C6FDF002D01D1 /* CAPInstanceDescriptor.m */,\n\t\t\t\t623D6913254C7030002D01D1 /* CAPInstanceDescriptor.swift */,\n\t\t\t\t623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */,\n\t\t\t\t623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */,\n\t\t\t\t6214934625509C3F006C36F9 /* CAPInstanceConfiguration.swift */,\n\t\t\t\t62959B082524DA7700A3D7F1 /* CAPLog.swift */,\n\t\t\t\t62959AE72524DA7700A3D7F1 /* CAPFile.swift */,\n\t\t\t\t62959B0E2524DA7700A3D7F1 /* TmpViewController.swift */,\n\t\t\t\t62959B102524DA7700A3D7F1 /* DocLinks.swift */,\n\t\t\t\t62959B152524DA7700A3D7F1 /* CAPNotifications.swift */,\n\t\t\t\t62959B072524DA7700A3D7F1 /* CapacitorExtension.swift */,\n\t\t\t\t62959B112524DA7700A3D7F1 /* Data+Capacitor.swift */,\n\t\t\t\t62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */,\n\t\t\t\t62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */,\n\t\t\t\t62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */,\n\t\t\t\t2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */,\n\t\t\t\t2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */,\n\t\t\t\t62959AE92524DA7700A3D7F1 /* UIColor.swift */,\n\t\t\t\t62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */,\n\t\t\t\t62959B122524DA7700A3D7F1 /* Info.plist */,\n\t\t\t\t62959B8225253A9500A3D7F1 /* Capacitor.modulemap */,\n\t\t\t\t373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */,\n\t\t\t\t373A69F1255C95D0000A6F44 /* NotificationRouter.swift */,\n\t\t\t\t62E79C562638AF7500414164 /* assets */,\n\t\t\t\tA71289E527F380A500DADDF3 /* Router.swift */,\n\t\t\t\tA7BE62CB2B486A5400165ACB /* KeyValueStore.swift */,\n\t\t\t\t0F83E884285A332D006C43CB /* AppUUID.swift */,\n\t\t\t);\n\t\t\tpath = Capacitor;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t62959AEA2524DA7700A3D7F1 /* Plugins */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t957BD93E2E78A4A20056874C /* SystemBars.swift */,\n\t\t\t\tA327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */,\n\t\t\t\tA327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */,\n\t\t\t\tA327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */,\n\t\t\t\t62959AEF2524DA7700A3D7F1 /* Console.swift */,\n\t\t\t\t62959AF32524DA7700A3D7F1 /* WebView.swift */,\n\t\t\t\tA38C3D7628484E76004B3680 /* CapacitorCookies.swift */,\n\t\t\t\tA38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */,\n\t\t\t);\n\t\t\tpath = Plugins;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t62959B9C2526474300A3D7F1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t62959BA02526474300A3D7F1 /* Cordova.framework */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t6296A77C253A2E49005A202A /* TestsHostApp */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t6296A77D253A2E49005A202A /* AppDelegate.swift */,\n\t\t\t\t6296A781253A2E49005A202A /* ViewController.swift */,\n\t\t\t\t62E0735125535E6500BAAADB /* configurations */,\n\t\t\t\t6296A783253A2E49005A202A /* Main.storyboard */,\n\t\t\t\t6296A786253A2E49005A202A /* Assets.xcassets */,\n\t\t\t\t6296A788253A2E49005A202A /* LaunchScreen.storyboard */,\n\t\t\t\t6296A78B253A2E49005A202A /* Info.plist */,\n\t\t\t);\n\t\t\tpath = TestsHostApp;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t62E0735125535E6500BAAADB /* configurations */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t62E0735225535E6500BAAADB /* server.json */,\n\t\t\t\t62E0735325535E6500BAAADB /* bad.json */,\n\t\t\t\t62E0735425535E6500BAAADB /* flat.json */,\n\t\t\t\t626D2D902613B4BB0046CE81 /* hidinglogs.json */,\n\t\t\t\t62E0735525535E6500BAAADB /* hierarchy.json */,\n\t\t\t\t62A91C392553710300861508 /* nonjson.json */,\n\t\t\t);\n\t\t\tpath = configurations;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t62E79C562638AF7500414164 /* assets */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t62E79C572638AF7500414164 /* native-bridge.js */,\n\t\t\t);\n\t\t\tpath = assets;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tA7D8B3612B263B8D0003FAD6 /* CodableTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tA7D8B3622B263B8D0003FAD6 /* NestedCodableTests.swift */,\n\t\t\t\tA7D8B3562B23B2110003FAD6 /* CodableTests.swift */,\n\t\t\t\tA7D8B36D2B2692300003FAD6 /* SuperCodableTests.swift */,\n\t\t\t\tA771ADED2C8B845000AF234D /* DateCodableTests.swift */,\n\t\t\t\tA771ADF02C8B909100AF234D /* URLCodableTests.swift */,\n\t\t\t\tA7D474D42C8BA8E8005620A8 /* DataCodableTests.swift */,\n\t\t\t\tA7D474D72C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift */,\n\t\t\t);\n\t\t\tpath = CodableTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tA7D9312C2B2370EF00FF59A2 /* Codable */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tA7D9312E2B23710300FF59A2 /* JSValueDecoder.swift */,\n\t\t\t\tA7D8B3512B238A840003FAD6 /* JSValueEncoder.swift */,\n\t\t\t);\n\t\t\tpath = Codable;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\t50503EDC1FC08594003606DC /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t62959B412524DA7800A3D7F1 /* Capacitor.h in Headers */,\n\t\t\t\t62959B452524DA7800A3D7F1 /* CAPPlugin.h in Headers */,\n\t\t\t\t62959B162524DA7800A3D7F1 /* CAPPluginCall.h in Headers */,\n\t\t\t\t62959B3B2524DA7800A3D7F1 /* CAPPluginMethod.h in Headers */,\n\t\t\t\t623D691D254C7462002D01D1 /* CAPInstanceConfiguration.h in Headers */,\n\t\t\t\t623D6909254C6FDF002D01D1 /* CAPInstanceDescriptor.h in Headers */,\n\t\t\t\t62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */,\n\t\t\t\t621ECCB82542045900D3D615 /* CAPBridgedJSTypes.h in Headers */,\n\t\t\t\t2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\t50503EDE1FC08594003606DC /* Capacitor */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 50503EF31FC08595003606DC /* Build configuration list for PBXNativeTarget \"Capacitor\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t50503EDA1FC08594003606DC /* Sources */,\n\t\t\t\t50503EDB1FC08594003606DC /* Frameworks */,\n\t\t\t\t50503EDC1FC08594003606DC /* Headers */,\n\t\t\t\t50503EDD1FC08594003606DC /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Capacitor;\n\t\t\tproductName = Avocado;\n\t\t\tproductReference = 50503EDF1FC08594003606DC /* Capacitor.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\t50503EE71FC08595003606DC /* CapacitorTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 50503EF61FC08595003606DC /* Build configuration list for PBXNativeTarget \"CapacitorTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t50503EE41FC08595003606DC /* Sources */,\n\t\t\t\t50503EE51FC08595003606DC /* Frameworks */,\n\t\t\t\t50503EE61FC08595003606DC /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t50503EEB1FC08595003606DC /* PBXTargetDependency */,\n\t\t\t\t6296A797253A2EAE005A202A /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = CapacitorTests;\n\t\t\tproductName = AvocadoTests;\n\t\t\tproductReference = 50503EE81FC08595003606DC /* CapacitorTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t6296A77A253A2E49005A202A /* TestsHostApp */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 6296A78F253A2E49005A202A /* Build configuration list for PBXNativeTarget \"TestsHostApp\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t6296A777253A2E49005A202A /* Sources */,\n\t\t\t\t6296A778253A2E49005A202A /* Frameworks */,\n\t\t\t\t6296A779253A2E49005A202A /* Resources */,\n\t\t\t\t622BB9C32541FE1900A5DBCA /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = TestsHostApp;\n\t\t\tproductName = TestsHostApp;\n\t\t\tproductReference = 6296A77B253A2E49005A202A /* TestsHostApp.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tA7D8B35F2B263B8D0003FAD6 /* CodableTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = A7D8B3672B263B8D0003FAD6 /* Build configuration list for PBXNativeTarget \"CodableTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tA7D8B35C2B263B8D0003FAD6 /* Sources */,\n\t\t\t\tA7D8B35D2B263B8D0003FAD6 /* Frameworks */,\n\t\t\t\tA7D8B35E2B263B8D0003FAD6 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tA7D8B3662B263B8D0003FAD6 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = CodableTests;\n\t\t\tproductName = CodableTests;\n\t\t\tproductReference = A7D8B3602B263B8D0003FAD6 /* CodableTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t50503ED61FC08594003606DC /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1500;\n\t\t\t\tLastUpgradeCheck = 1240;\n\t\t\t\tORGANIZATIONNAME = \"Drifty Co.\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t50503EDE1FC08594003606DC = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.0;\n\t\t\t\t\t\tLastSwiftMigration = 0940;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\t50503EE71FC08595003606DC = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.0;\n\t\t\t\t\t\tLastSwiftMigration = 1200;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tTestTargetID = 6296A77A253A2E49005A202A;\n\t\t\t\t\t};\n\t\t\t\t\t6296A77A253A2E49005A202A = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 12.0;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tA7D8B35F2B263B8D0003FAD6 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 15.0.1;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 50503ED91FC08594003606DC /* Build configuration list for PBXProject \"Capacitor\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 50503ED51FC08594003606DC;\n\t\t\tproductRefGroup = 50503EE01FC08594003606DC /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectReferences = (\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 62959B9C2526474300A3D7F1 /* Products */;\n\t\t\t\t\tProjectRef = 62959B9B2526474300A3D7F1 /* CapacitorCordova.xcodeproj */;\n\t\t\t\t},\n\t\t\t);\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t50503EDE1FC08594003606DC /* Capacitor */,\n\t\t\t\t50503EE71FC08595003606DC /* CapacitorTests */,\n\t\t\t\t6296A77A253A2E49005A202A /* TestsHostApp */,\n\t\t\t\tA7D8B35F2B263B8D0003FAD6 /* CodableTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXReferenceProxy section */\n\t\t62959BA02526474300A3D7F1 /* Cordova.framework */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = wrapper.framework;\n\t\t\tpath = Cordova.framework;\n\t\t\tremoteRef = 62959B9F2526474300A3D7F1 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n/* End PBXReferenceProxy section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t50503EDD1FC08594003606DC /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tA76739792B98E09700795F7B /* PrivacyInfo.xcprivacy in Resources */,\n\t\t\t\t62E79CD7263A178B00414164 /* native-bridge.js in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t50503EE61FC08595003606DC /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t6296A779253A2E49005A202A /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t6296A78A253A2E49005A202A /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t6296A787253A2E49005A202A /* Assets.xcassets in Resources */,\n\t\t\t\t6296A785253A2E49005A202A /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tA7D8B35E2B263B8D0003FAD6 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t50503EDA1FC08594003606DC /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tA38C3D7728484E76004B3680 /* CapacitorCookies.swift in Sources */,\n\t\t\t\tA71289E627F380A500DADDF3 /* Router.swift in Sources */,\n\t\t\t\tA327E6B828DB8B2900CA8B0A /* CapacitorUrlRequest.swift in Sources */,\n\t\t\t\tA7D9312F2B23710300FF59A2 /* JSValueDecoder.swift in Sources */,\n\t\t\t\t62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */,\n\t\t\t\t621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */,\n\t\t\t\t62959B402524DA7800A3D7F1 /* TmpViewController.swift in Sources */,\n\t\t\t\t621ECCD6254205BD00D3D615 /* CAPBridgeProtocol.swift in Sources */,\n\t\t\t\t62D43AF02581817500673C24 /* WKWebView+Capacitor.swift in Sources */,\n\t\t\t\t62959B432524DA7800A3D7F1 /* Data+Capacitor.swift in Sources */,\n\t\t\t\t62E207AE2588234500A78983 /* WebViewDelegationHandler.swift in Sources */,\n\t\t\t\tA7187FD22BD1CB7D00093C45 /* CAPPluginMethod.swift in Sources */,\n\t\t\t\t621ECCBC2542046400D3D615 /* JSTypes.swift in Sources */,\n\t\t\t\t621ECCDA254205C400D3D615 /* CapacitorBridge.swift in Sources */,\n\t\t\t\t62959B382524DA7800A3D7F1 /* CAPPluginCall.m in Sources */,\n\t\t\t\t623D690A254C6FDF002D01D1 /* CAPInstanceDescriptor.m in Sources */,\n\t\t\t\tA7D8B3522B238A840003FAD6 /* JSValueEncoder.swift in Sources */,\n\t\t\t\tA7F7EDD5292BE8520015B73B /* CAPInstancePlugin.swift in Sources */,\n\t\t\t\tA38C3D7B2848BE6F004B3680 /* CapacitorCookieManager.swift in Sources */,\n\t\t\t\t62959B1B2524DA7800A3D7F1 /* CAPFile.swift in Sources */,\n\t\t\t\t62959B462524DA7800A3D7F1 /* CAPBridge.swift in Sources */,\n\t\t\t\t62959B1D2524DA7800A3D7F1 /* UIColor.swift in Sources */,\n\t\t\t\t62959B332524DA7800A3D7F1 /* CAPPlugin.m in Sources */,\n\t\t\t\t62959B1C2524DA7800A3D7F1 /* CAPPluginMethod.m in Sources */,\n\t\t\t\t2F81F5CA26FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m in Sources */,\n\t\t\t\t62ADC0CA25CB678000E914DE /* PluginCallResult.swift in Sources */,\n\t\t\t\t62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */,\n\t\t\t\t62D43B652582A13D00673C24 /* WKWebView+Capacitor.m in Sources */,\n\t\t\t\t62959B312524DA7800A3D7F1 /* JS.swift in Sources */,\n\t\t\t\t373A69F2255C95D0000A6F44 /* NotificationRouter.swift in Sources */,\n\t\t\t\t62959B1A2524DA7800A3D7F1 /* CAPPluginCall.swift in Sources */,\n\t\t\t\t62959B302524DA7800A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m in Sources */,\n\t\t\t\t62959B392524DA7800A3D7F1 /* CapacitorExtension.swift in Sources */,\n\t\t\t\tA327E6B628DB8B2900CA8B0A /* HttpRequestHandler.swift in Sources */,\n\t\t\t\t957BD9402E78A4A50056874C /* SystemBars.swift in Sources */,\n\t\t\t\t62959B422524DA7800A3D7F1 /* DocLinks.swift in Sources */,\n\t\t\t\t62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */,\n\t\t\t\tA7BE62CC2B486A5400165ACB /* KeyValueStore.swift in Sources */,\n\t\t\t\t62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */,\n\t\t\t\t373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */,\n\t\t\t\t0F83E885285A332E006C43CB /* AppUUID.swift in Sources */,\n\t\t\t\t625AF1ED258963C700869675 /* WebViewAssetHandler.swift in Sources */,\n\t\t\t\tA327E6B728DB8B2900CA8B0A /* CapacitorHttp.swift in Sources */,\n\t\t\t\t0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */,\n\t\t\t\t62959B3C2524DA7800A3D7F1 /* CAPBridgeDelegate.swift in Sources */,\n\t\t\t\t623D691E254C7462002D01D1 /* CAPInstanceConfiguration.m in Sources */,\n\t\t\t\t623D68FA254C5037002D01D1 /* KeyPath.swift in Sources */,\n\t\t\t\t62959B222524DA7800A3D7F1 /* Console.swift in Sources */,\n\t\t\t\t62959B3A2524DA7800A3D7F1 /* CAPLog.swift in Sources */,\n\t\t\t\tA7DB03AC29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift in Sources */,\n\t\t\t\t6214934725509C3F006C36F9 /* CAPInstanceConfiguration.swift in Sources */,\n\t\t\t\t623D6914254C7030002D01D1 /* CAPInstanceDescriptor.swift in Sources */,\n\t\t\t\t621ECCE3254206A600D3D615 /* CAPApplicationDelegateProxy.swift in Sources */,\n\t\t\t\tA7F7EDCD291EC75C0015B73B /* CAPPlugin+LoadInstance.swift in Sources */,\n\t\t\t\t62959B262524DA7800A3D7F1 /* WebView.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t50503EE41FC08595003606DC /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t62E79C722638B23300414164 /* JSExportTests.swift in Sources */,\n\t\t\t\t50503EEE1FC08595003606DC /* CapacitorTests.swift in Sources */,\n\t\t\t\t62FABD2B25AE6182007B3814 /* BridgedTypesHelper.swift in Sources */,\n\t\t\t\t621ECCC8254204BE00D3D615 /* JSONSerializationWrapper.m in Sources */,\n\t\t\t\t62A91C3425535F5700861508 /* ConfigurationTests.swift in Sources */,\n\t\t\t\t62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */,\n\t\t\t\t621ECCC3254204B700D3D615 /* BridgedTypesTests.swift in Sources */,\n\t\t\t\tA71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */,\n\t\t\t\t6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t6296A777253A2E49005A202A /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t6296A782253A2E49005A202A /* ViewController.swift in Sources */,\n\t\t\t\t6296A77E253A2E49005A202A /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tA7D8B35C2B263B8D0003FAD6 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tA771ADEE2C8B845000AF234D /* DateCodableTests.swift in Sources */,\n\t\t\t\tA7D8B3632B263B8D0003FAD6 /* NestedCodableTests.swift in Sources */,\n\t\t\t\tA7D474D52C8BA8E8005620A8 /* DataCodableTests.swift in Sources */,\n\t\t\t\tA7D8B36A2B263B990003FAD6 /* CodableTests.swift in Sources */,\n\t\t\t\tA7D8B36E2B2692300003FAD6 /* SuperCodableTests.swift in Sources */,\n\t\t\t\tA7D474D82C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift in Sources */,\n\t\t\t\tA771ADF12C8B909100AF234D /* URLCodableTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t50503EEB1FC08595003606DC /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 50503EDE1FC08594003606DC /* Capacitor */;\n\t\t\ttargetProxy = 50503EEA1FC08595003606DC /* PBXContainerItemProxy */;\n\t\t};\n\t\t6296A797253A2EAE005A202A /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 6296A77A253A2E49005A202A /* TestsHostApp */;\n\t\t\ttargetProxy = 6296A796253A2EAE005A202A /* PBXContainerItemProxy */;\n\t\t};\n\t\tA7D8B3662B263B8D0003FAD6 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 50503EDE1FC08594003606DC /* Capacitor */;\n\t\t\ttargetProxy = A7D8B3652B263B8D0003FAD6 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t6296A783253A2E49005A202A /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t6296A784253A2E49005A202A /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t6296A788253A2E49005A202A /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t6296A789253A2E49005A202A /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t50503EF11FC08595003606DC /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tBUILD_LIBRARY_FOR_DISTRIBUTION = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t50503EF21FC08595003606DC /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tBUILD_LIBRARY_FOR_DISTRIBUTION = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Owholemodule\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t50503EF41FC08595003606DC /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Capacitor/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMODULEMAP_FILE = Capacitor/Capacitor.modulemap;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.Capacitor;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t50503EF51FC08595003606DC /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Capacitor/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMODULEMAP_FILE = Capacitor/Capacitor.modulemap;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.Capacitor;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t50503EF71FC08595003606DC /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUILD_LIBRARY_FOR_DISTRIBUTION = NO;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = CapacitorTests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CapacitorTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"CapacitorTests/CapacitorTests-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t50503EF81FC08595003606DC /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUILD_LIBRARY_FOR_DISTRIBUTION = NO;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = CapacitorTests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CapacitorTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"CapacitorTests/CapacitorTests-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t6296A78C253A2E49005A202A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = TestsHostApp/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.TestsHostApp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t6296A78D253A2E49005A202A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = TestsHostApp/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.TestsHostApp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tA7D8B3682B263B8D0003FAD6 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CodableTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"DEBUG $(inherited)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tA7D8B3692B263B8D0003FAD6 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CodableTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t50503ED91FC08594003606DC /* Build configuration list for PBXProject \"Capacitor\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t50503EF11FC08595003606DC /* Debug */,\n\t\t\t\t50503EF21FC08595003606DC /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t50503EF31FC08595003606DC /* Build configuration list for PBXNativeTarget \"Capacitor\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t50503EF41FC08595003606DC /* Debug */,\n\t\t\t\t50503EF51FC08595003606DC /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t50503EF61FC08595003606DC /* Build configuration list for PBXNativeTarget \"CapacitorTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t50503EF71FC08595003606DC /* Debug */,\n\t\t\t\t50503EF81FC08595003606DC /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t6296A78F253A2E49005A202A /* Build configuration list for PBXNativeTarget \"TestsHostApp\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t6296A78C253A2E49005A202A /* Debug */,\n\t\t\t\t6296A78D253A2E49005A202A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tA7D8B3672B263B8D0003FAD6 /* Build configuration list for PBXNativeTarget \"CodableTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tA7D8B3682B263B8D0003FAD6 /* Debug */,\n\t\t\t\tA7D8B3692B263B8D0003FAD6 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 50503ED61FC08594003606DC /* Project object */;\n}\n"
  },
  {
    "path": "ios/Capacitor/Capacitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Capacitor/Capacitor.xcodeproj/xcshareddata/xcschemes/Capacitor.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1240\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"50503EDE1FC08594003606DC\"\n               BuildableName = \"Capacitor.framework\"\n               BlueprintName = \"Capacitor\"\n               ReferencedContainer = \"container:Capacitor.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"50503EE71FC08595003606DC\"\n               BuildableName = \"CapacitorTests.xctest\"\n               BlueprintName = \"CapacitorTests\"\n               ReferencedContainer = \"container:Capacitor.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"A7D8B35F2B263B8D0003FAD6\"\n               BuildableName = \"CodableTests.xctest\"\n               BlueprintName = \"CodableTests\"\n               ReferencedContainer = \"container:Capacitor.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"50503EDE1FC08594003606DC\"\n            BuildableName = \"Capacitor.framework\"\n            BlueprintName = \"Capacitor\"\n            ReferencedContainer = \"container:Capacitor.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ios/Capacitor/Capacitor.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/BridgedTypesHelper.swift",
    "content": "import Foundation\n@testable import Capacitor\n\nenum BridgeTypeError: Error {\n    case badCast\n}\n\n@objc class BridgedTypesHelper: NSObject {\n    @objc static let shared = BridgedTypesHelper()\n    \n    var untypedArray: [Any] {\n        return []\n    }\n    \n    @objc func validTransformationOf(array: [Any]) -> [Any] {\n        let result = JSTypes.coerceArrayToJSArray(array)!.capacitor.replacingNullValues()\n        return result.capacitor.replacingOptionalValues() as [Any]\n    }\n    \n    @objc func invalidTransformationOf(array: [Any]) -> [Any] {\n        let result = JSTypes.coerceArrayToJSArray(array)!.capacitor.replacingNullValues()\n        return result as [Any]\n    }\n    \n    @objc func testCast(of array: [Any], atIndex index: Int) throws -> Any {\n        if let castArray = array as? [JSValue] {\n            return castArray[index] as Any\n        }\n        throw BridgeTypeError.badCast\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/BridgedTypesTests.m",
    "content": "#import <XCTest/XCTest.h>\n#import <Capacitor/Capacitor.h>\n#import \"CapacitorTests-Swift.h\"\n\n// interface for this class\n@interface BridgedTypesTestsObjc : XCTestCase\n@end\n\n@implementation BridgedTypesTestsObjc\n\n- (void)setUp {\n    // Put setup code here. This method is called before the invocation of each test method in the class.\n}\n\n- (void)tearDown {\n    // Put teardown code here. This method is called after the invocation of each test method in the class.\n}\n\n- (void)testNullHandling {\n    NSArray* source = @[@\"test\", [NSNull null], @3];\n    NSArray* result = [[BridgedTypesHelper shared] validTransformationOfArray:source];\n    NSError *error = nil;\n    // test that the replaced null value exists\n    id value = [result objectAtIndex:1];\n    XCTAssertNotNil(value);\n    XCTAssertTrue([value isKindOfClass:[NSNull class]]);\n    // test that the null value casts to non-optional\n    value = [[BridgedTypesHelper shared] testCastOf:result atIndex:1 error:&error];\n    XCTAssertNotNil(value);\n    XCTAssertNil(error);\n}\n\n- (void)testOptionalHandling {\n    NSArray* source = @[@\"test\", [NSNull null], @3];\n    NSArray* result = [[BridgedTypesHelper shared] invalidTransformationOfArray:source];\n    NSError *error = nil;\n    // test that the removed null value, now optional, is automatically transformed back into a NSNull\n    id value = [result objectAtIndex:1];\n    XCTAssertNotNil(value);\n    XCTAssertTrue([value isKindOfClass:[NSNull class]]);\n    // test that the optional value fails to cast to non-optional\n    value = [[BridgedTypesHelper shared] testCastOf:result atIndex:1 error:&error];\n    XCTAssertNil(value);\n    XCTAssertNotNil(error);\n}\n@end\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/BridgedTypesTests.swift",
    "content": "import XCTest\n\n@testable import Capacitor\n\nclass TestContainer: NSObject, JSValueContainer {\n    var coercedDictionary: [AnyHashable: Any] = [:]\n    \n    public static var jsDateFormatter: ISO8601DateFormatter = {\n        return ISO8601DateFormatter()\n    }()\n    \n    public var jsObjectRepresentation: JSObject {\n        return coercedDictionary as? JSObject ?? [:]\n    }\n}\n\nclass BridgedTypesTests: XCTestCase {\n    static var unserializedDictionary: [AnyHashable: Any] = [:]\n    static var deserializedDictionary: [AnyHashable: Any] = [:]\n    \n    var unserializedDictionary: [AnyHashable: Any] = [:]\n    var deserializedDictionary: [AnyHashable: Any] = [:]\n    var testContainer = TestContainer()\n    \n    override class func setUp() {\n        let formatter = ISO8601DateFormatter()\n        // an ISO 8601 string does not necessarily include subsecond precision, so we can't just capture the current date\n        // or else we won't be able to compare the objects since they could differ by milliseconds or nanoseonds. so instead\n        // we use a fixed timestamp at a whole hour.\n        let date = NSDate(timeIntervalSinceReferenceDate: 632854800)\n        let subDictionary: [AnyHashable: Any] = [\"testIntArray\": [0, 1, 2], \"testStringArray\": [\"1\", \"2\", \"3\"], \"testDictionary\":[\"foo\":\"bar\"]]\n        var dictionary: [AnyHashable: Any] = [\"testInt\": 1 as Int, \"testFloat\": Float.pi, \"testBool\": true as Bool, \"testString\": \"Some string value\", \"testChild\": subDictionary, \"testDateString\": formatter.string(from: date as Date)]\n        let serializer = JSONSerializationWrapper(dictionary: dictionary)!\n        var unwrappedResult = serializer.unwrappedResult()!\n        // date objects are not handled by the JSON serializer, so we have to insert these after the roundtrip\n        unwrappedResult[\"testDateObject\"] = date\n        dictionary[\"testDateObject\"] = date\n        unserializedDictionary = dictionary\n        deserializedDictionary = unwrappedResult\n    }\n    \n    override func setUpWithError() throws {\n        // Put setup code here. This method is called before the invocation of each test method in the class.\n        unserializedDictionary = BridgedTypesTests.unserializedDictionary\n        deserializedDictionary = BridgedTypesTests.deserializedDictionary\n        testContainer.coercedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)!\n    }\n    \n    override func tearDownWithError() throws {\n        // Put teardown code here. This method is called after the invocation of each test method in the class.\n    }\n    \n    func testTranslation() throws {\n        XCTAssertTrue(unserializedDictionary.count > 0)\n        XCTAssertTrue(deserializedDictionary.count > 0)\n        XCTAssertTrue(testContainer.coercedDictionary.count > 0)\n    }\n    \n    func testCastingFailure() throws {\n        var castResult = deserializedDictionary as? JSObject\n        XCTAssertNil(castResult)\n        \n        castResult = unserializedDictionary as? JSObject\n        XCTAssertNil(castResult)\n    }\n    \n    func testCoercionSuccess() throws {\n        let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)\n        XCTAssertNotNil(coercedResult)\n    }\n    \n    func testRoundtripEquality() throws {\n        let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)!\n        let foo: NSDictionary = coercedResult as NSDictionary\n        let bar: NSDictionary = unserializedDictionary as NSDictionary\n        \n        XCTAssertEqual(foo, bar)\n    }\n    \n    func testTypeEquavalency() throws {\n        let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)!\n        let coercedFloat = coercedResult[\"testFloat\"] as? Float\n        let sourceFloat = unserializedDictionary[\"testFloat\"] as? Float\n        let resultFloat = deserializedDictionary[\"testFloat\"] as? Float\n        \n        XCTAssertNotNil(coercedFloat)\n        XCTAssertNotNil(sourceFloat)\n        XCTAssertNotNil(resultFloat)\n        \n        XCTAssertEqual(coercedFloat, sourceFloat)\n        XCTAssertEqual(sourceFloat, resultFloat)\n        XCTAssertEqual(coercedFloat, Float.pi)\n    }\n    \n    func testNumberWrapping() throws {\n        // the original number is a swift primitive float\n        let sourceFloat = unserializedDictionary[\"testFloat\"]!\n        XCTAssertTrue(type(of: sourceFloat) == Float.self)\n        \n        // but after serialization/deserilization, it will be wrapped as an NSNumber\n        let wrappedFloat = deserializedDictionary[\"testFloat\"]!\n        let underlyingType: AnyObject.Type = NSClassFromString(\"__NSCFNumber\")!\n        XCTAssertTrue(type(of: wrappedFloat) == underlyingType.self)\n        \n        // coercion will keep the NSNumber type since there's no way to recover it\n        let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)!\n        let coercedFloat = coercedResult[\"testFloat\"]!\n        XCTAssertTrue(type(of: coercedFloat) == underlyingType.self)\n        \n        // but the cast accessor should restore it\n        let castFloat = testContainer.getFloat(\"testFloat\")!\n        XCTAssertTrue(type(of: castFloat) == Float.self)\n        XCTAssertEqual(sourceFloat as! Float, castFloat)\n    }\n    \n    func testDateObject() throws {\n        let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)!\n        let date = coercedResult[\"testDateObject\"] as! Date\n        XCTAssertNotNil(date)\n        XCTAssertTrue(type(of: date) == Date.self)\n    }\n    \n    func testDateParsing() throws {\n        let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)!\n        let formatter = ISO8601DateFormatter()\n        let parsedDate = formatter.date(from: coercedResult[\"testDateString\"] as! String)!\n        let dateObject = coercedResult[\"testDateObject\"] as! Date\n        XCTAssertNotNil(parsedDate)\n        XCTAssertNotNil(dateObject)\n        XCTAssertTrue(dateObject.compare(parsedDate) == .orderedSame)\n    }\n    \n    func testDateExtensions() throws {\n        let parsedDate = testContainer.getDate(\"testDateString\")!\n        let dateObject = testContainer.getDate(\"testDateObject\")!\n        XCTAssertNotNil(parsedDate)\n        XCTAssertNotNil(dateObject)\n        XCTAssertTrue(dateObject.compare(parsedDate) == .orderedSame)\n    }\n    \n    func testDateCoercion() throws {\n        let stringifiedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary, formattingDatesAsStrings: true)!\n        let unstringifiedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary, formattingDatesAsStrings: false)!\n        let stringifiedValue = stringifiedDictionary[\"testDateObject\"]!\n        let unstringifiedValue = unstringifiedDictionary[\"testDateObject\"]!\n        XCTAssertTrue(type(of: stringifiedValue) == String.self)\n        XCTAssertTrue(type(of: unstringifiedValue) == Date.self)\n        XCTAssertEqual(stringifiedValue as! String, stringifiedDictionary[\"testDateString\"] as! String)\n    }\n    \n    func testDateResultWrapping() throws {\n        let result = try PluginCallResult.dictionary([\"date\": unserializedDictionary[\"testDateObject\"]!]).jsonRepresentation()\n        XCTAssertEqual(result, \"{\\\"date\\\":\\\"\\(unserializedDictionary[\"testDateString\"] as! String)\\\"}\")\n    }\n    \n    func testResultMerging() throws {\n        let result = try PluginCallResult.dictionary([\"number\": 1]).jsonRepresentation(includingFields: [\"string\":\"foo\"])\n        // ordering of the pairs should be non-deterministic\n        if result != \"{\\\"string\\\":\\\"foo\\\",\\\"number\\\":1}\" && result != \"{\\\"number\\\":1,\\\"string\\\":\\\"foo\\\"}\" {\n            XCTAssert(false)\n        }\n    }\n    \n    func testNullWrapping() throws {\n        let dictionary: [AnyHashable: Any] = [\"testInt\": 1 as Int, \"testNull\": NSNull()]\n        let coercedDictionary = JSTypes.coerceDictionaryToJSObject(dictionary)!\n        XCTAssertNotNil(coercedDictionary)\n        XCTAssertEqual(coercedDictionary.count, 2)\n        XCTAssertTrue(coercedDictionary[\"testNull\"]! is NSNull)\n    }\n    \n    func testNullTransformation() throws {\n        let array: [Any] = [1, NSNull(), \"test string\"]\n        let coercedArray = JSTypes.coerceArrayToJSArray(array)!\n        XCTAssertNotNil(coercedArray)\n        XCTAssertEqual(coercedArray.count, 3)\n        XCTAssertTrue(type(of: coercedArray[1]) == NSNull.self)\n        let filteredArray = coercedArray.capacitor.replacingNullValues()\n        XCTAssertEqual(filteredArray.count, 3)\n        XCTAssertNil(filteredArray[1])\n        let restoredArray = filteredArray.capacitor.replacingOptionalValues()\n        XCTAssertEqual(restoredArray.count, 3)\n        XCTAssertNotNil(restoredArray[1])\n        XCTAssertTrue(restoredArray[0] is NSNumber)\n        XCTAssertTrue(restoredArray[1] is NSNull)\n        XCTAssertTrue(restoredArray[2] is String)\n    }\n    \n    func testSparseArrayCastSuccess() throws {\n        let array: [Any] = [\"test string 1\", \"test string 2\", NSNull()]\n        let sparseArray = JSTypes.coerceArrayToJSArray(array)?.capacitor.replacingNullValues() as? [String?]\n        XCTAssertNotNil(sparseArray)\n        XCTAssertEqual(sparseArray!.count, 3)\n        XCTAssertNil(sparseArray![2])\n    }\n    \n    func testSparseArrayCastFailure() throws {\n        let array: [Any] = [\"test string 1\", 1, NSNull()]\n        let sparseArray = JSTypes.coerceArrayToJSArray(array)?.capacitor.replacingNullValues() as? [String?]\n        XCTAssertNil(sparseArray)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/CapacitorTests-Bridging-Header.h",
    "content": "//\n//  Use this file to import your target's public headers that you would like to expose to Swift.\n//\n\n#import \"JSONSerializationWrapper.h\"\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/CapacitorTests.swift",
    "content": "import XCTest\n@testable import Capacitor\n\nclass MockBridgeViewController: CAPBridgeViewController {\n}\n\nclass MockAssetHandler: WebViewAssetHandler {\n}\n\nclass MockDelegationHandler: WebViewDelegationHandler {\n}\n\nclass MockBridge: CapacitorBridge {\n    override public func registerPlugins() {\n        Swift.print(\"REGISTER PLUGINS\")\n    }\n}\n\nclass CapacitorTests: XCTestCase {\n    var bridge: MockBridge?\n\n    override func setUp() {\n        super.setUp()\n        // Put setup code here. This method is called before the invocation of each test method in the class.\n        let descriptor = InstanceDescriptor.init()\n        bridge = MockBridge(with: InstanceConfiguration(with: descriptor, isDebug: true), delegate: MockBridgeViewController(), cordovaConfiguration: descriptor.cordovaConfiguration, assetHandler: MockAssetHandler(router: CapacitorRouter()), delegationHandler: MockDelegationHandler())\n    }\n\n    override func tearDown() {\n        // Put teardown code here. This method is called after the invocation of each test method in the class.\n        super.tearDown()\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/ConfigurationTests.swift",
    "content": "import XCTest\n\n@testable import Capacitor\n\nclass ConfigurationTests: XCTestCase {\n    enum ConfigFile: String, CaseIterable {\n        case flat = \"flat\"\n        case nested = \"hierarchy\"\n        case server = \"server\"\n        case invalid = \"bad\"\n        case deprecated = \"hidinglogs\"\n        case nonparsable = \"nonjson\"\n    }\n    static var files: [ConfigFile: URL] = [:]\n    \n    override class func setUp() {\n        for file in ConfigFile.allCases {\n            if let url = Bundle.main.url(forResource: file.rawValue, withExtension: \"json\", subdirectory: \"configurations\") {\n                files[file] = url\n            }\n        }\n    }\n    \n    override func setUpWithError() throws {\n        XCTAssert(ConfigurationTests.files.count == ConfigFile.allCases.count, \"Not all configuration files were located\")\n    }\n\n    override func tearDownWithError() throws {\n        // Put teardown code here. This method is called after the invocation of each test method in the class.\n    }\n    \n    func testDefaultErrors() throws {\n        let descriptor = InstanceDescriptor.init()\n        XCTAssertTrue(descriptor.warnings.contains(.missingAppDir))\n        XCTAssertTrue(descriptor.warnings.contains(.missingFile))\n        XCTAssertTrue(descriptor.warnings.contains(.missingCordovaFile))\n    }\n    \n    func testMissingAppDetection() throws {\n        var url = Bundle.main.resourceURL!\n        url.appendPathComponent(\"app\", isDirectory: true)\n        let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil)\n        XCTAssertTrue(descriptor.warnings.contains(.missingAppDir), \"A missing app directory was ignored\")\n    }\n    \n    func testFailedParsing() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.nonparsable], cordovaConfiguration: nil)\n        XCTAssertTrue(descriptor.warnings.contains(.invalidFile))\n    }\n    \n    func testDefaults() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil)\n        XCTAssertNil(descriptor.backgroundColor)\n        XCTAssertEqual(descriptor.urlScheme, \"capacitor\")\n        XCTAssertEqual(descriptor.urlHostname, \"localhost\")\n        XCTAssertNil(descriptor.serverURL)\n        XCTAssertTrue(descriptor.scrollingEnabled)\n        XCTAssertEqual(descriptor.loggingBehavior, .debug)\n        XCTAssertTrue(descriptor.allowLinkPreviews)\n        XCTAssertEqual(descriptor.contentInsetAdjustmentBehavior, .never)\n    }\n    \n    func testDeprecatedParsing() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.deprecated], cordovaConfiguration: nil)\n        #warning(\"Is this supposed to fail?\")\n        XCTExpectFailure {\n            XCTAssertEqual(descriptor.loggingBehavior, .none)\n        }\n    }\n    \n    func testDeprecatedOverrideParsing() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil)\n        XCTAssertEqual(descriptor.loggingBehavior, .production)\n    }\n    \n    func testTopLevelParsing() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.flat], cordovaConfiguration: nil)\n        XCTAssertEqual(descriptor.backgroundColor, UIColor(red: 1, green: 1, blue: 1, alpha: 1))\n        XCTAssertEqual(descriptor.overridenUserAgentString, \"level 1 override\")\n        XCTAssertEqual(descriptor.appendedUserAgentString, \"level 1 append\")\n        XCTAssertEqual(descriptor.loggingBehavior, .debug)\n    }\n    \n    func testNestedParsing() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.nested], cordovaConfiguration: nil)\n        XCTAssertEqual(descriptor.backgroundColor, UIColor(red: 0, green: 0, blue: 0, alpha: 1))\n        XCTAssertEqual(descriptor.overridenUserAgentString, \"level 2 override\")\n        XCTAssertEqual(descriptor.appendedUserAgentString, \"level 2 append\")\n        XCTAssertEqual(descriptor.loggingBehavior, .none)\n        XCTAssertFalse(descriptor.scrollingEnabled)\n        XCTAssertEqual(descriptor.contentInsetAdjustmentBehavior, .scrollableAxes)\n    }\n    \n    func testServerParsing() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil)\n        XCTAssertEqual(descriptor.urlScheme, \"override\")\n        XCTAssertEqual(descriptor.urlHostname, \"myhost\")\n        XCTAssertEqual(descriptor.serverURL, \"http://192.168.100.1:2057\")\n    }\n    \n    func testBadDataParsing() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.invalid], cordovaConfiguration: nil)\n        XCTAssertNil(descriptor.backgroundColor)\n        XCTAssertEqual(descriptor.loggingBehavior, .debug)\n        XCTAssertEqual(descriptor.contentInsetAdjustmentBehavior, .never)\n    }\n    \n    func testBadDataTransformation() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.invalid], cordovaConfiguration: nil)\n        let configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        #warning(\"Address this. These tests haven't been run during CI since maybe ever?\")\n        XCTExpectFailure {\n            XCTAssertEqual(configuration.serverURL, URL(string: \"capacitor://myhost\"), \"Invalid server.url and invalid ioScheme were not ignored\")\n        }\n    }\n    \n    func testServerTransformation() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil)\n        let configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        XCTAssertEqual(configuration.serverURL, URL(string: \"http://192.168.100.1:2057\"))\n        XCTAssertEqual(configuration.localURL, URL(string: \"override://myhost\"))\n    }\n    \n    func testPluginConfig() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.flat], cordovaConfiguration: nil)\n        let configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        let value = configuration.getPluginConfigValue(\"SplashScreen\", \"launchShowDuration\") as? Int\n        XCTAssertNotNil(value)\n        XCTAssertTrue(value == 1)\n    }\n    \n    func testLegacyConfig() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.nested], cordovaConfiguration: nil)\n        let configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        var value = configuration.getValue(\"overrideUserAgent\") as? String\n        XCTAssertEqual(value, \"level 1 override\")\n        value = configuration.getValue(\"ios.overrideUserAgent\") as? String\n        XCTAssertEqual(value, \"level 2 override\")\n    }\n    \n    func testNavigationRules() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil)\n        let configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        XCTAssertTrue(configuration.shouldAllowNavigation(to: \"ionic.io\"))\n        XCTAssertTrue(configuration.shouldAllowNavigation(to: \"ionic.io\".uppercased()))\n        XCTAssertTrue(configuration.shouldAllowNavigation(to: \"test.capacitorjs.com\"))\n        XCTAssertTrue(configuration.shouldAllowNavigation(to: \"192.168.0.1\"))\n        XCTAssertTrue(configuration.shouldAllowNavigation(to: \"subdomain.test.ionicframework.com\"))\n        XCTAssertTrue(configuration.shouldAllowNavigation(to: \"wildcard1.wildcard2.example.com\"))\n        XCTAssertFalse(configuration.shouldAllowNavigation(to: \"wildcard1.example.com\"))\n        XCTAssertFalse(configuration.shouldAllowNavigation(to: \"google.com\"))\n        XCTAssertFalse(configuration.shouldAllowNavigation(to: \"192.168.0.2\"))\n        XCTAssertFalse(configuration.shouldAllowNavigation(to: \"ionicframework.com\"))\n    }\n    \n    func testNoLoggingTransformation() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil)\n        descriptor.loggingBehavior = .none\n        var configuration = InstanceConfiguration(with: descriptor, isDebug: false)\n        XCTAssertFalse(configuration.loggingEnabled)\n        configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        XCTAssertFalse(configuration.loggingEnabled)\n    }\n    \n    func testDebugLoggingTransformation() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil)\n        descriptor.loggingBehavior = .debug\n        var configuration = InstanceConfiguration(with: descriptor, isDebug: false)\n        XCTAssertFalse(configuration.loggingEnabled)\n        configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        XCTAssertTrue(configuration.loggingEnabled)\n    }\n    \n    func testProductionLoggingTransformation() throws {\n        let url = Bundle.main.url(forResource: \"configurations\", withExtension: \"\")!\n        let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil)\n        descriptor.loggingBehavior = .production\n        var configuration = InstanceConfiguration(with: descriptor, isDebug: false)\n        XCTAssertTrue(configuration.loggingEnabled)\n        configuration = InstanceConfiguration(with: descriptor, isDebug: true)\n        XCTAssertTrue(configuration.loggingEnabled)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>BNDL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/JSExportTests.swift",
    "content": "import XCTest\n\n@testable import Capacitor\n\nclass JSExportTests: XCTestCase {\n\n    override func setUpWithError() throws {\n        // Put setup code here. This method is called before the invocation of each test method in the class.\n    }\n\n    override func tearDownWithError() throws {\n        // Put teardown code here. This method is called after the invocation of each test method in the class.\n    }\n    \n    func testBridgeBundle() throws {\n        let contentController = WKUserContentController()\n        do {\n            try Capacitor.JSExport.exportBridgeJS(userContentController: contentController)\n        }\n        catch {\n            XCTFail()\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/JSONSerializationWrapper.h",
    "content": "#import <Foundation/Foundation.h>\n\n@interface JSONSerializationWrapper : NSObject\n@property (nonatomic, copy) NSDictionary* _Nonnull dictionary;\n\n- (instancetype _Nullable)initWithDictionary:(NSDictionary* _Nonnull)options;\n- (NSDictionary * _Nullable)unwrappedResult;\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/JSONSerializationWrapper.m",
    "content": "\n#import \"JSONSerializationWrapper.h\"\n\n@implementation JSONSerializationWrapper\n\n- (instancetype)initWithDictionary:(NSDictionary *)dictionary {\n    self = [super init];\n    if (self != nil) {\n        _dictionary = dictionary;\n    }\n    return self;\n}\n\n- (NSDictionary *)unwrappedResult {\n    NSError* error = nil;\n    NSData* serializedData = [NSJSONSerialization dataWithJSONObject:[self dictionary] options:NSJSONWritingPrettyPrinted error:&error];\n    if (serializedData != nil) {\n        NSDictionary* result = [NSJSONSerialization JSONObjectWithData:serializedData options:0 error:&error];\n        return result;\n    }\n    return nil;\n}\n\n@end\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m",
    "content": "#import <XCTest/XCTest.h>\n#import <Capacitor/Capacitor.h>\n#import <Capacitor/CAPBridgedJSTypes.h>\n#import \"CapacitorTests-Swift.h\"\n\n// interface for this class\n@interface PluginCallAccessorTests : XCTestCase\n@property (strong, nonatomic) CAPPluginCall* call;\n@end\n\n@implementation PluginCallAccessorTests\n\n- (void)setUp {\n    [super setUp];\n    NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:632854800];\n    NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];\n    NSDictionary* options = @{@\"testString\":@\"foo\",\n                              @\"testDict\": @{@\"testSubkey\":@\"sub value\"},\n                              @\"testFloat\": @3.14159,\n                              @\"testDateObject\": date,\n                              @\"testDateString\": [formatter stringFromDate:date],\n                              @\"testBoolTrue\": @TRUE,\n                              @\"testBoolFalse\": @FALSE};\n    [self setCall:[[CAPPluginCall alloc] initWithCallbackId:@\"test\" options:options  success:NULL error:NULL]];\n}\n\n- (void)testStringAccessor {\n    NSString* value = [[self call] getString:@\"testString\" defaultValue:NULL];\n    XCTAssertEqual(value, @\"foo\");\n    \n    value = [[self call] getString:@\"badString\" defaultValue:NULL];\n    XCTAssertNil(value);\n    \n    value = [[self call] getString:@\"badString\" defaultValue:@\"default\"];\n    XCTAssertEqual(value, @\"default\");\n}\n\n- (void)testDateObjectAccessor {\n    NSDate* value = [[self call] getDate:@\"testDateObject\" defaultValue:NULL];\n    XCTAssertEqual([value timeIntervalSinceReferenceDate], 632854800);\n    \n    value = [[self call] getDate:@\"badString\" defaultValue:NULL];\n    XCTAssertNil(value);\n\n    NSDate *defaultDate = [NSDate date];\n    value = [[self call] getDate:@\"badString\" defaultValue:defaultDate];\n    XCTAssertEqual(value, defaultDate);\n}\n\n- (void)testDateStringAccessor {\n    NSDate* objectValue = [[self call] getDate:@\"testDateObject\" defaultValue:NULL];\n    NSDate* stringValue = [[self call] getDate:@\"testDateString\" defaultValue:NULL];\n    XCTAssertNotNil(objectValue);\n    XCTAssertNotNil(stringValue);\n    XCTAssertEqual(objectValue, stringValue);\n}\n\n- (void)testObjectAccessor {\n    NSDictionary* value = [[self call] getObject:@\"testDict\" defaultValue:NULL];\n    XCTAssertEqual([value objectForKey:@\"testSubkey\"], @\"sub value\");\n    \n    value = [[self call] getObject:@\"badString\" defaultValue:NULL];\n    XCTAssertNil(value);\n    \n    value = [[self call] getObject:@\"badString\" defaultValue:@{@\"defaultKey\":@\"default\"}];\n    XCTAssertEqual([value objectForKey:@\"defaultKey\"], @\"default\");\n}\n\n- (void)testNumberAccessor {\n    NSNumber* value = [[self call] getNumber:@\"testFloat\" defaultValue:NULL];\n    XCTAssertNotNil(value);\n    XCTAssertTrue([value isEqualToNumber:@3.14159]);\n    \n    value = [[self call] getNumber:@\"badString\" defaultValue:NULL];\n    XCTAssertNil(value);\n    \n    value = [[self call] getNumber:@\"badString\" defaultValue:@100];\n    XCTAssertEqual([value intValue], 100);\n    \n    value = [[self call] getNumber:@\"testBoolTrue\" defaultValue:NULL];\n    XCTAssertNotNil(value);\n    XCTAssertEqual([value boolValue], TRUE);\n}\n\n- (void)testBoolAccessor {\n    BOOL value = [[self call] getBool:@\"testBoolTrue\" defaultValue:false];\n    XCTAssertTrue(value);\n    \n    value = [[self call] getBool:@\"testBoolFalse\" defaultValue:true];\n    XCTAssertFalse(value);\n    \n    value = [[self call] getBool:@\"badString\" defaultValue:true];\n    XCTAssertTrue(value);\n    \n    value = [[self call] getBool:@\"badString\" defaultValue:false];\n    XCTAssertFalse(value);\n}\n@end\n"
  },
  {
    "path": "ios/Capacitor/CapacitorTests/RouterTests.swift",
    "content": "//\n//  RouterTests.swift\n//  CapacitorTests\n//\n//  Created by Steven Sherry on 3/29/22.\n//  Copyright © 2022 Drifty Co. All rights reserved.\n//\n\nimport XCTest\n@testable import Capacitor\n\nclass RouterTests: XCTestCase {\n    \n    func testRouterReturnsIndexWhenProvidedEmptyPath() {\n        checkRouter(path: \"\", expected: \"/index.html\")\n    }\n    \n    func testRouterReturnsIndexWhenProviedPathWithoutExtension() {\n        checkRouter(path: \"/a/valid/path/no/ext\", expected: \"/index.html\")\n    }\n    \n    func testRouterReturnsPathWhenProvidedValidPath() {\n        checkRouter(path: \"/a/valid/path.ext\", expected: \"/a/valid/path.ext\")\n    }\n    \n    func testRouterReturnsPathWhenProvidedValidPathWithExtensionAndSpaces() {\n        checkRouter(path: \"/a/valid/file path.ext\", expected: \"/a/valid/file path.ext\")\n    }\n    \n    func checkRouter(path: String, expected: String) {\n        XCTContext.runActivity(named: \"router creates route path correctly\") { _ in\n            var router = CapacitorRouter()\n            XCTAssertEqual(router.route(for: path), expected)\n            router.basePath = \"/A/Route\"\n            XCTAssertEqual(router.route(for: path), \"/A/Route\" + expected)\n        }\n    }\n    \n}\n"
  },
  {
    "path": "ios/Capacitor/CodableTests/CodableTests.swift",
    "content": "//\n//  JSValueDecoderTest.swift\n//  CapacitorTests\n//\n//  Created by Steven Sherry on 12/8/23.\n//  Copyright © 2023 Drifty Co. All rights reserved.\n//\n\nimport XCTest\nimport Capacitor\n\nprivate struct Pet: Codable, Equatable {\n    var name: String\n    var breed: String\n    var isVaccinated: Bool\n}\n\nprivate struct Person: Codable, Equatable {\n    var name: String\n    var age: UInt\n    var pet: Pet?\n    var family: [Person]?\n}\n\nprivate let rawPet: JSObject = [\n    \"name\": \"Penny\",\n    \"breed\": \"Chihuahua\",\n    \"isVaccinated\": true\n]\n\nprivate let rawPeople: JSArray = [\n    [ \"name\": \"Anakin\",\n      \"age\": 41 as NSNumber\n    ],\n    [ \"name\": \"Leia\",\n      \"age\": 20 as NSNumber\n    ]\n]\n\nprivate let rawPerson: JSObject = [\n    \"name\": \"Luke\",\n    \"age\": 20 as NSNumber,\n    \"pet\": rawPet,\n    \"family\": rawPeople\n]\n\nprivate let person = Person(\n    name: \"Luke\",\n    age: 20,\n    pet: .init(\n        name: \"Penny\",\n        breed: \"Chihuahua\",\n        isVaccinated: true\n    ),\n    family: [\n        Person(name: \"Anakin\", age: 41),\n        Person(name: \"Leia\", age: 20)\n    ]\n)\n\nfinal class JSValueDecoderTest: XCTestCase {\n    func testDecode_when_provided_a_valid_keyed_container_for_the_target_type__decoding_is_successful() throws {\n        let decoder = JSValueDecoder()\n        let decodedPerson = try decoder.decode(Person.self, from: rawPerson)\n        XCTAssertEqual(decodedPerson, person)\n    }\n\n    func testDecode__when_provided_a_valid_unkeyed_container_for_the_target_type__decoding_is_successful() throws {\n        let decoder = JSValueDecoder()\n        let decodedPeople = try decoder.decode([Person].self, from: rawPeople)\n        XCTAssertEqual(person.family, decodedPeople)\n    }\n\n    func testDecode__when_provided_a_single_value_for_the_target_type__decoding_is_successful() throws {\n        let decoder = JSValueDecoder()\n        let decodedNumber = try decoder.decode(UInt.self, from: 100 as NSNumber)\n        XCTAssertEqual(100, decodedNumber)\n    }\n\n    func testDecode__when_provided_an_invalid_keyed_container_for_the_target_type__decoding_fails() throws {\n        let decoder = JSValueDecoder()\n        var invalidRawPerson = rawPerson\n        invalidRawPerson[\"name\"] = nil\n        XCTAssertThrowsError(try decoder.decode(Person.self, from: invalidRawPerson))\n    }\n\n    func testDecode__when_provided_an_invalid_unkeyed_container_for_the_target_type__decoding_fails() throws {\n        let decoder = JSValueDecoder()\n        var invalidRawPeople = try XCTUnwrap(rawPeople as? [JSObject])\n        invalidRawPeople[0][\"name\"] = nil\n        XCTAssertThrowsError(try decoder.decode([Person].self, from: invalidRawPeople))\n    }\n\n    func testDecode__when_provided_an_invalid_single_value_type_for_the_input_value__decoding_fails() throws {\n        let decoder = JSValueDecoder()\n        XCTAssertThrowsError(try decoder.decode(UInt.self, from: -1 as NSNumber))\n    }\n\n    func testDecode__when_provided_a_valid_nested_array__decoding_is_successful() throws {\n        let decoder = JSValueDecoder()\n        let nestedPeople: JSArray = [rawPeople, rawPeople]\n        let decodedPeople = try decoder.decode([[Person]].self, from: nestedPeople)\n        XCTAssertEqual([person.family, person.family], decodedPeople)\n    }\n\n    func testDecode_when_attempting_to_decode_a_class__decoding_fails() throws {\n        class Pet: Decodable {\n            var name: String\n            var breed: String\n            var isVaccinated: String\n            init(name: String, breed: String, isVaccinated: String) {\n                self.name = name\n                self.breed = breed\n                self.isVaccinated = isVaccinated\n            }\n        }\n\n        let decoder = JSValueDecoder()\n        XCTAssertThrowsError(try decoder.decode(Pet.self, from: rawPet))\n    }\n\n    func testDecode__when_nsnull_explicitly_present_in_container__it_correctly_decodes_to_nil() throws {\n        let decoder = JSValueDecoder()\n        var rawPerson = rawPerson\n        rawPerson[\"pet\"] = NSNull()\n\n        let decodedPerson = try decoder.decode(Person.self, from: rawPerson)\n        XCTAssertNil(decodedPerson.pet)\n    }\n}\n\nfinal class JSValueEncoderTest: XCTestCase {\n    func testEncode__when_provided_with_an_instance_of_nonclass_codable_instance__encoding_succeeds() throws {\n        let encoder = JSValueEncoder()\n        let encodedValue = try encoder.encode(person)\n        let encodedObject = try XCTUnwrap(encodedValue as? JSObject)\n\n        let name = try XCTUnwrap(encodedObject[\"name\"] as? String)\n        XCTAssertEqual(person.name, name)\n        let age = try XCTUnwrap(encodedObject[\"age\"] as? NSNumber)\n        XCTAssertEqual(person.age as NSNumber, age)\n\n        let pet = try XCTUnwrap(encodedObject[\"pet\"] as? JSObject)\n        let petName = try XCTUnwrap(pet[\"name\"] as? String)\n        XCTAssertEqual(person.pet?.name, petName)\n        let petBreed = try XCTUnwrap(pet[\"breed\"] as? String)\n        XCTAssertEqual(person.pet?.breed, petBreed)\n        let petIsVaccinated = try XCTUnwrap(pet[\"isVaccinated\"] as? Bool)\n        XCTAssertEqual(person.pet?.isVaccinated, petIsVaccinated)\n\n        let family = try XCTUnwrap(encodedObject[\"family\"] as? [JSObject])\n        XCTAssertEqual(person.family?.count, family.count)\n        let aniName = try XCTUnwrap(family[0][\"name\"] as? String)\n        XCTAssertEqual(person.family?[0].name, aniName)\n        let aniAge = try XCTUnwrap(family[0][\"age\"] as? NSNumber)\n        XCTAssertEqual(person.family?[0].age as? NSNumber, aniAge)\n\n        let leiaName = try XCTUnwrap(family[1][\"name\"] as? String)\n        XCTAssertEqual(person.family?[1].name, leiaName)\n        let leiaAge = try XCTUnwrap(family[1][\"age\"] as? NSNumber)\n        XCTAssertEqual(person.family?[1].age as? NSNumber, leiaAge)\n    }\n\n    func testEncode__when_provided_an_instance_of_a_nested_unkeyed_container__encoding_succedds() throws {\n        let encoder = JSValueEncoder()\n        let encodedValue = try encoder.encode([person.family, person.family])\n        let encodedArray = try XCTUnwrap(encodedValue as? [[JSObject]])\n        XCTAssertEqual(encodedArray.count, 2)\n        XCTAssertEqual(encodedArray[0].count, 2)\n        XCTAssertEqual(encodedArray[1].count, 2)\n\n        let family = try XCTUnwrap(person.family)\n\n        XCTAssertEqual(family[0].name, encodedArray[0][0][\"name\"] as? String)\n        XCTAssertEqual(family[0].name, encodedArray[1][0][\"name\"] as? String)\n        XCTAssertEqual(family[0].age as NSNumber, encodedArray[0][0][\"age\"] as? NSNumber)\n        XCTAssertEqual(family[0].age as NSNumber, encodedArray[1][0][\"age\"] as? NSNumber)\n        XCTAssertEqual(family[1].name, encodedArray[0][1][\"name\"] as? String)\n        XCTAssertEqual(family[1].name, encodedArray[1][1][\"name\"] as? String)\n        XCTAssertEqual(family[1].age as NSNumber, encodedArray[0][1][\"age\"] as? NSNumber)\n        XCTAssertEqual(family[1].age as NSNumber, encodedArray[1][1][\"age\"] as? NSNumber)\n    }\n\n    func testEncode__when_nil_is_present_in_value__and_optional_encoding_is_set_to_explicit_nulls__it_is_encoded_as_nsnull() throws {\n        struct Test: Encodable {\n            var name: String?\n        }\n\n        let explicitEncoder = JSValueEncoder(optionalEncodingStrategy: .explicitNulls)\n        let encoded = try XCTUnwrap(try explicitEncoder.encode(Test()) as? JSObject)\n        XCTAssertTrue(encoded[\"name\"] is NSNull)\n        XCTAssertNotNil(encoded[\"name\"])\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CodableTests/DataCodableTests.swift",
    "content": "//\n//  DataCodableTests.swift\n//  CodableTests\n//\n//  Created by Steven Sherry on 9/6/24.\n//  Copyright © 2024 Drifty Co. All rights reserved.\n//\n\nimport XCTest\nimport Capacitor\n\nprivate struct Foo: Codable, Equatable {\n    var data: Data\n}\n\nprivate let jsonString = #\"{ \"key\": \"value\" }\"#\nprivate let jsonData = jsonString.data(using: .utf8)!\nprivate let jsonByteArray: [NSNumber] = [123, 32, 34, 107, 101, 121, 34, 58, 32, 34, 118, 97, 108, 117, 101, 34, 32, 125]\nprivate let jsonBase64 = \"eyAia2V5IjogInZhbHVlIiB9\"\n\nclass JSValueDecoderDataTests: XCTestCase {\n    func testDecode_data__default_root() throws {\n        let decoder = JSValueDecoder()\n        let result = try decoder.decode(Data.self, from: jsonByteArray)\n        XCTAssertEqual(result, jsonData)\n    }\n\n    func testDecode_data__default_array() throws {\n        let decoder = JSValueDecoder()\n        let result = try decoder.decode([Data].self, from: [jsonByteArray, jsonByteArray])\n        XCTAssertEqual(result, [jsonData, jsonData])\n    }\n\n    func testDecode_data__default_struct() throws {\n        let decoder = JSValueDecoder()\n        let result = try decoder.decode(Foo.self, from: [\"data\": jsonByteArray])\n        XCTAssertEqual(result, .init(data: jsonData))\n    }\n\n    func testDecode_data__base64_root() throws {\n        let decoder = JSValueDecoder(dataDecodingStrategy: .base64)\n        let result = try decoder.decode(Data.self, from: jsonBase64)\n        XCTAssertEqual(result, jsonData)\n    }\n\n    func testDecode_data__base64_array() throws {\n        let decoder = JSValueDecoder(dataDecodingStrategy: .base64)\n        let result = try decoder.decode([Data].self, from: [jsonBase64, jsonBase64])\n        XCTAssertEqual(result, [jsonData, jsonData])\n    }\n\n    func testDecode_data__base64_struct() throws {\n        let decoder = JSValueDecoder(dataDecodingStrategy: .base64)\n        let result = try decoder.decode(Foo.self, from: [\"data\": jsonBase64])\n        XCTAssertEqual(result, .init(data: jsonData))\n    }\n\n    let customStrategy = JSValueDecoder.DataDecodingStrategy.custom { decoder in\n        var container = try decoder.unkeyedContainer()\n        var byteArray: [UInt8] = []\n        while !container.isAtEnd {\n            byteArray.append(try container.decode(UInt8.self))\n        }\n        return Data(byteArray)\n    }\n\n    func testDecode_data__custom_root() throws {\n        let decoder = JSValueDecoder(dataDecodingStrategy: customStrategy)\n        let result = try decoder.decode(Data.self, from: jsonByteArray)\n        XCTAssertEqual(result, jsonData)\n    }\n\n    func testDecode_data__custom_array() throws {\n        let decoder = JSValueDecoder(dataDecodingStrategy: customStrategy)\n        let result = try decoder.decode([Data].self, from: [jsonByteArray, jsonByteArray])\n        XCTAssertEqual(result, [jsonData, jsonData])\n    }\n\n    func testDecode_data__custom_struct() throws {\n        let decoder = JSValueDecoder(dataDecodingStrategy: customStrategy)\n        let result = try decoder.decode(Foo.self, from: [\"data\": jsonByteArray])\n        XCTAssertEqual(result, .init(data: jsonData))\n    }\n}\n\nclass JSValueEncoderDataTests: XCTestCase {\n    func testEncode_data__default_root() throws {\n        let encoder = JSValueEncoder()\n        let rawResult = try encoder.encode(jsonData)\n        let result = try XCTUnwrap(rawResult as? [NSNumber])\n        XCTAssertEqual(result, jsonByteArray)\n    }\n\n    func testEncode_data__default_array() throws {\n        let encoder = JSValueEncoder()\n        let rawResult = try encoder.encode([jsonData, jsonData])\n        let result = try XCTUnwrap(rawResult as? [[NSNumber]])\n        XCTAssertEqual(result, [jsonByteArray, jsonByteArray])\n    }\n\n    func testEncode_data__default_struct() throws {\n        let encoder = JSValueEncoder()\n        let rawResult = try encoder.encode(Foo(data: jsonData))\n        let result = try XCTUnwrap(rawResult as? [String: [NSNumber]])\n        XCTAssertEqual(result, [\"data\": jsonByteArray])\n    }\n\n    func testEncode_data__base64_root() throws {\n        let encoder = JSValueEncoder(dataEncodingStrategy: .base64)\n        let rawResult = try encoder.encode(jsonData)\n        let result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, jsonBase64)\n    }\n\n    func testEncode_data__base64_array() throws {\n        let encoder = JSValueEncoder(dataEncodingStrategy: .base64)\n        let rawResult = try encoder.encode([jsonData, jsonData])\n        let result = try XCTUnwrap(rawResult as? [String])\n        XCTAssertEqual(result, [jsonBase64, jsonBase64])\n    }\n\n    func testEncode_data__base64_struct() throws {\n        let encoder = JSValueEncoder(dataEncodingStrategy: .base64)\n        let rawResult = try encoder.encode(Foo(data: jsonData))\n        let result = try XCTUnwrap(rawResult as? [String: String])\n        XCTAssertEqual(result, [\"data\": jsonBase64])\n    }\n\n    let customStrategy = JSValueEncoder.DataEncodingStrategy.custom { data, encoder in\n        let byteArray = data.map { $0 }\n        var unkeyedContainer = encoder.unkeyedContainer()\n        try unkeyedContainer.encode(contentsOf: byteArray)\n    }\n\n    func testEncode_data__custom_root() throws {\n        let encoder = JSValueEncoder(dataEncodingStrategy: customStrategy)\n        let rawResult = try encoder.encode(jsonData)\n        let result = try XCTUnwrap(rawResult as? [NSNumber])\n        XCTAssertEqual(result, jsonByteArray)\n    }\n\n    func testEncode_data__custom_array() throws {\n        let encoder = JSValueEncoder(dataEncodingStrategy: customStrategy)\n        let rawResult = try encoder.encode([jsonData, jsonData])\n        let result = try XCTUnwrap(rawResult as? [[NSNumber]])\n        XCTAssertEqual(result, [jsonByteArray, jsonByteArray])\n    }\n\n    func testEncode_data__custom_struct() throws {\n        let encoder = JSValueEncoder(dataEncodingStrategy: customStrategy)\n        let rawResult = try encoder.encode(Foo(data: jsonData))\n        let result = try XCTUnwrap(rawResult as? [String: [NSNumber]])\n        XCTAssertEqual(result, [\"data\": jsonByteArray])\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CodableTests/DateCodableTests.swift",
    "content": "//\n//  DateCodableTests.swift\n//  CodableTests\n//\n//  Created by Steven Sherry on 9/6/24.\n//  Copyright © 2024 Drifty Co. All rights reserved.\n//\n\nimport XCTest\nimport Capacitor\n\n// Fixture data that all refers to the same Date and Time\nprivate let timeIntervalSinceReferenceDate: TimeInterval = 747268580\nprivate let referenceDate = Date(timeIntervalSinceReferenceDate: timeIntervalSinceReferenceDate)\nprivate let secondsSince1970 = 1725575780 as Double\nprivate let millisecondsSince1970 = 1725575780000 as Double\nprivate let iso8601 = \"2024-09-05T22:36:20Z\"\n\nprivate let formatter: DateFormatter = {\n    let formatter = DateFormatter()\n    formatter.dateStyle = .medium\n    formatter.timeStyle = .long\n    formatter.timeZone = .init(abbreviation: \"CDT\")\n    formatter.locale = .init(identifier: \"en_US\")\n    return formatter\n}()\nprivate let formatted = \"Sep 5, 2024 at 5:36:20 PM CDT\"\n\nprivate struct Foo: Codable, Equatable {\n    var date: Date\n}\n\nfinal class JSValueDecoderDateTests: XCTestCase {\n    func testDecode_date__default() throws {\n        let reference = timeIntervalSinceReferenceDate\n        let decoder = JSValueDecoder()\n        let result = try decoder.decode(Date.self, from: reference)\n        XCTAssertEqual(result, referenceDate)\n    }\n\n    func testDecode_date__secondsSince1970() throws {\n        let decoder = JSValueDecoder(dateDecodingStrategy: .secondsSince1970)\n        let result = try decoder.decode(Date.self, from: secondsSince1970)\n        XCTAssertEqual(result, referenceDate)\n    }\n\n    func testDecode_date__millisecondsSince1970() throws {\n        let decoder = JSValueDecoder(dateDecodingStrategy: .millisecondsSince1970)\n        let result = try decoder.decode(Date.self, from: millisecondsSince1970)\n        XCTAssertEqual(result, referenceDate)\n    }\n\n    func testDecode_date__iso8601() throws {\n        let decoder = JSValueDecoder(dateDecodingStrategy: .iso8601)\n        let result = try decoder.decode(Date.self, from: iso8601)\n        XCTAssertEqual(result, referenceDate)\n    }\n\n    func testDecode_date__formatted() throws {\n        let decoder = JSValueDecoder(dateDecodingStrategy: .formatted(formatter))\n        let result = try decoder.decode(Date.self, from: formatted)\n        XCTAssertEqual(result, referenceDate)\n    }\n\n    func testDecode_date__custom() throws {\n        let strategy = JSValueDecoder.DateDecodingStrategy.custom { decoder in\n            let container = try decoder.singleValueContainer()\n            let referenceDateString = try container.decode(String.self)\n            guard let referenceDateSeconds = Double(referenceDateString) else {\n                throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: \"Unable to decode Double from String\"))\n            }\n            return Date(timeIntervalSinceReferenceDate: referenceDateSeconds)\n        }\n\n        let referenceString = \"\\(timeIntervalSinceReferenceDate)\"\n        let decoder = JSValueDecoder(dateDecodingStrategy: strategy)\n        let result = try decoder.decode(Date.self, from: referenceString)\n        XCTAssertEqual(result, referenceDate)\n    }\n\n    func testDecode_date__array() throws {\n        let dateArray = [iso8601, iso8601]\n        let decoder = JSValueDecoder(dateDecodingStrategy: .iso8601)\n        let result = try decoder.decode([Date].self, from: dateArray)\n        XCTAssertEqual(result, [referenceDate, referenceDate])\n    }\n\n    func testDecode_date__struct() throws {\n        let value = [\"date\": iso8601] as JSObject\n        let decoder = JSValueDecoder(dateDecodingStrategy: .iso8601)\n        let result = try decoder.decode(Foo.self, from: value)\n        XCTAssertEqual(result, Foo(date: referenceDate))\n    }\n}\n\nfinal class JSValueEncoderDateTests: XCTestCase {\n    func testEncode_date__default() throws {\n        let encoder = JSValueEncoder()\n        let rawResult = try encoder.encode(referenceDate)\n        let result = try XCTUnwrap(rawResult as? Double)\n        XCTAssertEqual(result, timeIntervalSinceReferenceDate)\n    }\n\n    func testEncode_date__secondsSince1970() throws {\n        let encoder = JSValueEncoder(dateEncodingStrategy: .secondsSince1970)\n        let rawResult = try encoder.encode(referenceDate)\n        let result = try XCTUnwrap(rawResult as? Double)\n        XCTAssertEqual(result, secondsSince1970)\n    }\n\n    func testEncode_date__millisecondsSince1970() throws {\n        let encoder = JSValueEncoder(dateEncodingStrategy: .millisecondsSince1970)\n        let rawResult = try encoder.encode(referenceDate)\n        let result = try XCTUnwrap(rawResult as? Double)\n        XCTAssertEqual(result, millisecondsSince1970)\n    }\n\n    func testEncode_date__iso8601() throws {\n        let encoder = JSValueEncoder(dateEncodingStrategy: .iso8601)\n        let rawResult = try encoder.encode(referenceDate)\n        let result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, iso8601)\n    }\n\n    func testEncode_date__formatted() throws {\n        let encoder = JSValueEncoder(dateEncodingStrategy: .formatted(formatter))\n        let rawResult = try encoder.encode(referenceDate)\n        let result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, formatted)\n    }\n\n    func testEncode_date__custom() throws {\n        let strategy = JSValueEncoder.DateEncodingStrategy.custom { date, encoder in\n            var container = encoder.singleValueContainer()\n            try container.encode(\"\\(date.timeIntervalSinceReferenceDate)\")\n        }\n\n        let encoder = JSValueEncoder(dateEncodingStrategy: strategy)\n        let rawResult = try encoder.encode(referenceDate)\n        let result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, \"\\(timeIntervalSinceReferenceDate)\")\n    }\n\n    func testEncode_date__array() throws {\n        let encoder = JSValueEncoder(dateEncodingStrategy: .iso8601)\n        let array = [referenceDate, referenceDate]\n        let rawResult = try encoder.encode(array)\n        let result = try XCTUnwrap(rawResult as? [String])\n        XCTAssertEqual(result, [iso8601, iso8601])\n    }\n\n    func testEncode_date__struct() throws {\n        let encoder = JSValueEncoder(dateEncodingStrategy: .iso8601)\n        let rawResult = try encoder.encode(Foo(date: referenceDate))\n        let result = try XCTUnwrap(rawResult as? [String: String])\n        XCTAssertEqual(result, [\"date\": iso8601])\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CodableTests/NestedCodableTests.swift",
    "content": "//\n//  CodableTests.swift\n//  CodableTests\n//\n//  Created by Steven Sherry on 12/10/23.\n//  Copyright © 2023 Drifty Co. All rights reserved.\n//\n\nimport XCTest\nimport Capacitor\n\nfinal class NestedCodableTests: XCTestCase {\n    private let nestedData: JSObject = [\n        \"id\": 1,\n        \"user\": [\n            \"userName\": \"Tester\",\n            \"realInfo\": [\n                \"fullName\": \"John Doe\"\n            ] as JSObject\n        ],\n        \"reviewCount\": [\n            [\"count\": 4] as JSObject\n        ]\n    ]\n\n    private let flatData = Flattened(\n        id: 1,\n        userName: \"Tester\",\n        fullName: \"John Doe\",\n        reviewCount: 4\n    )\n\n    func testDecode__when_decoding_a_decodable_value_with_a_custom_implementation_with_nested_values__it_successfully_decodes() throws {\n        let decoder = JSValueDecoder()\n        let decoded = try decoder.decode(Flattened.self, from: nestedData)\n        XCTAssertEqual(decoded, flatData)\n    }\n\n    func testEncode__when_encoding_an_encodable_value_with_a_custom_implementation_with_nested_values__it_successfully_encodes() throws {\n        let encoder = JSValueEncoder()\n        let encoded = try XCTUnwrap(try encoder.encode(flatData) as? JSObject)\n\n        print(encoded)\n        let encodedId = try XCTUnwrap(encoded[\"id\"] as? NSNumber)\n        let encodedUser = try XCTUnwrap(encoded[\"user\"] as? JSObject)\n        let encodedUserName = try XCTUnwrap(encodedUser[\"userName\"] as? String)\n        let encodedRealInfo = try XCTUnwrap(encodedUser[\"realInfo\"] as? JSObject)\n        let encodedFullName = try XCTUnwrap(encodedRealInfo[\"fullName\"] as? String)\n        let encodedReviewCount = try XCTUnwrap(encoded[\"reviewCount\"] as? JSArray)\n        let encodedCountEntry = try XCTUnwrap(encodedReviewCount[0] as? JSObject)\n        let encodedCount = try XCTUnwrap(encodedCountEntry[\"count\"] as? NSNumber)\n\n        XCTAssertEqual(encodedId, flatData.id as NSNumber)\n        XCTAssertEqual(encodedUserName, flatData.userName)\n        XCTAssertEqual(encodedFullName, flatData.fullName)\n        XCTAssertEqual(encodedCount, flatData.reviewCount as NSNumber)\n    }\n}\n\n// Example taken from https://stackoverflow.com/questions/44549310/how-to-decode-a-nested-json-struct-with-swift-decodable-protocol\nprivate struct Flattened: Equatable {\n    let id: Int\n    let userName: String\n    let fullName: String\n    let reviewCount: Int\n}\n\nextension Flattened: Decodable {\n    enum RootKeys: String, CodingKey {\n        case id, user, reviewCount\n    }\n\n    enum UserKeys: String, CodingKey {\n        case userName, realInfo\n    }\n\n    enum RealInfoKeys: String, CodingKey {\n        case fullName\n    }\n\n    enum ReviewCountKeys: String, CodingKey {\n        case count\n    }\n\n    init(from decoder: Decoder) throws {\n        // id\n        let container = try decoder.container(keyedBy: RootKeys.self)\n        id = try container.decode(Int.self, forKey: .id)\n        let userContainer = try container.nestedContainer(keyedBy: UserKeys.self, forKey: .user)\n        userName = try userContainer.decode(String.self, forKey: .userName)\n        let realInfoKeysContainer = try userContainer.nestedContainer(keyedBy: RealInfoKeys.self, forKey: .realInfo)\n        fullName = try realInfoKeysContainer.decode(String.self, forKey: .fullName)\n\n        var reviewUnkeyedContainer = try container.nestedUnkeyedContainer(forKey: .reviewCount)\n        var reviewCountArray = [Int]()\n        while !reviewUnkeyedContainer.isAtEnd {\n            let reviewCountContainer = try reviewUnkeyedContainer.nestedContainer(keyedBy: ReviewCountKeys.self)\n            reviewCountArray.append(try reviewCountContainer.decode(Int.self, forKey: .count))\n        }\n        guard let reviewCount = reviewCountArray.first else {\n            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath + [RootKeys.reviewCount], debugDescription: \"reviews_count cannot be empty\"))\n        }\n        self.reviewCount = reviewCount\n    }\n}\n\nextension Flattened: Encodable {\n    func encode(to encoder: Encoder) throws {\n        var topLevelContainer = encoder.container(keyedBy: RootKeys.self)\n        try topLevelContainer.encode(id, forKey: .id)\n        var userContainer = topLevelContainer.nestedContainer(keyedBy: UserKeys.self, forKey: .user)\n        try userContainer.encode(userName, forKey: .userName)\n        var infoContainer = userContainer.nestedContainer(keyedBy: RealInfoKeys.self, forKey: .realInfo)\n        try infoContainer.encode(fullName, forKey: .fullName)\n        var reviewsContainer = topLevelContainer.nestedUnkeyedContainer(forKey: .reviewCount)\n\n        try reviewsContainer.encode([\"count\": reviewCount])\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CodableTests/NonconformingFloatCodableTests.swift",
    "content": "//\n//  NonconformingFloatCodableTests.swift\n//  CodableTests\n//\n//  Created by Steven Sherry on 9/6/24.\n//  Copyright © 2024 Drifty Co. All rights reserved.\n//\n\nimport XCTest\nimport Capacitor\n\nprivate struct Foo: Codable, Equatable {\n    var number: Double\n}\n\nclass JSValueEncoderNonConformingFloatTests: XCTestCase {\n    func testEncode_float__default_root() throws {\n        let encoder = JSValueEncoder()\n        let rawResult = try encoder.encode(Double.infinity)\n        let result = try XCTUnwrap(rawResult as? Double)\n        XCTAssertEqual(result, .infinity)\n    }\n\n    func testEncode_float__default_array() throws {\n        let encoder = JSValueEncoder()\n        let rawResult = try encoder.encode([Double.infinity, -.infinity, .nan])\n        let result = try XCTUnwrap(rawResult as? [Double])\n        XCTAssertEqual(result[0...1], [.infinity, -.infinity])\n        XCTAssertTrue(result[2].isNaN)\n    }\n\n    func testEncode_float__default_struct() throws {\n        let encoder = JSValueEncoder()\n        let rawResult = try encoder.encode(Foo.init(number: .infinity))\n        let result = try XCTUnwrap(rawResult as? [String: Double])\n        XCTAssertEqual(result, [\"number\": .infinity])\n    }\n\n    func testEncode_float__convertToString_root() throws {\n        let encoder = JSValueEncoder(\n            nonConformingFloatEncodingStategy: .convertToString(\n                positiveInfinity: \"pos\",\n                negativeInfinity: \"neg\",\n                nan: \"nan\"\n            )\n        )\n\n        var rawResult = try encoder.encode(Double.infinity)\n        var result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, \"pos\")\n\n        rawResult = try encoder.encode(-Double.infinity)\n        result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, \"neg\")\n\n        rawResult = try encoder.encode(Double.nan)\n        result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, \"nan\")\n    }\n\n    func testEncode_float__convertToString_array() throws {\n        let encoder = JSValueEncoder(\n            nonConformingFloatEncodingStategy: .convertToString(\n                positiveInfinity: \"pos\",\n                negativeInfinity: \"neg\",\n                nan: \"nan\"\n            )\n        )\n\n        let rawResult = try encoder.encode([Double.infinity, -.infinity, .nan])\n        let result = try XCTUnwrap(rawResult as? [String])\n        XCTAssertEqual(result, [\"pos\", \"neg\", \"nan\"])\n    }\n\n    func testEncode_float__convertToString_struct() throws {\n        let encoder = JSValueEncoder(\n            nonConformingFloatEncodingStategy: .convertToString(\n                positiveInfinity: \"pos\",\n                negativeInfinity: \"neg\",\n                nan: \"nan\"\n            )\n        )\n\n        var rawResult = try encoder.encode(Foo(number: .infinity))\n        var result = try XCTUnwrap(rawResult as? [String: String])\n        XCTAssertEqual(result, [\"number\": \"pos\"])\n\n        rawResult = try encoder.encode(Foo(number: -.infinity))\n        result = try XCTUnwrap(rawResult as? [String: String])\n        XCTAssertEqual(result, [\"number\": \"neg\"])\n\n        rawResult = try encoder.encode(Foo(number: .nan))\n        result = try XCTUnwrap(rawResult as? [String: String])\n        XCTAssertEqual(result, [\"number\": \"nan\"])\n    }\n\n    func testEncode_float__throw_root() throws {\n        let encoder = JSValueEncoder(nonConformingFloatEncodingStategy: .throw)\n        XCTAssertThrowsError(try encoder.encode(Double.infinity))\n    }\n\n    func testEncode_float__throw_array() throws {\n        let encoder = JSValueEncoder(nonConformingFloatEncodingStategy: .throw)\n        XCTAssertThrowsError(try encoder.encode([Double.infinity, -.infinity, .nan]))\n    }\n\n    func testEncode_float__throw_struct() throws {\n        let encoder = JSValueEncoder(nonConformingFloatEncodingStategy: .throw)\n        XCTAssertThrowsError(try encoder.encode(Foo(number: .infinity)))\n    }\n}\n\nclass JSValueDecoderNonConformingFloatTests: XCTestCase {\n    func testDecode_float__default_root() throws {\n        let decoder = JSValueDecoder()\n        let result = try decoder.decode(Double.self, from: Double.infinity)\n        XCTAssertEqual(result, .infinity)\n    }\n\n    func testDecode_float__default_array() throws {\n        let decoder = JSValueDecoder()\n        let result = try decoder.decode([Double].self, from: [Double.infinity, Double.infinity])\n        XCTAssertEqual(result, [.infinity, .infinity])\n    }\n\n    func testDecode_float__default_struct() throws {\n        let decoder = JSValueDecoder()\n        let result = try decoder.decode(Foo.self, from: [\"number\": Double.infinity])\n        XCTAssertEqual(result, .init(number: .infinity))\n    }\n\n    func testDecode_float__throw_root() throws {\n        let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .throw)\n        XCTAssertThrowsError(try decoder.decode(Double.self, from: Double.infinity))\n    }\n\n    func testDecode_float__throw_array() throws {\n        let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .throw)\n        XCTAssertThrowsError(try decoder.decode([Double].self, from: [Double.infinity, Double.infinity]))\n    }\n\n    func testDecode_float__throw_struct() throws {\n        let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .throw)\n        XCTAssertThrowsError(try decoder.decode(Foo.self, from: [\"number\": Double.infinity]))\n    }\n\n    func testDecode_float__convertFromString_root() throws {\n        let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .convertFromString(positiveInfinity: \"pos\", negativeInfinity: \"neg\", nan: \"nan\"))\n        var result = try decoder.decode(Double.self, from: \"pos\")\n        XCTAssertEqual(result, .infinity)\n        result = try decoder.decode(Double.self, from: \"neg\")\n        XCTAssertEqual(result, -.infinity)\n        result = try decoder.decode(Double.self, from: \"nan\")\n        XCTAssertTrue(result.isNaN)\n    }\n\n    func testDecode_float__convertFromString_array() throws {\n        let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .convertFromString(positiveInfinity: \"pos\", negativeInfinity: \"neg\", nan: \"nan\"))\n        let result = try decoder.decode([Double].self, from: [\"pos\", \"neg\", \"nan\"])\n        XCTAssertEqual(result[0...1], [.infinity, -.infinity])\n        XCTAssertTrue(result[2].isNaN)\n    }\n\n    func testDecode_float__convertFromString_struct() throws {\n        let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .convertFromString(positiveInfinity: \"pos\", negativeInfinity: \"neg\", nan: \"nan\"))\n        var result = try decoder.decode(Foo.self, from: [\"number\": \"pos\"])\n        XCTAssertEqual(result, .init(number: .infinity))\n        result = try decoder.decode(Foo.self, from: [\"number\": \"neg\"])\n        XCTAssertEqual(result, .init(number: -.infinity))\n        result = try decoder.decode(Foo.self, from: [\"number\": \"nan\"])\n        XCTAssertTrue(result.number.isNaN)\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CodableTests/SuperCodableTests.swift",
    "content": "//\n//  SuperCodableTests.swift\n//  CodableTests\n//\n//  Created by Steven Sherry on 12/10/23.\n//  Copyright © 2023 Drifty Co. All rights reserved.\n//\n\nimport XCTest\nimport Capacitor\n\nfinal class SuperCodableTests: XCTestCase {\n    // MARK: Keyed Super Encoding/Decoding\n    func testEncode__when_given_a_value_that_encodes_to_a_keyed_superEncoder_without_specifying_a_key__it_encodes_the_super_container_with_the_string_key_super() throws {\n        let sut = JSValueEncoder()\n        let value = KeyedSubSuper(bool: true)\n        let encoded = try sut.encodeJSObject(value)\n\n        let bool = try XCTUnwrap(encoded[\"bool\"] as? Bool)\n        XCTAssertTrue(bool)\n        let superObject = try XCTUnwrap(encoded[\"super\"] as? JSObject)\n        let number = try XCTUnwrap(superObject[\"number\"] as? NSNumber)\n        XCTAssertEqual(0, number)\n        let string = try XCTUnwrap(superObject[\"string\"] as? String)\n        XCTAssertEqual(\"empty\", string)\n    }\n    func testEncode__when_given_a_value_that_encodes_to_a_keyed_superEncoder_with_a_specific_key__it_encodes_the_super_container_with_the_provided_key() throws {\n        let sut = JSValueEncoder()\n        let value = KeyedSubSuperKeyed(bool: false)\n        value.number = 5\n        value.string = \"encoding\"\n        let encoded = try sut.encodeJSObject(value)\n\n        let bool = try XCTUnwrap(encoded[\"bool\"] as? Bool)\n        XCTAssertFalse(bool)\n        let superObject = try XCTUnwrap(encoded[\"info\"] as? JSObject)\n        let number = try XCTUnwrap(superObject[\"number\"] as? NSNumber)\n        XCTAssertEqual(5, number)\n        let string = try XCTUnwrap(superObject[\"string\"] as? String)\n        XCTAssertEqual(\"encoding\", string)\n    }\n\n    func testEncode__when_given_a_value_that_encodes_its_superclass_without_a_superEncoder__it_encodes_the_entire_structure_flattened() throws {\n        let sut = JSValueEncoder()\n        let value = KeyedSubSuperFlat(bool: true)\n        value.number = 10\n        value.string = \"flattened\"\n        let encoded = try sut.encodeJSObject(value)\n\n        let bool = try XCTUnwrap(encoded[\"bool\"] as? Bool)\n        XCTAssertTrue(bool)\n        let number = try XCTUnwrap(encoded[\"number\"] as? NSNumber)\n        XCTAssertEqual(10, number)\n        let string = try XCTUnwrap(encoded[\"string\"] as? String)\n        XCTAssertEqual(\"flattened\", string)\n    }\n\n    func testDecode__when_given_a_value_that_decodes_its_superclass_without_specifying_a_key__it_will_attempt_to_decode_the_super_container_from_the_super_key() throws {\n        let sut = JSValueDecoder()\n        let value: JSObject = [\n            \"super\": [\n                \"number\": 5,\n                \"string\": \"super decoding\"\n            ],\n            \"bool\": true\n        ]\n\n        let decoded = try sut.decode(KeyedSubSuper.self, from: value)\n        XCTAssertTrue(decoded.bool)\n        XCTAssertEqual(decoded.number, 5)\n        XCTAssertEqual(decoded.string, \"super decoding\")\n    }\n\n    func testDecode__when_given_a_value_that_decodes_its_superclass_with_a_specific_key__it_will_attempt_to_decode_the_super_container_from_the_specified_key() throws {\n        let sut = JSValueDecoder()\n        let value: JSObject = [\n            \"info\": [\n                \"number\": 9,\n                \"string\": \"info decoding\"\n            ],\n            \"bool\": false\n        ]\n\n        let decoded = try sut.decode(KeyedSubSuperKeyed.self, from: value)\n        XCTAssertFalse(decoded.bool)\n        XCTAssertEqual(decoded.number, 9)\n        XCTAssertEqual(decoded.string, \"info decoding\")\n    }\n\n    func testDecode__when_given_a_value_that_decodes_its_superclass_without_a_superContainer__it_will_attempt_to_decode_a_flat_structure() throws {\n        let sut = JSValueDecoder()\n        let value: JSObject = [\n            \"number\": 20,\n            \"string\": \"flat decoding\",\n            \"bool\": true\n        ]\n\n        let decoded = try sut.decode(KeyedSubSuperFlat.self, from: value)\n        XCTAssertTrue(decoded.bool)\n        XCTAssertEqual(decoded.number, 20)\n        XCTAssertEqual(decoded.string, \"flat decoding\")\n    }\n\n    // MARK: Unkeyed Super Encoding/Decoding\n    func testEncode__when_given_a_value_that_encodes_its_superclass_with_a_superContainer__it_will_encode_the_super_container_as_a_nested_array() throws {\n        let sut = JSValueEncoder()\n        let value = UnkeyedSubSuper(bool: true)\n        value.number = -3\n        value.string = \"unkeyed encoding\"\n\n        let encoded = try XCTUnwrap(try sut.encode(value) as? JSArray)\n        XCTAssertEqual(encoded[0] as? Bool, true)\n        let nested = try XCTUnwrap(encoded[1] as? JSArray)\n        XCTAssertEqual(nested[0] as? NSNumber, -3)\n        XCTAssertEqual(nested[1] as? String, \"unkeyed encoding\")\n    }\n\n    func testDecode__when_given_a_type_that_decodes_its_superclass_with_a_superContainer__it_will_decode_the_superclass_as_a_nested_array() throws {\n        let sut = JSValueDecoder()\n        let value: JSArray = [\n            true, [4, \"unkeyed decoding\"]\n        ]\n\n        let decoded = try sut.decode(UnkeyedSubSuper.self, from: value)\n        XCTAssertTrue(decoded.bool)\n        XCTAssertEqual(decoded.number, 4)\n        XCTAssertEqual(decoded.string, \"unkeyed decoding\")\n    }\n}\n\nprivate class KeyedBase: Codable {\n    var number: Int\n    var string: String\n\n    init(number: Int, string: String) {\n        self.number = number\n        self.string = string\n    }\n}\n\nprivate class KeyedSubSuper: KeyedBase {\n    var bool: Bool\n\n    init(bool: Bool) {\n        self.bool = bool\n        super.init(number: 0, string: \"empty\")\n    }\n\n    required init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        bool = try container.decode(Bool.self, forKey: .bool)\n        try super.init(from: container.superDecoder())\n    }\n\n    enum CodingKeys: String, CodingKey {\n        case bool\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(bool, forKey: .bool)\n        try super.encode(to: container.superEncoder())\n    }\n}\n\nprivate class KeyedSubSuperKeyed: KeyedBase {\n    var bool: Bool\n\n    init(bool: Bool) {\n        self.bool = bool\n        super.init(number: 0, string: \"empty\")\n    }\n\n    enum CodingKeys: String, CodingKey {\n        case bool, info\n    }\n\n    required init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        bool = try container.decode(Bool.self, forKey: .bool)\n        try super.init(from: container.superDecoder(forKey: .info))\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(bool, forKey: .bool)\n        try super.encode(to: container.superEncoder(forKey: .info))\n    }\n}\n\nprivate class KeyedSubSuperFlat: KeyedBase {\n    var bool: Bool\n\n    init(bool: Bool) {\n        self.bool = bool\n        super.init(number: 0, string: \"empty\")\n    }\n\n    enum CodingKeys: String, CodingKey {\n        case bool\n    }\n\n    required init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        bool = try container.decode(Bool.self, forKey: .bool)\n        try super.init(from: decoder)\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(bool, forKey: .bool)\n        try super.encode(to: encoder)\n    }\n}\n\nprivate class UnkeyedBase: Codable {\n    var number: Int\n    var string: String\n\n    init(number: Int, string: String) {\n        self.number = number\n        self.string = string\n    }\n\n    required init(from decoder: Decoder) throws {\n        var container = try decoder.unkeyedContainer()\n        self.number = try container.decode(Int.self)\n        self.string = try container.decode(String.self)\n    }\n\n    func encode(to encoder: Encoder) throws {\n        var container = encoder.unkeyedContainer()\n        try container.encode(self.number)\n        try container.encode(self.string)\n    }\n}\n\nprivate class UnkeyedSubSuper: UnkeyedBase {\n    var bool: Bool\n\n    init(bool: Bool) {\n        self.bool = bool\n        super.init(number: 0, string: \"empty\")\n    }\n\n    required init(from decoder: Decoder) throws {\n        var container = try decoder.unkeyedContainer()\n        bool = try container.decode(Bool.self)\n        try super.init(from: container.superDecoder())\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.unkeyedContainer()\n        try container.encode(bool)\n        try super.encode(to: container.superEncoder())\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/CodableTests/URLCodableTests.swift",
    "content": "//\n//  URLCodableTests.swift\n//  CodableTests\n//\n//  Created by Steven Sherry on 9/6/24.\n//  Copyright © 2024 Drifty Co. All rights reserved.\n//\n\nimport XCTest\nimport Capacitor\n\nprivate let urlString = \"https://capacitorjs.com\"\nprivate let url = URL(string: urlString)!\n\nprivate struct Website: Codable, Equatable {\n    var url: URL\n}\n\nclass JSValueDecoderURLTests: XCTestCase {\n    let decoder = JSValueDecoder()\n\n    func testDecode_url__root() throws {\n        let result = try decoder.decode(URL.self, from: urlString)\n        XCTAssertEqual(result, url)\n    }\n\n    func testDecode_url__array() throws {\n        let result = try decoder.decode([URL].self, from: [urlString, urlString])\n        XCTAssertEqual(result, [url, url])\n    }\n\n    func testDecode_url__struct() throws {\n        let result = try decoder.decode(Website.self, from: [\"url\": urlString])\n        XCTAssertEqual(result, .init(url: url))\n    }\n\n    func testDecode_url__fails_when_invalid_url_string_is_provided() {\n        XCTAssertThrowsError(try decoder.decode(URL.self, from: \"🐞://🐞.com/🐞\"))\n    }\n}\n\nclass JSValueEncoderURLTests: XCTestCase {\n    let encoder = JSValueEncoder()\n\n    func testEncode_url__root() throws {\n        let rawResult = try encoder.encode(url)\n        let result = try XCTUnwrap(rawResult as? String)\n        XCTAssertEqual(result, urlString)\n    }\n\n    func testEncode_url__array() throws {\n        let rawResult = try encoder.encode([url, url])\n        let result = try XCTUnwrap(rawResult as? [String])\n        XCTAssertEqual(result, [urlString, urlString])\n    }\n\n    func testEncode_url__struct() throws {\n        let rawResult = try encoder.encode(Website(url: url))\n        let result = try XCTUnwrap(rawResult as? [String: String])\n        XCTAssertEqual(result, [\"url\": urlString])\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/AppDelegate.swift",
    "content": "import UIKit\n\n@main\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n        // Override point for customization after application launch.\n        return true\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"83.5x83.5\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<false/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Default Configuration</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>\n\t\t\t\t\t<key>UISceneStoryboardFile</key>\n\t\t\t\t\t<string>Main</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/ViewController.swift",
    "content": "import UIKit\n\nclass ViewController: UIViewController {\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        // Do any additional setup after loading the view.\n    }\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/configurations/bad.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"invalid string\",\n  \"loggingBehavior\": \"foo\",\n  \"ios\": {\n  \t\"allowsLinkPreview\": false,\n  \t\"scrollEnabled\": false,\n  \t\"contentInset\": \"what's an axis?\"\n  },\n  \"server\": {\n  \t\"iosScheme\": \"http\",\n  \t\"allowNavigation\": [\"capacitorjs.com\", \"ionic.io\", \"192.168.0.1\"],\n  \t\"hostname\": \"myhost\",\n  \t\"url\": \"not a real domain\"\n  },\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 0\n    }\n  },\n  \"cordova\": {}\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/configurations/flat.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"#ffffff\",\n  \"loggingBehavior\": \"debug\",\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 1\n    }\n  },\n  \"cordova\": {}\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/configurations/hidinglogs.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"#ffffff\",\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 1\n    }\n  },\n  \"cordova\": {}\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/configurations/hierarchy.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"#ffffff\",\n  \"loggingBehavior\": \"none\",\n  \"ios\": {\n    \"overrideUserAgent\": \"level 2 override\",\n  \t\"appendUserAgent\": \"level 2 append\",\n  \t\"backgroundColor\": \"#000000\",\n  \t\"allowsLinkPreview\": false,\n  \t\"scrollEnabled\": false,\n  \t\"contentInset\": \"scrollableAxes\"\n  },\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 0\n    }\n  },\n  \"cordova\": {}\n}\n"
  },
  {
    "path": "ios/Capacitor/TestsHostApp/configurations/nonjson.json",
    "content": "This is not even JSON."
  },
  {
    "path": "ios/Capacitor/TestsHostApp/configurations/server.json",
    "content": "{\n  \"appId\": \"com.capacitorjs.testshostapp\",\n  \"appName\": \"testshostapp\",\n  \"npmClient\": \"npm\",\n  \"webDir\": \"build\",\n  \"overrideUserAgent\": \"level 1 override\",\n  \"appendUserAgent\": \"level 1 append\",\n  \"backgroundColor\": \"#ffffff\",\n  \"loggingBehavior\": \"production\",\n  \"server\": {\n  \t\"iosScheme\": \"override\",\n  \t\"allowNavigation\": [\"*.capacitorjs.com\", \"ionic.io\", \"192.168.0.1\", \"subdomain.*.ionicframework.com\", \"*.*.example.com\"],\n  \t\"hostname\": \"myhost\",\n  \t\"url\": \"http://192.168.100.1:2057\"\n  },\n  \"plugins\": {\n    \"SplashScreen\": {\n      \"launchShowDuration\": 0\n    }\n  },\n  \"cordova\": {}\n}\n"
  },
  {
    "path": "ios/Capacitor.podspec",
    "content": "require 'json'\npackage = JSON.parse(File.read(File.join(__dir__, 'package.json')))\nprefix = if ENV['NATIVE_PUBLISH'] == 'true'\n           'ios/'\n         else\n           ''\n         end\n\nPod::Spec.new do |s|\n  s.name = 'Capacitor'\n  s.version = package['version']\n  s.summary = 'Capacitor for iOS'\n  s.license = 'MIT'\n  s.homepage = 'https://capacitorjs.com/'\n  s.ios.deployment_target = '15.0'\n  s.authors = { 'Ionic Team' => 'hi@ionicframework.com' }\n  s.source = { git: 'https://github.com/ionic-team/capacitor.git', tag: package['version'] }\n  s.source_files = \"#{prefix}Capacitor/Capacitor/**/*.{swift,h,m}\"\n  s.module_map = \"#{prefix}Capacitor/Capacitor/Capacitor.modulemap\"\n  s.resources = [\"#{prefix}Capacitor/Capacitor/assets/native-bridge.js\"]\n  s.resource_bundles = { 'Capacitor' => [\"#{prefix}Capacitor/Capacitor/PrivacyInfo.xcprivacy\"] }\n  s.dependency 'CapacitorCordova'\n  s.swift_version = '5.1'\nend\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.h",
    "content": "#import <UIKit/UIKit.h>\n\n//! Project version number for CapacitorCordova.\nFOUNDATION_EXPORT double CapacitorCordovaVersionNumber;\n\n//! Project version string for CapacitorCordova.\nFOUNDATION_EXPORT const unsigned char CapacitorCordovaVersionString[];\n\n#import <Cordova/AppDelegate.h>\n#import <Cordova/CDV.h>\n#import <Cordova/CDVAvailability.h>\n#import <Cordova/CDVAvailabilityDeprecated.h>\n#import <Cordova/CDVCommandDelegate.h>\n#import <Cordova/CDVCommandDelegateImpl.h>\n#import <Cordova/CDVConfigParser.h>\n#import <Cordova/CDVInvokedUrlCommand.h>\n#import <Cordova/CDVPlugin+Resources.h>\n#import <Cordova/CDVPlugin.h>\n#import <Cordova/CDVPluginManager.h>\n#import <Cordova/CDVPluginResult.h>\n#import <Cordova/CDVScreenOrientationDelegate.h>\n#import <Cordova/CDVURLProtocol.h>\n#import <Cordova/CDVViewController.h>\n#import <Cordova/CDVWebViewProcessPoolFactory.h>\n#import <Cordova/NSDictionary+CordovaPreferences.h>\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap",
    "content": "framework module Cordova {\n  umbrella header \"CapacitorCordova.h\"\n\n  export *\n  module * { export * }\n}\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.h",
    "content": "#import <Foundation/Foundation.h>\n#import \"CDVViewController.h\"\n\n@interface AppDelegate : NSObject\n\n@property (nonatomic, strong) CDVViewController* viewController;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.m",
    "content": "#import \"AppDelegate.h\"\n\n@implementation AppDelegate\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVAvailability.h\"\n#import \"CDVPlugin.h\"\n#import \"CDVPluginResult.h\"\n#import \"CDVCommandDelegate.h\"\n#import \"CDVInvokedUrlCommand.h\"\n#import \"CDVViewController.h\"\n#import \"CDVURLProtocol.h\"\n#import \"CDVScreenOrientationDelegate.h\"\n#import \"CDVWebViewProcessPoolFactory.h\"\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#define __CORDOVA_IOS__\n\n#define __CORDOVA_0_9_6 906\n#define __CORDOVA_1_0_0 10000\n#define __CORDOVA_1_1_0 10100\n#define __CORDOVA_1_2_0 10200\n#define __CORDOVA_1_3_0 10300\n#define __CORDOVA_1_4_0 10400\n#define __CORDOVA_1_4_1 10401\n#define __CORDOVA_1_5_0 10500\n#define __CORDOVA_1_6_0 10600\n#define __CORDOVA_1_6_1 10601\n#define __CORDOVA_1_7_0 10700\n#define __CORDOVA_1_8_0 10800\n#define __CORDOVA_1_8_1 10801\n#define __CORDOVA_1_9_0 10900\n#define __CORDOVA_2_0_0 20000\n#define __CORDOVA_2_1_0 20100\n#define __CORDOVA_2_2_0 20200\n#define __CORDOVA_2_3_0 20300\n#define __CORDOVA_2_4_0 20400\n#define __CORDOVA_2_5_0 20500\n#define __CORDOVA_2_6_0 20600\n#define __CORDOVA_2_7_0 20700\n#define __CORDOVA_2_8_0 20800\n#define __CORDOVA_2_9_0 20900\n#define __CORDOVA_3_0_0 30000\n#define __CORDOVA_3_1_0 30100\n#define __CORDOVA_3_2_0 30200\n#define __CORDOVA_3_3_0 30300\n#define __CORDOVA_3_4_0 30400\n#define __CORDOVA_3_4_1 30401\n#define __CORDOVA_3_5_0 30500\n#define __CORDOVA_3_6_0 30600\n#define __CORDOVA_3_7_0 30700\n#define __CORDOVA_3_8_0 30800\n#define __CORDOVA_3_9_0 30900\n#define __CORDOVA_3_9_1 30901\n#define __CORDOVA_3_9_2 30902\n#define __CORDOVA_4_0_0 40000\n#define __CORDOVA_4_0_1 40001\n#define __CORDOVA_4_1_0 40100\n#define __CORDOVA_4_1_1 40101\n#define __CORDOVA_4_2_0 40200\n#define __CORDOVA_4_2_1 40201\n#define __CORDOVA_4_3_0 40300\n#define __CORDOVA_4_3_1 40301\n#define __CORDOVA_4_4_0 40400\n#define __CORDOVA_4_5_0 40500\n#define __CORDOVA_4_5_1 40501\n#define __CORDOVA_4_5_2 40502\n#define __CORDOVA_4_5_4 40504\n/* coho:next-version,insert-before */\n#define __CORDOVA_NA 99999      /* not available */\n\n/*\n #if CORDOVA_VERSION_MIN_REQUIRED >= __CORDOVA_4_0_0\n    // do something when its at least 4.0.0\n #else\n    // do something else (non 4.0.0)\n #endif\n */\n#ifndef CORDOVA_VERSION_MIN_REQUIRED\n    /* coho:next-version-min-required,replace-after */\n    #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_4_5_4\n#endif\n\n/*\n Returns YES if it is at least version specified as NSString(X)\n Usage:\n     if (IsAtLeastiOSVersion(@\"5.1\")) {\n         // do something for iOS 5.1 or greater\n     }\n */\n#define IsAtLeastiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending)\n\n/* Return the string version of the decimal version */\n#define CDV_VERSION [NSString stringWithFormat:@\"%d.%d.%d\", \\\n    (CORDOVA_VERSION_MIN_REQUIRED / 10000),                 \\\n    (CORDOVA_VERSION_MIN_REQUIRED % 10000) / 100,           \\\n    (CORDOVA_VERSION_MIN_REQUIRED % 10000) % 100]\n\n// Enable this to log all exec() calls.\n#define CDV_ENABLE_EXEC_LOGGING 0\n#if CDV_ENABLE_EXEC_LOGGING\n    #define CDV_EXEC_LOG NSLog\n#else\n    #define CDV_EXEC_LOG(...) do { \\\n} while (NO)\n#endif\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailabilityDeprecated.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#define CDV_DEPRECATED(version, msg) __attribute__((deprecated(\"Deprecated in Cordova \" #version \". \" msg)))\n#define CDV_DEPRECATED_WITH_REPLACEMENT(version, msg, repl) __attribute__((deprecated(\"Deprecated in Cordova \" #version \". \" msg, repl)))\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVInvokedUrlCommand.h\"\n\n@class CDVPlugin;\n@class CDVPluginResult;\n\ntypedef NSURL* (^ UrlTransformerBlock)(NSURL*);\n\n@protocol CDVCommandDelegate <NSObject>\n\n@property (nonatomic, readonly) NSDictionary* settings;\n@property (nonatomic, copy) UrlTransformerBlock urlTransformer;\n\n- (NSString*)pathForResource:(NSString*)resourcepath;\n- (id)getCommandInstance:(NSString*)pluginName;\n\n// Sends a plugin result to the JS. This is thread-safe.\n- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId;\n// Evaluates the given JS. This is thread-safe.\n- (void)evalJs:(NSString*)js;\n// Can be used to evaluate JS right away instead of scheduling it on the run-loop.\n// This is required for dispatch resign and pause events, but should not be used\n// without reason. Without the run-loop delay, alerts used in JS callbacks may result\n// in dead-lock. This method must be called from the UI thread.\n- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop;\n// Run the javascript\n- (void)evalJsHelper2:(NSString*)js;\n// Runs the given block on a background thread using a shared thread-pool.\n- (void)runInBackground:(void (^)(void))block;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <UIKit/UIKit.h>\n#import \"CDVCommandDelegate.h\"\n#import <WebKit/WebKit.h>\n#import \"CDVPluginManager.h\"\n\n@class CDVViewController;\n@class CDVCommandQueue;\n\n@interface CDVCommandDelegateImpl : NSObject <CDVCommandDelegate>{\n    @private\n    __weak WKWebView* _webView;\n    __weak CDVPluginManager* _manager;\n    NSRegularExpression* _callbackIdPattern;\n    @protected\n    __weak CDVCommandQueue* _commandQueue;\n    BOOL _delayResponses;\n}\n- (id)initWithWebView:(WKWebView*)webView pluginManager:(CDVPluginManager *)manager;\n- (void)flushCommandQueueWithDelayedJs;\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVCommandDelegateImpl.h\"\n#import \"CDVPluginResult.h\"\n#import <WebKit/WebKit.h>\n\n@implementation CDVCommandDelegateImpl\n\n@synthesize urlTransformer;\n\n- (id)initWithWebView:(WKWebView*)webView pluginManager:(CDVPluginManager *)manager\n{\n    self = [super init];\n    if (self != nil) {\n        _webView = webView;\n        _manager = manager;\n        NSError* err = nil;\n        _callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@\"[^A-Za-z0-9._-]\" options:0 error:&err];\n        if (err != nil) {\n            // Couldn't initialize Regex\n            NSLog(@\"Error: Couldn't initialize regex\");\n            _callbackIdPattern = nil;\n        }\n    }\n    return self;\n}\n\n- (NSString*)pathForResource:(NSString*)resourcepath\n{\n    NSBundle* mainBundle = [NSBundle mainBundle];\n    NSMutableArray* directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@\"/\"]];\n    NSString* filename = [directoryParts lastObject];\n\n    [directoryParts removeLastObject];\n\n    NSString* directoryPartsJoined = [directoryParts componentsJoinedByString:@\"/\"];\n    NSString* baseFolder = @\"public\";\n    NSString* directoryStr = baseFolder;\n\n    if ([directoryPartsJoined length] > 0) {\n        directoryStr = [NSString stringWithFormat:@\"%@/%@\", baseFolder, [directoryParts componentsJoinedByString:@\"/\"]];\n    }\n\n    return [mainBundle pathForResource:filename ofType:@\"\" inDirectory:directoryStr];\n}\n\n- (void)flushCommandQueueWithDelayedJs\n{\n    _delayResponses = YES;\n    _delayResponses = NO;\n}\n\n- (void)evalJsHelper2:(NSString*)js\n{\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [self->_webView evaluateJavaScript:js completionHandler:^(id obj, NSError* error) {\n        // TODO: obj can be something other than string\n        if ([obj isKindOfClass:[NSString class]]) {\n            NSString* commandsJSON = (NSString*)obj;\n            if ([commandsJSON length] > 0) {\n                NSLog(@\"Exec: Retrieved new exec messages by chaining.\");\n            }\n        }\n        }];\n    });\n}\n\n- (BOOL)isValidCallbackId:(NSString*)callbackId\n{\n    if ((callbackId == nil) || (_callbackIdPattern == nil)) {\n        return NO;\n    }\n\n    // Disallow if too long or if any invalid characters were found.\n    if (([callbackId length] > 100) || [_callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {\n        return NO;\n    }\n    return YES;\n}\n\n- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId\n{\n    // This occurs when there is are no win/fail callbacks for the call.\n    if ([@\"INVALID\" isEqualToString:callbackId]) {\n        return;\n    }\n    // This occurs when the callback id is malformed.\n    if (![self isValidCallbackId:callbackId]) {\n        NSLog(@\"Invalid callback id received by sendPluginResult\");\n        return;\n    }\n    int status = [result.status intValue];\n    BOOL keepCallback = [result.keepCallback boolValue];\n    NSString* argumentsAsJSON = [result argumentsAsJSON];\n    BOOL debug = NO;\n    \n#ifdef DEBUG\n    debug = YES;\n#endif\n\n    NSString* js = [NSString stringWithFormat:@\"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d, %d)\", callbackId, status, argumentsAsJSON, keepCallback, debug];\n\n    [self evalJsHelper2:js];\n}\n\n- (void)evalJs:(NSString*)js\n{\n    [self evalJs:js scheduledOnRunLoop:YES];\n}\n\n- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop\n{\n    js = [NSString stringWithFormat:@\"try{cordova.require('cordova/exec').nativeEvalAndFetch(function(){%@})}catch(e){console.log('exception nativeEvalAndFetch : '+e);};\", js];\n     [self evalJsHelper2:js];\n}\n\n- (id)getCommandInstance:(NSString*)pluginName\n{\n    return [_manager getCommandInstance:pluginName];\n}\n\n- (void)runInBackground:(void (^)(void))block\n{\n    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);\n}\n\n- (NSDictionary*)settings\n{\n    return _manager.settings;\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n#import <Foundation/Foundation.h>\n\n@interface CDVConfigParser : NSObject <NSXMLParserDelegate>\n{\n    NSString* featureName;\n}\n\n@property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict;\n@property (nonatomic, readonly, strong) NSMutableDictionary* settings;\n@property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames;\n@property (nonatomic, readonly, strong) NSString* startPage;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVConfigParser.h\"\n\n@interface CDVConfigParser ()\n\n@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict;\n@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;\n@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;\n@property (nonatomic, readwrite, strong) NSString* startPage;\n\n@end\n\n@implementation CDVConfigParser\n\n@synthesize pluginsDict, settings, startPage, startupPluginNames;\n\n- (id)init\n{\n    self = [super init];\n    if (self != nil) {\n        self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30];\n        self.settings = [[NSMutableDictionary alloc] initWithCapacity:30];\n        self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8];\n        featureName = nil;\n    }\n    return self;\n}\n\n- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict\n{\n    if ([elementName isEqualToString:@\"preference\"]) {\n        settings[[attributeDict[@\"name\"] lowercaseString]] = attributeDict[@\"value\"];\n    } else if ([elementName isEqualToString:@\"feature\"]) { // store feature name to use with correct parameter set\n        featureName = [attributeDict[@\"name\"] lowercaseString];\n    } else if ((featureName != nil) && [elementName isEqualToString:@\"param\"]) {\n        NSString* paramName = [attributeDict[@\"name\"] lowercaseString];\n        id value = attributeDict[@\"value\"];\n        if ([paramName isEqualToString:@\"ios-package\"]) {\n            pluginsDict[featureName] = value;\n        }\n        BOOL paramIsOnload = ([paramName isEqualToString:@\"onload\"] && [@\"true\" isEqualToString : value]);\n        BOOL attribIsOnload = [@\"true\" isEqualToString :[attributeDict[@\"onload\"] lowercaseString]];\n        if (paramIsOnload || attribIsOnload) {\n            [self.startupPluginNames addObject:featureName];\n        }\n    } else if ([elementName isEqualToString:@\"content\"]) {\n        self.startPage = attributeDict[@\"src\"];\n    }\n}\n\n- (void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName\n{\n    if ([elementName isEqualToString:@\"feature\"]) { // no longer handling a feature so release\n        featureName = nil;\n    }\n}\n\n- (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError\n{\n    NSAssert(NO, @\"config.xml parse error line %ld col %ld\", (long)[parser lineNumber], (long)[parser columnNumber]);\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <Foundation/Foundation.h>\n\n@interface CDVInvokedUrlCommand : NSObject {\n    NSString* _callbackId;\n    NSString* _className;\n    NSString* _methodName;\n    NSArray* _arguments;\n}\n\n@property (nonatomic, readonly) NSArray* arguments;\n@property (nonatomic, readonly) NSString* callbackId;\n@property (nonatomic, readonly) NSString* className;\n@property (nonatomic, readonly) NSString* methodName;\n\n+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry;\n\n- (id)initWithArguments:(NSArray*)arguments\n             callbackId:(NSString*)callbackId\n              className:(NSString*)className\n             methodName:(NSString*)methodName;\n\n- (id)initFromJson:(NSArray*)jsonEntry;\n\n// Returns the argument at the given index.\n// If index >= the number of arguments, returns nil.\n// If the argument at the given index is NSNull, returns nil.\n- (id)argumentAtIndex:(NSUInteger)index;\n// Same as above, but returns defaultValue instead of nil.\n- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue;\n// Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue\n- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVInvokedUrlCommand.h\"\n\n\n@implementation CDVInvokedUrlCommand\n\n@synthesize arguments = _arguments;\n@synthesize callbackId = _callbackId;\n@synthesize className = _className;\n@synthesize methodName = _methodName;\n\n+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry\n{\n    return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry];\n}\n\n- (id)initFromJson:(NSArray*)jsonEntry\n{\n    id tmp = [jsonEntry objectAtIndex:0];\n    NSString* callbackId = tmp == [NSNull null] ? nil : tmp;\n    NSString* className = [jsonEntry objectAtIndex:1];\n    NSString* methodName = [jsonEntry objectAtIndex:2];\n    NSMutableArray* arguments = [jsonEntry objectAtIndex:3];\n\n    return [self initWithArguments:arguments\n                        callbackId:callbackId\n                         className:className\n                        methodName:methodName];\n}\n\n- (id)initWithArguments:(NSArray*)arguments\n             callbackId:(NSString*)callbackId\n              className:(NSString*)className\n             methodName:(NSString*)methodName\n{\n    self = [super init];\n    if (self != nil) {\n        _arguments = arguments;\n        _callbackId = callbackId;\n        _className = className;\n        _methodName = methodName;\n    }\n    [self massageArguments];\n    return self;\n}\n\n- (void)massageArguments\n{\n    NSMutableArray* newArgs = nil;\n\n    for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) {\n        id arg = [_arguments objectAtIndex:i];\n        if (![arg isKindOfClass:[NSDictionary class]]) {\n            continue;\n        }\n        NSDictionary* dict = arg;\n        NSString* type = [dict objectForKey:@\"CDVType\"];\n        if (!type || ![type isEqualToString:@\"ArrayBuffer\"]) {\n            continue;\n        }\n        NSString* data = [dict objectForKey:@\"data\"];\n        if (!data) {\n            continue;\n        }\n        if (newArgs == nil) {\n            newArgs = [NSMutableArray arrayWithArray:_arguments];\n            _arguments = newArgs;\n        }\n        [newArgs replaceObjectAtIndex:i withObject:[[NSData alloc] initWithBase64EncodedString:data options:0]];\n    }\n}\n\n- (id)argumentAtIndex:(NSUInteger)index\n{\n    return [self argumentAtIndex:index withDefault:nil];\n}\n\n- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue\n{\n    return [self argumentAtIndex:index withDefault:defaultValue andClass:nil];\n}\n\n- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass\n{\n    if (index >= [_arguments count]) {\n        return defaultValue;\n    }\n    id ret = [_arguments objectAtIndex:index];\n    if (ret == [NSNull null]) {\n        ret = defaultValue;\n    }\n    if ((aClass != nil) && ![ret isKindOfClass:aClass]) {\n        ret = defaultValue;\n    }\n    return ret;\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <UIKit/UIKit.h>\n#import \"CDVPlugin.h\"\n\n@interface CDVPlugin (CDVPluginResources)\n\n/*\n This will return the localized string for a key in a .bundle that is named the same as your class\n For example, if your plugin class was called Foo, and you have a Spanish localized strings file, it will\n try to load the desired key from Foo.bundle/es.lproj/Localizable.strings\n */\n- (NSString*)pluginLocalizedString:(NSString*)key;\n\n/*\n This will return the image for a name in a .bundle that is named the same as your class\n For example, if your plugin class was called Foo, and you have an image called \"bar\",\n it will try to load the image from Foo.bundle/bar.png (and appropriately named retina versions)\n */\n- (UIImage*)pluginImageResource:(NSString*)name;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVPlugin+Resources.h\"\n\n@implementation CDVPlugin (CDVPluginResources)\n\n- (NSString*)pluginLocalizedString:(NSString*)key\n{\n    NSBundle* bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:NSStringFromClass([self class]) ofType:@\"bundle\"]];\n\n    return [bundle localizedStringForKey:(key) value:nil table:nil];\n}\n\n- (UIImage*)pluginImageResource:(NSString*)name\n{\n    NSString* resourceIdentifier = [NSString stringWithFormat:@\"%@.bundle/%@\", NSStringFromClass([self class]), name];\n\n    return [UIImage imageNamed:resourceIdentifier];\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <Foundation/Foundation.h>\n#import <UIKit/UIKit.h>\n#import \"CDVPluginResult.h\"\n#import \"CDVCommandDelegate.h\"\n#import \"CDVAvailability.h\"\n#import <WebKit/WebKit.h>\n\nextern NSString* const CDVPageDidLoadNotification;\nextern NSString* const CDVPluginHandleOpenURLNotification;\nextern NSString* const CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification;\nextern NSString* const CDVPluginResetNotification;\nextern NSString* const CDVViewWillAppearNotification;\nextern NSString* const CDVViewDidAppearNotification;\nextern NSString* const CDVViewWillDisappearNotification;\nextern NSString* const CDVViewDidDisappearNotification;\nextern NSString* const CDVViewWillLayoutSubviewsNotification;\nextern NSString* const CDVViewDidLayoutSubviewsNotification;\nextern NSString* const CDVViewWillTransitionToSizeNotification;\n\n/*\n * The local and remote push notification functionality has been removed from the core in cordova-ios 4.x,\n * but these constants have unfortunately have not been removed, but will be removed in 5.x.\n * \n * To have the same functionality as 3.x, use a third-party plugin or the experimental\n * https://github.com/apache/cordova-plugins/tree/master/notification-rebroadcast\n */\n\n\n@interface CDVPlugin : NSObject {}\n\n- (instancetype)initWithWebViewEngine:(WKWebView *)theWebViewEngine;\n@property (nonatomic, weak) WKWebView* webView;\n@property (nonatomic, weak) WKWebView * webViewEngine;\n@property (nonatomic, strong) NSString * className;\n\n@property (nonatomic, weak) UIViewController* viewController;\n@property (nonatomic, weak) id <CDVCommandDelegate> commandDelegate;\n\n- (void)pluginInitialize;\n\n- (void)handleOpenURL:(NSNotification*)notification;\n- (void)onAppTerminate;\n- (void)onMemoryWarning;\n- (void)onReset;\n- (void)dispose;\n\n/*\n // see initWithWebView implementation\n - (void) onPause {}\n - (void) onResume {}\n - (void) onOrientationWillChange {}\n - (void) onOrientationDidChange {}\n */\n\n- (id)appDelegate;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVPlugin.h\"\n\nNSString* const CDVPageDidLoadNotification = @\"CDVPageDidLoadNotification\";\nNSString* const CDVPluginHandleOpenURLNotification = @\"CDVPluginHandleOpenURLNotification\";\nNSString* const CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification = @\"CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification\";\nNSString* const CDVPluginResetNotification = @\"CDVPluginResetNotification\";\nNSString* const CDVLocalNotification = @\"CDVLocalNotification\";\nNSString* const CDVRemoteNotification = @\"CDVRemoteNotification\";\nNSString* const CDVRemoteNotificationError = @\"CDVRemoteNotificationError\";\nNSString* const CDVViewWillAppearNotification = @\"CDVViewWillAppearNotification\";\nNSString* const CDVViewDidAppearNotification = @\"CDVViewDidAppearNotification\";\nNSString* const CDVViewWillDisappearNotification = @\"CDVViewWillDisappearNotification\";\nNSString* const CDVViewDidDisappearNotification = @\"CDVViewDidDisappearNotification\";\nNSString* const CDVViewWillLayoutSubviewsNotification = @\"CDVViewWillLayoutSubviewsNotification\";\nNSString* const CDVViewDidLayoutSubviewsNotification = @\"CDVViewDidLayoutSubviewsNotification\";\nNSString* const CDVViewWillTransitionToSizeNotification = @\"CDVViewWillTransitionToSizeNotification\";\n\n@interface CDVPlugin ()\n\n@property (readwrite, assign) BOOL hasPendingOperation;\n\n@end\n\n@implementation CDVPlugin\n@synthesize webViewEngine, viewController, commandDelegate, hasPendingOperation, webView;\n\n// Do not override these methods. Use pluginInitialize instead.\n- (instancetype)initWithWebViewEngine:(WKWebView *)theWebViewEngine\n{\n    self = [super init];\n    if (self) {\n        self.webViewEngine = theWebViewEngine;\n    }\n    return self;\n}\n\n- (void)pluginInitialize\n{\n    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil];\n    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];\n    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil];\n    // You can listen to more app notifications, see:\n    // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4\n\n    // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler\n\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];\n\n    // Added in 2.5.0\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView];\n    //Added in 4.3.0\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillAppear:) name:CDVViewWillAppearNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidAppear:) name:CDVViewDidAppearNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillDisappear:) name:CDVViewWillDisappearNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidDisappear:) name:CDVViewDidDisappearNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillLayoutSubviews:) name:CDVViewWillLayoutSubviewsNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidLayoutSubviews:) name:CDVViewDidLayoutSubviewsNotification object:nil];\n    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillTransitionToSize:) name:CDVViewWillTransitionToSizeNotification object:nil];\n}\n\n- (void)dispose\n{\n    viewController = nil;\n    commandDelegate = nil;\n}\n\n/*\n// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts\n- (void) onPause {}\n- (void) onResume {}\n- (void) onOrientationWillChange {}\n- (void) onOrientationDidChange {}\n*/\n\n/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */\n- (void)handleOpenURL:(NSNotification*)notification\n{\n    // override to handle urls sent to your app\n    // register your url schemes in your App-Info.plist\n\n    NSURL* url = [notification object];\n\n    if ([url isKindOfClass:[NSURL class]]) {\n        /* Do your thing! */\n    }\n}\n\n/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */\n- (void)onAppTerminate\n{\n    // override this if you need to do any cleanup on app exit\n}\n\n- (void)onMemoryWarning\n{\n    // override to remove caches, etc\n}\n\n- (void)onReset\n{\n    // Override to cancel any long-running requests when the WebView navigates or refreshes.\n}\n\n- (void)dealloc\n{\n    [[NSNotificationCenter defaultCenter] removeObserver:self];   // this will remove all notifications unless added using addObserverForName:object:queue:usingBlock:\n}\n\n- (id)appDelegate\n{\n    return [[UIApplication sharedApplication] delegate];\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.h",
    "content": "//\n//  CDVPluginManager.h\n//  CapacitorCordova\n//\n//  Created by Julio Cesar Sanchez Hernandez on 26/2/18.\n//\n\n#import <Foundation/Foundation.h>\n#import \"CDVPlugin.h\"\n#import \"CDVConfigParser.h\"\n#import \"CDVCommandDelegate.h\"\n\n@interface CDVPluginManager : NSObject\n\n@property (nonatomic, strong) NSMutableDictionary * pluginsMap;\n@property (nonatomic, strong) NSMutableDictionary * pluginObjects;\n@property (nonatomic, strong) NSMutableDictionary * settings;\n@property (nonatomic, weak) UIViewController * viewController;\n@property (nonatomic, weak) WKWebView * webView;\n@property (nonatomic, strong) id <CDVCommandDelegate> commandDelegate;\n\n- (id)initWithParser:(CDVConfigParser*)parser viewController:(UIViewController*)viewController webView:(WKWebView *)webview;\n- (CDVPlugin *)getCommandInstance:(NSString*)pluginName;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.m",
    "content": "#import \"CDVPluginManager.h\"\n#import \"CDVPlugin.h\"\n#import \"CDVCommandDelegateImpl.h\"\n\n@implementation CDVPluginManager\n\n- (id)initWithParser:(CDVConfigParser*)parser viewController:(UIViewController*)viewController webView:(WKWebView *)webview\n{\n  self = [super init];\n  if (self != nil) {\n    _pluginsMap = parser.pluginsDict;\n    _settings = parser.settings;\n    _viewController = viewController;\n    _webView = webview;\n    _pluginObjects = [[NSMutableDictionary alloc] init];\n    _commandDelegate = [[CDVCommandDelegateImpl alloc] initWithWebView:_webView pluginManager:self];\n    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:)\n                                                 name:UIApplicationDidEnterBackgroundNotification object:nil];\n    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:)\n                                                 name:UIApplicationWillEnterForegroundNotification object:nil];\n  }\n  return self;\n}\n\n/**\n Returns an instance of a CordovaCommand object, based on its name.  If one exists already, it is returned.\n */\n- (CDVPlugin *)getCommandInstance:(NSString*)pluginName\n{\n  NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]];\n\n  if (className == nil) {\n    return nil;\n  }\n\n  id obj = [self.pluginObjects objectForKey:className];\n  if (!obj) {\n    obj = [[NSClassFromString(className)alloc] initWithWebViewEngine: self.webView];\n    if (!obj) {\n      NSString* fullClassName = [NSString stringWithFormat:@\"%@.%@\",\n                                 NSBundle.mainBundle.infoDictionary[@\"CFBundleExecutable\"],\n                                 className];\n      obj = [[NSClassFromString(fullClassName)alloc] initWithWebViewEngine: self.webView];\n    }\n\n    if (obj != nil) {\n      [self registerPlugin:obj withClassName:className];\n    } else {\n      NSLog(@\"CDVPlugin class %@ (pluginName: %@) does not exist.\", className, pluginName);\n    }\n  }\n  [obj setClassName:className];\n\n  return obj;\n}\n\n\n- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className\n{\n  [self.pluginObjects setObject:plugin forKey:className];\n  plugin.viewController = self.viewController;\n  plugin.webView = self.webView;\n  plugin.commandDelegate = self.commandDelegate;\n  [plugin pluginInitialize];\n}\n\n- (void)onAppDidEnterBackground:(NSNotification*)notification\n{\n  [self.commandDelegate evalJsHelper2:@\"window.Capacitor.triggerEvent('pause', 'document');\"];\n}\n\n- (void)onAppWillEnterForeground:(NSNotification*)notification\n{\n  [self.commandDelegate evalJsHelper2:@\"window.Capacitor.triggerEvent('resume', 'document');\"];\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <Foundation/Foundation.h>\n\ntypedef NS_ENUM(NSUInteger, CDVCommandStatus) {\n    CDVCommandStatus_NO_RESULT NS_SWIFT_NAME(noResult) = 0,\n    CDVCommandStatus_OK NS_SWIFT_NAME(ok),\n    CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION NS_SWIFT_NAME(classNotFoundException),\n    CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION NS_SWIFT_NAME(illegalAccessException),\n    CDVCommandStatus_INSTANTIATION_EXCEPTION NS_SWIFT_NAME(instantiationException),\n    CDVCommandStatus_MALFORMED_URL_EXCEPTION NS_SWIFT_NAME(malformedUrlException),\n    CDVCommandStatus_IO_EXCEPTION NS_SWIFT_NAME(ioException),\n    CDVCommandStatus_INVALID_ACTION NS_SWIFT_NAME(invalidAction),\n    CDVCommandStatus_JSON_EXCEPTION NS_SWIFT_NAME(jsonException),\n    CDVCommandStatus_ERROR NS_SWIFT_NAME(error)\n};\n\n// This exists to preserve compatibility with early Swift plugins, who are\n// using CDVCommandStatus as ObjC-style constants rather than as Swift enum\n// values.\n// This declares extern'ed constants (implemented in CDVPluginResult.m)\n#define SWIFT_ENUM_COMPAT_HACK(enumVal) extern const CDVCommandStatus SWIFT_##enumVal NS_SWIFT_NAME(enumVal)\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_NO_RESULT);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_OK);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INSTANTIATION_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_MALFORMED_URL_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_IO_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INVALID_ACTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_JSON_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ERROR);\n#undef SWIFT_ENUM_COMPAT_HACK\n\n@interface CDVPluginResult : NSObject {}\n\n@property (nonatomic, strong, readonly) NSNumber* status;\n@property (nonatomic, strong, readonly) id message;\n@property (nonatomic, strong)           NSNumber* keepCallback;\n// This property can be used to scope the lifetime of another object. For example,\n// Use it to store the associated NSData when `message` is created using initWithBytesNoCopy.\n@property (nonatomic, strong) id associatedObject;\n\n- (CDVPluginResult*)init;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages;\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode;\n\n+ (void)setVerbose:(BOOL)verbose;\n+ (BOOL)isVerbose;\n\n- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback;\n\n- (NSString*)argumentsAsJSON;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"CDVPluginResult.h\"\n\n// This exists to preserve compatibility with early Swift plugins, who are\n// using CDVCommandStatus as ObjC-style constants rather than as Swift enum\n// values.\n// These constants alias the enum values back to their previous names.\n#define SWIFT_ENUM_COMPAT_HACK(enumVal) const CDVCommandStatus SWIFT_##enumVal NS_SWIFT_NAME(enumVal) = enumVal\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_NO_RESULT);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_OK);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INSTANTIATION_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_MALFORMED_URL_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_IO_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INVALID_ACTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_JSON_EXCEPTION);\nSWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ERROR);\n#undef SWIFT_ENUM_COMPAT_HACK\n\n@interface CDVPluginResult ()\n\n- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage;\n\n@end\n\n@implementation CDVPluginResult\n@synthesize status, message, keepCallback, associatedObject;\n\nstatic NSArray* org_apache_cordova_CommandStatusMsgs;\n\nid messageFromArrayBuffer(NSData* data)\n{\n    return @{\n               @\"CDVType\" : @\"ArrayBuffer\",\n               @\"data\" :[data base64EncodedStringWithOptions:0]\n    };\n}\n\nid massageMessage(id message)\n{\n    if ([message isKindOfClass:[NSData class]]) {\n        return messageFromArrayBuffer(message);\n    }\n    return message;\n}\n\nid messageFromMultipart(NSArray* theMessages)\n{\n    NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages];\n\n    for (NSUInteger i = 0; i < messages.count; ++i) {\n        [messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])];\n    }\n\n    return @{\n               @\"CDVType\" : @\"MultiPart\",\n               @\"messages\" : messages\n    };\n}\n\n+ (void)initialize\n{\n    org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@\"No result\",\n        @\"OK\",\n        @\"Class not found\",\n        @\"Illegal access\",\n        @\"Instantiation error\",\n        @\"Malformed url\",\n        @\"IO error\",\n        @\"Invalid action\",\n        @\"JSON error\",\n        @\"Error\",\n        nil];\n}\n\n- (CDVPluginResult*)init\n{\n    return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil];\n}\n\n- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage\n{\n    self = [super init];\n    if (self) {\n        status = [NSNumber numberWithUnsignedLong:statusOrdinal];\n        message = theMessage;\n        keepCallback = [NSNumber numberWithBool:NO];\n    }\n    return self;\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:nil];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:theMessage];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:theMessage];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInteger:theMessage]];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithUnsignedInteger:theMessage]];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:theMessage];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages\n{\n    return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)];\n}\n\n+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode\n{\n    NSDictionary* errDict = @{@\"code\" :[NSNumber numberWithInt:errorCode]};\n\n    return [[self alloc] initWithStatus:statusOrdinal message:errDict];\n}\n\n- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback\n{\n    [self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]];\n}\n\n- (NSString*)argumentsAsJSON\n{\n    id arguments = (self.message == nil ? [NSNull null] : self.message);\n    NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments];\n    \n    NSString* argumentsJSON = [self JSONStringFromArray:argumentsWrappedInArray];\n    \n    argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)];\n    \n    return argumentsJSON;\n}\n\nstatic BOOL gIsVerbose = NO;\n+ (void)setVerbose:(BOOL)verbose\n{\n    gIsVerbose = verbose;\n}\n\n+ (BOOL)isVerbose\n{\n    return gIsVerbose;\n}\n\n- (NSString*)JSONStringFromArray:(NSArray *) array\n{\n    NSError* error = nil;\n    NSData* jsonData = [NSJSONSerialization dataWithJSONObject:array\n                                                       options:0\n                                                         error:&error];\n    \n    if (error != nil) {\n        NSLog(@\"NSArray JSONString error: %@\", [error localizedDescription]);\n        return nil;\n    } else {\n        return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];\n    }\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVScreenOrientationDelegate.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <Foundation/Foundation.h>\n\n@protocol CDVScreenOrientationDelegate <NSObject>\n\n#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000  \n- (NSUInteger)supportedInterfaceOrientations;  \n#else  \n- (UIInterfaceOrientationMask)supportedInterfaceOrientations;\n#endif\n\n- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;\n- (BOOL)shouldAutorotate;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <Foundation/Foundation.h>\n#import \"CDVAvailability.h\"\n\n@class CDVViewController;\n\n@interface CDVURLProtocol : NSURLProtocol {}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <MobileCoreServices/MobileCoreServices.h>\n#import \"CDVURLProtocol.h\"\n#import \"CDVViewController.h\"\n\n// Contains a set of NSNumbers of addresses of controllers. It doesn't store\n// the actual pointer to avoid retaining.\nstatic NSMutableSet* gRegisteredControllers = nil;\n\nNSString* const kCDVAssetsLibraryPrefixes = @\"assets-library://\";\n\n@implementation CDVURLProtocol\n\n\n+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest\n{\n    return NO;\n}\n\n+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request\n{\n    // NSLog(@\"%@ received %@\", self, NSStringFromSelector(_cmd));\n    return request;\n}\n\n- (void)startLoading\n{\n    return;\n}\n\n- (void)stopLoading\n{\n    // do any cleanup here\n}\n\n+ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB\n{\n    return NO;\n}\n\n- (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType\n{\n    if (mimeType == nil) {\n        mimeType = @\"text/plain\";\n    }\n\n    NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[[self request] URL] statusCode:statusCode HTTPVersion:@\"HTTP/1.1\" headerFields:@{@\"Content-Type\" : mimeType}];\n\n    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];\n    if (data != nil) {\n        [[self client] URLProtocol:self didLoadData:data];\n    }\n    [[self client] URLProtocolDidFinishLoading:self];\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <UIKit/UIKit.h>\n\n@interface CDVViewController : UIViewController\n\n@property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects;\n@property (nonatomic, readonly, strong) NSMutableDictionary* settings;\n@property (nonatomic, readonly, weak) UIView* webView;\n\n- (id) getCommandInstance:(NSString*)className;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n\n#import \"CDVViewController.h\"\n\n@interface CDVViewController () {\n  \n}\n\n\n@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;\n\n@end\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wincomplete-implementation\"\n@implementation CDVViewController\n\n@end\n#pragma clang diagnostic pop\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n \n http://www.apache.org/licenses/LICENSE-2.0\n \n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <WebKit/WebKit.h>\n#import <Cordova/CDVAvailabilityDeprecated.h>\n\n/**\n @Metadata {\n    @Available(Cordova, introduced: \"6.2.0\", deprecated: \"8.0.0\")\n }\n */\nCDV_DEPRECATED(8.0.0, \"WebKit WKProcessPool is deprecated in iOS\")\n@interface CDVWebViewProcessPoolFactory : NSObject\n@property (nonatomic, retain) WKProcessPool* sharedPool;\n\n+(instancetype) sharedFactory;\n-(WKProcessPool*) sharedProcessPool;\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n \n http://www.apache.org/licenses/LICENSE-2.0\n \n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n@import Foundation;\n@import WebKit;\n#import <Cordova/CDVWebViewProcessPoolFactory.h>\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n#pragma clang diagnostic ignored \"-Wdeprecated-implementations\"\n\nstatic CDVWebViewProcessPoolFactory *factory = nil;\n\n@implementation CDVWebViewProcessPoolFactory\n\n+ (instancetype)sharedFactory\n{\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        factory = [[CDVWebViewProcessPoolFactory alloc] init];\n    });\n    \n    return factory;\n}\n\n- (instancetype)init\n{\n    if (self = [super init]) {\n        _sharedPool = [[WKProcessPool alloc] init];\n    }\n    return self;\n}\n\n- (WKProcessPool*) sharedProcessPool {\n    return _sharedPool;\n}\n@end\n\n#pragma clang diagnostic pop\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.h",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import <Foundation/Foundation.h>\n#import <UIKit/UIKit.h>\n\n@interface NSDictionary (CordovaPreferences)\n\n- (id)cordovaSettingForKey:(NSString*)key;\n- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue;\n- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue;\n\n@end\n\n@interface NSMutableDictionary (CordovaPreferences)\n\n- (void)setCordovaSetting:(id)value forKey:(NSString*)key;\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.m",
    "content": "/*\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied.  See the License for the\n specific language governing permissions and limitations\n under the License.\n */\n\n#import \"NSDictionary+CordovaPreferences.h\"\n#import <Foundation/Foundation.h>\n\n@implementation NSDictionary (CordovaPreferences)\n\n- (id)cordovaSettingForKey:(NSString*)key\n{\n    return [self objectForKey:[key lowercaseString]];\n}\n\n- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue\n{\n    BOOL value = defaultValue;\n    id prefObj = [self cordovaSettingForKey:key];\n\n    if (prefObj != nil) {\n        value = [(NSNumber*)prefObj boolValue];\n    }\n\n    return value;\n}\n\n- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue\n{\n    CGFloat value = defaultValue;\n    id prefObj = [self cordovaSettingForKey:key];\n\n    if (prefObj != nil) {\n        value = [prefObj floatValue];\n    }\n\n    return value;\n}\n\n@end\n\n@implementation NSMutableDictionary (CordovaPreferences)\n\n- (void)setCordovaSetting:(id)value forKey:(NSString*)key\n{\n    [self setObject:value forKey:[key lowercaseString]];\n}\n\n@end\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>Cordova</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>NSPrincipalClass</key>\n\t<string></string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>NSPrivacyCollectedDataTypes</key>\n\t<array/>\n\t<key>NSPrivacyAccessedAPITypes</key>\n\t<array/>\n\t<key>NSPrivacyTrackingDomains</key>\n\t<array/>\n\t<key>NSPrivacyTracking</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t0B61A7E52B114AA00035F2DB /* CDVWebViewProcessPoolFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B61A7E32B114A9F0035F2DB /* CDVWebViewProcessPoolFactory.m */; };\n\t\t0B61A7E62B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B61A7E42B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t1DBF5C182E9E908A00FAC24F /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DBF5C172E9E908A00FAC24F /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F4F657C2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F4F657A2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m */; };\n\t\t2F4F657D2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F4F657B2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C86E11FE94845004B09C7 /* CapacitorCordova.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C86DF1FE94845004B09C7 /* CapacitorCordova.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C871D1FE98418004B09C7 /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87121FE98417004B09C7 /* CDVPluginResult.m */; };\n\t\t2F5C871E1FE98418004B09C7 /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87131FE98417004B09C7 /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C871F1FE98418004B09C7 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87141FE98417004B09C7 /* CDVCommandDelegateImpl.m */; };\n\t\t2F5C87201FE98418004B09C7 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87151FE98417004B09C7 /* CDVInvokedUrlCommand.m */; };\n\t\t2F5C87211FE98418004B09C7 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87161FE98417004B09C7 /* CDVPlugin.m */; };\n\t\t2F5C87221FE98418004B09C7 /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87171FE98417004B09C7 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C87231FE98418004B09C7 /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87181FE98417004B09C7 /* CDVCommandDelegateImpl.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C87241FE98418004B09C7 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87191FE98418004B09C7 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C87251FE98418004B09C7 /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C871A1FE98418004B09C7 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C87261FE98418004B09C7 /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C871B1FE98418004B09C7 /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F5C87271FE98418004B09C7 /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C871C1FE98418004B09C7 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F856BFF203DEB320047344A /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F856BFD203DEB320047344A /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F856C00203DEB320047344A /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F856BFE203DEB320047344A /* CDVViewController.m */; };\n\t\t2F8AC283217F3A20008C2C33 /* CDVURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F8AC281217F3A20008C2C33 /* CDVURLProtocol.m */; };\n\t\t2F8AC284217F3A20008C2C33 /* CDVURLProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F8AC282217F3A20008C2C33 /* CDVURLProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2F92AB5224D9ABA000954A4A /* CDVPlugin+Resources.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F92AB5024D9ABA000954A4A /* CDVPlugin+Resources.m */; };\n\t\t2F92AB5324D9ABA000954A4A /* CDVPlugin+Resources.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F92AB5124D9ABA000954A4A /* CDVPlugin+Resources.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2FAD9772203C77B9000D30F8 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FAD9770203C77B8000D30F8 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2FAD9773203C77B9000D30F8 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FAD9771203C77B9000D30F8 /* CDVConfigParser.m */; };\n\t\t2FE19E2A20473160002A4E89 /* AppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FE19E2820473160002A4E89 /* AppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t2FE19E2B20473160002A4E89 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FE19E2920473160002A4E89 /* AppDelegate.m */; };\n\t\t62959B66252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B65252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t62959B6A252524D700A3D7F1 /* CDVPluginManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B68252524D700A3D7F1 /* CDVPluginManager.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t62959B6B252524D700A3D7F1 /* CDVPluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959B69252524D700A3D7F1 /* CDVPluginManager.m */; };\n\t\tA76739742B98CC7800795F7B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A76739732B98CC7800795F7B /* PrivacyInfo.xcprivacy */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t0B61A7E32B114A9F0035F2DB /* CDVWebViewProcessPoolFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewProcessPoolFactory.m; sourceTree = \"<group>\"; };\n\t\t0B61A7E42B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewProcessPoolFactory.h; sourceTree = \"<group>\"; };\n\t\t1DBF5C172E9E908A00FAC24F /* CDVAvailabilityDeprecated.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDVAvailabilityDeprecated.h; sourceTree = \"<group>\"; };\n\t\t2F4F657A2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"NSDictionary+CordovaPreferences.m\"; sourceTree = \"<group>\"; };\n\t\t2F4F657B2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"NSDictionary+CordovaPreferences.h\"; sourceTree = \"<group>\"; };\n\t\t2F5C86DC1FE94845004B09C7 /* Cordova.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cordova.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t2F5C86DF1FE94845004B09C7 /* CapacitorCordova.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CapacitorCordova.h; sourceTree = \"<group>\"; };\n\t\t2F5C86E01FE94845004B09C7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t2F5C87121FE98417004B09C7 /* CDVPluginResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPluginResult.m; sourceTree = \"<group>\"; };\n\t\t2F5C87131FE98417004B09C7 /* CDV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDV.h; sourceTree = \"<group>\"; };\n\t\t2F5C87141FE98417004B09C7 /* CDVCommandDelegateImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandDelegateImpl.m; sourceTree = \"<group>\"; };\n\t\t2F5C87151FE98417004B09C7 /* CDVInvokedUrlCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVInvokedUrlCommand.m; sourceTree = \"<group>\"; };\n\t\t2F5C87161FE98417004B09C7 /* CDVPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPlugin.m; sourceTree = \"<group>\"; };\n\t\t2F5C87171FE98417004B09C7 /* CDVCommandDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegate.h; sourceTree = \"<group>\"; };\n\t\t2F5C87181FE98417004B09C7 /* CDVCommandDelegateImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegateImpl.h; sourceTree = \"<group>\"; };\n\t\t2F5C87191FE98418004B09C7 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVInvokedUrlCommand.h; sourceTree = \"<group>\"; };\n\t\t2F5C871A1FE98418004B09C7 /* CDVAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailability.h; sourceTree = \"<group>\"; };\n\t\t2F5C871B1FE98418004B09C7 /* CDVPluginResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPluginResult.h; sourceTree = \"<group>\"; };\n\t\t2F5C871C1FE98418004B09C7 /* CDVPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPlugin.h; sourceTree = \"<group>\"; };\n\t\t2F856BFD203DEB320047344A /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVViewController.h; sourceTree = \"<group>\"; };\n\t\t2F856BFE203DEB320047344A /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVViewController.m; sourceTree = \"<group>\"; };\n\t\t2F8AC281217F3A20008C2C33 /* CDVURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVURLProtocol.m; sourceTree = \"<group>\"; };\n\t\t2F8AC282217F3A20008C2C33 /* CDVURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVURLProtocol.h; sourceTree = \"<group>\"; };\n\t\t2F92AB5024D9ABA000954A4A /* CDVPlugin+Resources.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"CDVPlugin+Resources.m\"; sourceTree = \"<group>\"; };\n\t\t2F92AB5124D9ABA000954A4A /* CDVPlugin+Resources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"CDVPlugin+Resources.h\"; sourceTree = \"<group>\"; };\n\t\t2FAD9770203C77B8000D30F8 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVConfigParser.h; sourceTree = \"<group>\"; };\n\t\t2FAD9771203C77B9000D30F8 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVConfigParser.m; sourceTree = \"<group>\"; };\n\t\t2FE19E2820473160002A4E89 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\t2FE19E2920473160002A4E89 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\t62959B5F252522CB00A3D7F1 /* CapacitorCordova.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = \"sourcecode.module-map\"; path = CapacitorCordova.modulemap; sourceTree = \"<group>\"; };\n\t\t62959B65252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVScreenOrientationDelegate.h; sourceTree = \"<group>\"; };\n\t\t62959B68252524D700A3D7F1 /* CDVPluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPluginManager.h; sourceTree = \"<group>\"; };\n\t\t62959B69252524D700A3D7F1 /* CDVPluginManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPluginManager.m; sourceTree = \"<group>\"; };\n\t\tA76739732B98CC7800795F7B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t2F5C86D81FE94845004B09C7 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t2F5C86D21FE94845004B09C7 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2F5C86DE1FE94845004B09C7 /* CapacitorCordova */,\n\t\t\t\t2F5C86DD1FE94845004B09C7 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2F5C86DD1FE94845004B09C7 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2F5C86DC1FE94845004B09C7 /* Cordova.framework */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2F5C86DE1FE94845004B09C7 /* CapacitorCordova */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2F5C86E71FE94859004B09C7 /* Classes */,\n\t\t\t\t2F5C86DF1FE94845004B09C7 /* CapacitorCordova.h */,\n\t\t\t\t62959B5F252522CB00A3D7F1 /* CapacitorCordova.modulemap */,\n\t\t\t\t2F5C86E01FE94845004B09C7 /* Info.plist */,\n\t\t\t\tA76739732B98CC7800795F7B /* PrivacyInfo.xcprivacy */,\n\t\t\t);\n\t\t\tpath = CapacitorCordova;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2F5C86E71FE94859004B09C7 /* Classes */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2F5C86E81FE94861004B09C7 /* Public */,\n\t\t\t);\n\t\t\tpath = Classes;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2F5C86E81FE94861004B09C7 /* Public */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2FE19E2820473160002A4E89 /* AppDelegate.h */,\n\t\t\t\t2FE19E2920473160002A4E89 /* AppDelegate.m */,\n\t\t\t\t2F5C87131FE98417004B09C7 /* CDV.h */,\n\t\t\t\t2F5C871A1FE98418004B09C7 /* CDVAvailability.h */,\n\t\t\t\t1DBF5C172E9E908A00FAC24F /* CDVAvailabilityDeprecated.h */,\n\t\t\t\t2F5C87171FE98417004B09C7 /* CDVCommandDelegate.h */,\n\t\t\t\t2F5C87141FE98417004B09C7 /* CDVCommandDelegateImpl.m */,\n\t\t\t\t2F5C87181FE98417004B09C7 /* CDVCommandDelegateImpl.h */,\n\t\t\t\t2FAD9770203C77B8000D30F8 /* CDVConfigParser.h */,\n\t\t\t\t2FAD9771203C77B9000D30F8 /* CDVConfigParser.m */,\n\t\t\t\t2F5C87191FE98418004B09C7 /* CDVInvokedUrlCommand.h */,\n\t\t\t\t2F5C87151FE98417004B09C7 /* CDVInvokedUrlCommand.m */,\n\t\t\t\t2F92AB5124D9ABA000954A4A /* CDVPlugin+Resources.h */,\n\t\t\t\t2F92AB5024D9ABA000954A4A /* CDVPlugin+Resources.m */,\n\t\t\t\t2F5C871C1FE98418004B09C7 /* CDVPlugin.h */,\n\t\t\t\t2F5C87161FE98417004B09C7 /* CDVPlugin.m */,\n\t\t\t\t62959B68252524D700A3D7F1 /* CDVPluginManager.h */,\n\t\t\t\t62959B69252524D700A3D7F1 /* CDVPluginManager.m */,\n\t\t\t\t2F5C871B1FE98418004B09C7 /* CDVPluginResult.h */,\n\t\t\t\t2F5C87121FE98417004B09C7 /* CDVPluginResult.m */,\n\t\t\t\t62959B65252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h */,\n\t\t\t\t2F8AC282217F3A20008C2C33 /* CDVURLProtocol.h */,\n\t\t\t\t2F8AC281217F3A20008C2C33 /* CDVURLProtocol.m */,\n\t\t\t\t2F4F657B2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h */,\n\t\t\t\t2F4F657A2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m */,\n\t\t\t\t2F856BFD203DEB320047344A /* CDVViewController.h */,\n\t\t\t\t2F856BFE203DEB320047344A /* CDVViewController.m */,\n\t\t\t\t0B61A7E42B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h */,\n\t\t\t\t0B61A7E32B114A9F0035F2DB /* CDVWebViewProcessPoolFactory.m */,\n\t\t\t);\n\t\t\tpath = Public;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\t2F5C86D91FE94845004B09C7 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2FE19E2A20473160002A4E89 /* AppDelegate.h in Headers */,\n\t\t\t\t2F5C871E1FE98418004B09C7 /* CDV.h in Headers */,\n\t\t\t\t2F5C87231FE98418004B09C7 /* CDVCommandDelegateImpl.h in Headers */,\n\t\t\t\t2F5C87251FE98418004B09C7 /* CDVAvailability.h in Headers */,\n\t\t\t\t2F5C87271FE98418004B09C7 /* CDVPlugin.h in Headers */,\n\t\t\t\t2F5C87261FE98418004B09C7 /* CDVPluginResult.h in Headers */,\n\t\t\t\t2F5C87221FE98418004B09C7 /* CDVCommandDelegate.h in Headers */,\n\t\t\t\t2F5C87241FE98418004B09C7 /* CDVInvokedUrlCommand.h in Headers */,\n\t\t\t\t2FAD9772203C77B9000D30F8 /* CDVConfigParser.h in Headers */,\n\t\t\t\t2F856BFF203DEB320047344A /* CDVViewController.h in Headers */,\n\t\t\t\t2F4F657D2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h in Headers */,\n\t\t\t\t2F8AC284217F3A20008C2C33 /* CDVURLProtocol.h in Headers */,\n\t\t\t\t2F92AB5324D9ABA000954A4A /* CDVPlugin+Resources.h in Headers */,\n\t\t\t\t62959B66252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h in Headers */,\n\t\t\t\t0B61A7E62B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h in Headers */,\n\t\t\t\t62959B6A252524D700A3D7F1 /* CDVPluginManager.h in Headers */,\n\t\t\t\t1DBF5C182E9E908A00FAC24F /* CDVAvailabilityDeprecated.h in Headers */,\n\t\t\t\t2F5C86E11FE94845004B09C7 /* CapacitorCordova.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\t2F5C86DB1FE94845004B09C7 /* Cordova */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 2F5C86E41FE94845004B09C7 /* Build configuration list for PBXNativeTarget \"Cordova\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t2F5C86D71FE94845004B09C7 /* Sources */,\n\t\t\t\t2F5C86D81FE94845004B09C7 /* Frameworks */,\n\t\t\t\t2F5C86D91FE94845004B09C7 /* Headers */,\n\t\t\t\t2F5C86DA1FE94845004B09C7 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Cordova;\n\t\t\tproductName = AvocadoCordova;\n\t\t\tproductReference = 2F5C86DC1FE94845004B09C7 /* Cordova.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t2F5C86D31FE94845004B09C7 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1220;\n\t\t\t\tORGANIZATIONNAME = jcesarmobile;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t2F5C86DB1FE94845004B09C7 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 2F5C86D61FE94845004B09C7 /* Build configuration list for PBXProject \"CapacitorCordova\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 2F5C86D21FE94845004B09C7;\n\t\t\tproductRefGroup = 2F5C86DD1FE94845004B09C7 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t2F5C86DB1FE94845004B09C7 /* Cordova */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t2F5C86DA1FE94845004B09C7 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tA76739742B98CC7800795F7B /* PrivacyInfo.xcprivacy in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t2F5C86D71FE94845004B09C7 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t62959B6B252524D700A3D7F1 /* CDVPluginManager.m in Sources */,\n\t\t\t\t2F5C871F1FE98418004B09C7 /* CDVCommandDelegateImpl.m in Sources */,\n\t\t\t\t2F5C871D1FE98418004B09C7 /* CDVPluginResult.m in Sources */,\n\t\t\t\t2F4F657C2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m in Sources */,\n\t\t\t\t0B61A7E52B114AA00035F2DB /* CDVWebViewProcessPoolFactory.m in Sources */,\n\t\t\t\t2F5C87211FE98418004B09C7 /* CDVPlugin.m in Sources */,\n\t\t\t\t2F92AB5224D9ABA000954A4A /* CDVPlugin+Resources.m in Sources */,\n\t\t\t\t2FAD9773203C77B9000D30F8 /* CDVConfigParser.m in Sources */,\n\t\t\t\t2F856C00203DEB320047344A /* CDVViewController.m in Sources */,\n\t\t\t\t2F5C87201FE98418004B09C7 /* CDVInvokedUrlCommand.m in Sources */,\n\t\t\t\t2FE19E2B20473160002A4E89 /* AppDelegate.m in Sources */,\n\t\t\t\t2F8AC283217F3A20008C2C33 /* CDVURLProtocol.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\t2F5C86E21FE94845004B09C7 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2F5C86E31FE94845004B09C7 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t2F5C86E51FE94845004B09C7 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tINFOPLIST_FILE = CapacitorCordova/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMODULEMAP_FILE = CapacitorCordova/CapacitorCordova.modulemap;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.ios.CapacitorCordova;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2F5C86E61FE94845004B09C7 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tINFOPLIST_FILE = CapacitorCordova/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMODULEMAP_FILE = CapacitorCordova/CapacitorCordova.modulemap;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.ios.CapacitorCordova;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t2F5C86D61FE94845004B09C7 /* Build configuration list for PBXProject \"CapacitorCordova\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2F5C86E21FE94845004B09C7 /* Debug */,\n\t\t\t\t2F5C86E31FE94845004B09C7 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t2F5C86E41FE94845004B09C7 /* Build configuration list for PBXNativeTarget \"Cordova\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2F5C86E51FE94845004B09C7 /* Debug */,\n\t\t\t\t2F5C86E61FE94845004B09C7 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 2F5C86D31FE94845004B09C7 /* Project object */;\n}\n"
  },
  {
    "path": "ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/CapacitorCordova.podspec",
    "content": "require 'json'\npackage = JSON.parse(File.read(File.join(__dir__, 'package.json')))\nprefix = if ENV['NATIVE_PUBLISH'] == 'true'\n           'ios/'\n         else\n           ''\n         end\n\nPod::Spec.new do |s|\n  s.name         = 'CapacitorCordova'\n  s.module_name  = 'Cordova'\n  s.version      = package['version']\n  s.summary      = 'Capacitor Cordova Compatibility Layer'\n  s.homepage     = 'https://capacitorjs.com'\n  s.license      = 'MIT'\n  s.authors      = { 'Ionic Team' => 'hi@ionicframework.com' }\n  s.source       = { git: 'https://github.com/ionic-team/capacitor', tag: s.version.to_s }\n  s.platform     = :ios, 15.0\n  s.source_files = \"#{prefix}CapacitorCordova/CapacitorCordova/**/*.{h,m}\"\n  s.public_header_files = \"#{prefix}CapacitorCordova/CapacitorCordova/Classes/Public/*.h\",\n                          \"#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.h\"\n  s.module_map = \"#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap\"\n  s.resource_bundles = { 'CapacitorCordova' => [\"#{prefix}CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy\"] }\n  s.requires_arc = true\n  s.framework    = 'WebKit'\nend\n"
  },
  {
    "path": "ios/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017-present Drifty Co.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "ios/package.json",
    "content": "{\n  \"name\": \"@capacitor/ios\",\n  \"version\": \"8.2.0\",\n  \"description\": \"Capacitor: Cross-platform apps with JavaScript and the web\",\n  \"homepage\": \"https://capacitorjs.com\",\n  \"author\": \"Ionic Team <hi@ionic.io> (https://ionic.io)\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/ionic-team/capacitor.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/ionic-team/capacitor/issues\"\n  },\n  \"files\": [\n    \"Capacitor/Capacitor/\",\n    \"CapacitorCordova/CapacitorCordova/\",\n    \"Capacitor.podspec\",\n    \"CapacitorCordova.podspec\",\n    \"scripts/pods_helpers.rb\"\n  ],\n  \"scripts\": {\n    \"verify\": \"npm run xc:build:Capacitor && npm run xc:build:CapacitorCordova\",\n    \"xc:build:Capacitor\": \"cd Capacitor && xcodebuild clean test -workspace Capacitor.xcworkspace -scheme Capacitor -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.0.1' && cd ..\",\n    \"xc:build:CapacitorCordova\": \"cd CapacitorCordova && xcodebuild && cd ..\"\n  },\n  \"peerDependencies\": {\n    \"@capacitor/core\": \"^8.2.0\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  }\n}\n"
  },
  {
    "path": "ios/scripts/pods_helpers.rb",
    "content": "def assertDeploymentTarget(installer)\n  installer.pods_project.targets.each do |target|\n    target.build_configurations.each do |config|\n      # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 15.0\n      deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f\n      should_upgrade = deployment_target < 15.0 && deployment_target != 0.0\n      if should_upgrade\n        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'\n      end\n    end\n  end\nend"
  },
  {
    "path": "ios-pods-template/.gitignore",
    "content": "App/build\nApp/Pods\nApp/output\nApp/App/public\nDerivedData\nxcuserdata\n\n# Cordova plugins for Capacitor\ncapacitor-cordova-ios-plugins\n\n# Generated Config files\nApp/App/capacitor.config.json\nApp/App/config.xml\n"
  },
  {
    "path": "ios-pods-template/App/App/AppDelegate.swift",
    "content": "import UIKit\nimport Capacitor\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    var window: UIWindow?\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n        // Override point for customization after application launch.\n        return true\n    }\n\n    func applicationWillResignActive(_ application: UIApplication) {\n        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n    }\n\n    func applicationDidEnterBackground(_ application: UIApplication) {\n        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n    }\n\n    func applicationWillEnterForeground(_ application: UIApplication) {\n        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n    }\n\n    func applicationDidBecomeActive(_ application: UIApplication) {\n        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n    }\n\n    func applicationWillTerminate(_ application: UIApplication) {\n        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n    }\n\n    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {\n        // Called when the app was launched with a url. Feel free to add additional processing here,\n        // but if you want the App API to support tracking app url opens, make sure to keep this call\n        return ApplicationDelegateProxy.shared.application(app, open: url, options: options)\n    }\n\n    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {\n        // Called when the app was launched with an activity, including Universal Links.\n        // Feel free to add additional processing here, but if you want the App API to support\n        // tracking app url opens, make sure to keep this call\n        return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)\n    }\n\n}\n"
  },
  {
    "path": "ios-pods-template/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-512@2x.png\",\n      \"idiom\" : \"universal\",\n      \"platform\" : \"ios\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios-pods-template/App/App/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "ios-pods-template/App/App/Assets.xcassets/Splash.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"splash-2732x2732-2.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"splash-2732x2732-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"splash-2732x2732.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "ios-pods-template/App/App/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"17132\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina4_7\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"17105\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <imageView key=\"view\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"Splash\" id=\"snD-IY-ifK\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                    </imageView>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"Splash\" width=\"1366\" height=\"1366\"/>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "ios-pods-template/App/App/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14111\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14088\"/>\n    </dependencies>\n    <scenes>\n        <!--Bridge View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"CAPBridgeViewController\" customModule=\"Capacitor\" sceneMemberID=\"viewController\"/>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios-pods-template/App/App/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>My App</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios-pods-template/App/App.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };\n\t\t50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };\n\t\t504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };\n\t\t504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };\n\t\t504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };\n\t\t504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };\n\t\t50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };\n\t\tA084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = \"<group>\"; };\n\t\t50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = \"<group>\"; };\n\t\t504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = \"<group>\"; };\n\t\tAF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tAF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-App.release.xcconfig\"; path = \"Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tFC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-App.debug.xcconfig\"; path = \"Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t504EC3011FED79650016851F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tA084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t504EC2FB1FED79650016851F = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t504EC3061FED79650016851F /* App */,\n\t\t\t\t504EC3051FED79650016851F /* Products */,\n\t\t\t\t7F8756D8B27F46E3366F6CEA /* Pods */,\n\t\t\t\t27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t504EC3051FED79650016851F /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t504EC3041FED79650016851F /* App.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t504EC3061FED79650016851F /* App */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t50379B222058CBB4000EE86E /* capacitor.config.json */,\n\t\t\t\t504EC3071FED79650016851F /* AppDelegate.swift */,\n\t\t\t\t504EC30B1FED79650016851F /* Main.storyboard */,\n\t\t\t\t504EC30E1FED79650016851F /* Assets.xcassets */,\n\t\t\t\t504EC3101FED79650016851F /* LaunchScreen.storyboard */,\n\t\t\t\t504EC3131FED79650016851F /* Info.plist */,\n\t\t\t\t2FAD9762203C412B000D30F8 /* config.xml */,\n\t\t\t\t50B271D01FEDC1A000F3C39B /* public */,\n\t\t\t);\n\t\t\tpath = App;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F8756D8B27F46E3366F6CEA /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tFC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,\n\t\t\t\tAF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,\n\t\t\t);\n\t\t\tname = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t504EC3031FED79650016851F /* App */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget \"App\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t504EC3001FED79650016851F /* Sources */,\n\t\t\t\t504EC3011FED79650016851F /* Frameworks */,\n\t\t\t\t504EC3021FED79650016851F /* Resources */,\n\t\t\t\t9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = App;\n\t\t\tproductName = App;\n\t\t\tproductReference = 504EC3041FED79650016851F /* App.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t504EC2FC1FED79650016851F /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 0920;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t504EC3031FED79650016851F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject \"App\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 504EC2FB1FED79650016851F;\n\t\t\tpackageReferences = (\n\t\t\t);\n\t\t\tproductRefGroup = 504EC3051FED79650016851F /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t504EC3031FED79650016851F /* App */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t504EC3021FED79650016851F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t50B271D11FEDC1A000F3C39B /* public in Resources */,\n\t\t\t\t504EC30F1FED79650016851F /* Assets.xcassets in Resources */,\n\t\t\t\t50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,\n\t\t\t\t504EC30D1FED79650016851F /* Main.storyboard in Resources */,\n\t\t\t\t2FAD9763203C412B000D30F8 /* config.xml in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t504EC3001FED79650016851F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t504EC3081FED79650016851F /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t504EC30B1FED79650016851F /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t504EC30C1FED79650016851F /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t504EC3111FED79650016851F /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t504EC3141FED79650016851F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t504EC3151FED79650016851F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Owholemodule\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t504EC3171FED79650016851F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tINFOPLIST_FILE = App/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tOTHER_SWIFT_FLAGS = \"$(inherited) \\\"-D\\\" \\\"COCOAPODS\\\" \\\"-DDEBUG\\\"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t504EC3181FED79650016851F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tINFOPLIST_FILE = App/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t504EC2FF1FED79650016851F /* Build configuration list for PBXProject \"App\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t504EC3141FED79650016851F /* Debug */,\n\t\t\t\t504EC3151FED79650016851F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget \"App\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t504EC3171FED79650016851F /* Debug */,\n\t\t\t\t504EC3181FED79650016851F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 504EC2FC1FED79650016851F /* Project object */;\n}\n"
  },
  {
    "path": "ios-pods-template/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios-pods-template/App/Podfile",
    "content": "require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'\n\nplatform :ios, '15.0'\nuse_frameworks!\n\n# workaround to avoid Xcode caching of Pods that requires\n# Product -> Clean Build Folder after new Cordova plugins installed\n# Requires CocoaPods 1.6 or newer\ninstall! 'cocoapods', :disable_input_output_paths => true\n\ndef capacitor_pods\n  pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'\n  pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'\nend\n\ntarget 'App' do\n  capacitor_pods\n  # Add your Pods here\nend\n\npost_install do |installer|\n  assertDeploymentTarget(installer)\nend\n"
  },
  {
    "path": "ios-spm-template/.gitignore",
    "content": "App/build\nApp/Pods\nApp/output\nApp/App/public\nDerivedData\nxcuserdata\n\n# Cordova plugins for Capacitor\ncapacitor-cordova-ios-plugins\n\n# Generated Config files\nApp/App/capacitor.config.json\nApp/App/config.xml\n"
  },
  {
    "path": "ios-spm-template/App/App/AppDelegate.swift",
    "content": "import UIKit\nimport Capacitor\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    var window: UIWindow?\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n        // Override point for customization after application launch.\n        return true\n    }\n\n    func applicationWillResignActive(_ application: UIApplication) {\n        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n    }\n\n    func applicationDidEnterBackground(_ application: UIApplication) {\n        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n    }\n\n    func applicationWillEnterForeground(_ application: UIApplication) {\n        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n    }\n\n    func applicationDidBecomeActive(_ application: UIApplication) {\n        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n    }\n\n    func applicationWillTerminate(_ application: UIApplication) {\n        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n    }\n\n    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {\n        // Called when the app was launched with a url. Feel free to add additional processing here,\n        // but if you want the App API to support tracking app url opens, make sure to keep this call\n        return ApplicationDelegateProxy.shared.application(app, open: url, options: options)\n    }\n\n    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {\n        // Called when the app was launched with an activity, including Universal Links.\n        // Feel free to add additional processing here, but if you want the App API to support\n        // tracking app url opens, make sure to keep this call\n        return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)\n    }\n\n}\n"
  },
  {
    "path": "ios-spm-template/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-512@2x.png\",\n      \"idiom\" : \"universal\",\n      \"platform\" : \"ios\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios-spm-template/App/App/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "ios-spm-template/App/App/Assets.xcassets/Splash.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"splash-2732x2732-2.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"splash-2732x2732-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"splash-2732x2732.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "ios-spm-template/App/App/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"17132\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina4_7\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"17105\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <imageView key=\"view\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"Splash\" id=\"snD-IY-ifK\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                    </imageView>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"Splash\" width=\"1366\" height=\"1366\"/>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "ios-spm-template/App/App/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14111\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14088\"/>\n    </dependencies>\n    <scenes>\n        <!--Bridge View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"CAPBridgeViewController\" customModule=\"Capacitor\" sceneMemberID=\"viewController\"/>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios-spm-template/App/App/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>CAPACITOR_DEBUG</key>\n\t<string>$(CAPACITOR_DEBUG)</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>My App</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios-spm-template/App/App.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 60;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };\n\t\t4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */ = {isa = PBXBuildFile; productRef = 4D22ABE82AF431CB00220026 /* CapApp-SPM */; };\n\t\t50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };\n\t\t504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };\n\t\t504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };\n\t\t504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };\n\t\t504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };\n\t\t50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = \"<group>\"; };\n\t\t50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = \"<group>\"; };\n\t\t504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = \"<group>\"; };\n\t\t958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t504EC3011FED79650016851F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t504EC2FB1FED79650016851F = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t958DCC722DB07C7200EA8C5F /* debug.xcconfig */,\n\t\t\t\t504EC3061FED79650016851F /* App */,\n\t\t\t\t504EC3051FED79650016851F /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t504EC3051FED79650016851F /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t504EC3041FED79650016851F /* App.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t504EC3061FED79650016851F /* App */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t50379B222058CBB4000EE86E /* capacitor.config.json */,\n\t\t\t\t504EC3071FED79650016851F /* AppDelegate.swift */,\n\t\t\t\t504EC30B1FED79650016851F /* Main.storyboard */,\n\t\t\t\t504EC30E1FED79650016851F /* Assets.xcassets */,\n\t\t\t\t504EC3101FED79650016851F /* LaunchScreen.storyboard */,\n\t\t\t\t504EC3131FED79650016851F /* Info.plist */,\n\t\t\t\t2FAD9762203C412B000D30F8 /* config.xml */,\n\t\t\t\t50B271D01FEDC1A000F3C39B /* public */,\n\t\t\t);\n\t\t\tpath = App;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t504EC3031FED79650016851F /* App */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget \"App\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t504EC3001FED79650016851F /* Sources */,\n\t\t\t\t504EC3011FED79650016851F /* Frameworks */,\n\t\t\t\t504EC3021FED79650016851F /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = App;\n\t\t\tpackageProductDependencies = (\n\t\t\t\t4D22ABE82AF431CB00220026 /* CapApp-SPM */,\n\t\t\t);\n\t\t\tproductName = App;\n\t\t\tproductReference = 504EC3041FED79650016851F /* App.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t504EC2FC1FED79650016851F /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 0920;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t504EC3031FED79650016851F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject \"App\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 504EC2FB1FED79650016851F;\n\t\t\tpackageReferences = (\n\t\t\t\tD4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference \"CapApp-SPM\" */,\n\t\t\t);\n\t\t\tproductRefGroup = 504EC3051FED79650016851F /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t504EC3031FED79650016851F /* App */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t504EC3021FED79650016851F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t50B271D11FEDC1A000F3C39B /* public in Resources */,\n\t\t\t\t504EC30F1FED79650016851F /* Assets.xcassets in Resources */,\n\t\t\t\t50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,\n\t\t\t\t504EC30D1FED79650016851F /* Main.storyboard in Resources */,\n\t\t\t\t2FAD9763203C412B000D30F8 /* config.xml in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t504EC3001FED79650016851F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t504EC3081FED79650016851F /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t504EC30B1FED79650016851F /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t504EC30C1FED79650016851F /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t504EC3111FED79650016851F /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t504EC3141FED79650016851F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t504EC3151FED79650016851F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t504EC3171FED79650016851F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tINFOPLIST_FILE = App/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tOTHER_SWIFT_FLAGS = \"$(inherited) \\\"-D\\\" \\\"COCOAPODS\\\" \\\"-DDEBUG\\\"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t504EC3181FED79650016851F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tINFOPLIST_FILE = App/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t504EC2FF1FED79650016851F /* Build configuration list for PBXProject \"App\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t504EC3141FED79650016851F /* Debug */,\n\t\t\t\t504EC3151FED79650016851F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget \"App\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t504EC3171FED79650016851F /* Debug */,\n\t\t\t\t504EC3181FED79650016851F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCLocalSwiftPackageReference section */\n\t\tD4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference \"CapApp-SPM\" */ = {\n\t\t\tisa = XCLocalSwiftPackageReference;\n\t\t\trelativePath = \"CapApp-SPM\";\n\t\t};\n/* End XCLocalSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\t4D22ABE82AF431CB00220026 /* CapApp-SPM */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference \"CapApp-SPM\" */;\n\t\t\tproductName = \"CapApp-SPM\";\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = 504EC2FC1FED79650016851F /* Project object */;\n}\n"
  },
  {
    "path": "ios-spm-template/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios-spm-template/App/CapApp-SPM/.gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/config/registries.json\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n.netrc\n"
  },
  {
    "path": "ios-spm-template/App/CapApp-SPM/Package.swift",
    "content": "// swift-tools-version: 5.9\nimport PackageDescription\n\n// DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands\nlet package = Package(\n    name: \"CapApp-SPM\",\n    platforms: [.iOS(.v15)],\n    products: [\n        .library(\n            name: \"CapApp-SPM\",\n            targets: [\"CapApp-SPM\"])\n    ],\n    dependencies: [\n        .package(url: \"https://github.com/ionic-team/capacitor-swift-pm.git\", from: \"8.0.0\")\n    ],\n    targets: [\n        .target(\n            name: \"CapApp-SPM\",\n            dependencies: [\n                .product(name: \"Capacitor\", package: \"capacitor-swift-pm\"),\n                .product(name: \"Cordova\", package: \"capacitor-swift-pm\")\n            ]\n        )\n    ]\n)\n"
  },
  {
    "path": "ios-spm-template/App/CapApp-SPM/README.md",
    "content": "# CapApp-SPM\n\nThis package is used to host SPM dependencies for your Capacitor project\n\nDo not modify the contents of it or there may be unintended consequences.\n"
  },
  {
    "path": "ios-spm-template/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift",
    "content": "public let isCapacitorApp = true\n"
  },
  {
    "path": "ios-spm-template/debug.xcconfig",
    "content": "CAPACITOR_DEBUG = true\n"
  },
  {
    "path": "lerna.json",
    "content": "{\n  \"command\": {\n    \"bootstrap\": {\n      \"hoist\": true\n    },\n    \"version\": {\n      \"allowBranch\": \"main\",\n      \"conventionalCommits\": true,\n      \"createRelease\": \"github\",\n      \"message\": \"Release %s\",\n      \"tagVersionPrefix\": \"\"\n    }\n  },\n  \"version\": \"8.2.0\",\n  \"$schema\": \"node_modules/lerna/schemas/lerna-schema.json\"\n}\n"
  },
  {
    "path": "nx.json",
    "content": "{\n  \"tasksRunnerOptions\": {\n    \"default\": {\n      \"runner\": \"nx/tasks-runners/default\",\n      \"options\": {\n        \"cacheableOperations\": [\n          \"build\"\n        ],\n        \"parallel\": 5\n      }\n    }\n  },\n  \"targetDefaults\": {\n    \"build\": {\n      \"dependsOn\": [\n        \"^build\"\n      ],\n      \"outputs\": [\n        \"{projectRoot}/core/dist\",\n        \"{projectRoot}/cli/dist\",\n        \"{projectRoot}/core/types\",\n        \"{projectRoot}/core/build\"\n      ]\n    }\n  },\n  \"$schema\": \"./node_modules/nx/schemas/nx-schema.json\",\n  \"namedInputs\": {\n    \"default\": [\n      \"{projectRoot}/**/*\",\n      \"sharedGlobals\"\n    ],\n    \"sharedGlobals\": [],\n    \"production\": [\n      \"default\"\n    ]\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"capacitor\",\n  \"private\": true,\n  \"workspaces\": [\n    \"android\",\n    \"ios\",\n    \"cli\",\n    \"core\"\n  ],\n  \"scripts\": {\n    \"ci:publish:nightly\": \"lerna version prerelease --conventional-commits --conventional-prerelease --preid nightly-$(date +\\\"%Y%m%dT%H%M%S\\\") --force-publish --no-changelog --no-git-tag-version --no-push --yes && lerna exec -- npm publish --tag nightly --provenance\",\n    \"ci:publish:alpha\": \"lerna version prerelease --conventional-commits --conventional-prerelease --preid alpha --force-publish --yes && lerna exec -- npm publish --tag next --provenance\",\n    \"ci:publish:beta\": \"lerna version prerelease --conventional-commits --conventional-prerelease --preid beta --force-publish --yes && lerna exec -- npm publish --tag next --provenance\",\n    \"ci:publish:rc\": \"lerna version prerelease --conventional-commits --conventional-prerelease --preid rc --force-publish --yes && lerna exec -- npm publish --tag next --provenance\",\n    \"ci:publish:latest\": \"lerna version --conventional-commits --force-publish --yes && lerna exec -- npm publish --tag latest --provenance\",\n    \"ci:publish:latest-from-pre\": \"lerna version --conventional-commits --conventional-graduate --force-publish --yes && lerna exec -- npm publish --tag latest --provenance\",\n    \"ci:publish:dev\": \"lerna version prerelease --conventional-commits --conventional-prerelease --force-publish --preid dev-$(date +\\\"%Y%m%dT%H%M%S\\\") --no-changelog --no-git-tag-version --no-push --yes && lerna exec -- npm publish --tag dev --provenance\",\n    \"build:nativebridge\": \"lerna run build:nativebridge\",\n    \"sync-peer-dependencies\": \"node scripts/sync-peer-dependencies.mjs\",\n    \"lint\": \"npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint\",\n    \"fmt\": \"npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format\",\n    \"prettier\": \"prettier \\\"**/*.{css,html,java,js,mjs,ts}\\\" --plugin=prettier-plugin-java\",\n    \"eslint\": \"eslint . --ext ts\",\n    \"swiftlint\": \"node-swiftlint\",\n    \"version\": \"npm run sync-peer-dependencies\"\n  },\n  \"prettier\": \"@ionic/prettier-config\",\n  \"eslintConfig\": {\n    \"extends\": \"@ionic/eslint-config/recommended\"\n  },\n  \"devDependencies\": {\n    \"@ionic/eslint-config\": \"^0.4.0\",\n    \"@ionic/prettier-config\": \"^4.0.0\",\n    \"@ionic/swiftlint-config\": \"^2.0.0\",\n    \"@types/node\": \"18.18.6\",\n    \"eslint\": \"^8.57.0\",\n    \"lerna\": \"^7.1.3\",\n    \"prettier\": \"^3.3.0\",\n    \"prettier-plugin-java\": \"^2.6.4\",\n    \"rimraf\": \"^4.4.1\",\n    \"semver\": \"^7.3.7\",\n    \"swiftlint\": \"^2.0.0\",\n    \"tar\": \"^7.5.3\"\n  }\n}\n"
  },
  {
    "path": "scripts/.gitignore",
    "content": "tmp\n"
  },
  {
    "path": "scripts/lib/cli.mjs",
    "content": "export const execute = (fn) => {\n  fn().catch((e) => {\n    process.stderr.write(e.stack ?? e);\n    process.exit(1);\n  });\n};\n"
  },
  {
    "path": "scripts/lib/fn.mjs",
    "content": "export const identity = (v) => v;\nexport const pipe =\n  (...fns) =>\n  (v) =>\n    fns.reduce((r, fn) => fn(r), v);\n"
  },
  {
    "path": "scripts/lib/fs.mjs",
    "content": "import * as fs from 'fs';\nimport * as util from 'util';\n\nexport const mkdir = util.promisify(fs.mkdir);\nexport const readFile = util.promisify(fs.readFile);\nexport const readJSON = async (p) => JSON.parse(await readFile(p));\nexport const writeFile = util.promisify(fs.writeFile);\nexport const writeJSON = async (p, contents, space = 2) =>\n  writeFile(p, JSON.stringify(contents, undefined, space) + '\\n');\n"
  },
  {
    "path": "scripts/lib/git.mjs",
    "content": "import * as cp from './subprocess.mjs';\nimport { root } from './repo.mjs';\n\nconst cwd = root;\n\nconst execGit = async (command, options) => await cp.exec(`git ${command}`, { cwd, ...options });\n\nexport const lsfiles = async (p, options) => (await execGit(`ls-files \"${p}\"`, options)).stdout.trim().split('\\n');\n"
  },
  {
    "path": "scripts/lib/lerna.mjs",
    "content": "import { root } from './repo.mjs';\nimport * as cp from './subprocess.mjs';\n\nconst stdio = 'inherit';\nconst cwd = root;\n\nconst execLerna = async (command) => await cp.exec(`npx lerna ${command}`, { cwd });\nconst runLerna = async (args = []) => await cp.run('npx', ['lerna', ...args], { cwd, stdio });\n\nexport const ls = async () => JSON.parse((await execLerna('ls --json')).stdout);\nexport const exec = async (args = []) => await runLerna(['exec', ...args]);\nexport const bootstrap = async (args = []) => runLerna(['bootstrap', ...args]);\n"
  },
  {
    "path": "scripts/lib/repo.mjs",
    "content": "import { fileURLToPath } from 'url';\nimport { dirname } from 'path';\n\nimport { pipe } from './fn.mjs';\n\nexport const root = pipe(fileURLToPath, ...Array(3).fill(dirname))(import.meta.url);\n"
  },
  {
    "path": "scripts/lib/subprocess.mjs",
    "content": "import * as cp from 'child_process';\nimport * as util from 'util';\n\nexport const exec = util.promisify(cp.exec);\nexport const spawn = cp.spawn;\nexport const run = (cmd, args, options) => wait(spawn(cmd, args, options));\nexport const wait = async (p) => {\n  return new Promise((resolve, reject) => {\n    p.on('error', reject);\n\n    p.on('close', (code, signal) => {\n      if (code === 0) {\n        resolve();\n      } else {\n        reject(new Error(`bad subprocess exit (code=${code}, signal=${signal})`));\n      }\n    });\n  });\n};\n"
  },
  {
    "path": "scripts/lib/version.mjs",
    "content": "import { readJSON, writeJSON } from './fs.mjs';\n\nexport const setPackageJsonDependencies = async (path, packages, key = 'dependencies') => {\n  const pkg = await readJSON(path);\n\n  for (const [dep, version] of Object.entries(packages)) {\n    pkg[key][dep] = version;\n  }\n\n  await writeJSON(path, pkg);\n};\n"
  },
  {
    "path": "scripts/native-podspec.sh",
    "content": "#!/usr/bin/env sh\nset -eo pipefail\n\ncase $1 in\n     lint) \n       pod lib lint ios/CapacitorCordova.podspec --allow-warnings\n       pod lib lint ios/Capacitor.podspec --allow-warnings;;\n\n     publish) \n       export NATIVE_PUBLISH=true\n       pod trunk push ios/CapacitorCordova.podspec --allow-warnings\n       pod trunk push ios/Capacitor.podspec --allow-warnings;;\n\n     *) echo \"'lint' or 'publish' were not provided. Exiting...\";;\nesac\n"
  },
  {
    "path": "scripts/pack-cli-assets.mjs",
    "content": "import { resolve } from 'path';\nimport { create } from 'tar';\n\nimport { execute } from './lib/cli.mjs';\nimport { mkdir } from './lib/fs.mjs';\nimport { lsfiles } from './lib/git.mjs';\nimport { root } from './lib/repo.mjs';\n\nexecute(async () => {\n  const assetsDir = resolve(root, 'cli', 'assets');\n\n  const templates = [\n    'android-template',\n    'ios-pods-template',\n    'ios-spm-template',\n    'capacitor-cordova-android-plugins',\n    'capacitor-cordova-ios-plugins',\n  ];\n\n  await mkdir(assetsDir, { recursive: true });\n\n  for (const template of templates) {\n    const dest = resolve(assetsDir, template + '.tar.gz');\n    const templatePath = resolve(root, template);\n\n    const files = await lsfiles(templatePath, { cwd: templatePath });\n\n    await create({ gzip: true, file: dest, cwd: templatePath }, files);\n\n    console.log(`Packed ${dest}!`);\n  }\n});\n"
  },
  {
    "path": "scripts/publish-android.sh",
    "content": "#!/usr/bin/env bash\n\nDIR=../android\nLOG_OUTPUT=./tmp/capacitor-android.txt\nCAP_VERSION=`grep '\"version\": ' $DIR/package.json | awk '{print $2}' | tr -d '\",'`\necho Attempting to build and publish Capacitor native libraries with version $CAP_VERSION\n\n# Make log dir if doesnt exist\nmkdir -p ./tmp\n\n# Export ENV variable used by Gradle for Versioning\nexport CAP_VERSION\nexport CAP_PUBLISH=true\n\n# Get latest com.capacitorjs:core XML version info\nCAPACITOR_PUBLISHED_URL=\"https://repo1.maven.org/maven2/com/capacitorjs/core/maven-metadata.xml\"\nCAPACITOR_PUBLISHED_DATA=$(curl -s $CAPACITOR_PUBLISHED_URL)\nCAPACITOR_PUBLISHED_VERSION=\"$(perl -ne 'print and last if s/.*<latest>(.*)<\\/latest>.*/\\1/;' <<< $CAPACITOR_PUBLISHED_DATA)\"\n\n# Check if we need to publish a new native version of the Capacitor Android library\nif [[ $CAP_VERSION == $CAPACITOR_PUBLISHED_VERSION ]]; then\n    printf %\"s\\n\" \"Native Capacitor Android library version $CAPACITOR_PUBLISHED_VERSION is already published on MavenCentral, skipping.\"\nelse\n    printf %\"s\\n\" \"Publishing $CAP_VERSION to MavenCentral production...\"\n\n    # Build and publish\n    $DIR/gradlew clean build publishReleasePublicationToSonatypeRepository closeAndReleaseSonatypeStagingRepository --max-workers 1 -b $DIR/capacitor/build.gradle -Pandroid.useAndroidX=true > $LOG_OUTPUT 2>&1\n\n    echo $RESULT\n\n    if grep --quiet \"BUILD SUCCESSFUL\" $LOG_OUTPUT; then\n        printf %\"s\\n\" \"Success: Capacitor Android Library published to MavenCentral.\"\n    else\n        printf %\"s\\n\\n\" \"Error publishing, check $LOG_OUTPUT for more info! Manually review and release from the Central Portal may be necessary https://central.sonatype.com/publishing/deployments/\"\n        cat $LOG_OUTPUT\n        exit 1\n    fi\nfi\n"
  },
  {
    "path": "scripts/sync-peer-dependencies.mjs",
    "content": "import { resolve } from 'path';\nimport semver from 'semver';\n\nimport { execute } from './lib/cli.mjs';\nimport { ls } from './lib/lerna.mjs';\nimport { setPackageJsonDependencies } from './lib/version.mjs';\n\nexecute(async () => {\n  const CORE_DEPENDENTS = ['@capacitor/android', '@capacitor/ios'];\n  const pkgs = await ls();\n\n  const corePkg = pkgs.find((p) => p.name === '@capacitor/core');\n  const { major, minor, prerelease } = semver.parse(corePkg.version);\n\n  const range = `^${major}.${minor}.0${prerelease.length > 0 ? `-${prerelease.join('.')}` : ''}`;\n\n  for (const pkg of pkgs.filter((p) => CORE_DEPENDENTS.includes(p.name))) {\n    const p = resolve(pkg.location, 'package.json');\n\n    await setPackageJsonDependencies(p, { '@capacitor/core': range }, 'peerDependencies');\n  }\n});\n"
  },
  {
    "path": "swiftlint.config.js",
    "content": "module.exports = {\n  ...require('@ionic/swiftlint-config'),\n  included: ['${PWD}/ios', '${PWD}/ios-pods-template', '${PWD}/ios-spm-template'],\n  excluded: ['${PWD}/ios/Capacitor/CapacitorTests', '${PWD}/ios/Capacitor/TestsHostApp', '${PWD}/ios/Frameworks'],\n};\n"
  }
]