[
  {
    "path": ".Rbuildignore",
    "content": "^.*\\.Rproj$\n^\\.Rproj\\.user$\n^examples$\n^R/\\.editorconfig$\n^README\\.Rmd$\n^README\\.html$\n^RELEASE\\.md$\n^_pkgdown\\.yml$\n^docs$\n^pkgdown$\ntags\n^\\.github$\n^\\.lintr$\n^cran-comments\\.md$\n^revdep$\n^CRAN-SUBMISSION$\n^\\.positai$\n^\\.claude$\n"
  },
  {
    "path": ".github/.gitignore",
    "content": "*.html\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Report an error or unexpected behavior\nlabels: [bug]\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thank you for taking the time to share feedback on rsconnect. Please read the following to help your question find the right answer:\n\n        - If you're reporting an issue with *shinyapps.io*, please visit https://forum.posit.co/c/posit-professional-hosted/shinyappsio/24\n        - If you're reporting an issue with *Connect Cloud*, please visit https://forum.posit.co/c/posit-professional-hosted/posit-connect-cloud/67\n        - If you're reporting an issue with *Posit Connect*, please visit https://support.posit.co/hc/en-us/requests/new or contact your Posit representative\n\n        To report a bug with rsconnect, please provide details about the issue and, if possible, a reproducible example.\n  - type: textarea\n    attributes:\n      label: Description\n      description: |\n        Description of the bug, and how to reproduce it\n  - type: textarea\n    attributes:\n      label: Your environment\n      description: |\n        Please document the version of rsconnect you are using\n      placeholder: |\n        - rsconnect: 1.6.2\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: Help with shinyapps.io\n    url: https://forum.posit.co/c/posit-professional-hosted/shinyappsio/24\n    about: For questions about shinyapps.io, visit Posit Community\n  - name: Help with Connect Cloud\n    url: https://forum.posit.co/c/posit-professional-hosted/posit-connect-cloud/67\n    about: For questions about Connect Cloud, visit Posit Community\n  - name: Help with Posit Connect\n    url: https://support.posit.co/hc/en-us\n    about: For questions about Posit Connect, visit the support forum or contact your Posit representative\n"
  },
  {
    "path": ".github/workflows/R-CMD-check.yaml",
    "content": "# Workflow derived from https://github.com/r-lib/actions/tree/master/examples\n# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help\non:\n  push:\n    branches: [main, master]\n  pull_request:\n    branches: [main, master]\n\nname: R-CMD-check\n\njobs:\n  R-CMD-check:\n    runs-on: ${{ matrix.config.os }}\n\n    name: ${{ matrix.config.os }} (${{ matrix.config.r }})\n\n    strategy:\n      fail-fast: false\n      matrix:\n        config:\n          - {os: macos-latest,   r: 'release'}\n          - {os: windows-latest, r: 'release'}\n          - {os: ubuntu-latest,   r: 'devel', http-user-agent: 'release'}\n          - {os: ubuntu-latest,   r: 'release'}\n          - {os: ubuntu-latest,   r: 'oldrel-1'}\n          - {os: ubuntu-latest,   r: 'oldrel-2'}\n          - {os: ubuntu-latest,   r: 'oldrel-3'}\n          - {os: ubuntu-latest,   r: 'oldrel-4'}\n\n    env:\n      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}\n      R_KEEP_PKG_SOURCE: yes\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: r-lib/actions/setup-pandoc@v2\n\n      - uses: r-lib/actions/setup-r@v2\n        with:\n          r-version: ${{ matrix.config.r }}\n          http-user-agent: ${{ matrix.config.http-user-agent }}\n          use-public-rspm: true\n\n      - uses: quarto-dev/quarto-actions/setup@v2\n\n      - uses: r-lib/actions/setup-r-dependencies@v2\n        with:\n          extra-packages: any::rcmdcheck, Biobase=?ignore, BiocManager=?ignore\n          needs: check\n\n      - uses: r-lib/actions/check-r-package@v2\n        with:\n          upload-snapshots: true\n"
  },
  {
    "path": ".github/workflows/check-no-suggests.yaml",
    "content": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help\n#\n# NOTE: This workflow only directly installs \"hard\" dependencies, i.e. Depends,\n# Imports, and LinkingTo dependencies. Notably, Suggests dependencies are never\n# installed, with the exception of testthat, knitr, and rmarkdown. The cache is\n# never used to avoid accidentally restoring a cache containing a suggested\n# dependency.\non:\n  push:\n    branches: [main, master]\n  pull_request:\n    branches: [main, master]\n\nname: check-no-suggests.yaml\n\npermissions: read-all\n\njobs:\n  check-no-suggests:\n    runs-on: ${{ matrix.config.os }}\n\n    name: ${{ matrix.config.os }} (${{ matrix.config.r }})\n\n    strategy:\n      fail-fast: false\n      matrix:\n        config:\n          - {os: ubuntu-latest,   r: 'release'}\n\n    env:\n      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}\n      R_KEEP_PKG_SOURCE: yes\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: r-lib/actions/setup-pandoc@v2\n\n      - uses: r-lib/actions/setup-r@v2\n        with:\n          r-version: ${{ matrix.config.r }}\n          http-user-agent: ${{ matrix.config.http-user-agent }}\n          use-public-rspm: true\n\n      - uses: r-lib/actions/setup-r-dependencies@v2\n        with:\n          dependencies: '\"hard\"'\n          cache: false\n          extra-packages: |\n            any::rcmdcheck\n            any::testthat\n            any::knitr\n            any::rmarkdown\n          needs: check\n\n      - uses: r-lib/actions/check-r-package@v2\n        with:\n          upload-snapshots: true\n          build_args: 'c(\"--no-manual\",\"--compact-vignettes=gs+qpdf\")'\n"
  },
  {
    "path": ".github/workflows/connect-integration.yaml",
    "content": "name: Connect Integration Tests\non:\n  push:\n    branches: [main, master]\n  pull_request:\n    branches: [main, master]\n\njobs:\n  connect-integration:\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n\n    strategy:\n      fail-fast: false\n      matrix:\n        version:\n          - \"preview\"   # Nightly builds of Connect\n          - \"release\"   # special value that always points to the latest Connect release\n          - \"2025.09.0\" # jammy\n          - \"2025.03.0\" # jammy (every 6 months, just bc every release would be overkill)\n          - \"2024.09.0\" # jammy\n          # These versions use R 4.2 and seem to take forever to install R dependencies\n          # - \"2024.03.0\" # jammy\n          # - \"2023.09.0\" # jammy\n    name: Connect ${{ matrix.version }}\n\n    env:\n      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}\n      R_KEEP_PKG_SOURCE: yes\n\n    steps:\n      - uses: actions/checkout@v6\n\n      - uses: r-lib/actions/setup-r@v2\n        with:\n          use-public-rspm: true\n\n      - uses: r-lib/actions/setup-r-dependencies@v2\n        with:\n          # Only install what we need for this test, which is not all of Suggests\n          dependencies: '\"hard\"'\n          extra-packages: |\n            local::.\n            any::testthat\n            any::shiny\n\n      - name: Run integration tests\n        uses: posit-dev/with-connect@main\n        with:\n          version: ${{ matrix.version }}\n          license: ${{ secrets.CONNECT_LICENSE_FILE }}\n          command: |\n            Rscript -e 'testthat::test_dir(\"tests/integration\", package=\"rsconnect\", load_package=\"installed\")'\n"
  },
  {
    "path": ".github/workflows/format-suggest.yaml",
    "content": "# Workflow derived from https://github.com/posit-dev/setup-air/tree/main/examples\non:\n  pull_request:\n\nname: format-suggest.yaml\n\npermissions: read-all\n\njobs:\n  format-suggest:\n    name: format-suggest\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Install\n        uses: posit-dev/setup-air@v1\n\n      - name: Format\n        run: air format .\n\n      - name: Suggest\n        uses: reviewdog/action-suggester@v1\n        with:\n          level: error\n          fail_level: error\n          tool_name: air\n"
  },
  {
    "path": ".github/workflows/lint.yaml",
    "content": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help\non:\n  push:\n    branches: [main, master]\n  pull_request:\n    branches: [main, master]\n\nname: lint\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    env:\n      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}\n    steps:\n      - uses: actions/checkout@v3\n\n      - uses: r-lib/actions/setup-r@v2\n        with:\n          use-public-rspm: true\n\n      - uses: r-lib/actions/setup-r-dependencies@v2\n        with:\n          extra-packages: any::lintr, local::.\n          needs: lint\n\n      - name: Lint\n        run: lintr::lint_package()\n        shell: Rscript {0}\n        env:\n          LINTR_ERROR_ON_LINT: true\n"
  },
  {
    "path": ".github/workflows/pkgdown.yaml",
    "content": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help\non:\n  push:\n    branches: [main, master]\n  pull_request:\n    branches: [main, master]\n  release:\n    types: [published]\n  workflow_dispatch:\n\nname: pkgdown\n\njobs:\n  pkgdown:\n    runs-on: ubuntu-latest\n    # Only restrict concurrency for non-PR jobs\n    concurrency:\n      group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}\n    env:\n      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}\n    steps:\n      - uses: actions/checkout@v3\n\n      - uses: r-lib/actions/setup-pandoc@v2\n\n      - uses: r-lib/actions/setup-r@v2\n        with:\n          use-public-rspm: true\n\n      - uses: r-lib/actions/setup-r-dependencies@v2\n        with:\n          extra-packages: any::pkgdown, local::.\n          needs: website\n\n      - name: Build site\n        run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)\n        shell: Rscript {0}\n\n      - name: Deploy to GitHub pages 🚀\n        if: github.event_name != 'pull_request'\n        uses: JamesIves/github-pages-deploy-action@v4.4.1\n        with:\n          clean: false\n          branch: gh-pages\n          folder: docs\n"
  },
  {
    "path": ".github/workflows/rhub.yaml",
    "content": "# R-hub's generic GitHub Actions workflow file. It's canonical location is at\n# https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml\n# You can update this file to a newer version using the rhub2 package:\n#\n# rhub::rhub_setup()\n#\n# It is unlikely that you need to modify this file manually.\n\nname: R-hub\nrun-name: \"${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}\"\n\non:\n  workflow_dispatch:\n    inputs:\n      config:\n        description: 'A comma separated list of R-hub platforms to use.'\n        type: string\n        default: 'linux,windows,macos'\n      name:\n        description: 'Run name. You can leave this empty now.'\n        type: string\n      id:\n        description: 'Unique ID. You can leave this empty now.'\n        type: string\n\njobs:\n\n  setup:\n    runs-on: ubuntu-latest\n    outputs:\n      containers: ${{ steps.rhub-setup.outputs.containers }}\n      platforms: ${{ steps.rhub-setup.outputs.platforms }}\n\n    steps:\n    # NO NEED TO CHECKOUT HERE\n    - uses: r-hub/actions/setup@v1\n      with:\n        config: ${{ github.event.inputs.config }}\n      id: rhub-setup\n\n  linux-containers:\n    needs: setup\n    if: ${{ needs.setup.outputs.containers != '[]' }}\n    runs-on: ubuntu-latest\n    name: ${{ matrix.config.label }}\n    strategy:\n      fail-fast: false\n      matrix:\n        config: ${{ fromJson(needs.setup.outputs.containers) }}\n    container:\n      image: ${{ matrix.config.container }}\n\n    steps:\n      - uses: r-hub/actions/checkout@v1\n      - uses: r-hub/actions/platform-info@v1\n        with:\n          token: ${{ secrets.RHUB_TOKEN }}\n          job-config: ${{ matrix.config.job-config }}\n      - uses: r-hub/actions/setup-deps@v1\n        with:\n          token: ${{ secrets.RHUB_TOKEN }}\n          job-config: ${{ matrix.config.job-config }}\n      - uses: r-hub/actions/run-check@v1\n        with:\n          token: ${{ secrets.RHUB_TOKEN }}\n          job-config: ${{ matrix.config.job-config }}\n\n  other-platforms:\n    needs: setup\n    if: ${{ needs.setup.outputs.platforms != '[]' }}\n    runs-on: ${{ matrix.config.os }}\n    name: ${{ matrix.config.label }}\n    strategy:\n      fail-fast: false\n      matrix:\n        config: ${{ fromJson(needs.setup.outputs.platforms) }}\n\n    steps:\n      - uses: r-hub/actions/checkout@v1\n      - uses: r-hub/actions/setup-r@v1\n        with:\n          job-config: ${{ matrix.config.job-config }}\n          token: ${{ secrets.RHUB_TOKEN }}\n      - uses: r-hub/actions/platform-info@v1\n        with:\n          token: ${{ secrets.RHUB_TOKEN }}\n          job-config: ${{ matrix.config.job-config }}\n      - uses: r-hub/actions/setup-deps@v1\n        with:\n          job-config: ${{ matrix.config.job-config }}\n          token: ${{ secrets.RHUB_TOKEN }}\n      - uses: r-hub/actions/run-check@v1\n        with:\n          job-config: ${{ matrix.config.job-config }}\n          token: ${{ secrets.RHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/shinyapps-integration.yaml",
    "content": "name: shinyapps.io Integration Tests\non:\n  push:\n    branches: [main, master]\n  pull_request:\n    branches: [main, master]\n\njobs:\n  shinyapps-integration:\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n    # Only run if shinyapps.io credentials are available (not on forks)\n    if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork\n\n    env:\n      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}\n      SHINYAPPS_NAME: ${{ secrets.SHINYAPPS_NAME }}\n      SHINYAPPS_TOKEN: ${{ secrets.SHINYAPPS_TOKEN }}\n      SHINYAPPS_SECRET: ${{ secrets.SHINYAPPS_SECRET }}\n\n    steps:\n      - uses: actions/checkout@v6\n\n      - uses: r-lib/actions/setup-r@v2\n        with:\n          use-public-rspm: true\n\n      - uses: r-lib/actions/setup-r-dependencies@v2\n        with:\n          dependencies: '\"hard\"'\n          extra-packages: |\n            local::.\n            any::testthat\n            any::shiny\n\n      - name: Run shinyapps.io integration tests\n        if: env.SHINYAPPS_SECRET != ''\n        run: |\n          Rscript -e 'testthat::test_dir(\"tests/shinyapps-integration\", package=\"rsconnect\", load_package=\"installed\")'\n"
  },
  {
    "path": ".gitignore",
    "content": ".Rproj.user\n.Rhistory\n.RData\n.DS_Store\nREADME.html\n*.Rcheck/\n*.tar.gz\ndocs/\n.rscignore\ntags\ninst/doc\ninst/resources/__pycache__/\ntests/**/*.quarto_ipynb\n\n# license files should not be commited to this repository\n*.lic\n.positai\n"
  },
  {
    "path": ".lintr",
    "content": "linters: linters_with_defaults(\n    line_length_linter(160),\n    indentation_linter = NULL,\n    object_length_linter(60),\n    object_name_linter = NULL,\n    object_usage_linter = NULL,\n    brace_linter = NULL,\n    commented_code_linter = NULL,\n    seq_linter = NULL,\n    return_linter = NULL\n  )\n"
  },
  {
    "path": "DESCRIPTION",
    "content": "Type: Package\nPackage: rsconnect\nTitle: Deploy Docs, Apps, and APIs to 'Posit Connect', 'shinyapps.io', and 'RPubs'\nVersion: 1.8.0.9000\nAuthors@R: c(\n    person(\"Aron\", \"Atkins\", , \"aron@posit.co\", role = c(\"aut\", \"cre\")),\n    person(\"Toph\", \"Allen\", role = \"aut\"),\n    person(\"Hadley\", \"Wickham\", role = \"aut\"),\n    person(\"Jonathan\", \"McPherson\", role = \"aut\"),\n    person(\"JJ\", \"Allaire\", role = \"aut\"),\n    person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"))\n  )\nDescription: Programmatic deployment interface for 'RPubs',\n    'shinyapps.io', and 'Posit Connect'. Supported content types include R\n    Markdown documents, Shiny applications, Plumber APIs, plots, and\n    static web content.\nLicense: GPL-2\nURL: https://rstudio.github.io/rsconnect/, https://github.com/rstudio/rsconnect\nBugReports: https://github.com/rstudio/rsconnect/issues\nDepends:\n    R (>= 3.5.0)\nImports:\n    cli,\n    curl,\n    digest,\n    httr2,\n    jsonlite,\n    lifecycle,\n    openssl (>= 2.0.0),\n    PKI,\n    packrat (>= 0.6),\n    renv (>= 1.0.0),\n    rlang (>= 1.0.0),\n    rstudioapi (>= 0.18.0),\n    snowflakeauth,\n    tools,\n    yaml (>= 2.1.5),\n    utils\nSuggests:\n    Biobase,\n    BiocManager,\n    foreign,\n    knitr,\n    MASS,\n    plumber (>= 0.3.2),\n    quarto,\n    reticulate,\n    rmarkdown (>= 1.1),\n    shiny,\n    testthat (>= 3.1.9),\n    webfakes,\n    withr\nVignetteBuilder:\n    knitr, rmarkdown\nConfig/Needs/website: tidyverse/tidytemplate\nConfig/testthat/edition: 3\nConfig/testthat/parallel: true\nConfig/Python/Note: Python <= 3.10 requires tomllib package for pyproject.toml parsing\nEncoding: UTF-8\nRoxygen: list(markdown = TRUE)\nRoxygenNote: 7.3.3\n"
  },
  {
    "path": "NAMESPACE",
    "content": "# Generated by roxygen2: do not edit by hand\n\nS3method(as.data.frame,rsconnect_secret)\nS3method(format,rsconnect_secret)\nS3method(print,linterResults)\nS3method(print,rsconnect_secret)\nS3method(str,rsconnect_secret)\nexport(accountInfo)\nexport(accountUsage)\nexport(accounts)\nexport(addAuthorizedUser)\nexport(addLinter)\nexport(addServer)\nexport(addServerCertificate)\nexport(appDependencies)\nexport(applications)\nexport(configureApp)\nexport(connectApiUser)\nexport(connectCloudUser)\nexport(connectSPCSUser)\nexport(connectUser)\nexport(deployAPI)\nexport(deployApp)\nexport(deployDoc)\nexport(deploySite)\nexport(deployTFModel)\nexport(deployments)\nexport(forgetDeployment)\nexport(generateAppName)\nexport(getLogs)\nexport(lint)\nexport(linter)\nexport(listAccountEnvVars)\nexport(listBundleFiles)\nexport(listDeploymentFiles)\nexport(purgeApp)\nexport(removeAccount)\nexport(removeAuthorizedUser)\nexport(removeServer)\nexport(resendInvitation)\nexport(restartApp)\nexport(rpubsUpload)\nexport(serverInfo)\nexport(servers)\nexport(setAccountInfo)\nexport(setProperty)\nexport(showInvited)\nexport(showLogs)\nexport(showMetrics)\nexport(showProperties)\nexport(showUsage)\nexport(showUsers)\nexport(syncAppMetadata)\nexport(taskLog)\nexport(tasks)\nexport(terminateApp)\nexport(unsetProperty)\nexport(updateAccountEnvVars)\nexport(writeManifest)\nimport(rlang)\nimportFrom(lifecycle,deprecated)\nimportFrom(stats,na.omit)\nimportFrom(stats,setNames)\nimportFrom(utils,available.packages)\nimportFrom(utils,contrib.url)\nimportFrom(utils,getFromNamespace)\nimportFrom(utils,glob2rx)\nimportFrom(utils,head)\nimportFrom(utils,installed.packages)\nimportFrom(utils,packageDescription)\nimportFrom(utils,packageVersion)\nimportFrom(utils,read.csv)\n"
  },
  {
    "path": "NEWS.md",
    "content": "# rsconnect (development version)\n\n* Added support for deploying Node.js applications to Posit Connect.\n  `deployApp()` and `writeManifest()` now automatically detect Node.js content\n  from `package.json` and generate the appropriate manifest. Added\n  `envManagementNodejs` parameter for controlling Node.js environment\n  management. [Node.js support](https://docs.posit.co/connect/user/nodejs/) is\n  in Early Access as of Connect version 2026.04.0. (#1322)\n\n* Fixed an issue where Bioconductor packages could be incorrectly associated\n  with a CRAN repository URL when the same package appeared in CRAN's Transit\n  directory. (#1314)\n\n* The global deployment history file used by the Workbench dashboard no longer\n  uses a fixed temporary file name during updates, eliminating a race\n  condition that could cause the file to rapidly grow during concurrent\n  deployments. The history is also capped at 100 records and resets if the\n  file grows excessively large. (#1320)\n\n* `deployApp()` with `logLevel = \"verbose\"` no longer errors using the `httr2` backend. (#1312)\n\n* `deployApp()`, `writeManifest()`, and `appDependencies()` gain a\n  `dependencyResolution` parameter. Set `dependencyResolution = \"library\"` to ignore\n  the `renv.lock` file and resolve package dependencies by scanning the code.\n  The version that is recorded is what is installed in the libraries active in the\n  R session (i.e. what is displayed with `.libPaths()`). This is useful when\n  deploying from environments where there is a mismatch between the\n  renv lock file and the user's environment. (#1046, #1315, #1317)\n\n* Improved error messages when `renv::snapshot()` fails during dependency\n  discovery. (#1078)\n\n# rsconnect 1.8.0\n\n* `rsconnect` now uses [`httr2`](https://httr2.r-lib.org/) as its HTTP client.\n  There should be no user-visible changes as a result, but if something does\n  not work as expected, please file an issue, and you can set\n  `options(rsconnect.httr2 = FALSE)` as a temporary workaround. (#1284)\n\n* Added support for renv profiles and renv lockfiles that are located outside of\n  the project root. (#1122)\n\n* Resolved a bug where `renv.lock` files that had multiple repositories were not\n  being translated faithfully when creating the manifest file. (#1268)\n\n* Packages installed locally via pak are resolved against configured\n  repositories. (#1305)\n\n* Added support for overriding R package repository resolution behavior. (#1272)\n\n* Push-button publishing from desktop RStudio is now compatible with Connect\n  servers hosted on Snowflake. This includes support for browser-based\n  authentication during deployment. (#1289)\n\n* The `snowflakeConnectionName` parameter now respects the default Snowflake\n  connection name in the `connections.toml` file (when it exists), making it\n  optional in many cases. This is only applicable to Connect servers hosted on\n  Snowflake. (#1283)\n\n* Added support for using identity federation to authenticate against Connect\n  when running in Posit Workbench, when available. This allows deploying to\n  Connect servers without the need to store long-lived credentials. (#1287)\n\n* Upgraded to use `v1` APIs for deploying to Connect servers, which enables\n  new features for specifying settings in the manifest file. (#1280)\n\n* Removed support for log streaming from shinyapps.io due to loss of support\n  for this feature on the shinyapps.io platform (`showLogs(streaming = TRUE)`).\n  If this feature is important to your workflow, please file an issue and we\n  will consider reintroduction of log streaming via rsconnect in Connect Cloud.\n  (#1292)\n\n* Removed several functions, including `addConnectServer()` and\n  `discoverServer()`, as well as HTTP backends other than libcurl,\n  which were deprecated in rsconnect 1.0.0. (#1282)\n\n# rsconnect 1.7.0\n\n* Added support for deploying from `manifest.json` files created by\n  `writeManifest()`: use the `manifestPath` argument of `deployApp()` and related\n  functions to specify the path to an existing manifest file. (#1259)\n\n* `urlEncode()` now uses `curl::curl_escape()` instead of `RCurl::curlEscape()`,\n  as RCurl is a Suggests dependency. (#1265)\n\n* The `User-Agent` header in requests made from rsconnect will now be of the\n  format `RSConnect/x.y.z` instead of `rsconnect/x.y.z` in order to satisfy web\n  application firewalls that enforce Pascal case.\n\n# rsconnect 1.6.2\n\n* Fix an opaque error when creating a manifest using Python <= 3.10 with a\n  version requirement in a `pyproject.toml` file. A warning is shown rather\n  than an error when the tomllib package is not present. (#1226)\n\n* Address CRAN test failures caused by some openssl configurations. (#1255)\n\n# rsconnect 1.6.1\n\n* Fix account registration from RStudio. (#1250)\n\n* SPCS/Snowflake authentication supports Connect API keys for user\n  identification. The `connectSPCSUser()` function now requires an `apiKey`\n  parameter, and the API key is included in the `X-RSC-Authorization` header\n  alongside Snowflake token authentication. This aligns with updated Connect\n  server requirements where Snowflake tokens provide proxied authentication\n  while API keys identify users to the Connect server itself.\n\n# rsconnect 1.6.0\n\n* Support deploying to Posit Connect Cloud. Use `connectCloudUser()` to add\n  Connect Cloud credentials.\n\n* `rsconnect` now sets the `rsconnect.max.bundle.size` and\n  `rsconnect.max.bundle.files` options to their default values on startup\n  if they have not yet been set. (#1204)\n\n* The default `rsconnect.max.bundle.size` limit has increased to 5 GiB. (#1200)\n\n* `getLogs()` returns log lines for a shinyapps.io hosted application. (#1209)\n\n* Python environment inspection errors include the path to the target Python\n  binary. (#1207)\n\n* Improve cookie expiration date handling. (#1212)\n\n* Improve documentation and advice for `deployApp(envVars...)`.\n\n* Removed support for publishing to Posit Cloud. (#1215)\n\n  Existing Posit Cloud account records may be removed by using\n  `removeAccount(\"yourname\", \"posit.cloud\")`.\n\n  Existing Posit Cloud deployment records may be removed by using\n  `forgetDeployment(name=\"deployment\", account=\"yourname\", server=\"posit.cloud\")`.\n\n* Removed the Posit Cloud-exclusive `space` argument from `deployApp()`. (#1215)\n\n# rsconnect 1.5.1\n\n* Address user registration for Posit Connect deployments hosted in Snowpark\n  Container Services when there is more than one configured Snowflake\n  connection. (#1189)\n\n* Process cookie expiration dates in addition to the cookie max-age. Some\n  servers return already-expired cookies. (1187)\n\n* Removed unused internal methods from Connect client. (#1182)\n\n# rsconnect 1.5.0\n\n* Functions for interacting with Posit Connect deployments in\n  Snowpark Container Services are now provided by the snowflakeauth package.\n\n# rsconnect 1.4.2\n\n* Address duplicate certificate errors on macOS with newer curl. (#1175)\n\n# rsconnect 1.4.1\n\n* Fixed processing error during server validation, which prevented\n  registration of new Connect accounts. (#1166)\n\n* When waiting for initial Connect account authorization, allow HTTP 401\n  responses. (#1167)\n\n# rsconnect 1.4.0\n\n* Content directories with a period in their name are no longer treated as a\n  document path when computing the location for deployment records. (#1138)\n\n* Quarto documents which specify a server must include executable code or an\n  engine declaration. (#1145)\n\n* Fixed errors when analyzing Quarto documents containing long chunks. (#1114)\n\n* A `_server.yml` file indicates that the content is an API. (#1144)\n\n* Expand tilde when resolving the `rsconnect.ca.bundle` option. (#1152)\n\n* Added support for interaction with Posit Connect deployments\n  hosted in Snowpark Container Services.\n\n* Introduced detection of required R interpreter version based on\n  `DESCRIPTION` file and `renv.lock` file. This setting is inserted\n  into the manifest as `environment.r.requires`.\n\n* Introduced detection of required Python interpreter version based on\n  project files `.python-version`, `pyproject.toml` and `setup.cfg`.\n  This setting is inserted into the manifest as `environment.python.requires`.\n\n# rsconnect 1.3.4\n\n* Use base64 encoded test data. Addresses CRAN test failures when run with\n  newer libssl. (#1130)\n\n# rsconnect 1.3.3\n\n* Avoid \"legacy\" time zone names in tests, as they are not available by\n  default in all environments. Addresses CRAN test failures. (#1115)\n\n# rsconnect 1.3.2\n\n* Primary Quarto document detection only considers `.R`, `.Rmd`, and `.qmd` as\n  end-of-file extensions. Previously, a file with `.R` elsewhere in its name,\n  such as `.Rprofile`, was incorrectly considered. (#1106)\n\n* Use the repository name identified by renv when `available.packages()` does\n  not enumerate the package, which occurs for archived packages. (#1110)\n\n* Remove remaining directory layout validation check. (#1102)\n\n* Use the public Connect server API endpoint `/v1/tasks/{id}` to poll task\n  progress. (#1088)\n\n# rsconnect 1.3.1\n\n* Skip tests when packages \"foreign\" and \"MASS\" are not available. (#1081)\n\n# rsconnect 1.3.0\n\n* `deployApp(logLevel = \"quiet\")` suppresses Posit Connect deployment task\n  output. (#1051)\n\n* `deployApp(logLevel = \"quiet\")` and `writeManifest(quiet=TRUE)` suppress\n  output when using renv to analyze dependencies. (#1051)\n\n* `deployApp()` and `writeManifest()` receive the default value for the\n  `image` argument from the `RSCONNECT_IMAGE` environment variable. (#1063)\n\n* `deployTF()` can deploy a TensorFlow model to Posit Connect. Requires Posit\n  Connect 2024.05.0 or higher.\n\n* Skip tests when suggested packages are not available. Skip Quarto tests when\n  run by CRAN. (#1074)\n\n# rsconnect 1.2.2\n\n* Use internally computed SHA1 sums and PKI signing when SHA1 is disabled\n  in FIPS mode. (#768, #1054)\n\n* Allow Quarto content with a rendered script as its primary target. (#1055)\n\n# rsconnect 1.2.1\n\n* Restore the `LC_TIME` locale after computing an RFC-2616 date. (#1035)\n* Address a problem inspecting Quarto content when the file names and paths\n  needed to be quoted. The resulting manifest lacked information about the\n  Quarto runtime, which caused difficult-to-understand deployment errors.\n  (#1037)\n* Produce an error when Quarto content cannot be inspected. (#1032)\n\n# rsconnect 1.2.0\n\n* Addressed a number of republishing and collaboration issues where the\n  content was incorrectly published to a new location rather than reusing an\n  existing deployment. (#981, #1007, #1013, #1019)\n\n* `showLogs()`, `configureApp()`, `setProperty()`, and `unsetProperty()`\n  search for the application by name when there are no matching deployment\n  records. (#985, #989)\n\n* `rpubsUpload()` correctly records the initial RPubs destination, allowing\n  republishing. (#976)\n\n* `deployApp()` and friends record multi-value `metadata` entries as\n  comma-separated values. (#1017)\n\n* `accountInfo()` includes `name` and `username` fields. Older versions of\n  rsconnect store account records with a `username` field. Recent rsconnect\n  versions record `name`. Both `name` and `username` should contain the same\n  value. (#1024)\n\n# rsconnect 1.1.1\n\n* Added `space` parameter to deploy directly to a space in Posit Cloud.\n\n* Improve reporting of errors returned by shinyapps.io. (#997)\n\n* Remove most directory layout validation checks. (#998)\n\n* Do not use `getOption(\"available_packages_filters\")` option when calling\n  `available.packages()`. (#1002)\n\n* Packages installed from source within an renv project are not associated\n  with repositories. (#1004)\n\n# rsconnect 1.1.0\n\n* Fixed analysis of directories that were smaller than the\n  `rsconnect.max.bundle.files=10000` limit but larger than the\n  `renv.config.dependencies.limit=1000` limit. (#968)\n\n* Ignore `.env`, `.venv`, and `venv` files only when they reference Python\n  virtual environments. (#972)\n\n* `deployApp()` and `writeManifest()` accept optional `envManagement`,\n  `envManagementR`, and `envManagementPy` arguments. These args specify whether\n  Posit Connect should install packages in the package cache.\n  If `envManagement` is `FALSE` then Connect will not perform any package\n  installation and it is the administrator's responsibility to ensure the\n  required R/Python packages are available in the runtime environment. This is\n  especially useful if off-host execution is enabled, when the execution\n  environment (specified by the `image` argument) already contains the required\n  packages. These values are ignored when\n  `Applications.ManifestEnvironmentManagementSelection = false`.\n  Requires Posit Connect `>=2023.07.0`. (#977)\n\n* Fix account discovery by `showProperties()`. (#980)\n\n# rsconnect 1.0.2\n\n* Fixed redeployments to shinyapps.io where `appName` is provided, but no local\n  record of the deployment exists. (#932)\n\n* `deployApp()` and `writeManifest()` now error if your library and `renv.lock`\n  are out-of-sync. Previously it always used what was defined in the `renv.lock`\n  but that was (a) slow and (b) could lead to different results than what you\n  see when running locally (#930).\n\n* Deploying from an renv project includes the `renv.lock` in the bundle. A\n  manifest created for an renv project references the `renv.lock` in the\n  `manifest.json`. (#926)\n\n* Use the environment variable `RSCONNECT_PACKRAT` to analyze dependencies\n  using packrat, as was done prior to rsconnect-1.0.0. Use of the\n  `rsconnect.packrat` option is discouraged, as it is not effective when using\n  push-button deployment in the RStudio IDE. (#935)\n\n* The `renv.lock` is ignored when the `RSCONNECT_PACKRAT` environment variable\n  or the `rsconnect.packrat` option is set. (#936)\n\n* The content type is inferred by analyzing the set of top-level files. (#942)\n\n* `deployApp()` and `writeManifest()` accept an optional `appMode` argument.\n  Provide this argument if your project includes auxiliary files which mislead\n  the existing `appMode` inference. For example, if an HTML project includes\n  a downloadable Shiny `app.R`, that content will be assumed to be a Shiny\n  application even if that application is not meant to be run. (#948)\n\n* `appDependencies()` accepts an `appFileManifest` argument as an alternate\n  way of providing the target set of files.\n\n# rsconnect 1.0.1\n\n* `deployDoc()` includes `.Rprofile`, `requirements.txt` and `renv.lock` when\n  deploying `.Rmd` or `.qmd`. These additional files are not included with\n  rendered HTML documents. (#919)\n\n* Explicit renv dependencies are preserved. (#916)\n\n# rsconnect 1.0.0\n\n## New features\n\n* `deployApp()` and `deployDoc()` now support deploying static content to Posit\n  Cloud. Static RMarkdown and Quarto content can be rendered server-side.\n\n* rsconnect requires renv 1.0.0.\n\n* `deployApp()` and `writeManifest()` now respect renv lock files, if present.\n  If you don't want to use these lockfiles, and instead return the previous\n  behaviour of snapshotting on every deploy, add your `renv.lock` to\n  `.rscignore` (#671). Learn more `?appDependencies()`.\n\n  Additionally, `deployApp()` and `writeManifest()` now use renv to capture app\n  dependencies,  rather than packrat. If this causes a previously working deploy\n  to fail, please file an issue then set `options(rsconnect.packrat = TRUE)` to\n  revert to the previous behaviour.\n\n* `deployApp()`'s `quarto` argument now takes values `TRUE`, `FALSE` or\n  `NA`. The previous value (a path to a quarto binary) is now ignored,\n  and instead we automatically figure out the package from `QUARTO_PATH` and\n  `PATH` env vars (#658). `deploySite()` now supports quarto websites (#813).\n\n* `deployApp()` gains a new `envVars` argument which takes a vector of the\n  names of environment variables that should be securely copied to the server.\n  The names (not values) of these environment variables are also saved in the\n  deployment record and will be updated each time you re-deploy the app (#667).\n  This currently only works with Connect, but we hope to add support to\n  Posit cloud and shinyapps.io in the future.\n\n* rsconnect gains two new functions for understanding and updating the\n  environment variables that your apps currently use. `listServerEnvVars()`\n  will return a data frame of applications, with a `envVars` list-column\n  giving the names of the environment variables used by each application.\n  `updateServerEnvVars()` will update all applications that use a specific\n  environment variable with the current value of that environment variable\n  (#667).\n\n## Lifecycle changes\n\n* Non-libcurl `rsconnect.http` options have been deprecated. This allows us to\n  focus our efforts on a single backend, rather than spreading development\n  efforts across five. The old backends will remain available for at least 2\n  years, but if you are using them because libcurl doesn't work for you, please\n  report the problem ASAP so we can fix it.\n\n* `addConnectServer()` has been deprecated because it does the same\n  thing as `addServer()` now that `addServer()` also validates URLs.\n\n* `deployTFModel()` is defunct. Posit Connect no longer supports hosting of\n  TensorFlow Model APIs. A TensorFlow model can be deployed as a [Plumber\n  API](https://tensorflow.rstudio.com/guides/deploy/plumber.html), [Shiny\n  application](https://tensorflow.rstudio.com/guides/deploy/shiny), or other\n  supported content type.\n\n* `discoverServer()` has been deprecated; it never worked.\n\n* `deployApp(\"foo.Rmd\")` has been deprecated. It was never documented, and\n  it does the same job as `deployDoc()` (#698).\n\n## Minor improvements and bug fixes\n\n* New `rsconnect.http.headers` and `rsconnect.http.cookies` allow you to\n  set extra arbitrary additional headers/cookies on each request (#405).\n  Their use is documented in the new `vignette(\"custom-http\")`.\n\n* Uploading large files to RPubs works once more (#450).\n\n* When recording details about deployments to Posit Cloud, appId now represents\n  the content id (as seen in URLs of the format\n  `https://posit.cloud/content/{id}`) instead of the application id.\n\n* Deployment records no longer contain the time the app was deployed (`when`)\n  or when it's metadata was last synced (`lastSyncTime`) as these variables\n  are not very useful, and they lead to uninteresting diffs if you have\n  committed the deployment records to git (#770). A `version` field has been\n  added to deployment DCF files to facilitate future file format changes, if\n  needed. Its value for this release is `1`.,\n\n* `accounts()` returns a zero-row data frame if no accounts are registered.\n\n* `accountInfo()` and `removeAccount()` no longer require `account` be\n  supplied (#666).\n\n* `accountInfo()` and `servers()` redact sensitive information (secrets,\n  private keys, and certificates) to make it hard to accidentally reveal\n  such information in logs (#675).\n\n* `addServer()` includes the port in the default server name, if present.\n\n* `appDependencies()` includes implicit dependencies, and returns an additional\n  column giving the Repository (#670). Its documentation contains more\n  information about how dependency discovery works, and how you can control\n  it, if needed.\n\n* `applications()` now returns the application title, if available (#484),\n  and processes multiple pages of results from a Connect server (#860).\n\n* `connectApiUser()` now clearly requires an `apiKey` (#741).\n\n* `deployApp()` output has been thoroughly reviewed and tweaked. As well as\n  general polish it now gives you more information about what it has discovered\n  about the deployment, like the app name, account & server, and which files\n  are included in the bundle (#669).\n\n* `deployApp()` is more aggressive about saving deployment data, which should\n  make it less likely that you need to repeat yourself after a failed\n  deployment. In particular, it now saves both before and after uploading the\n  contents (#677) and it saves when you're updating content originally created\n  by someone else (#270).\n\n* `deployApp()` now gives an actionable error if you attempt to set\n  visibility of an app deployed to posit.cloud (#838).\n\n* `deployApp()` now uses a stricter policy for determining whether or not\n  a locally installed package can be successfully installed on the deployment\n  server. This means that you're more likely to get a clean failure prior to\n  deployment (#659).\n\n* `deployApp()` will now detect if you're attempting to publish to an app\n  that has been deleted and will prompt you to create a new app (#226).\n\n* `deployApp()` includes some new conveniences for large uploads including\n  reporting the size of the bundle you're uploading and showing a progress bar\n  in interactive sessions  (#754).\n\n* `deployApp()` now follows redirects, which should make it more robust to your\n  server moving to a new url (#674).\n\n* `deployApp()` uses simpler logic for determining whether it should create a\n  new app or update an existing app. Now `appName`, `account`, and `server` are\n  used to find existing deployments. If none are found, it will create a new\n  deployment; if one is found, it'll be updated; if more than one are found, it\n  will prompt you to disambiguate (#666).\n\n* `deployApp()` improves account resolution from `account` and `server`\n  arguments by giving specific recommendations on the values that you might use\n  in the case of ambiguity or lack of matches (#666). Additionally, you'll now\n  receive a clear error if you accidentally provide something other than a\n  string or `NULL` to these arguments.\n\n* `deployApp()` now generates an interactive prompt to select `account`/`server`\n  (if no previous deployments) or `appName`/`account`/`server` (if multiple\n  previous deployments) (#691).\n\n* `deployApp()` now advertises which startup scripts are run at the normal\n  `logLevel`, and it evaluates each script in its own environment (#542).\n\n* `deployApp()` now derives `appName` from `appDir` and `appPrimaryDoc`,\n  never using the title (#538). It now only simplifies the path if you are\n  publishing to shinyapps.io, since its restrictions on application names are\n  much tighter than those of Posit Connect.\n\n* `deployApp()` will now warn if `appFiles` or `appManifestFiles` contain\n  files that don't exist, rather than silently ignoring them (#706).\n\n* `deployApp()` excludes temporary backup files (names starting or ending\n  with `~`) when automatically determining files to bundle (#111) as well as\n  directories that are likely to be Python virtual environments (#632).\n  Additionally, ignore rules are always now applied to all directories;\n  previously some (like `.Rproj.user` and `\"manifest.json\"`) were only\n  applied to the root directory. It correctly handles `.rscignore`  files\n  (i.e. as documented) (#568).\n\n* `deployApp(appSourceDoc)` has been deprecated; it did the same job as\n  `recordDir`.\n\n* `deployDoc()` includes a `.Rprofile` in the bundle, if one is found in the\n  same directory as the document.\n\n* `lint()` should have fewer false positives for path problems:\n  the relative path linter has been removed (#244) and the case-sensitive\n  linter now only checks strings containing a `/` (#611).\n\n* New `listDeploymentFiles()`, which supsersedes `listBundleFiles()`.\n  It now errors when if the bundle is either too large or contains too many\n  files, rather than silently truncating as before (#684).\n\n* `serverInfo()` and `removeServer()` no longer require a `server` when\n  called interactively.\n\n* `showMetrics()` once again returns a correctly named data frame (#528).\n\n* Removed Rmd generation code (`writeRmdIndex()`) which had not worked, or\n  been necessary, for quite some time (#106, #109).\n\n* Locale detection has been improved on windows (#233).\n\n* The `rsconnect.pre.deploy` and `rsconnect.post.deploy` hooks are now always\n  called with the content directory, not sometimes the path to a specific file\n  (#696).\n\n* Functions that should only interact with shinyapps.io enforce the server\n  type. Updated `addAuthorizedUser()`, `removeAuthorizedUser()`,\n  `showUsers()`, `showInvited()`, `resendInvitation()`, `configureApp()`,\n  `setProperty()`, `unsetProperty()`, `purgeApp()`, `restartApp()`,\n  `terminateApp()`, `showUsage()`, and `showMetrics()` (#863, #864).\n\n* When needed packages are not installed, and you're in an interactive\n  environment, rsconnect will now prompt you to install them (#665).\n\n* The confirmation prompt presented upon lint failures indicates \"no\" as its\n  default. (#652)\n\n# rsconnect 0.8.29\n\n* Introduced support for publishing to Posit Cloud. This feature is currently\n  in closed beta and requires access to an enabled account on Posit Cloud.\n  See [Posit Cloud's Announcement](https://posit.cloud/learn/whats-new#publishing)\n  for more information and to request access.\n\n* Update company and product names for rebranding to Posit.\n\n# rsconnect 0.8.28\n\n* Shiny applications and Shiny documents no longer include an implicit\n  dependency on [`ragg`](https://ragg.r-lib.org) when that package is present\n  in the local environment. This reverts a change introduced in 0.8.27.\n\n  Shiny applications should add an explicit dependency on `ragg` (usually with\n  a `library(\"ragg\")` statement) to see it used by `shiny::renderPlot` (via\n  `shiny::plotPNG`).\n\n  The documentation for `shiny::plotPNG` explains the use of `ragg`. (#598)\n\n* Fix bug that prevented publishing or writing manifests for non-Quarto content\n  when a Quarto path was provided to the `quarto` argument of `writeManifest()`,\n  `deployApp()`, and related functions.\n\n* Escape account names when performing a directory search to determine an\n  appropriate server. (#620)\n\n# rsconnect 0.8.27\n\n* Quarto content will no longer silently deploy as R Markdown content when\n  Quarto metadata is missing or cannot be gathered. Functions will error,\n  requesting the path to a Quarto binary in the `quarto` argument. (#594)\n* Fix typo for `.rscignore`. (#599)\n* Quarto deployments specifying only an `appDir` and `quarto` binary but not an\n  `appPrimaryDoc` work more consistently. A directory containing a `.qmd` file\n  will deploy as Quarto content instead of failing, and a directory containing\n  an `.Rmd` file will successfully deploy as Quarto content instead of falling\n  back to R Markdown. (#601)\n* If the `ragg` package is installed locally, it is now added as an implicit\n  dependency to `shiny` apps since `shiny::renderPlot()` now uses it by default\n  (when available). This way, `shiny` apps won't have to add `library(ragg)` to\n  get consistent (higher-quality) PNG images when deployed. (#598)\n\n# rsconnect 0.8.26\n\n* Add ability to resend shinyapps.io application invitations (#543)\n* Show expiration status in shinyapps.io for invitations (#543)\n* Allow shinyapps.io deployments to be private at creation time (#403)\n* Update the minimum `openssl` version to 2.0.0 to enable publishing for users\n  on FIPS-compliant systems without the need for API keys. (#452)\n* Added Quarto support to `writeManifest`, which requires passing the absolute\n  path to a Quarto executable to its new `quarto` parameter\n* Added `quarto` parameter to `deployApp` to enable deploying Quarto documents\n  and websites by supplying the path to a Quarto executable\n* Added support for deploying Quarto content that uses only the `jupyter` runtime\n* Added support for selecting a target `image` in the bundle manifest\n* The `showLogs` function takes a `server` parameter. (#57)\n* Added the `rsconnect.tar` option, which can be used to specify the path to a\n  `tar` implementation instead of R's internal implementation. The previous\n  method, using the `RSCONNECT_TAR` environment variable, still works, but the\n  new option will take precedence if both are set.\n\n# rsconnect 0.8.25\n\n* Use the `curl` option `-T` when uploading files to avoid out of memory\n  errors with large files. (#544)\n* The `rsconnect.max.bundle.size` and `rsconnect.max.bundle.files` options are\n  enforced when processing an enumerated set of files. Previously, these\n  limits were enforced only when bundling an entire content directory. (#542)\n* Preserve file time stamps when copying files into the bundle staging\n  directory, which then propagates into the created tar file. (#540)\n* Configuration directories align with CRAN policy and use the location named\n  by `tools::R_user_dir`. Configuration created by earlier versions of this\n  package is automatically migrated to the new location. (#550)\n\n# rsconnect 0.8.24\n\n* Added support for publishing Quarto documents and websites\n* Added support for `.rscignore` file to exclude files or directories from publishing (#368)\n* Fixed issue causing missing value errors when publishing content containing filenames with extended characters (#514)\n* Fixed issue preventing error tracebacks from displaying (#518)\n\n# rsconnect 0.8.18\n\n* Fixed issue causing configuration directory to be left behind after `R CMD CHECK`\n* Fixed incorrect subdirectory nesting when storing configuration in `R_USER_CONFIG_DIR`\n* Added linter for different-case Markdown links (#388)\n* Use new Packrat release on CRAN, 0.6.0 (#501)\n* Fix incorrect linter messages referring to `shiny.R` instead of `server.R` (#509)\n* Warn, rather than err, when the repository URL for a package dependency\n  cannot be validated. This allows deployment when using archived CRAN\n  packages, or when using packages installed from source that are available on\n  the server. (#508)\n* Err when the app-mode cannot be inferred; seen with empty directories/file-sets (#512)\n* Add `verbose` option to `writeManifest` utility (#468)\n\n# rsconnect 0.8.17\n\n* Fixed issue where setting `options(rsconnect.http.trace.json = TRUE)` could cause deployment errors with some HTTP transports (#490)\n* Improve how large bundles (file size and count) are detected (#464)\n* The `RSCONNECT_TAR` environment variable can be used to select the tar implementation used to create bundles (#446)\n* Warn when files are owned by users or groups with long names, as this can cause the internal R tar implementation to produce invalid archives (#446)\n* Add support for syncing the deployment metadata with the server (#396)\n* Insist on ShinyApps accounts in `showUsers()` (#398)\n* Improve the regex used for the browser and browseURL lints to include a word boundary (#400)\n* Fixed bug where `connectApiUser()` did not set a user id (#407)\n* New arguments to `deployApp` to force the generation of a Python environment file or a `requirements.txt` file (#409)\n* Fail when no repository URL is available for a dependent package (#410)\n* Fix error when an old version of a package is installed and a current version isn't available (#431, #436)\n* Fix error where packages couldn't be found with nonstandard contrib URLs. (#451, #457)\n* Improve detection of Shiny R Markdown files when `server.R` is present (#461)\n* Fix failure to write manifest when package requires a newer R version than the active version (#467)\n* Increase default HTTP timeout on non-Windows platforms (#476)\n* Require `packrat` 0.5 or later (#434)\n* Fix error when handling empty application / content lists (#417, #395)\n* Calls to `writeManifest()` no longer reference `packrat` files in the generated `manifest.json`. The `packrat` entries were transient and only existed while computing dependencies. (#472)\n* Fix `applications` when ShinyApps does not return `size` details (#496)\n* GitLab is seen as a valid SCM source (#491)\n\n# rsconnect 0.8.16\n\n* Prevent attempts to deploy Connect applications without uploading (#145)\n* Flag usage of `browser()` debugging calls when deploying (#196)\n* Prevent accidental deployment of Plumber APIs to shinyapps.io (#204)\n* Allow `appId` and other global deployment parameters to `deploySite` (#231)\n* Fix error when running `deployments()` without any registered accounts (#261)\n* Omit `renv` files from deployment bundle (#367)\n* Fix failure to deploy in Packrat projects (#370)\n* Fix issue deploying when a package exists in multiple repos (#372)\n* Honor `RETICULATE_PYTHON` when writing manifests (#374)\n* Add `on.failure` user hook to run a function when `deployApp()` fails (#375)\n* Fix error when showing non-streaming logs (#377)\n* Use internally computed MD5 sums when MD5 is disabled in FIPS mode (#378, #382)\n* Make it clearer which log entries are emitted by RStudio Connect (#385)\n* Add support for `requirements.txt` for Python, if it exists (#386)\n* Restore compatibility with R < 3.5 (#394)\n* Add support for authenticating with Connect via an API key rather than a token (#393)\n\n# rsconnect 0.8.15\n\n* Switch from **RCurl** to **curl** as the default HTTP backend (#325)\n* Add `purgeApp()` function to purge previously deployed shinyapps.io applications (#352)\n"
  },
  {
    "path": "R/.editorconfig",
    "content": "[*.R]\nindent_style = space\nindent_size = 2\nend_of_line = lf\nmax_line_length = 100\ninsert_final_newline = true\n"
  },
  {
    "path": "R/account-find.R",
    "content": "# Return a list containing the name and server associated with a matching account.\n#\n# Use `accountInfo()` and `findAccountInfo()` to load credentials associated with this account.\nfindAccount <- function(\n  accountName = NULL,\n  server = NULL,\n  error_call = caller_env()\n) {\n  check_string(\n    accountName,\n    allow_null = TRUE,\n    arg = \"account\",\n    call = error_call\n  )\n  check_string(server, allow_null = TRUE, call = error_call)\n\n  accounts <- accounts()\n  if (nrow(accounts) == 0) {\n    cli::cli_abort(\n      c(\n        \"No accounts registered.\",\n        i = \"To register an account, call {.fun rsconnect::connectCloudUser} (Posit Connect Cloud), \n        {.fun rsconnect::connectUser} (Posit Connect), \n        or {.fun rsconnect::setAccountInfo} (shinyapps.io).\"\n      ),\n      call = error_call\n    )\n  }\n\n  if (!is.null(accountName) && !is.null(server)) {\n    selected <- accounts$server == server & accounts$name == accountName\n    theseAccounts <- accounts[selected, , drop = FALSE]\n    if (nrow(theseAccounts) == 0) {\n      cli::cli_abort(\n        c(\n          \"Can't find account with {.arg name} = {.str {accountName}} and {.arg server} = {.str {server}}\",\n          i = \"Call {.fun accounts} to see available options.\"\n        ),\n        call = error_call\n      )\n    }\n  } else if (is.null(accountName) && !is.null(server)) {\n    selected <- accounts$server == server\n    theseAccounts <- accounts[selected, , drop = FALSE]\n    if (nrow(theseAccounts) == 0) {\n      cli::cli_abort(\n        c(\n          \"Can't find any accounts with {.arg server} = {.str {server}}.\",\n          i = \"Known servers are {.str {unique(accounts$server)}}.\"\n        ),\n        call = error_call\n      )\n    } else if (nrow(theseAccounts) > 1) {\n      cli::cli_abort(\n        c(\n          \"Found multiple accounts for {.arg server} = {.str {server}}.\",\n          \"Please disambiguate by setting {.arg account}.\",\n          i = \"Known account names are {.str {theseAccounts$name}}.\"\n        ),\n        call = error_call\n      )\n    }\n  } else if (!is.null(accountName) && is.null(server)) {\n    selected <- accounts$name == accountName\n    theseAccounts <- accounts[selected, , drop = FALSE]\n    if (nrow(theseAccounts) == 0) {\n      cli::cli_abort(\n        c(\n          \"Can't find any accounts with {.arg account} = {.str {accountName}}.\",\n          i = \"Available account names: {.str {unique(accounts$name)}}.\"\n        ),\n        call = error_call\n      )\n    } else if (nrow(theseAccounts) > 1) {\n      cli::cli_abort(\n        c(\n          \"Found multiple accounts for {.arg account} = {.str {accountName}}.\",\n          \"Please disambiguate by setting {.arg server}.\",\n          i = \"Available servers: {.str {theseAccounts$server}}.\"\n        ),\n        call = error_call\n      )\n    }\n  } else {\n    theseAccounts <- accounts\n\n    if (nrow(theseAccounts) > 1) {\n      if (is_interactive()) {\n        labels <- accountLabel(accounts$name, accounts$server)\n        choice <- cli_menu(\n          \"Found multiple accounts.\",\n          \"Which one do you want to use?\",\n          labels\n        )\n        theseAccounts <- theseAccounts[choice, , drop = FALSE]\n      } else {\n        cli::cli_abort(\n          c(\n            \"Found multiple accounts.\",\n            \"Please disambiguate by setting {.arg server} and/or {.arg account}.\",\n            i = \"Available servers: {.str {unique(theseAccounts$server)}}.\",\n            i = \"Available account names: {.str {unique(theseAccounts$name)}}.\"\n          ),\n          call = error_call\n        )\n      }\n    }\n  }\n  as.list(theseAccounts)\n}\n"
  },
  {
    "path": "R/accounts.R",
    "content": "#' Account Management Functions\n#'\n#' @description\n#' Functions to enumerate and remove accounts on the local system. Prior to\n#' deploying applications you need to register your account on the local system.\n#'\n#' Supported servers: All servers\n#'\n#' @details\n#' You register an account using the [setAccountInfo()] function (for\n#' ShinyApps) or [connectUser()] function (for other servers). You can\n#' subsequently remove the account using the `removeAccount` function.\n#'\n#' The `accounts` and `accountInfo` functions are provided for viewing\n#' previously registered accounts.\n#'\n#' @param name Name of account\n#' @param server Name of the server on which the account is registered\n#'   (optional; see [servers()])\n#'\n#' @return `accounts` returns a data frame with the names of all accounts\n#' registered on the system and the servers on which they reside.\n#' `accountInfo` returns a list with account details.\n#'\n#' @rdname accounts\n#' @export\naccounts <- function(server = NULL) {\n  configPaths <- accountConfigFiles(server)\n\n  names <- file_path_sans_ext(basename(configPaths))\n\n  servers <- basename(dirname(configPaths))\n  servers[servers == \".\"] <- \"shinyapps.io\"\n\n  data.frame(name = names, server = servers, stringsAsFactors = FALSE)\n}\n\n#' Register account on Posit Connect\n#\n#' @description\n#' `connectUser()` and `connectApiUser()` connect your Posit Connect account to\n#' the rsconnect package so that it can deploy and manage applications on\n#' your behalf.\n#'\n#' `connectUser()` is the easiest place to start because it allows you to\n#' authenticate in-browser to your Posit Connect server. `connectApiUser()` is\n#' appropriate for non-interactive settings; you'll need to copy-and-paste the\n#' API key from your account settings.\n#'\n#' Supported servers: Posit Connect servers\n#'\n#' @param account A name for the account to connect.\n#' @param server The server to connect to.\n#' @param launch.browser If true, the system's default web browser will be\n#'   launched automatically after the app is started. Defaults to `TRUE` in\n#'   interactive sessions only. If a function is passed, it will be called\n#'   after the app is started, with the app URL as a parameter.\n#' @param apiKey The API key used to authenticate the user\n#' @param quiet Whether or not to show messages and prompts while connecting the\n#'   account.\n#' @family Account functions\n#' @export\nconnectApiUser <- function(\n  account = NULL,\n  server = NULL,\n  apiKey,\n  quiet = FALSE\n) {\n  server <- findServer(server)\n  checkConnectServer(server)\n\n  user <- getAuthedUser(server, apiKey = apiKey)\n\n  registerAccount(\n    serverName = server,\n    accountName = account %||% user$username,\n    accountId = user$id,\n    apiKey = apiKey\n  )\n\n  if (!quiet) {\n    accountLabel <- accountLabel(user$username, server)\n    cli::cli_alert_success(\"Registered account for {accountLabel}\")\n  }\n  invisible()\n}\n\n\n#' Register account on Posit Connect in Snowpark Container Services\n#'\n#' @description\n#' `connectSPCSUser()` connects your Posit Connect account to the rsconnect\n#' package so it can deploy and manage applications on your behalf.\n#' Configure a\n#' [`connections.toml` file](https://docs.snowflake.com/en/developer-guide/snowflake-cli/connecting/configure-cli#location-of-the-toml-configuration-fil)\n#' in the appropriate location.\n#'\n#' SPCS deployments require both Snowflake authentication (via the connection\n#' name) and a Posit Connect API key. The Snowflake token provides proxied\n#' authentication to reach the Connect server, while the API key identifies\n#' the user to Connect itself.\n#'\n#' If `snowflakeConnectionName` is not provided, \\pkg{rsconnect} will attempt to\n#' use the default Snowflake connection from the `connections.toml` file,\n#' provided that the account matches the Connect server's URL.\n#'\n#' Supported servers: Posit Connect servers\n#'\n#' @inheritParams connectApiUser\n#' @param snowflakeConnectionName Name of the Snowflake connection in\n#'   `connections.toml` to use for authentication or `NULL` to use the default\n#'   (when applicable).\n#' @export\nconnectSPCSUser <- function(\n  account = NULL,\n  server = NULL,\n  apiKey,\n  snowflakeConnectionName = NULL,\n  quiet = FALSE\n) {\n  server <- findServer(server)\n  checkConnectServer(server)\n\n  serverUrl <- serverInfo(server)$url\n  snowflakeConnectionName <- snowflakeConnectionName %||%\n    getDefaultSnowflakeConnectionName(serverUrl)\n\n  user <- getSPCSAuthedUser(server, apiKey, snowflakeConnectionName)\n\n  registerAccount(\n    serverName = server,\n    accountName = account %||% user$username,\n    accountId = user$id,\n    apiKey = apiKey,\n    snowflakeConnectionName = snowflakeConnectionName\n  )\n\n  if (!quiet) {\n    accountLabel <- accountLabel(user$username, server)\n    cli::cli_alert_success(\"Registered account for {accountLabel}\")\n  }\n\n  invisible()\n}\n\ngetSPCSAuthedUser <- function(server, apiKey, snowflakeConnectionName) {\n  serverAddress <- serverInfo(server)\n  account <- list(\n    server = server,\n    apiKey = apiKey,\n    snowflakeConnectionName = snowflakeConnectionName\n  )\n\n  client <- clientForAccount(account)\n  client$currentUser()\n}\n\n#' @rdname connectApiUser\n#' @export\nconnectUser <- function(\n  account = NULL,\n  server = NULL,\n  quiet = FALSE,\n  launch.browser = getOption(\"rsconnect.launch.browser\", interactive())\n) {\n  server <- findServer(server)\n  checkConnectServer(server)\n\n  resp <- getAuthTokenAndUser(server, launch.browser)\n\n  registerAccount(\n    serverName = server,\n    accountName = account %||% resp$user$username,\n    accountId = resp$user$id,\n    token = resp$token$token,\n    private_key = resp$token$private_key\n  )\n\n  if (!quiet) {\n    accountLabel <- accountLabel(resp$user$username, server)\n    cli::cli_alert_success(\"Registered account for {accountLabel}\")\n  }\n  invisible()\n}\n\n# Filter accounts to those where the user has permission to create (i.e. publish) content.\nfilterPublishableAccounts <- function(accounts) {\n  Filter(\n    function(account) {\n      any(vapply(\n        account$permissions,\n        function(permission) {\n          identical(permission, \"content:create\")\n        },\n        logical(1)\n      ))\n    },\n    accounts\n  )\n}\n\n#' Register account on Posit Connect Cloud\n#\n#' @description\n#' `connectCloudUser()` connects your Posit Connect Cloud account to\n#' the rsconnect package so that it can deploy and manage applications on\n#' your behalf. It will open a browser window to authenticate, then prompt\n#' you to create an account or select an account to use if you have multiple.\n#'\n#' Supported servers: Posit Connect Cloud servers\n#'\n#' @param launch.browser If true, the system's default web browser will be\n#'   launched automatically after the app is started. Defaults to `TRUE` in\n#'   interactive sessions only. If a function is passed, it will be called\n#'   after the app is started, with the app URL as a parameter.\n#'\n#' @family Account functions\n#' @export\nconnectCloudUser <- function(launch.browser = TRUE) {\n  authClient <- cloudAuthClient()\n  deviceAuth <- authClient$createDeviceAuth()\n\n  verificationUriComplete <- addUtmParameters(\n    deviceAuth$verification_uri_complete\n  )\n\n  # Alert user and open browser for verification\n  if (isTRUE(launch.browser)) {\n    cli::cli_alert_info(\n      \"Opening login page - confirm the code entered matches the code: {deviceAuth$user_code}.\"\n    )\n    utils::browseURL(verificationUriComplete)\n  } else if (is.function(launch.browser)) {\n    cli::cli_alert_info(\n      \"Opening login page - confirm the code entered matches the code: {deviceAuth$user_code}.\"\n    )\n    launch.browser(verificationUriComplete)\n  } else {\n    cli::cli_alert_info(\n      \"Open {.url {verificationUriComplete}} to authenticate and confirm the code entered matches the code: {deviceAuth$user_code}.\"\n    )\n  }\n\n  tokenResponse <- waitForDeviceAuth(authClient, deviceAuth)\n  accessToken <- tokenResponse$access_token\n  refreshToken <- tokenResponse$refresh_token\n\n  client <- connectCloudClient(\n    parseHttpUrl(connectCloudUrls()$api),\n    list(accessToken = accessToken, refreshToken = refreshToken)\n  )\n\n  getAccounts <- function() {\n    accountsResponse <- tryCatch(\n      {\n        response <- client$getAccounts()\n        response$data\n      },\n      rsconnect_http_401 = function(err) {\n        if (err$errorType == \"no_user_for_lucid_user\") {\n          return(list())\n        }\n        stop(err)\n      }\n    )\n  }\n\n  accounts <- getAccounts()\n  accountsWhereUserCanPublish <- filterPublishableAccounts(accounts)\n  cloudUiUrl <- connectCloudUrls()$ui\n  if (length(accountsWhereUserCanPublish) == 0) {\n    if (length(accounts) == 0) {\n      accountCreationPage <- addUtmParameters(paste0(\n        cloudUiUrl,\n        \"/account/done\"\n      ))\n      if (isTRUE(launch.browser)) {\n        cli::cli_alert_info(\n          \"To deploy, you must first create an account. Opening account creation page...\"\n        )\n        utils::browseURL(accountCreationPage)\n      } else if (is.function(launch.browser)) {\n        cli::cli_alert_info(\n          \"To deploy, you must first create an account. Opening account creation page...\"\n        )\n        launch.browser(accountCreationPage)\n      } else {\n        cli::cli_alert_info(\n          \"To deploy, you must first create an account. Please go to {.url accountCreationPage} to create one.\"\n        )\n      }\n      # poll for account for up to 10 minutes\n      for (i in 1:300) {\n        Sys.sleep(2)\n        accounts <- getAccounts()\n        if (length(accounts) > 0) {\n          accountsWhereUserCanPublish <- filterPublishableAccounts(accounts)\n          if (length(accountsWhereUserCanPublish) > 0) {\n            break\n          }\n        }\n      }\n      if (length(accountsWhereUserCanPublish) == 0) {\n        cli::cli_abort(\n          \"Timed out waiting for an account to be created. Try again after creating a new account.\"\n        )\n      }\n    } else {\n      cli::cli_abort(\n        \"You do not have permission to publish content on any of your accounts. To publish, you may create a new account at {.url cloudUiUrl}.\"\n      )\n    }\n  }\n\n  # prompt the user to select an account if there's more than one they can publish to\n  if (length(accountsWhereUserCanPublish) > 1) {\n    cli::cli_alert_info(\"You have permission to publish to multiple accounts.\")\n    accountNames <- vapply(\n      accountsWhereUserCanPublish,\n      function(account) account$name,\n      character(1)\n    )\n    # selected <- utils::menu(accountNames, title = \"Select an account to use:\")\n    selected <- cli_menu(\n      \"Multiple accounts found.\",\n      \"Which account do you want to use?\",\n      accountNames\n    )\n    account <- accountsWhereUserCanPublish[[selected]]\n  } else {\n    account <- accountsWhereUserCanPublish[[1]]\n  }\n\n  registerAccount(\n    serverName = \"connect.posit.cloud\",\n    accountName = account$name,\n    accountId = account$id,\n    accessToken = accessToken,\n    refreshToken = refreshToken\n  )\n\n  cli::cli_alert_success(\"Registered account.\")\n}\n\n# Poll the server until the user has completed device authentication, returning\n# the token response once finished.\nwaitForDeviceAuth <- function(authClient, deviceAuth) {\n  pollingInterval <- deviceAuth$interval\n  while (TRUE) {\n    Sys.sleep(pollingInterval)\n    tokenResponse <- tryCatch(\n      authClient$exchangeToken(list(\n        grant_type = \"urn:ietf:params:oauth:grant-type:device_code\",\n        device_code = deviceAuth$device_code\n      )),\n      rsconnect_http_400 = function(err) {\n        errorCode <- err$body\n        if (errorCode == \"authorization_pending\") {\n          return(NULL)\n        } else if (errorCode == \"slow_down\") {\n          pollingInterval <<- pollingInterval + 5\n          return(NULL)\n        } else if (errorCode == \"expired_token\") {\n          cli::cli_abort(\"Verification code has expired.\")\n        } else if (errorCode == \"access_denied\") {\n          cli::cli_abort(\"Authorization request was denied.\")\n        }\n        cli::cli_abort(\"Error during authentication: {error_code}\")\n      }\n    )\n    if (!is.null(tokenResponse)) {\n      return(tokenResponse)\n    }\n  }\n}\n\n\ngetAuthTokenAndUser <- function(server, launch.browser = TRUE) {\n  token <- getAuthToken(server)\n\n  if (isTRUE(launch.browser)) {\n    utils::browseURL(token$claim_url)\n  } else if (is.function(launch.browser)) {\n    launch.browser(token$claim_url)\n  }\n\n  if (isFALSE(launch.browser)) {\n    cli::cli_alert_warning(\"Open {.url {token$claim_url}} to authenticate\")\n  } else {\n    cli::cli_alert_info(\n      \"A browser window should open to complete authentication\"\n    )\n    cli::cli_alert_warning(\n      \"If it doesn't open, please go to {.url {token$claim_url}}\"\n    )\n  }\n\n  user <- waitForAuthedUser(\n    server,\n    token = token$token,\n    private_key = token$private_key\n  )\n\n  list(\n    token = token,\n    user = user\n  )\n}\n\n# Used by the IDE\ngetAuthToken <- function(server, userId = 0) {\n  account <- list(server = server)\n  client <- clientForAccount(account)\n\n  # Check if we already have an API key for this server, in which case we can\n  # bypass the token auth flow.\n  serverUrl <- serverInfo(server)$url\n  cachedApiKey <- getCachedApiKey(serverUrl)\n  if (!is.null(cachedApiKey)) {\n    # Verify that the API key actually works.\n    user <- tryCatch(client$currentUser(), error = function(e) NULL)\n    if (!is.null(user)) {\n      # Return the API key as the \"token\" with a zero-length private key.\n      # waitForAuthedUser will use this to detect federated authentication later\n      # on.\n      #\n      # This in fairly awkward in-band signalling, but reflects the fact that we\n      # can't change what RStudio expects to happen here.\n      return(list(\n        token = cachedApiKey,\n        private_key = secret(\"\"),\n        # Open a special \"you're already authenticated\" page as the \"claim URL\".\n        claim_url = sub(\"/__api__$\", \"/connect/#/auth-success\", serverUrl),\n        # Signal to future RStudio versions that auth is already complete and\n        # there is no need to open a browser window.\n        authenticated = TRUE\n      ))\n    }\n  }\n\n  token <- generateToken()\n\n  # Send public key to server, and generate URL where the token can be claimed\n  response <- client$addToken(list(\n    token = token$token,\n    public_key = token$public_key,\n    user_id = 0L\n  ))\n\n  list(\n    token = token$token,\n    private_key = secret(token$private_key),\n    claim_url = response$token_claim_url\n  )\n}\n\n# generateToken generates a token for signing requests sent to the Posit\n# Connect service. The token's ID and public key are sent to the server, and\n# the private key is saved locally.\ngenerateToken <- function() {\n  key <- openssl::rsa_keygen(2048L)\n  priv.der <- openssl::write_der(key)\n  pub.der <- openssl::write_der(key$pubkey)\n  tokenId <- paste(c(\"T\", openssl::rand_bytes(16)), collapse = \"\")\n\n  list(\n    token = tokenId,\n    public_key = openssl::base64_encode(pub.der),\n    private_key = openssl::base64_encode(priv.der)\n  )\n}\n\nwaitForAuthedUser <- function(\n  server,\n  token = NULL,\n  private_key = NULL,\n  apiKey = NULL\n) {\n  # Detect when the \"token\" is actually an API key by looking for an empty\n  # secret.\n  if (!is.null(token) && !nzchar(private_key)) {\n    return(getAuthedUser(server, apiKey = token))\n  }\n\n  # keep trying to authenticate until we're successful; server returns\n  # 500 \"Token is unclaimed error\" (Connect before 2024.05.0)\n  # 401 \"Unauthorized\" occurs before the token has been claimed.\n  cli::cli_progress_bar(format = \"{cli::pb_spin} Waiting for authentication...\")\n\n  repeat {\n    for (i in 1:10) {\n      Sys.sleep(0.1)\n      cli::cli_progress_update()\n    }\n    user <- tryCatch(\n      getAuthedUser(\n        server,\n        token = token,\n        private_key = private_key,\n        apiKey = apiKey\n      ),\n      rsconnect_http_401 = function(err) NULL,\n      rsconnect_http_500 = function(err) NULL\n    )\n    if (!is.null(user)) {\n      cli::cli_progress_done()\n      break\n    }\n  }\n\n  user\n}\n\ngetAuthedUser <- function(\n  server,\n  token = NULL,\n  private_key = NULL,\n  apiKey = NULL\n) {\n  if (!xor(is.null(token) && is.null(private_key), is.null(apiKey))) {\n    cli::cli_abort(\n      \"Must supply either {.arg token} + {private_key} or {.arg apiKey}\"\n    )\n  }\n\n  account <- list(\n    server = server,\n    apiKey = apiKey,\n    token = token,\n    private_key = private_key\n  )\n  client <- clientForAccount(account)\n  client$currentUser()\n}\n\n#' Register account on shinyapps.io\n#'\n#' @description\n#' Configure a ShinyApps account for publishing from this system.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param name Name of account to save or remove\n#' @param token User token for the account\n#' @param secret User secret for the account\n#' @param server Server to associate account with.\n#'\n#' @examples\n#' \\dontrun{\n#'\n#' # register an account\n#' setAccountInfo(\"user\", \"token\", \"secret\")\n#'\n#' # remove the same account\n#' removeAccount(\"user\")\n#' }\n#'\n#' @family Account functions\n#' @export\nsetAccountInfo <- function(name, token, secret, server = \"shinyapps.io\") {\n  check_string(name)\n  check_string(token)\n  check_string(secret)\n  check_string(server)\n\n  accountId <- findShinyAppsAccountId(name, token, secret, server)\n\n  registerAccount(\n    serverName = server,\n    accountName = name,\n    accountId = accountId,\n    token = token,\n    secret = secret\n  )\n  invisible()\n}\n\n# A user can have multiple accounts, so iterate over all accounts looking\n# for one with the specified name\nfindShinyAppsAccountId <- function(\n  name,\n  token,\n  secret,\n  server,\n  error_call = caller_env()\n) {\n  if (secret == \"<SECRET>\") {\n    cli::cli_abort(\n      c(\n        \"You've copied and pasted the wrong thing.\",\n        i = \"Either click 'Show secret' or 'Copy to clipboard'.\"\n      ),\n      call = error_call\n    )\n  }\n\n  account <- list(token = token, secret = secret, server = server)\n  client <- clientForAccount(account)\n\n  userId <- client$currentUser()$id\n\n  accountId <- NULL\n  accounts <- client$accountsForUser(userId)\n  for (account in accounts) {\n    if (identical(account$name, name)) {\n      return(account$id)\n    }\n  }\n  cli::cli_abort(\n    \"Unable to determine {.arg accountId} for account {.str {name}}\"\n  )\n}\n\n#' @rdname accounts\n#' @family Account functions\n#' @export\naccountInfo <- function(name = NULL, server = NULL) {\n  findAccountInfo(name, server)\n}\n\n# Discovers then loads details about an account from disk.\n# Internal equivalent to accountInfo that lets callers provide error context.\nfindAccountInfo <- function(\n  name = NULL,\n  server = NULL,\n  error_call = caller_env()\n) {\n  fullAccount <- findAccount(name, server, error_call = error_call)\n  configFile <- accountConfigFile(fullAccount$name, fullAccount$server)\n\n  accountDcf <- read.dcf(configFile, all = TRUE)\n  info <- as.list(accountDcf)\n\n  # Account records previously had username, now have name. Internal callers expect \"name\", but\n  # external callers may expect \"username\". (#1024)\n  info$name <- info$name %||% info$username\n  info$username <- info$name\n\n  # remove all whitespace from private key\n  if (!is.null(info$private_key)) {\n    info$private_key <- gsub(\"[[:space:]]\", \"\", info$private_key)\n  }\n\n  # For standard Connect servers where there are no persisted credentials, try\n  # identity federation (added in v2026.01.0).\n  if (isConnectServer(fullAccount$server) && hasNoCredentials(info)) {\n    tryCatch(\n      {\n        serverUrl <- serverInfo(fullAccount$server)$url\n        info$apiKey <- attemptIdentityFederation(serverUrl)\n      },\n      error = function(e) NULL\n    )\n  }\n\n  # Hide credentials\n  info$private_key <- secret(info$private_key)\n  info$secret <- secret(info$secret)\n  info$apiKey <- secret(info$apiKey)\n  info$snowflakeToken <- secret(info$snowflakeToken)\n\n  info\n}\n\nhasAccount <- function(name, server) {\n  file.exists(accountConfigFile(name, server))\n}\n\n#' @rdname accounts\n#' @export\nremoveAccount <- function(name = NULL, server = NULL) {\n  fullAccount <- findAccount(name, server)\n\n  configFile <- accountConfigFile(fullAccount$name, fullAccount$server)\n  file.remove(configFile)\n\n  invisible(NULL)\n}\n\nregisterAccount <- function(\n  serverName,\n  accountName,\n  accountId,\n  token = NULL,\n  secret = NULL,\n  private_key = NULL,\n  apiKey = NULL,\n  snowflakeConnectionName = NULL,\n  accessToken = NULL,\n  refreshToken = NULL\n) {\n  check_string(serverName)\n  check_string(accountName)\n  if (!is.null(secret)) {\n    secret <- as.character(secret)\n  }\n\n  fields <- list(\n    name = accountName,\n    server = serverName,\n    accountId = accountId,\n    token = token,\n    secret = secret,\n    private_key = private_key,\n    apiKey = apiKey,\n    snowflakeConnectionName = snowflakeConnectionName,\n    accessToken = accessToken,\n    refreshToken = refreshToken\n  )\n\n  path <- accountConfigFile(accountName, serverName)\n  dir.create(dirname(path), recursive = TRUE, showWarnings = FALSE)\n  write.dcf(compact(fields), path, width = 100)\n\n  # set restrictive permissions on it if possible\n  if (identical(.Platform$OS.type, \"unix\")) {\n    Sys.chmod(path, mode = \"0600\")\n  }\n\n  path\n}\n\naccountLabel <- function(account, server) {\n  # Note: The incoming \"account\" may correspond to our local account name, which does not always\n  # match the remote username.\n  paste0(\"server: \", server, \" / username: \", account)\n}\n"
  },
  {
    "path": "R/appDependencies.R",
    "content": "#' Detect application dependencies\n#'\n#' @description\n#' `appDependencies()` recursively detects all R package dependencies for an\n#' application by parsing all `.R` and `.Rmd` files and looking for calls\n#' to `library()`, `require()`, `requireNamespace()`, `::`, and so on.\n#' It then adds implicit dependencies (i.e. an `.Rmd` requires Rmarkdown)\n#' and adds all recursive dependencies to create a complete manifest of\n#' package packages need to be installed to run the app.\n#'\n#' Supported servers: All servers\n#'\n#' # Dependency discovery\n#'\n#' rsconnect use one of three mechanisms to find which packages your application\n#' uses:\n#'\n#' 1. If `renv.lock` is present, it will use the versions and sources defined in\n#'    that file. If you're using the lockfile for some other purpose and\n#'    don't want it to affect deployment, add `renv.lock` to `.rscignore`.\n#'\n#' 2. Otherwise, rsconnect will call `renv::snapshot()` to find all packages\n#'    used by your code. If you'd instead prefer to only use the packages\n#'    declared in a `DESCRIPTION` file, run\n#'    `renv::settings$snapshot.type(\"explicit\")` to activate renv's \"explicit\"\n#'    mode.\n#'\n#' 3. Dependency resolution using renv is a new feature in rsconnect 1.0.0, and\n#'    while we have done our best to test it, it still might fail for your app.\n#'    If this happens, please [file an issue](https://github.com/rstudio/rsconnect/issues)\n#'    then set `options(rsconnect.packrat = TRUE)` to revert to the old\n#'    dependency discovery mechanism.\n#'\n#' # Remote installation\n#'\n#' When deployed, the app must first install all of these packages, and\n#' rsconnect ensures the versions used on the server will match the versions\n#' you used locally. It knows how to install packages from the following\n#' sources:\n#'\n#' * CRAN and BioConductor (`Source: CRAN` or `Source: Bioconductor`). The\n#'   remote server will ignore the specific CRAN or Bioconductor mirror that\n#'   you use locally, always using the CRAN/BioC mirror that has been configured\n#'   on the server.\n#'\n#' * Other CRAN like and CRAN-like repositories. These packages will have\n#'   a `Source` determined by the value of `getOptions(\"repos\")`. For example,\n#'   if you've set the following options:\n#'\n#'   ```R\n#'   options(\n#'      repos = c(\n#'        CRAN = \"https://cran.rstudio.com/\",\n#'        CORPORATE = \"https://corporate-packages.development.company.com\"\n#'      )\n#'   )\n#'   ```\n#'\n#'   Then packages installed from your corporate package repository will\n#'   have source `CORPORATE`. Posit Connect\n#'   [can be configured](https://docs.posit.co/connect/admin/appendix/configuration/#RPackageRepository)\n#'   to override their repository url so that (e.g.) you can use different\n#'   packages versions on staging and production servers.\n#'\n#' * Packages installed from GitHub, GitLab, or BitBucket, have `Source`\n#'   `github`, `gitlab`, and `bitbucket` respectively. When deployed, the\n#'   bundle contains the additional metadata needed to precisely recreated\n#'   the installed version.\n#'\n#' It's not possible to recreate the packages that you have built and installed\n#' from a directory on your local computer. This will have `Source: NA` and\n#' will cause the deployment to error. To resolve this issue, you'll need to\n#' install from one of the known sources described above.\n#'\n#' # Suggested packages\n#'\n#' The `Suggests` field is not included when determining recursive dependencies,\n#' so it's possible that not every package required to run your application will\n#' be detected.\n#'\n#' For example, ggplot2's `geom_hex()` requires the hexbin package to be\n#' installed, but it is only suggested by ggplot2. So if your app uses\n#' `geom_hex()` it will fail, reporting that the hexbin package is not\n#' installed.\n#'\n#' You can overcome this problem with (e.g.) `requireNamespace(hexbin)`.\n#' This will tell rsconnect that your app needs the hexbin package, without\n#' otherwise affecting your code.\n#'\n#' @inheritParams deployApp\n#' @returns A data frame with one row for each dependency (direct, indirect,\n#'   and inferred), and 4 columns:\n#'\n#'   * `Package`: package name.\n#'   * `Version`: local version.\n#'   * `Source`: a short string describing the source of the package install,\n#'      as described above.\n#'   * `Repository`: for CRAN and CRAN-like repositories, the URL to the\n#'      repository. This will be ignored by the server if it has been configured\n#'      with its own repository name -> repository URL mapping.\n#' @examples\n#' \\dontrun{\n#'\n#' # dependencies for the app in the current working dir\n#' appDependencies()\n#'\n#' # dependencies for an app in another directory\n#' appDependencies(\"~/projects/shiny/app1\")\n#' }\n#' @seealso [rsconnectPackages](Using Packages with rsconnect)\n#' @export\nappDependencies <- function(\n  appDir = getwd(),\n  appFiles = NULL,\n  appFileManifest = NULL,\n  appMode = NULL,\n  dependencyResolution = c(\"strict\", \"library\")\n) {\n  dependencyResolution <- match.arg(dependencyResolution)\n\n  appFiles <- listDeploymentFiles(appDir, appFiles, appFileManifest)\n  appMetadata <- appMetadata(appDir, appFiles = appFiles, appMode = appMode)\n  if (!needsR(appMetadata)) {\n    return(data.frame(\n      Package = character(),\n      Version = character(),\n      Source = character(),\n      Repository = character(),\n      stringsAsFactors = FALSE\n    ))\n  }\n\n  bundleDir <- bundleAppDir(\n    appDir = appDir,\n    appFiles = appFiles,\n    appMode = appMetadata$appMode\n  )\n  defer(unlink(bundleDir, recursive = TRUE))\n\n  extraPackages <- inferRPackageDependencies(appMetadata)\n  deps <- computePackageDependencies(\n    bundleDir,\n    extraPackages,\n    quiet = TRUE,\n    dependencyResolution = dependencyResolution\n  )\n  deps[c(\"Package\", \"Version\", \"Source\", \"Repository\")]\n}\n\nneedsR <- function(appMetadata) {\n  if (\n    appMetadata$appMode %in% c(\"static\", \"tensorflow-saved-model\", \"nodejs\")\n  ) {\n    return(FALSE)\n  }\n\n  # All non-Quarto content currently uses R by default.\n  # Currently R is only supported by the \"knitr\" engine, not \"jupyter\" or\n  # \"markdown\"\n  is.null(appMetadata$quartoInfo) ||\n    \"knitr\" %in% appMetadata$quartoInfo[[\"engines\"]]\n}\n\ninferRPackageDependencies <- function(appMetadata) {\n  deps <- switch(\n    appMetadata$appMode,\n    \"rmd-static\" = c(\"rmarkdown\", if (appMetadata$hasParameters) \"shiny\"),\n    \"quarto-static\" = \"rmarkdown\",\n    \"quarto-shiny\" = c(\"rmarkdown\", \"shiny\"),\n    \"rmd-shiny\" = c(\"rmarkdown\", \"shiny\"),\n    \"shiny\" = \"shiny\",\n    \"api\" = appMetadata$plumberInfo\n  )\n  if (appMetadata$documentsHavePython) {\n    deps <- c(deps, \"reticulate\")\n  }\n  deps\n}\n"
  },
  {
    "path": "R/appMetadata-quarto.R",
    "content": "# Called only when the content is known to be Quarto.\ninferQuartoInfo <- function(metadata, appDir, appPrimaryDoc) {\n  if (hasQuartoMetadata(metadata)) {\n    return(list(\n      version = metadata[[\"quarto_version\"]],\n      engines = metadata[[\"quarto_engines\"]]\n    ))\n  }\n\n  # If we don't yet have Quarto details, run quarto inspect ourselves\n  inspect <- quartoInspect(\n    appDir = appDir,\n    appPrimaryDoc = appPrimaryDoc\n  )\n  list(\n    version = inspect[[\"quarto\"]][[\"version\"]],\n    engines = I(inspect[[\"engines\"]])\n  )\n}\n\nhasQuartoMetadata <- function(x) {\n  !is.null(x$quarto_version)\n}\n\n# Run \"quarto inspect\" on the target and returns its output as a parsed object.\nquartoInspect <- function(appDir = NULL, appPrimaryDoc = NULL) {\n  # If \"quarto inspect appDir\" fails, we will try \"quarto inspect\n  # appPrimaryDoc\", so that we can support single files as well as projects.\n  quarto <- quarto_path()\n  if (is.null(quarto)) {\n    cli::cli_abort(c(\n      \"`quarto` not found.\",\n      i = \"Check that it is installed and available on your {.envvar PATH}.\"\n    ))\n  }\n\n  json <- suppressWarnings(\n    system2(\n      quarto,\n      c(\"inspect\", shQuote(appDir)),\n      stdout = TRUE,\n      stderr = TRUE\n    )\n  )\n  status <- attr(json, \"status\")\n\n  if (!is.null(status) && !is.null(appPrimaryDoc)) {\n    json <- suppressWarnings(\n      system2(\n        quarto,\n        c(\"inspect\", shQuote(file.path(appDir, appPrimaryDoc))),\n        stdout = TRUE,\n        stderr = TRUE\n      )\n    )\n    status <- attr(json, \"status\")\n  }\n\n  if (!is.null(status)) {\n    cli::cli_abort(\n      c(\n        \"Failed to run `quarto inspect` against your content:\",\n        json\n      )\n    )\n  }\n  jsonlite::fromJSON(sanitizeSystem2json(json))\n}\n\n# inlined from quarto::quarto_path()\nquarto_path <- function() {\n  path_env <- Sys.getenv(\"QUARTO_PATH\", unset = NA)\n  if (is.na(path_env)) {\n    path <- unname(Sys.which(\"quarto\"))\n    if (nzchar(path)) {\n      path\n    } else {\n      NULL\n    }\n  } else {\n    path_env\n  }\n}\n"
  },
  {
    "path": "R/appMetadata.R",
    "content": "appMetadata <- function(\n  appDir,\n  appFiles,\n  appPrimaryDoc = NULL,\n  quarto = NA,\n  appMode = NULL,\n  contentCategory = NULL,\n  isShinyappsServer = FALSE,\n  metadata = list()\n) {\n  check_bool(quarto, allow_na = TRUE)\n\n  # If quarto package/IDE has supplied metadata, always use quarto\n  # https://github.com/quarto-dev/quarto-r/blob/08caf0f42504e7/R/publish.R#L117-L121\n  # https://github.com/rstudio/rstudio/blob/3d45a20307f650/src/cpp/session/modules/SessionRSConnect.cpp#L81-L123\n  if (hasQuartoMetadata(metadata)) {\n    quarto <- TRUE\n  }\n\n  inferredPrimaryFile <- NULL\n  if (is.null(appMode)) {\n    # Generally we want to infer appPrimaryDoc from appMode, but there's one\n    # special case: RStudio provides appPrimaryDoc when deploying Shiny\n    # applications. They may have name.R, not app.R or server.R.\n    #\n    # This file is later renamed to app.R when deployed by bundleAppDir().\n    if (\n      !is.null(appPrimaryDoc) &&\n        tolower(tools::file_ext(appPrimaryDoc)) == \"r\"\n    ) {\n      appMode <- \"shiny\"\n    } else {\n      # Inference only uses top-level files\n      appModeResult <- inferAppMode(\n        appDir,\n        appFiles,\n        usesQuarto = quarto,\n        isShinyappsServer = isShinyappsServer\n      )\n      appMode <- appModeResult$appMode\n      inferredPrimaryFile <- appModeResult$primaryFile\n    }\n  }\n\n  appPrimaryDoc <- inferAppPrimaryDoc(\n    appPrimaryDoc = appPrimaryDoc,\n    appFiles = appFiles,\n    appMode = appMode\n  )\n  hasParameters <- appHasParameters(\n    appDir = appDir,\n    appPrimaryDoc = appPrimaryDoc,\n    appMode = appMode,\n    contentCategory = contentCategory\n  )\n  documentsHavePython <- detectPythonInDocuments(\n    appDir = appDir,\n    files = appFiles\n  )\n\n  if (appIsQuartoDocument(appMode)) {\n    quartoInfo <- inferQuartoInfo(\n      metadata = metadata,\n      appDir = appDir,\n      appPrimaryDoc = appPrimaryDoc\n    )\n    if (appMode == \"quarto-shiny\") {\n      if (!any(c(\"knitr\", \"jupyter\") %in% quartoInfo[[\"engines\"]])) {\n        cli::cli_abort(c(\n          \"The Quarto document requires a server but does not use an executable engine.\",\n          \"Consider including some executable code, specifying an engine, or removing the server configuration.\"\n        ))\n      }\n    }\n  } else {\n    quartoInfo <- NULL\n  }\n\n  plumberInfo <- NULL\n  if (appMode == \"api\") {\n    plumberInfo <- inferPlumberInfo(appDir)\n  }\n\n  nodejsInfo <- NULL\n  if (appMode == \"nodejs\") {\n    nodejsInfo <- inferNodejsInfo(appDir)\n    if (!nodejsInfo$hasLockfile) {\n      cli::cli_abort(c(\n        \"Node.js deployments require a {.file package-lock.json}.\",\n        \"i\" = \"Run {.code npm install} to generate one.\"\n      ))\n    }\n    if (!file.exists(file.path(appDir, nodejsInfo$entrypoint))) {\n      cli::cli_warn(c(\n        \"Entrypoint {.file {nodejsInfo$entrypoint}} not found in app directory.\",\n        \"i\" = \"Set the {.field main} field in {.file package.json} to your app\\'s entry file.\"\n      ))\n    }\n  }\n\n  list(\n    appMode = appMode,\n    appPrimaryDoc = appPrimaryDoc,\n    inferredPrimaryFile = inferredPrimaryFile,\n    hasParameters = hasParameters,\n    contentCategory = contentCategory,\n    documentsHavePython = documentsHavePython,\n    quartoInfo = quartoInfo,\n    plumberInfo = plumberInfo,\n    nodejsInfo = nodejsInfo\n  )\n}\n\n# Infer the mode of the application from included files. Most content types\n# only consider files at the directory root. TensorFlow saved models may be\n# anywhere in the hierarchy.\ninferAppMode <- function(\n  appDir,\n  appFiles,\n  usesQuarto = NA,\n  isShinyappsServer = FALSE\n) {\n  rootFiles <- appFiles[dirname(appFiles) == \".\"]\n  absoluteRootFiles <- file.path(appDir, rootFiles)\n  absoluteAppFiles <- file.path(appDir, appFiles)\n\n  matchingNames <- function(paths, pattern) {\n    idx <- grepl(pattern, basename(paths), ignore.case = TRUE, perl = TRUE)\n    paths[idx]\n  }\n\n  # plumber API\n  plumberFiles <- matchingNames(absoluteRootFiles, \"^(plumber|entrypoint).r$\")\n  if (length(plumberFiles) > 0) {\n    return(list(appMode = \"api\", primaryFile = basename(plumberFiles[1])))\n  }\n\n  # general API\n  server_yml <- matchingNames(absoluteRootFiles, \"^_server.ya?ml$\")\n  if (length(server_yml) > 0) {\n    return(list(appMode = \"api\", primaryFile = basename(server_yml[1])))\n  }\n\n  # Shiny application using single-file app.R style.\n  appR <- matchingNames(absoluteRootFiles, \"^app.r$\")\n  if (length(appR) > 0) {\n    return(list(appMode = \"shiny\", primaryFile = basename(appR[1])))\n  }\n\n  rmdFiles <- matchingNames(absoluteRootFiles, \"\\\\.rmd$\")\n  hasRmd <- length(rmdFiles) > 0\n  qmdFiles <- matchingNames(absoluteRootFiles, \"\\\\.qmd$\")\n  hasQmd <- length(qmdFiles) > 0\n  rFiles <- matchingNames(absoluteRootFiles, \"\\\\.r$\")\n  hasR <- length(rFiles) > 0\n  quartoYml <- matchingNames(absoluteRootFiles, \"^_quarto.y(a)?ml$\")\n  hasQuartoYml <- length(quartoYml) > 0\n\n  if (is.na(usesQuarto)) {\n    # Determine if the incoming content implies the need for Quarto.\n    #\n    # *.qmd files are enough of an indication by themselves.\n    # *.rmd and *.r files need a _quarto.yml file to emphasize the need for Quarto.\n    #\n    # Do not rely on _quarto.yml alone, as RStudio includes that file even when\n    # publishing HTML. https://github.com/rstudio/rstudio/issues/11444\n    usesQuarto <- (hasQmd ||\n      (hasQuartoYml && hasRmd) ||\n      (hasQuartoYml && hasR))\n  }\n\n  # Documents with \"server: shiny\" in their YAML front matter need shiny too\n  shinyRmdFiles <- NULL\n  if (length(rmdFiles) > 0) {\n    shinyRmdFiles <- rmdFiles[sapply(rmdFiles, isShinyRmd)]\n  }\n  shinyQmdFiles <- NULL\n  if (length(qmdFiles) > 0) {\n    shinyQmdFiles <- qmdFiles[sapply(qmdFiles, isShinyRmd)]\n  }\n\n  hasShinyRmd <- length(shinyRmdFiles) > 0\n  hasShinyQmd <- length(shinyQmdFiles) > 0\n\n  if (hasShinyQmd) {\n    return(list(\n      appMode = \"quarto-shiny\",\n      primaryFile = basename(shinyQmdFiles[1])\n    ))\n  } else if (hasShinyRmd) {\n    shinyRmdFile <- shinyRmdFiles[1]\n    if (usesQuarto) {\n      return(list(\n        appMode = \"quarto-shiny\",\n        primaryFile = basename(shinyRmdFile)\n      ))\n    } else {\n      return(list(appMode = \"rmd-shiny\", primaryFile = basename(shinyRmdFile)))\n    }\n  }\n\n  # Shiny application using server.R; checked later than Rmd with shiny runtime\n  # because server.R may contain the server code paired with a ShinyRmd and\n  # needs to be run by rmarkdown::run (rmd-shiny).\n  serverR <- matchingNames(absoluteRootFiles, \"^server.r$\")\n  if (length(serverR) > 0) {\n    return(list(appMode = \"shiny\", primaryFile = basename(serverR[1])))\n  }\n\n  # Any non-Shiny R Markdown or Quarto documents are rendered content and get\n  # rmd-static or quarto-static.\n  if (length(rmdFiles) > 0 || length(qmdFiles) > 0) {\n    # Prefer qmd files over rmd files for primary file selection\n    primaryDocFile <- if (length(qmdFiles) > 0) qmdFiles[1] else rmdFiles[1]\n    if (usesQuarto) {\n      return(list(\n        appMode = \"quarto-static\",\n        primaryFile = basename(primaryDocFile)\n      ))\n    } else {\n      # For shinyapps.io, treat \"rmd-static\" app mode as \"rmd-shiny\" so that\n      # it can be served from a shiny process in Connect\n      if (isShinyappsServer) {\n        return(list(\n          appMode = \"rmd-shiny\",\n          primaryFile = basename(primaryDocFile)\n        ))\n      }\n      return(list(\n        appMode = \"rmd-static\",\n        primaryFile = basename(primaryDocFile)\n      ))\n    }\n  }\n\n  if (hasR) {\n    # We have R scripts but it was not otherwise identified as Shiny or Plumber\n    # and also not accompanied by *.qmd or *.rmd files.\n    #\n    # Assume that this is a rendered script, as this is a better fall-back than\n    # \"static\".\n    return(list(appMode = \"quarto-static\", primaryFile = basename(rFiles[1])))\n  }\n\n  # Node.js application (detected after all R/Python/Quarto signals)\n  packageJsonFiles <- matchingNames(absoluteRootFiles, \"^package\\\\.json$\")\n  if (length(packageJsonFiles) > 0) {\n    return(list(appMode = \"nodejs\", primaryFile = NULL))\n  }\n\n  # TensorFlow model files are lower in the hierarchy, not at the root.\n  modelFiles <- matchingNames(\n    absoluteAppFiles,\n    \"^(saved_model.pb|saved_model.pbtxt)$\"\n  )\n  if (length(modelFiles) > 0) {\n    return(list(\n      appMode = \"tensorflow-saved-model\",\n      # TODO: primaryFile is only required for Connect Cloud, but Connect Cloud\n      # doesn't support TensorFlow yet. More work needed here to be able to\n      #  support this.\n      NULL\n    ))\n  }\n\n  # no renderable content\n  list(appMode = \"static\", primaryFile = NULL)\n}\n\nisShinyRmd <- function(filename) {\n  yaml <- yamlFromRmd(filename)\n  if (is.null(yaml)) {\n    return(FALSE)\n  }\n  is_shiny_prerendered(yaml$runtime, yaml$server)\n}\n\nyamlFromRmd <- function(filename) {\n  lines <- readLines(filename, warn = FALSE, encoding = \"UTF-8\")\n  delim <- grep(\"^(---|\\\\.\\\\.\\\\.)\\\\s*$\", lines)\n  if (length(delim) >= 2) {\n    # If at least two --- or ... lines were found...\n    if (delim[[1]] == 1 || all(grepl(\"^\\\\s*$\", lines[1:delim[[1]]]))) {\n      # and the first is a ---\n      if (grepl(\"^---\\\\s*$\", lines[delim[[1]]])) {\n        # ...and the first --- line is not preceded by non-whitespace...\n        if (diff(delim[1:2]) > 1) {\n          # ...and there is actually something between the two --- lines...\n          yamlData <- paste(\n            lines[(delim[[1]] + 1):(delim[[2]] - 1)],\n            collapse = \"\\n\"\n          )\n          return(yaml::yaml.load(yamlData))\n        }\n      }\n    }\n  }\n  return(NULL)\n}\n\n# Adapted from rmarkdown:::is_shiny_prerendered()\nis_shiny_prerendered <- function(runtime, server = NULL) {\n  if (!is.null(runtime) && grepl(\"^shiny\", runtime)) {\n    TRUE\n  } else if (identical(server, \"shiny\")) {\n    TRUE\n  } else if (is.list(server) && identical(server[[\"type\"]], \"shiny\")) {\n    TRUE\n  } else {\n    FALSE\n  }\n}\n\n# If deploying an R Markdown, Quarto, or static content, infer a primary\n# document if one is not already specified.\n# Note: functionality in inferQuartoInfo() depends on primary doc inference\n# working the same across app modes.\ninferAppPrimaryDoc <- function(appPrimaryDoc, appFiles, appMode) {\n  if (!is.null(appPrimaryDoc)) {\n    return(appPrimaryDoc)\n  }\n\n  # Only documents and static apps don't have primary _doc_\n  if (!(appIsDocument(appMode) || appMode == \"static\")) {\n    return(appPrimaryDoc)\n  }\n\n  # determine expected primary document extension\n  ext <- switch(\n    appMode,\n    \"static\" = \"\\\\.html?$\",\n    \"quarto-static\" = \"\\\\.(r|rmd|qmd)$\",\n    \"quarto-shiny\" = \"\\\\.(rmd|qmd)$\",\n    \"\\\\.rmd$\"\n  )\n\n  # use index file if it exists\n  matching <- grepl(paste0(\"^index\", ext), appFiles, ignore.case = TRUE)\n  if (!any(matching)) {\n    # no index file found, so pick the first one we find\n    matching <- grepl(ext, appFiles, ignore.case = TRUE)\n\n    if (!any(matching)) {\n      cli::cli_abort(c(\n        \"Failed to determine {.arg appPrimaryDoc} for {.str {appMode}} content.\",\n        x = \"No files matching {.str {ext}}.\"\n      ))\n    }\n  }\n\n  if (sum(matching) > 1) {\n    # if we have multiple matches, pick the first\n    appFiles[matching][[1]]\n  } else {\n    appFiles[matching]\n  }\n}\n\nappIsDocument <- function(appMode) {\n  appMode %in%\n    c(\n      \"rmd-static\",\n      \"rmd-shiny\",\n      \"quarto-static\",\n      \"quarto-shiny\"\n    )\n}\n\nappIsQuartoDocument <- function(appMode) {\n  appMode %in%\n    c(\n      \"quarto-static\",\n      \"quarto-shiny\"\n    )\n}\n\nappHasParameters <- function(\n  appDir,\n  appPrimaryDoc,\n  appMode,\n  contentCategory = NULL\n) {\n  # Only Rmd deployments are marked as having parameters. Shiny applications\n  # may distribute an Rmd alongside app.R, but that does not cause the\n  # deployment to be considered parameterized.\n  #\n  # https://github.com/rstudio/rsconnect/issues/246\n  if (!(appIsDocument(appMode))) {\n    return(FALSE)\n  }\n  # Sites don't ever have parameters\n  if (identical(contentCategory, \"site\")) {\n    return(FALSE)\n  }\n\n  # Only Rmd files have parameters.\n  if (tolower(tools::file_ext(appPrimaryDoc)) == \"rmd\") {\n    filename <- file.path(appDir, appPrimaryDoc)\n    yaml <- yamlFromRmd(filename)\n    if (!is.null(yaml)) {\n      params <- yaml[[\"params\"]]\n      # We don't care about deep parameter processing, only that they exist.\n      return(!is.null(params) && length(params) > 0)\n    }\n  }\n  FALSE\n}\n\ndetectPythonInDocuments <- function(appDir, files = NULL) {\n  if (is.null(files)) {\n    # for testing\n    files <- bundleFiles(appDir)\n  }\n\n  rmdFiles <- grep(\n    \"^[^/\\\\\\\\]+\\\\.[rq]md$\",\n    files,\n    ignore.case = TRUE,\n    perl = TRUE,\n    value = TRUE\n  )\n  for (rmdFile in rmdFiles) {\n    if (documentHasPythonChunk(file.path(appDir, rmdFile))) {\n      return(TRUE)\n    }\n  }\n  return(FALSE)\n}\ndocumentHasPythonChunk <- function(filename) {\n  lines <- readLines(filename, warn = FALSE, encoding = \"UTF-8\")\n  matches <- grep(\"`{python\", lines, fixed = TRUE)\n  return(length(matches) > 0)\n}\n\n#' Infer node.js information\n#'\n#' @param appDir directory containing content\n#' @return list containing the entrypoint, engines, and whether a\n#'   package-lock.json exists.\n#' @noRd\ninferNodejsInfo <- function(appDir) {\n  packageJsonPath <- file.path(appDir, \"package.json\")\n  pkg <- jsonlite::fromJSON(packageJsonPath, simplifyVector = FALSE)\n\n  entrypoint <- pkg$main\n  if (is.null(entrypoint) || !nzchar(entrypoint)) {\n    entrypoint <- \"index.js\"\n  }\n\n  enginesNode <- NULL\n  if (!is.null(pkg$engines) && !is.null(pkg$engines$node)) {\n    enginesNode <- pkg$engines$node\n  }\n\n  lockfilePath <- file.path(appDir, \"package-lock.json\")\n  hasLockfile <- file.exists(lockfilePath)\n\n  list(\n    entrypoint = entrypoint,\n    enginesNode = enginesNode,\n    hasLockfile = hasLockfile\n  )\n}\n\n#' Infer plumber information\n#'\n#' @param appDir directory containing content\n#' @return `\"plumber\"` for plumber APIs; the contents of the `engine` field in\n#'   `_server.yml`/`_server.yaml` (usually `\"plumber2\"`) for plumber2 APIs.\n#' @noRd\ninferPlumberInfo <- function(appDir) {\n  files <- list.files(appDir)\n  is_plumber2 <- any(grepl(\"^_server\\\\.ya?ml$\", files))\n\n  if (!is_plumber2) {\n    return(\"plumber\")\n  }\n\n  server_file <- list.files(\n    appDir,\n    pattern = \"^_server.ya?ml$\",\n    full.names = TRUE\n  )\n  if (length(server_file) > 1) {\n    stop(\n      \"Found both _server.yaml and _server.yml, please remove one from your project\",\n      call. = FALSE\n    )\n  }\n  server_yaml <- yaml::read_yaml(server_file)\n  return(server_yaml$engine)\n}\n"
  },
  {
    "path": "R/applications.R",
    "content": "#' List Deployed Applications\n#'\n#' @description\n#' List all applications currently deployed for a given account.\n#'\n#' Supported servers: All servers\n#'\n#' @inheritParams deployApp\n#' @return\n#' Returns a data frame with the following columns:\n#' \\tabular{ll}{\n#' `id`         \\tab Application unique id \\cr\n#' `name`       \\tab Name of application \\cr\n#' `title`       \\tab Application title \\cr\n#' `url`        \\tab URL where application can be accessed \\cr\n#'\n#' `status`     \\tab Current status of application. Valid values are `pending`,\n#'                   `deploying`, `running`, `terminating`, and `terminated` \\cr\n#' `size`       \\tab Instance size (small, medium, large, etc.) (on\n#'                   ShinyApps.io) \\cr\n#' `instances`  \\tab Number of instances (on ShinyApps.io) \\cr\n#' `config_url` \\tab URL where application can be configured \\cr\n#' }\n#' @note To register an account you call the [setAccountInfo()] function.\n#' @examples\n#' \\dontrun{\n#'\n#' # list all applications for the default account\n#' applications()\n#'\n#' # list all applications for a specific account\n#' applications(\"myaccount\")\n#'\n#' # view the list of applications in the data viewer\n#' View(applications())\n#' }\n#' @seealso [deployApp()], [terminateApp()]\n#' @family Deployment functions\n#' @export\napplications <- function(account = NULL, server = NULL) {\n  # resolve account and create connect client\n  accountDetails <- accountInfo(account, server)\n  serverDetails <- serverInfo(accountDetails$server)\n  client <- clientForAccount(accountDetails)\n\n  if (isPositConnectCloudServer(accountDetails$server)) {\n    cli::cli_abort(\n      \"The applications() function is not supported for Posit Connect Cloud accounts.\"\n    )\n  }\n\n  isConnect <- isConnectServer(accountDetails$server)\n\n  # retrieve applications\n  apps <- client$listApplications(accountDetails$accountId)\n\n  # extract the subset of fields we're interested in\n  keep <- if (isConnect) {\n    c(\n      \"id\",\n      \"name\",\n      \"title\",\n      \"url\",\n      \"build_status\",\n      \"created_time\",\n      \"last_deployed_time\",\n      \"guid\"\n    )\n  } else {\n    c(\n      \"id\",\n      \"name\",\n      \"url\",\n      \"status\",\n      \"created_time\",\n      \"updated_time\",\n      \"deployment\"\n    )\n  }\n  res <- lapply(apps, `[`, keep)\n\n  res <- if (isConnect) {\n    lapply(res, function(x) {\n      # set size and instance to NA since Connect doesn't return this info\n      x$size <- NA\n      x$instances <- NA\n      x$title <- x$title %||% NA_character_\n      x\n    })\n  } else {\n    lapply(res, function(x) {\n      # promote the size and instance data to first-level fields\n      x$size <- x$deployment$properties$application.instances.template\n      if (is.null(x$size)) {\n        x$size <- NA\n      }\n      x$instances <- x$deployment$properties$application.instances.count\n      if (is.null(x$instances)) {\n        x$instances <- NA\n      }\n      x$deployment <- NULL\n      x$guid <- NA\n      x$title <- NA_character_\n      x\n    })\n  }\n\n  # The config URL may be provided by the server at some point, but for now\n  # infer it from the account type\n  res <- lapply(res, function(row) {\n    if (isConnect) {\n      prefix <- sub(\"/__api__\", \"\", serverDetails$url)\n      row$config_url <- paste(prefix, \"connect/#/apps\", row$id, sep = \"/\")\n    } else {\n      row$config_url <- paste(\n        \"https://www.shinyapps.io/admin/#/application\",\n        row$id,\n        sep = \"/\"\n      )\n    }\n    row\n  })\n\n  # convert to data frame\n  res <- lapply(res, as.data.frame, stringsAsFactors = FALSE)\n  res <- do.call(\"rbind\", res)\n\n  # Ensure the Connect and ShinyApps.io data frames have same column names\n  idx <- match(\"last_deployed_time\", names(res))\n  if (!is.na(idx)) {\n    names(res)[idx] <- \"updated_time\"\n  }\n\n  idx <- match(\"build_status\", names(res))\n  if (!is.na(idx)) {\n    names(res)[idx] <- \"status\"\n  }\n\n  return(res)\n}\n\n# Use the API to filter applications by name and error when it does not exist.\ngetAppByName <- function(client, accountInfo, name, error_call = caller_env()) {\n  # NOTE: returns a list with 0 or 1 elements\n  app <- client$listApplications(\n    accountInfo$accountId,\n    filters = list(name = name)\n  )\n  if (length(app)) {\n    return(app[[1]])\n  }\n  cli::cli_abort(\n    c(\n      \"No application found\",\n      i = \"Specify the application directory, name, and/or associated account.\"\n    ),\n    call = error_call,\n    class = \"rsconnect_app_not_found\"\n  )\n}\n\n# Use the API to list all applications then filter the results client-side.\nresolveApplication <- function(accountDetails, appName) {\n  client <- clientForAccount(accountDetails)\n  apps <- client$listApplications(accountDetails$accountId)\n  for (app in apps) {\n    if (identical(app$name, appName)) {\n      return(app)\n    }\n  }\n\n  stopWithApplicationNotFound(appName)\n}\n\ngetApplication <- function(account, server, appId) {\n  accountDetails <- accountInfo(account, server)\n  client <- clientForAccount(accountDetails)\n\n  withCallingHandlers(\n    client$getApplication(appId, \"unknown\"),\n    rsconnect_http_404 = function(err) {\n      cli::cli_abort(\"Can't find app with id {.str {appId}}\", parent = err)\n    }\n  )\n}\n\nstopWithApplicationNotFound <- function(appName) {\n  stop(\n    paste(\n      \"No application named '\",\n      appName,\n      \"' is currently deployed.\",\n      sep = \"\"\n    ),\n    call. = FALSE\n  )\n}\n\napplicationTask <- function(taskDef, appName, accountDetails, quiet) {\n  # resolve target account and application\n  application <- resolveApplication(accountDetails, appName)\n\n  # get status function and display initial status\n  displayStatus <- displayStatus(quiet)\n  displayStatus(paste(taskDef$beginStatus, \"...\\n\", sep = \"\"))\n\n  # perform the action\n  client <- clientForAccount(accountDetails)\n  task <- taskDef$action(client, application)\n  client$waitForTask(task$task_id, quiet)\n  displayStatus(paste(taskDef$endStatus, \"\\n\", sep = \"\"))\n\n  invisible(NULL)\n}\n\n#' Application Logs\n#'\n#' @description\n#' These functions provide access to the logs for deployed ShinyApps applications:\n#'\n#' * `showLogs()` displays the logs.\n#' * `getLogs()` returns the logged lines.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appPath The path to the directory or file that was deployed.\n#' @param appFile The path to the R source file that contains the application\n#'   (for single file applications).\n#' @param appName The name of the application to show logs for. May be omitted\n#'   if only one application deployment was made from `appPath`.\n#' @param account The account under which the application was deployed. May be\n#'   omitted if only one account is registered on the system.\n#' @param server Server name. Required only if you use the same account name on\n#'   multiple servers.\n#' @param entries The number of log entries to show. Defaults to 50 entries.\n#' @param streaming Deprecated. Streaming logs is not currently supported\n#'   as the ShinyApps.io backend no longer supports this feature. If `TRUE`,\n#'   an error will be thrown. Defaults to `FALSE`.\n#'\n#' @note These functions only work\n#'   for applications deployed to ShinyApps.io.\n#'\n#' @return `getLogs()` returns a data frame containing the logged lines.\n#'\n#' @export\nshowLogs <- function(\n  appPath = getwd(),\n  appFile = NULL,\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  entries = 50,\n  streaming = FALSE\n) {\n  # determine the log target and target account info\n  deployment <- findDeployment(\n    appPath = appPath,\n    appName = appName,\n    server = server,\n    account = account\n  )\n\n  checkShinyappsServer(deployment$server)\n\n  if (streaming) {\n    cli::cli_abort(\n      c(\n        \"Streaming logs is not currently supported.\",\n        i = \"The ShinyApps.io backend no longer supports the streaming API.\",\n        i = \"Use {.arg streaming = FALSE} (the default) to retrieve recent log entries.\"\n      )\n    )\n  }\n\n  accountDetails <- accountInfo(deployment$account, deployment$server)\n  client <- clientForAccount(accountDetails)\n  application <- getAppByName(client, accountDetails, deployment$name)\n\n  # Poll for the entries directly\n  logs <- client$getLogs(application$id, entries)\n  cat(logs)\n}\n\n#' @rdname showLogs\n#' @export\ngetLogs <- function(\n  appPath = getwd(),\n  appFile = NULL,\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  entries = 50\n) {\n  # determine the log target and target account info\n  deployment <- findDeployment(\n    appPath = appPath,\n    appName = appName,\n    server = server,\n    account = account\n  )\n  accountDetails <- accountInfo(deployment$account, deployment$server)\n  client <- clientForAccount(accountDetails)\n  application <- getAppByName(client, accountDetails, deployment$name)\n\n  payload <- client$getLogs(application$id, entries, format = \"json\")\n\n  # Convert to a dataframe before combining because the JSON payload has inconsistent field order\n  # containing nested single-element lists.\n  converted <- lapply(payload$results, as.data.frame)\n  df <- do.call(rbind, converted)\n\n  # shinyapps.io returns ns timestamps.\n  df$timestamp <- as.POSIXct(df$timestamp / (1000 * 1000))\n\n  # Return a subset of the included fields.\n  result <- df[c(\n    \"timestamp\",\n    \"account_id\",\n    \"application_id\",\n    \"message\"\n  )]\n  result\n}\n\n#' Update deployment records\n#'\n#' @description\n#' Update the deployment records for applications published to Posit Connect.\n#' This updates application title and URL, and deletes records for deployments\n#' where the application has been deleted on the server.\n#'\n#' Supported servers: Posit Connect servers\n#'\n#' @param appPath The path to the directory or file that was deployed.\n#' @export\nsyncAppMetadata <- function(appPath = \".\") {\n  check_directory(appPath)\n\n  deploys <- deployments(appPath)\n  for (i in seq_len(nrow(deploys))) {\n    curDeploy <- deploys[i, ]\n\n    # don't sync if published to RPubs or Connect Cloud\n    if (isRPubs(curDeploy$server)) {\n      next\n    } else if (isPositConnectCloudServer(curDeploy$server)) {\n      next\n    }\n\n    account <- accountInfo(curDeploy$account, curDeploy$server)\n    client <- clientForAccount(account)\n\n    application <- tryCatch(\n      client$getApplication(curDeploy$appId),\n      rsconnect_http_404 = function(c) {\n        # if the app has been deleted, delete the deployment record\n        file.remove(curDeploy$deploymentFile)\n        cli::cli_inform(\n          \"Deleting deployment record for deleted app {curDeploy$appId}.\"\n        )\n        NULL\n      }\n    )\n    if (is.null(application)) {\n      next\n    }\n\n    # update the record and save out a new config file\n    path <- curDeploy$deploymentFile\n    curDeploy$deploymentFile <- NULL # added on read\n\n    # remove old fields\n    curDeploy$when <- NULL\n    curDeploy$lastSyncTime <- NULL\n\n    curDeploy$title <- application$title\n    curDeploy$url <- application$url\n\n    writeDeploymentRecord(curDeploy, path)\n  }\n}\n"
  },
  {
    "path": "R/auth.R",
    "content": "cleanupPasswordFile <- function(appDir) {\n  check_directory(appDir)\n  appDir <- normalizePath(appDir)\n\n  # get data dir from appDir\n  dataDir <- file.path(appDir, \"shinyapps\")\n\n  # get password file\n  passwordFile <- file.path(dataDir, paste(\"passwords\", \".txt\", sep = \"\"))\n\n  # check if password file exists\n  if (file.exists(passwordFile)) {\n    message(\n      \"WARNING: Password file found! This application is configured to use scrypt \",\n      \"authentication, which is no longer supported.\\nIf you choose to proceed, \",\n      \"all existing users of this application will be removed, \",\n      \"and will NOT be recoverable.\\nFor for more information please visit: \",\n      \"http://shiny.rstudio.com/articles/migration.html\"\n    )\n    response <- readline(\"Do you want to proceed? [Y/n]: \")\n    if (tolower(substring(response, 1, 1)) != \"y\") {\n      stop(\"Cancelled\", call. = FALSE)\n    } else {\n      # remove old password file\n      file.remove(passwordFile)\n    }\n  }\n\n  invisible(TRUE)\n}\n\n#' Add authorized user to application\n#'\n#' @description\n#' Add authorized user to application\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param email Email address of user to add.\n#' @param appDir Directory containing application. Defaults to\n#'   current working directory.\n#' @param appName Name of application.\n#' @inheritParams deployApp\n#' @param sendEmail Send an email letting the user know the application\n#'   has been shared with them.\n#' @param emailMessage Optional character vector of length 1 containing a\n#'   custom message to send in email invitation. Defaults to NULL, which\n#'   will use default invitation message.\n#' @seealso [removeAuthorizedUser()] and [showUsers()]\n#' @note This function works only for ShinyApps servers.\n#' @export\naddAuthorizedUser <- function(\n  email,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  sendEmail = NULL,\n  emailMessage = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # resolve application\n  if (is.null(appName)) {\n    appName <- basename(appDir)\n  }\n  application <- resolveApplication(accountDetails, appName)\n\n  # check for and remove password file\n  cleanupPasswordFile(appDir)\n\n  # fetch authoriztion list\n  api <- clientForAccount(accountDetails)\n  api$inviteApplicationUser(\n    application$id,\n    validateEmail(email),\n    sendEmail,\n    emailMessage\n  )\n\n  message(paste(\"Added:\", email, \"to application\", sep = \" \"))\n\n  invisible(TRUE)\n}\n\n#' Remove authorized user from an application\n#'\n#' @description\n#' Remove authorized user from an application\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param user The user to remove. Can be id or email address.\n#' @param appDir Directory containing application. Defaults to\n#' current working directory.\n#' @param appName Name of application.\n#' @inheritParams deployApp\n#' @seealso [addAuthorizedUser()] and [showUsers()]\n#' @note This function works only for ShinyApps servers.\n#' @export\nremoveAuthorizedUser <- function(\n  user,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # resolve application\n  if (is.null(appName)) {\n    appName <- basename(appDir)\n  }\n  application <- resolveApplication(accountDetails, appName)\n\n  # check and remove password file\n  cleanupPasswordFile(appDir)\n\n  # get users\n  users <- showUsers(appDir, appName, account, server)\n\n  if (is.numeric(user)) {\n    # lookup by id\n    if (user %in% users$id) {\n      user <- users[users$id == user, ]\n    } else {\n      stop(\"User \", user, \" not found\", call. = FALSE)\n    }\n  } else {\n    # lookup by email\n    if (user %in% users$email) {\n      user <- users[users$email == user, ]\n    } else {\n      stop(\"User \\\"\", user, \"\\\" not found\", call. = FALSE)\n    }\n  }\n\n  # remove user\n  api <- clientForAccount(accountDetails)\n  api$removeApplicationUser(application$id, user$id)\n\n  message(paste(\"Removed:\", user$email, \"from application\", sep = \" \"))\n\n  invisible(TRUE)\n}\n\n#' List authorized users for an application\n#'\n#' @description\n#' List authorized users for an application\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appDir Directory containing application. Defaults to\n#'   current working directory.\n#' @param appName Name of application.\n#' @inheritParams deployApp\n#' @seealso [addAuthorizedUser()] and [showInvited()]\n#' @note This function works only for ShinyApps servers.\n#' @export\nshowUsers <- function(\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # resolve application\n  if (is.null(appName)) {\n    appName <- basename(appDir)\n  }\n  application <- resolveApplication(accountDetails, appName)\n\n  # fetch authoriztion list\n  api <- clientForAccount(accountDetails)\n  res <- api$listApplicationAuthorization(application$id)\n\n  # get interesting fields\n  users <- lapply(res, function(x) {\n    a <- list()\n    a$id <- x$user$id\n    a$email <- x$user$email\n    if (!is.null(x$account)) {\n      a$account <- x$account\n    } else {\n      a$account <- NA\n    }\n    return(a)\n  })\n\n  # convert to data frame\n  users <- do.call(rbind, users)\n  df <- as.data.frame(users, stringsAsFactors = FALSE)\n  return(df)\n}\n\n#' List invited users for an application\n#'\n#' @description\n#' List invited users for an application\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appDir Directory containing application. Defaults to\n#'   current working directory.\n#' @param appName Name of application.\n#' @inheritParams deployApp\n#' @seealso [addAuthorizedUser()] and [showUsers()]\n#' @note This function works only for ShinyApps servers.\n#' @export\nshowInvited <- function(\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # resolve application\n  if (is.null(appName)) {\n    appName <- basename(appDir)\n  }\n  application <- resolveApplication(accountDetails, appName)\n\n  # fetch invitation list\n  api <- clientForAccount(accountDetails)\n  res <- api$listApplicationInvitations(application$id)\n\n  # get interesting fields\n  users <- lapply(res, function(x) {\n    a <- list()\n    a$id <- x$id\n    a$email <- x$email\n    a$link <- x$link\n    a$expired <- x$expired\n    return(a)\n  })\n\n  # convert to data frame\n  users <- do.call(rbind, users)\n  df <- as.data.frame(users, stringsAsFactors = FALSE)\n  return(df)\n}\n\n#' Resend invitation for invited users of an application\n#'\n#' @description\n#' Resend invitation for invited users of an application\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param invite The invitation to resend. Can be id or email address.\n#' @param regenerate Regenerate the invite code. Can be helpful is the\n#' invitation has expired.\n#' @param appDir Directory containing application. Defaults to\n#'   current working directory.\n#' @param appName Name of application.\n#' @inheritParams deployApp\n#' @seealso [showInvited()]\n#' @note This function works only for ShinyApps servers.\n#' @export\nresendInvitation <- function(\n  invite,\n  regenerate = FALSE,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # get invitations\n  invited <- showInvited(appDir, appName, account, server)\n\n  if (is.numeric(invite)) {\n    # lookup by id\n    if (invite %in% invited$id) {\n      invite <- invited[invited$id == invite, ]\n    } else {\n      stop(\"Invitation \\\"\", invite, \"\\\" not found\", call. = FALSE)\n    }\n  } else {\n    # lookup by email\n    if (invite %in% invited$email) {\n      invite <- invited[invited$email == invite, ]\n    } else {\n      stop(\"Invitiation for \\\"\", invite, \"\\\" not found\", call. = FALSE)\n    }\n  }\n\n  # resend invitation\n  api <- clientForAccount(accountDetails)\n  api$resendApplicationInvitation(invite$id, regenerate)\n\n  message(paste(\"Sent invitation to\", invite$email, \"\", sep = \" \"))\n\n  invisible(TRUE)\n}\n\n# Previously exported, but deprecated since 2015\nauthorizedUsers <- function(appDir = getwd()) {\n  # read password file\n  path <- getPasswordFile(appDir)\n  if (file.exists(path)) {\n    passwords <- readPasswordFile(path)\n  } else {\n    passwords <- NULL\n  }\n\n  return(passwords)\n}\n\nvalidateEmail <- function(email) {\n  if (is.null(email) || !grepl(\".+\\\\@.+\\\\..+\", email)) {\n    stop(\"Invalid email address.\", call. = FALSE)\n  }\n\n  invisible(email)\n}\n\ngetPasswordFile <- function(appDir) {\n  check_directory(appDir)\n\n  file.path(normalizePath(appDir), \"shinyapps\", \"passwords.txt\")\n}\n\nreadPasswordFile <- function(path) {\n  # open and read file\n  lines <- readLines(path)\n\n  # extract fields\n  fields <- do.call(rbind, strsplit(lines, \":\"))\n  users <- fields[, 1]\n  hashes <- fields[, 2]\n\n  # convert to data frame\n  df <- data.frame(user = users, hash = hashes, stringsAsFactors = FALSE)\n\n  # return data frame\n  return(df)\n}\n\nwritePasswordFile <- function(path, passwords) {\n  # open and file\n  f <- file(path, open = \"w\")\n  defer(close(f))\n\n  # write passwords\n  apply(passwords, 1, function(r) {\n    l <- paste(r[1], \":\", r[2], \"\\n\", sep = \"\")\n    cat(l, file = f, sep = \"\")\n  })\n  message(\n    \"Password file updated. You must deploy your application for these changes to take effect.\"\n  )\n}\n"
  },
  {
    "path": "R/bundle.R",
    "content": "# Given a path to an directory and a list of files in that directory, copies\n# those files to a new temporary directory. Performs some small modifications\n# in this process, including renaming single-file Shiny apps to \"app.R\" and\n# stripping packrat and renv commands from .Rprofile. Returns the path to the\n# temporary directory.\nbundleAppDir <- function(\n  appDir,\n  appFiles,\n  appPrimaryDoc = NULL,\n  appMode = NULL,\n  verbose = FALSE\n) {\n  logger <- verboseLogger(verbose)\n\n  logger(\"Creating bundle staging directory\")\n  bundleDir <- dirCreate(tempfile())\n  defer(unlink(bundleDir))\n\n  logger(\"Copying files into bundle staging directory\")\n  for (file in appFiles) {\n    logger(\"Copying\", file)\n    from <- file.path(appDir, file)\n    to <- file.path(bundleDir, file)\n\n    if (!is.null(appMode) && appMode == \"shiny\") {\n      # When deploying a single-file Shiny application and we have been provided\n      # appPrimaryDoc (usually by RStudio), rename that file to `app.R` so it\n      # will be discovered and run by shiny::runApp(getwd()).\n      #\n      # Note: We do not expect to see writeManifest(appPrimaryDoc=\"notapp.R\").\n      if (\n        is.character(appPrimaryDoc) &&\n          tolower(tools::file_ext(appPrimaryDoc)) == \"r\" &&\n          file == appPrimaryDoc\n      ) {\n        to <- file.path(bundleDir, \"app.R\")\n      }\n    }\n\n    dirCreate(dirname(to))\n    file.copy(from, to, copy.date = TRUE)\n\n    # ensure .Rprofile doesn't call packrat/init.R or renv/activate.R\n    if (basename(to) == \".Rprofile\") {\n      tweakRProfile(to)\n    }\n  }\n\n  # When an renv profile is active, the profile-specific lockfile lives under\n  # renv/profiles/<name>/renv.lock, which is excluded from the bundle (the\n  # entire renv/ directory is ignored). Resolve the correct lockfile from the\n  # original appDir and copy it into the bundle so that\n  # parseRenvDependencies() compares against the right lockfile.\n  profileLockfile <- tryCatch(\n    resolveRenvLockFile(appDir),\n    error = function(e) NULL\n  )\n  if (!is.null(profileLockfile) && file.exists(profileLockfile)) {\n    bundleLockfile <- file.path(bundleDir, \"renv.lock\")\n    file.copy(profileLockfile, bundleLockfile, overwrite = TRUE)\n  }\n\n  bundleDir\n}\n\ntweakRProfile <- function(path) {\n  lines <- readLines(path)\n\n  packratLines <- grep('source(\"packrat/init.R\")', lines, fixed = TRUE)\n  if (length(packratLines) > 0) {\n    lines[packratLines] <- paste0(\n      \"# Packrat initialization disabled in published application\\n\",\n      '# source(\"packrat/init.R\")'\n    )\n  }\n\n  renvLines <- grep('source(\"renv/activate.R\")', lines, fixed = TRUE)\n  if (length(renvLines) > 0) {\n    lines[renvLines] <- paste0(\n      \"# renv initialization disabled in published application\\n\",\n      '# source(\"renv/activate.R\")'\n    )\n  }\n\n  if (length(renvLines) > 0 || length(packratLines) > 0) {\n    msg <- sprintf(\n      \"# Modified by rsconnect package %s on %s\",\n      packageVersion(\"rsconnect\"),\n      Sys.time()\n    )\n    lines <- c(msg, lines)\n  }\n\n  writeLines(lines, path)\n}\n\n# Writes a tar.gz file located at bundlePath containing all files in bundleDir.\nwriteBundle <- function(bundleDir, bundlePath, verbose = FALSE) {\n  logger <- verboseLogger(verbose)\n\n  prevDir <- setwd(bundleDir)\n  defer(setwd(prevDir))\n\n  tarImplementation <- getTarImplementation()\n  logger(\"Using tar: \", tarImplementation)\n\n  if (tarImplementation == \"internal\") {\n    detectLongNames(bundleDir)\n  }\n\n  utils::tar(\n    bundlePath,\n    files = NULL,\n    compression = \"gzip\",\n    tar = tarImplementation\n  )\n}\n\ngetTarImplementation <- function() {\n  # Check the rsconnect.tar option first. If that is unset, check the\n  # RSCONNECT_TAR environment var. If neither are set, use \"internal\".\n  tarImplementation <- getOption(\"rsconnect.tar\", default = NA)\n  if (is.na(tarImplementation) || !nzchar(tarImplementation)) {\n    tarImplementation <- Sys.getenv(\"RSCONNECT_TAR\", unset = NA)\n  }\n  if (is.na(tarImplementation) || !nzchar(tarImplementation)) {\n    tarImplementation <- \"internal\"\n  }\n  return(tarImplementation)\n}\n\nisWindows <- function() {\n  Sys.info()[[\"sysname\"]] == \"Windows\"\n}\n\nversionFromDescription <- function(appDir) {\n  descriptionFilepath <- file.path(appDir, \"DESCRIPTION\")\n  if (!file.exists(descriptionFilepath)) {\n    return(NULL)\n  }\n\n  desc <- read.dcf(descriptionFilepath)\n  depends <- as.list(desc[1, ])$Depends\n  if (is.null(depends)) {\n    return(NULL)\n  }\n\n  regexExtract(\"R \\\\((.*?)\\\\)\", depends)\n}\n\nversionFromLockfile <- function(appDir) {\n  tryCatch(\n    {\n      lockfile <- suppressWarnings(renv::lockfile_read(project = appDir))\n      v <- lockfile$R$Version\n      # only major specified “3” → ~=3.0 → >=3.0,<4.0\n      # major and minor specified “3.8” or “3.8.11” → ~=3.8.0 → >=3.8.0,<3.9.0\n      parts <- strsplit(v, \"\\\\.\")[[1]]\n      new_parts <- c(head(parts, 2), \"0\")\n      paste0(\"~=\", paste(new_parts, collapse = \".\"))\n    },\n    error = function(e) {\n      return(NULL)\n    }\n  )\n}\n\nrVersionRequires <- function(appDir) {\n  # Look for requirement at DESCRIPTION file\n  requires <- versionFromDescription(appDir)\n\n  # If DESCRIPTION file does not have R requirement\n  # Look it up on renv lockfile\n  if (is.null(requires)) {\n    requires <- versionFromLockfile(appDir)\n  }\n\n  requires\n}\n\ncreateAppManifest <- function(\n  appDir,\n  appMetadata,\n  users = NULL,\n  pythonConfig = NULL,\n  retainPackratDirectory = TRUE,\n  image = NULL,\n  envManagement = NULL,\n  envManagementR = NULL,\n  envManagementPy = NULL,\n  envManagementNodejs = NULL,\n  packageRepositoryResolutionR = NULL,\n  dependencyResolution = \"strict\",\n  verbose = FALSE,\n  quiet = FALSE\n) {\n  if (is.null(image)) {\n    imageEnv <- Sys.getenv(\"RSCONNECT_IMAGE\", unset = NA)\n    if (!is.na(imageEnv) && nchar(imageEnv) > 0) {\n      image <- imageEnv\n    }\n  }\n\n  # Validate packageRepositoryResolutionR\n  if (!is.null(packageRepositoryResolutionR)) {\n    match.arg(\n      packageRepositoryResolutionR,\n      c(\"lax\", \"strict\", \"legacy\", \"lockfile\")\n    )\n  }\n\n  if (needsR(appMetadata)) {\n    extraPackages <- inferRPackageDependencies(appMetadata)\n    # provide package entries for all dependencies\n    packages <- bundlePackages(\n      bundleDir = appDir,\n      extraPackages = extraPackages,\n      verbose = verbose,\n      quiet = quiet,\n      dependencyResolution = dependencyResolution\n    )\n    rVersionReq <- rVersionRequires(appDir)\n  } else {\n    packages <- list()\n    rVersionReq <- NULL\n  }\n\n  needsPython <- appMetadata$documentsHavePython ||\n    \"jupyter\" %in% appMetadata$quartoInfo$engines ||\n    \"reticulate\" %in% names(packages)\n  if (needsPython && !is.null(pythonConfig)) {\n    python <- pythonConfig(appDir)\n    pyVersionReq <- python$requires\n\n    packageFile <- file.path(appDir, python$package_manager$package_file)\n    writeLines(python$package_manager$contents, packageFile)\n    python$package_manager$contents <- NULL\n  } else {\n    python <- NULL\n    pyVersionReq <- NULL\n  }\n\n  if (!retainPackratDirectory) {\n    # Optionally remove the packrat directory when it will not be included in\n    # deployments, such as manifest-only deployments.\n    unlink(file.path(appDir, \"packrat\"), recursive = TRUE)\n  }\n\n  # build the list of files to checksum\n  files <- list.files(\n    appDir,\n    recursive = TRUE,\n    all.files = TRUE,\n    full.names = FALSE\n  )\n\n  # provide checksums for all files\n  filelist <- list()\n  for (file in files) {\n    filepath <- file.path(appDir, file)\n    checksum <- list(checksum = fileMD5(filepath))\n    filelist[[file]] <- I(checksum)\n  }\n\n  # create userlist\n  userlist <- list()\n  if (!is.null(users) && length(users) > 0) {\n    for (i in 1:nrow(users)) {\n      user <- users[i, \"user\"]\n      hash <- users[i, \"hash\"]\n      userinfo <- list()\n      userinfo$hash <- hash\n      userlist[[user]] <- userinfo\n    }\n  }\n\n  nodejsVersionReq <- if (appMetadata$appMode == \"nodejs\") {\n    appMetadata$nodejsInfo$enginesNode\n  }\n\n  # create the manifest\n  manifest <- list()\n  manifest$version <- 1\n  manifest$locale <- getOption(\"rsconnect.locale\", detectLocale())\n  manifest$platform <- paste(R.Version()$major, R.Version()$minor, sep = \".\")\n\n  metadata <- list(appmode = appMetadata$appMode)\n\n  # emit appropriate primary document information\n  primaryDoc <- ifelse(\n    is.null(appMetadata$appPrimaryDoc) ||\n      tolower(tools::file_ext(appMetadata$appPrimaryDoc)) == \"r\",\n    NA,\n    appMetadata$appPrimaryDoc\n  )\n  metadata$primary_rmd <- ifelse(\n    appMetadata$appMode %in%\n      c(\"rmd-shiny\", \"rmd-static\", \"quarto-shiny\", \"quarto-static\"),\n    primaryDoc,\n    NA\n  )\n  metadata$primary_html <- ifelse(\n    appMetadata$appMode == \"static\",\n    primaryDoc,\n    NA\n  )\n\n  # emit content category (plots, etc)\n  metadata$content_category <- ifelse(\n    !is.null(appMetadata$contentCategory),\n    appMetadata$contentCategory,\n    NA\n  )\n  metadata$has_parameters <- appMetadata$hasParameters\n\n  if (appMetadata$appMode == \"nodejs\") {\n    metadata$entrypoint <- appMetadata$nodejsInfo$entrypoint\n  }\n\n  # add metadata\n  manifest$metadata <- metadata\n\n  # handle shorthand arg to enable/disable all runtimes\n  if (!is.null(envManagement)) {\n    envManagementR <- envManagement\n    envManagementPy <- envManagement\n    envManagementNodejs <- envManagement\n  }\n\n  # if envManagement is explicitly enabled/disabled,\n  # create an environment_management obj\n  envManagementInfo <- list()\n  if (!is.null(envManagementR)) {\n    envManagementInfo$r <- envManagementR\n  }\n  if (!is.null(envManagementPy)) {\n    envManagementInfo$python <- envManagementPy\n  }\n  if (!is.null(envManagementNodejs)) {\n    envManagementInfo$nodejs <- envManagementNodejs\n  }\n\n  # emit the environment field\n  if (\n    !is.null(image) ||\n      length(envManagementInfo) > 0 ||\n      !is.null(rVersionReq) ||\n      !is.null(pyVersionReq) ||\n      !is.null(nodejsVersionReq) ||\n      !is.null(packageRepositoryResolutionR)\n  ) {\n    manifest$environment <- list()\n\n    # if there is a target image, attach it to the environment\n    if (!is.null(image)) {\n      manifest$environment$image <- image\n    }\n\n    # if there is an R version constraint or package repository resolution\n    if (!is.null(rVersionReq) || !is.null(packageRepositoryResolutionR)) {\n      manifest$environment$r <- list()\n      if (!is.null(rVersionReq)) {\n        manifest$environment$r$requires <- rVersionReq\n      }\n      if (!is.null(packageRepositoryResolutionR)) {\n        manifest$environment$r$package_repository_resolution <- packageRepositoryResolutionR\n      }\n    }\n\n    # if there is a Python version constraint\n    if (!is.null(pyVersionReq)) {\n      manifest$environment$python <- list(requires = pyVersionReq)\n    }\n\n    # if there is a Node.js version constraint\n    if (!is.null(nodejsVersionReq)) {\n      manifest$environment$nodejs <- list(requires = nodejsVersionReq)\n    }\n\n    # if environment_management is provided for any runtime,\n    # write the environment_management field\n    if (length(envManagementInfo) > 0) {\n      manifest$environment$environment_management <- envManagementInfo\n    }\n  }\n\n  # indicate whether this is a quarto app/doc\n  manifest$quarto <- appMetadata$quartoInfo\n\n  # if there is python info for reticulate or Quarto, attach it\n  if (!is.null(python)) {\n    manifest$python <- python\n  }\n\n  # node.js content includes an empty nodejs object\n  if (appMetadata$appMode == \"nodejs\") {\n    manifest$nodejs <- structure(list(), names = character(0))\n  }\n  # if there are no packages set manifest$packages to NA (json null)\n  if (length(packages) > 0) {\n    manifest$packages <- I(packages)\n  } else {\n    manifest$packages <- NA\n  }\n  # if there are no files, set manifest$files to NA (json null)\n  if (length(files) > 0) {\n    manifest$files <- I(filelist)\n  } else {\n    manifest$files <- NA\n  }\n  # if there are no users set manifest$users to NA (json null)\n  if (length(users) > 0) {\n    manifest$users <- I(userlist)\n  } else {\n    manifest$users <- NA\n  }\n\n  manifest\n}\n"
  },
  {
    "path": "R/bundleFiles.R",
    "content": "#' Gather files to be bundled with an app\n#'\n#' @description\n#' Given an app directory, and optional `appFiles` and `appFileManifest`\n#' arguments, returns vector of paths to bundle in the app. (Note that\n#' documents follow a different strategy; see [deployDoc()] for details.)\n#'\n#' When neither `appFiles` nor `appFileManifest` is supplied,\n#' `listDeploymentFiles()` will include all files under `appDir`, apart\n#' from the following:\n#'\n#' *  Certain files and folders that don't need to be bundled, such as\n#'    version control directories, internal config files, and RStudio state,\n#'    are automatically excluded.\n#'\n#' *  You can exclude additional files by listing them in in a `.rscignore`\n#'    file. This file must have one file or directory per line (with path\n#'    relative to the current directory). It doesn't support wildcards, or\n#'    ignoring files in subdirectories.\n#'\n#' `listDeploymentFiles()` will throw an error if the total file size exceeds\n#' the maximum bundle size (as controlled by option `rsconnect.max.bundle.size`),\n#' or the number of files exceeds the maximum file limit (as controlled by\n#' option `rsconnect.max.bundle.files`). This prevents you from accidentally\n#' bundling a very large direcfory (i.e. you home directory).\n#'\n#' Supported servers: All servers\n#'\n#' @inheritParams deployApp\n#' @param error_call The call or environment for error reporting; expert\n#'   use only.\n#' @return Character of paths to bundle, relative to `appDir`.\n#' @export\nlistDeploymentFiles <- function(\n  appDir,\n  appFiles = NULL,\n  appFileManifest = NULL,\n  error_call = caller_env()\n) {\n  no_content <- function(message) {\n    cli::cli_abort(\n      c(\"No content to deploy.\", x = message),\n      call = error_call\n    )\n  }\n\n  if (!is.null(appFiles) && !is.null(appFileManifest)) {\n    cli::cli_abort(\n      \"Must specify at most one of {.arg appFiles} and {.arg appFileManifest}\",\n      call = error_call\n    )\n  } else if (is.null(appFiles) && is.null(appFileManifest)) {\n    # no files supplied at all, just bundle the whole directory\n    appFiles <- bundleFiles(appDir)\n    if (length(appFiles) == 0) {\n      no_content(\"{.arg appDir} is empty.\")\n    }\n  } else if (!is.null(appFiles)) {\n    check_character(appFiles, allow_null = TRUE, call = error_call)\n    appFiles <- explodeFiles(appDir, appFiles, \"appFiles\")\n    if (length(appFiles) == 0) {\n      no_content(\"{.arg appFiles} didn't match any files in {.arg appDir}.\")\n    }\n  } else if (!is.null(appFileManifest)) {\n    check_file(appFileManifest, error_call = error_call)\n    appFiles <- readFileManifest(appFileManifest)\n    appFiles <- explodeFiles(appDir, appFiles, \"appFileManifest\")\n    if (length(appFiles) == 0) {\n      no_content(\"{.arg appFileManifest} contains no usable files.\")\n    }\n  }\n\n  appFiles\n}\n\nreadFileManifest <- function(appFileManifest, error_call = caller_env()) {\n  lines <- readLines(appFileManifest, warn = FALSE)\n\n  # remove empty/comment lines\n  lines <- lines[nzchar(lines)]\n  lines <- lines[!grepl(\"^#\", lines)]\n  lines\n}\n\n#' List Files to be Bundled\n#'\n#' @description\n#' `r lifecycle::badge(\"superseded\")`\n#'\n#' `listBundleFiles()` has been superseded in favour of [listDeploymentFiles()].\n#'\n#' Given a directory containing an application, returns the names of the files\n#' that by default will be bundled in the application. It works similarly to\n#' a recursive directory listing from [list.files()] but enforces bundle sizes\n#' as described in [listDeploymentFiles()]\n#'\n#' @param appDir Directory containing the application.\n#' @return Returns a list containing the following elements:\n#' * `totalFiles`: Total number of files.\n#' * `totalSize`: Total size of the files (in bytes).\n#' * `contents`: Paths to bundle, relative to `appDir`.\n#' @export\n#' @keywords internal\nlistBundleFiles <- function(appDir) {\n  recursiveBundleFiles(appDir)\n}\n\nbundleFiles <- function(appDir) {\n  listBundleFiles(appDir)$contents\n}\n\nexplodeFiles <- function(dir, files, error_arg = \"appFiles\") {\n  missing <- !file.exists(file.path(dir, files))\n  if (any(missing)) {\n    cli::cli_warn(c(\n      \"All files listed in {.arg {error_arg}} must exist.\",\n      \"Problems: {.file {files[missing]}}\"\n    ))\n\n    files <- files[!missing]\n  }\n\n  recursiveBundleFiles(dir, contents = files, ignoreFiles = FALSE)$contents\n}\n\nrecursiveBundleFiles <- function(\n  dir,\n  contents = NULL,\n  rootDir = dir,\n  totalFiles = 0,\n  totalSize = 0,\n  ignoreFiles = TRUE\n) {\n  # generate a list of files at this level\n  if (is.null(contents)) {\n    contents <- list.files(dir, all.files = TRUE, no.. = TRUE)\n  }\n  if (ignoreFiles) {\n    contents <- ignoreBundleFiles(dir, contents)\n  }\n\n  # Info for each file lets us know to recurse (directories) or aggregate (files).\n  is_dir <- dir.exists(file.path(dir, contents))\n  names(is_dir) <- contents\n\n  children <- character()\n  for (name in contents) {\n    if (isTRUE(is_dir[[name]])) {\n      out <- recursiveBundleFiles(\n        dir = file.path(dir, name),\n        rootDir = rootDir,\n        totalFiles = totalFiles,\n        totalSize = totalSize,\n        ignoreFiles = ignoreFiles\n      )\n\n      children <- append(children, file.path(name, out$contents))\n      totalFiles <- out$totalFiles\n      totalSize <- out$totalSize\n    } else {\n      children <- append(children, name)\n      totalFiles <- totalFiles + 1\n      totalSize <- totalSize + file_size(file.path(dir, name))\n    }\n\n    enforceBundleLimits(rootDir, totalFiles, totalSize)\n  }\n\n  list(\n    contents = children,\n    totalFiles = totalFiles,\n    totalSize = totalSize\n  )\n}\n\nignoreBundleFiles <- function(dir, contents) {\n  # entries ignored regardless of type\n  ignored <- c(\n    # rsconnect packages\n    \"rsconnect\",\n    \"rsconnect-python\",\n    \"manifest.json\",\n    # packrat + renv,\n    \"renv\",\n    \"packrat\",\n    # version control\n    \".git\",\n    \".gitignore\",\n    \".svn\",\n    # R/RStudio\n    \".Rhistory\",\n    \".Rproj.user\",\n    # Node.js\n    \"node_modules\",\n    \".npm\",\n    # other\n    \".DS_Store\",\n    \".quarto\",\n    \"app_cache\",\n    \"__pycache__/\"\n  )\n\n  contents <- setdiff(contents, ignored)\n  contents <- contents[!isKnitrCacheDir(contents)]\n  contents <- contents[!isPythonEnv(dir, contents)]\n  contents <- contents[!grepl(\"^~|~$\", contents)]\n  contents <- contents[!grepl(glob2rx(\"*.Rproj\"), contents)]\n\n  # remove any files lines listed .rscignore\n  if (\".rscignore\" %in% contents) {\n    ignoreContents <- readLines(file.path(dir, \".rscignore\"))\n    contents <- setdiff(contents, c(ignoreContents, \".rscignore\"))\n  }\n\n  contents\n}\n\nisKnitrCacheDir <- function(files) {\n  is_cache <- grepl(\"^.+_cache$\", files)\n\n  cache_rmd <- gsub(\"_cache$\", \".Rmd\", files)\n  has_rmd <- tolower(cache_rmd) %in% tolower(files)\n\n  ifelse(is_cache, has_rmd, FALSE)\n}\n\n# https://github.com/rstudio/rsconnect-python/blob/94dbd28797ee503d6/rsconnect/bundle.py#L541-L543\nisPythonEnv <- function(dir, files) {\n  (file.exists(file.path(dir, files, \"bin\", \"python\")) |\n    file.exists(file.path(dir, files, \"Scripts\", \"python.exe\")) |\n    file.exists(file.path(dir, files, \"Scripts\", \"pythond.exe\")) |\n    file.exists(file.path(dir, files, \"Scripts\", \"pythonw.exe\")))\n}\n\nenforceBundleLimits <- function(appDir, totalFiles, totalSize) {\n  maxSize <- getOption(\"rsconnect.max.bundle.size\", defaultMaxBundleSize)\n  maxFiles <- getOption(\"rsconnect.max.bundle.files\", defaultMaxBundleFiles)\n\n  if (totalSize > maxSize) {\n    cli::cli_abort(c(\n      \"{.arg appDir} ({.path {appDir}}) is too large to be deployed.\",\n      x = \"The maximum size is {maxSize} bytes.\",\n      x = \"This directory is at least {totalSize} bytes.\",\n      i = \"Remove some files or adjust the rsconnect.max.bundle.size option.\",\n      \" \" = \"e.g., options(rsconnect.max.bundle.size = 6 * 1024^3)\",\n      \" \" = \"See {.topic rsconnect::rsconnectOptions} for additional guidance.\"\n    ))\n  }\n\n  if (totalFiles > maxFiles) {\n    cli::cli_abort(c(\n      \"{.arg appDir} ({.path {appDir}}) is too large to be deployed.\",\n      x = \"The maximum number of files is {maxFiles}.\",\n      x = \"This directory contains at least {totalFiles} files.\",\n      i = \"Remove some files or adjust the rsconnect.max.bundle.files option.\",\n      \" \" = \"e.g., options(rsconnect.max.bundle.files = 15000)\",\n      \" \" = \"See {.topic rsconnect::rsconnectOptions} for additional guidance.\"\n    ))\n  }\n}\n\n# Scan the bundle directory looking for long user/group names.\n#\n# Warn that the internal tar implementation may produce invalid archives.\n# https://github.com/rstudio/rsconnect/issues/446\n# https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17871\ndetectLongNames <- function(bundleDir, lengthLimit = 32) {\n  files <- list.files(\n    bundleDir,\n    recursive = TRUE,\n    all.files = TRUE,\n    include.dirs = TRUE,\n    no.. = TRUE\n  )\n\n  info <- file.info(file.path(bundleDir, files))\n  ok <- (is.na(info$uname) | nchar(info$uname) <= lengthLimit) &\n    (is.na(info$grname) | nchar(info$grname) <= lengthLimit)\n\n  if (all(ok)) {\n    return(invisible(FALSE))\n  }\n\n  bad_files <- files[!ok]\n\n  cli::cli_warn(\n    c(\n      \"The bundle contains files with user/group names longer than {lengthLimit}.\",\n      x = \"Files: {.path {bad_files}}\",\n      x = \"Long user and group names cause the internal R tar implementation to produce invalid archives\",\n      i = \"Set the {.code rsconnect.tar} option or the {.code RSCONNECT_TAR} environment variable to the path to a tar executable.\"\n    )\n  )\n  return(invisible(FALSE))\n}\n"
  },
  {
    "path": "R/bundlePackage.R",
    "content": "bundlePackages <- function(\n  bundleDir,\n  extraPackages = character(),\n  quiet = FALSE,\n  verbose = FALSE,\n  dependencyResolution = \"strict\",\n  error_call = caller_env()\n) {\n  deps <- computePackageDependencies(\n    bundleDir,\n    extraPackages,\n    quiet = quiet,\n    verbose = verbose,\n    dependencyResolution = dependencyResolution\n  )\n  if (nrow(deps) == 0) {\n    return(list())\n  }\n  checkBundlePackages(deps, call = error_call)\n\n  # Manifest packages used to generate packrat file on Connect\n  # https://github.com/rstudio/connect/blob/v2023.03.0/src/connect/manifest/convert.go#L261-L320\n  packages_list <- lapply(seq_len(nrow(deps)), function(i) {\n    out <- as.list(deps[i, , drop = FALSE])\n    out$description <- out$description[[1]]\n    out$Package <- NULL\n    out$Version <- NULL\n    out\n  })\n  names(packages_list) <- deps$Package\n\n  packages_list\n}\n\nusePackrat <- function() {\n  # Use RSCONNECT_PACKRAT when it has any value; fall-back to rsconnect.packrat when the environment\n  # variable is unset.\n  value <- Sys.getenv(\"RSCONNECT_PACKRAT\", unset = NA)\n  if (is.na(value)) {\n    value <- getOption(\"rsconnect.packrat\", default = FALSE)\n  }\n\n  return(truthy(value))\n}\n\ncomputePackageDependencies <- function(\n  bundleDir,\n  extraPackages = character(),\n  quiet = FALSE,\n  verbose = FALSE,\n  dependencyResolution = \"strict\"\n) {\n  if (usePackrat()) {\n    taskStart(quiet, \"Capturing R dependencies with packrat\")\n    # Remove renv.lock so the packrat call to renv::dependencies does not report an implicit renv\n    # dependency. Mirrors rsconnect before 1.0.0, which did not include renv.lock in bundles.\n    # https://github.com/rstudio/rsconnect/blob/v0.8.29/R/bundle.R#L96\n    removeRenv(bundleDir)\n    deps <- snapshotPackratDependencies(\n      bundleDir,\n      extraPackages,\n      verbose = verbose\n    )\n  } else if (\n    dependencyResolution != \"library\" &&\n      !is.null(resolveRenvLockFile(bundleDir))\n  ) {\n    lockfile <- resolveRenvLockFile(bundleDir)\n    # This ignores extraPackages; if you're using a lockfile it's your\n    # responsibility to install any other packages you need\n    taskStart(quiet, \"Capturing R dependencies from renv.lock\")\n    deps <- parseRenvDependencies(\n      lockfile,\n      bundleDir\n    )\n    # Once we've captured the deps, we can remove the renv directory\n    # from the bundle (retaining the renv.lock).\n    removeRenv(bundleDir, lockfile = FALSE)\n  } else {\n    taskStart(quiet, \"Capturing R dependencies\")\n    # TODO: give user option to choose between implicit and explicit\n    deps <- snapshotRenvDependencies(\n      bundleDir,\n      extraPackages,\n      quiet = quiet,\n      verbose = verbose\n    )\n  }\n  taskComplete(quiet, \"Found {nrow(deps)} dependenc{?y/ies}\")\n\n  deps\n}\n\ncheckBundlePackages <- function(deps, call = caller_env()) {\n  unknown_source <- is.na(deps$Source)\n  if (any(unknown_source)) {\n    pkgs <- deps$Package[unknown_source]\n    cli::cli_abort(\n      c(\n        \"All packages must be installed from a reproducible location.\",\n        x = \"Can't re-install packages installed from source: {.pkg {pkgs}}.\",\n        i = \"See {.fun rsconnect::appDependencies} for more details.\"\n      ),\n      call = call\n    )\n  }\n}\n\nmanifestPackageColumns <- function(df) {\n  # Fields defined in https://bit.ly/42CbD4P\n  # Most fields are retrieved from the complete embedded description.\n  # shinyapps.io needs GitHub fields for backward compatibility\n\n  github_cols <- grep(\"^Github\", names(df), perl = TRUE, value = TRUE)\n  intersect(\n    c(\"Package\", \"Version\", \"Source\", \"Repository\", github_cols),\n    names(df)\n  )\n}\n\navailablePackages <- function(repos) {\n  # read available.packages filters (allow user to override if necessary;\n  # this is primarily to allow debugging)\n  #\n  # note that we explicitly exclude the \"R_version\" filter as we want to ensure\n  # that packages which require newer versions of R than the one currently\n  # in use can still be marked as available on CRAN -- for example, currently\n  # the package \"foreign\" requires \"R (>= 4.0.0)\" but older versions of R\n  # can still successfully install older versions from the CRAN archive\n  filters <- c(\n    getOption(\"rsconnect.available_packages_filters\", default = c()),\n    \"duplicates\"\n  )\n  available.packages(\n    repos = repos,\n    type = \"source\",\n    filters = filters\n  )\n}\n\npackage_record <- function(name, lib_dir = NULL) {\n  record <- packageDescription(name, lib.loc = lib_dir, encoding = \"UTF-8\")\n  unclass(record)\n}\n"
  },
  {
    "path": "R/bundlePackagePackrat.R",
    "content": "snapshotPackratDependencies <- function(\n  bundleDir,\n  implicit_dependencies = character(),\n  verbose = FALSE\n) {\n  addPackratSnapshot(bundleDir, implicit_dependencies, verbose = verbose)\n\n  lockFilePath <- packratLockFile(bundleDir)\n  df <- as.data.frame(read.dcf(lockFilePath), stringsAsFactors = FALSE)\n  unlink(dirname(lockFilePath), recursive = TRUE)\n\n  # get repos defined in the lockfile\n  repos <- gsub(\"[\\r\\n]\", \" \", df[1, \"Repos\"])\n  repos <- strsplit(\n    unlist(strsplit(repos, \"\\\\s*,\\\\s*\", perl = TRUE)),\n    \"=\",\n    fixed = TRUE\n  )\n  repos <- setNames(\n    sapply(repos, \"[[\", 2),\n    sapply(repos, \"[[\", 1)\n  )\n\n  # get packages records defined in the lockfile\n  records <- utils::tail(df, -1)\n  if (nrow(records) == 0) {\n    return(data.frame())\n  }\n\n  rownames(records) <- NULL\n  records <- records[manifestPackageColumns(records)]\n  records[c(\"Source\", \"Repository\")] <- standardizeRecords(records, repos)\n  records$description <- lapply(records$Package, package_record)\n\n  records\n}\n\nstandardizeRecords <- function(records, repos) {\n  availablePackages <- availablePackages(repos)\n  repos <- standardizeRepos(repos)\n\n  rows <- lapply(seq_len(nrow(records)), function(i) {\n    standardizePackratPackage(records[i, ], availablePackages, repos = repos)\n  })\n  rows <- lapply(rows, as.data.frame, stringsAsFactors = FALSE)\n  rbind_fill(rows, c(\"Source\", \"Repository\"))\n}\n\nstandardizeRepos <- function(repos) {\n  # Ensure that each repository has a unique name\n  names(repos) <- ifelse(\n    names2(repos) == \"\",\n    paste0(\"repo_\", seq_along(repos)),\n    names2(repos)\n  )\n\n  # And strip trailing /\n  repos <- gsub(\"/$\", \"\", repos)\n\n  repos\n}\n\nstandardizePackratPackage <- function(\n  record,\n  availablePackages,\n  repos = character()\n) {\n  pkg <- record$Package\n  source <- record$Source\n\n  # source types are defined by packrat:\n  # https://github.com/rstudio/packrat/blob/v0.9.0/R/pkg.R#L328\n  if (source %in% c(\"github\", \"gitlab\", \"bitbucket\")) {\n    # SCM information is recorded elsewhere\n    repository <- NA_character_\n  } else if (source == \"source\") {\n    # can't install source packages elsewhere\n    repository <- NA_character_\n    source <- NA_character_\n  } else if (\n    source == \"CustomCRANLikeRepository\" &&\n      isDevVersion(record, availablePackages)\n  ) {\n    # Package was installed from source, but packrat guessed it was installed\n    # from a known repo.\n    repository <- NA_character_\n    source <- NA_character_\n  } else if (source %in% c(\"CRAN\", \"Bioconductor\")) {\n    # shinyapps will ignore, but connect will use (unless admin\n    # has set up an override)\n    repository <- findRepoUrl(pkg, availablePackages)\n  } else {\n    # Installed from custom repository. Find URL from available.packages()\n    # and then name from repos.\n    repository <- findRepoUrl(pkg, availablePackages)\n    source <- findRepoName(repository, repos)\n  }\n  list(Source = source, Repository = repository)\n}\n\nfindRepoName <- function(repository, repos) {\n  idx <- match(repository, repos)\n  names(repos)[idx]\n}\n\nfindRepoUrl <- function(pkg, availablePackages) {\n  idx <- match(pkg, availablePackages[, \"Package\"])\n\n  if (!is.na(idx)) {\n    repo <- availablePackages[[idx, \"Repository\"]]\n    # Strip `/src/contrib/*` from package repository: `contrib.url()`\n    # adds /src/contrib, and RSPM adds additional directories\n    gsub(\"/src/contrib.*$\", \"\", repo)\n  } else {\n    NA_character_\n  }\n}\n\nisDevVersion <- function(record, availablePackages) {\n  idx <- match(record$Package, availablePackages[, \"Package\"])\n\n  if (is.na(idx)) {\n    return(FALSE)\n  }\n\n  local_version <- record$Version\n  repo_version <- availablePackages[idx, \"Version\"]\n\n  package_version(local_version) > package_version(repo_version)\n}\n\naddPackratSnapshot <- function(\n  bundleDir,\n  implicit_dependencies = character(),\n  verbose = FALSE\n) {\n  # if we discovered any extra dependencies, write them to a file for packrat to\n  # discover when it creates the snapshot\n  recordExtraDependencies(bundleDir, implicit_dependencies)\n\n  withCallingHandlers(\n    performPackratSnapshot(bundleDir, verbose = verbose),\n    error = function(err) {\n      abort(\"Failed to snapshot dependencies\", parent = err)\n    },\n    warning = function(cnd) {\n      invokeRestart(\"muffleWarning\")\n    }\n  )\n\n  invisible()\n}\n\nrecordExtraDependencies <- function(bundleDir, pkgs, env = caller_env()) {\n  if (length(pkgs) == 0) {\n    return()\n  }\n\n  depPath <- file.path(bundleDir, \"__rsconnect_deps.R\")\n  writeLines(paste0(\"library(\", pkgs, \")\\n\"), depPath)\n\n  # Automatically delete when the _caller_ finishes\n  defer(unlink(depPath), env = env)\n  invisible()\n}\n\nperformPackratSnapshot <- function(bundleDir, verbose = FALSE) {\n  # ensure we snapshot recommended packages\n  srp <- packrat::opts$snapshot.recommended.packages()\n  packrat::opts$snapshot.recommended.packages(TRUE, persist = FALSE)\n  defer(packrat::opts$snapshot.recommended.packages(srp, persist = FALSE))\n\n  # Force renv dependency scanning within packrat unless the option has been\n  # explicitly configured. This is a no-op for older versions of packrat.\n  renvDiscovery <- getOption(\"packrat.dependency.discovery.renv\")\n  if (is.null(renvDiscovery)) {\n    old <- options(\"packrat.dependency.discovery.renv\" = TRUE)\n    defer(options(old))\n  }\n\n  # attempt to eagerly load the BiocInstaller or BiocManaager package if\n  # installed, to work around an issue where attempts to load the package could\n  # fail within a 'suppressMessages()' context\n  packages <- c(\"BiocManager\", \"BiocInstaller\")\n  for (package in packages) {\n    if (is_installed(package)) {\n      requireNamespace(package, quietly = TRUE)\n      break\n    }\n  }\n\n  suppressMessages(\n    packrat::.snapshotImpl(\n      project = bundleDir,\n      snapshot.sources = FALSE,\n      fallback.ok = TRUE,\n      verbose = verbose,\n      implicit.packrat.dependency = FALSE\n    )\n  )\n\n  invisible()\n}\n\npackratLockFile <- function(bundleDir) {\n  file.path(bundleDir, \"packrat\", \"packrat.lock\")\n}\n"
  },
  {
    "path": "R/bundlePackageRenv.R",
    "content": "snapshotRenvDependencies <- function(\n  bundleDir,\n  extraPackages = character(),\n  quiet = FALSE,\n  verbose = FALSE\n) {\n  recordExtraDependencies(bundleDir, extraPackages)\n\n  old <- options(\n    renv.verbose = FALSE,\n    renv.consent = TRUE\n  )\n  defer(options(old))\n\n  dependenciesLimit <- getOption(\"renv.config.dependencies.limit\")\n  if (is.null(dependenciesLimit)) {\n    maxFiles <- getOption(\"rsconnect.max.bundle.files\", 10000)\n    oldlim <- options(\n      renv.config.dependencies.limit = maxFiles\n    )\n    defer(options(oldlim))\n  }\n\n  # analyze code dependencies ourselves rather than relying on the scan during renv::snapshot, as\n  # that will add renv to renv.lock as a dependency.\n  deps <- renv::dependencies(\n    bundleDir,\n    root = bundleDir,\n    quiet = if (quiet) TRUE else NULL,\n    progress = FALSE\n  )\n\n  tryCatch(\n    renv::snapshot(bundleDir, packages = deps$Package, prompt = FALSE),\n    error = function(err) {\n      cli::cli_abort(\n        c(\n          \"Failed to snapshot dependencies with renv.\",\n          i = \"For example, you have a locally-developed package that is installed from disk.\"\n        ),\n        parent = err\n      )\n    }\n  )\n  # renv::snapshot() respects RENV_PATHS_LOCKFILE and renv profiles, so the\n  # lockfile may have been written to a non-standard location.\n  lockfile <- resolveRenvLockFile(bundleDir)\n  if (is.null(lockfile)) {\n    cli::cli_abort(\"renv::snapshot() did not produce a lockfile\")\n  }\n  defer(removeRenv(bundleDir))\n\n  parseRenvDependencies(lockfile, bundleDir, snapshot = TRUE)\n}\n\nparseRenvDependencies <- function(lockfile, bundleDir, snapshot = FALSE) {\n  renvLock <- jsonlite::read_json(lockfile)\n  repos <- setNames(\n    vapply(renvLock$R$Repositories, \"[[\", \"URL\", FUN.VALUE = character(1)),\n    vapply(renvLock$R$Repositories, \"[[\", \"Name\", FUN.VALUE = character(1))\n  )\n  bioc <- biocRepos(bundleDir)\n  if (any(bioc %in% repos)) {\n    biocPkgs <- NULL\n  } else {\n    signal(\"evaluating\", class = \"rsconnect_biocRepos\")\n    biocPkgs <- availablePackages(bioc)\n  }\n  deps <- standardizeRenvPackages(\n    renvLock$Packages,\n    repos,\n    biocPackages = biocPkgs\n  )\n  if (nrow(deps) == 0) {\n    return(data.frame())\n  }\n  deps$description <- lapply(deps$Package, package_record)\n  if (!snapshot) {\n    lib_versions <- unlist(lapply(deps$description, \"[[\", \"Version\"))\n\n    if (any(deps$Version != lib_versions)) {\n      cli::cli_abort(c(\n        \"Library and lockfile are out of sync\",\n        i = \"Use renv::restore() or renv::snapshot() to synchronise\",\n        i = \"Or ignore the lockfile by adding to your .rscignore\",\n        i = \"Or set {.code dependencyResolution = \\\"library\\\"} to ignore the lockfile and use the local library instead\"\n      ))\n    }\n  }\n\n  deps\n}\n\nstandardizeRenvPackages <- function(packages, repos, biocPackages = NULL) {\n  repos <- standardizeRepos(repos)\n  availablePackages <- availablePackages(repos)\n\n  names(packages) <- NULL\n  out <- lapply(\n    packages,\n    standardizeRenvPackage,\n    availablePackages = availablePackages,\n    biocPackages = biocPackages,\n    repos = repos\n  )\n  out <- compact(out)\n  out <- lapply(out, as.data.frame, stringsAsFactors = FALSE)\n  rbind_fill(out)\n}\n\nstandardizeRenvPackage <- function(\n  pkg,\n  availablePackages,\n  biocPackages = NULL,\n  repos = character(),\n  bioc\n) {\n  # Convert renv source to manifest source/repository\n  # https://github.com/rstudio/renv/blob/0.17.2/R/snapshot.R#L730-L773\n\n  if (\n    is.null(pkg$Repository) &&\n      !is.null(pkg$RemoteRepos) &&\n      grepl(\"bioconductor.org\", pkg$RemoteRepos)\n  ) {\n    # Work around bug where renv fails to detect BioC package installed by pak\n    # https://github.com/rstudio/renv/issues/1202\n    pkg$Source <- \"Bioconductor\"\n  }\n\n  if (pkg$Source == \"Repository\") {\n    if (identical(pkg$Repository, \"CRAN\")) {\n      if (isDevVersion(pkg, availablePackages)) {\n        pkg$Source <- NA_character_\n        pkg$Repository <- NA_character_\n      } else {\n        pkg$Source <- \"CRAN\"\n        pkg$Repository <- findRepoUrl(pkg$Package, availablePackages)\n      }\n    } else {\n      # $Repository comes from DESCRIPTION and is set by repo, so can be\n      # anything. First check if it matches a known repository name,\n      # otherwise look up from the package name in availablePackages\n      # Note: our terminology is confusing: the name of the repsoitory is\n      # we call \"Source\" even though renv uses \"Repository\" and the URL of\n      # the repository we call \"Repository\"\n      originalRepository <- pkg$Repository\n\n      if (\n        !is.null(originalRepository) && originalRepository %in% names(repos)\n      ) {\n        # Use the repository specified in renv.lock\n        pkg$Repository <- repos[[originalRepository]]\n        pkg$Source <- originalRepository\n      } else {\n        # Fall back to looking up from availablePackages\n        pkg$Repository <- findRepoUrl(pkg$Package, availablePackages)\n        pkg$Source <- findRepoName(pkg$Repository, repos)\n        if (is.na(pkg$Source)) {\n          # Archived packages are not publicized by available.packages(). Use\n          # the renv.lock repository as source, expecting that the package is\n          # available through one of the repository URLs.\n          pkg$Source <- originalRepository\n        }\n      }\n    }\n  } else if (pkg$Source == \"Bioconductor\") {\n    pkg$Repository <- findRepoUrl(pkg$Package, biocPackages)\n    if (is.na(pkg$Repository)) {\n      pkg$Repository <- findRepoUrl(pkg$Package, availablePackages)\n    }\n  } else if (pkg$Source %in% c(\"Bitbucket\", \"GitHub\", \"GitLab\")) {\n    pkg$Source <- tolower(pkg$Source)\n  } else if (pkg$Source == \"Local\") {\n    # A locally-installed package (e.g. via pak \"local::\") may still be\n    # available from a configured repository. Make a best effort to locate\n    # it, the same way we handle packages installed from source.\n    pkg$Repository <- findRepoUrl(pkg$Package, availablePackages)\n    pkg$Source <- findRepoName(pkg$Repository, repos)\n    if (is.na(pkg$Source)) {\n      pkg$Source <- NA_character_\n      pkg$Repository <- NA_character_\n    }\n  } else if (pkg$Source == \"unknown\") {\n    pkg$Source <- NA_character_\n    pkg$Repository <- NA_character_\n  }\n\n  # Remove Remote fields that pak adds for \"standard\" installs from CRAN\n  if (identical(pkg$RemoteType, \"standard\")) {\n    pkg <- pkg[!grepl(\"^Remote\", names(pkg))]\n  }\n\n  pkg[manifestPackageColumns(pkg)]\n}\n\nbiocRepos <- function(bundleDir) {\n  repos <- getFromNamespace(\"renv_bioconductor_repos\", \"renv\")(bundleDir)\n  repos[setdiff(names(repos), \"CRAN\")]\n}\n\n# Find the renv lockfile, checking both the renv-resolved path and the\n# standard location. Returns the path if found, NULL otherwise.\nresolveRenvLockFile <- function(bundleDir) {\n  resolved <- renv::paths$lockfile(project = bundleDir)\n  if (file.exists(resolved)) {\n    return(resolved)\n  }\n\n  standard <- file.path(bundleDir, \"renv.lock\")\n  if (file.exists(standard)) {\n    return(standard)\n  }\n\n  NULL\n}\n\nremoveRenv <- function(path, lockfile = TRUE) {\n  if (lockfile) {\n    unlink(resolveRenvLockFile(path))\n  }\n  unlink(file.path(path, \"renv\"), recursive = TRUE)\n}\n"
  },
  {
    "path": "R/bundlePython.R",
    "content": "# Create anonymous function that we can later call to get all needed python\n# metdata for the manifest\npythonConfigurator <- function(python, forceGenerate = FALSE) {\n  if (is.null(python)) {\n    return(NULL)\n  }\n\n  force(forceGenerate)\n\n  function(appDir) {\n    withCallingHandlers(\n      inferPythonEnv(\n        appDir,\n        python = python,\n        forceGenerate = forceGenerate\n      ),\n      error = function(err) {\n        cli::cli_abort(\n          \"Failed to detect python environment using {.val {python}}\",\n          parent = err\n        )\n      }\n    )\n  }\n}\n\n# python is enabled on Connect, but not on Shinyapps\ngetPythonForTarget <- function(path, accountDetails) {\n  targetIsShinyapps <- isShinyappsServer(accountDetails$server)\n  pythonEnabled <- getOption(\"rsconnect.python.enabled\", !targetIsShinyapps)\n  if (pythonEnabled) {\n    getPython(path)\n  } else {\n    NULL\n  }\n}\n\ngetPython <- function(path = NULL) {\n  if (!is.null(path)) {\n    return(path.expand(path))\n  }\n\n  path <- Sys.getenv(\"RETICULATE_PYTHON\")\n  if (path != \"\") {\n    return(path.expand(path))\n  }\n\n  path <- Sys.getenv(\"RETICULATE_PYTHON_FALLBACK\")\n  if (path != \"\") {\n    return(path.expand(path))\n  }\n\n  NULL\n}\n\ninferPythonEnv <- function(\n  workdir,\n  python = getPython(),\n  forceGenerate = FALSE\n) {\n  # run the python introspection script\n  env_py <- system.file(\"resources/environment.py\", package = \"rsconnect\")\n  args <- c(\n    shQuote(env_py),\n    if (forceGenerate) \"-f\",\n    shQuote(workdir)\n  )\n\n  hasConda <- is_installed(\"reticulate\") &&\n    reticulate::py_available(initialize = FALSE) &&\n    reticulate::py_config()$anaconda\n\n  if (hasConda) {\n    prefix <- getCondaEnvPrefix(python)\n    conda <- getCondaExeForPrefix(prefix)\n    args <- c(\"run\", \"-p\", prefix, python, args)\n    # conda run -p <prefix> python inst/resources/environment.py <flags> <dir>\n    output <- system2(\n      command = conda,\n      args = args,\n      stdout = TRUE,\n      stderr = NULL,\n      wait = TRUE\n    )\n  } else {\n    output <- system2(\n      command = python,\n      args = args,\n      stdout = TRUE,\n      stderr = NULL,\n      wait = TRUE\n    )\n  }\n\n  environment <- jsonlite::fromJSON(sanitizeSystem2json(output))\n  if (!is.null(environment$warning)) {\n    warning(environment$warning)\n  }\n  if (is.null(environment$error)) {\n    list(\n      version = environment$python,\n      requires = environment$requires,\n      package_manager = list(\n        name = environment$package_manager,\n        version = environment[[environment$package_manager]],\n        package_file = environment$filename,\n        contents = environment$contents\n      )\n    )\n  } else {\n    cli::cli_abort(environment$error)\n  }\n}\n\ngetCondaEnvPrefix <- function(python) {\n  prefix <- dirname(dirname(python))\n  if (!file.exists(file.path(prefix, \"conda-meta\"))) {\n    stop(paste(\n      \"Python from\",\n      python,\n      \"does not look like a conda environment: cannot find `conda-meta`\"\n    ))\n  }\n  prefix\n}\n\ngetCondaExeForPrefix <- function(prefix) {\n  miniconda <- dirname(dirname(prefix))\n  conda <- file.path(miniconda, \"bin\", \"conda\")\n  if (isWindows()) {\n    conda <- paste(conda, \".exe\", sep = \"\")\n  }\n  if (!file.exists(conda)) {\n    stop(paste(\n      \"Conda env prefix\",\n      prefix,\n      \"does not have the `conda` command line interface.\"\n    ))\n  }\n  conda\n}\n"
  },
  {
    "path": "R/certificates.R",
    "content": "# sanity check to make sure we're looking at an ASCII armored cert\nvalidateCertificate <- function(certificate) {\n  return(any(grepl(\"-----BEGIN CERTIFICATE-----\", certificate, fixed = TRUE)))\n}\n\ncreateCertificateFile <- function(certificate) {\n  certificateFile <- NULL\n\n  # check the R option first, then fall back on the environment variable\n  systemStore <- getOption(\"rsconnect.ca.bundle\")\n  if (is.null(systemStore) || !nzchar(systemStore)) {\n    systemStore <- Sys.getenv(\"RSCONNECT_CA_BUNDLE\")\n  }\n\n  # start by checking for a cert file specified in an environment variable\n  if (!is.null(systemStore) && nzchar(systemStore)) {\n    if (file.exists(systemStore)) {\n      certificateFile <- path.expand(systemStore)\n    } else {\n      warning(\n        \"The certificate store '\",\n        systemStore,\n        \"' specified in the \",\n        if (identical(systemStore, getOption(\"rsconnect.ca.bundle\"))) {\n          \"rsconnect.ca.bundle option \"\n        } else {\n          \"RSCONNECT_CA_BUNDLE environment variable \"\n        },\n        \"does not exist. The system certificate store will be used instead.\"\n      )\n    }\n  }\n\n  # if no certificate contents specified, we're done\n  if (is.null(certificate)) {\n    return(certificateFile)\n  }\n\n  # if we don't have a certificate file yet, try to find the system store\n  if (is.null(certificateFile)) {\n    if (.Platform$OS.type == \"unix\") {\n      # search known locations on Unix-like\n      stores <- c(\n        \"/etc/ssl/certs/ca-certificates.crt\",\n        \"/etc/pki/tls/certs/ca-bundle.crt\",\n        \"/usr/share/ssl/certs/ca-bundle.crt\",\n        \"/usr/local/share/certs/ca-root.crt\",\n        \"/etc/ssl/cert.pem\",\n        \"/var/lib/ca-certificates/ca-bundle.pem\"\n      )\n    } else {\n      # mirror behavior of curl on Windows, which looks in system folders,\n      # the working directory, and %PATH%.\n      stores <- c(\n        file.path(getwd(), \"curl-ca-bundle.crt\"),\n        \"C:/Windows/System32/curl-ca-bundle.crt\",\n        \"C:/Windows/curl-ca-bundle.crt\",\n        file.path(\n          strsplit(Sys.getenv(\"PATH\"), \";\", fixed = TRUE),\n          \"curl-ca-bundle.crt\"\n        )\n      )\n    }\n\n    # use our own baked-in bundle as a last resort\n    stores <- c(\n      stores,\n      system.file(package = \"rsconnect\", \"cert\", \"cacert.pem\")\n    )\n\n    for (store in stores) {\n      if (file.exists(store)) {\n        # if the bundle exists, stop here\n        certificateFile <- store\n        break\n      }\n    }\n\n    # if we didn't find the system store, it's okay; the fact that we're here\n    # means that we have a server-specific certificate so it's probably going\n    # to be all right to use only that cert.\n  }\n\n  # Combine the incoming server certificate with the discovered system\n  # certificate, removing duplicates (#1175).\n  allcerts <- c()\n\n  if (!is.null(certificateFile)) {\n    allcerts <- c(allcerts, openssl::read_cert_bundle(certificateFile))\n  }\n  allcerts <- c(allcerts, openssl::read_cert_bundle(certificate))\n\n  allcerts <- allcerts[!duplicated(allcerts)]\n  txtlines <- sapply(allcerts, openssl::write_pem)\n\n  # create a temporary file to house the certificates\n  certificateStore <- tempfile(pattern = \"cacerts\", fileext = \".pem\")\n  writeLines(txtlines, certificateStore)\n\n  return(certificateStore)\n}\n\ninferCertificateContents <- function(certificate) {\n  # certificate can be specified as either a character vector or a filename;\n  # infer which we're dealing with\n\n  # tolerate NULL, which is a valid case representing no certificate\n  if (is.null(certificate) || identical(certificate, \"\")) {\n    return(NULL)\n  }\n\n  # collapse to a single string if we got a vector of lines\n  if (length(certificate) > 1) {\n    certificate <- paste(certificate, collapse = \"\\n\")\n  }\n\n  # looks like ASCII armored certificate data, return as-is\n  if (validateCertificate(substr(certificate, 1, 27))) {\n    return(certificate)\n  }\n\n  # looks like a file; return its contents\n  if (file.exists(certificate)) {\n    if (file.size(certificate) > 1048576) {\n      stop(\n        \"The file '\",\n        certificate,\n        \"' is too large. Certificate files must \",\n        \"be less than 1MB.\"\n      )\n    }\n    contents <- paste(\n      readLines(con = certificate, warn = FALSE),\n      collapse = \"\\n\"\n    )\n    if (validateCertificate(contents)) {\n      return(contents)\n    } else {\n      stop(\n        \"The file '\",\n        certificate,\n        \"' does not appear to be a certificate. \",\n        \"Certificate files should be in ASCII armored PEM format, with a \",\n        \"first line reading -----BEGIN CERTIFICATE-----.\"\n      )\n    }\n  }\n\n  # doesn't look like something we can deal with; guess error based on length\n  if (nchar(certificate) < 200) {\n    stop(\"The certificate file '\", certificate, \"' does not exist.\")\n  } else {\n    stop(\n      \"The certificate '\",\n      substr(certificate, 1, 10),\n      \"...' is not \",\n      \"correctly formed. Specify the certificate as either an ASCII armored string, \",\n      \"beginning with -----BEGIN CERTIFICATE----, or a valid path to a file \",\n      \"containing the certificate.\"\n    )\n  }\n}\n"
  },
  {
    "path": "R/client-cloudAuth.R",
    "content": "# Returns the OAuth client ID to use for the configured Connect Cloud environment.\ngetClientId <- function() {\n  switch(\n    connectCloudEnvironment(),\n    production = \"rsconnect\",\n    staging = \"rsconnect-staging\",\n    development = \"rsconnect-development\",\n  )\n}\n\n# Creates a client for interacting with the Cloud Auth API.\ncloudAuthClient <- function() {\n  service <- parseHttpUrl(connectCloudUrls()$auth)\n  authInfo <- list()\n\n  list(\n    createDeviceAuth = function() {\n      # Create form-encoded body\n      client_id <- getClientId()\n      content <- paste0(\"client_id=\", getClientId(), \"&scope=vivid\")\n\n      response <- POST(\n        service,\n        list(),\n        path = \"/oauth/device/authorize\",\n        contentType = \"application/x-www-form-urlencoded\",\n        content = content\n      )\n\n      response\n    },\n\n    exchangeToken = function(request) {\n      client_id <- getClientId()\n      content <- paste0(\n        \"client_id=\",\n        client_id,\n        \"&grant_type=\",\n        request$grant_type,\n        \"&scope=vivid\"\n      )\n\n      if (!is.null(request$device_code)) {\n        content <- paste0(\n          content,\n          \"&device_code=\",\n          urlEncode(request$device_code)\n        )\n      }\n      if (!is.null(request$refresh_token)) {\n        content <- paste0(\n          content,\n          \"&refresh_token=\",\n          request$refresh_token\n        )\n      }\n\n      POST(\n        service,\n        list(),\n        path = \"/oauth/token\",\n        contentType = \"application/x-www-form-urlencoded\",\n        content = content\n      )\n    }\n  )\n}\n"
  },
  {
    "path": "R/client-connect.R",
    "content": "# Docs: https://docs.posit.co/connect/api/\n\nstripConnectTimestamps <- function(messages) {\n  # Strip timestamps, if found\n  timestamp_re <- \"^\\\\d{4}/\\\\d{2}/\\\\d{2} \\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3,} \"\n  gsub(timestamp_re, \"\", messages)\n}\n\nconnectClient <- function(service, authInfo) {\n  list(\n    service = function() {\n      \"connect\"\n    },\n\n    ## Server settings API\n\n    serverSettings = function() {\n      GET(service, authInfo, unversioned_url(\"server_settings\"))\n    },\n\n    ## User API\n\n    currentUser = function() {\n      # All callers only need $id and $username,\n      # passed to registerAccount() (where account means user)\n      # and that gets written to a .dcf file\n      # /v1/user/ does not include $id\n      # But it looks like none of the Connect code paths use the account/user id,\n      # username is used to identify the \"account\", so this should be safe\n      # to upgrade to v1.\n      GET(service, authInfo, unversioned_url(\"users\", \"current\"))\n    },\n\n    ## Tokens API\n\n    addToken = function(token) {\n      POST_JSON(service, authInfo, unversioned_url(\"tokens\"), token)\n    },\n\n    ## Applications API\n\n    listApplications = function(accountId, filters = NULL) {\n      if (is.null(filters)) {\n        filters <- vector()\n      }\n      path <- unversioned_url(\"applications\")\n      query <- paste(\n        filterQuery(\n          c(\"account_id\", names(filters)),\n          c(accountId, unname(filters))\n        ),\n        collapse = \"&\"\n      )\n      listApplicationsRequest(service, authInfo, path, query, \"applications\")\n    },\n\n    createApplication = function(\n      name,\n      title,\n      template,\n      accountId,\n      appMode,\n      contentCategory = NULL\n    ) {\n      # add name; inject title if specified\n      details <- list(name = name)\n      if (!is.null(title) && nzchar(title)) {\n        details$title <- title\n      }\n\n      # Connect doesn't use the template or account ID\n      # parameters; they exist for compatibility with lucid.\n      result <- POST_JSON(service, authInfo, v1_url(\"content\"), details)\n      list(\n        id = result$id,\n        guid = result$guid,\n        url = result$content_url,\n        # Include dashboard_url so we can open it or logs path after deploy\n        dashboard_url = result$dashboard_url\n      )\n    },\n\n    uploadBundle = function(contentGuid, bundlePath) {\n      path <- v1_url(\"content\", contentGuid, \"bundles\")\n      POST(\n        service,\n        authInfo,\n        path,\n        contentType = \"application/x-gzip\",\n        file = bundlePath\n      )\n    },\n\n    deployApplication = function(application, bundleId = NULL) {\n      path <- v1_url(\"content\", application$guid, \"deploy\")\n      POST_JSON(\n        service,\n        authInfo,\n        path,\n        json = list(bundle_id = bundleId)\n      )\n    },\n\n    getApplication = function(applicationId, deploymentRecordVersion) {\n      GET(service, authInfo, unversioned_url(\"applications\", applicationId))\n    },\n\n    waitForTask = function(taskId, quiet = FALSE) {\n      path <- v1_url(\"tasks\", taskId)\n      query <- list(first = 0, wait = 1)\n\n      while (TRUE) {\n        # ick, manual url construction\n        queryString <- paste(names(query), query, sep = \"=\", collapse = \"&\")\n        url <- paste0(path, \"?\", queryString)\n\n        response <- GET(service, authInfo, url)\n\n        if (length(response$output) > 0) {\n          if (!quiet) {\n            messages <- unlist(response$output)\n            messages <- stripConnectTimestamps(messages)\n\n            # Made headers more prominent.\n            heading <- grepl(\"^# \", messages)\n            messages[heading] <- cli::style_bold(messages[heading])\n            cat(paste0(messages, \"\\n\", collapse = \"\"))\n          }\n\n          query$first <- response$last\n        }\n\n        if (length(response$finished) > 0 && response$finished) {\n          return(response)\n        }\n      }\n    },\n\n    # - Environment variables -----------------------------------------------\n    # https://docs.posit.co/connect/api/#get-/v1/content/{guid}/environment\n\n    getEnvVars = function(guid) {\n      path <- v1_url(\"content\", guid, \"environment\")\n      as.character(unlist(GET(service, authInfo, path, list())))\n    },\n\n    setEnvVars = function(guid, vars) {\n      path <- v1_url(\"content\", guid, \"environment\")\n      body <- unname(Map(\n        function(name, value) {\n          list(\n            name = name,\n            value = if (is.na(value)) NULL else value\n          )\n        },\n        vars,\n        Sys.getenv(vars, unset = NA)\n      ))\n      PATCH_JSON(service, authInfo, path, body)\n    }\n  )\n}\n\ngetSnowflakeAuthToken <- function(url, snowflakeConnectionName) {\n  parsedURL <- parseHttpUrl(url)\n  ingressURL <- parsedURL$host\n\n  # Detect when we're running in the Deploy pane of RStudio and enable\n  # \"interactive\" temporarily so that external browser authentication is\n  # permitted.\n  if (rstudioapi::isBackgroundJob()) {\n    rlang::local_options(rlang_interactive = TRUE)\n  }\n\n  token <- snowflakeauth::snowflake_credentials(\n    snowflakeauth::snowflake_connection(snowflakeConnectionName),\n    spcs_endpoint = ingressURL\n  )\n\n  token\n}\n\n# Gets the default Snowflake connection name if (1) it exists; and (2) it seems\n# to match the server URL.\ngetDefaultSnowflakeConnectionName <- function(url) {\n  connection <- tryCatch(\n    snowflakeauth::snowflake_connection(),\n    error = function(e) {\n      cli::cli_abort(\n        c(\n          \"No default {.arg snowflakeConnectionName}.\",\n          i = \"Provide {.arg snowflakeConnectionName} explicitly.\"\n        ),\n        parent = e\n      )\n    }\n  )\n\n  # Validate that the default connection seems to match the account hosting the\n  # Connect server.\n  parsedURL <- parseHttpUrl(url)\n  serverAccount <- extractSnowflakeAccount(parsedURL$host)\n  normalizedAccount <- gsub(\"_\", \"-\", connection$account, fixed = TRUE)\n  if (!identical(normalizedAccount, serverAccount)) {\n    cli::cli_abort(c(\n      \"The default Snowflake connection account {.str {connection$account}} does\n       not appear to match the Connect server.\",\n      i = \"Pass {.arg snowflakeConnectionName} to use a different connection.\"\n    ))\n  }\n\n  connectionName <- connection$name\n  if (is.null(connectionName) || !nzchar(connectionName)) {\n    # This should never happen.\n    cli::cli_abort(c(\n      \"The Snowflake connection has an empty or missing name field.\",\n      i = \"Provide {.arg snowflakeConnectionName} explicitly.\"\n    ))\n  }\n\n  connectionName\n}\n\n# Extract account name from an SPCS hostname.\nextractSnowflakeAccount <- function(hostname) {\n  # For SPCS (including privatelink) URLs, there is some alphanumeric prefix\n  # followed by the hyphenated form of the account, followed by the Snowflake or\n  # Snowflake Computing domain, e.g. \"bf2oiajb-testorg-testaccount.snowflakecomputing.app\".\n  gsub(\n    \"([^-]+)-([^\\\\.]+)(|\\\\.privatelink)\\\\.(snowflakecomputing|snowflake)\\\\.app$\",\n    \"\\\\2\\\\3\",\n    hostname\n  )\n}\n\n# Utilities for URL construction\n# Also to make it easier to identify where we're calling public APIs and not\nv1_url <- function(...) {\n  # Start with empty string so we get a leading slash\n  paste(\"\", \"v1\", ..., sep = \"/\")\n}\n\nunversioned_url <- function(...) {\n  paste(\"\", ..., sep = \"/\")\n}\n"
  },
  {
    "path": "R/client-connectCloud.R",
    "content": "# Docs: https://posit-hosted.github.io/vivid-api\n\n# Map rsconnect appMode to Connect Cloud contentType\ncloudContentTypeFromAppMode <- function(appMode) {\n  switch(\n    appMode,\n    \"jupyter-notebook\" = \"jupyter\",\n    \"python-bokeh\" = \"bokeh\",\n    \"python-dash\" = \"dash\",\n    \"python-shiny\" = \"shiny\",\n    \"shiny\" = \"shiny\",\n    \"python-streamlit\" = \"streamlit\",\n    \"quarto\" = \"quarto\",\n    \"quarto-static\" = \"quarto\",\n    \"quarto-shiny\" = \"quarto\",\n    \"rmd-static\" = \"rmarkdown\",\n    \"rmd-shiny\" = \"rmarkdown\",\n    \"static\" = \"static\",\n    stop(\n      \"appMode '\",\n      appMode,\n      \"' is not supported by Connect Cloud\",\n      call. = FALSE\n    )\n  )\n}\n\n# Creates a client for interacting with the Connect Cloud API.\nconnectCloudClient <- function(service, authInfo) {\n  # Generic retry wrapper. If a request fails with 401 Unauthorized, it will\n  # exchange the refresh token for a new access token and retry the request\n  # once.\n  withTokenRefreshRetry <- function(request_fn, ...) {\n    tryCatch(\n      {\n        request_fn(service, authInfo, ...)\n      },\n      rsconnect_http_401 = function(e) {\n        # Exchange refresh token for new access token\n        authClient <- cloudAuthClient()\n        tokenResponse <- authClient$exchangeToken(list(\n          grant_type = \"refresh_token\",\n          refresh_token = authInfo$refreshToken\n        ))\n\n        # Save updated tokens\n        registerAccount(\n          authInfo$server,\n          authInfo$name,\n          authInfo$accountId,\n          accessToken = tokenResponse$access_token,\n          refreshToken = tokenResponse$refresh_token\n        )\n\n        # Retry the original request with refreshed token\n        authInfo$accessToken <<- tokenResponse$access_token\n        authInfo$refreshToken <<- tokenResponse$refresh_token\n        request_fn(service, authInfo, ...)\n      }\n    )\n  }\n\n  getAuthorization <- function(logChannel) {\n    json <- list(\n      resource_type = \"log_channel\",\n      resource_id = logChannel,\n      permission = \"revision.logs:read\"\n    )\n\n    response <- withTokenRefreshRetry(\n      POST_JSON,\n      \"/authorization\",\n      json\n    )\n\n    # Return the token from the response\n    response$token\n  }\n\n  list(\n    service = function() {\n      \"connect.posit.cloud\"\n    },\n\n    currentUser = function() {\n      GET(service, authInfo, \"/users/me\")\n    },\n\n    withTokenRefreshRetry = withTokenRefreshRetry,\n\n    listApplications = function(accountId, filters = list()) {\n      # TODO: call the real API when available (api doesn't support filtering by name yet)\n      return(list())\n    },\n\n    createContent = function(\n      name,\n      title,\n      accountId,\n      appMode,\n      primaryFile,\n      envVars\n    ) {\n      title <- if (nzchar(title)) title else name\n      contentType <- cloudContentTypeFromAppMode(appMode)\n\n      # Build revision object, conditionally including primary_file\n      revision <- list(\n        source_type = \"bundle\",\n        content_type = contentType,\n        app_mode = appMode,\n        primary_file = primaryFile\n      )\n\n      secrets <- unname(Map(\n        function(name, value) {\n          list(\n            name = name,\n            value = value\n          )\n        },\n        envVars,\n        Sys.getenv(envVars)\n      ))\n\n      json <- list(\n        account_id = accountId,\n        title = title,\n        next_revision = revision,\n        secrets = secrets\n      )\n\n      content <- withTokenRefreshRetry(\n        POST_JSON,\n        \"/contents\",\n        json\n      )\n      content$application_id <- content$id\n      content\n    },\n\n    getContent = function(contentId) {\n      path <- paste0(\"/contents/\", contentId)\n      content <- withTokenRefreshRetry(GET, path)\n      if (content$state == \"deleted\") {\n        cli::cli_abort(\n          \"Content is pending deletion.\",\n          class = c(\n            \"rsconnect_http_404\",\n            \"rsconnect_http\"\n          )\n        )\n      }\n      content\n    },\n\n    updateContent = function(\n      contentId,\n      envVars,\n      newBundle = FALSE,\n      primaryFile,\n      appMode\n    ) {\n      path <- paste0(\"/contents/\", contentId)\n      if (newBundle) {\n        path <- paste0(path, \"?new_bundle=true\")\n      }\n\n      secrets <- unname(Map(\n        function(name, value) {\n          list(\n            name = name,\n            value = value\n          )\n        },\n        envVars,\n        Sys.getenv(envVars)\n      ))\n\n      json <- list(\n        secrets = secrets,\n        revision_overrides = list(\n          primary_file = primaryFile,\n          app_mode = appMode\n        )\n      )\n\n      content <- withTokenRefreshRetry(PATCH_JSON, path, json)\n      content$application_id <- content$id\n      content\n    },\n\n    uploadBundle = function(bundlePath, uploadUrl) {\n      uploadService <- parseHttpUrl(uploadUrl)\n      headers <- list()\n      headers$`Content-Type` <- \"application/gzip\"\n\n      response <- httpLibCurl(\n        uploadService$protocol,\n        uploadService$host,\n        uploadService$port,\n        \"POST\",\n        uploadService$path,\n        headers,\n        headers$`Content-Type`,\n        bundlePath\n      )\n\n      response$status <= 299\n    },\n\n    publish = function(contentId) {\n      path <- paste0(\"/contents/\", contentId, \"/publish\")\n      withTokenRefreshRetry(POST_JSON, path, list())\n    },\n\n    # Polls the revision until the publish process completes, returning whether\n    # the publish request succeeded and the error message if it failed.\n    awaitCompletion = function(revisionId) {\n      stateMessages <- list(\n        publish_deferred = \"Content is currently publishing; your request will start soon.\",\n        publish_requested = \"Publish requested; waiting to start...\",\n        publish_started = \"Publish started.\",\n        fetching = \"Retrieving code...\",\n        building = \"Installing dependencies...\",\n        rendering = \"Rendering...\",\n        publishing = \"Publishing content...\",\n        published = \"Done.\"\n      )\n      lastStatus <- NULL\n      repeat {\n        path <- paste0(\"/revisions/\", revisionId)\n        revision <- withTokenRefreshRetry(GET, path)\n        newStatus <- revision$status\n        if (!isTRUE(newStatus == lastStatus)) {\n          # Note: since we poll every second, it's possible to skip states in\n          # the output here\n          cli::cli_alert_info(stateMessages[[newStatus]])\n          lastStatus <- newStatus\n        }\n\n        contentUrl <- paste0(\n          connectCloudUrls()$ui,\n          \"/\",\n          authInfo$username,\n          \"/content/\",\n          revision$content_id\n        )\n\n        if (!is.null(revision$publish_result)) {\n          if (revision$publish_result == \"failure\") {\n            # Try to retrieve logs if log channel is available\n            if (!is.null(revision$publish_log_channel)) {\n              tryCatch(\n                {\n                  # Get authorization token for the log channel\n                  authToken <- getAuthorization(\n                    revision$publish_log_channel\n                  )\n\n                  # Create logs client and fetch logs\n                  logsClient <- connectCloudLogsClient()\n                  logs <- logsClient$getLogs(\n                    revision$publish_log_channel,\n                    authToken\n                  )\n\n                  # Print logs to stderr\n                  if (!is.null(logs) && !is.null(logs$data)) {\n                    cli::cat_rule(\n                      \"Begin Publishing Log\",\n                      line = \"#\",\n                      file = stderr()\n                    )\n                    for (log_entry in logs$data) {\n                      local_timestamp <- as.POSIXct(\n                        # Convert to seconds\n                        log_entry$timestamp / 1e6,\n                        origin = \"1970-01-01\",\n                      )\n                      # Format with millisecond precision\n                      formatted_timestamp <- format(\n                        local_timestamp,\n                        \"%Y-%m-%d %H:%M:%OS3\"\n                      )\n                      cat(\n                        sprintf(\n                          \"[%s] %s: %s\\n\",\n                          formatted_timestamp,\n                          toupper(log_entry$level),\n                          log_entry$message\n                        ),\n                        file = stderr()\n                      )\n                    }\n                    cli::cat_rule(\n                      \"End Publishing Log\",\n                      line = \"#\",\n                      file = stderr()\n                    )\n                  }\n                },\n                error = function(e) {\n                  # If log retrieval fails, continue without logs\n                  # Don't fail the entire operation just because logs couldn't be retrieved\n                  cli::cli_alert_warning(\n                    \"Failed to retrieve logs: {e$message}\"\n                  )\n                }\n              )\n            }\n\n            return(list(\n              success = FALSE,\n              url = contentUrl,\n              error = revision$publish_error_details\n            ))\n          }\n\n          return(list(success = TRUE, url = contentUrl, error = NULL))\n        }\n\n        Sys.sleep(1)\n      }\n    },\n\n    getAuthorization = getAuthorization,\n\n    getAccounts = function(revisionId) {\n      GET(service, authInfo, \"/accounts?has_user_role=true\")\n    }\n  )\n}\n"
  },
  {
    "path": "R/client-connectCloudLogs.R",
    "content": "# Creates a client for interacting with the Connect Cloud Logs API.\nconnectCloudLogsClient <- function() {\n  list(\n    getLogs = function(logChannel, authToken) {\n      # Parse the logs URL to get service components\n      logsUrl <- connectCloudUrls()$logs\n      service <- parseHttpUrl(paste0(logsUrl, \"/v1\"))\n\n      # Create auth info with the authorization token\n      authInfo <- list(\n        accessToken = authToken\n      )\n\n      # Make the GET request to fetch logs\n      path <- paste0(\n        \"/logs/\",\n        logChannel,\n        \"?traversal_direction=backward&limit=1500\"\n      )\n      response <- GET(service, authInfo, path)\n\n      # Return the logs data\n      response\n    }\n  )\n}\n"
  },
  {
    "path": "R/client-identityFederation.R",
    "content": "# Attempt exchange an identity token sourced from Posit Workbench for an\n# ephemeral Connect API key. Returns NULL if this exchange fails or an API key\n# otherwise.\nattemptIdentityFederation <- function(serverUrl) {\n  cached <- getCachedApiKey(serverUrl)\n  if (!is.null(cached)) {\n    return(cached)\n  }\n\n  # Only attempt this in Workbench.\n  if (\n    Sys.getenv(\"POSIT_PRODUCT\") != \"WORKBENCH\" &&\n      !nzchar(Sys.getenv(\"RS_SERVER_ADDRESS\"))\n  ) {\n    return(NULL)\n  }\n\n  token <- tryCatch(rstudioapi::getIdentityToken(), error = function(e) NULL)\n  if (is.null(token)) {\n    return(NULL)\n  }\n\n  # Call Connect's exchange endpoint.\n  service <- parseHttpUrl(serverUrl)\n  body <- paste0(\n    \"grant_type=\",\n    urlEncode(\"urn:ietf:params:oauth:grant-type:token-exchange\"),\n    \"&subject_token_type=\",\n    urlEncode(\"urn:ietf:params:oauth:token-type:id_token\"),\n    \"&subject_token=\",\n    urlEncode(token$token),\n    \"&requested_token_type=\",\n    urlEncode(\"urn:posit:connect:api-key\")\n  )\n  tryCatch(\n    {\n      response <- POST(\n        service,\n        authInfo = list(),\n        path = \"/v1/oauth/integrations/credentials\",\n        contentType = \"application/x-www-form-urlencoded\",\n        content = body\n      )\n      apiKey <- response$access_token\n      if (!is.null(apiKey)) {\n        cacheApiKey(serverUrl, apiKey, token$expiry)\n      }\n      apiKey\n    },\n    error = function(e) NULL\n  )\n}\n\ncacheApiKey <- function(serverUrl, apiKey, expiry = NULL) {\n  env_poke(apiKeyCache, serverUrl, list(apiKey = apiKey, expiry = expiry))\n}\n\ngetCachedApiKey <- function(serverUrl) {\n  cached <- env_get(apiKeyCache, serverUrl, default = NULL)\n  if (is.null(cached)) {\n    return(NULL)\n  }\n\n  # Evict expired API keys.\n  if (!is.null(cached$expiry) && Sys.time() >= (cached$expiry - 60L)) {\n    env_unbind(apiKeyCache, serverUrl)\n    return(NULL)\n  }\n\n  cached$apiKey\n}\n\n# Session-level cache for ephemeral API keys.\napiKeyCache <- new.env(parent = emptyenv())\n"
  },
  {
    "path": "R/client-shinyapps.R",
    "content": "shinyAppsClient <- function(service, authInfo) {\n  list(\n    status = function() {\n      GET(service, authInfo, \"/internal/status\")\n    },\n\n    service = function() {\n      \"shinyapps.io\"\n    },\n\n    currentUser = function() {\n      GET(service, authInfo, \"/users/current/\")\n    },\n\n    accountsForUser = function(userId) {\n      path <- \"/accounts/\"\n      query <- \"\"\n      listRequest(service, authInfo, path, query, \"accounts\")\n    },\n\n    getAccountUsage = function(\n      accountId,\n      usageType = \"hours\",\n      applicationId = NULL,\n      from = NULL,\n      until = NULL,\n      interval = NULL\n    ) {\n      path <- paste(\n        \"/accounts/\",\n        accountId,\n        \"/usage/\",\n        usageType,\n        \"/\",\n        sep = \"\"\n      )\n      query <- list()\n      if (!is.null(applicationId)) {\n        query$application <- applicationId\n      }\n      if (!is.null(from)) {\n        query$from <- from\n      }\n      if (!is.null(until)) {\n        query$until <- until\n      }\n      if (!is.null(interval)) {\n        query$interval <- interval\n      }\n      GET(service, authInfo, path, queryString(query))\n    },\n\n    getBundle = function(bundleId) {\n      path <- paste(\"/bundles/\", bundleId, sep = \"\")\n      GET(service, authInfo, path)\n    },\n\n    updateBundleStatus = function(bundleId, status) {\n      path <- paste(\"/bundles/\", bundleId, \"/status\", sep = \"\")\n      json <- list()\n      json$status <- status\n      POST_JSON(service, authInfo, path, json)\n    },\n\n    createBundle = function(\n      application,\n      content_type,\n      content_length,\n      checksum\n    ) {\n      json <- list()\n      json$application <- application\n      json$content_type <- content_type\n      json$content_length <- content_length\n      json$checksum <- checksum\n      POST_JSON(service, authInfo, \"/bundles\", json)\n    },\n\n    listApplications = function(accountId, filters = list()) {\n      path <- \"/applications/\"\n      query <- paste(\n        filterQuery(\n          c(\"account_id\", \"type\", names(filters)),\n          c(accountId, \"shiny\", unname(filters))\n        ),\n        collapse = \"&\"\n      )\n      listRequest(service, authInfo, path, query, \"applications\")\n    },\n\n    getApplication = function(applicationId, deploymentRecordVersion) {\n      path <- paste(\"/applications/\", applicationId, sep = \"\")\n      application <- GET(service, authInfo, path)\n      application$application_id <- application$id\n      application\n    },\n\n    getApplicationMetrics = function(\n      applicationId,\n      series,\n      metrics,\n      from = NULL,\n      until = NULL,\n      interval = NULL\n    ) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/metrics/\",\n        series,\n        \"/\",\n        sep = \"\"\n      )\n      query <- list()\n      m <- paste(\n        lapply(metrics, function(x) {\n          paste(\"metric\", urlEncode(x), sep = \"=\")\n        }),\n        collapse = \"&\"\n      )\n      if (!is.null(from)) {\n        query$from <- from\n      }\n      if (!is.null(until)) {\n        query$until <- until\n      }\n      if (!is.null(interval)) {\n        query$interval <- interval\n      }\n      GET(service, authInfo, path, paste(m, queryString(query), sep = \"&\"))\n    },\n\n    getLogs = function(applicationId, entries = 50, format = NULL) {\n      path <- paste0(\"/applications/\", applicationId, \"/logs\")\n      query <- paste0(\"count=\", entries, \"&tail=0\")\n      if (!is.null(format)) {\n        # format=json returns a structured response.\n        query <- paste0(query, \"&format=\", format)\n      }\n      GET(service, authInfo, path, query)\n    },\n\n    createApplication = function(\n      name,\n      title,\n      template,\n      accountId,\n      appMode,\n      contentCategory = NULL\n    ) {\n      json <- list()\n      json$name <- name\n      # the title field is only used on connect\n      json$template <- template\n      json$account <- as.numeric(accountId)\n      application <- POST_JSON(service, authInfo, \"/applications/\", json)\n      list(\n        id = application$id,\n        application_id = application$id,\n        url = application$url\n      )\n    },\n\n    listApplicationProperties = function(applicationId) {\n      path <- paste(\"/applications/\", applicationId, \"/properties/\", sep = \"\")\n      GET(service, authInfo, path)\n    },\n\n    setApplicationProperty = function(\n      applicationId,\n      propertyName,\n      propertyValue,\n      force = FALSE\n    ) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/properties/\",\n        propertyName,\n        sep = \"\"\n      )\n      v <- list()\n      v$value <- propertyValue\n      query <- paste(\"force=\", if (force) \"1\" else \"0\", sep = \"\")\n      PUT_JSON(service, authInfo, path, v, query)\n    },\n\n    unsetApplicationProperty = function(\n      applicationId,\n      propertyName,\n      force = FALSE\n    ) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/properties/\",\n        propertyName,\n        sep = \"\"\n      )\n      query <- paste(\"force=\", if (force) \"1\" else \"0\", sep = \"\")\n      DELETE(service, authInfo, path, query)\n    },\n\n    uploadApplication = function(applicationId, bundlePath) {\n      path <- paste(\"/applications/\", applicationId, \"/upload\", sep = \"\")\n      POST(\n        service,\n        authInfo,\n        path,\n        contentType = \"application/x-gzip\",\n        file = bundlePath\n      )\n    },\n\n    deployApplication = function(application, bundleId = NULL) {\n      path <- paste(\"/applications/\", application$id, \"/deploy\", sep = \"\")\n      json <- list()\n      if (length(bundleId) > 0 && nzchar(bundleId)) {\n        json$bundle <- as.numeric(bundleId)\n      } else {\n        json$rebuild <- FALSE\n      }\n      POST_JSON(service, authInfo, path, json)\n    },\n\n    terminateApplication = function(applicationId) {\n      path <- paste(\"/applications/\", applicationId, \"/terminate\", sep = \"\")\n      POST(service, authInfo, path)\n    },\n\n    purgeApplication = function(applicationId) {\n      path <- paste(\"/applications/\", applicationId, \"/purge\", sep = \"\")\n      POST(service, authInfo, path)\n    },\n\n    inviteApplicationUser = function(\n      applicationId,\n      email,\n      invite_email = NULL,\n      invite_email_message = NULL\n    ) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/authorization/users\",\n        sep = \"\"\n      )\n      json <- list()\n      json$email <- email\n      if (!is.null(invite_email)) {\n        json$invite_email <- invite_email\n      }\n      if (!is.null(invite_email_message)) {\n        json$invite_email_message <- invite_email_message\n      }\n      POST_JSON(service, authInfo, path, json)\n    },\n\n    addApplicationUser = function(applicationId, userId) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/authorization/users/\",\n        userId,\n        sep = \"\"\n      )\n      PUT(service, authInfo, path, NULL)\n    },\n\n    removeApplicationUser = function(applicationId, userId) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/authorization/users/\",\n        userId,\n        sep = \"\"\n      )\n      DELETE(service, authInfo, path, NULL)\n    },\n\n    listApplicationAuthorization = function(applicationId) {\n      path <- paste(\"/applications/\", applicationId, \"/authorization\", sep = \"\")\n      listRequest(service, authInfo, path, NULL, \"authorization\")\n    },\n\n    listApplicationUsers = function(applicationId) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/authorization/users\",\n        sep = \"\"\n      )\n      listRequest(service, authInfo, path, NULL, \"users\")\n    },\n\n    listApplicationGroups = function(applicationId) {\n      path <- paste(\n        \"/applications/\",\n        applicationId,\n        \"/authorization/groups\",\n        sep = \"\"\n      )\n      listRequest(service, authInfo, path, NULL, \"groups\")\n    },\n\n    listApplicationInvitations = function(applicationId) {\n      path <- \"/invitations/\"\n      query <- paste(filterQuery(\"app_id\", applicationId), collapse = \"&\")\n      listRequest(service, authInfo, path, query, \"invitations\")\n    },\n\n    resendApplicationInvitation = function(invitationId, regenerate = FALSE) {\n      path <- paste(\"/invitations/\", invitationId, \"/send\", sep = \"\")\n      json <- list()\n      json$regenerate <- regenerate\n      POST_JSON(service, authInfo, path, json)\n    },\n\n    listTasks = function(accountId, filters = NULL) {\n      if (is.null(filters)) {\n        filters <- vector()\n      }\n      path <- \"/tasks/\"\n      filters <- c(filterQuery(\"account_id\", accountId), filters)\n      query <- paste(filters, collapse = \"&\")\n      listRequest(service, authInfo, path, query, \"tasks\", max = 100)\n    },\n\n    getTaskInfo = function(taskId) {\n      path <- paste(\"/tasks/\", taskId, sep = \"\")\n      GET(service, authInfo, path)\n    },\n\n    getTaskLogs = function(taskId) {\n      path <- paste(\"/tasks/\", taskId, \"/logs/\", sep = \"\")\n      GET(service, authInfo, path)\n    },\n\n    waitForTask = function(taskId, quiet = FALSE) {\n      if (!quiet) {\n        cat(\"Waiting for task: \", taskId, \"\\n\", sep = \"\")\n      }\n\n      path <- paste(\"/tasks/\", taskId, sep = \"\")\n\n      lastStatus <- NULL\n      while (TRUE) {\n        # check status\n        status <- GET(service, authInfo, path)\n\n        # display status to the user if it changed\n        if (!identical(lastStatus, status$description)) {\n          if (!quiet) {\n            cat(\"  \", status$status, \": \", status$description, \"\\n\", sep = \"\")\n          }\n          lastStatus <- status$description\n        }\n\n        # are we finished? (note: this codepath is the only way to exit this function)\n        if (status$finished) {\n          if (identical(status$status, \"success\")) {\n            return(NULL)\n          } else {\n            # always show task log on error\n            cli::cat_rule(\"Begin Task Log\", line = \"#\")\n            taskLog(taskId, authInfo$name, authInfo$server, output = \"stderr\")\n            cli::cat_rule(\"End Task Log\", line = \"#\")\n            stop(status$error, call. = FALSE)\n          }\n        }\n\n        # wait for 1 second before polling again\n        Sys.sleep(1)\n      }\n    }\n  )\n}\n"
  },
  {
    "path": "R/client.R",
    "content": "clientForAccount <- function(account) {\n  serverInfo <- serverInfo(account$server)\n  account$certificate <- serverInfo$certificate\n  serverUrl <- parseHttpUrl(serverInfo$url)\n\n  if (isShinyappsServer(account$server)) {\n    shinyAppsClient(serverUrl, account)\n  } else if (isPositConnectCloudServer(account$server)) {\n    connectCloudClient(serverUrl, account)\n  } else if (isSPCSServer(account$server)) {\n    account$snowflakeToken <- getSnowflakeAuthToken(\n      serverInfo$url,\n      account$snowflakeConnectionName\n    )\n    connectClient(serverUrl, account)\n  } else {\n    # Standard Connect server - try identity federation if no credentials\n    if (hasNoCredentials(account)) {\n      ephemeralApiKey <- attemptIdentityFederation(serverInfo$url)\n      if (!is.null(ephemeralApiKey)) {\n        account$apiKey <- ephemeralApiKey\n      }\n    }\n    connectClient(serverUrl, account)\n  }\n}\n\nhasNoCredentials <- function(account) {\n  is.null(account$apiKey) &&\n    is.null(account$token) &&\n    is.null(account$secret) &&\n    is.null(account$private_key) &&\n    is.null(account$accessToken)\n}\n\n# Appropriate when the list API includes \"count\" and \"total\" fields in the response JSON and the API\n# supports pagination with the query arguments count=PAGE_SIZE&offset=STARTING_POINT.\nlistRequest <- function(\n  service,\n  authInfo,\n  path,\n  query,\n  listName,\n  page = 100,\n  max = NULL\n) {\n  # accumulate multiple pages of results\n  offset <- 0\n  results <- list()\n\n  repeat {\n    # add query params\n    queryWithList <- paste(query, \"&count=\", page, \"&offset=\", offset, sep = \"\")\n\n    # make request and append the results\n    response <- GET(service, authInfo, path, queryWithList)\n    results <- append(results, response[[listName]])\n\n    # update the offset\n    offset <- offset + response$count\n\n    # get all results if no max was specified\n    if (is.null(max)) {\n      max <- response$total\n    }\n\n    # exit if we've got them all\n    if (length(results) >= response$total || length(results) >= max) {\n      break\n    }\n  }\n\n  return(results)\n}\n\n# /__api__/applications response with { applications: [], count: M, total: N, continuation: \"CONTINUATION\" }\n# To paginate, use the query arguments cont=CONTINUATION&start=START&count=MAX\nlistApplicationsRequest <- function(\n  service,\n  authInfo,\n  path,\n  query,\n  listName,\n  page = 100,\n  max = NULL\n) {\n  # accumulate multiple pages of results\n  start <- 0\n  cont <- \"\"\n  results <- list()\n\n  repeat {\n    # add query params\n    queryWithList <- paste(\n      query,\n      \"&count=\",\n      page,\n      \"&start=\",\n      start,\n      \"&cont=\",\n      cont,\n      sep = \"\"\n    )\n\n    # make request and append the results\n    response <- GET(service, authInfo, path, queryWithList)\n    results <- append(results, response[[listName]])\n\n    # update the starting point for the next request\n    start <- start + response$count\n    cont <- response$continuation\n\n    # get all results if no max was specified\n    if (is.null(max)) {\n      max <- response$total\n    }\n\n    # exit if we've got them all\n    if (length(results) >= response$total || length(results) >= max) {\n      break\n    }\n  }\n\n  return(results)\n}\n\nfilterQuery <- function(param, value, operator = NULL) {\n  if (is.null(operator)) {\n    op <- \":\"\n  } else {\n    op <- paste(\":\", operator, \":\", sep = \"\")\n  }\n  q <- paste(\"filter=\", param, op, value, sep = \"\")\n  return(q)\n}\n\nisContentType <- function(x, contentType) {\n  grepl(contentType, x, fixed = TRUE)\n}\n\n# These functions should move to client-shinyapps.R\nuploadShinyappsBundle <- function(\n  client,\n  application_id,\n  bundlePath,\n  verbose = FALSE\n) {\n  # Step 1. Create presigned URL and register pending bundle.\n  bundleSize <- file.info(bundlePath)$size\n  bundle <- client$createBundle(\n    application_id,\n    content_type = \"application/x-tar\",\n    content_length = bundleSize,\n    checksum = fileMD5(bundlePath)\n  )\n\n  # Step 2. Upload Bundle to presigned URL\n  logger <- verboseLogger(verbose)\n  logger(\"Starting upload now\")\n  if (!uploadBundle(bundle, bundleSize, bundlePath)) {\n    stop(\"Could not upload file.\")\n  }\n  logger(\"Upload complete\")\n\n  # Step 3. Upload revise bundle status.\n  response <- client$updateBundleStatus(bundle$id, status = \"ready\")\n\n  # Step 4. Retrieve updated bundle post status change\n  client$getBundle(bundle$id)\n}\n\n\nuploadBundle <- function(bundle, bundleSize, bundlePath) {\n  presigned_service <- parseHttpUrl(bundle$presigned_url)\n\n  headers <- list()\n  headers$`Content-Type` <- \"application/x-tar\"\n  headers$`Content-Length` <- bundleSize\n\n  # AWS requires a base64 encoded hash\n  headers$`Content-MD5` <- bundle$presigned_checksum\n\n  # AWS seems very sensitive to additional headers (likely becauseit was not included and signed\n  # for when the presigned link was created). So the lower level library is used here.\n  response <- httpLibCurl(\n    presigned_service$protocol,\n    presigned_service$host,\n    presigned_service$port,\n    \"PUT\",\n    presigned_service$path,\n    headers,\n    headers$`Content-Type`,\n    bundlePath\n  )\n\n  response$status == 200\n}\n"
  },
  {
    "path": "R/config.R",
    "content": "#' rsconnect Configuration Directory\n#'\n#' Forms the path to a location on disk where user-level configuration data for\n#' the package is stored.\n#'\n#' @param subDir An optional subdirectory to be included as the last element of\n#'   the path.\n#'\n#' @return The path to the configuration directory.\n#'\n#' @keywords internal\nrsconnectConfigDir <- function(subDir = NULL) {\n  configDir <- applicationConfigDir()\n\n  # If the configuration directory doesn't exist, see if there's an old one to\n  # migrate\n  if (!dirExists(configDir)) {\n    migrateConfig(configDir)\n  }\n\n  # Form the target and append the optional subdirectory if given\n  target <- configDir\n  if (!is.null(subDir)) {\n    target <- file.path(target, subDir)\n  }\n\n  dirCreate(target)\n}\n\n#' Application Configuration Directory\n#'\n#' Returns the root path used to store per user configuration data. Does not\n#' check old locations or create the path; use \\code{rsconnectConfigDir} for\n#' most cases.\n#'\n#' @return A string containing the path of the configuration folder.\n#'\n#' @keywords internal\napplicationConfigDir <- function() {\n  if (exists(\"R_user_dir\", envir = asNamespace(\"tools\"))) {\n    # In newer versions of R (>=4.0), we can ask R itself where configuration should be stored.\n    # Load from the namespace to avoid check warnings with old R.\n    f <- get(\"R_user_dir\", envir = asNamespace(\"tools\"))\n    f(\"rsconnect\", \"config\")\n  } else {\n    # In older versions of R, use an implementation derived from R_user_dir\n    home <- Sys.getenv(\"HOME\", unset = normalizePath(\"~\"))\n    path <-\n      if (nzchar(p <- Sys.getenv(\"R_USER_CONFIG_DIR\"))) {\n        p\n      } else if (nzchar(p <- Sys.getenv(\"XDG_CONFIG_HOME\"))) {\n        p\n      } else if (.Platform$OS.type == \"windows\") {\n        file.path(Sys.getenv(\"APPDATA\"), \"R\", \"config\")\n      } else if (Sys.info()[\"sysname\"] == \"Darwin\") {\n        file.path(home, \"Library\", \"Preferences\", \"org.R-project.R\")\n      } else {\n        file.path(home, \".config\")\n      }\n    file.path(path, \"R\", \"rsconnect\")\n  }\n}\n\n# server ------------------------------------------------------------------\n\nserverConfigDir <- function() {\n  rsconnectConfigDir(\"servers\")\n}\n\nserverConfigFile <- function(name) {\n  normalizePath(\n    file.path(serverConfigDir(), paste(name, \".dcf\", sep = \"\"))\n  )\n}\n\nserverConfigFiles <- function() {\n  list.files(serverConfigDir(), pattern = glob2rx(\"*.dcf\"), full.names = TRUE)\n}\n\n# account -----------------------------------------------------------------\n\naccountConfigDir <- function() {\n  rsconnectConfigDir(\"accounts\")\n}\n\naccountConfigFile <- function(name, server) {\n  normalizePath(\n    file.path(accountConfigDir(), server, paste(name, \".dcf\", sep = \"\"))\n  )\n}\n\naccountConfigFiles <- function(server = NULL) {\n  path <- accountConfigDir()\n  if (!is.null(server)) {\n    path <- file.path(path, server)\n  }\n\n  list.files(\n    path,\n    pattern = glob2rx(\"*.dcf\"),\n    recursive = TRUE,\n    full.names = TRUE\n  )\n}\n\n\n# deployments -------------------------------------------------------------\n\n# returns the path to the history file. When new=TRUE, computes a temporary location to use during\n# history-file modification. The caller is responsible for removing this temporary file.\ndeploymentHistoryPath <- function(new = FALSE) {\n  dir <- rsconnectConfigDir(\"deployments\")\n  if (new) {\n    tempfile(pattern = \"history\", tmpdir = dir, fileext = \".dcf\")\n  } else {\n    file.path(dir, \"history.dcf\")\n  }\n}\n\n# given a path, return the directory under which rsconnect package state is\n# stored\ndeploymentConfigDir <- function(recordPath) {\n  if (isDocumentPath(recordPath)) {\n    file.path(\n      dirname(recordPath),\n      \"rsconnect\",\n      \"documents\",\n      basename(recordPath)\n    )\n  } else {\n    file.path(recordPath, \"rsconnect\")\n  }\n}\n\ndeploymentConfigFile <- function(recordPath, name, account, server) {\n  accountDir <- file.path(deploymentConfigDir(recordPath), server, account)\n  dirCreate(accountDir)\n  file.path(accountDir, paste0(name, \".dcf\"))\n}\n\ndeploymentConfigFiles <- function(recordPath) {\n  dir <- deploymentConfigDir(recordPath)\n  list.files(dir, glob2rx(\"*.dcf\"), recursive = TRUE, full.names = TRUE)\n}\n\n# Does the path point to an individual piece of content?\nisDocumentPath <- function(recordPath) {\n  # deployDoc and deployApp have already decided if the record path corresponds to a file\n  # (single-file document deploys) or a directory (all other content).\n  #\n  # The record path is a document path whenever it is not a directory.\n  !dir.exists(recordPath)\n}\n"
  },
  {
    "path": "R/configMigrate.R",
    "content": "# account/server ----------------------------------------------------------\n\nmigrateConfig <- function(configDir) {\n  # For historical reasons too painful to enumerate here, there are not one\n  # but *two* old locations for configuration files to check. If we find\n  # one, we need to move its contents to the new folder.\n  oldConfigDir <- oldApplicationConfigDir(\"rsconnect\")\n  if (!dirExists(oldConfigDir)) {\n    oldConfigDir <- oldApplicationConfigDir(\"connect\")\n  }\n\n  # We have no configuration directory but we do have an old one; migrate it.\n  if (dirExists(oldConfigDir)) {\n    # Create the parent folder if necessary\n    dirCreate(dirname(configDir))\n\n    # Migrate the old directory to the new one\n    file.rename(oldConfigDir, configDir)\n  }\n}\n\n#' Old Application Config Directory\n#'\n#' Returns the old application configuration directory used by rsconnect\n#' 0.8.24 and prior. These versions wrote configuration data to XDG compliant\n#' locations, but CRAN policy has since further restricted the disk locations\n#' that are permitted. See:\n#'\n#' https://cran.r-project.org/web/packages/policies.html\n#'\n#' @param appName The application's name (connect or rsconnect)\n#'\n#' @return The old application configuration directory.\n#'\n#' @keywords internal\noldApplicationConfigDir <- function(appName) {\n  # get the home directory from the operating system (in case\n  # the user has redefined the meaning of ~) but fault back\n  # to ~ if there is no HOME variable defined\n  homeDir <- Sys.getenv(\"HOME\", unset = \"~\")\n\n  # check for R specific config dir\n  configDir <- Sys.getenv(\"R_USER_CONFIG_DIR\")\n\n  if (nzchar(configDir)) {\n    # R specific config dir, append app name only\n    configDir <- file.path(configDir, appName)\n  } else {\n    # no R specific config dir; determine application config dir (platform specific)\n    sysName <- Sys.info()[[\"sysname\"]]\n    if (identical(sysName, \"Windows\")) {\n      configDir <- Sys.getenv(\"APPDATA\")\n    } else if (identical(sysName, \"Darwin\")) {\n      configDir <- file.path(homeDir, \"Library/Application Support\")\n    } else {\n      configDir <- Sys.getenv(\"XDG_CONFIG_HOME\", file.path(homeDir, \".config\"))\n    }\n\n    # append the application name\n    configDir <- file.path(configDir, \"R\", appName)\n  }\n\n  # normalize path\n  normalizePath(configDir)\n}\n\n# deployments -------------------------------------------------------------\n\nmigrateDeploymentsConfig <- function(appPath) {\n  # calculate migration dir--all shinyapps deployment records go into the root\n  # folder since it wasn't possible to deploy individual docs using the\n  # shinyapps package\n  migrateRoot <- if (isDocumentPath(appPath)) {\n    dirname(appPath)\n  } else {\n    appPath\n  }\n\n  # migrate shinyapps package created records if necessary\n  shinyappsDir <- file.path(migrateRoot, \"shinyapps\")\n  if (!file.exists(shinyappsDir)) {\n    return()\n  }\n\n  migrateDir <- file.path(migrateRoot, \"rsconnect\")\n  for (shinyappsFile in list.files(\n    shinyappsDir,\n    glob2rx(\"*.dcf\"),\n    recursive = TRUE\n  )) {\n    # read deployment record\n    shinyappsDCF <- file.path(shinyappsDir, shinyappsFile)\n    deployment <- as.data.frame(\n      read.dcf(shinyappsDCF),\n      stringsAsFactors = FALSE\n    )\n    deployment$server <- \"shinyapps.io\"\n\n    # write the new record\n    rsconnectDCF <- file.path(migrateDir, \"shinyapps.io\", shinyappsFile)\n    dirCreate(dirname(rsconnectDCF))\n    write.dcf(deployment, rsconnectDCF)\n\n    # remove old DCF\n    file.remove(shinyappsDCF)\n  }\n\n  # remove shinyapps dir if it's completely empty\n  remainingFiles <- list.files(shinyappsDir, recursive = TRUE, all.files = TRUE)\n  if (length(remainingFiles) == 0) {\n    unlink(shinyappsDir, recursive = TRUE)\n  }\n}\n"
  },
  {
    "path": "R/configureApp.R",
    "content": "#' Configure an Application\n#'\n#' @description\n#' Configure an application running on a remote server.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @inheritParams deployApp\n#'\n#' @param appName Name of application to configure\n#' @param appDir Directory containing application. Defaults to\n#'   current working directory.\n#' @inheritParams deployApp\n#' @param redeploy Re-deploy application after its been configured.\n#' @param size Configure application instance size\n#' @param instances Configure number of application instances\n#' @examples\n#' \\dontrun{\n#'\n#' # set instance size for an application\n#' configureApp(\"myapp\", size=\"xlarge\")\n#' }\n#' @seealso [applications()], [deployApp()]\n#' @note This function works only for ShinyApps servers.\n#' @export\nconfigureApp <- function(\n  appName,\n  appDir = getwd(),\n  account = NULL,\n  server = NULL,\n  redeploy = TRUE,\n  size = NULL,\n  instances = NULL,\n  logLevel = c(\"normal\", \"quiet\", \"verbose\")\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  if (is.null(appName)) {\n    appName <- basename(appDir)\n  }\n  application <- resolveApplication(accountDetails, appName)\n\n  displayStatus <- displayStatus(identical(logLevel, \"quiet\"))\n\n  # some properties may required a rebuild to take effect\n  rebuildRequired <- FALSE\n\n  # get a list of properties to set\n  properties <- list()\n  if (!is.null(size)) {\n    properties[[\"application.instances.template\"]] <- size\n  }\n  if (!is.null(instances)) {\n    properties[[\"application.instances.count\"]] <- instances\n  }\n\n  # set application properties\n  client <- clientForAccount(accountDetails)\n  for (i in names(properties)) {\n    propertyName <- i\n    propertyValue <- properties[[i]]\n\n    # dispatch to the appropriate client implementation\n    if (is.function(client$setApplicationProperty)) {\n      client$setApplicationProperty(\n        application$id,\n        propertyName,\n        propertyValue\n      )\n    } else {\n      stop(\n        \"Server \",\n        accountDetails$server,\n        \" has no appropriate configuration method.\"\n      )\n    }\n  }\n\n  # redeploy application if requested\n  if (redeploy) {\n    if (length(properties) > 0) {\n      deployApp(\n        appDir = appDir,\n        appName = appName,\n        account = account,\n        logLevel = logLevel,\n        upload = rebuildRequired\n      )\n    } else {\n      displayStatus(\"No configuration changes to deploy\")\n    }\n  }\n}\n\n#' Set Application property\n#'\n#' @description\n#' Set a property on currently deployed ShinyApps application.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param propertyName Name of property\n#' @param propertyValue Property value\n#' @param appName Name of application\n#' @param appPath Directory or file that was deployed. Defaults to current\n#'   working directory.\n#' @inheritParams deployApp\n#' @param force Forcibly set the property\n#'\n#' @note This function only works for ShinyApps servers.\n#'\n#' @examples\n#' \\dontrun{\n#'\n#' # set instance size for an application\n#' setProperty(\"application.instances.count\", 1)\n#'\n#' # disable application package cache\n#' setProperty(\"application.package.cache\", FALSE)\n#'\n#' }\n#' @export\nsetProperty <- function(\n  propertyName,\n  propertyValue,\n  appPath = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  force = FALSE\n) {\n  deployment <- findDeployment(\n    appPath = appPath,\n    appName = appName,\n    server = server,\n    account = account\n  )\n  accountDetails <- accountInfo(deployment$account, deployment$server)\n  checkShinyappsServer(accountDetails$server)\n\n  client <- clientForAccount(accountDetails)\n  application <- getAppByName(client, accountDetails, deployment$name)\n\n  invisible(client$setApplicationProperty(\n    application$id,\n    propertyName,\n    propertyValue,\n    force\n  ))\n}\n\n#' Unset Application property\n#'\n#' @description\n#' Unset a property on currently deployed ShinyApps application (restoring to\n#' its default value)\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @inheritParams setProperty\n#' @param force Forcibly unset the property\n#'\n#' @note This function only works for ShinyApps servers.\n#'\n#' @examples\n#' \\dontrun{\n#'\n#' # unset application package cache property to revert to default\n#' unsetProperty(\"application.package.cache\")\n#'\n#' }\n#' @export\nunsetProperty <- function(\n  propertyName,\n  appPath = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  force = FALSE\n) {\n  deployment <- findDeployment(\n    appPath = appPath,\n    appName = appName,\n    server = server,\n    account = account\n  )\n  accountDetails <- accountInfo(deployment$account, deployment$server)\n  checkShinyappsServer(accountDetails$server)\n\n  client <- clientForAccount(accountDetails)\n  application <- getAppByName(client, accountInfo, deployment$name)\n\n  invisible(client$unsetApplicationProperty(\n    application$id,\n    propertyName,\n    force\n  ))\n}\n\n\n#' Show Application property\n#'\n#' @description\n#' Show properties of an application deployed to ShinyApps.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appName Name of application\n#' @param appPath Directory or file that was deployed. Defaults to current\n#'   working directory.\n#' @inheritParams deployApp\n#'\n#' @note This function works only for ShinyApps servers.\n#'\n#' @export\nshowProperties <- function(\n  appPath = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n) {\n  deployment <- findDeployment(\n    appPath = appPath,\n    appName = appName,\n    account = account,\n    server = server\n  )\n  accountDetails <- accountInfo(deployment$account, deployment$server)\n  checkShinyappsServer(accountDetails$server)\n\n  client <- clientForAccount(accountDetails)\n  application <- getAppByName(client, accountDetails, deployment$name)\n\n  # convert to data frame\n  res <- do.call(rbind, application$deployment$properties)\n  df <- as.data.frame(res, stringsAsFactors = FALSE)\n  names(df) <- c(\"value\")\n  return(df)\n}\n"
  },
  {
    "path": "R/cookies.R",
    "content": "# Environment in which cookies will be stored. Cookies are expected to survive\n# the duration of the R session, but are not persisted outside of the R\n# session.\n.cookieStore <- new.env(parent = emptyenv())\n\n# Returns the cookies associated with a particular host/port\n# If no hostname is specified, returns all cookies\ngetCookies <- function(hostname, port = NULL) {\n  if (missing(hostname)) {\n    hosts <- ls(envir = .cookieStore)\n    cookies <- lapply(hosts, function(h) {\n      getCookiesHostname(h)\n    })\n    do.call(\"rbind\", cookies)\n  } else {\n    host <- getCookieHost(list(host = hostname, port = port))\n    getCookiesHostname(host)\n  }\n}\n\n# Get cookies for a particular hostname(:port)\ngetCookiesHostname <- function(host) {\n  if (!exists(host, .cookieStore)) {\n    NULL\n  } else {\n    cookies <- get(host, envir = .cookieStore)\n    cookies$host <- host\n    cookies\n  }\n}\n\n# Clears the cookies associated with a particular hostname/port combination.\n# If hostname and port are omitted, clears all the cookies\nclearCookies <- function(hostname, port = NULL) {\n  if (missing(hostname)) {\n    rm(list = ls(envir = .cookieStore), envir = .cookieStore)\n  } else {\n    host <- getCookieHost(list(host = hostname, port = port))\n    rm(list = host, envir = .cookieStore)\n  }\n}\n\n# Parse out the raw headers provided and insert them into the cookieStore\n# NOTE: Domain attribute is currently ignored\n# @param requestURL the parsed URL as returned from `parseHttpUrl`\n# @param cookieHeaders a list of characters strings representing the raw\n#   Set-Cookie header value with the \"Set-Cookie: \" prefix omitted\nstoreCookies <- function(requestURL, cookieHeaders) {\n  cookies <- lapply(cookieHeaders, parseCookie, requestPath = requestURL$path)\n\n  # Filter out invalid cookies (which would return as NULL)\n  cookies <- Filter(Negate(is.null), cookies)\n\n  host <- getCookieHost(requestURL)\n\n  hostCookies <- NULL\n  if (!exists(host, .cookieStore)) {\n    # Create a new data frame for this host\n    hostCookies <- data.frame(\n      path = character(0),\n      name = character(0),\n      value = character(0),\n      secure = logical(0),\n      expires = character(0),\n      stringsAsFactors = FALSE\n    )\n  } else {\n    hostCookies <- get(host, envir = .cookieStore)\n  }\n\n  lapply(cookies, function(co) {\n    # Remove any duplicates\n    # RFC says duplicate cookies are ones that have the same domain, name, and path\n    hostCookies <<- hostCookies[\n      !(co$name == hostCookies$name & co$path == hostCookies$path),\n    ]\n\n    # append this new cookie on\n    hostCookies <<- rbind(\n      as.data.frame(co, stringsAsFactors = FALSE),\n      hostCookies\n    )\n  })\n\n  # Save this host's cookies into the cookies store.\n  assign(host, hostCookies, envir = .cookieStore)\n}\n\n# Parse out an individual cookie\n# @param cookieHeader the raw text contents of the Set-Cookie header with the\n#   header name omitted.\n# @param requestPath the parsed URL as returned from `parseHttpUrl`\nparseCookie <- function(cookieHeader, requestPath = NULL) {\n  keyval <- regmatches(\n    cookieHeader,\n    regexec(\n      # https://curl.haxx.se/rfc/cookie_spec.html\n      # \"characters excluding semi-colon, comma and white space\"\n      # white space is not excluded from values so we can capture `expires`\n      \"^([^;=, ]+)\\\\s*=\\\\s*([^;,]*)(;|$)\",\n      cookieHeader,\n      ignore.case = TRUE\n    )\n  )[[1]]\n  if (length(keyval) == 0) {\n    # Invalid cookie format.\n    warning(\"Unable to parse set-cookie header: \", cookieHeader)\n    return(NULL)\n  }\n  key <- keyval[2]\n  val <- keyval[3]\n\n  # Path\n  path <- regmatches(\n    cookieHeader,\n    regexec(\n      \"^.*\\\\sPath\\\\s*=\\\\s*([^;]+)(;|$).*$\",\n      cookieHeader,\n      ignore.case = TRUE\n    )\n  )[[1]]\n  if (length(path) == 0) {\n    path <- \"/\"\n  } else {\n    path <- path[2]\n  }\n\n  # Per the RFC, the cookie's path must be a prefix of the request URL\n  if (!is.null(requestPath) && !hasPrefix(requestPath, path)) {\n    warning(\n      \"Invalid path set for cookie on request for '\",\n      requestPath,\n      \"': \",\n      cookieHeader\n    )\n    return(NULL)\n  }\n\n  # Cookies without max-age= or expires= are session cookies.\n  #\n  # Mark session cookies as never expiring (far in the future). Cookies\n  # are managed in-memory and never survive beyond the current R session.\n  expires <- Sys.time() + 10^10\n\n  # Max-Age takes precedence over Expires.\n  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie\n  maxage <- regmatches(\n    cookieHeader,\n    regexec(\n      \"^.*\\\\sMax-Age\\\\s*=\\\\s*(-?\\\\d+)(;|$).*$\",\n      cookieHeader,\n      ignore.case = TRUE\n    )\n  )[[1]]\n  if (length(maxage) > 0) {\n    # Compute time maxage seconds from now\n    expires <- Sys.time() + as.numeric(maxage[2])\n  } else {\n    expiration_value <- regmatches(\n      cookieHeader,\n      regexec(\n        \"^.*\\\\sExpires\\\\s*=\\\\s*([^;]+)(;|$).*$\",\n        cookieHeader,\n        ignore.case = TRUE\n      )\n    )[[1]]\n    if (length(expiration_value) > 0) {\n      # Time format is Wdy, DD-Mon-YYYY HH:MM:SS GMT\n      parsed <- curl::parse_date(expiration_value[2])\n      if (!is.na(parsed)) {\n        expires <- parsed\n      }\n    }\n  }\n\n  # Secure\n  secure <- grepl(\";\\\\s+Secure(;|$)\", cookieHeader, ignore.case = TRUE)\n\n  list(name = key, value = val, expires = expires, path = path, secure = secure)\n}\n\n# Appends a cookie header from the .cookieStore to the existing set of headers\n# @param requestURL the parsed URL as returned from `parseHttpUrl`\n# @param headers a named character vector containing the set of headers to be extended\nappendCookieHeaders <- function(requestURL, headers) {\n  host <- getCookieHost(requestURL)\n\n  if (!exists(host, .cookieStore)) {\n    # Nothing to do\n    return(headers)\n  }\n\n  cookies <- get(host, envir = .cookieStore)\n\n  # If any cookies are expired, remove them from the cookie store\n  if (isTRUE(any(cookies$expires < as.integer(Sys.time())))) {\n    cookies <- cookies[cookies$expires >= as.integer(Sys.time()), ]\n    # Update the store, removing the expired cookies\n    assign(host, cookies, envir = .cookieStore)\n  }\n\n  if (nrow(cookies) == 0) {\n    # all cookies expired.\n    return(headers)\n  }\n\n  # Filter to only include cookies that match the path prefix\n  cookies <- cookies[\n    substring(requestURL$path, 1, nchar(cookies$path)) == cookies$path,\n  ]\n\n  # If insecure channel, filter out secure cookies\n  if (tolower(requestURL$protocol) != \"https\") {\n    cookies <- cookies[!cookies$secure, ]\n  }\n\n  # TODO: Technically per the RFC we're supposed to order these cookies by which\n  # paths most specifically match the request.\n  cookieHeader <- paste(\n    apply(cookies, 1, function(x) paste0(x[\"name\"], \"=\", x[\"value\"])),\n    collapse = \"; \"\n  )\n\n  c(headers, cookie = cookieHeader)\n}\n\ngetCookieHost <- function(requestURL) {\n  host <- requestURL$host\n  port <- requestURL$port\n  if (!is.null(port) && nchar(port) > 0) {\n    port <- sub(\"^:\", \"\", port)\n    # By my reading of the RFC, we technically only need to include the port #\n    # in the index if the host is an IP address. But here we're including the\n    # port number as a part of the host whether using a domain name or IP.\n    # Erring on the side of not sending the cookies to the wrong services\n    host <- paste(host, port, sep = \":\")\n  }\n  host\n}\n\nshowCookies <- function(urlstr) {\n  url <- parseHttpUrl(urlstr)\n  cat(\"Cookies:\", \"\\n\")\n  host <- getCookieHost(url)\n  if (exists(host, .cookieStore)) {\n    print(get(host, envir = .cookieStore))\n  } else {\n    print(\"None\")\n  }\n}\n"
  },
  {
    "path": "R/deployAPI.R",
    "content": "#' Deploy a Plumber API\n#'\n#' @description\n#' Deploys an application consisting of plumber API routes. The given directory\n#' must contain a script returning a `plumb` object or a plumber API definition.\n#'\n#' Supported servers: Posit Connect and ShinyApps servers\n#'\n#' @param api Path to the API project directory. Must contain either\n#'   `entrypoint.R` or `plumber.R` (for plumber APIs) or `_server.yml` (for\n#'   plumber2 APIs)\n#' @param ... Additional arguments to [deployApp()].\n#'\n#' @details Deploy a plumber API definition by either supplying a directory\n#'   containing `plumber.R` (an API definition) or `entrypoint.R` that returns a\n#'   `plumb` object created by `plumber::plumb()`. See the plumber documentation\n#'   for more information. Alternatively, deploy a plumber2 API by supplying a\n#'   directory containing `_server.yml`.\n#'\n#' @family Deployment functions\n#' @export\ndeployAPI <- function(api, ...) {\n  check_directory(api)\n\n  # Checking for entrypoint.R or plumber.R is done in `lint-framework.R`\n  deployApp(appDir = api, ...)\n}\n"
  },
  {
    "path": "R/deployApp.R",
    "content": "#' Deploy an Application\n#'\n#' @description\n#' Deploy a [shiny][shiny::shiny-package] application, an\n#' [RMarkdown][rmarkdown::rmarkdown-package] document, a plumber API, or HTML\n#' content to a server.\n#'\n#' Supported servers: All servers\n#'\n#' @details\n#' ## Deployment records\n#'\n#' When deploying an app, `deployApp()` will save a deployment record that\n#' makes it easy to update the app on server from your local source code. This\n#' generally means that you need to only need to supply important arguments\n#' (e.g. `appName`, `appTitle`, `server`/`account`) on the first deploy, and\n#' rsconnect will reuse the same settings on subsequent deploys.\n#'\n#' The metadata needs to make this work is stored in `{appDir}/rsconnect/`.\n#' You should generally check these files into version control to ensure that\n#' future you and other collaborators will publish to the same location.\n#'\n#' If you have lost this directory, all is not lost, as `deployApp()` will\n#' attempt to rediscover existing deployments. This is easiest if you are\n#' updating an app that you created, as you can just supply the `appName`\n#' (and `server`/`account` if you have multiple accounts) and `deployApp()`\n#' will find the existing application account. If you need to update an app\n#' that was created by someone else (that you have write permission) for, you'll\n#' instead need to supply the `appId`.\n#'\n#' @param appDir A directory containing an application (e.g. a Shiny app\n#'   or plumber API). Defaults to the current directory.\n#' @param appFiles,appFileManifest Use `appFiles` to specify a\n#'   character vector of files to bundle in the app or `appFileManifest`\n#'   to provide a path to a file containing a list of such files. If neither\n#'   are supplied, will bundle all files in `appDir`, apart from standard\n#'   exclusions and files listed in a `.rscignore` file. See\n#'   [listDeploymentFiles()] for more details.\n#' @param manifestPath Path to an existing `manifest.json` file to use for\n#'   deployment. When provided, `deployApp()` will use the file list and\n#'   metadata from this manifest instead of generating a new one. This allows\n#'   you to pre-generate a manifest with [writeManifest()], customize it, check\n#'   it into version control, and deploy from that exact manifest. If `NULL`\n#'   (the default), a new manifest will be generated. When using `manifestPath`,\n#'   `appFiles`, `appFileManifest`, `appMode`, `appPrimaryDoc`, `contentCategory`,\n#'   `envManagement(R|Py)`, and `image` are ignored since their values are taken\n#'   from the manifest.\n#' @param appPrimaryDoc If the application contains more than one document, this\n#'   parameter indicates the primary one, as a path relative to `appDir`. Can be\n#'   `NULL`, in which case the primary document is inferred from the contents\n#'   being deployed.\n#' @param appSourceDoc `r lifecycle::badge(\"deprecated\")` Please use\n#'   `recordDir` instead.\n#' @param appName Application name, a string consisting of letters, numbers,\n#'   `_` and `-`. The application name is used to identify applications on a\n#'   server, so must be unique.\n#'\n#'   If not specified, the first deployment will be automatically it from the\n#'   `appDir` for directory and website, and from the `appPrimaryDoc` for\n#'   document. On subsequent deploys, it will use the previously stored value.\n#' @param appTitle Free-form descriptive title of application. Optional; if\n#'   supplied, will often be displayed in favor of the name. If ommitted,\n#'   on second and subsequent deploys, the title will be unchanged.\n#' @param envVars A character vector giving the names of environment variables\n#'   whose values should be synchronised with the server (currently supported by\n#'   Connect only). The values of the environment variables are sent over an\n#'   encrypted connection and are not stored in the bundle, making this a safe\n#'   way to send private data to Connect.\n#'\n#'   The values of sensitive environment variables should be set in the current\n#'   session via an `.Renviron` file or with the help of a credential store like\n#'   [keyring](https://keyring.r-lib.org/). Avoid using\n#'   [Sys.setenv()] for sensitive values, as that results in the value appearing\n#'   in your `.Rhistory`.\n#'\n#'   The names (not values) are stored in the deployment record so that future\n#'   deployments will automatically update their values. Other environment\n#'   variables on the server will not be affected. This means that removing an\n#'   environment variable from `envVars` will leave it unchanged on the server.\n#'   To remove it, either delete it using the Connect UI, or temporarily unset\n#'   it (with [Sys.unsetenv()] or similar) then re-deploy.\n#'\n#'   Environment variables are set prior to deployment so that your code\n#'   can use them and the first deployment can still succeed. Note that means\n#'   that if the deployment fails, the values will still be updated.\n#' @param appId Use this to deploy to an exact known application, ignoring all\n#'   existing deployment records and `appName`.\n#'\n#'   You can use this to update an existing application that is missing a\n#'   deployment record. If you're re-deploying an application that you\n#'   created it's generally easier to use `appName`; `appId` is best reserved\n#'   for re-deploying apps created by someone else.\n#'\n#'   You can find the `appId` in the following places:\n#'   * For Posit Connect, it's `guid` from the info tab on the content page.\n#'   * For Posit Connect Cloud, it can be found in the content admin page's URL `https://connect.posit.cloud/{accountName}/content/{appId}`).\n#'   * On shinyapps.io, it's the `id` listed on the applications page.\n#' @param appMode Optional; the type of content being deployed.\n#'   Provide this option when the inferred type of content is incorrect. This\n#'   can happen, for example, when static HTML content includes a downloadable\n#'   Shiny application `app.R`. Accepted values include `\"shiny\"`, `\"api\"`,\n#'   `\"rmd-static\"`, `\"rmd-shiny\"`, `\"quarto-static\"`, `\"quarto-shiny\"`,\n#'   `\"nodejs\"`, and `\"static\"`. The Posit Connect API Reference contains a\n#'   full set of available values. Not all servers support all types of content.\n#' @param contentCategory Optional; classifies the kind of content being\n#'   deployed (e.g. `\"plot\"` or `\"site\"`).\n#' @param account,server Uniquely identify a remote server with either your\n#'   user `account`, the `server` name, or both. If neither are supplied, and\n#'   there are multiple options, you'll be prompted to pick one.\n#'\n#'   Use [accounts()] to see the full list of available options.\n#' @param upload If `TRUE` (the default) then the application is uploaded from\n#'   the local system prior to deployment. If `FALSE` then it is re-deployed\n#'   using the last version that was uploaded. `FALSE` is only supported on\n#'   Posit Connect Cloud and shinyapps.io; `TRUE` is required on Posit Connect.\n#' @param recordDir Directory where deployment record is written. The default,\n#'   `NULL`, uses `appDir`, since this is usually where you want the deployment\n#'   data to be stored. This argument is typically only needed when deploying\n#'   a directory of static files since you want to store the record with the\n#'   code that generated those files, not the files themselves.\n#' @param launch.browser If true, the system's default web browser will be\n#'   launched automatically after the app is started. Defaults to `TRUE` in\n#'   interactive sessions only. If a function is passed, it will be called\n#'   after the app is started, with the app URL as a paramter.\n#' @param on.failure Function to be called if the deployment fails. If a\n#'   deployment log URL is available, it's passed as a parameter.\n#' @param logLevel One of `\"quiet\"`, `\"normal\"` or `\"verbose\"`; indicates how\n#'   much logging to the console is to be performed. At `\"quiet\"` reports no\n#'   information; at `\"verbose\"`, a full diagnostic log is captured.\n#' @param lint Lint the project before initiating deployment, to identify\n#'   potentially problematic code?\n#' @param metadata Additional metadata fields to save with the deployment\n#'   record. These fields will be returned on subsequent calls to\n#'   [deployments()].\n#'\n#'   Multi-value fields are recorded as comma-separated values and returned in\n#'   that form. Custom value serialization is the responsibility of the caller.\n#' @param forceUpdate What should happen if there's no deployment record for\n#'   the app, but there's an app with the same name on the server? If `TRUE`,\n#'   will always update the previously-deployed app. If `FALSE`, will ask\n#'   the user what to do, or fail if not in an interactive context.\n#'\n#'   Defaults to `TRUE` when called automatically by the IDE, and `FALSE`\n#'   otherwise. You can override the default by setting option\n#'   `rsconnect.force.update.apps`.\n#' @param python Full path to a python binary for use by `reticulate`.\n#'   Required if `reticulate` is a dependency of the app being deployed.\n#'   If python = NULL, and RETICULATE_PYTHON or RETICULATE_PYTHON_FALLBACK is\n#'   set in the environment, its value will be used. The specified python binary\n#'   will be invoked to determine its version and to list the python packages\n#'   installed in the environment.\n#' @param forceGeneratePythonEnvironment Optional. If an existing\n#'   `requirements.txt` file is found, it will be overwritten when this argument\n#'   is `TRUE`.\n#' @param quarto Should the deployed content be built by quarto?\n#'   (`TRUE`, `FALSE`, or `NA`). The default, `NA`, will use quarto if\n#'   there are `.qmd` files in the bundle, or if there is a\n#'   `_quarto.yml` and `.Rmd` files.\n#'\n#'   (This option is ignored and quarto will always be used if the\n#'   `metadata` contains `quarto_version` and `quarto_engines` fields.)\n#' @param appVisibility One of `NULL`, `\"private\"`, or `\"public\"`; the\n#'   visibility of the deployment. When `NULL`, no change to visibility is\n#'   made. Currently has an effect only on deployments to shinyapps.io.\n#' @param image Optional. The name of the image to use when building and\n#'   executing this content. If none is provided, Posit Connect will\n#'   attempt to choose an image based on the content requirements. You can\n#'   override the default by setting the environment variable `RSCONNECT_IMAGE`.\n#' @param envManagement Optional. Should Posit Connect install packages\n#'   for this content? (`TRUE`, `FALSE`, or `NULL`).\n#'   The default, `NULL`, will not write any values to the bundle manifest,\n#'   and Connect will fall back to the application default environment\n#'   management strategy, or the server default if no application default\n#'   is defined.\n#'\n#'   (This option is a shorthand flag which overwrites the values of\n#'   `envManagementR`, `envManagementPy`, and `envManagementNodejs`.)\n#' @param envManagementR Optional. Should Posit Connect install R packages\n#'   for this content? (`TRUE`, `FALSE`, or `NULL`). The default, `NULL`, will\n#'   not write any values to the bundle manifest, and Connect will fall back to\n#'   the application default R environment management strategy, or the server\n#'   default if no application default is defined.\n#'\n#'   (This option is ignored when `envManagement` is non-`NULL`.)\n#' @param envManagementPy Optional. Should Posit Connect install Python packages\n#'   for this content? (`TRUE`, `FALSE`, or `NULL`). The default, `NULL`, will\n#'   not write any values to the bundle manifest, and Connect will fall back to\n#'   the application default Python environment management strategy, or the\n#'   server default if no application default is defined.\n#'\n#'   (This option is ignored when `envManagement` is non-`NULL`.)\n#' @param envManagementNodejs Optional. Should Posit Connect install Node.js\n#'   packages for this content? (`TRUE`, `FALSE`, or `NULL`). The default,\n#'   `NULL`, will not write any values to the bundle manifest, and Connect will\n#'   fall back to the application default Node.js environment management\n#'   strategy, or the server default if no application default is defined.\n#'\n#'   (This option is ignored when `envManagement` is non-`NULL`.)\n#' @param packageRepositoryResolutionR Optional. Specifies the package repository\n#'   resolution strategy for R packages. Must be one of `\"lax\"`, `\"strict\"`,\n#'   `\"legacy\"`, `\"lockfile\"`, or `NULL`. The default, `NULL`, will not write\n#'   any values to the bundle manifest and Connect will fall back to the\n#'   server's package repository resolution strategy.\n#' @param dependencyResolution Controls how R package dependencies are resolved.\n#'   Must be one of `\"strict\"` or `\"library\"`. When `\"strict\"`, the\n#'   `renv.lock` file is used if present and must match the local library.\n#'   When `\"library\"`, the lockfile is ignored and dependencies are resolved\n#'   from the available local libraries instead. This is useful when the\n#'   lockfile is out of sync with the local library and cannot be updated. Note that the deployed content will\n#'   reflect the local library, not the lockfile. Defaults to `\"strict\"`.\n#' @examples\n#' \\dontrun{\n#'\n#' # deploy the application in the current working dir\n#' deployApp()\n#'\n#' # deploy an application in another directory\n#' deployApp(\"~/projects/shiny/app1\")\n#'\n#' # deploy using an alternative application name and title\n#' deployApp(\"~/projects/shiny/app1\", appName = \"myapp\",\n#'           appTitle = \"My Application\")\n#'\n#' # deploy specifying an explicit account name, then\n#' # redeploy with no arguments (will automatically use\n#' # the previously specified account)\n#' deployApp(account = \"jsmith\")\n#' deployApp()\n#'\n#' # deploy but don't launch a browser when completed\n#' deployApp(launch.browser = FALSE)\n#'\n#' # deploy a Quarto website, using the quarto package to\n#' # find the Quarto binary\n#' deployApp(\"~/projects/quarto/site1\")\n#'\n#' # deploy application with environment variables\n#' # (e.g., `SECRET_PASSWORD=XYZ` is set via an ~/.Renviron file)\n#' rsconnect::deployApp(envVars = c(\"SECRET_PASSWORD\"))\n#'\n#' # deploy from a pre-generated manifest\n#' writeManifest(\"~/projects/shiny/app1\")\n#' # ...optionally customize manifest.json here...\n#' deployApp(\"~/projects/shiny/app1\", manifestPath = \"manifest.json\")\n#' }\n#' @seealso [applications()], [terminateApp()], and [restartApp()]\n#' @family Deployment functions\n#' @export\ndeployApp <- function(\n  appDir = getwd(),\n  appFiles = NULL,\n  appFileManifest = NULL,\n  manifestPath = NULL,\n  appPrimaryDoc = NULL,\n  appSourceDoc = NULL,\n  appName = NULL,\n  appTitle = NULL,\n  envVars = NULL,\n  appId = NULL,\n  appMode = NULL,\n  contentCategory = NULL,\n  account = NULL,\n  server = NULL,\n  upload = TRUE,\n  recordDir = NULL,\n  launch.browser = getOption(\"rsconnect.launch.browser\", is_interactive()),\n  on.failure = NULL,\n  logLevel = c(\"normal\", \"quiet\", \"verbose\"),\n  lint = TRUE,\n  metadata = list(),\n  forceUpdate = NULL,\n  python = NULL,\n  forceGeneratePythonEnvironment = FALSE,\n  quarto = NA,\n  appVisibility = NULL,\n  image = NULL,\n  envManagement = NULL,\n  envManagementR = NULL,\n  envManagementPy = NULL,\n  envManagementNodejs = NULL,\n  packageRepositoryResolutionR = NULL,\n  dependencyResolution = c(\"strict\", \"library\")\n) {\n  check_string(appDir)\n  check_directory(appDir)\n  appDir <- normalizePath(appDir)\n\n  dependencyResolution <- match.arg(dependencyResolution)\n  check_string(appName, allow_null = TRUE)\n\n  if (!is.null(appPrimaryDoc)) {\n    check_string(appPrimaryDoc)\n    if (!file.exists(file.path(appDir, appPrimaryDoc))) {\n      cli::cli_abort(\"`appPrimaryDoc` not found inside `appDir`\")\n    }\n  }\n\n  check_character(envVars, allow_null = TRUE)\n  if (!is.null(envVars) && !is.null(names(envVars))) {\n    cli::cli_abort(c(\n      \"{.arg envVars} must be a character vector containing only environment variable {.strong names}.\",\n      \"i\" = \"Set environment variables with `Sys.setenv() or an `.Renviron` file.\",\n      \"i\" = \"Use {.fn unname} to remove the names from the vector passed to {.arg envVars}.\"\n    ))\n  }\n\n  if (!is.null(appSourceDoc)) {\n    # Used by IDE so can't deprecate\n    recordDir <- appSourceDoc\n  }\n\n  # set up logging helpers\n  logLevel <- match.arg(logLevel)\n  quiet <- identical(logLevel, \"quiet\")\n  verbose <- identical(logLevel, \"verbose\")\n  logger <- verboseLogger(verbose)\n  displayStatus <- displayStatus(quiet)\n\n  # run startup scripts to pick up any user options and establish pre/post deploy hooks\n  runStartupScripts(appDir, quiet = quiet, verbose = verbose)\n\n  # at verbose log level, turn on all tracing options implicitly for the\n  # duration of the call\n  if (verbose) {\n    old_verbose <- options(\n      rsconnect.http.trace = TRUE,\n      rsconnect.http.trace.json = TRUE,\n      rsconnect.error.trace = TRUE\n    )\n    defer(options(old_verbose))\n  }\n\n  # install error handler if requested\n  if (isTRUE(getOption(\"rsconnect.error.trace\"))) {\n    old_error <- options(error = function(e) {\n      cat(\"----- Deployment error -----\\n\")\n      cat(geterrmessage(), \"\\n\")\n      cat(\"----- Error stack trace -----\\n\")\n      traceback(x = sys.calls(), max.lines = 3)\n    })\n    defer(options(old_error))\n  }\n\n  # at verbose log level, generate header\n  if (verbose) {\n    logger(\"Deployment log started\")\n    cat(\"Deploy command:\", \"\\n\", deparse(sys.call(1)), \"\\n\\n\")\n    cat(\"Session information: \\n\")\n    print(utils::sessionInfo())\n  }\n\n  # invoke pre-deploy hook if we have one\n  runDeploymentHook(appDir, \"rsconnect.pre.deploy\", verbose = verbose)\n\n  # Handle manifest-based deployment\n  if (!is.null(manifestPath)) {\n    # Validate and read manifest\n    if (!file.exists(manifestPath)) {\n      # Try relative to appDir\n      manifestPath <- file.path(appDir, manifestPath)\n    }\n    if (!file.exists(manifestPath)) {\n      cli::cli_abort(c(\n        \"Manifest file not found.\",\n        \"x\" = \"Could not find {.file {manifestPath}}.\",\n        \"i\" = \"Create a manifest with {.fn writeManifest} first.\"\n      ))\n    }\n\n    manifest <- tryCatch(\n      jsonlite::read_json(manifestPath, simplifyVector = FALSE),\n      error = function(e) {\n        cli::cli_abort(c(\n          \"Failed to parse manifest file.\",\n          \"x\" = \"Error reading {.file {manifestPath}}: {e$message}\"\n        ))\n      }\n    )\n\n    # Validate manifest structure\n    if (is.null(manifest$metadata$appmode)) {\n      cli::cli_abort(c(\n        \"Invalid manifest file.\",\n        \"x\" = \"Manifest must contain {.field metadata$appmode}.\"\n      ))\n    }\n\n    # Extract file list from manifest\n    appFiles <- names(manifest$files)\n    if (length(appFiles) == 0) {\n      cli::cli_abort(c(\n        \"Invalid manifest file.\",\n        \"x\" = \"Manifest contains no files.\"\n      ))\n    }\n\n    # Verify all files exist\n    missingFiles <- c()\n    for (file in appFiles) {\n      if (!file.exists(file.path(appDir, file))) {\n        missingFiles <- c(missingFiles, file)\n      }\n    }\n    if (length(missingFiles) > 0) {\n      cli::cli_abort(c(\n        \"Files listed in manifest are missing from {.arg appDir}.\",\n        \"x\" = \"Missing files: {.file {missingFiles}}\"\n      ))\n    }\n\n    # Extract metadata\n    appMode <- manifest$metadata$appmode\n\n    appPrimaryDoc <- manifest$metadata$primary_rmd\n    if (identical(appPrimaryDoc, NA) || is.null(appPrimaryDoc)) {\n      appPrimaryDoc <- manifest$metadata$primary_html\n    }\n    if (identical(appPrimaryDoc, NA)) {\n      appPrimaryDoc <- NULL\n    }\n    manifestCategory <- manifest$metadata$content_category\n    if (!identical(manifestCategory, NA) && !is.null(manifestCategory)) {\n      contentCategory <- manifestCategory\n    }\n  } else {\n    appFiles <- listDeploymentFiles(appDir, appFiles, appFileManifest)\n    manifest <- NULL\n  }\n\n  if (isTRUE(lint)) {\n    lintResults <- lint(appDir, appFiles, appPrimaryDoc)\n    showLintResults(appDir, lintResults)\n  }\n\n  if (!quiet) {\n    cli::cli_rule(\"Preparing for deployment\")\n  }\n\n  forceUpdate <- forceUpdate %||%\n    getOption(\"rsconnect.force.update.apps\") %||%\n    fromIDE()\n\n  # determine the target deployment record and deploying account\n  recordPath <- findRecordPath(appDir, recordDir, appPrimaryDoc)\n  target <- findDeploymentTarget(\n    recordPath = recordPath,\n    appId = appId,\n    appName = appName,\n    appTitle = appTitle,\n    envVars = envVars,\n    account = account,\n    server = server,\n    forceUpdate = forceUpdate\n  )\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n\n  if (is.null(deployment$appId)) {\n    dest <- accountLabel(accountDetails$name, accountDetails$server)\n    taskComplete(\n      quiet,\n      \"Deploying {.val {deployment$name}} using {.val {dest}}\"\n    )\n  } else {\n    dest <- accountLabel(accountDetails$name, accountDetails$server)\n    taskComplete(\n      quiet,\n      \"Re-deploying {.val {deployment$name}} using {.val {dest}}\"\n    )\n  }\n\n  # Run checks prior to first saveDeployment() to avoid errors that will always\n  # prevent a successful upload from generating a partial deployment\n  if (\n    isConnectServer(accountDetails$server) &&\n      identical(upload, FALSE)\n  ) {\n    # it is not possible to deploy to Connect without uploading\n    stop(\n      \"Posit Connect does not support deploying without uploading. \",\n      \"Specify upload=TRUE to upload and re-deploy your application.\"\n    )\n  }\n\n  client <- clientForAccount(accountDetails)\n\n  if (\n    !serverSupportsEnvVars(accountDetails$server, client) &&\n      length(envVars) > 0\n  ) {\n    cli::cli_abort(\n      \"{accountDetails$server} does not support setting {.arg envVars}\"\n    )\n  }\n\n  if (verbose) {\n    showCookies(serverInfo(accountDetails$server)$url)\n  }\n\n  isShinyappsServer <- isShinyappsServer(accountDetails$server)\n\n  logger(\"Inferring App mode and parameters\")\n  appMetadata <- appMetadata(\n    appDir = appDir,\n    appFiles = appFiles,\n    appPrimaryDoc = appPrimaryDoc,\n    quarto = quarto,\n    appMode = appMode,\n    contentCategory = contentCategory,\n    isShinyappsServer = isShinyappsServer,\n    metadata = metadata\n  )\n\n  if (appMetadata$appMode == \"nodejs\") {\n    if (isShinyappsServer(accountDetails$server)) {\n      cli::cli_abort(\"Node.js content is not supported on shinyapps.io.\")\n    }\n    if (isPositConnectCloudServer(accountDetails$server)) {\n      cli::cli_abort(\"Node.js content is not supported on Posit Connect Cloud.\")\n    }\n    checkConnectSupportsNodejs(client)\n  }\n\n  if (is.null(deployment$appId)) {\n    taskStart(quiet, \"Creating content on server...\")\n    if (isPositConnectCloudServer(accountDetails$server)) {\n      # Use appPrimaryDoc if available, otherwise fall back to inferredPrimaryFile\n      primaryFile <- appMetadata$appPrimaryDoc %||%\n        appMetadata$inferredPrimaryFile\n      application <- client$createContent(\n        deployment$name,\n        deployment$title,\n        accountDetails$accountId,\n        appMetadata$appMode,\n        primaryFile,\n        deployment$envVars\n      )\n    } else {\n      application <- client$createApplication(\n        deployment$name,\n        deployment$title,\n        \"shiny\",\n        accountDetails$accountId,\n        appMetadata$appMode,\n        contentCategory\n      )\n    }\n    taskComplete(quiet, \"Created content with id {.val {application$id}}\")\n  } else {\n    if (isPositConnectCloudServer(accountDetails$server)) {\n      taskStart(\n        quiet,\n        \"Looking up content with id {.val {deployment$appId}}...\"\n      )\n      application <- tryCatch(\n        {\n          application <- client$getContent(deployment$appId)\n          taskComplete(quiet, \"Found content\")\n          application\n        },\n        rsconnect_http_404 = function(err) {\n          application <- applicationDeleted(\n            client,\n            deployment,\n            recordPath,\n            appMetadata\n          )\n          taskComplete(\n            quiet,\n            \"Created content with id {.val {application$id}}\"\n          )\n          application\n        }\n      )\n    } else {\n      taskStart(\n        quiet,\n        \"Looking up content with id {.val {deployment$appId}}...\"\n      )\n      application <- tryCatch(\n        {\n          application <- client$getApplication(\n            deployment$appId,\n            deployment$version\n          )\n          taskComplete(quiet, \"Found content {.url {application$url}}\")\n          application\n        },\n        rsconnect_http_404 = function(err) {\n          application <- applicationDeleted(\n            client,\n            deployment,\n            recordPath,\n            appMetadata\n          )\n          taskComplete(\n            quiet,\n            \"Created content with id {.val {application$id}}\"\n          )\n          application\n        }\n      )\n    }\n  }\n  saveDeployment(\n    recordPath,\n    deployment = deployment,\n    application = application,\n    metadata = metadata\n  )\n\n  # Change _visibility_ & set env vars before uploading contents\n  if (isPositConnectCloudServer(accountDetails$server)) {\n    # no update needed if we just created the content\n    # current revision will be null only when creating new content\n    if (!is.null(application$current_revision)) {\n      taskStart(quiet, \"Updating content...\")\n      # Use appPrimaryDoc if available, otherwise fall back to inferredPrimaryFile\n      primaryFile <- appMetadata$appPrimaryDoc %||%\n        appMetadata$inferredPrimaryFile\n      application <- client$updateContent(\n        application$id,\n        deployment$envVars,\n        newBundle = upload,\n        primaryFile,\n        appMetadata$appMode\n      )\n      taskComplete(quiet, \"Content updated\")\n    }\n  } else {\n    if (\n      needsVisibilityChange(accountDetails$server, application, appVisibility)\n    ) {\n      taskStart(quiet, \"Setting visibility to {appVisibility}...\")\n      client$setApplicationProperty(\n        application$id,\n        \"application.visibility\",\n        appVisibility\n      )\n      taskComplete(quiet, \"Visibility updated\")\n    }\n    if (length(deployment$envVars) > 0) {\n      taskStart(quiet, \"Updating environment variables {envVars}...\")\n      client$setEnvVars(application$guid, deployment$envVars)\n      taskComplete(quiet, \"Environment variables updated\")\n    }\n  }\n\n  if (upload) {\n    python <- getPythonForTarget(python, accountDetails)\n    pythonConfig <- pythonConfigurator(python, forceGeneratePythonEnvironment)\n\n    if (dependencyResolution == \"library\") {\n      confirmDependencySourceLibrary()\n    }\n\n    taskStart(quiet, \"Bundling {length(appFiles)} file{?s}: {.file {appFiles}}\")\n    bundlePath <- bundleApp(\n      appName = deployment$name,\n      appDir = appDir,\n      appFiles = appFiles,\n      appMetadata = appMetadata,\n      quiet = quiet,\n      verbose = verbose,\n      pythonConfig = pythonConfig,\n      image = image,\n      envManagement = envManagement,\n      envManagementR = envManagementR,\n      envManagementPy = envManagementPy,\n      envManagementNodejs = envManagementNodejs,\n      packageRepositoryResolutionR = packageRepositoryResolutionR,\n      dependencyResolution = dependencyResolution,\n      existingManifest = manifest\n    )\n    size <- format(file_size(bundlePath), big.mark = \",\")\n    taskComplete(quiet, \"Created bundle of size: {size}b\")\n\n    # create, and upload the bundle\n    taskStart(quiet, \"Uploading bundle...\")\n    if (isPositConnectCloudServer(accountDetails$server)) {\n      uploadUrl <- application$next_revision$source_bundle_upload_url\n      success <- client$uploadBundle(bundlePath, uploadUrl)\n      if (!success) {\n        cli::cli_abort(\"Could not upload bundle.\")\n      }\n      bundle <- NULL # PCC doesn't use bundle objects like other servers\n    } else if (isShinyappsServer(accountDetails$server)) {\n      bundle <- uploadShinyappsBundle(\n        client,\n        application$application_id,\n        bundlePath\n      )\n    } else {\n      bundle <- client$uploadBundle(application$guid, bundlePath)\n    }\n    if (isPositConnectCloudServer(accountDetails$server)) {\n      taskComplete(quiet, \"Uploaded bundle\")\n    } else {\n      taskComplete(quiet, \"Uploaded bundle with id {.val {bundle$id}}\")\n    }\n\n    saveDeployment(\n      recordPath,\n      deployment = deployment,\n      application = application,\n      bundleId = bundle$id,\n      metadata = metadata\n    )\n  } else {\n    # redeploy existing bundle\n    if (isShinyappsServer(accountDetails$server)) {\n      bundle <- application$deployment$bundle\n    }\n  }\n\n  if (!quiet) {\n    cli::cli_rule(\"Deploying to server\")\n  }\n  if (isPositConnectCloudServer(accountDetails$server)) {\n    client$publish(application$id)\n    revisionId <- application$next_revision$id\n    response <- client$awaitCompletion(revisionId)\n    deploymentSucceeded <- response$success\n    application$url <- response$url\n\n    # save the deployment record one more time now that we have a URL\n    saveDeployment(\n      recordPath,\n      deployment = deployment,\n      application = application,\n      bundleId = bundle$id,\n      metadata = metadata\n    )\n  } else {\n    task <- client$deployApplication(application, bundle$id)\n    taskId <- if (is.null(task$task_id)) task$id else task$task_id\n    # wait for the deployment to complete (will raise an error if it can't)\n    response <- client$waitForTask(taskId, quiet)\n  }\n  if (!quiet) {\n    cli::cli_rule(\"Deployment complete\")\n  }\n\n  # wait 1/10th of a second for any queued output get picked by RStudio\n  # before emitting the final status, to ensure it's the last line the user sees\n  Sys.sleep(0.10)\n\n  if (!isPositConnectCloudServer(accountDetails$server)) {\n    deploymentSucceeded <- is.null(response$code) || response$code == 0\n  }\n  if (!quiet) {\n    if (deploymentSucceeded) {\n      cli::cli_alert_success(\n        \"Successfully deployed to {.url {application$url}}\"\n      )\n    } else {\n      cli::cli_alert_danger(\"Deployment failed with error: {response$error}\")\n    }\n  }\n\n  if (!quiet) {\n    openURL(\n      client,\n      application,\n      accountDetails$server,\n      launch.browser,\n      on.failure,\n      deploymentSucceeded\n    )\n  }\n\n  # invoke post-deploy hook if we have one\n  if (deploymentSucceeded) {\n    runDeploymentHook(appDir, \"rsconnect.post.deploy\", verbose = verbose)\n  }\n\n  logger(\"Deployment log finished\")\n\n  invisible(deploymentSucceeded)\n}\n\ncheckConnectSupportsNodejs <- function(client) {\n  settings <- tryCatch(\n    client$serverSettings(),\n    error = function(e) NULL\n  )\n\n  version <- settings$version\n  result <- connectVersionLt(version, \"2026.04.0\")\n\n  if (is.na(result)) {\n    cli::cli_inform(c(\n      \"i\" = \"Could not determine the Posit Connect server version.\",\n      \"i\" = \"Node.js support requires Connect {.val 2026.04.0} or later.\"\n    ))\n  } else if (result) {\n    cli::cli_abort(\n      c(\n        \"Node.js content requires Posit Connect {.val 2026.04.0} or later.\",\n        \"x\" = \"This server is running version {.val {version}}.\"\n      ),\n      call = NULL\n    )\n  }\n  invisible()\n}\n\nconnectVersionLt <- function(version, minimum) {\n  if (is.null(version) || !nzchar(version)) {\n    return(NA)\n  }\n  tryCatch(\n    suppressWarnings(utils::compareVersion(version, minimum)) < 0,\n    error = function(e) NA\n  )\n}\n\nserverSupportsEnvVars <- function(server, client) {\n  return(\n    # Connect Cloud supports setting environment variables, but not through a\n    # setEnvVars client method\n    isPositConnectCloudServer(server) ||\n      (isConnectServer(server) && \"setEnvVars\" %in% names(client))\n  )\n}\n\ntaskStart <- function(quiet, message, .envir = caller_env()) {\n  if (quiet) {\n    return()\n  }\n  cli::cli_alert_info(message, .envir = .envir)\n}\ntaskComplete <- function(quiet, message, .envir = caller_env()) {\n  if (quiet) {\n    return()\n  }\n  cli::cli_alert_success(message, .envir = .envir)\n}\n\nfindRecordPath <- function(appDir, recordDir = NULL, appPrimaryDoc = NULL) {\n  if (!is.null(recordDir)) {\n    recordDir\n  } else if (!is.null(appPrimaryDoc)) {\n    file.path(appDir, appPrimaryDoc)\n  } else {\n    appDir\n  }\n}\n\n# Need to set _before_ deploy\nneedsVisibilityChange <- function(server, application, appVisibility = NULL) {\n  if (is.null(appVisibility)) {\n    return(FALSE)\n  }\n\n  if (isConnectServer(server)) {\n    # Defaults to private visibility\n    return(FALSE)\n  }\n  if (isPositConnectCloudServer(server)) {\n    return(FALSE)\n  }\n\n  cur <- application$deployment$properties$application.visibility\n  if (is.null(cur)) {\n    cur <- \"public\"\n  }\n  cur != appVisibility\n}\n\nrunDeploymentHook <- function(appDir, option, verbose = FALSE) {\n  hook <- getOption(option)\n  if (!is.function(hook)) {\n    return()\n  }\n\n  if (verbose) {\n    cat(\"Invoking `\", option, \"` hook\\n\", sep = \"\")\n  }\n  hook(appDir)\n}\n\napplicationDeleted <- function(client, deployment, recordPath, appMetadata) {\n  header <- \"Failed to find existing content on server; it's probably been deleted.\"\n  not_interactive <- c(\n    i = \"Use {.fn forgetDeployment} to remove outdated record and try again.\",\n    i = \"Or use {.fn applications} to see other content deployed to the the server.\"\n  )\n  prompt <- \"What do you want to do?\"\n  choices <- c(\n    \"Give up and try again later\",\n    \"Delete existing deployment record & deploy this content as a new item\"\n  )\n\n  cli_menu(header, prompt, choices, not_interactive = not_interactive, quit = 1)\n  # Must be option 2\n\n  path <- deploymentConfigFile(\n    recordPath,\n    deployment$name,\n    deployment$account,\n    deployment$server\n  )\n  unlink(path)\n\n  accountDetails <- accountInfo(deployment$account, deployment$server)\n  if (isPositConnectCloudServer(accountDetails$server)) {\n    # Use appPrimaryDoc if available, otherwise fall back to inferredPrimaryFile\n    primaryFile <- appMetadata$appPrimaryDoc %||%\n      appMetadata$inferredPrimaryFile\n    client$createContent(\n      deployment$name,\n      deployment$title,\n      accountDetails$accountId,\n      appMetadata$appMode,\n      primaryFile,\n      deployment$envVars\n    )\n  } else {\n    client$createApplication(\n      deployment$name,\n      deployment$title,\n      \"shiny\",\n      accountDetails$accountId,\n      appMetadata$appMode\n    )\n  }\n}\n\n# Does almost exactly the same work as writeManifest(), but called within\n# deployApp() instead of being exposed to the user. Returns the path to the\n# bundle directory, whereas writeManifest() returns nothing and deletes the\n# bundle directory after writing the manifest.\nbundleApp <- function(\n  appName,\n  appDir,\n  appFiles,\n  appMetadata,\n  verbose = FALSE,\n  quiet = FALSE,\n  pythonConfig = NULL,\n  image = NULL,\n  envManagement = NULL,\n  envManagementR = NULL,\n  envManagementPy = NULL,\n  envManagementNodejs = NULL,\n  packageRepositoryResolutionR = NULL,\n  dependencyResolution = \"strict\",\n  existingManifest = NULL\n) {\n  logger <- verboseLogger(verbose)\n\n  # get application users (for non-document deployments)\n  users <- NULL\n  if (is.null(appMetadata$appPrimaryDoc)) {\n    users <- suppressWarnings(authorizedUsers(appDir))\n  }\n\n  # copy files to bundle dir to stage\n  logger(\"Bundling app dir\")\n  bundleDir <- bundleAppDir(\n    appDir = appDir,\n    appFiles = appFiles,\n    appPrimaryDoc = appMetadata$appPrimaryDoc,\n    appMode = appMetadata$appMode\n  )\n  defer(unlink(bundleDir, recursive = TRUE))\n\n  # generate or use existing manifest\n  if (!is.null(existingManifest)) {\n    logger(\"Using existing manifest: \", existingManifest)\n    manifest <- existingManifest\n  } else {\n    # generate the manifest\n    logger(\"Generate manifest.json\")\n    manifest <- createAppManifest(\n      appDir = bundleDir,\n      appMetadata = appMetadata,\n      users = users,\n      pythonConfig = pythonConfig,\n      retainPackratDirectory = TRUE,\n      image = image,\n      envManagement = envManagement,\n      envManagementR = envManagementR,\n      envManagementPy = envManagementPy,\n      envManagementNodejs = envManagementNodejs,\n      packageRepositoryResolutionR = packageRepositoryResolutionR,\n      dependencyResolution = dependencyResolution,\n      verbose = verbose,\n      quiet = quiet\n    )\n  }\n\n  # Write manifest into the bundle dir\n  manifestJson <- toJSON(manifest)\n  manifestPath <- file.path(bundleDir, \"manifest.json\")\n  writeLines(manifestJson, manifestPath, useBytes = TRUE)\n\n  # create the bundle and return its path\n  logger(\"Compressing the bundle\")\n  bundlePath <- tempfile(\"rsconnect-bundle\", fileext = \".tar.gz\")\n  writeBundle(bundleDir, bundlePath)\n  bundlePath\n}\n\nconfirmDependencySourceLibrary <- function() {\n  if (is_interactive()) {\n    response <- cli_readline(paste0(\n      \"renv.lock will be ignored. \",\n      \"Package dependencies will be resolved from the local library instead.\\n\",\n      \"Do you want to proceed? [Y/n]: \"\n    ))\n    if (nzchar(response) && tolower(substring(response, 1, 1)) != \"y\") {\n      cli::cli_abort(\"Deployment cancelled.\")\n    }\n  } else {\n    cli::cli_inform(c(\n      \"{.file renv.lock} will be ignored.\",\n      \"!\" = \"Package dependencies will be resolved from the local library instead.\"\n    ))\n  }\n}\n\nvalidURL <- function(url) {\n  !(is.null(url) || url == \"\")\n}\n\nopenURL <- function(\n  client,\n  application,\n  server,\n  launch.browser,\n  on.failure,\n  deploymentSucceeded\n) {\n  # function to browse to a URL using user-supplied browser (config or final)\n  showURL <- function(url) {\n    if (isPositConnectCloudServer(server)) {\n      url <- addUtmParameters(url)\n    }\n    if (isTRUE(launch.browser)) {\n      utils::browseURL(url)\n    } else if (is.function(launch.browser)) {\n      launch.browser(url)\n    }\n  }\n\n  # Check to see if we should open config url or app url\n  if (!is.null(application$dashboard_url)) {\n    # Posit Connect should land here because it inserts a dashboard_url\n    if (deploymentSucceeded) {\n      url <- paste(application$dashboard_url, \"access\", sep = \"/\")\n    } else {\n      url <- paste(application$dashboard_url, \"logs\", sep = \"/\")\n    }\n    if (validURL(url)) {\n      # Connect should always end up here, even on deployment failures\n      if (deploymentSucceeded) {\n        showURL(url)\n      } else if (is.function(on.failure)) {\n        on.failure(url)\n      }\n    }\n  } else if (deploymentSucceeded) {\n    # shinyapps.io should land here if things succeeded\n    showURL(application$url)\n  } else if (is.function(on.failure)) {\n    on.failure(NULL)\n  }\n  # or open no url if things failed\n}\n\nrunStartupScripts <- function(appDir, quiet = FALSE, verbose = FALSE) {\n  scripts <- c(\n    # the site-wide startup script\n    file.path(R.home(\"etc\"), \"rsconnect.site\"),\n    # the user startup script\n    path.expand(\"~/.rsconnect_profile\"),\n    # a startup script specific to this application\n    file.path(appDir, \".rsconnect_profile\")\n  )\n  scripts <- scripts[file.exists(scripts)]\n\n  # iterate over the startup scripts\n  for (script in scripts) {\n    taskStart(quiet, \"Running {script}\")\n\n    env <- new_environment(parent = globalenv())\n    source(script, verbose = verbose, local = env)\n  }\n}\n"
  },
  {
    "path": "R/deployDoc.R",
    "content": "#' Deploy a single document\n#'\n#' @description\n#' Deploys a single R Markdown, Quarto document, or other file (e.g. `.html` or\n#' `.pdf`).\n#'\n#' When deploying an `.Rmd`, `.Qmd`, or `.html`, `deployDoc()` will attempt to\n#' automatically discover dependencies using [rmarkdown::find_external_resources()],\n#' and include an `.Rprofile` if present. If you find that the document is\n#' missing dependencies, either specify the dependencies explicitly in the\n#' document (see [rmarkdown::find_external_resources()] for details), or call\n#' [deployApp()] directly and specify your own file list in `appFiles`.\n#'\n#' Supported servers: All servers\n#'\n#' @param doc Path to the document to deploy.\n#' @param ... Additional arguments to [deployApp()]. Do not supply `appDir`,\n#'   `appFiles`, or `appPrimaryDoc`; these three parameters are automatically\n#'   generated by `deployDoc` from the document.\n#' @inheritParams deployApp\n#' @family Deployment functions\n#' @export\n#' @examples\n#' \\dontrun{\n#' deployDoc(\"my-report.Rmd\")\n#' deployDoc(\"static-file.html\")\n#' }\ndeployDoc <- function(doc, ..., logLevel = c(\"normal\", \"quiet\", \"verbose\")) {\n  logLevel <- arg_match(logLevel)\n\n  doc <- standardizeSingleDocDeployment(doc, quiet = logLevel == \"quiet\")\n  deployApp(\n    appDir = doc$appDir,\n    appPrimaryDoc = doc$appPrimaryDoc,\n    appFiles = doc$appFiles,\n    ...,\n    logLevel = logLevel\n  )\n}\n\nstandardizeSingleDocDeployment <- function(\n  path,\n  quiet = FALSE,\n  error_call = caller_env(),\n  error_arg = caller_arg(path)\n) {\n  check_installed(\n    \"rmarkdown\",\n    version = \"0.5.2\",\n    reason = \"to deploy individual R Markdown documents\"\n  )\n  check_file(path, error_call = error_call, error_arg = error_arg)\n  path <- normalizePath(path)\n\n  if (isShinyRmd(path)) {\n    # deploy entire directory\n    appFiles <- NULL\n  } else if (isStaticFile(path)) {\n    taskStart(quiet, \"Discovering document dependencies...\")\n    resources <- rmarkdown::find_external_resources(path)\n    taskComplete(quiet, \"Document dependencies discovered\")\n\n    appFiles <- c(basename(path), resources$path)\n\n    if (isRenderedFile(path)) {\n      candidates <- c(\".Rprofile\", \"renv.lock\", \"requirements.txt\")\n      exists <- file.exists(file.path(dirname(path), candidates))\n      appFiles <- c(appFiles, candidates[exists])\n    }\n\n    appFiles\n  } else {\n    # deploy just the file\n    appFiles <- basename(path)\n  }\n\n  list(\n    appDir = normalizePath(dirname(path)),\n    appPrimaryDoc = basename(path),\n    appFiles = appFiles\n  )\n}\n\nisStaticFile <- function(path) {\n  ext <- tolower(tools::file_ext(path))\n  ext %in% c(\"rmd\", \"qmd\", \"html\", \"htm\")\n}\n\nisRenderedFile <- function(path) {\n  ext <- tolower(tools::file_ext(path))\n  ext %in% c(\"rmd\", \"qmd\")\n}\n"
  },
  {
    "path": "R/deploySite.R",
    "content": "#' Deploy a website\n#'\n#' @description\n#' Deploy an R Markdown or quarto website to a server.\n#'\n#' Supported servers: Posit Connect, Posit Connect Cloud and ShinyApps servers\n#'\n#' @inheritParams deployApp\n#' @param siteDir Directory containing website. Defaults to current directory.\n#' @param siteName Name for the site (names must be unique within\n#'   an account). Defaults to the base name of the specified `siteDir`\n#'   or to the name provided by a custom site generation function.\n#' @param siteTitle Title for the site. For quarto sites only, if not\n#'   supplied uses the title recorded in `_quarto.yml`.\n#' @param render Rendering behavior for site:\n#'\n#'   * `\"none\"` uploads a static version of the current contents of\n#'     the site directory.\n#'   * `\"local\"` renders the site locally then uploads it.\n#'   * `\"server\"` uploads the source of the site to render on the server.\n#'\n#'   Note that for `\"none\"` and `\"local\"` source files (e.g. `.R`, `.Rmd` and\n#'   `.md`) will not be uploaded to the server.\n#' @param recordDir The default, `NULL`, uses `siteDir`.\n#' @param ... Additional arguments to [deployApp()]. Do not supply `appDir`\n#'   or `appFiles`; these parameters are automatically generated by\n#'   `deploySite()`.\n#' @family Deployment functions\n#' @export\ndeploySite <- function(\n  siteDir = getwd(),\n  siteName = NULL,\n  siteTitle = NULL,\n  account = NULL,\n  server = NULL,\n  render = c(\"none\", \"local\", \"server\"),\n  launch.browser = getOption(\"rsconnect.launch.browser\", interactive()),\n  logLevel = c(\"normal\", \"quiet\", \"verbose\"),\n  lint = FALSE,\n  metadata = list(),\n  python = NULL,\n  recordDir = NULL,\n  ...\n) {\n  check_directory(siteDir)\n  isQuarto <- file.exists(file.path(siteDir, \"_quarto.yml\")) ||\n    file.exists(file.path(siteDir, \"_quarto.yaml\"))\n\n  quiet <- identical(match.arg(logLevel), \"quiet\")\n  if (isQuarto) {\n    site <- quartoSite(siteDir, quiet = quiet)\n  } else {\n    site <- rmarkdownSite(siteDir, quiet = quiet)\n  }\n\n  # render locally if requested\n  render <- arg_match(render)\n  if (render == \"local\") {\n    site$render()\n  }\n\n  # determine appDir based on whether we are rendering on the server\n  if (render == \"server\") {\n    appDir <- siteDir\n  } else {\n    appDir <- site$output_dir\n  }\n\n  # Need to override recordDir to always record in the source directory\n  if (is.null(recordDir)) {\n    # We're deploying an entire directory, so we don't really need to set\n    # a path here, but we don't want to break existing deployments so\n    # we leave the existing behaviour for RMarkdown\n    if (!isQuarto) {\n      name <- if (file.exists(\"index.Rmd\")) \"index.Rmd\" else \"index.md\"\n      recordDir <- file.path(siteDir, name)\n    } else {\n      recordDir <- siteDir\n    }\n  }\n\n  deployApp(\n    appName = siteName %||% site$name,\n    appTitle = siteTitle %||% site$title,\n    appDir = appDir,\n    recordDir = recordDir,\n    contentCategory = \"site\",\n    account = account,\n    server = server,\n    launch.browser = launch.browser,\n    logLevel = logLevel,\n    lint = lint,\n    metadata = metadata,\n    python = python,\n    ...\n  )\n}\n\nquartoSite <- function(path, quiet = FALSE, error_call = caller_env()) {\n  check_installed(\n    \"quarto\",\n    reason = \"to deploy quarto sites\",\n    call = error_call\n  )\n\n  config <- quarto::quarto_inspect(path)$config\n\n  list(\n    render = function() {\n      quarto::quarto_render(path, quiet = quiet, as_job = FALSE)\n    },\n    name = basename(normalizePath(path)),\n    title = config$website$title %||% config$book$title %||% config$title,\n    # non-site projects build in current directory\n    output_dir = outputDir(path, config$project$`output-dir` %||% \".\")\n  )\n}\n\nrmarkdownSite <- function(siteDir, quiet = FALSE, error_call = caller_env()) {\n  check_installed(\n    \"rmarkdown\",\n    version = \"0.9.5.3\",\n    reason = \"to deploy RMarkdown sites\",\n    call = error_call\n  )\n\n  # discover the site generator\n  siteGenerator <- rmarkdown::site_generator(siteDir)\n  if (is.null(siteGenerator)) {\n    cli::cli_abort(\n      \"No `_site.yml` found in {.path {siteDir}.}\",\n      call = error_call\n    )\n  }\n\n  list(\n    render = function() {\n      siteGenerator$render(\n        input_file = NULL,\n        output_format = NULL,\n        envir = new.env(),\n        quiet = quiet,\n        encoding = getOption(\"encoding\")\n      )\n    },\n    name = siteGenerator$name,\n    title = NULL,\n    output_dir = outputDir(siteDir, siteGenerator$output_dir)\n  )\n}\n\noutputDir <- function(wd, path) {\n  old <- setwd(wd)\n  defer(setwd(old))\n\n  normalizePath(dirCreate(path), mustWork = FALSE)\n}\n"
  },
  {
    "path": "R/deployTFModel.R",
    "content": "#' Deploy a TensorFlow saved model\n#'\n#' @description\n#' Deploys a directory containing a TensorFlow saved model.\n#'\n#' Supported servers: Posit Connect and ShinyApps servers\n#'\n#' @param ... Additional arguments to [deployApp()].\n#'\n#' @family Deployment functions\n#' @export\ndeployTFModel <- function(...) {\n  deployApp(appMode = \"tensorflow-saved-model\", ...)\n}\n"
  },
  {
    "path": "R/deploymentTarget.R",
    "content": "# Discover the deployment target given the passed information.\n#\n# Returns a list containing a deployment record and the account details to use\n# when performing the deployment.\n#\n# When appId is provided, it must identify an existing application. The\n# application may have been created by some other user. That application may\n# or may not have an existing deployment record on disk.\n#\n# When using appId, a search across all deployment records occurs, even when\n# there is no local account+server referenced by the deployment record. This\n# lets us identify on-disk deployment records created by some collaborator.\n# When there is no on-disk deployment record, the configured account+server is\n# queried for the appId.\n#\n# It is an error when appId does not identify an existing application.\n#\n# When appName is provided, it may identify an existing application owned by\n# the calling user (e.g. associated with a locally known account).\n#\n# When using appName, the search across deployment records is restricted to\n# the incoming account+server. When there is no incoming account+server, the\n# search is restricted to deployments which have a corresponding local\n# account.\n#\n# Without appId or appName to identify an existing deployment, deployment\n# records associated with local accounts (possibly restricted by incoming\n# account+server) are considered before falling back to a generated name.\n#\n# When the targeted name does not exist locally or on the targeted\n# account+server, a deployment record with NULL appId is returned, which\n# signals to the caller that an application should be created.\nfindDeploymentTarget <- function(\n  recordPath = \".\",\n  appId = NULL,\n  appName = NULL,\n  appTitle = NULL,\n  envVars = NULL,\n  account = NULL,\n  server = NULL,\n  forceUpdate = FALSE,\n  error_call = caller_env()\n) {\n  if (!is.null(appId)) {\n    return(findDeploymentTargetByAppId(\n      recordPath = recordPath,\n      appId = appId,\n      appName = appName,\n      appTitle = appTitle,\n      envVars = envVars,\n      account = account,\n      server = server,\n      error_call = error_call\n    ))\n  }\n\n  if (!is.null(appName)) {\n    return(findDeploymentTargetByAppName(\n      recordPath = recordPath,\n      appName = appName,\n      appTitle = appTitle,\n      envVars = envVars,\n      account = account,\n      server = server,\n      forceUpdate = forceUpdate,\n      error_call = error_call\n    ))\n  }\n\n  # No identifying appId or appName.\n\n  # When there are existing deployments, ask the user to select one and use\n  # it. Only deployments associated with locally configured account+server\n  # combinations are considered.\n  allDeployments <- deployments(\n    appPath = recordPath,\n    accountFilter = account,\n    serverFilter = server\n  )\n  if (nrow(allDeployments) > 0) {\n    deployment <- disambiguateDeployments(\n      allDeployments,\n      error_call = error_call\n    )\n    deployment <- updateDeployment(deployment, appTitle, envVars)\n    accountDetails <- findAccountInfo(\n      deployment$account,\n      deployment$server,\n      error_call = error_call\n    )\n    return(list(\n      accountDetails = accountDetails,\n      deployment = deployment\n    ))\n  }\n\n  # Otherwise, identify a target account (given just one available or prompted\n  # by the user), generate a name, and locate the deployment.\n  accountDetails <- findAccountInfo(account, server, error_call = error_call)\n  appName <- generateAppName(\n    appTitle,\n    recordPath,\n    accountDetails$name,\n    unique = FALSE\n  )\n  return(findDeploymentTargetByAppName(\n    recordPath = recordPath,\n    appName = appName,\n    appTitle = appTitle,\n    envVars = envVars,\n    account = accountDetails$name,\n    server = accountDetails$server,\n    forceUpdate = forceUpdate,\n    error_call = error_call\n  ))\n}\n\n# Discover the deployment target given appId.\n#\n# When appId is provided, all other information is secondary. An appId is an\n# indication from the caller that the content has already been deployed\n# elsewhere. If we cannot locate that content, deployment fails.\n#\n# Local deployment records are considered first before looking for the appId\n# on the target server.\n#\n# The target content may have been created by some other user; the account for\n# this session may differ from the account used when creating the content.\nfindDeploymentTargetByAppId <- function(\n  recordPath = \".\",\n  appId = NULL,\n  appName = NULL,\n  appTitle = NULL,\n  envVars = NULL,\n  account = NULL,\n  server = NULL,\n  error_call = caller_env()\n) {\n  # We must have a target account+server in order to use the appId.\n  # The selected account may not be the original creator of the content.\n  accountDetails <- findAccountInfo(account, server, error_call = error_call)\n\n  # Filtering is only by server and includes all deployments in case we have a deployment record\n  # from a collaborator.\n  appDeployments <- deployments(\n    appPath = recordPath,\n    serverFilter = server,\n    excludeOrphaned = FALSE\n  )\n  appDeployments <- appDeployments[appDeployments$appId == appId, ]\n  if (nrow(appDeployments) > 1) {\n    cli::cli_abort(\n      c(\n        \"Supplied {.arg appId} ({appId}) identifies multiple deployments.\",\n        i = \"Manage obsolete deployments with rsconnect::forgetDeployment().\"\n      ),\n      call = error_call\n    )\n  }\n\n  # Existing local deployment record.\n  if (nrow(appDeployments) == 1) {\n    deployment <- appDeployments[1, ]\n    deployment <- updateDeployment(deployment, appTitle, envVars)\n    return(list(\n      accountDetails = accountDetails,\n      deployment = deployment\n    ))\n  }\n\n  # No local deployment record. Get it from the server.\n  application <- getApplication(\n    accountDetails$name,\n    accountDetails$server,\n    appId\n  )\n\n  # Note: The account+server of this deployment record may\n  # not correspond to the original content creator.\n  deployment <- createDeploymentFromApplication(application, accountDetails)\n  deployment <- updateDeployment(deployment, appTitle, envVars)\n  return(list(\n    accountDetails = accountDetails,\n    deployment = deployment\n  ))\n}\n\n# Discover the deployment target given appName.\n#\n# When appName is provided it identifies content previously created by a\n# locally configured account.\n#\n# The account details from the deployment record identify the final\n# credentials we will use, as account+server may not have been specified by\n# the caller.\nfindDeploymentTargetByAppName <- function(\n  recordPath = \".\",\n  appName = NULL,\n  appTitle = NULL,\n  envVars = NULL,\n  account = NULL,\n  server = NULL,\n  forceUpdate = FALSE,\n  error_call = caller_env()\n) {\n  appDeployments <- deployments(\n    appPath = recordPath,\n    nameFilter = appName,\n    accountFilter = account,\n    serverFilter = server\n  )\n\n  # When the appName along with the (optional) account+server identifies\n  # exactly one previous deployment, use it.\n  if (nrow(appDeployments) == 1) {\n    deployment <- appDeployments[1, ]\n    deployment <- updateDeployment(deployment, appTitle, envVars)\n    accountDetails <- findAccountInfo(\n      deployment$account,\n      deployment$server,\n      error_call = error_call\n    )\n    return(list(\n      accountDetails = accountDetails,\n      deployment = deployment\n    ))\n  }\n\n  # When the appName identifies multiple records, we may not have had an\n  # account+server constraint. Ask the user to choose.\n  if (nrow(appDeployments) > 1) {\n    deployment <- disambiguateDeployments(\n      appDeployments,\n      error_call = error_call\n    )\n    deployment <- updateDeployment(deployment, appTitle, envVars)\n    accountDetails <- findAccountInfo(\n      deployment$account,\n      deployment$server,\n      error_call = error_call\n    )\n    return(list(\n      accountDetails = accountDetails,\n      deployment = deployment\n    ))\n  }\n\n  # When the appName does not identify a record, see if it exists on the\n  # server. That content is conditionally used. A resolved account is\n  # required.\n  accountDetails <- findAccountInfo(account, server, error_call = error_call)\n  client <- clientForAccount(accountDetails)\n  application <- tryCatch(\n    getAppByName(client, accountDetails, appName, error_call = error_call),\n    rsconnect_app_not_found = function(err) NULL\n  )\n  if (!is.null(application)) {\n    uniqueName <- findUnique(appName, application$name)\n    if (\n      shouldUpdateApp(\n        application,\n        uniqueName,\n        forceUpdate,\n        error_call = error_call\n      )\n    ) {\n      deployment <- createDeploymentFromApplication(\n        application,\n        accountDetails\n      )\n      deployment <- updateDeployment(deployment, appTitle, envVars)\n      return(list(\n        accountDetails = accountDetails,\n        deployment = deployment\n      ))\n    } else {\n      appName <- uniqueName\n    }\n  }\n\n  # No existing deployment, or the caller does not want to re-use that content.\n  deployment <- createDeployment(\n    appName = appName,\n    appTitle = appTitle,\n    appId = NULL,\n    envVars = envVars,\n    username = accountDetails$name,\n    account = accountDetails$name,\n    server = accountDetails$server\n  )\n  return(list(\n    accountDetails = accountDetails,\n    deployment = deployment\n  ))\n}\n\ncreateDeployment <- function(\n  appName,\n  appTitle,\n  appId,\n  envVars,\n  username,\n  account,\n  server,\n  version = deploymentRecordVersion\n) {\n  # Consider merging this object with the object returned by\n  # deploymentRecord().\n  #\n  # Field names are shared with deploymentRecord() objects to avoid lots of\n  # record rewriting. Objects returned by findDeploymentTargetByAppName may\n  # have fields from the on-disk records, which were created by\n  # deploymentRecord().\n  list(\n    name = appName,\n    title = appTitle %||% \"\",\n    envVars = envVars,\n    appId = appId,\n    username = username,\n    account = account,\n    server = server,\n    version = version\n  )\n}\n\ncreateDeploymentFromApplication <- function(application, accountDetails) {\n  createDeployment(\n    appName = application$name,\n    appTitle = application$title,\n    appId = application$id,\n    envVars = NULL,\n    username = application$owner_username %||% accountDetails$name,\n    account = accountDetails$name,\n    server = accountDetails$server\n  )\n}\n\nupdateDeployment <- function(previous, appTitle = NULL, envVars = NULL) {\n  createDeployment(\n    appName = previous$name,\n    appTitle = appTitle %||% previous$title,\n    appId = previous$appId,\n    envVars = envVars %||% previous$envVars[[1]],\n    # if username not previously recorded, use current account\n    username = previous$username %||% previous$account,\n    account = previous$account,\n    server = previous$server,\n    version = previous$version\n  )\n}\n\nshouldUpdateApp <- function(\n  application,\n  uniqueName,\n  forceUpdate = FALSE,\n  error_call = caller_env()\n) {\n  if (forceUpdate) {\n    return(TRUE)\n  }\n\n  message <- c(\n    \"Discovered a previously deployed app named {.str {application$name}}\",\n    \"(View it at {.url {application$url}})\"\n  )\n\n  prompt <- \"What do you want to do?\"\n\n  choices <- c(\n    \"Update the existing app.\",\n    \"Create a new app with automatically generated name ({.str {uniqueName}}).\",\n    \"Abort this deployment and supply a custom `appName`.\"\n  )\n\n  not_interactive <- c(\n    i = \"Set `forceUpdate = TRUE` to update it.\",\n    i = \"Supply a unique `appName` to deploy a new application.\"\n  )\n\n  cli_menu(\n    message,\n    prompt,\n    choices,\n    not_interactive,\n    quit = 3,\n    error_call = error_call\n  ) ==\n    1\n}\n\n\nfindUnique <- function(x, existing) {\n  i <- 1\n  name <- paste0(x, \"-\", i)\n\n  while (name %in% existing) {\n    i <- i + 1\n    name <- paste0(x, \"-\", i)\n  }\n\n  name\n}\n"
  },
  {
    "path": "R/deployments-find.R",
    "content": "findDeployment <- function(\n  appPath = getwd(),\n  appName = NULL,\n  server = NULL,\n  account = NULL,\n  error_call = caller_env()\n) {\n  deps <- deployments(\n    appPath,\n    nameFilter = appName,\n    serverFilter = server,\n    accountFilter = account\n  )\n\n  if (nrow(deps) == 0) {\n    # When the name and account information does not discover a single deployment, return a skeleton\n    # deployment object, which can subsequently be used to query the service.\n\n    # Infers account when not provided...\n    accountDetails <- accountInfo(account, server)\n    if (is.null(appName)) {\n      appName <- generateAppName(\n        NULL,\n        appPath,\n        account = accountDetails$name,\n        unique = FALSE\n      )\n    }\n    list(\n      name = appName,\n      account = accountDetails$name,\n      server = accountDetails$server\n    )\n  } else if (nrow(deps) == 1) {\n    as.list(deps)\n  } else {\n    disambiguateDeployments(deps, error_call = error_call)\n  }\n}\n\ndisambiguateDeployments <- function(appDeployments, error_call = caller_env()) {\n  if (nrow(appDeployments) == 1) {\n    return(appDeployments[1, ])\n  }\n\n  apps <- sprintf(\n    \"%s (%s): {.url %s}\",\n    appDeployments$name,\n    accountLabel(appDeployments$account, appDeployments$server),\n    appDeployments$url\n  )\n  not_interactive <- c(\n    \"Please use {.arg appName}, {.arg server} or {.arg account} to disambiguate.\",\n    \"Known applications:\",\n    set_names(apps, \"*\")\n  )\n  idx <- cli_menu(\n    \"This directory has been previously deployed in multiple places.\",\n    \"Which deployment do you want to use?\",\n    choices = apps,\n    not_interactive = not_interactive,\n    error_call = error_call\n  )\n  as.list(appDeployments[idx, ])\n}\n"
  },
  {
    "path": "R/deployments.R",
    "content": "#' List Application Deployments\n#'\n#' @description\n#' List deployment records for a given application.\n#'\n#' Supported servers: All servers\n#'\n#' @param appPath The path to the content that was deployed, either a directory\n#'   or an individual document.\n#' @param nameFilter Return only deployments matching the given name (optional)\n#' @param accountFilter Return only deployments matching the given account\n#'   (optional)\n#' @param serverFilter Return only deployments matching the given server\n#'   (optional)\n#' @param excludeOrphaned If `TRUE` (the default), return only deployments\n#'   made by a currently registered account. Deployments made from accounts that\n#'   are no longer registered (via e.g.[removeAccount()]) will not be\n#'   returned.\n#' @return\n#' Returns a data frame with at least following columns:\n#' \\tabular{ll}{\n#' `name` \\tab Name of deployed application\\cr\n#' `account` \\tab Account owning deployed application\\cr\n#' `bundleId` \\tab Identifier of deployed application's bundle\\cr\n#' `url` \\tab URL of deployed application\\cr\n#' `deploymentFile` \\tab Name of configuration file\\cr\n#' }\n#'\n#' If additional metadata has been saved with the deployment record using the\n#' `metadata` argument to [deployApp()], the frame will include\n#' additional columns.\n#'\n#' @examples\n#' \\dontrun{\n#'\n#' # Return all deployments of the ~/r/myapp directory made with the 'abc'\n#' # account\n#' deployments(\"~/r/myapp\", accountFilter=\"abc\")\n#' }\n#' @seealso [applications()] to get a list of deployments from the\n#'   server, and [deployApp()] to create a new deployment.\n#' @export\ndeployments <- function(\n  appPath = \".\",\n  nameFilter = NULL,\n  accountFilter = NULL,\n  serverFilter = NULL,\n  excludeOrphaned = TRUE\n) {\n  migrateDeploymentsConfig(appPath)\n  paths <- deploymentConfigFiles(appPath)\n\n  dcf <- lapply(paths, read.dcf)\n  dcf <- lapply(dcf, as.data.frame, stringsAsFactors = FALSE)\n\n  deployments <- rbind_fill(dcf, deploymentFields)\n  deployments$deploymentFile <- paths\n\n  # Apply filters\n  ok <- rep(TRUE, nrow(deployments))\n  if (!is.null(nameFilter)) {\n    ok <- ok & deployments$name == nameFilter\n  }\n  if (!is.null(accountFilter)) {\n    ok <- ok & deployments$account == accountFilter\n  }\n  if (!is.null(serverFilter)) {\n    ok <- ok & deployments$server == serverFilter\n  }\n  if (excludeOrphaned) {\n    activeAccounts <- accounts()\n    activeAccountServers <- paste0(\n      activeAccounts$server,\n      \"@\",\n      activeAccounts$name\n    )\n    accountServer <- paste0(deployments$server, \"@\", deployments$account)\n    okServer <- isRPubs(deployments$server) |\n      accountServer %in% activeAccountServers\n    ok <- ok & okServer\n  }\n\n  deployments$envVars[is.na(deployments$envVars)] <- \"\"\n  if (is.character(deployments$envVars)) {\n    deployments$envVars <- strsplit(deployments$envVars, \", \")\n  }\n\n  deployments[ok, , drop = FALSE]\n}\n\ndeploymentFields <- c(\n  \"name\",\n  \"title\",\n  \"username\",\n  \"account\",\n  \"server\",\n  \"hostUrl\",\n  \"appId\",\n  \"bundleId\",\n  \"url\",\n  \"envVars\",\n  \"version\"\n)\n\ndeploymentRecordVersion <- 1L\n\n# Save a deployment record to disk using an incoming record (which may or may\n# not correspond to an existing on-disk deployment record). Created by\n# deploymentRecord() or by findDeploymentTarget(), and possibly loaded from\n# disk.\nsaveDeployment <- function(\n  recordDir,\n  deployment,\n  application,\n  bundleId = NULL,\n  hostUrl = serverInfo(deployment$server)$url,\n  metadata = list(),\n  addToHistory = TRUE\n) {\n  deployment <- deploymentRecord(\n    name = deployment$name,\n    title = deployment$title,\n    username = deployment$username,\n    account = deployment$account,\n    server = deployment$server,\n    envVars = deployment$envVars,\n    version = deployment$version,\n    hostUrl = hostUrl,\n    appId = application$id,\n    bundleId = bundleId,\n    url = application$url,\n    metadata = metadata\n  )\n  path <- deploymentConfigFile(\n    recordDir,\n    deployment$name,\n    deployment$account,\n    deployment$server\n  )\n  writeDeploymentRecord(deployment, path)\n\n  # also save to global history\n  if (addToHistory) {\n    addToDeploymentHistory(recordDir, deployment)\n  }\n\n  invisible(path)\n}\n\ndeploymentRecord <- function(\n  name,\n  title,\n  username,\n  account,\n  server,\n  envVars = NULL,\n  hostUrl = NULL,\n  appId = NULL,\n  bundleId = NULL,\n  url = NULL,\n  version = deploymentRecordVersion,\n  metadata = list()\n) {\n  check_character(envVars, allow_null = TRUE)\n\n  standard <- list(\n    name = name,\n    title = title %||% \"\",\n    username = username,\n    account = account,\n    server = server,\n    envVars = if (length(envVars) > 0) paste0(envVars, collapse = \", \") else NA,\n    hostUrl = hostUrl %||% \"\",\n    appId = appId %||% \"\",\n    bundleId = bundleId %||% \"\",\n    url = url %||% \"\",\n    version = version\n  )\n  # convert any multi-value metadata entries into comma-separated values\n  # this prevents write.dcf from writing multiple records into one file.\n  metadata <- lapply(metadata, function(v) paste0(v, collapse = \", \"))\n  c(standard, metadata)\n}\n\nwriteDeploymentRecord <- function(record, filePath) {\n  # use a long width so URLs don't line-wrap\n  write.dcf(record, filePath, width = 4096)\n}\n\n# Workbench uses to show a list of recently deployed content on user dashboard.\naddToDeploymentHistory <- function(appPath, deploymentRecord) {\n  # deployment history is global; reference back to the app path.\n  deploymentRecord$appPath <- appPath\n\n  # Truncate the history file when it grows beyond this size.\n  # Do not read large files; they may be enormous (#1320).\n  max_bytes <- getOption(\"rsconnect.max.history.bytes\", 1024^2)\n\n  # When the history file is reasonably sized, preserve this number of records.\n  max_records <- max(1L, getOption(\"rsconnect.max.history.records\", 100L))\n\n  history <- deploymentHistoryPath()\n  prior <- if (file.exists(history) && file.size(history) <= max_bytes) {\n    read.dcf(history)\n  } else {\n    matrix(character(0), nrow = 0)\n  }\n\n  # The history file lists most-recent first; write the new record before\n  # appending older items, capped by max_records.\n  newHistory <- deploymentHistoryPath(new = TRUE)\n  on.exit(unlink(newHistory), add = TRUE)\n  writeDeploymentRecord(deploymentRecord, newHistory)\n  n_prior <- min(nrow(prior), max_records - 1L)\n  if (n_prior > 0L) {\n    cat(\"\\n\", file = newHistory, append = TRUE)\n    write.dcf(head(prior, n_prior), newHistory, append = TRUE, width = 4096)\n  }\n\n  file.rename(newHistory, history)\n  invisible()\n}\n\n#' Forget Application Deployment\n#'\n#' @description\n#' Forgets about an application deployment. This is useful if the application\n#' has been deleted on the server, or the local deployment information needs to\n#' be reset.\n#'\n#' Supported servers: All servers\n#'\n#' @param appPath The path to the content that was deployed, either a directory\n#'   or an individual document.\n#' @param name The name of the content that was deployed (optional)\n#' @param account The name of the account to which the content was deployed\n#'   (optional)\n#' @param server The name of the server to which the content was deployed\n#'   (optional)\n#' @param dryRun Set to TRUE to preview the files/directories to be removed\n#'   instead of actually removing them. Defaults to FALSE.\n#' @param force Set to TRUE to remove files and directories without prompting.\n#'   Defaults to FALSE in interactive sessions.\n#' @return NULL, invisibly.\n#'\n#' @details This method removes from disk the file containing deployment\n#'   metadata. If \"name\", \"account\", and \"server\" are all NULL, then all of the\n#'   deployments for the application are forgotten; otherwise, only the\n#'   specified deployment is forgotten.\n#'\n#' @export\nforgetDeployment <- function(\n  appPath = getwd(),\n  name = NULL,\n  account = NULL,\n  server = NULL,\n  dryRun = FALSE,\n  force = !interactive()\n) {\n  if (is.null(name) && is.null(account) && is.null(server)) {\n    dcfDir <- deploymentConfigDir(appPath)\n    if (dryRun) {\n      message(\"Would remove the directory \", dcfDir)\n    } else if (file.exists(dcfDir)) {\n      if (!force) {\n        prompt <- paste(\n          \"Forget all deployment records for \",\n          appPath,\n          \"? [Y/n] \",\n          sep = \"\"\n        )\n        input <- readline(prompt)\n        if (nzchar(input) && !identical(input, \"y\") && !identical(input, \"Y\")) {\n          stop(\"No deployment records removed.\", call. = FALSE)\n        }\n      }\n      unlink(dcfDir, recursive = TRUE)\n    } else {\n      message(\"No deployments found for the application at \", appPath)\n    }\n  } else {\n    if (is.null(name) || is.null(account) || is.null(server)) {\n      stop(\n        \"Invalid argument. \",\n        \"Supply the name, account, and server of the deployment record to delete. \",\n        \"Supply NULL for all three to delete all deployment records.\"\n      )\n    }\n    dcf <- deploymentConfigFile(appPath, name, account, server)\n    if (dryRun) {\n      message(\"Would remove the file \", dcf)\n    } else if (file.exists(dcf)) {\n      if (!force) {\n        prompt <- paste(\n          \"Forget deployment of \",\n          appPath,\n          \" to '\",\n          name,\n          \"' on \",\n          server,\n          \"? [Y/n] \",\n          sep = \"\"\n        )\n        input <- readline(prompt)\n        if (nzchar(input) && !identical(input, \"y\") && !identical(input, \"Y\")) {\n          stop(\"Cancelled. No deployment records removed.\", call. = FALSE)\n        }\n      }\n      unlink(dcf)\n    } else {\n      message(\n        \"No deployment of \",\n        appPath,\n        \" to '\",\n        name,\n        \"' on \",\n        server,\n        \" found.\"\n      )\n    }\n  }\n\n  invisible(NULL)\n}\n"
  },
  {
    "path": "R/envvars.R",
    "content": "#' Maintain environment variables across multiple applications\n#'\n#' @description\n#' * `listAccountEnvVars()` lists the environment variables used by\n#'   every application published to the specified account.\n#' * `updateAccountEnvVars()` updates the specified environment variables with\n#'   their current values for every app that uses them.\n#'\n#' Secure environment variable are currently only supported by Posit Connect\n#' so other server types will generate an error.\n#'\n#' Supported servers: Posit Connect servers\n#'\n#' @inheritParams deployApp\n#' @export\n#' @return `listAccountEnvVars()` returns a data frame with one row\n#'   for each data frame. It has variables `id`, `guid`, `name`, and\n#'   `envVars`. `envVars` is a list-column.\nlistAccountEnvVars <- function(server = NULL, account = NULL) {\n  accountDetails <- accountInfo(account, server)\n  checkServerHasEnvVars(accountDetails$server)\n\n  apps <- applications(\n    account = accountDetails$name,\n    server = accountDetails$server\n  )\n  apps <- apps[c(\"id\", \"guid\", \"name\")]\n\n  client <- clientForAccount(accountDetails)\n  envVars <- lapply(apps$guid, client$getEnvVars)\n  apps$envVars <- envVars\n  apps\n}\n\n#' @export\n#' @rdname listAccountEnvVars\n#' @param envVars Names of environment variables to update. Their\n#'   values will be automatically retrieved from the current process.\n#'\n#'   If you specify multiple environment variables, any application that\n#'   uses any of them will be updated with all of them.\nupdateAccountEnvVars <- function(envVars, server = NULL, account = NULL) {\n  check_character(envVars)\n\n  accountDetails <- accountInfo(account, server)\n  checkServerHasEnvVars(accountDetails$server)\n\n  apps <- listAccountEnvVars(\n    account = accountDetails$name,\n    server = accountDetails$server\n  )\n  uses_vars <- vapply(apps$envVars, function(x) any(envVars %in% x), logical(1))\n  if (!any(uses_vars)) {\n    cli::cli_abort(\n      \"No applications use environment variable{?s} {.arg {envVars}}\"\n    )\n  }\n\n  guids <- apps$guid[uses_vars]\n  cli::cli_progress_bar(\"Updating application...\", total = length(guids))\n\n  client <- clientForAccount(accountDetails)\n  for (guid in guids) {\n    client$setEnvVars(guid, envVars)\n    cli::cli_progress_update()\n  }\n}\n\n# Helpers -----------------------------------------------------------------\n\ncheckServerHasEnvVars <- function(server, error_call = caller_env()) {\n  if (isConnectServer(server)) {\n    return()\n  }\n\n  cli::cli_abort(\n    \"The {.arg server} {.str {server}} does not support environment variables\"\n  )\n}\n"
  },
  {
    "path": "R/http-httr2.R",
    "content": "# httr2-based HTTP backend for rsconnect\n#\n# These functions are called when getOption(\"rsconnect.httr2\") is TRUE.\n# They return httr2_response objects, letting httr2 handle cookies,\n# redirects, and other HTTP concerns natively.\n\nhttr2Request <- function(\n  service,\n  authInfo,\n  method,\n  path,\n  headers = list(),\n  timeout = NULL,\n  certificate = NULL,\n  contentType = NULL,\n  file = NULL\n) {\n  service$path <- path\n  url <- buildHttpUrl(service)\n\n  # Store user-specified cookies and append to headers (same as libcurl backend)\n  headers <- appendCookieHeaders(service, headers)\n\n  # No pipe, for compatibility with older R versions\n  req <- httr2::request(url)\n  req <- httr2::req_method(req, method)\n  req <- httr2::req_headers(req, !!!headers)\n  req <- httr2::req_user_agent(req, userAgent())\n\n  # Apply more options\n\n  # SSL certificate handling\n  if (isFALSE(getOption(\"rsconnect.check.certificate\", TRUE))) {\n    req <- httr2::req_options(req, ssl_verifypeer = FALSE)\n  } else if (!is.null(certificate)) {\n    req <- httr2::req_options(req, cainfo = certificate)\n  }\n  # User-specified libcurl options\n  user_opts <- getOption(\"rsconnect.libcurl.options\")\n  if (is.list(user_opts)) {\n    req <- httr2::req_options(req, !!!user_opts)\n  }\n\n  # Timeout\n  if (!is.null(timeout)) {\n    req <- httr2::req_timeout(req, timeout)\n  }\n\n  # Verbose output\n  if (httpVerbose()) {\n    req <- httr2::req_verbose(req)\n  }\n\n  # Add file body with progress for large files\n  if (!is.null(file)) {\n    file_size <- file.info(file)$size\n    req <- httr2::req_body_file(req, file, type = contentType)\n    if (is_interactive() && file_size >= 10 * 1024^2) {\n      req <- httr2::req_progress(req)\n    }\n  }\n\n  # Don't error on HTTP failures - we handle that in handleResponse\n  req <- httr2::req_options(req, followlocation = FALSE)\n  req <- httr2::req_error(req, is_error = function(resp) FALSE)\n\n  resp <- httr2::req_perform(req)\n  httpTrace(method, path, httr2::resp_timing(resp)[\"total\"])\n\n  # Store cookies from response (same as libcurl backend)\n  cookieHeaders <- httr2::resp_headers(resp, \"set-cookie\")\n  storeCookies(service, cookieHeaders)\n\n  resp\n}\n\n# Convert httr2 response to the list format expected by handleResponse\nhttr2_response_to_list <- function(resp) {\n  # Parse the final URL (after any redirects) to build the req structure\n  final_url <- httr2::url_parse(resp$url)\n\n  # Handle empty bodies (httr2::resp_body_string errors on empty body)\n  content <- tryCatch(\n    httr2::resp_body_string(resp),\n    error = function(e) \"\"\n  )\n\n  list(\n    req = list(\n      protocol = final_url$scheme,\n      host = final_url$hostname,\n      port = final_url$port %||% \"\",\n      path = final_url$path\n    ),\n    status = httr2::resp_status(resp),\n    location = httr2::resp_header(resp, \"location\"),\n    contentType = httr2::resp_content_type(resp) %||% \"text/plain\",\n    content = content\n  )\n}\n"
  },
  {
    "path": "R/http-libcurl.R",
    "content": "httpLibCurl <- function(\n  protocol,\n  host,\n  port,\n  method,\n  path,\n  headers,\n  contentType = NULL,\n  contentFile = NULL,\n  certificate = NULL,\n  timeout = NULL\n) {\n  request <- list(\n    protocol = protocol,\n    host = host,\n    port = port,\n    method = method,\n    path = path\n  )\n\n  handle <- createCurlHandle(\n    method = method,\n    timeout = timeout,\n    certificate = certificate\n  )\n\n  if (!is.null(contentFile)) {\n    if (is.null(contentType)) {\n      stop(\"You must specify a contentType for the specified file\")\n    }\n\n    fileLength <- file.info(contentFile)$size\n    headers$`Content-Type` <- contentType\n    headers$`Content-Length` <- as.character(fileLength)\n\n    # open a connection to read the file, and ensure it's closed when we're done\n    contentCon <- file(contentFile, \"rb\")\n    defer(if (!is.null(contentCon)) close(contentCon))\n\n    progress <- is_interactive() && fileLength >= 10 * 1024^2\n\n    curl::handle_setopt(\n      handle,\n      noprogress = !progress,\n      upload = TRUE,\n      infilesize_large = fileLength,\n      readfunction = function(nbytes, ...) {\n        if (is.null(contentCon)) {\n          return(raw())\n        }\n        bin <- readBin(contentCon, \"raw\", nbytes)\n        if (length(bin) < nbytes) {\n          close(contentCon)\n          contentCon <<- NULL\n        }\n        bin\n      }\n    )\n  }\n\n  headers <- appendCookieHeaders(request, headers)\n  curl::handle_setheaders(handle, .list = headers)\n\n  # make the request\n  url <- buildHttpUrl(request)\n  start <- proc.time()\n  response <- curl::curl_fetch_memory(url, handle = handle)\n  time <- proc.time() - start\n\n  httpTrace(method, path, time[[\"elapsed\"]])\n\n  # Process headers\n  headers <- curl::parse_headers_list(rawToChar(response$headers))\n\n  # Parse cookies from header; bear in mind that there may be multiple headers\n  cookieHeaders <- headers[names(headers) == \"set-cookie\"]\n  storeCookies(request, cookieHeaders)\n\n  # presume a plain text response unless specified otherwise\n  contentType <- headers[[\"content-type\"]] %||% \"text/plain\"\n  contentValue <- rawToChar(response$content)\n\n  # emit JSON trace if requested\n  jsonTracingEnabled <- httpTraceJson() && contentType == \"application/json\"\n  if (jsonTracingEnabled) {\n    if (!is.null(contentFile)) {\n      cat(paste0(\n        \"<< \",\n        readLines(contentFile, warn = FALSE),\n        \"\\n\",\n        collapse = \"\"\n      ))\n    }\n    lines <- strsplit(contentValue, \"\\n\")[[1]]\n    cat(paste0(\">> \", lines, \"\\n\", collapse = \"\"))\n  }\n\n  list(\n    req = request,\n    status = response$status_code,\n    location = headers$location,\n    contentType = contentType,\n    content = contentValue\n  )\n}\n\ncreateCurlHandle <- function(method, timeout = NULL, certificate = NULL) {\n  # create curl handle\n  handle <- curl::new_handle()\n\n  # overlay user-supplied options\n  userOptions <- getOption(\"rsconnect.libcurl.options\")\n  if (is.list(userOptions)) {\n    curl::handle_setopt(handle, .list = userOptions)\n  }\n\n  curl::handle_setopt(handle, customrequest = method)\n  curl::handle_setopt(handle, useragent = userAgent())\n\n  if (isTRUE(getOption(\"rsconnect.check.certificate\", TRUE))) {\n    curl::handle_setopt(handle, ssl_verifypeer = TRUE)\n\n    # apply certificate information if present\n    if (!is.null(certificate)) {\n      curl::handle_setopt(handle, cainfo = certificate)\n    }\n  } else {\n    # don't verify peer (less secure but tolerant to self-signed cert issues)\n    curl::handle_setopt(handle, ssl_verifypeer = FALSE)\n  }\n\n  # use timeout if supplied\n  if (!is.null(timeout)) {\n    curl::handle_setopt(handle, timeout = timeout)\n  }\n\n  # verbose if requested\n  if (httpVerbose()) {\n    curl::handle_setopt(handle, verbose = TRUE)\n  }\n\n  # suppress curl's automatically handling of redirects, since we have to\n  # handle ourselves in httpRequest()/httpRequestWithBody() due to our\n  # specialised auth needs\n  curl::handle_setopt(handle, followlocation = FALSE)\n\n  handle\n}\n"
  },
  {
    "path": "R/http.R",
    "content": "#' @param authInfo Typically an object created by `accountInfo()` augmented\n#'   with the `certificate` from the corresponding `serverInfo()`.\n#'\n#'   There are three different fields used for auth:\n#'   * `secret`: set in `setAccountInfo()`\n#'   * `private_key`: set in `connectUser()`\n#'   * `apiKey`: set in `connectApiUser()`\n#'\n#' @noRd\nhttpRequest <- function(\n  service,\n  authInfo,\n  method,\n  path,\n  query,\n  headers = list(),\n  timeout = NULL,\n  rawResponse = FALSE,\n  error_call = caller_env()\n) {\n  storeCookies(service, httpCookies())\n  path <- buildPath(service$path, path, query)\n  headers <- c(headers, authHeaders(authInfo, method, path), httpHeaders())\n  certificate <- requestCertificate(service$protocol, authInfo$certificate)\n\n  if (isTRUE(getOption(\"rsconnect.httr2\", TRUE))) {\n    # httr2 backend - handles cookies and redirects automatically\n    resp <- httr2Request(\n      service,\n      authInfo,\n      method,\n      path,\n      headers,\n      timeout,\n      certificate\n    )\n    httpResponse <- httr2_response_to_list(resp)\n\n    while (isRedirect(httpResponse$status)) {\n      service <- redirectService(service, httpResponse$location)\n      resp <- httr2Request(\n        service,\n        authInfo,\n        \"GET\",\n        service$path,\n        headers,\n        timeout,\n        certificate\n      )\n      httpResponse <- httr2_response_to_list(resp)\n    }\n  } else {\n    # Legacy libcurl backend\n    httpResponse <- httpLibCurl(\n      protocol = service$protocol,\n      host = service$host,\n      port = service$port,\n      method = method,\n      path = path,\n      headers = headers,\n      timeout = timeout,\n      certificate = certificate\n    )\n\n    while (isRedirect(httpResponse$status)) {\n      service <- redirectService(service, httpResponse$location)\n      httpResponse <- httpLibCurl(\n        protocol = service$protocol,\n        host = service$host,\n        port = service$port,\n        method = method,\n        path = service$path,\n        headers = headers,\n        timeout = timeout,\n        certificate = certificate\n      )\n    }\n  }\n\n  handleResponse(\n    httpResponse,\n    rawResponse = rawResponse,\n    error_call = error_call\n  )\n}\n\nhttpRequestWithBody <- function(\n  service,\n  authInfo,\n  method,\n  path,\n  query = NULL,\n  contentType = NULL,\n  file = NULL,\n  content = NULL,\n  headers = list(),\n  rawResponse = FALSE,\n  error_call = caller_env()\n) {\n  if ((is.null(file) && is.null(content))) {\n    stop(\"You must specify either the file or content parameter.\")\n  }\n  if ((!is.null(file) && !is.null(content))) {\n    stop(\"You must specify either the file or content parameter but not both.\")\n  }\n\n  # if we have content then write it to a temp file before posting\n  if (!is.null(content)) {\n    file <- tempfile()\n    writeChar(content, file, eos = NULL, useBytes = TRUE)\n  }\n\n  storeCookies(service, httpCookies())\n  path <- buildPath(service$path, path, query)\n  headers <- c(headers, httpHeaders())\n  authed_headers <- c(headers, authHeaders(authInfo, method, path, file))\n  certificate <- requestCertificate(service$protocol, authInfo$certificate)\n\n  if (isTRUE(getOption(\"rsconnect.httr2\", TRUE))) {\n    # httr2 backend - handles cookies and redirects automatically\n    resp <- httr2Request(\n      service,\n      authInfo,\n      method,\n      path,\n      authed_headers,\n      certificate = certificate,\n      contentType = contentType,\n      file = file\n    )\n    httpResponse <- httr2_response_to_list(resp)\n\n    while (isRedirect(httpResponse$status)) {\n      service <- redirectService(service, httpResponse$location)\n      authed_headers <- c(headers, authHeaders(authInfo, \"GET\", service$path))\n      resp <- httr2Request(\n        service,\n        authInfo,\n        \"GET\",\n        service$path,\n        authed_headers,\n        certificate = certificate\n      )\n      httpResponse <- httr2_response_to_list(resp)\n    }\n  } else {\n    # Legacy libcurl backend\n    httpResponse <- httpLibCurl(\n      protocol = service$protocol,\n      host = service$host,\n      port = service$port,\n      method = method,\n      path = path,\n      headers = authed_headers,\n      contentType = contentType,\n      contentFile = file,\n      certificate = certificate\n    )\n    while (isRedirect(httpResponse$status)) {\n      # This is a simplification of the spec, since we should preserve\n      # the method for 307 and 308, but that's unlikely to arise for our apps\n      # https://www.rfc-editor.org/rfc/rfc9110.html#name-redirection-3xx\n      service <- redirectService(service, httpResponse$location)\n      authed_headers <- c(headers, authHeaders(authInfo, \"GET\", service$path))\n      httpResponse <- httpLibCurl(\n        protocol = service$protocol,\n        host = service$host,\n        port = service$port,\n        method = \"GET\",\n        path = service$path,\n        headers = authed_headers,\n        certificate = certificate\n      )\n      httpResponse\n    }\n  }\n\n  handleResponse(\n    httpResponse,\n    rawResponse = rawResponse,\n    error_call = error_call\n  )\n}\n\nisRedirect <- function(status) {\n  status %in% c(301, 302, 307, 308)\n}\n\nredirectService <- function(service, location) {\n  if (grepl(\"^/\", location)) {\n    service$path <- location\n    service\n  } else {\n    parseHttpUrl(location)\n  }\n}\n\nhandleResponse <- function(\n  response,\n  rawResponse = FALSE,\n  error_call = caller_env()\n) {\n  url <- buildHttpUrl(response$req)\n  reportError <- function(msg, errorType = NULL) {\n    # msg may be JSON; wrapping with \"{msg}\" avoids treating it as a formatting string.\n    cli::cli_abort(\n      c(\"<{url}> failed with HTTP status {response$status}\", \"{msg}\"),\n      class = c(paste0(\"rsconnect_http_\", response$status), \"rsconnect_http\"),\n      errorType = errorType,\n      call = error_call\n    )\n  }\n\n  if (rawResponse) {\n    if (response$status %in% 200:399) {\n      out <- response$content\n    } else {\n      reportError(response$content)\n    }\n  } else if (isContentType(response$contentType, \"application/json\")) {\n    # parse json responses\n    if (nzchar(response$content)) {\n      json <- jsonlite::fromJSON(response$content, simplifyVector = FALSE)\n    } else {\n      json <- list()\n    }\n\n    if (response$status %in% 200:399) {\n      out <- json\n    } else if (!is.null(json$error)) {\n      reportError(unlist(json$error), errorType = unlist(json$error_type))\n    } else {\n      reportError(paste(\"Unexpected json response:\", response$content))\n    }\n  } else if (isContentType(response$contentType, \"text/html\")) {\n    # extract body of html responses\n    body <- regexExtract(\".*?<body>(.*?)</body>.*\", response$content)\n    if (response$status >= 200 && response$status < 400) {\n      # Good response, return the body if we have one, or the content if not\n      if (!is.null(body)) {\n        out <- body\n      } else {\n        out <- response$content\n      }\n    } else {\n      # Error response\n      if (!is.null(body)) {\n        reportError(body)\n      } else {\n        reportError(response$content)\n      }\n    }\n  } else {\n    # otherwise just dump the whole thing\n    if (response$status %in% 200:399) {\n      out <- response$content\n    } else {\n      reportError(response$content)\n    }\n  }\n\n  attr(out, \"httpContentType\") <- response$contentType\n  attr(out, \"httpUrl\") <- url\n  out\n}\n\n# Wrappers for HTTP methods -----------------------------------------------\n\nGET <- function(\n  service,\n  authInfo,\n  path,\n  query = NULL,\n  headers = list(),\n  timeout = NULL,\n  rawResponse = FALSE\n) {\n  httpRequest(\n    service,\n    authInfo,\n    \"GET\",\n    path,\n    query,\n    headers,\n    timeout,\n    rawResponse\n  )\n}\n\nDELETE <- function(\n  service,\n  authInfo,\n  path,\n  query = NULL,\n  headers = list(),\n  rawResponse = FALSE\n) {\n  httpRequest(\n    service,\n    authInfo,\n    \"DELETE\",\n    path,\n    query,\n    headers,\n    rawResponse = rawResponse\n  )\n}\n\nPOST <- function(\n  service,\n  authInfo,\n  path,\n  query = NULL,\n  contentType = NULL,\n  file = NULL,\n  content = NULL,\n  headers = list(),\n  rawResponse = FALSE\n) {\n  # check if the request needs a body\n  if ((is.null(file) && is.null(content))) {\n    # no file or content, don't include a body with the request\n    httpRequest(\n      service,\n      authInfo,\n      \"POST\",\n      path,\n      query,\n      headers,\n      rawResponse = rawResponse\n    )\n  } else {\n    # include the request's data in the body\n    httpRequestWithBody(\n      service = service,\n      authInfo = authInfo,\n      method = \"POST\",\n      path = path,\n      query = query,\n      contentType = contentType,\n      file = file,\n      content = content,\n      headers = headers,\n      rawResponse = rawResponse\n    )\n  }\n}\n\nPOST_JSON <- function(\n  service,\n  authInfo,\n  path,\n  json,\n  query = NULL,\n  headers = list(),\n  rawResponse = FALSE\n) {\n  POST(\n    service = service,\n    authInfo = authInfo,\n    path = path,\n    query = query,\n    contentType = \"application/json\",\n    content = toJSON(json),\n    headers = headers,\n    rawResponse = rawResponse\n  )\n}\n\nPUT <- function(\n  service,\n  authInfo,\n  path,\n  query = NULL,\n  contentType = NULL,\n  file = NULL,\n  content = NULL,\n  headers = list(),\n  rawResponse = FALSE\n) {\n  httpRequestWithBody(\n    service = service,\n    authInfo = authInfo,\n    method = \"PUT\",\n    path = path,\n    query = query,\n    contentType = contentType,\n    file = file,\n    content = content,\n    headers = headers,\n    rawResponse = rawResponse\n  )\n}\n\nPUT_JSON <- function(\n  service,\n  authInfo,\n  path,\n  json,\n  query = NULL,\n  headers = list(),\n  rawResponse = FALSE\n) {\n  PUT(\n    service = service,\n    authInfo = authInfo,\n    path = path,\n    query = query,\n    contentType = \"application/json\",\n    content = toJSON(json),\n    headers = headers,\n    rawResponse = rawResponse\n  )\n}\n\nPATCH <- function(\n  service,\n  authInfo,\n  path,\n  query = NULL,\n  contentType = NULL,\n  file = NULL,\n  content = NULL,\n  headers = list(),\n  rawResponse = FALSE\n) {\n  httpRequestWithBody(\n    service = service,\n    authInfo = authInfo,\n    method = \"PATCH\",\n    path = path,\n    query = query,\n    contentType = contentType,\n    file = file,\n    content = content,\n    headers = headers,\n    rawResponse = rawResponse\n  )\n}\n\nPATCH_JSON <- function(\n  service,\n  authInfo,\n  path,\n  json,\n  query = NULL,\n  headers = list(),\n  rawResponse = FALSE\n) {\n  PATCH(\n    service = service,\n    authInfo = authInfo,\n    path = path,\n    query = query,\n    contentType = \"application/json\",\n    content = toJSON(json),\n    headers = headers,\n    rawResponse = rawResponse\n  )\n}\n\n# User options ------------------------------------------------------------\n\nhttpVerbose <- function() {\n  getOption(\"rsconnect.http.verbose\", FALSE)\n}\n\nhttpTraceJson <- function() {\n  getOption(\"rsconnect.http.trace.json\", FALSE)\n}\n\nhttpTrace <- function(method, path, time) {\n  if (getOption(\"rsconnect.http.trace\", FALSE)) {\n    cat(\n      method,\n      \" \",\n      path,\n      \" \",\n      as.integer(time * 1000),\n      \"ms\\n\",\n      sep = \"\"\n    )\n  }\n}\n\nhttpCookies <- function() {\n  getOption(\"rsconnect.http.cookies\", character())\n}\n\nhttpHeaders <- function() {\n  getOption(\"rsconnect.http.headers\", character())\n}\n\n# URL manipulation --------------------------------------------------------\n\nparseHttpUrl <- function(urlText) {\n  matches <- regexec(\"(http|https)://([^:/#?]+)(?::(\\\\d+))?(.*)\", urlText)\n  components <- regmatches(urlText, matches)[[1]]\n  if (length(components) == 0) {\n    stop(\"Invalid url: \", urlText)\n  }\n\n  url <- list()\n  url$protocol <- components[[2]]\n  url$host <- components[[3]]\n  url$port <- components[[4]]\n  url$path <- components[[5]]\n  url\n}\n\nbuildHttpUrl <- function(x) {\n  colon <- if (!is.null(x$port) && nzchar(x$port)) \":\"\n  paste0(x$protocol, \"://\", x$host, colon, x$port, x$path)\n}\n\nurlDecode <- function(x) {\n  curl::curl_unescape(x)\n}\n\nurlEncode <- function(x) {\n  if (inherits(x, \"AsIs\")) {\n    return(x)\n  }\n  curl::curl_escape(x)\n}\n\nbuildPath <- function(apiPath, path, query = NULL) {\n  # prepend the service path\n  url <- paste(apiPath, path, sep = \"\")\n\n  # append the query\n  if (!is.null(query)) {\n    # URL encode query args\n    query <- utils::URLencode(query)\n    url <- paste(url, \"?\", query, sep = \"\")\n  }\n\n  url\n}\n\nqueryString <- function(elements) {\n  stopifnot(is.list(elements))\n  elements <- elements[!sapply(elements, is.null)]\n\n  names <- curl::curl_escape(names(elements))\n  values <- vapply(elements, urlEncode, character(1))\n  if (length(elements) > 0) {\n    result <- paste0(names, \"=\", values, collapse = \"&\")\n  } else {\n    result <- \"\"\n  }\n  return(result)\n}\n\n# Auth --------------------------------------------------------------------\n\nrequestCertificate <- function(protocol, certificate = NULL) {\n  if (identical(protocol, \"https\")) {\n    createCertificateFile(certificate)\n  } else {\n    NULL\n  }\n}\n\nauthHeaders <- function(authInfo, method, path, file = NULL) {\n  if (!is.null(authInfo$snowflakeToken)) {\n    # snowflakeauth returns a list of named header values\n    headers <- authInfo$snowflakeToken\n    # The SPCS/Snowflake token is in the Authorization header and the Connect API key is passed\n    # using X-RSC-Authorization.\n    if (!is.null(authInfo$apiKey)) {\n      headers$`X-RSC-Authorization` <- paste(\"Key\", authInfo$apiKey)\n    }\n    # When using the \"token\" flow rather than an API key, we also need the\n    # signature headers.\n    if (!is.null(authInfo$secret) || !is.null(authInfo$private_key)) {\n      sig_headers <- signatureHeaders(authInfo, method, path, file)\n      headers <- c(headers, sig_headers)\n    }\n    headers\n  } else if (!is.null(authInfo$secret) || !is.null(authInfo$private_key)) {\n    signatureHeaders(authInfo, method, path, file)\n  } else if (!is.null(authInfo$apiKey)) {\n    list(`Authorization` = paste(\"Key\", authInfo$apiKey))\n  } else if (!is.null(authInfo$accessToken)) {\n    list(`Authorization` = paste(\"Bearer\", authInfo$accessToken))\n  } else {\n    # The value doesn't actually matter here, but the header needs to be set.\n    list(`X-Auth-Token` = \"anonymous-access\")\n  }\n}\n\n# https://github.com/rstudio/connect/wiki/token-authentication#request-signing-rsconnect\nsignatureHeaders <- function(authInfo, method, path, file = NULL) {\n  # headers to return\n  headers <- list()\n\n  # remove query string from path if necessary\n  path <- strsplit(path, \"?\", fixed = TRUE)[[1]][[1]]\n\n  # generate date\n  date <- rfc2616Date()\n\n  if (!is.null(authInfo$secret)) {\n    # the content hash is a string of hex characters when using secret.\n    md5 <- fileMD5(file)\n\n    # build canonical request\n    canonicalRequest <- paste(method, path, date, md5, sep = \"\\n\")\n\n    # sign request using shared secret\n    decodedSecret <- openssl::base64_decode(authInfo$secret)\n    hmac <- openssl::sha256(canonicalRequest, key = decodedSecret)\n    signature <- paste(openssl::base64_encode(hmac), \"; version=1\", sep = \"\")\n  } else if (!is.null(authInfo$private_key)) {\n    # the raw content hash is base64 encoded hex values when using private key.\n    md5 <- openssl::base64_encode(fileMD5(file, raw = TRUE))\n\n    # build canonical request\n    canonicalRequest <- paste(method, path, date, md5, sep = \"\\n\")\n\n    # sign request using local private key\n    private_key <- openssl::read_key(\n      openssl::base64_decode(authInfo$private_key),\n      der = TRUE\n    )\n\n    signature <- signRequestPrivateKey(private_key, canonicalRequest)\n  } else {\n    stop(\"can't sign request: no shared secret or private key\")\n  }\n\n  # return headers\n  headers$Date <- date\n  headers$`X-Auth-Token` <- authInfo$token\n  headers$`X-Auth-Signature` <- signature\n  headers$`X-Content-Checksum` <- md5\n  headers\n}\n\nsignRequestPrivateKey <- function(private_key, canonicalRequest) {\n  # convert key into PKI format for signing, note this only accepts RSA, but\n  # that's what rsconnect generates already\n  pem <- openssl::write_pem(private_key)\n  pem_lines <- readLines(textConnection(pem))\n  pki_key <- PKI::PKI.load.key(pem_lines, format = \"PEM\")\n\n  # use sha1 digest and then sign. digest and PKI avoid using system openssl which\n  # can be problematic in strict FIPS environments\n  digested <- digest::digest(\n    charToRaw(canonicalRequest),\n    \"sha1\",\n    serialize = FALSE,\n    raw = TRUE\n  )\n  rawsig <- PKI::PKI.sign(key = pki_key, digest = digested)\n  openssl::base64_encode(rawsig)\n}\n\nrfc2616Date <- function(time = Sys.time()) {\n  # set locale to POSIX/C to ensure ASCII date\n  old <- Sys.getlocale(\"LC_TIME\")\n  Sys.setlocale(\"LC_TIME\", \"C\")\n  defer(Sys.setlocale(\"LC_TIME\", old))\n\n  strftime(time, \"%a, %d %b %Y %H:%M:%S GMT\", tz = \"GMT\")\n}\n\n# Helpers -----------------------------------------------------------------\n\nuserAgent <- function() {\n  paste(\"RSConnect\", packageVersion(\"rsconnect\"), sep = \"/\")\n}\n\nparseHttpHeader <- function(header) {\n  split <- strsplit(header, \": \")[[1]]\n  if (length(split) == 2) {\n    return(list(name = split[1], value = split[2]))\n  } else {\n    return(NULL)\n  }\n}\n\nparseHttpStatusCode <- function(statusLine) {\n  # extract status code; needs to deal with HTTP/1.0, HTTP/1.1, and HTTP/2\n  statusCode <- regexExtract(\"HTTP/[0-9]+\\\\.?[0-9]* ([0-9]+).*\", statusLine)\n  if (is.null(statusCode)) {\n    return(-1)\n  } else {\n    return(as.integer(statusCode))\n  }\n}\n\n# @param request A list containing protocol, host, port, method, and path fields\n# @param conn The connection to read the response from.\nreadHttpResponse <- function(request, conn) {\n  # read status code\n  resp <- readLines(conn, 1)\n  statusCode <- parseHttpStatusCode(resp[1])\n\n  # read response headers\n  contentLength <- NULL\n  contentType <- NULL\n  location <- NULL\n  setCookies <- NULL\n  repeat {\n    resp <- readLines(conn, 1)\n    if (nzchar(resp) == 0) {\n      break()\n    }\n\n    header <- parseHttpHeader(resp)\n    if (!is.null(header)) {\n      name <- tolower(header$name)\n      if (name == \"content-type\") {\n        contentType <- header$value\n      }\n      if (name == \"content-length\") {\n        contentLength <- as.integer(header$value)\n      }\n      if (name == \"location\") {\n        location <- header$value\n      }\n      if (name == \"set-cookie\") {\n        setCookies <- c(setCookies, header$value)\n      }\n    }\n  }\n\n  # Store the cookies that were found in the request\n  storeCookies(request, setCookies)\n\n  # read the response content\n  if (is.null(contentLength)) {\n    # content length is unknown, so stream remaining text\n    content <- paste(readLines(con = conn), collapse = \"\\n\")\n  } else {\n    # we know the content length, so read exactly that many bytes\n    content <- rawToChar(readBin(\n      con = conn,\n      what = \"raw\",\n      n = contentLength\n    ))\n  }\n\n  # emit JSON trace if requested\n  if (httpTraceJson() && identical(contentType, \"application/json\")) {\n    cat(paste0(\">> \", content, \"\\n\"))\n  }\n\n  # return list\n  list(\n    req = request,\n    status = statusCode,\n    location = location,\n    contentType = contentType,\n    content = content\n  )\n}\n"
  },
  {
    "path": "R/ide.R",
    "content": "# These functions are intended to be called primarily by the RStudio IDE.\n\n# This function is poorly named because as well as validating the server\n# url it will also register the server if needed.\nvalidateServerUrl <- function(url, certificate = NULL) {\n  snowflakeConnectionName <- NULL\n  if (isSPCSUrl(url)) {\n    snowflakeConnectionName <- getDefaultSnowflakeConnectionName(url)\n  }\n\n  res <- validateConnectUrl(url, certificate, snowflakeConnectionName)\n\n  if (res$valid) {\n    name <- findAndRegisterLocalServer(res$url)\n    c(list(valid = TRUE, url = res$url, name = name), res$response)\n  } else {\n    res\n  }\n}\n\n# given a server URL, returns that server's short name. if the server is not\n# currently registered, the server is registered and the short name of the newly\n# registered server is returned.\nfindAndRegisterLocalServer <- function(url) {\n  # helper to find a server given its URL\n  findServerByUrl <- function(url) {\n    allServers <- rsconnect::servers(local = TRUE)\n    match <- allServers[allServers$url == url, , drop = FALSE]\n    if (nrow(match) == 0) {\n      NULL\n    } else {\n      as.character(match[1, \"name\"])\n    }\n  }\n\n  # if there are no local servers with the given URL, add one and return its\n  # name\n  name <- findServerByUrl(url)\n  if (is.null(name)) {\n    url <- ensureConnectServerUrl(url)\n    addServer(\n      url = url,\n      name = NULL,\n      certificate = NULL,\n      quiet = TRUE,\n      validate = FALSE\n    )\n    findServerByUrl(url)\n  } else {\n    name\n  }\n}\n\n# Called directly by RStudio.\n# See https://github.com/rstudio/rstudio/blob/main/src/cpp/session/modules/SessionRSConnect.R\nregisterUserToken <- function(\n  serverName,\n  accountName,\n  userId,\n  token,\n  privateKey,\n  accessToken = NULL,\n  refreshToken = NULL\n) {\n  # If privateKey is empty, we're using identity federation and don't want to\n  # persist credentials.\n  if (!nzchar(privateKey)) {\n    return(registerAccount(\n      serverName = serverName,\n      accountName = accountName,\n      accountId = userId\n    ))\n  }\n\n  registerAccount(\n    serverName = serverName,\n    accountName = accountName,\n    accountId = userId,\n    token = token,\n    private_key = privateKey,\n    accessToken = accessToken,\n    refreshToken = refreshToken\n  )\n}\n\n# generate the markers\nshowRstudioSourceMarkers <- function(basePath, lint) {\n  markers <- list()\n  applied <- lapply(lint, function(file) {\n    lapply(file, function(linter) {\n      lapply(linter$indices, function(index) {\n        marker <- list()\n        marker$type <- \"warning\"\n        marker$file <- file.path(basePath, linter$file)\n        marker$line <- index\n        marker$column <- 1\n        marker$message <- linter$suggestion\n        markers <<- c(markers, list(marker))\n        marker\n      })\n    })\n  })\n\n  rstudioapi::callFun(\n    \"sourceMarkers\",\n    name = \"Publish Content Issues\",\n    markers = markers,\n    basePath = basePath,\n    autoSelect = \"first\"\n  )\n}\n\n# getAppById() -----------------------------------------------------------------\n\n# https://github.com/rstudio/rstudio/blob/ee56d49b0fca5f3d7c3f5214a4010355d1bb0212/src/gwt/src/org/rstudio/studio/client/rsconnect/ui/RSConnectDeploy.java#L699\n\ngetAppById <- function(id, account, server, hostUrl) {\n  check_string(account)\n  check_string(server)\n  check_string(hostUrl)\n\n  if (!hasAccount(account, server)) {\n    # If can't find record for account + server, try hostUrl\n    servers <- servers()\n    matches <- servers$url == hostUrl\n    if (any(matches)) {\n      server <- servers$name[which(matches)[[1]]]\n      if (!hasAccount(account, server)) {\n        cli::cli_abort(\n          \"Can't find account {.str {account}} on server {.str {server}}.\"\n        )\n      }\n    } else {\n      cli::cli_abort(\"Can't find server with url {.str {hostUrl}}.\")\n    }\n  }\n\n  getApplication(account, server, id)\n}\n\n\n# -------------------------------------------------------------------------\n\n# passthrough function for compatibility with old IDE versions\ngetUserFromRawToken <- function(\n  serverUrl,\n  token,\n  privateKey,\n  serverCertificate = NULL\n) {\n  # Look up server name from url\n  servers <- servers()\n  matches <- servers$url == serverUrl\n  server <- servers$name[which(matches)[[1]]]\n\n  waitForAuthedUser(server, token = token, private_key = privateKey)\n}\n"
  },
  {
    "path": "R/import-standalone-obj-type.R",
    "content": "# Standalone file: do not edit by hand\n# Source: <https://github.com/r-lib/rlang/blob/main/R/standalone-obj-type.R>\n# ----------------------------------------------------------------------\n#\n# ---\n# repo: r-lib/rlang\n# file: standalone-obj-type.R\n# last-updated: 2022-10-04\n# license: https://unlicense.org\n# ---\n#\n# ## Changelog\n#\n# 2022-10-04:\n# - `obj_type_friendly(value = TRUE)` now shows numeric scalars\n#   literally.\n# - `stop_friendly_type()` now takes `show_value`, passed to\n#   `obj_type_friendly()` as the `value` argument.\n#\n# 2022-10-03:\n# - Added `allow_na` and `allow_null` arguments.\n# - `NULL` is now backticked.\n# - Better friendly type for infinities and `NaN`.\n#\n# 2022-09-16:\n# - Unprefixed usage of rlang functions with `rlang::` to\n#   avoid onLoad issues when called from rlang (#1482).\n#\n# 2022-08-11:\n# - Prefixed usage of rlang functions with `rlang::`.\n#\n# 2022-06-22:\n# - `friendly_type_of()` is now `obj_type_friendly()`.\n# - Added `obj_type_oo()`.\n#\n# 2021-12-20:\n# - Added support for scalar values and empty vectors.\n# - Added `stop_input_type()`\n#\n# 2021-06-30:\n# - Added support for missing arguments.\n#\n# 2021-04-19:\n# - Added support for matrices and arrays (#141).\n# - Added documentation.\n# - Added changelog.\n#\n# nocov start\n\n#' Return English-friendly type\n#' @param x Any R object.\n#' @param value Whether to describe the value of `x`. Special values\n#'   like `NA` or `\"\"` are always described.\n#' @param length Whether to mention the length of vectors and lists.\n#' @return A string describing the type. Starts with an indefinite\n#'   article, e.g. \"an integer vector\".\n#' @noRd\nobj_type_friendly <- function(x, value = TRUE) {\n  if (is_missing(x)) {\n    return(\"absent\")\n  }\n\n  if (is.object(x)) {\n    if (inherits(x, \"quosure\")) {\n      type <- \"quosure\"\n    } else {\n      type <- paste(class(x), collapse = \"/\")\n    }\n    return(sprintf(\"a <%s> object\", type))\n  }\n\n  if (!is_vector(x)) {\n    return(.rlang_as_friendly_type(typeof(x)))\n  }\n\n  n_dim <- length(dim(x))\n\n  if (!n_dim) {\n    if (!is_list(x) && length(x) == 1) {\n      if (is_na(x)) {\n        return(switch(\n          typeof(x),\n          logical = \"`NA`\",\n          integer = \"an integer `NA`\",\n          double =\n            if (is.nan(x)) {\n              \"`NaN`\"\n            } else {\n              \"a numeric `NA`\"\n            },\n          complex = \"a complex `NA`\",\n          character = \"a character `NA`\",\n          .rlang_stop_unexpected_typeof(x)\n        ))\n      }\n\n      show_infinites <- function(x) {\n        if (x > 0) {\n          \"`Inf`\"\n        } else {\n          \"`-Inf`\"\n        }\n      }\n      str_encode <- function(x, width = 30, ...) {\n        if (nchar(x) > width) {\n          x <- substr(x, 1, width - 3)\n          x <- paste0(x, \"...\")\n        }\n        encodeString(x, ...)\n      }\n\n      if (value) {\n        if (is.numeric(x) && is.infinite(x)) {\n          return(show_infinites(x))\n        }\n\n        if (is.numeric(x) || is.complex(x)) {\n          number <- as.character(round(x, 2))\n          what <- if (is.complex(x)) \"the complex number\" else \"the number\"\n          return(paste(what, number))\n        }\n\n        return(switch(\n          typeof(x),\n          logical = if (x) \"`TRUE`\" else \"`FALSE`\",\n          character = {\n            what <- if (nzchar(x)) \"the string\" else \"the empty string\"\n            paste(what, str_encode(x, quote = \"\\\"\"))\n          },\n          raw = paste(\"the raw value\", as.character(x)),\n          .rlang_stop_unexpected_typeof(x)\n        ))\n      }\n\n      return(switch(\n        typeof(x),\n        logical = \"a logical value\",\n        integer = \"an integer\",\n        double = if (is.infinite(x)) show_infinites(x) else \"a number\",\n        complex = \"a complex number\",\n        character = if (nzchar(x)) \"a string\" else \"\\\"\\\"\",\n        raw = \"a raw value\",\n        .rlang_stop_unexpected_typeof(x)\n      ))\n    }\n\n    if (length(x) == 0) {\n      return(switch(\n        typeof(x),\n        logical = \"an empty logical vector\",\n        integer = \"an empty integer vector\",\n        double = \"an empty numeric vector\",\n        complex = \"an empty complex vector\",\n        character = \"an empty character vector\",\n        raw = \"an empty raw vector\",\n        list = \"an empty list\",\n        .rlang_stop_unexpected_typeof(x)\n      ))\n    }\n  }\n\n  vec_type_friendly(x)\n}\n\nvec_type_friendly <- function(x, length = FALSE) {\n  if (!is_vector(x)) {\n    abort(\"`x` must be a vector.\")\n  }\n  type <- typeof(x)\n  n_dim <- length(dim(x))\n\n  add_length <- function(type) {\n    if (length && !n_dim) {\n      paste0(type, sprintf(\" of length %s\", length(x)))\n    } else {\n      type\n    }\n  }\n\n  if (type == \"list\") {\n    if (n_dim < 2) {\n      return(add_length(\"a list\"))\n    } else if (is.data.frame(x)) {\n      return(\"a data frame\")\n    } else if (n_dim == 2) {\n      return(\"a list matrix\")\n    } else {\n      return(\"a list array\")\n    }\n  }\n\n  type <- switch(\n    type,\n    logical = \"a logical %s\",\n    integer = \"an integer %s\",\n    numeric = ,\n    double = \"a double %s\",\n    complex = \"a complex %s\",\n    character = \"a character %s\",\n    raw = \"a raw %s\",\n    type = paste0(\"a \", type, \" %s\")\n  )\n\n  if (n_dim < 2) {\n    kind <- \"vector\"\n  } else if (n_dim == 2) {\n    kind <- \"matrix\"\n  } else {\n    kind <- \"array\"\n  }\n  out <- sprintf(type, kind)\n\n  if (n_dim >= 2) {\n    out\n  } else {\n    add_length(out)\n  }\n}\n\n.rlang_as_friendly_type <- function(type) {\n  switch(\n    type,\n\n    list = \"a list\",\n\n    NULL = \"`NULL`\",\n    environment = \"an environment\",\n    externalptr = \"a pointer\",\n    weakref = \"a weak reference\",\n    S4 = \"an S4 object\",\n\n    name = ,\n    symbol = \"a symbol\",\n    language = \"a call\",\n    pairlist = \"a pairlist node\",\n    expression = \"an expression vector\",\n\n    char = \"an internal string\",\n    promise = \"an internal promise\",\n    ... = \"an internal dots object\",\n    any = \"an internal `any` object\",\n    bytecode = \"an internal bytecode object\",\n\n    primitive = ,\n    builtin = ,\n    special = \"a primitive function\",\n    closure = \"a function\",\n\n    type\n  )\n}\n\n.rlang_stop_unexpected_typeof <- function(x, call = caller_env()) {\n  abort(\n    sprintf(\"Unexpected type <%s>.\", typeof(x)),\n    call = call\n  )\n}\n\n#' Return OO type\n#' @param x Any R object.\n#' @return One of `\"bare\"` (for non-OO objects), `\"S3\"`, `\"S4\"`,\n#'   `\"R6\"`, or `\"R7\"`.\n#' @noRd\nobj_type_oo <- function(x) {\n  if (!is.object(x)) {\n    return(\"bare\")\n  }\n\n  class <- inherits(x, c(\"R6\", \"R7_object\"), which = TRUE)\n\n  if (class[[1]]) {\n    \"R6\"\n  } else if (class[[2]]) {\n    \"R7\"\n  } else if (isS4(x)) {\n    \"S4\"\n  } else {\n    \"S3\"\n  }\n}\n\n#' @param x The object type which does not conform to `what`. Its\n#'   `obj_type_friendly()` is taken and mentioned in the error message.\n#' @param what The friendly expected type as a string. Can be a\n#'   character vector of expected types, in which case the error\n#'   message mentions all of them in an \"or\" enumeration.\n#' @param show_value Passed to `value` argument of `obj_type_friendly()`.\n#' @param ... Arguments passed to [abort()].\n#' @inheritParams args_error_context\n#' @noRd\nstop_input_type <- function(x,\n                            what,\n                            ...,\n                            allow_na = FALSE,\n                            allow_null = FALSE,\n                            show_value = TRUE,\n                            arg = caller_arg(x),\n                            call = caller_env()) {\n  # From standalone-cli.R\n  cli <- env_get_list(\n    nms = c(\"format_arg\", \"format_code\"),\n    last = topenv(),\n    default = function(x) sprintf(\"`%s`\", x),\n    inherit = TRUE\n  )\n\n  if (allow_na) {\n    what <- c(what, cli$format_code(\"NA\"))\n  }\n  if (allow_null) {\n    what <- c(what, cli$format_code(\"NULL\"))\n  }\n  if (length(what)) {\n    what <- oxford_comma(what)\n  }\n\n  message <- sprintf(\n    \"%s must be %s, not %s.\",\n    cli$format_arg(arg),\n    what,\n    obj_type_friendly(x, value = show_value)\n  )\n\n  abort(message, ..., call = call, arg = arg)\n}\n\noxford_comma <- function(chr, sep = \", \", final = \"or\") {\n  n <- length(chr)\n\n  if (n < 2) {\n    return(chr)\n  }\n\n  head <- chr[seq_len(n - 1)]\n  last <- chr[n]\n\n  head <- paste(head, collapse = sep)\n\n  # Write a or b. But a, b, or c.\n  if (n > 2) {\n    paste0(head, sep, final, \" \", last)\n  } else {\n    paste0(head, \" \", final, \" \", last)\n  }\n}\n\n# nocov end\n"
  },
  {
    "path": "R/import-standalone-types-check.R",
    "content": "# Standalone file: do not edit by hand\n# Source: <https://github.com/r-lib/rlang/blob/main/R/standalone-types-check.R>\n# ----------------------------------------------------------------------\n#\n# ---\n# repo: r-lib/rlang\n# file: standalone-types-check.R\n# last-updated: 2022-10-07\n# license: https://unlicense.org\n# dependencies: standalone-obj-type.R\n# ---\n#\n# ## Changelog\n#\n# 2022-10-07:\n# - `check_number_whole()` and `_decimal()` no longer treat\n#   non-numeric types such as factors or dates as numbers.  Numeric\n#   types are detected with `is.numeric()`.\n#\n# 2022-10-04:\n# - Added `check_name()` that forbids the empty string.\n#   `check_string()` allows the empty string by default.\n#\n# 2022-09-28:\n# - Removed `what` arguments.\n# - Added `allow_na` and `allow_null` arguments.\n# - Added `allow_decimal` and `allow_infinite` arguments.\n# - Improved errors with absent arguments.\n#\n#\n# 2022-09-16:\n# - Unprefixed usage of rlang functions with `rlang::` to\n#   avoid onLoad issues when called from rlang (#1482).\n#\n# 2022-08-11:\n# - Added changelog.\n#\n# nocov start\n\n# Scalars -----------------------------------------------------------------\n\ncheck_bool <- function(x,\n                       ...,\n                       allow_na = FALSE,\n                       allow_null = FALSE,\n                       arg = caller_arg(x),\n                       call = caller_env()) {\n  if (!missing(x)) {\n    if (is_bool(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_na && identical(x, NA)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    c(\"`TRUE`\", \"`FALSE`\"),\n    ...,\n    allow_na = allow_na,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_string <- function(x,\n                         ...,\n                         allow_empty = TRUE,\n                         allow_na = FALSE,\n                         allow_null = FALSE,\n                         arg = caller_arg(x),\n                         call = caller_env()) {\n  if (!missing(x)) {\n    is_string <- .rlang_check_is_string(\n      x,\n      allow_empty = allow_empty,\n      allow_na = allow_na,\n      allow_null = allow_null\n    )\n    if (is_string) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"a single string\",\n    ...,\n    allow_na = allow_na,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\n.rlang_check_is_string <- function(x,\n                                   allow_empty,\n                                   allow_na,\n                                   allow_null) {\n  if (is_string(x)) {\n    if (allow_empty || !is_string(x, \"\")) {\n      return(TRUE)\n    }\n  }\n\n  if (allow_null && is_null(x)) {\n    return(TRUE)\n  }\n\n  if (allow_na && (identical(x, NA) || identical(x, na_chr))) {\n    return(TRUE)\n  }\n\n  FALSE\n}\n\ncheck_name <- function(x,\n                       ...,\n                       allow_null = FALSE,\n                       arg = caller_arg(x),\n                       call = caller_env()) {\n  if (!missing(x)) {\n    is_string <- .rlang_check_is_string(\n      x,\n      allow_empty = FALSE,\n      allow_na = FALSE,\n      allow_null = allow_null\n    )\n    if (is_string) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"a valid name\",\n    ...,\n    allow_na = FALSE,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_number_decimal <- function(x,\n                                 ...,\n                                 min = -Inf,\n                                 max = Inf,\n                                 allow_infinite = TRUE,\n                                 allow_na = FALSE,\n                                 allow_null = FALSE,\n                                 arg = caller_arg(x),\n                                 call = caller_env()) {\n  .rlang_types_check_number(\n    x,\n    ...,\n    min = min,\n    max = max,\n    allow_decimal = TRUE,\n    allow_infinite = allow_infinite,\n    allow_na = allow_na,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_number_whole <- function(x,\n                               ...,\n                               min = -Inf,\n                               max = Inf,\n                               allow_na = FALSE,\n                               allow_null = FALSE,\n                               arg = caller_arg(x),\n                               call = caller_env()) {\n  .rlang_types_check_number(\n    x,\n    ...,\n    min = min,\n    max = max,\n    allow_decimal = FALSE,\n    allow_infinite = FALSE,\n    allow_na = allow_na,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\n.rlang_types_check_number <- function(x,\n                                      ...,\n                                      min = -Inf,\n                                      max = Inf,\n                                      allow_decimal = FALSE,\n                                      allow_infinite = FALSE,\n                                      allow_na = FALSE,\n                                      allow_null = FALSE,\n                                      arg = caller_arg(x),\n                                      call = caller_env()) {\n  if (allow_decimal) {\n    what <- \"a number\"\n  } else {\n    what <- \"a whole number\"\n  }\n\n  .stop <- function(x, what, ...) stop_input_type(\n    x,\n    what,\n    ...,\n    allow_na = allow_na,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n\n  if (!missing(x)) {\n    is_number <- is_number(\n      x,\n      allow_decimal = allow_decimal,\n      allow_infinite = allow_infinite\n    )\n\n    if (is_number) {\n      if (min > -Inf && max < Inf) {\n        what <- sprintf(\"a number between %s and %s\", min, max)\n      } else {\n        what <- NULL\n      }\n      if (x < min) {\n        what <- what %||% sprintf(\"a number larger than %s\", min)\n        .stop(x, what, ...)\n      }\n      if (x > max) {\n        what <- what %||% sprintf(\"a number smaller than %s\", max)\n        .stop(x, what, ...)\n      }\n      return(invisible(NULL))\n    }\n\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_na && (identical(x, NA) ||\n                     identical(x, na_dbl) ||\n                     identical(x, na_int))) {\n      return(invisible(NULL))\n    }\n  }\n\n  .stop(x, what, ...)\n}\n\nis_number <- function(x,\n                      allow_decimal = FALSE,\n                      allow_infinite = FALSE) {\n  if (!typeof(x) %in% c(\"integer\", \"double\")) {\n    return(FALSE)\n  }\n  if (!is.numeric(x)) {\n    return(FALSE)\n  }\n  if (length(x) != 1) {\n    return(FALSE)\n  }\n  if (is.na(x)) {\n    return(FALSE)\n  }\n  if (!allow_decimal && !is_integerish(x)) {\n    return(FALSE)\n  }\n  if (!allow_infinite && is.infinite(x)) {\n    return(FALSE)\n  }\n  TRUE\n}\n\ncheck_symbol <- function(x,\n                         ...,\n                         allow_null = FALSE,\n                         arg = caller_arg(x),\n                         call = caller_env()) {\n  if (!missing(x)) {\n    if (is_symbol(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"a symbol\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_arg <- function(x,\n                      ...,\n                      allow_null = FALSE,\n                      arg = caller_arg(x),\n                      call = caller_env()) {\n  if (!missing(x)) {\n    if (is_symbol(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"an argument name\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_call <- function(x,\n                       ...,\n                       allow_null = FALSE,\n                       arg = caller_arg(x),\n                       call = caller_env()) {\n  if (!missing(x)) {\n    if (is_call(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"a defused call\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_environment <- function(x,\n                              ...,\n                              allow_null = FALSE,\n                              arg = caller_arg(x),\n                              call = caller_env()) {\n  if (!missing(x)) {\n    if (is_environment(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"an environment\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_function <- function(x,\n                           ...,\n                           allow_null = FALSE,\n                           arg = caller_arg(x),\n                           call = caller_env()) {\n  if (!missing(x)) {\n    if (is_function(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"a function\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_closure <- function(x,\n                          ...,\n                          allow_null = FALSE,\n                          arg = caller_arg(x),\n                          call = caller_env()) {\n  if (!missing(x)) {\n    if (is_closure(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"an R function\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\ncheck_formula <- function(x,\n                          ...,\n                          allow_null = FALSE,\n                          arg = caller_arg(x),\n                          call = caller_env()) {\n  if (!missing(x)) {\n    if (is_formula(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"a formula\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\n\n# Vectors -----------------------------------------------------------------\n\ncheck_character <- function(x,\n                            ...,\n                            allow_null = FALSE,\n                            arg = caller_arg(x),\n                            call = caller_env()) {\n  if (!missing(x)) {\n    if (is_character(x)) {\n      return(invisible(NULL))\n    }\n    if (allow_null && is_null(x)) {\n      return(invisible(NULL))\n    }\n  }\n\n  stop_input_type(\n    x,\n    \"a character vector\",\n    ...,\n    allow_null = allow_null,\n    arg = arg,\n    call = call\n  )\n}\n\n# nocov end\n"
  },
  {
    "path": "R/imports.R",
    "content": "#' @importFrom stats na.omit setNames\n#' @importFrom utils available.packages installed.packages contrib.url getFromNamespace glob2rx head packageVersion packageDescription read.csv\nNULL\n"
  },
  {
    "path": "R/lint-framework.R",
    "content": ".__LINTERS__. <- new.env(parent = emptyenv())\n\n##' Add a Linter\n##'\n##' @description\n##' Add a linter, to be used in subsequent calls to [lint()].\n##'\n##' Supported servers: All servers\n##'\n##' @param name The name of the linter, as a string.\n##' @param linter A [linter()].\n##' @export\n##' @example examples/example-linter.R\naddLinter <- function(name, linter) {\n  assign(name, linter, envir = .__LINTERS__.)\n}\n\n\n##' Create a Linter\n##'\n##' @description\n##' Generate a linter, which can identify errors or problematic regions in a\n##' project.\n##'\n##' Supported servers: All servers\n##'\n##' @param apply Function that, given the content of a file, returns the indices\n##'   at which problems were found.\n##' @param takes Function that, given a set of paths, returns the subset of\n##'   paths that this linter uses.\n##' @param message Function that, given content and lines, returns an\n##'   informative message for the user. Typically generated with\n##'   [makeLinterMessage()].\n##' @param suggestion String giving a prescribed fix for the linted problem.\n##' @export\n##' @example examples/example-linter.R\nlinter <- function(apply, takes, message, suggestion) {\n  result <- list(\n    apply = apply,\n    takes = takes,\n    message = message,\n    suggestion = suggestion\n  )\n  class(result) <- \"linter\"\n  result\n}\n\ngetLinterApplicableFiles <- function(linter, files) {\n  result <- linter$takes(files)\n  if (is.numeric(result) || is.logical(result)) {\n    files[result]\n  } else {\n    result\n  }\n}\n\napplyLinter <- function(linter, ...) {\n  result <- linter$apply(...)\n  if (is.logical(result)) {\n    output <- which(result)\n  } else {\n    output <- as.numeric(result)\n  }\n  attributes(output) <- attributes(result)\n  output\n}\n"
  },
  {
    "path": "R/lint.R",
    "content": "##' Lint a Project\n##'\n##' @description\n##' Takes the set of active linters (see [addLinter()]), and applies\n##' them to all files within a project.\n##'\n##' Supported servers: All servers\n##'\n##' @param project Path to a project directory.\n##' @param files Specific files to lint. Can be NULL, in which case all\n##'   the files in the directory will be linted.\n##' @param appPrimaryDoc The primary file in the project directory. Can be NULL,\n##'   in which case it's inferred (if possible) from the directory contents.\n##' @export\nlint <- function(project, files = NULL, appPrimaryDoc = NULL) {\n  check_directory(project)\n  files <- listDeploymentFiles(project, files)\n\n  # Perform actions within the project directory (so relative paths are easily used)\n  owd <- getwd()\n  defer(setwd(owd))\n  setwd(project)\n\n  linters <- mget(objects(.__LINTERS__.), envir = .__LINTERS__.)\n\n  # Identify all files that will be read in by one or more linters\n  projectFilesToLint <- Reduce(\n    union,\n    lapply(linters, function(linter) {\n      getLinterApplicableFiles(linter, files)\n    })\n  )\n\n  # Read in the files\n  encoding <- activeEncoding(project)\n  projectContent <- lapply(projectFilesToLint, function(file) {\n    # force native encoding (disable any potential internal conversion)\n    old <- options(encoding = \"native.enc\")\n    defer(options(old))\n\n    # read content with requested encoding\n    # TODO: may consider converting from native encoding to UTF-8 if appropriate\n    readLines(file, encoding = encoding, warn = FALSE)\n  })\n\n  names(projectContent) <- projectFilesToLint\n  lintResults <- vector(\"list\", length(linters))\n  names(lintResults) <- names(linters)\n\n  ## Apply each linter\n  for (i in seq_along(linters)) {\n    linter <- linters[[i]]\n    applicableFiles <- getLinterApplicableFiles(linter, projectFilesToLint)\n    lintIndices <- vector(\"list\", length(applicableFiles))\n    names(lintIndices) <- applicableFiles\n\n    ## Apply linter to each file\n    for (j in seq_along(applicableFiles)) {\n      file <- applicableFiles[[j]]\n      tryCatch(\n        expr = {\n          lintIndices[[j]] <- applyLinter(\n            linter,\n            projectContent[[file]],\n            project = project,\n            path = file,\n            files = files\n          )\n        },\n        error = function(e) {\n          message(\"Failed to lint file '\", file, \"'\")\n          message(\"The linter failed with message:\\n\")\n          message(e$message)\n          lintIndices[[j]] <- numeric(0)\n        }\n      )\n    }\n\n    ## Get the messages associated with each lint\n    lintMessages <- enumerate(lintIndices, function(x, i) {\n      if (length(x)) {\n        message <- linter$message\n        if (is.function(message)) {\n          linter$message(projectContent[[names(lintIndices)[i]]], x)\n        } else {\n          makeLinterMessage(message, projectContent[[names(lintIndices)[i]]], x)\n        }\n      } else {\n        character()\n      }\n    })\n\n    ## Assign the result\n    lintResults[[i]] <- list(\n      files = applicableFiles,\n      indices = lintIndices,\n      message = lintMessages,\n      suggestion = linter$suggestion\n    )\n  }\n\n  ## Get all of the linted files, and transform the results into a by-file format\n  lintedFiles <- Reduce(\n    union,\n    lapply(lintResults, function(x) {\n      names(x$indices)\n    })\n  )\n\n  lintFields <- c(\"indices\", \"message\")\n  fileResults <- lapply(lintedFiles, function(file) {\n    result <- lapply(lintResults, function(result) {\n      subResult <- lapply(lintFields, function(field) {\n        result[[field]][[file]]\n      })\n      names(subResult) <- lintFields\n      subResult$suggestion <- result$suggestion\n      subResult$file <- file\n      class(subResult) <- \"lint\"\n      subResult\n    })\n    class(result) <- \"lintList\"\n    result\n  })\n  names(fileResults) <- lintedFiles\n  class(fileResults) <- \"linterResults\"\n  fileResults\n}\n\nlintHeader <- function(file) {\n  dashSep <- paste(rep(\"-\", nchar(file)), collapse = \"\")\n  header <- paste0(\n    dashSep,\n    \"\\n\",\n    file,\n    \"\\n\",\n    dashSep,\n    \"\\n\"\n  )\n  cat(paste(header, collapse = \"\\n\"))\n}\n\n#' @export\nprint.linterResults <- function(x, ...) {\n  if (!hasLint(x)) {\n    cat(\"No problems found\\n\")\n    return(invisible(x))\n  }\n\n  lapply(x, printLintList, ...)\n\n  cat(paste(collectSuggestions(x), collapse = \"\\n\"))\n\n  invisible(x)\n}\n\nprintLintList <- function(x, ...) {\n  has_message <- vapply(x, function(x) length(x$message), integer(1))\n  if (!any(has_message)) {\n    return()\n  }\n  lintHeader(x[[1]]$file)\n  lapply(x, function(x) {\n    cat(paste(x$message, collapse = \"\\n\"))\n  })\n}\n\ncollectSuggestions <- function(fileResults) {\n  suggestions <- lapply(fileResults, function(fileResult) {\n    unlist(lapply(fileResult, function(lintInfo) {\n      if (length(lintInfo$indices) > 0) {\n        paste(as.character(lintInfo$suggestion), collapse = \"\\n\")\n      }\n    }))\n  })\n  Reduce(union, suggestions)\n}\n\nshowLintResults <- function(appDir, lintResults) {\n  if (!hasLint(lintResults)) {\n    return(invisible())\n  }\n\n  if (interactive()) {\n    # if enabled, show warnings in friendly marker tab in addition to\n    # printing to console\n    if (\n      getOption(\"rsconnect.rstudio_source_markers\", TRUE) &&\n        rstudioapi::hasFun(\"sourceMarkers\")\n    ) {\n      showRstudioSourceMarkers(appDir, lintResults)\n    }\n  }\n\n  message(\n    \"The following potential problems were identified in the project files:\\n\"\n  )\n  print(lintResults)\n\n  if (interactive()) {\n    response <- readline(\"Do you want to proceed with deployment? [y/N]: \")\n    if (tolower(substring(response, 1, 1)) != \"y\") {\n      cli::cli_abort(\"Cancelling deployment.\")\n    }\n  } else {\n    # message(\n    #   \"\\nIf you believe these errors are spurious, run:\\n\\n\",\n    #   \"\\tdeployApp(lint = FALSE)\\n\\n\",\n    #   \"to disable linting.\"\n    # )\n    message(\n      \"If your code fails to run post-deployment, \",\n      \"please double-check these messages.\"\n    )\n\n    invisible()\n  }\n}\n\n#' Construct a Linter Message\n#'\n#' @description\n#' Pretty-prints a linter message. Primarily used as a helper\n#' for constructing linter messages with [linter()].\n#'\n#' Supported servers: All servers\n#'\n#' @param header A header message describing the linter.\n#' @param content The content of the file that was linted.\n#' @param lines The line numbers from `content` that contain lint.\nmakeLinterMessage <- function(header, content, lines) {\n  lint <- attr(lines, \"lint\")\n\n  c(\n    paste0(header, \":\"),\n    paste(\n      lines,\n      \": \",\n      content[lines],\n      if (!is.null(lint)) paste(\"    \", lint, sep = \"\"),\n      sep = \"\"\n    ),\n    \"\\n\"\n  )\n}\n\nenumerate <- function(X, FUN, ...) {\n  FUN <- match.fun(FUN)\n  result <- vector(\"list\", length(X))\n  for (i in seq_along(X)) {\n    result[[i]] <- FUN(X[[i]], i, ...)\n  }\n  names(result) <- names(X)\n  result\n}\n\nhasLint <- function(x) {\n  any(unlist(lapply(x, function(x) {\n    lapply(x, function(x) {\n      length(x$indices) > 0\n    })\n  })))\n}\n\nactiveEncoding <- function(project = getwd()) {\n  defaultEncoding <- getOption(\"encoding\")\n  if (identical(defaultEncoding, \"native.enc\")) {\n    defaultEncoding <- \"unknown\"\n  }\n\n  # attempt to locate .Rproj file\n  files <- list.files(project, full.names = TRUE)\n  rprojFile <- grep(\"\\\\.Rproj$\", files, value = TRUE)\n  if (length(rprojFile) != 1) {\n    return(defaultEncoding)\n  }\n\n  # read the file\n  contents <- readLines(rprojFile, warn = FALSE, encoding = \"UTF-8\")\n  encodingLine <- grep(\"^Encoding:\", contents, value = TRUE)\n  if (length(encodingLine) != 1) {\n    return(defaultEncoding)\n  }\n\n  # remove \"Encoding:\" prefix\n  sub(\"^Encoding:\\\\s*\", \"\", encodingLine)\n}\n"
  },
  {
    "path": "R/linters.R",
    "content": ".__LINTERS__. <- new.env(parent = emptyenv())\n\n##' Add a Linter\n##'\n##' Add a linter, to be used in subsequent calls to [lint()].\n##'\n##' @param name The name of the linter, as a string.\n##' @param linter A [linter()].\n##' @export\n##' @example examples/example-linter.R\naddLinter <- function(name, linter) {\n  assign(name, linter, envir = .__LINTERS__.)\n}\n\n##' Create a Linter\n##'\n##' Generate a linter, which can identify errors or problematic regions in a\n##' project.\n##'\n##' @param apply Function that, given the content of a file, returns the indices\n##'   at which problems were found.\n##' @param takes Function that, given a set of paths, returns the subset of\n##'   paths that this linter uses.\n##' @param message Function that, given content and lines, returns an\n##'   informative message for the user. Typically generated with\n##'   [makeLinterMessage()].\n##' @param suggestion String giving a prescribed fix for the linted problem.\n##' @export\n##' @example examples/example-linter.R\nlinter <- function(apply, takes, message, suggestion) {\n  result <- list(\n    apply = apply,\n    takes = takes,\n    message = message,\n    suggestion = suggestion\n  )\n  class(result) <- \"linter\"\n  result\n}\n\ngetLinterApplicableFiles <- function(linter, files) {\n  result <- linter$takes(files)\n  if (is.numeric(result) || is.logical(result)) {\n    files[result]\n  } else {\n    result\n  }\n}\n\napplyLinter <- function(linter, ...) {\n  result <- linter$apply(...)\n  if (is.logical(result)) {\n    output <- which(result)\n  } else {\n    output <- as.numeric(result)\n  }\n  attributes(output) <- attributes(result)\n  output\n}\n\n\nisRCodeFile <- function(path) {\n  grepl(\"\\\\.[rR]$|\\\\.[rR]md$|\\\\.[rR]nw$\", path)\n}\n\n\naddLinter(\n  \"absolute.paths\",\n  linter(\n    apply = function(content, ...) {\n      content <- stripComments(content)\n      which(hasAbsolutePaths(content))\n    },\n\n    takes = isRCodeFile,\n\n    message = function(content, lines) {\n      makeLinterMessage(\n        \"The following lines contain absolute paths\",\n        content,\n        lines\n      )\n    },\n\n    suggestion = \"Paths should be to files within the project directory.\"\n  )\n)\n\naddLinter(\n  \"filepath.capitalization\",\n  linter(\n    apply = function(content, project, path, files) {\n      content <- stripComments(content)\n\n      # Extract references between bounding (unescaped) quotes.\n      extractQuoted <- function(regex) {\n        matches <- gregexpr(regex, content, perl = TRUE)\n        results <- vector(\"list\", length(content))\n        for (i in seq_along(matches)) {\n          x <- matches[[i]]\n          if (x[[1]] == -1L || length(x) %% 2 != 0) {\n            next\n          }\n          starts <- x[seq(1, length(x), by = 2)]\n          ends <- x[seq(2, length(x), by = 2)]\n          results[[i]] <- character(length(starts))\n          for (j in seq_along(starts)) {\n            results[[i]][[j]] <- substring(\n              content[i],\n              starts[j] + 1,\n              ends[j] - 1\n            )\n          }\n        }\n        results\n      }\n\n      # Extract targets from Markdown links.\n      extractLinked <- function() {\n        regex <- \"\\\\]\\\\(.*?\\\\)\"\n        matches <- gregexpr(regex, content, perl = TRUE)\n        results <- vector(\"list\", length(content))\n        for (i in seq_along(matches)) {\n          x <- matches[[i]]\n          if (x[[1]] == -1L) {\n            next\n          }\n          results[[i]] <- character(length(x))\n          attr <- attributes(x)\n          for (j in seq_along(x)) {\n            raw <- x[[j]]\n            raw.length <- attr$match.length[[j]]\n            # skip past \"](\" and discard \")\"\n            start <- as.integer(raw) + 2\n            end <- as.integer(raw) + raw.length - 2\n            results[[i]][[j]] <- substring(content[i], start, end)\n          }\n        }\n        results\n      }\n\n      # Inferred files within source documents; between matching quotes or in\n      # something that looks like a Markdown link.\n      inferredFiles <- list(\n        \"single.quotes\" = extractQuoted(\"(?!\\\\\\\\)\\'\"),\n        \"double.quotes\" = extractQuoted(\"(?!\\\\\\\\)\\\"\"),\n        \"markdown.links\" = extractLinked()\n      )\n\n      ## Replace '\\' with '/' in filepaths for consistency in comparisons\n      inferredFiles <- lapply(inferredFiles, function(x) {\n        lapply(x, function(xx) {\n          gsub(\"\\\\\\\\\", \"/\", xx, perl = TRUE)\n        })\n      })\n\n      # Compare in case sensitive, case insensitive fashion\n      projectFiles <- files\n      projectFilesLower <- tolower(files)\n\n      badLines <- lapply(inferredFiles, function(regex) {\n        lapply(regex, function(x) {\n          which(\n            # Only examine paths containing a slash to reduce false positives\n            grepl(\"/\", x, fixed = TRUE) &\n              (tolower(x) %in% projectFilesLower) &\n              (!(x %in% projectFiles))\n          )\n        })\n      })\n\n      indices <- Reduce(\n        union,\n        lapply(badLines, function(x) {\n          which(sapply(x, length) > 0)\n        })\n      )\n\n      if (!length(indices)) {\n        return(integer())\n      }\n\n      from <- lapply(inferredFiles, function(x) x[indices])\n      to <- lapply(from, function(x) {\n        lapply(x, function(xx) {\n          projectFiles[tolower(xx) == projectFilesLower]\n        })\n      })\n\n      messages <- lapply(seq_along(from), function(regex) {\n        lapply(seq_along(regex), function(i) {\n          if (length(from[[regex]][[i]])) {\n            paste(\n              collapse = \", \",\n              paste(\n                \"[\",\n                sQuote(from[[regex]][[i]]),\n                \" -> \",\n                sQuote(to[[regex]][[i]]),\n                \"]\",\n                sep = \"\"\n              )\n            )\n          } else {\n            \"\"\n          }\n        })\n      })\n\n      transposed <- transposeList(messages)\n      lint <- sapply(transposed, function(x) {\n        paste(x[x != \"\"], collapse = \", \")\n      })\n\n      indices <- as.numeric(indices)\n      attr(indices, \"lint\") <- lint\n      indices\n    },\n\n    takes = isRCodeFile,\n\n    message = function(content, lines) {\n      makeLinterMessage(\n        \"The following lines contain paths to files not matching in case sensitivity\",\n        content,\n        lines\n      )\n    },\n\n    suggestion = \"Filepaths are case-sensitive on deployment server.\"\n  )\n)\n\naddLinter(\n  \"browser\",\n  linter(\n    apply = function(content, ...) {\n      content <- stripComments(content)\n      which(hasBrowserCalls(content))\n    },\n\n    takes = isRCodeFile,\n\n    message = function(content, lines) {\n      makeLinterMessage(\n        \"The following lines contain the browser() debugging function\",\n        content,\n        lines\n      )\n    },\n\n    suggestion = \"The browser() debugging function should be removed.\"\n  )\n)\n\naddLinter(\n  \"browseURL\",\n  linter(\n    apply = function(content, ...) {\n      content <- stripComments(content)\n      which(hasBrowseURLCalls(content))\n    },\n\n    takes = isRCodeFile,\n\n    message = function(content, lines) {\n      makeLinterMessage(\n        \"The following lines contain calls to the browseURL function\",\n        content,\n        lines\n      )\n    },\n\n    suggestion = \"Remove browseURL calls; browseURL does not work in deployed applications.\"\n  )\n)\n\n\nstripComments <- function(content) {\n  gsub(\"#.*\", \"\", content, perl = TRUE)\n}\n\nhasBrowserCalls <- function(content) {\n  # look for calls to browser(); they will cause a debug halt, which is almost\n  # never wanted in a production application\n  grepl(\"\\\\bbrowser\\\\([^)]*\\\\)\", content, perl = TRUE)\n}\n\nhasBrowseURLCalls <- function(content) {\n  # look for calls to browseURL(); browsers can't be opened on the server in\n  # deployed applications\n  grepl(\"\\\\bbrowseURL\\\\([^)]*\\\\)\", content, perl = TRUE)\n}\n\nhasAbsolutePaths <- function(content) {\n  regex <- c(\n    \"\\\\[(.*?)\\\\]\\\\(\\\\s*[a-zA-Z]:/[^\\\"\\']\", ## windows-style markdown references [Some image](C:/...)\n    \"\\\\[(.*?)\\\\]\\\\(\\\\s*/[^/]\", ## unix-style markdown references [Some image](/Users/...)\n    NULL ## so we don't worry about commas above\n  )\n\n  regexResults <- as.logical(Reduce(\n    `+`,\n    lapply(regex, function(rex) {\n      grepl(rex, content, perl = TRUE)\n    })\n  ))\n\n  # Strip out all strings in the document, and check to see if any of them\n  # resolve to absolute paths on the system.\n  sQuoteRegex <- \"['](?:(?:\\\\\\\\.)|(?:[^'\\\\\\\\]))*?[']\"\n  dQuoteRegex <- '[\"](?:(?:\\\\\\\\.)|(?:[^\"\\\\\\\\]))*?[\"]'\n\n  extractedStrings <- lapply(c(sQuoteRegex, dQuoteRegex), function(regex) {\n    matches <- gregexpr(regex, content, perl = TRUE)\n    lapply(seq_along(matches), function(i) {\n      match <- matches[[i]]\n      if (c(match[[1]]) == -1L) {\n        return(character())\n      }\n      starts <- as.integer(match) + 1\n      ends <- starts + attr(match, \"match.length\") - 3\n      substring(content[[i]], starts, ends)\n    })\n  })\n\n  strings <- vector(\"list\", length(extractedStrings[[1]]))\n  for (i in seq_along(extractedStrings[[1]])) {\n    strings[[i]] <- unique(c(\n      extractedStrings[[1]][[i]],\n      extractedStrings[[2]][[i]]\n    ))\n    strings[[i]] <- strings[[i]][nchar(strings[[i]]) >= 5]\n  }\n\n  lineHasAbsolutePath <- unlist(lapply(strings, function(x) {\n    any(\n      grepl(\"^/|^[a-zA-Z]:/|^~\", x, perl = TRUE) &\n        file.exists(x) &\n        !dirExists(x) &\n        vapply(\n          gregexpr(\"[~/]\", x, perl = TRUE),\n          USE.NAMES = FALSE,\n          FUN.VALUE = numeric(1),\n          length\n        ) >=\n          3\n    )\n  }))\n\n  as.logical(lineHasAbsolutePath + regexResults)\n}\n\nnoMatch <- function(x) {\n  identical(attr(x, \"match.length\"), -1L)\n}\n\ntransposeList <- function(list) {\n  unname(as.list(\n    as.data.frame(\n      t(\n        as.matrix(\n          as.data.frame(list, stringsAsFactors = FALSE)\n        )\n      ),\n      stringsAsFactors = FALSE\n    )\n  ))\n}\n"
  },
  {
    "path": "R/locale.R",
    "content": "detectLocale <- function() {\n  if (!isWindows()) {\n    locales <- strsplit(Sys.getlocale(\"LC_CTYPE\"), \".\", fixed = TRUE)[[1]]\n    locales[[1]]\n  } else {\n    tryCatch(windowsLocale(), error = function(err) \"en_US\")\n  }\n}\n\n# Possible values\n# listed at https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid\nwindowsLocale <- function() {\n  key <- utils::readRegistry(\"Control Panel\\\\International\", hive = \"HCU\")\n  normalizeLocale(key$LocaleName)\n}\n\n# Remove script tag, if present\nnormalizeLocale <- function(x) {\n  pieces <- strsplit(x, \"-\")[[1]]\n  all_upper <- pieces[pieces == toupper(pieces)]\n\n  if (length(all_upper) == 0) {\n    pieces[[1]]\n  } else {\n    paste0(pieces[[1]], \"_\", all_upper[[1]])\n  }\n}\n"
  },
  {
    "path": "R/purgeApp.R",
    "content": "#' Purge an Application\n#'\n#' @description\n#' Purge a currently archived ShinyApps application.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appName Name of application to purge\n#' @param account Account name. If a single account is registered on the system\n#'   then this parameter can be omitted.\n#' @param server Server name. Required only if you use the same account name on\n#'   multiple servers (see [servers()])\n#' @param quiet Request that no status information be printed to the console\n#'   during the termination.\n#'\n#' @note This function only works for ShinyApps servers.\n#'\n#' @examples\n#' \\dontrun{\n#'\n#' # purge an application\n#' purgeApp(\"myapp\")\n#' }\n#' @seealso [applications()], [deployApp()], and\n#'   [restartApp()]\n#' @export\npurgeApp <- function(appName, account = NULL, server = NULL, quiet = FALSE) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # define purge task\n  taskDef <- list()\n  taskDef$beginStatus <- \"Purging application\"\n  taskDef$endStatus <- \"Application successfully purged\"\n  taskDef$action <- function(client, application) {\n    client$purgeApplication(application$id)\n  }\n\n  # perform it\n  applicationTask(taskDef, appName, accountDetails, quiet)\n}\n"
  },
  {
    "path": "R/restartApp.R",
    "content": "#' Restart an Application\n#'\n#' @description\n#' Restart an application currently running on a remote server.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appName Name of application to restart\n#' @param account Account name. If a single account is registered on the system\n#'   then this parameter can be omitted.\n#' @param server Server name. Required only if you use the same account name on\n#'   multiple servers (see [servers()])\n#' @param quiet Request that no status information be printed to the console\n#'   during the operation.\n#' @examples\n#' \\dontrun{\n#'\n#' # restart an application\n#' restartApp(\"myapp\")\n#' }\n#' @seealso [applications()], [deployApp()], and\n#'   [terminateApp()]\n#' @note This function works only for ShinyApps servers.\n#' @export\nrestartApp <- function(appName, account = NULL, server = NULL, quiet = FALSE) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # define deploy task\n  taskDef <- list()\n  taskDef$beginStatus <- \"Restarting application\"\n  taskDef$endStatus <- \"Application successfully restarted\"\n  taskDef$action <- function(client, application) {\n    client$deployApplication(application)\n  }\n\n  # perform it\n  applicationTask(taskDef, appName, accountDetails, quiet)\n}\n"
  },
  {
    "path": "R/rpubs.R",
    "content": "#' Upload a file to RPubs\n#'\n#' @description\n#' This function publishes a file to rpubs.com. If the upload succeeds a\n#' list that includes an `id` and `continueUrl` is returned. A browser\n#' should be opened to the `continueUrl` to complete publishing of the\n#' document. If an error occurs then a diagnostic message is returned in the\n#' `error` element of the list.\n#'\n#' Supported servers: RPubs servers\n#'\n#' @param title The title of the document.\n#' @param contentFile The path to the content file to upload.\n#' @param originalDoc The document that was rendered to produce the\n#'   `contentFile`. May be `NULL` if the document is not known.\n#' @param id If this upload is an update of an existing document then the id\n#'   parameter should specify the document id to update. Note that the id is\n#'   provided as an element of the list returned by successful calls to\n#'   `rpubsUpload`.\n#' @param properties A named list containing additional document properties\n#'   (RPubs doesn't currently expect any additional properties, this parameter\n#'   is reserved for future use).\n#'\n#' @return A named list. If the upload was successful then the list contains a\n#'   `id` element that can be used to subsequently update the document as\n#'   well as a `continueUrl` element that provides a URL that a browser\n#'   should be opened to in order to complete publishing of the document. If the\n#'   upload fails then the list contains an `error` element which contains\n#'   an explanation of the error that occurred.\n#'\n#' @examples\n#' \\dontrun{\n#' # upload a document\n#' result <- rpubsUpload(\"My document title\", \"Document.html\")\n#' if (!is.null(result$continueUrl))\n#'    browseURL(result$continueUrl)\n#' else\n#'    stop(result$error)\n#'\n#' # update the same document with a new title\n#' updateResult <- rpubsUpload(\"My updated title\", \"Document.html\",\n#'                             id = result$id)\n#' }\n#' @export\nrpubsUpload <- function(\n  title,\n  contentFile,\n  originalDoc,\n  id = NULL,\n  properties = list()\n) {\n  check_string(title, allow_empty = FALSE)\n  check_file(contentFile)\n  if (!is.list(properties)) {\n    stop(\"properties paramater must be a named list\")\n  }\n\n  pathFromId <- function(id) {\n    split <- strsplit(id, \"^https?://[^/]+\")[[1]]\n    if (length(split) == 2) {\n      return(split[2])\n    } else {\n      return(NULL)\n    }\n  }\n\n  buildPackage <- function(title, contentFile, properties = list()) {\n    # build package.json\n    properties$title <- title\n    packageJson <- toJSON(properties)\n\n    # create a tempdir to build the package in and copy the files to it\n    fileSep <- .Platform$file.sep\n    packageDir <- dirCreate(tempfile())\n    packageFile <- function(fileName) {\n      paste(packageDir, fileName, sep = fileSep)\n    }\n    writeLines(packageJson, packageFile(\"package.json\"))\n    file.copy(contentFile, packageFile(\"index.html\"))\n\n    # create the tarball\n    tarfile <- tempfile(\"package\", fileext = \".tar.gz\")\n    writeBundle(packageDir, tarfile)\n\n    # return the full path to the tarball\n    return(tarfile)\n  }\n\n  # build the package\n  packageFile <- buildPackage(title, contentFile, properties)\n\n  # determine whether this is a new doc or an update\n  isUpdate <- FALSE\n  method <- \"POST\"\n  path <- \"/api/v1/document\"\n  headers <- list()\n  headers$Connection <- \"close\"\n  if (!is.null(id)) {\n    isUpdate <- TRUE\n    path <- pathFromId(id)\n    method <- \"PUT\"\n  }\n\n  # send the request\n  result <- httpLibCurl(\n    protocol = \"https\",\n    host = \"api.rpubs.com\",\n    port = 443,\n    method = method,\n    path = path,\n    headers = headers,\n    contentType = \"application/x-compressed\",\n    contentFile = packageFile\n  )\n\n  # check for success\n  succeeded <- FALSE\n  if (isUpdate && (result$status == 200)) {\n    succeeded <- TRUE\n  } else if (result$status == 201) {\n    succeeded <- TRUE\n  }\n\n  # mark content as UTF-8\n  content <- result$content\n  Encoding(content) <- \"UTF-8\"\n\n  # return either id & continueUrl or error\n  if (succeeded) {\n    parsedContent <- jsonlite::fromJSON(content)\n    id <- ifelse(isUpdate, id, result$location)\n    url <- as.character(parsedContent[\"continueUrl\"])\n\n    # we use the source doc as the key for the deployment record as long as\n    # it's a recognized document path; otherwise we use the content file\n    recordSource <- ifelse(\n      !is.null(originalDoc) && isDocumentPath(originalDoc),\n      originalDoc,\n      contentFile\n    )\n\n    # use the title if given, and the filename name of the document if not\n    recordName <- ifelse(\n      is.null(title) || nchar(title) == 0,\n      basename(recordSource),\n      title\n    )\n\n    rpubsRec <- deploymentRecord(\n      name = recordName,\n      title = \"\",\n      username = \"\",\n      account = \"rpubs\",\n      server = \"rpubs.com\",\n      hostUrl = \"rpubs.com\",\n      appId = id,\n      bundleId = id,\n      url = url\n    )\n    rpubsRecFile <- deploymentConfigFile(\n      recordSource,\n      recordName,\n      \"rpubs\",\n      \"rpubs.com\"\n    )\n    write.dcf(rpubsRec, rpubsRecFile, width = 4096)\n\n    # record in global history\n    if (!is.null(originalDoc) && nzchar(originalDoc)) {\n      addToDeploymentHistory(originalDoc, rpubsRec)\n    }\n\n    # return the publish information\n    return(list(id = id, continueUrl = url))\n  } else {\n    return(list(error = content))\n  }\n}\n"
  },
  {
    "path": "R/rsconnect-package.R",
    "content": "#' @keywords internal\n\"_PACKAGE\"\n\n#' Using Packages with rsconnect\n#'\n#' See ?[appDependencies()]\n#' @keywords internal\n#' @name rsconnectPackages\nNULL\n\n#' HTTP Proxy Configuration\n#'\n#' Please see `vignette(\"custom-http\")`.\n#'\n#' @keywords internal\n#' @name rsconnectProxies\nNULL\n\n## usethis namespace: start\n#' @import rlang\n#' @importFrom lifecycle deprecated\n## usethis namespace: end\nNULL\n"
  },
  {
    "path": "R/secret.R",
    "content": "secret <- function(x) {\n  if (is.null(x)) {\n    return(NULL)\n  }\n\n  stopifnot(is.character(x) || all(is.na(x)))\n  structure(x, class = \"rsconnect_secret\")\n}\n\n#' @export\nformat.rsconnect_secret <- function(x, ...) {\n  paste0(substr(x, 1, 6), \"... (redacted)\")\n}\n\n#' @export\nprint.rsconnect_secret <- function(x, ...) {\n  print(format(x))\n  invisible(x)\n}\n\n#' @export\nstr.rsconnect_secret <- function(object, ...) {\n  cat(\" \", format(object), \"\\n\", sep = \"\")\n}\n\n#' @export\nas.data.frame.rsconnect_secret <- function(x, ...) {\n  structure(\n    list(x),\n    row.names = .set_row_names(length(x)),\n    class = \"data.frame\"\n  )\n}\n"
  },
  {
    "path": "R/servers.R",
    "content": "#' Server metadata\n#'\n#' @description\n#' `servers()` lists all known servers; `serverInfo()` gets metadata about\n#' a specific server. Cloud server `shinyapps.io` is always automatically\n#' registered and available.\n#'\n#' Supported servers: All servers\n#'\n#' @param name Server name. If omitted, you'll be prompted to pick a server.\n#' @param local Return only local servers? (i.e. not automatically registered\n#'   cloud servers)\n#' @return\n#' `servers()` returns a data frame with registered server names and URLs.\n#' `serverInfo()` returns a list with details for a particular server.\n#' @export\n#' @examples\n#' # List all registered servers\n#' servers()\n#'\n#' # Get information about a server\n#' serverInfo(\"shinyapps.io\")\nservers <- function(local = FALSE) {\n  servers <- serverNames(local)\n\n  info <- lapply(servers, serverInfo)\n  info <- lapply(info, as.data.frame, stringsAsFactors = FALSE)\n\n  out <- rbind_fill(info, c(\"name\", \"url\", \"certificate\"))\n  out$certificate <- secret(out$certificate)\n  out\n}\n\n#' @rdname servers\n#' @export\nserverInfo <- function(name = NULL) {\n  name <- findServer(name, local = FALSE)\n\n  if (isShinyappsServer(name)) {\n    info <- shinyappsServerInfo(name, \"https://api.shinyapps.io/v1\")\n  } else if (isPositConnectCloudServer(name)) {\n    info <- connectCloudServerInfo(name, \"https://api.connect.posit.cloud/v1\")\n  } else {\n    configFile <- serverConfigFile(name)\n    serverDcf <- read.dcf(serverConfigFile(name), all = TRUE)\n    info <- as.list(serverDcf)\n  }\n\n  info$certificate <- secret(info$certificate)\n  info\n}\n\nserverNames <- function(local = FALSE) {\n  names <- gsub(\"\\\\.dcf$\", \"\", basename(serverConfigFiles()))\n  if (!local) {\n    names <- c(names, \"shinyapps.io\", \"connect.posit.cloud\")\n  }\n\n  names\n}\n\nisShinyappsServer <- function(server) {\n  identical(server, \"shinyapps.io\")\n}\n\nisPositConnectCloudServer <- function(server) {\n  identical(server, \"connect.posit.cloud\")\n}\n\n\nisSPCSUrl <- function(url) {\n  grepl(pattern = \"(snowflakecomputing|snowflake)\\\\.app\", x = url)\n}\n\nisSPCSServer <- function(server) {\n  info <- serverInfo(server)\n  isSPCSUrl(info$url)\n}\n\ncheckShinyappsServer <- function(server, call = caller_env()) {\n  if (!isShinyappsServer(server)) {\n    cli::cli_abort(\"`server` must be shinyapps.io\", call = call)\n  }\n}\n\ncheckConnectServer <- function(server, call = caller_env()) {\n  if (!isConnectServer(server)) {\n    cli::cli_abort(\"`server` must be a Posit Connect server\", call = call)\n  }\n}\n\nisRPubs <- function(server) {\n  identical(server, \"rpubs.com\")\n}\n\nisConnectServer <- function(server) {\n  !isShinyappsServer(server) &&\n    !isRPubs(server) &&\n    !isPositConnectCloudServer(server)\n}\n\nshinyappsServerInfo <- function(name, url) {\n  list(\n    name = name,\n    url = getOption(\"rsconnect.shinyapps_url\", url),\n    certificate = inferCertificateContents(\n      system.file(\"cert/shinyapps.io.pem\", package = \"rsconnect\")\n    )\n  )\n}\n\n# Determine which Posit Connect Cloud environment to use. Valid values are\n# development, staging, and production.\nconnectCloudEnvironment <- function() {\n  getOption(\n    \"rsconnect.connect_cloud_environment\",\n    \"production\"\n  )\n}\n\n# Returns various base URLs based on the configured Connect Cloud environment.\nconnectCloudUrls <- function() {\n  switch(\n    connectCloudEnvironment(),\n    production = list(\n      api = \"https://api.connect.posit.cloud/v1\",\n      ui = \"https://connect.posit.cloud\",\n      auth = \"https://login.posit.cloud\",\n      logs = \"https://logs.connect.posit.cloud\"\n    ),\n    staging = list(\n      api = \"https://api.staging.connect.posit.cloud/v1\",\n      ui = \"https://staging.connect.posit.cloud\",\n      auth = \"https://login.staging.posit.cloud\",\n      logs = \"https://logs.staging.connect.posit.cloud\"\n    ),\n    development = list(\n      api = \"https://api.dev.connect.posit.cloud/v1\",\n      ui = \"https://dev.connect.posit.cloud\",\n      auth = \"https://login.staging.posit.cloud\",\n      logs = \"https://logs.dev.connect.posit.cloud\"\n    ),\n  )\n}\n\n# Returns metadata about the Posit Connect Cloud API server.\nconnectCloudServerInfo <- function(name, url) {\n  list(\n    name = name,\n    url = connectCloudUrls()$api,\n    certificate = inferCertificateContents(\n      system.file(\"cert/api.connect.posit.cloud.pem\", package = \"rsconnect\")\n    )\n  )\n}\n\nfindServer <- function(server = NULL, local = TRUE, error_call = caller_env()) {\n  if (!is.null(server)) {\n    check_string(server, call = error_call)\n\n    existing <- serverNames()\n    if (!server %in% existing) {\n      cli::cli_abort(c(\n        \"Can't find {.arg server} with name {.str {server}}.\",\n        i = \"Known servers are {.str {existing}}.\"\n      ))\n    }\n    server\n  } else {\n    existing <- servers(local = local)\n\n    if (length(existing) == 0 || nrow(existing) == 0) {\n      cli::cli_abort(\"No local servers have been registered\")\n    } else if (nrow(existing) == 1) {\n      existing$name\n    } else {\n      idx <- cli_menu(\n        \"Multiple servers found.\",\n        \"Which one do you want to use?\",\n        c(i = \"Use {.arg server} to pick one of {.str {existing$name}}.\"),\n        choices = existing$name\n      )\n      existing$name[idx]\n    }\n  }\n}\n\n#' Server management\n#'\n#' @description\n#' These functions manage the list of known servers:\n#'\n#' * `addServer()` registers a Posit connect server. Once it has been\n#'   registered, you can connect to an account on the server using\n#'   [connectUser()].\n#' * `removeServer()` removes a server from the registry.\n#' * `addServerCertificate()` adds a certificate to a server.\n#'\n#' Supported servers: Posit Connect servers\n#'\n#' @param url URL for the server. Can be a bare hostname like\n#'   `connect.mycompany.com` or a url like `http://posit.mycompany.com/connect`.\n#' @param name Server name. If omitted, the server hostname is used.\n#' @param certificate Optional. Either a path to certificate file or a\n#'   character vector containing the certificate's contents.\n#' @param validate Validate that `url` actually points to a Posit Connect\n#'   server?\n#' @param snowflakeConnectionName Name for the Snowflake connection in\n#'   `connections.toml` to use for authentication or `NULL` to use the default\n#'   (when applicable).\n#' @param quiet Suppress output and prompts where possible.\n#' @export\n#' @examples\n#' \\dontrun{\n#' # register a local server\n#' addServer(\"http://myrsconnect/\", \"myserver\")\n#'\n#' # list servers\n#' servers(local = TRUE)\n#'\n#' # connect to an account on the server\n#' connectUser(server = \"myserver\")\n#' }\n#' @export\naddServer <- function(\n  url,\n  name = NULL,\n  certificate = NULL,\n  validate = TRUE,\n  snowflakeConnectionName = NULL,\n  quiet = FALSE\n) {\n  check_string(url)\n  check_name(name, allow_null = TRUE)\n\n  if (validate) {\n    # Check for SPCS-like URLs.\n    if (isSPCSUrl(url)) {\n      snowflakeConnectionName <- snowflakeConnectionName %||%\n        getDefaultSnowflakeConnectionName(url)\n    }\n    out <- validateConnectUrl(url, certificate, snowflakeConnectionName)\n    if (!out$valid) {\n      cli::cli_abort(\"{.arg url} does not appear to be a Posit Connect server.\")\n    }\n    url <- out$url\n  }\n\n  name <- name %||% serverName(url)\n  registerServer(name, url, certificate)\n\n  if (!quiet) {\n    message(\"Server '\", name, \"' added successfully: \", url)\n  }\n}\n\n\n# Validate a connect server URL by hitting a known configuration endpoint\n# The URL may be specified with or without the protocol and port; this function\n# will try both http and https and follow any redirects given by the server.\nvalidateConnectUrl <- function(\n  url,\n  certificate = NULL,\n  snowflakeConnectionName = NULL\n) {\n  # Add protocol if missing, assuming https except for local installs\n  if (!grepl(\"://\", url, fixed = TRUE)) {\n    if (grepl(\":3939\", url, fixed = TRUE)) {\n      url <- paste0(\"http://\", url)\n    } else {\n      url <- paste0(\"https://\", url)\n    }\n  }\n  url <- ensureConnectServerUrl(url)\n  is_http <- grepl(\"^http://\", url)\n\n  GET_server_settings <- function(url) {\n    timeout <- getOption(\"rsconnect.http.timeout\", if (isWindows()) 20 else 10)\n    auth_info <- list(certificate = inferCertificateContents(certificate))\n\n    if (!is.null(snowflakeConnectionName)) {\n      auth_info$snowflakeToken <- getSnowflakeAuthToken(\n        url,\n        snowflakeConnectionName\n      )\n    }\n    GET(\n      parseHttpUrl(url),\n      auth_info,\n      \"/server_settings\",\n      timeout = timeout\n    )\n  }\n  response <- NULL\n  cnd <- catch_cnd(response <- GET_server_settings(url), \"error\")\n\n  if (is_http && cnd_inherits(cnd, \"OPERATION_TIMEDOUT\")) {\n    url <- gsub(\"^http://\", \"https://\", url)\n    cnd <- catch_cnd(response <- GET_server_settings(url), \"error\")\n  }\n\n  if (!is.null(cnd)) {\n    return(list(valid = FALSE, message = conditionMessage(cnd)))\n  }\n\n  contentType <- attr(response, \"httpContentType\")\n  if (!isContentType(contentType, \"application/json\")) {\n    return(list(valid = FALSE, message = \"Endpoint did not return JSON\"))\n  }\n\n  url <- gsub(\"/server_settings$\", \"\", attr(response, \"httpUrl\"))\n  list(valid = TRUE, url = url, response = response)\n}\n\n# Return a URL that can be concatenated with sub-paths like /content\nensureConnectServerUrl <- function(url) {\n  # strip trailing /\n  url <- gsub(\"/$\", \"\", url)\n\n  # ensure 'url' ends with '/__api__'\n  if (!grepl(\"/__api__$\", url)) {\n    url <- paste(url, \"/__api__\", sep = \"\")\n  }\n\n  url\n}\n\nregisterServer <- function(\n  name,\n  url,\n  certificate = NULL,\n  error_call = caller_env()\n) {\n  certificate <- inferCertificateContents(certificate)\n\n  if (!identical(substr(url, 1, 5), \"https\") && !is.null(certificate)) {\n    cli::cli_abort(\n      c(\n        \"Certificates may only be attached to servers that use the HTTPS protocol.\",\n        i = \"Specify an HTTPS URL for the server, or omit the certificate.\"\n      ),\n      call = error_call\n    )\n  }\n\n  path <- serverConfigFile(name)\n  fields <- list(\n    name = name,\n    url = url,\n    certificate = certificate %||% NA\n  )\n  write.dcf(fields, path, keep.white = \"certificate\")\n}\n\n#' @rdname addServer\n#' @export\nremoveServer <- function(name = NULL) {\n  checkConnectServer(name)\n  name <- findServer(name)\n\n  configFile <- serverConfigFile(name)\n  unlink(configFile)\n}\n\n#' @rdname addServer\n#' @export\naddServerCertificate <- function(name, certificate, quiet = FALSE) {\n  checkConnectServer(name)\n  info <- serverInfo(name)\n  registerServer(name, info$url, certificate)\n\n  if (!quiet) {\n    message(\"Certificate added to server '\", name, \"'\")\n  }\n\n  invisible(NULL)\n}\n\nserverName <- function(url) {\n  url <- parseHttpUrl(url)\n  paste0(url$host, if (nchar(url$port) > 0) \":\", url$port)\n}\n"
  },
  {
    "path": "R/tasks.R",
    "content": "#' List Tasks\n#'\n#' @description\n#' List Tasks\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @inheritParams deployApp\n#' @return\n#' Returns a data frame with the following columns:\n#' \\tabular{ll}{\n#' `id` \\tab Task id \\cr\n#' `action` \\tab Task action\\cr\n#' `status` \\tab Current task status\\cr\n#' `created_time` \\tab Task creation time\\cr\n#' `finished_time` \\tab Task finished time\\cr\n#' }\n#' @examples\n#' \\dontrun{\n#'\n#' # list tasks for the default account\n#' tasks()\n#'\n#' }\n#' @seealso [taskLog()]\n#' @note This function works only with shinyapps.io.\n#' @export\ntasks <- function(account = NULL, server = NULL) {\n  # resolve account and create connect client\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  client <- clientForAccount(accountDetails)\n\n  # list tasks\n  tasks <- client$listTasks(accountDetails$accountId)\n\n  # extract the subset of fields we're interested in\n  res <- lapply(tasks, `[`, c(\"id\", \"action\", \"status\", \"created_time\"))\n\n  # convert to data frame\n  res <- do.call(rbind, res)\n\n  as.data.frame(res, stringsAsFactors = FALSE)\n}\n\n\n#' Show task log\n#'\n#' @description\n#' Writes the task log for the given task\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param taskId Task Id\n#' @inheritParams deployApp\n#' @param output Where to write output. Valid values are `NULL` or `stderr`\n#' @examples\n#' \\dontrun{\n#'\n#' # write task log to stdout\n#' taskLog(12345)\n#'\n#' # write task log to stderr\n#' taskLog(12345, output=\"stderr\")\n#'\n#' }\n#' @seealso [tasks()]\n#' @note This function works only with shinyapps.io.\n#' @export\ntaskLog <- function(taskId, account = NULL, server = NULL, output = NULL) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  client <- clientForAccount(accountDetails)\n\n  if (identical(output, \"stderr\")) {\n    conn <- stderr()\n  } else {\n    conn <- \"\"\n  }\n\n  # show task log\n  cat(client$getTaskLogs(taskId), file = conn)\n\n  # get child tasks\n  tasks <- client$listTasks(\n    accountDetails$accountId,\n    filters = filterQuery(\"parent_id\", taskId)\n  )\n\n  # get child task logs\n  for (task in tasks) {\n    taskLog(task[\"id\"], account = account, server = server, output = output)\n  }\n}\n"
  },
  {
    "path": "R/terminateApp.R",
    "content": "#' Terminate an Application\n#'\n#' @description\n#' Terminate and archive a currently deployed ShinyApps application.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appName Name of application to terminate\n#' @param account Account name. If a single account is registered on the system\n#'   then this parameter can be omitted.\n#' @param server Server name. Required only if you use the same account name on\n#'   multiple servers (see [servers()])\n#' @param quiet Request that no status information be printed to the console\n#'   during the termination.\n#'\n#' @note This function only works for ShinyApps servers.\n#'\n#' @examples\n#' \\dontrun{\n#'\n#' # terminate an application\n#' terminateApp(\"myapp\")\n#' }\n#' @seealso [applications()], [deployApp()], and\n#'   [restartApp()]\n#' @export\nterminateApp <- function(\n  appName,\n  account = NULL,\n  server = NULL,\n  quiet = FALSE\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  # define terminate task\n  taskDef <- list()\n  taskDef$beginStatus <- \"Terminating application\"\n  taskDef$endStatus <- \"Application successfully terminated\"\n  taskDef$action <- function(client, application) {\n    client$terminateApplication(application$id)\n  }\n\n  # perform it\n  applicationTask(taskDef, appName, accountDetails, quiet)\n}\n"
  },
  {
    "path": "R/title.R",
    "content": "#' Generate Application Name\n#'\n#' @description\n#' Generate a short name (identifier) for an application given an application\n#' title.\n#'\n#' @param appTitle A descriptive title for the application.\n#' @param appPath The path to the application's content, either a directory\n#'   or an individual document. Optional.\n#' @param account The account where the application will be deployed. Optional.\n#' @param unique Whether to try to generate a unique name.\n#' @return\n#' Returns a valid short name for the application.\n#'\n#' @details\n#' This function modifies the title until it forms a suitable application name.\n#' Suitable application names are 3 - 64 characters long and contain only\n#' alphanumeric characters.\n#'\n#' The function is intended to be used to find a name for a new application.\n#' If `appPath` and `account` are both specified, then the returned\n#' name will also be unique among locally known deployments of the directory\n#' (note that it is not guaranteed to be unique on the server). This behavior\n#' can be disabled by setting `unique = FALSE`.\n#'\n#' @keywords internal\n#' @examples\n#' \\dontrun{\n#' # Generate a short name for a sample application\n#' generateAppName(\"My Father's Country\", \"~/fathers-country\", \"myacct\")\n#' }\n#' @export\n\ngenerateAppName <- function(\n  appTitle,\n  appPath = NULL,\n  account = NULL,\n  unique = TRUE\n) {\n  munge <- function(title) {\n    # safe default if no title specified\n    if (is.null(title)) {\n      return(\"\")\n    }\n\n    # start by removing most non-Latin characters and converting to lowercase\n    name <- tolower(gsub(\"[^A-Za-z0-9_ -]+\", \"\", title))\n\n    # replace spaces with underscores\n    name <- gsub(\" \", \"_\", name, fixed = TRUE)\n\n    # trim to 64 characters\n    if (nchar(name) > 64) {\n      name <- substr(name, 1, 64)\n    }\n\n    name\n  }\n\n  name <- munge(appTitle)\n\n  # if we wound up with too few characters, try generating from the directory\n  # name instead\n  if (nchar(name) < 3 && !is.null(appPath) && file.exists(appPath)) {\n    # strip extension if present\n    base <- basename(appPath)\n    if (nzchar(tools::file_ext(base))) {\n      base <- file_path_sans_ext(base)\n\n      # if we stripped an extension and the name is now \"index\", use the parent\n      # folder's name\n      if (identical(base, \"index\")) {\n        base <- basename(dirname(appPath))\n      }\n    }\n    name <- munge(base)\n  }\n\n  # validate that we wound up with a valid name\n  if (nchar(name) < 3) {\n    stop(\n      \"The generated app name '\",\n      name,\n      \"' is invalid. Include at least 3 \",\n      \"alphanumeric characters in the title.\"\n    )\n  }\n\n  # if we have an account and a directory, make the new app name unique to the\n  # best of our local knowledge\n  if (unique && !is.null(appPath) && nzchar(appPath) && !is.null(account)) {\n    apps <- deployments(appPath, accountFilter = account)\n    if (name %in% apps$name) {\n      # trim a few characters if necessary so we can add unique numbers to the end\n      base <- substr(name, 1, 62)\n      suffix <- 2\n      candidate <- paste0(base, suffix)\n\n      # keep incrementing the suffix until we find a unique name\n      while (candidate %in% apps$name) {\n        suffix <- suffix + 1\n        candidate <- paste0(base, suffix)\n      }\n      name <- candidate\n    }\n  }\n\n  name\n}\n"
  },
  {
    "path": "R/usage.R",
    "content": "#' Show Application Usage\n#'\n#' @description\n#' Show application usage of a currently deployed application\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param appName Name of application\n#' @param appDir Directory containing application. Defaults to\n#'   current working directory.\n#' @inheritParams deployApp\n#' @param usageType Use metric to retreive (for example: \"hours\")\n#' @param from Date range starting timestamp (Unix timestamp or relative time\n#'   delta such as \"2d\" or \"3w\").\n#' @param until Date range ending timestamp (Unix timestamp or relative time\n#'   delta such as \"2d\" or \"3w\").\n#' @param interval Summarization interval. Data points at intervals less then this\n#'   will be grouped. (Relative time delta e.g. \"120s\" or \"1h\" or \"30d\").\n#' @note This function only works for ShinyApps servers.\n#' @export\nshowUsage <- function(\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  usageType = \"hours\",\n  from = NULL,\n  until = NULL,\n  interval = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  api <- clientForAccount(accountDetails)\n\n  # resolve application\n  if (is.null(appName)) {\n    appName <- basename(appDir)\n  }\n  application <- resolveApplication(accountDetails, appName)\n\n  # get application usage\n  data <- api$getAccountUsage(\n    accountDetails$accountId,\n    usageType,\n    application$id,\n    from,\n    until,\n    interval\n  )\n\n  if (length(data$points) < 1) {\n    stop(\"No data.\", call. = FALSE)\n  }\n\n  # get data points\n  points <- data$points[[1]]\n  points <- lapply(points, function(X) {\n    X[[1]] <- X[[1]] / 1000 # convert from milliseconds to seconds\n    X\n  })\n\n  # convert to data frame\n  df <- data.frame(\n    matrix(unlist(points), nrow = length(points), byrow = TRUE),\n    stringsAsFactors = FALSE\n  )\n  colnames(df) <- c(\"timestamp\", usageType)\n  return(df)\n}\n\n#' Show Application Metrics\n#'\n#' @description\n#' Show application metrics of a currently deployed application.\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @param metricSeries Metric series to query. Refer to the\n#'   [shinyapps.io documentation](<https://docs.posit.co/shinyapps.io/metrics.html#ApplicationMetrics>)\n#'   for available series.\n#' @param metricNames Metric names in the series to query. Refer to the\n#'   [shinyapps.io documentation](<https://docs.posit.co/shinyapps.io/metrics.html#ApplicationMetrics>)\n#'   for available metrics.\n#' @inheritParams deployApp\n#' @param from Date range starting timestamp (Unix timestamp or relative time\n#'   delta such as \"2d\" or \"3w\").\n#' @param until Date range ending timestamp (Unix timestamp or relative time\n#'   delta such as \"2d\" or \"3w\").\n#' @param interval Summarization interval. Data points at intervals less then this\n#'   will be grouped. (Relative time delta e.g. \"120s\" or \"1h\" or \"30d\").\n#' @export\nshowMetrics <- function(\n  metricSeries,\n  metricNames,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = \"shinyapps.io\",\n  from = NULL,\n  until = NULL,\n  interval = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  api <- clientForAccount(accountDetails)\n\n  # resolve application\n  if (is.null(appName)) {\n    appName <- basename(appDir)\n  }\n  application <- resolveApplication(accountDetails, appName)\n\n  # get application usage\n  data <- api$getApplicationMetrics(\n    application$id,\n    metricSeries,\n    metricNames,\n    from,\n    until,\n    interval\n  )\n\n  if (length(data$points) < 1) {\n    stop(\"No data.\", call. = FALSE)\n  }\n\n  points <- lapply(data$points, as.data.frame, stringsAsFactors = FALSE)\n  points <- do.call(rbind, points)\n  points$time <- .POSIXct(points$time / 1000)\n  points\n}\n\n#' Show Account Usage\n#'\n#' @description\n#' Show account usage\n#'\n#' Supported servers: ShinyApps servers\n#'\n#' @inheritParams deployApp\n#' @param usageType Use metric to retreive (for example: \"hours\")\n#' @param from Date range starting timestamp (Unix timestamp or relative time\n#'   delta such as \"2d\" or \"3w\").\n#' @param until Date range ending timestamp (Unix timestamp or relative time\n#'   delta such as \"2d\" or \"3w\").\n#' @param interval Summarization interval. Data points at intervals less then this\n#'   will be grouped. (Number of seconds or relative time delta e.g. \"1h\").\n#' @note This function only works for ShinyApps servers.\n#' @export\naccountUsage <- function(\n  account = NULL,\n  server = NULL,\n  usageType = \"hours\",\n  from = NULL,\n  until = NULL,\n  interval = NULL\n) {\n  accountDetails <- accountInfo(account, server)\n  checkShinyappsServer(accountDetails$server)\n\n  api <- clientForAccount(accountDetails)\n\n  # get application usage\n  data <- api$getAccountUsage(\n    accountDetails$accountId,\n    usageType,\n    NULL,\n    from,\n    until,\n    interval\n  )\n}\n"
  },
  {
    "path": "R/utils-cli.R",
    "content": "# https://github.com/r-lib/cli/issues/228 ---------------------------------\n\nsimulate_user_input <- function(x, env = caller_env()) {\n  withr::local_options(\n    rlang_interactive = TRUE,\n    cli_prompt = as.character(x),\n    .local_envir = env\n  )\n}\n\ncli_menu <- function(\n  header,\n  prompt,\n  choices,\n  not_interactive = choices,\n  quit = integer(),\n  .envir = caller_env(),\n  error_call = caller_env()\n) {\n  if (!is_interactive()) {\n    cli::cli_abort(\n      c(header, not_interactive),\n      .envir = .envir,\n      call = error_call\n    )\n  }\n\n  choices <- paste0(cli::style_bold(seq_along(choices)), \": \", choices)\n  cli::cli_inform(\n    c(header, prompt, choices),\n    .envir = .envir\n  )\n\n  repeat {\n    selected <- cli_readline(\"Selection: \")\n    if (selected %in% c(\"0\", seq_along(choices))) {\n      break\n    }\n    cli::cli_inform(\n      \"Type a number between 1 and {length(choices)}, or type 0 to exit.\"\n    )\n  }\n\n  selected <- as.integer(selected)\n  if (selected %in% c(0, quit)) {\n    if (is_testing()) {\n      cli::cli_abort(\"Quitting...\", call = NULL)\n    } else {\n      cli::cli_alert_danger(\"Quitting...\")\n      # simulate user pressing Ctrl + C\n      invokeRestart(\"abort\", cnd)\n    }\n  }\n  selected\n}\n\ncli_readline <- function(prompt) {\n  testing <- getOption(\"cli_prompt\", character())\n\n  if (length(testing) > 0) {\n    selected <- testing[[1]]\n    cli::cli_inform(paste0(prompt, selected))\n    options(cli_prompt = testing[-1])\n    selected\n  } else {\n    readline(prompt)\n  }\n}\n\nis_testing <- function() {\n  identical(Sys.getenv(\"TESTTHAT\"), \"true\")\n}\n"
  },
  {
    "path": "R/utils.R",
    "content": "# Returns a logging function when enabled, a noop function otherwise.\nverboseLogger <- function(verbose) {\n  if (verbose) {\n    function(...) {\n      timestamp <- paste(\"[\", as.character(Sys.time()), \"]\", sep = \"\")\n      cat(paste0(timestamp, \" \", ..., \"\\n\"))\n    }\n  } else {\n    function(...) {}\n  }\n}\n\nregexExtract <- function(re, input) {\n  match <- regexec(re, input)\n  matchLoc <- match[1][[1]]\n  if (length(matchLoc) > 1) {\n    matchLen <- attributes(matchLoc)$match.length\n    return(substr(input, matchLoc[2], matchLoc[2] + matchLen[2] - 1))\n  } else {\n    return(NULL)\n  }\n}\n\ndisplayStatus <- function(quiet) {\n  quiet <- quiet || httpDiagnosticsEnabled()\n  function(status) {\n    if (!quiet) {\n      cat(status)\n    }\n  }\n}\n\nhttpDiagnosticsEnabled <- function() {\n  return(\n    getOption(\"rsconnect.http.trace\", FALSE) ||\n      getOption(\"rsconnect.http.verbose\", FALSE)\n  )\n}\n\n# Replacement for tools::file_path_sans_ext to work around an issue where\n# filenames like \"foo..ext\" are not returned as \"foo.\".\nfile_path_sans_ext <- function(x, compression = FALSE) {\n  if (compression) {\n    x <- sub(\"[.](gz|bz2|xz)$\", \"\", x)\n  }\n  sub(\"(.+)\\\\.[[:alnum:]]+$\", \"\\\\1\", x)\n}\n\ndirExists <- function(x) {\n  utils::file_test(\"-d\", x)\n}\n\n# Returns the MD5 for path as a raw sequence of 16 hexadecimal pairs.\nfileMD5 <- function(path, raw = FALSE) {\n  # Use digest::digest to compute file MD5. FIPS mode disables openssl::md5. Workaround until we can\n  # migrate away from MD5 for file content checks.\n  #\n  # See: https://github.com/rstudio/rsconnect/issues/363\n\n  if (is.null(path)) {\n    digest::digest(\"\", algo = \"md5\", serialize = FALSE, raw = raw)\n  } else {\n    digest::digest(path, algo = \"md5\", file = TRUE, raw = raw)\n  }\n}\n\ncheck_file <- function(\n  x,\n  error_arg = caller_arg(x),\n  error_call = caller_env()\n) {\n  check_string(\n    x,\n    allow_empty = FALSE,\n    error_arg = error_arg,\n    error_call = error_call\n  )\n  if (!file.exists(x)) {\n    cli::cli_abort(\n      \"{.arg {error_arg}}, {.str {x}}, does not exist.\",\n      call = error_call\n    )\n  }\n}\n\ncheck_directory <- function(\n  x,\n  error_arg = caller_arg(x),\n  error_call = caller_env()\n) {\n  check_file(x, error_arg = error_arg, error_call = error_call)\n  if (!dirExists(x)) {\n    cli::cli_abort(\n      \"{.arg {error_arg}}, {.str {x}}, is not a directory.\",\n      call = error_call\n    )\n  }\n}\n\nfile_size <- function(path) {\n  x <- file.info(path)$size\n  x[is.na(x)] <- 0\n  x\n}\n\nrbind_fill <- function(dfs, col_names = character()) {\n  if (length(dfs) == 0) {\n    df <- rep(list(logical(0)), length(col_names))\n    names(df) <- col_names\n    return(as.data.frame(df))\n  }\n\n  all_names <- unique(unlist(lapply(dfs, names)))\n  all_names <- union(col_names, all_names)\n\n  add_missing_cols <- function(df) {\n    df[setdiff(all_names, names(df))] <- rep(NA, nrow(df))\n    df\n  }\n\n  complete <- lapply(dfs, add_missing_cols)\n  out <- do.call(\"rbind\", complete)\n  out[all_names]\n}\n\n# Ensure slashes are the same direction on every platform to make snapshot\n# testing simpler\nnormalizePath <- function(path, mustWork = FALSE) {\n  base::normalizePath(path, winslash = \"/\", mustWork = mustWork)\n}\n\ncompact <- function(x) {\n  x[!vapply(x, is.null, logical(1))]\n}\n\nhasPrefix <- function(x, prefix) {\n  substring(x, 1, nchar(prefix)) == prefix\n}\n\n# Lightweight equivalent of withr::defer()\ndefer <- function(expr, env = caller_env(), after = FALSE) {\n  thunk <- as.call(list(function() expr))\n  do.call(on.exit, list(thunk, TRUE, after), envir = env)\n}\n\ndirCreate <- function(paths) {\n  for (path in paths) {\n    dir.create(path, showWarnings = FALSE, recursive = TRUE)\n  }\n  paths\n}\n\nfromIDE <- function() {\n  !is.na(Sys.getenv(\"RSTUDIO\", unset = NA)) &&\n    !identical(.Platform$GUI, \"RStudio\")\n}\n\ntoJSON <- function(x, ...) {\n  jsonlite::toJSON(\n    x,\n    dataframe = \"columns\",\n    null = \"null\",\n    na = \"null\",\n    auto_unbox = TRUE,\n    pretty = TRUE,\n    digits = 30,\n    ...\n  )\n}\n\ntruthy <- function(value, default = FALSE) {\n  if (!is.atomic(value) || length(value) != 1 || is.na(value)) {\n    default\n  } else if (is.character(value)) {\n    value %in% c(\"TRUE\", \"True\", \"true\", \"T\", \"1\")\n  } else {\n    as.logical(value)\n  }\n}\n\nsanitizeSystem2json <- function(x) {\n  paste(trimws(x, \"right\", \"[\\r\\n]\"), collapse = \"\")\n}\n"
  },
  {
    "path": "R/utm.R",
    "content": "getUtmValue <- function() {\n  if (Sys.getenv(\"RSTUDIO\") == \"1\") {\n    return(\"rsconnect-rstudio\")\n  } else {\n    return(\"rsconnect\")\n  }\n}\n\n# Add UTM query parameter for Posit Connect Cloud.\naddUtmParameters <- function(url) {\n  utmValue <- getUtmValue()\n  if (grepl(\"\\\\?\", url)) {\n    paste0(url, \"&utm_source=\", utmValue)\n  } else {\n    paste0(url, \"?utm_source=\", utmValue)\n  }\n}\n"
  },
  {
    "path": "R/writeManifest.R",
    "content": "#' Create a `manifest.json`\n#'\n#' @description\n#' Use `writeManifest()` to generate a `manifest.json`. Among other things,\n#' you can commit this file to git to activate\n#' [Git-Backed content](https://docs.posit.co/connect/user/git-backed/)\n#' for Posit Connect.\n#'\n#' `manifest.json` contains a list of all files in the app along with their\n#' dependencies, so you will need to re-run `writeManifest()` when either of\n#' these change.\n#'\n#' Supported servers: All servers\n#'\n#' @inheritParams deployApp\n#' @param contentCategory Set this to `\"site\"` if you'd deploy with\n#'   [deploySite()]; otherwise leave as is.\n#' @param verbose If `TRUE`, prints detailed progress messages.\n#' @param quiet If `FALSE`, prints progress messages.\n#' @export\nwriteManifest <- function(\n  appDir = getwd(),\n  appFiles = NULL,\n  appFileManifest = NULL,\n  appPrimaryDoc = NULL,\n  appMode = NULL,\n  contentCategory = NULL,\n  python = NULL,\n  forceGeneratePythonEnvironment = FALSE,\n  quarto = NA,\n  image = NULL,\n  envManagement = NULL,\n  envManagementR = NULL,\n  envManagementPy = NULL,\n  envManagementNodejs = NULL,\n  packageRepositoryResolutionR = NULL,\n  dependencyResolution = c(\"strict\", \"library\"),\n  verbose = FALSE,\n  quiet = FALSE\n) {\n  dependencyResolution <- match.arg(dependencyResolution)\n  appFiles <- listDeploymentFiles(\n    appDir,\n    appFiles = appFiles,\n    appFileManifest = appFileManifest\n  )\n\n  appMetadata <- appMetadata(\n    appDir = appDir,\n    appFiles = appFiles,\n    appPrimaryDoc = appPrimaryDoc,\n    quarto = quarto,\n    appMode = appMode,\n    contentCategory = contentCategory,\n  )\n\n  # copy files to bundle dir to stage\n  bundleDir <- bundleAppDir(\n    appDir = appDir,\n    appFiles = appFiles,\n    appPrimaryDoc = appMetadata$appPrimaryDoc,\n    appMode = appMetadata$appMode\n  )\n  defer(unlink(bundleDir, recursive = TRUE))\n\n  python <- getPython(python)\n  pythonConfig <- pythonConfigurator(python, forceGeneratePythonEnvironment)\n\n  if (dependencyResolution == \"library\") {\n    confirmDependencySourceLibrary()\n  }\n\n  # generate the manifest and write it into the bundle dir\n  manifest <- createAppManifest(\n    appDir = bundleDir,\n    appMetadata = appMetadata,\n    pythonConfig = pythonConfig,\n    retainPackratDirectory = FALSE,\n    image = image,\n    envManagement = envManagement,\n    envManagementR = envManagementR,\n    envManagementPy = envManagementPy,\n    envManagementNodejs = envManagementNodejs,\n    packageRepositoryResolutionR = packageRepositoryResolutionR,\n    dependencyResolution = dependencyResolution,\n    verbose = verbose,\n    quiet = quiet\n  )\n  manifestJson <- toJSON(manifest)\n  manifestPath <- file.path(appDir, \"manifest.json\")\n  writeLines(manifestJson, manifestPath, useBytes = TRUE)\n\n  requirementsFilename <- manifest$python$package_manager$package_file\n  if (is.null(requirementsFilename)) {\n    requirementsFilename <- \"requirements.txt\"\n  }\n  srcRequirementsFile <- file.path(bundleDir, requirementsFilename)\n  dstRequirementsFile <- file.path(appDir, requirementsFilename)\n  if (file.exists(srcRequirementsFile) && !file.exists(dstRequirementsFile)) {\n    file.copy(srcRequirementsFile, dstRequirementsFile)\n  }\n\n  invisible()\n}\n"
  },
  {
    "path": "R/zzz.R",
    "content": "defaultMaxBundleSize <- 5 * 1024^3\ndefaultMaxBundleFiles <- 10000\n\nsetOptionDefaults <- function(...) {\n  # Resolve dots\n  defaultOptions <- list(...)\n\n  # Get current options\n  currentOptions <- options()\n\n  # Remove any options which have already been set\n  alreadySet <- names(defaultOptions) %in% names(currentOptions)\n  defaultOptions <- defaultOptions[!alreadySet]\n\n  # Set the defaults\n  options(defaultOptions)\n}\n\n.onLoad <- function(libname, pkgname) {\n  setOptionDefaults(\n    rsconnect.max.bundle.size = defaultMaxBundleSize,\n    rsconnect.max.bundle.files = defaultMaxBundleFiles\n  )\n}\n"
  },
  {
    "path": "README.Rmd",
    "content": "---\noutput: github_document\n---\n\n<!-- README.md is generated from README.Rmd. Please edit that file -->\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#>\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n\n# rsconnect <a href='https://rstudio.github.io/rsconnect/'><img src=\"man/figures/logo.png\" align=\"right\" height=\"139\"/></a>\n\n<!-- badges: start -->\n\n[![CRAN status](https://www.r-pkg.org/badges/version/rsconnect)](https://cran.r-project.org/package=rsconnect) [![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) [![R-CMD-check](https://github.com/rstudio/rsconnect/workflows/R-CMD-check/badge.svg)](https://github.com/rstudio/rsconnect/actions)\n\n<!-- badges: end -->\n\nrsconnect makes it easy to publish your Shiny apps, RMarkdown and Quarto documents, and Plumber APIs<sup>*</sup> to [Posit Connect](https://posit.co/products/enterprise/connect/), [Posit Connect Cloud](https://connect.posit.cloud/), and [shinyapps.io](https://www.shinyapps.io/) from R.\n\n(If you're looking for the Python equivalent, try [rsconnect-python](https://pypi.org/project/rsconnect-python/).)\n\n## Installation\n\nYou can install the released version of rsconnect from [CRAN](https://CRAN.R-project.org) with:\n\n``` r\ninstall.packages(\"rsconnect\")\n```\n\nAnd the development version from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"devtools\")\ndevtools::install_github(\"rstudio/rsconnect\")\n```\n\n## Setup\n\nTo use rsconnect, you first need to teach it about the server you want to publish to.\nIf you use the RStudio IDE, the easiest way to get set up is to use the publishing dialog, which you can find by clicking the \"Tools\" menu, then selecting \"Global options\", then clicking \"Publishing\".\nClick \"Connect\" to add new servers.\n\nYou can also connect from any R session by running a little code:\n\n- For Posit Conect Cloud, call `connectCloudUser()` to authenticate through the browser.\n\n-   For Posit Connect, first use `addServer()` to register your server with rsconnect, then call either `connectUser()` or `connectApiUser()`.\n    `connectUser()` is a bit simpler if you're in an interactive session; `connectApiUser()` works anywhere but requires a you to copy and paste an API key from your user profile.\n\n-   For shinyapps.io, go to your [tokens page](https://www.shinyapps.io/admin/#/tokens) and click \"Add Token\", then follow the instructions to copy and paste the appropriate call to `setAccountInfo()`.\n    Learn more in the [Getting Started Guide](https://shiny.rstudio.com/articles/shinyapps.html).\n\nNow that you're setup you can use `deployApp()`, `deployDoc()`, and friends to publish your apps, documentations, APIs and more.\n"
  },
  {
    "path": "README.md",
    "content": "\n<!-- README.md is generated from README.Rmd. Please edit that file -->\n\n# rsconnect <a href='https://rstudio.github.io/rsconnect/'><img src=\"man/figures/logo.png\" align=\"right\" height=\"139\"/></a>\n\n<!-- badges: start -->\n\n[![CRAN\nstatus](https://www.r-pkg.org/badges/version/rsconnect)](https://cran.r-project.org/package=rsconnect)\n[![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable)\n[![R-CMD-check](https://github.com/rstudio/rsconnect/workflows/R-CMD-check/badge.svg)](https://github.com/rstudio/rsconnect/actions)\n\n<!-- badges: end -->\n\nrsconnect makes it easy to publish your Shiny apps, RMarkdown and Quarto\ndocuments, and Plumber APIs<sup>\\*</sup> to [Posit\nConnect](https://posit.co/products/enterprise/connect/), [Posit Connect\nCloud](https://connect.posit.cloud/), and\n[shinyapps.io](https://www.shinyapps.io/) from R.\n\n(If you’re looking for the Python equivalent, try\n[rsconnect-python](https://pypi.org/project/rsconnect-python/).)\n\n## Installation\n\nYou can install the released version of rsconnect from\n[CRAN](https://CRAN.R-project.org) with:\n\n``` r\ninstall.packages(\"rsconnect\")\n```\n\nAnd the development version from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"devtools\")\ndevtools::install_github(\"rstudio/rsconnect\")\n```\n\n## Setup\n\nTo use rsconnect, you first need to teach it about the server you want\nto publish to. If you use the RStudio IDE, the easiest way to get set up\nis to use the publishing dialog, which you can find by clicking the\n“Tools” menu, then selecting “Global options”, then clicking\n“Publishing”. Click “Connect” to add new servers.\n\nYou can also connect from any R session by running a little code:\n\n- For Posit Conect Cloud, call `connectCloudUser()` to authenticate\n  through the browser.\n\n- For Posit Connect, first use `addServer()` to register your server\n  with rsconnect, then call either `connectUser()` or\n  `connectApiUser()`. `connectUser()` is a bit simpler if you’re in an\n  interactive session; `connectApiUser()` works anywhere but requires a\n  you to copy and paste an API key from your user profile.\n\n- For shinyapps.io, go to your [tokens\n  page](https://www.shinyapps.io/admin/#/tokens) and click “Add Token”,\n  then follow the instructions to copy and paste the appropriate call to\n  `setAccountInfo()`. Learn more in the [Getting Started\n  Guide](https://shiny.rstudio.com/articles/shinyapps.html).\n\nNow that you’re setup you can use `deployApp()`, `deployDoc()`, and\nfriends to publish your apps, documentations, APIs and more.\n"
  },
  {
    "path": "RELEASE.md",
    "content": "## Release instructions\n\nCreate a release issue with:\n\n```r\nusethis::use_release_issue()\n```\n\nRun noSuggests test:\n\n```r\nrhub::rhub_check(platforms=\"nosuggests\")\n```\n"
  },
  {
    "path": "_pkgdown.yml",
    "content": "url: https://rstudio.github.io/rsconnect\n\ndevelopment:\n  mode: auto\n\ntemplate:\n  package: tidytemplate\n  bootstrap: 5\n\nreference:\n\n  - title: Package options\n    desc : ~\n    contents :\n    - matches(\"rsconnect\")\n\n  - title: Accounts\n    desc : ~\n    contents :\n    - matches(\"[Aa]ccount\")\n\n  - title: Users\n    desc : ~\n    contents :\n    - matches(\"[Uu]ser\")\n\n\n  - title: Deployments\n    desc : ~\n    contents :\n    - writeManifest\n    - matches(\"[Dd]eploy\")\n    - listDeploymentFiles\n\n  - title: Servers\n    desc : ~\n    contents :\n    - matches(\"[Ss]erver\")\n\n  - title: Properties and Metrics\n    desc : ~\n    contents :\n    - matches(\"[Pp]ropert\")\n    - matches(\"show\")\n    - getLogs\n    - resendInvitation\n\n  - title: Applications\n    desc : ~\n    contents :\n    - matches(\"[Aa]pp\")\n\n  - title: Other functions\n    desc : ~\n    contents :\n    - addLinter\n    - lint\n    - linter\n    - makeLinterMessage\n    - rpubsUpload\n    - taskLog\n    - tasks\n"
  },
  {
    "path": "cran-comments.md",
    "content": "## Summary\n\nUse httr2 as HTTP client. Support renv profiles. Remove several\nlong-deprecated functions, such as `addConnectServer()` and\n`discoverServer()`, and other HTTP backends.\n\nNote: The Rsconctdply package uses the removed `addConnectServer()` function.\nThis problem was reported to the project several weeks ago with no response.\nWe filed an issue (https://github.com/snchimata/Rsconctdply/issues/1) and\nprovided a fix (https://github.com/snchimata/Rsconctdply/pull/2).\n\n## R CMD check results\n\n0 errors ✔ | 0 warnings ✔ | 1 note ✖\n\n## revdepcheck results\n\nWe checked 24 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package.\n\n * We saw 1 new problems\n * We failed to check 0 packages\n\nIssues with CRAN packages are summarised below.\n\n### New problems\n(This reports the first line of each new failure)\n\n* Rsconctdply\n  checking dependencies in R code ... WARNING\n\n"
  },
  {
    "path": "examples/example-linter.R",
    "content": "addLinter(\"no.capitals\", linter(\n\n  ## Identify lines containing capital letters -- either by name or by index\n  apply = function(content, ...) {\n    grep(\"[A-Z]\", content)\n  },\n\n  ## Only use this linter on R files (paths ending with .r or .R)\n  takes = function(paths) {\n    grep(\"[rR]$\", paths)\n  },\n\n  # Use the default message constructor\n  message = function(content, lines, ...) {\n    makeLinterMessage(\"Capital letters found on the following lines\", content, lines)\n  },\n\n  # Give a suggested prescription\n  suggest = \"Do not use capital letters in these documents.\"\n))\n"
  },
  {
    "path": "inst/cert/api.connect.posit.cloud.pem",
    "content": "-----BEGIN CERTIFICATE-----\r\nMIIF2jCCBMKgAwIBAgIQAemXdyxv7CGHCzR9b3LgmTANBgkqhkiG9w0BAQsFADA8\r\nMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g\r\nUlNBIDIwNDggTTAyMB4XDTI0MTEwNzAwMDAwMFoXDTI1MTIwNzIzNTk1OVowIjEg\r\nMB4GA1UEAxMXYXBpLmNvbm5lY3QucG9zaXQuY2xvdWQwggEiMA0GCSqGSIb3DQEB\r\nAQUAA4IBDwAwggEKAoIBAQCebbt0qA8xrlrhGAM8Q+foOHFwYNm04tK+U9EZVgsb\r\nxCxv0tOIMZiIW8xR1GA2Ay9pEIq5MooIrxcJjRtwzVqXgwwPRMJTEVlxqIoA/nm8\r\nzp50rkz4ejf9mBg4cYZe3ds9h/1E/hRMbAJbl9ETcOUnp+cZ56uTrgLdZpeYM4zt\r\nIIpHE8BTwgHAiZ4TSR8w0yGkn4GE0w/w02blSwwPph58afaGreQruoSFw+5qJTpd\r\nhjSkieYxR8LIfEuwvnHCHu7PygHN6Cw9xBCHy/WEQ3rsdzLraccsBFWt0venqhgA\r\n2DlUk7uAR4j82W0j/bfuaADprP2vuSh5jbuMxMePQctHAgMBAAGjggLwMIIC7DAf\r\nBgNVHSMEGDAWgBTAMVLNWlDDgnx0cc7L6Zz5euuC4jAdBgNVHQ4EFgQU+R+/S+cS\r\nfSZ7atZWGcizk6W1fYAwIgYDVR0RBBswGYIXYXBpLmNvbm5lY3QucG9zaXQuY2xv\r\ndWQwEwYDVR0gBAwwCjAIBgZngQwBAgEwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW\r\nMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8v\r\nY3JsLnIybTAyLmFtYXpvbnRydXN0LmNvbS9yMm0wMi5jcmwwdQYIKwYBBQUHAQEE\r\naTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5yMm0wMi5hbWF6b250cnVzdC5j\r\nb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQucjJtMDIuYW1hem9udHJ1c3QuY29t\r\nL3IybTAyLmNlcjAMBgNVHRMBAf8EAjAAMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFq\r\nAWgAdgAS8U40vVNyTIQGGcOPP3oT+Oe1YoeInG0wBYTr5YYmOgAAAZMD+wSTAAAE\r\nAwBHMEUCIQCqGd7jUrKhkXIzF5x52oe+D/klDg5AmUx1mv7GxXgVzQIgbYKs+EMS\r\nm/Gb/yVc8RPeaD8zCHFtUGVJeuXAgViji9sAdgB9WR4S4XgqexxhZ3xe/fjQh1wU\r\noE6VnrkDL9kOjC55uAAAAZMD+wTUAAAEAwBHMEUCIGx/Ixv3Tzg70QYC60vDkuAk\r\nxRg1fSANLcHnVIVazrtGAiEAv6dlqrWjp85oWCTtqdxwWiastbs4O5byxAOae/7M\r\nxCIAdgDm0jFjQHeMwRBBBtdxuc7B0kD2loSG+7qHMh39HjeOUAAAAZMD+wTmAAAE\r\nAwBHMEUCIQDOH32+k4ODVfiZXTtzjfYuThvCXSiODxQepaMhgc+12QIgPO4BGPVS\r\nWsHKmYWbJRdoxtep1V55hIwqqLp0i2gvVyMwDQYJKoZIhvcNAQELBQADggEBAJD2\r\nrZU3V/SJln2wncKITd6kMj2PRkFcSGIvG0O4PYysYpXTHAnt2co6KDFui7YMn+zW\r\nKEoyMgRDxxvJ7GQIjEBda9oJDKU4RlSeXAAIV/nurJPOK2oeJ9rzK0o9/UGvNrTQ\r\njShasc90IiTTCYVq6XF31LOywXSsudVsi4+X8n6OFMnySFSVYqs2iX1VQJkZ/BEI\r\nYy6siGCKY9kKaysPvLMwA/czMCmJBN5uikVoIwgSLdB4wwFn11KDdv0V55+d4uDV\r\nxaEjnwvyIevOGJjvhoBt0xWOjDRDk5aJ1JCEpkU7eflN+h3wV8Sfrb3XnJR21Grk\r\n10o7q6aTCq0b+POHQBY=\r\n-----END CERTIFICATE-----\r\n"
  },
  {
    "path": "inst/cert/cacert.pem",
    "content": "##\n## Bundle of CA Root Certificates\n##\n## Certificate data from Mozilla as of: Wed Sep 20 03:12:05 2017 GMT\n##\n## This is a bundle of X.509 certificates of public Certificate Authorities\n## (CA). These were automatically extracted from Mozilla's root certificates\n## file (certdata.txt).  This file can be found in the mozilla source tree:\n## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt\n##\n## It contains the certificates in PEM format and therefore\n## can be directly used with curl / libcurl / php_curl, or with\n## an Apache+mod_ssl webserver for SSL client authentication.\n## Just configure this file as the SSLCACertificateFile.\n##\n## Conversion done with mk-ca-bundle.pl version 1.27.\n## SHA256: 2b2dbe5244e0047e088c597998883a913f6c5fffd1cb5c0fe5a368c8466cb2ec\n##\n\n\nGlobalSign Root CA\n==================\n-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\nGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\nb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\nBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\nVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\nDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\nTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\nKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\nc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\ngzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\nAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\nY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\nj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\nhm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\nX4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n-----END CERTIFICATE-----\n\nGlobalSign Root CA - R2\n=======================\n-----BEGIN CERTIFICATE-----\nMIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv\nYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh\nbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT\naWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln\nbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6\nErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp\ns6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN\nS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL\nTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C\nygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\nFgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i\nYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN\nBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp\n9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu\n01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7\n9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7\nTBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==\n-----END CERTIFICATE-----\n\nVerisign Class 3 Public Primary Certification Authority - G3\n============================================================\n-----BEGIN CERTIFICATE-----\nMIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV\nUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv\ncmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl\nIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw\nCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy\ndXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv\ncml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg\nQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1\nEUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc\ncLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw\nEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj\n055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f\nj267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC\n/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0\nxuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa\nt20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==\n-----END CERTIFICATE-----\n\nEntrust.net Premium 2048 Secure Server CA\n=========================================\n-----BEGIN CERTIFICATE-----\nMIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u\nZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp\nbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV\nBAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx\nNzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3\nd3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl\nMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u\nZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL\nGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr\nhRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW\nnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi\nVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E\nBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ\nKoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy\nT/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf\nzX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT\nJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e\nnNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=\n-----END CERTIFICATE-----\n\nBaltimore CyberTrust Root\n=========================\n-----BEGIN CERTIFICATE-----\nMIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE\nChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li\nZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC\nSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs\ndGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME\nuyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB\nUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C\nG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9\nXbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr\nl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI\nVDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB\nBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh\ncL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5\nhbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa\nY71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H\nRCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\n-----END CERTIFICATE-----\n\nAddTrust External Root\n======================\n-----BEGIN CERTIFICATE-----\nMIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML\nQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD\nVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw\nNDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU\ncnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg\nUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821\n+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw\nTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo\naSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy\n2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7\n7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P\nBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL\nVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk\nVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB\nIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl\nj7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5\n6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355\ne6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u\nG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=\n-----END CERTIFICATE-----\n\nEntrust Root Certification Authority\n====================================\n-----BEGIN CERTIFICATE-----\nMIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV\nBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw\nb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG\nA1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0\nMloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu\nMTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu\nY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v\ndCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz\nA9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww\nCj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68\nj6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN\nrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw\nDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1\nMzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH\nhmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA\nA4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM\nY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa\nv52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS\nW3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0\ntHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8\n-----END CERTIFICATE-----\n\nGeoTrust Global CA\n==================\n-----BEGIN CERTIFICATE-----\nMIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK\nEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw\nMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j\nLjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo\nBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet\n8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc\nT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU\nvTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD\nAQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk\nDBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q\nzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4\nd0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2\nmqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p\nXE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm\nMw==\n-----END CERTIFICATE-----\n\nGeoTrust Universal CA\n=====================\n-----BEGIN CERTIFICATE-----\nMIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN\nR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1\nMDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu\nYy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP\nADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t\nJPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e\nRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs\n7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d\n8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V\nqnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga\nRr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB\nZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu\nKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08\nni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0\nXG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB\nhjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc\naanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2\nqaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL\noJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK\nxr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF\nKyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2\nDFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK\nxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU\np8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI\nP/rmMuGNG2+k5o7Y+SlIis5z/iw=\n-----END CERTIFICATE-----\n\nGeoTrust Universal CA 2\n=======================\n-----BEGIN CERTIFICATE-----\nMIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN\nR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0\nMDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg\nSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0\nDE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17\nj1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q\nJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a\nQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2\nWP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP\n20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn\nZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC\nSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG\n8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2\n+/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E\nBAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z\ndXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ\n4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+\nmbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq\nA1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg\nY+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP\npm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d\nFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp\ngn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm\nX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS\n-----END CERTIFICATE-----\n\nVisa eCommerce Root\n===================\n-----BEGIN CERTIFICATE-----\nMIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG\nEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug\nQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2\nWhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm\nVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv\nbW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL\nF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b\nRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0\nTP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI\n/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs\nGHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG\nMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc\nCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW\nYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz\nzkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu\nYQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt\n398znM/jra6O1I7mT1GvFpLgXPYHDw==\n-----END CERTIFICATE-----\n\nCertum Root CA\n==============\n-----BEGIN CERTIFICATE-----\nMIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK\nExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla\nFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u\nby4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x\nwS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL\nkKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ\n89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K\nUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P\nNSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+\nGXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg\nGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/\n0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS\nqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw==\n-----END CERTIFICATE-----\n\nComodo AAA Services root\n========================\n-----BEGIN CERTIFICATE-----\nMIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS\nR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg\nTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw\nMFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl\nc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV\nBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG\nC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs\ni14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW\nY19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH\nYpy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK\nIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f\nBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl\ncy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz\nLmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm\n7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\nRt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z\n8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C\n12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n-----END CERTIFICATE-----\n\nQuoVadis Root CA\n================\n-----BEGIN CERTIFICATE-----\nMIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE\nChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0\neTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz\nMTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp\ncyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD\nEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk\nJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL\nF8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL\nYzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen\nAScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w\nPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y\nZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7\nMIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj\nYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs\nZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh\nY3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW\nFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu\nBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw\nFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6\ntlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo\nfFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul\nLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x\ngI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi\n5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi\n5nrQNiOKSnQ2+Q==\n-----END CERTIFICATE-----\n\nQuoVadis Root CA 2\n==================\n-----BEGIN CERTIFICATE-----\nMIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT\nEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx\nODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM\naW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC\nDwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6\nXJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk\nlvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB\nlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy\nlZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt\n66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn\nwQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh\nD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy\nBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie\nJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud\nDgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU\na6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT\nElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv\nZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3\nUIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm\nVjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK\n+JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW\nIozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1\nWVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X\nf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II\n4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8\nVCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u\n-----END CERTIFICATE-----\n\nQuoVadis Root CA 3\n==================\n-----BEGIN CERTIFICATE-----\nMIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT\nEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx\nOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM\naW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC\nDwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg\nDhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij\nKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K\nDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv\nBNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp\np5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8\nnT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX\nMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM\nGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz\nuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT\nBgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj\nYXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0\naWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB\nBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD\nVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4\nywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE\nAxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV\nqyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s\nhvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z\nPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2\nPb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp\n8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC\nbjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu\ng/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p\nvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr\nqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=\n-----END CERTIFICATE-----\n\nSecurity Communication Root CA\n==============================\n-----BEGIN CERTIFICATE-----\nMIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP\nU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw\nHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP\nU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw\n8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM\nDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX\n5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd\nDJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2\nJChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw\nDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g\n0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a\nmCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ\ns58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ\n6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi\nFL39vmwLAw==\n-----END CERTIFICATE-----\n\nSonera Class 2 Root CA\n======================\n-----BEGIN CERTIFICATE-----\nMIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG\nU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw\nNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh\nIENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3\n/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT\ndXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG\nf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P\ntOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH\nnfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT\nXjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt\n0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI\ncbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph\nOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx\nEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH\nllpwrN9M\n-----END CERTIFICATE-----\n\nCamerfirma Chambers of Commerce Root\n====================================\n-----BEGIN CERTIFICATE-----\nMIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe\nQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i\nZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx\nNjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp\ncm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn\nMSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC\nAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU\nxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH\nNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW\nDA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV\nd9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud\nEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v\ncmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P\nAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh\nbWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD\nVR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz\naWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi\nfJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD\nL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN\nUPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n\nADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1\nerfutGWaIZDgqtCYvDi1czyL+Nw=\n-----END CERTIFICATE-----\n\nCamerfirma Global Chambersign Root\n==================================\n-----BEGIN CERTIFICATE-----\nMIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe\nQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i\nZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx\nNDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt\nYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg\nMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw\nggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J\n1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O\nby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl\n6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c\n8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/\nBAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j\naGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B\nAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj\naGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y\nZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh\nbWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA\nPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y\ngOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ\nPJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4\nIBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes\nt2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==\n-----END CERTIFICATE-----\n\nXRamp Global CA Root\n====================\n-----BEGIN CERTIFICATE-----\nMIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE\nBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj\ndXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB\ndXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx\nHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg\nU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp\ndHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu\nIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx\nfoArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE\nzG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs\nAxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry\nxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud\nEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap\noCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC\nAQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc\n/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt\nqZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n\nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz\n8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=\n-----END CERTIFICATE-----\n\nGo Daddy Class 2 CA\n===================\n-----BEGIN CERTIFICATE-----\nMIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY\nVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp\nZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG\nA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g\nRGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD\nggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv\n2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32\nqRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j\nYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY\nvLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O\nBBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o\natTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu\nMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG\nA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim\nPQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt\nI3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ\nHmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI\nLs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b\nvZ8=\n-----END CERTIFICATE-----\n\nStarfield Class 2 CA\n====================\n-----BEGIN CERTIFICATE-----\nMIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc\nU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg\nQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo\nMQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG\nA1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG\nSIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY\nbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ\nJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm\nepsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN\nF4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF\nMIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f\nhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo\nbm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g\nQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs\nafPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM\nPUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\nxy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD\nKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3\nQBFGmh95DmK/D5fs4C8fF5Q=\n-----END CERTIFICATE-----\n\nStartCom Certification Authority\n================================\n-----BEGIN CERTIFICATE-----\nMIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN\nU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu\nZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0\nNjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk\nLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg\nU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\nggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y\no4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/\nHo/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d\neMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt\n2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z\n6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ\nosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/\nuntp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc\nUjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT\n37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE\nFE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0\nY29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj\nYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH\nAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw\nOi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg\nU3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5\nLCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl\ncnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh\ncnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT\ndGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC\nAgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh\n3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm\nvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk\nfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3\nfsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ\nEoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq\nyvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl\n1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/\nlwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro\ng14=\n-----END CERTIFICATE-----\n\nTaiwan GRCA\n===========\n-----BEGIN CERTIFICATE-----\nMIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG\nEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X\nDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv\ndmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD\nggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN\nw8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5\nBtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O\n1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO\nhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov\nJ5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7\nQ3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t\nB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB\nO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8\nlSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV\nHRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2\n09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ\nTulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj\nZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2\nNe//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU\nD7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz\nDxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk\nZ6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk\n7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ\nCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy\n+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS\n-----END CERTIFICATE-----\n\nDigiCert Assured ID Root CA\n===========================\n-----BEGIN CERTIFICATE-----\nMIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw\nIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx\nMTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL\nExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO\n9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy\nUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW\n/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy\noeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf\nGHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF\n66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq\nhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc\nEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn\nSbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i\n8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe\n+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==\n-----END CERTIFICATE-----\n\nDigiCert Global Root CA\n=======================\n-----BEGIN CERTIFICATE-----\nMIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw\nHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw\nMDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3\ndy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq\nhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn\nTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5\nBmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H\n4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y\n7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB\no2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm\n8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF\nBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr\nEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt\ntep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886\nUAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\nCAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n-----END CERTIFICATE-----\n\nDigiCert High Assurance EV Root CA\n==================================\n-----BEGIN CERTIFICATE-----\nMIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw\nKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw\nMFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ\nMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu\nY2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t\nMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS\nOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3\nMRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ\nNAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe\nh10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY\nJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ\nV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp\nmyPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK\nmNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe\nvEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K\n-----END CERTIFICATE-----\n\nCertplus Class 2 Primary CA\n===========================\n-----BEGIN CERTIFICATE-----\nMIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE\nBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN\nOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy\ndHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP\nADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR\n5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ\nVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO\nYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e\ne++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME\nCDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ\nYIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t\nL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD\nP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R\nTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+\n7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW\n//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7\nl7+ijrRU\n-----END CERTIFICATE-----\n\nDST Root CA X3\n==============\n-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK\nExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X\nDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1\ncmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT\nrE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9\nUL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy\nxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d\nutolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T\nAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ\nMA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug\ndB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE\nGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw\nRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS\nfZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----\n\nDST ACES CA X6\n==============\n-----BEGIN CERTIFICATE-----\nMIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG\nEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT\nMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha\nMFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE\nCxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI\nDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa\npCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow\nGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy\nMjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud\nEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu\nY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy\ndXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU\nCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2\n5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t\nFr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq\nnExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs\nvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3\noKfN5XozNmr6mis=\n-----END CERTIFICATE-----\n\nSwissSign Gold CA - G2\n======================\n-----BEGIN CERTIFICATE-----\nMIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw\nEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN\nMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp\nc3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq\nt2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C\njCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg\nvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF\nylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR\nAiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend\njIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO\npeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR\n7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi\nGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw\nAwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64\nOfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov\nL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm\n5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr\n44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf\nMke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m\nGu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp\nmo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk\nvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf\nKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br\nNU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj\nviOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ\n-----END CERTIFICATE-----\n\nSwissSign Silver CA - G2\n========================\n-----BEGIN CERTIFICATE-----\nMIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT\nBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X\nDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3\naXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644\nN0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm\n+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH\n6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu\nMGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h\nqAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5\nFZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs\nROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc\ncelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X\nCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\nBAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB\ntjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0\ncDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P\n4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F\nkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L\n3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx\n/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa\nDGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP\ne97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu\nWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ\nDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub\nDgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u\n-----END CERTIFICATE-----\n\nGeoTrust Primary Certification Authority\n========================================\n-----BEGIN CERTIFICATE-----\nMIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG\nEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD\nZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx\nCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ\ncmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN\nb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9\nnceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge\nRwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt\ntm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\nAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI\nhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K\nTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN\nNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa\nFloxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG\n1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=\n-----END CERTIFICATE-----\n\nthawte Primary Root CA\n======================\n-----BEGIN CERTIFICATE-----\nMIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE\nBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2\naWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv\ncml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3\nMDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg\nSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv\nKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT\nFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs\noPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ\n1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc\nq/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K\naAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p\nafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD\nVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF\nAAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE\nuzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX\nxPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89\njxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH\nz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA==\n-----END CERTIFICATE-----\n\nVeriSign Class 3 Public Primary Certification Authority - G5\n============================================================\n-----BEGIN CERTIFICATE-----\nMIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE\nBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO\nZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk\nIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp\nZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB\nyjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln\nbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh\ndXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt\nYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz\nj/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD\nY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/\nArr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r\nfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/\nBAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv\nZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\naXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG\nSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+\nX6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE\nKQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC\nKm0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE\nZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\n-----END CERTIFICATE-----\n\nSecureTrust CA\n==============\n-----BEGIN CERTIFICATE-----\nMIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG\nEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy\ndXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe\nBgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC\nASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX\nOZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t\nDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH\nGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b\n01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH\nursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/\nBAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj\naHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ\nKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu\nSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf\nmbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ\nnMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR\n3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=\n-----END CERTIFICATE-----\n\nSecure Global CA\n================\n-----BEGIN CERTIFICATE-----\nMIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG\nEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH\nbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg\nMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg\nQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx\nYDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ\nbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g\n8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV\nHDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi\n0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud\nEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn\noCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA\nMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+\nOYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn\nCDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5\n3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc\nf8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW\n-----END CERTIFICATE-----\n\nCOMODO Certification Authority\n==============================\n-----BEGIN CERTIFICATE-----\nMIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE\nBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG\nA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1\ndGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb\nMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD\nT01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH\n+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww\nxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV\n4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA\n1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI\nrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E\nBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k\nb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC\nAQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP\nOGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/\nRxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc\nIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN\n+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==\n-----END CERTIFICATE-----\n\nNetwork Solutions Certificate Authority\n=======================================\n-----BEGIN CERTIFICATE-----\nMIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG\nEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr\nIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx\nMjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu\nMTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G\nCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx\njOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT\naaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT\ncrA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc\n/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB\nAAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP\nBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv\nbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA\nA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q\n4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/\nGGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv\nwKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD\nydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey\n-----END CERTIFICATE-----\n\nCOMODO ECC Certification Authority\n==================================\n-----BEGIN CERTIFICATE-----\nMIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC\nR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE\nChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB\ndXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix\nGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\nQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo\nb3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X\n4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni\nwz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E\nBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG\nFAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA\nU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=\n-----END CERTIFICATE-----\n\nSecurity Communication EV RootCA1\n=================================\n-----BEGIN CERTIFICATE-----\nMIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc\nU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh\ndGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE\nBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl\nY3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO\n/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX\nWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z\nZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4\nbepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK\n9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG\nSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm\niEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG\nAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW\nmHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW\nT1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490\n-----END CERTIFICATE-----\n\nOISTE WISeKey Global Root GA CA\n===============================\n-----BEGIN CERTIFICATE-----\nMIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE\nBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG\nA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH\nbG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD\nVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw\nIAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5\nIEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9\nNt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg\nAsj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD\nd50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ\n/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R\nLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw\nAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ\nKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm\nMMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4\n+vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa\nhNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY\nokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=\n-----END CERTIFICATE-----\n\nCertigna\n========\n-----BEGIN CERTIFICATE-----\nMIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw\nEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3\nMDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI\nQ2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q\nXOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH\nGxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p\nogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg\nDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf\nIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ\ntCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ\nBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J\nSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA\nhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+\nImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu\nPBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY\n1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw\nWyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==\n-----END CERTIFICATE-----\n\nDeutsche Telekom Root CA 2\n==========================\n-----BEGIN CERTIFICATE-----\nMIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT\nRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG\nA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5\nMjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G\nA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS\nb290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5\nbzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI\nKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY\nAUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK\nSe5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV\njlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV\nHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr\nE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy\nzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8\nrZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G\ndyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU\nCm26OWMohpLzGITY+9HPBVZkVw==\n-----END CERTIFICATE-----\n\nCybertrust Global Root\n======================\n-----BEGIN CERTIFICATE-----\nMIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li\nZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4\nMDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD\nExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n+Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW\n0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL\nAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin\n89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT\n8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP\nBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2\nMDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G\nA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO\nlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi\n5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2\nhO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T\nX3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW\nWL1WMRJOEcgh4LMRkWXbtKaIOM5V\n-----END CERTIFICATE-----\n\nePKI Root Certification Authority\n=================================\n-----BEGIN CERTIFICATE-----\nMIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG\nEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg\nUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx\nMjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq\nMCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs\nIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi\nlTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv\nqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX\n12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O\nWQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+\nETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao\nlQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/\nvv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi\nZo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi\nMAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH\nClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0\n1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq\nKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV\nxrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP\nNXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r\nGNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE\nxJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx\ngMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy\nsP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD\nBCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=\n-----END CERTIFICATE-----\n\nT\\xc3\\x9c\\x42\\xC4\\xB0TAK UEKAE K\\xC3\\xB6k Sertifika Hizmet Sa\\xC4\\x9Flay\\xc4\\xb1\\x63\\xc4\\xb1s\\xc4\\xb1 - S\\xC3\\xBCr\\xC3\\xBCm 3\n=============================================================================================================================\n-----BEGIN CERTIFICATE-----\nMIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH\nDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q\naWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry\nb25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV\nBAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg\nS8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4\nMjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl\nIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF\nn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl\nIEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft\ndSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl\ncnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO\nEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1\nxnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR\n6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL\nhmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd\nBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\nMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4\nN5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT\ny9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh\nLBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M\ndqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=\n-----END CERTIFICATE-----\n\ncertSIGN ROOT CA\n================\n-----BEGIN CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD\nVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa\nFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE\nCxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I\nJUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH\nrfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2\nssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD\n0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943\nAAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\nAf8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB\nAQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8\nSG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0\nx2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt\nvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz\nTogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD\n-----END CERTIFICATE-----\n\nGeoTrust Primary Certification Authority - G3\n=============================================\n-----BEGIN CERTIFICATE-----\nMIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE\nBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0\nIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy\neSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz\nNTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo\nYykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT\nLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j\nK/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE\nc5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C\nIShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu\ndlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC\nMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr\n2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9\ncr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE\nAp7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD\nAWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s\nt/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt\n-----END CERTIFICATE-----\n\nthawte Primary Root CA - G2\n===========================\n-----BEGIN CERTIFICATE-----\nMIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC\nVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu\nIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg\nQ0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV\nMBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG\nb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt\nIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS\nLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5\n8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU\nmtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN\nG4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K\nrr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==\n-----END CERTIFICATE-----\n\nthawte Primary Root CA - G3\n===========================\n-----BEGIN CERTIFICATE-----\nMIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE\nBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2\naWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv\ncml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w\nODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh\nd3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD\nVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG\nA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At\nP0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC\n+BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY\n7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW\nvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E\nBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ\nKoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK\nA3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu\nt8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC\n8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm\ner/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A=\n-----END CERTIFICATE-----\n\nGeoTrust Primary Certification Authority - G2\n=============================================\n-----BEGIN CERTIFICATE-----\nMIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC\nVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu\nYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD\nZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1\nOVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg\nMjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl\nb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG\nBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc\nKiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD\nVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+\nEVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m\nndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2\nnpaqBA+K\n-----END CERTIFICATE-----\n\nVeriSign Universal Root Certification Authority\n===============================================\n-----BEGIN CERTIFICATE-----\nMIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE\nBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO\nZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk\nIHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u\nIEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV\nUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv\ncmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl\nIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj\n1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP\nMiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72\n9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I\nAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR\ntPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G\nCCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O\na8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud\nDgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3\nY8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx\nY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx\nP/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P\nwGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4\nmJO37M2CYfE45k+XmCpajQ==\n-----END CERTIFICATE-----\n\nVeriSign Class 3 Public Primary Certification Authority - G4\n============================================================\n-----BEGIN CERTIFICATE-----\nMIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC\nVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3\nb3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz\nZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj\nYXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL\nMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU\ncnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo\nb3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5\nIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8\nUtpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz\nrl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB\n/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw\nHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u\nY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD\nA2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx\nAJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==\n-----END CERTIFICATE-----\n\nNetLock Arany (Class Gold) Főtanúsítvány\n========================================\n-----BEGIN CERTIFICATE-----\nMIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G\nA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610\ndsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB\ncmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx\nMjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO\nZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv\nbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6\nc8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu\n0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw\n/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk\nH3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw\nfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1\nneWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB\nBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW\nqZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta\nYtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC\nbLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna\nNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu\ndZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=\n-----END CERTIFICATE-----\n\nStaat der Nederlanden Root CA - G2\n==================================\n-----BEGIN CERTIFICATE-----\nMIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE\nCgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g\nUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC\nTkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l\nZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ\n5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn\nvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj\nCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil\ne7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR\nOME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI\nCT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65\n48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi\ntrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737\nqWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB\nAAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC\nARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV\nHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA\nA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz\n+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj\nf/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN\nkqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk\nCpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF\nURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb\nCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h\noKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV\nIPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm\n66+KAQ==\n-----END CERTIFICATE-----\n\nHongkong Post Root CA 1\n=======================\n-----BEGIN CERTIFICATE-----\nMIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT\nDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx\nNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n\nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1\nApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr\nauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh\nqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY\nV18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV\nHRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i\nh9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio\nl7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei\nIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps\nT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT\nc4afU9hDDl3WY4JxHYB0yvbiAmvZWg==\n-----END CERTIFICATE-----\n\nSecureSign RootCA11\n===================\n-----BEGIN CERTIFICATE-----\nMIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi\nSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS\nb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw\nKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1\ncmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL\nTJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO\nwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq\ng6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP\nO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA\nbpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX\nt94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh\nOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r\nbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ\nOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01\ny8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061\nlgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=\n-----END CERTIFICATE-----\n\nACEDICOM Root\n=============\n-----BEGIN CERTIFICATE-----\nMIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD\nT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4\nMDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG\nA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF\nAAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk\nWLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD\nYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew\nMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb\nm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk\nHQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT\nxKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2\n3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9\n2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq\nTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz\n4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU\n9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv\nbS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg\naHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP\neGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk\nzQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1\nThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI\nKiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq\nnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE\nI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp\nMCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o\ntkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==\n-----END CERTIFICATE-----\n\nMicrosec e-Szigno Root CA 2009\n==============================\n-----BEGIN CERTIFICATE-----\nMIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER\nMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv\nc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o\ndTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE\nBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt\nU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA\nfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG\n0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA\npxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm\n1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC\nAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf\nQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE\nFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o\nlZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX\nI/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775\ntyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02\nyULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi\nLXpUq3DDfSJlgnCW\n-----END CERTIFICATE-----\n\nGlobalSign Root CA - R3\n=======================\n-----BEGIN CERTIFICATE-----\nMIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv\nYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh\nbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT\naWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln\nbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt\niHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ\n0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3\nrHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl\nOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2\nxmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE\nFI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7\nlgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8\nEpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E\nbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18\nYIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r\nkpeDMdmztcpHWD9f\n-----END CERTIFICATE-----\n\nAutoridad de Certificacion Firmaprofesional CIF A62634068\n=========================================================\n-----BEGIN CERTIFICATE-----\nMIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA\nBgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2\nMjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw\nQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB\nNjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD\nUtd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P\nB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY\n7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH\nECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI\nplD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX\nMbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX\nLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK\nbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU\nvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud\nEwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH\nDhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp\ncm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA\nbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx\nADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx\n51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk\nR71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP\nT481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f\nJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl\nosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR\ncrHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR\nsaS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD\nKCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi\n6Et8Vcad+qMUu2WFbm5PEn4KPJ2V\n-----END CERTIFICATE-----\n\nIzenpe.com\n==========\n-----BEGIN CERTIFICATE-----\nMIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG\nEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz\nMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu\nQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ\n03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK\nClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU\n+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC\nPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT\nOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK\nF7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK\n0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+\n0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB\nleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID\nAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+\nSVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG\nNjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx\nMCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O\nBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l\nFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga\nkEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q\nhT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs\ng1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5\naTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5\nnXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC\nClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo\nQ0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z\nWrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==\n-----END CERTIFICATE-----\n\nChambers of Commerce Root - 2008\n================================\n-----BEGIN CERTIFICATE-----\nMIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD\nMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv\nbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu\nQS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy\nMjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl\nZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF\nEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl\ncnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\nAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA\nXuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj\nh40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/\nikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk\nNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g\nD2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331\nlubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ\n0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj\nya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2\nEQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI\nG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ\nBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh\nbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh\nbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC\nCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH\nAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1\nwqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH\n3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU\nRWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6\nM6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1\nYJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF\n9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK\nzBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG\nnrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg\nOGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ\n-----END CERTIFICATE-----\n\nGlobal Chambersign Root - 2008\n==============================\n-----BEGIN CERTIFICATE-----\nMIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD\nMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv\nbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu\nQS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx\nNDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg\nY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ\nQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD\naGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf\nVtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf\nXjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0\nZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB\n/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA\nTH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M\nH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe\nOx2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF\nHTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh\nwZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB\nAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT\nBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE\nBhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm\naXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm\naXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp\n1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0\ndHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG\n/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6\nReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s\ndZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg\n9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH\nfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du\nqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr\nP3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq\nc5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z\n09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B\n-----END CERTIFICATE-----\n\nGo Daddy Root Certificate Authority - G2\n========================================\n-----BEGIN CERTIFICATE-----\nMIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT\nB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu\nMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5\nMDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\nb25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G\nA1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq\n9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD\n+qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd\nfMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl\nNAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC\nMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9\nBUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac\nvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r\n5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV\nN8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO\nLPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1\n-----END CERTIFICATE-----\n\nStarfield Root Certificate Authority - G2\n=========================================\n-----BEGIN CERTIFICATE-----\nMIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT\nB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s\nb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0\neSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw\nDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg\nVGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB\ndXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv\nW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs\nbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk\nN3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf\nZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU\nJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\nAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol\nTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx\n4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw\nF5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\npL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ\nc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n-----END CERTIFICATE-----\n\nStarfield Services Root Certificate Authority - G2\n==================================================\n-----BEGIN CERTIFICATE-----\nMIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT\nB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s\nb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl\nIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV\nBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT\ndGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg\nUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2\nh/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa\nhHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP\nLJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB\nrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw\nAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG\nSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP\nE95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy\nxQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd\niEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza\nYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6\n-----END CERTIFICATE-----\n\nAffirmTrust Commercial\n======================\n-----BEGIN CERTIFICATE-----\nMIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS\nBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw\nMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly\nbVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb\nDuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV\nC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6\nBfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww\nMmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV\nHQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\nAQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG\nhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi\nqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv\n0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh\nsUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=\n-----END CERTIFICATE-----\n\nAffirmTrust Networking\n======================\n-----BEGIN CERTIFICATE-----\nMIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS\nBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw\nMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly\nbVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE\nHi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI\ndIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24\n/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb\nh+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV\nHQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\nAQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu\nUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6\n12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23\nWJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9\n/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=\n-----END CERTIFICATE-----\n\nAffirmTrust Premium\n===================\n-----BEGIN CERTIFICATE-----\nMIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS\nBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy\nOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy\ndXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A\nMIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn\nBKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV\n5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs\n+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd\nGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R\np9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI\nS+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04\n6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5\n/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo\n+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB\n/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv\nMiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg\nNt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC\n6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S\nL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK\n+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV\nBtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg\nIxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60\ng2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb\nzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==\n-----END CERTIFICATE-----\n\nAffirmTrust Premium ECC\n=======================\n-----BEGIN CERTIFICATE-----\nMIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV\nBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx\nMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U\ncnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ\nN8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW\nBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK\nBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X\n57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM\neQ==\n-----END CERTIFICATE-----\n\nCertum Trusted Network CA\n=========================\n-----BEGIN CERTIFICATE-----\nMIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK\nExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv\nbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy\nMTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU\nZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5\nMSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC\nl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J\nJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4\nfOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0\ncvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw\nDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj\njSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1\nmS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj\nZt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI\n03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=\n-----END CERTIFICATE-----\n\nCertinomis - Autorité Racine\n============================\n-----BEGIN CERTIFICATE-----\nMIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK\nQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg\nLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG\nA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw\nJAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD\nggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa\nwE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly\nLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw\n2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N\njMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q\nc1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC\nlrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb\nxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g\n530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna\n4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\nA1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ\nKoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x\nWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva\nR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40\nnJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B\nCxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv\nJL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE\nqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b\nWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE\nwk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/\nvgt2Fl43N+bYdJeimUV5\n-----END CERTIFICATE-----\n\nTWCA Root Certification Authority\n=================================\n-----BEGIN CERTIFICATE-----\nMIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ\nVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG\nEwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB\nIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx\nQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC\noi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP\n4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r\ny+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB\nBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG\n9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC\nmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW\nQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY\nT0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny\nYh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==\n-----END CERTIFICATE-----\n\nSecurity Communication RootCA2\n==============================\n-----BEGIN CERTIFICATE-----\nMIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc\nU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh\ndGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC\nSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy\naXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++\n+T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R\n3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV\nspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K\nEOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8\nQIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB\nCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj\nu/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk\n3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q\ntnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29\nmvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03\n-----END CERTIFICATE-----\n\nEC-ACC\n======\n-----BEGIN CERTIFICATE-----\nMIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE\nBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w\nODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD\nVQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE\nCxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT\nBkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7\nMDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt\nSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl\nZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh\ncnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK\nw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT\nae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4\nHvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a\nE9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw\n0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E\nBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD\nVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0\nLm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l\ndC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ\nlF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa\nAl6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe\nl+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2\nE/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D\n5EI=\n-----END CERTIFICATE-----\n\nHellenic Academic and Research Institutions RootCA 2011\n=======================================================\n-----BEGIN CERTIFICATE-----\nMIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT\nO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y\naXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z\nIFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT\nAkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z\nIENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo\nIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI\n1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa\n71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u\n8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH\n3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/\nMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8\nMAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu\nb3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt\nXdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8\nTqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD\n/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N\n7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4\n-----END CERTIFICATE-----\n\nActalis Authentication Root CA\n==============================\n-----BEGIN CERTIFICATE-----\nMIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM\nBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE\nAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky\nMjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz\nIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290\nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ\nwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa\nby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6\nzfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f\nYVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2\noxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l\nEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7\nhNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8\nEBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5\njF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY\niDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt\nifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI\nWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0\nJZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx\nK3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+\nXlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC\n4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo\n2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz\nlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem\nOR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9\nvwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==\n-----END CERTIFICATE-----\n\nTrustis FPS Root CA\n===================\n-----BEGIN CERTIFICATE-----\nMIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG\nEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290\nIENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV\nBAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ\nRUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk\nH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa\ncY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt\no3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA\nAaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd\nBgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c\nGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC\nyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P\n8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV\nl/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl\niB6XzCGcKQENZetX2fNXlrtIzYE=\n-----END CERTIFICATE-----\n\nStartCom Certification Authority\n================================\n-----BEGIN CERTIFICATE-----\nMIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN\nU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu\nZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0\nNjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk\nLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg\nU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\nggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y\no4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/\nHo/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d\neMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt\n2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z\n6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ\nosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/\nuntp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc\nUjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT\n37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD\nVR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ\nQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0\ndHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu\nc3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv\nbW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0\naGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0\naW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t\nL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG\ncmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5\nfPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm\nN3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN\nOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T\ntn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX\ne2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA\n2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs\nHvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE\nJnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib\nD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8=\n-----END CERTIFICATE-----\n\nStartCom Certification Authority G2\n===================================\n-----BEGIN CERTIFICATE-----\nMIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN\nU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg\nRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE\nChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp\ndHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O\no1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG\n4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi\nAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul\nQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs\nO+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H\nvKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L\nnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS\nFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa\nz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E\nBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ\nKoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K\n2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk\nJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+\nJYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG\n/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc\nnIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld\nblhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc\nl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm\n7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm\nobp573PYtlNXLfbQ4ddI\n-----END CERTIFICATE-----\n\nBuypass Class 2 Root CA\n=======================\n-----BEGIN CERTIFICATE-----\nMIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU\nQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X\nDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1\neXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1\ng1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn\n9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b\n/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU\nCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff\nawrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI\nzRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn\nBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX\nUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs\nM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD\nVR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF\nAAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s\nA20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI\nosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S\naq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd\nDnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD\nLfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0\noyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC\nwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS\nCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN\nrJgWVqA=\n-----END CERTIFICATE-----\n\nBuypass Class 3 Root CA\n=======================\n-----BEGIN CERTIFICATE-----\nMIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU\nQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X\nDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1\neXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH\nsJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR\n5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh\n7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ\nZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH\n2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV\n/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ\nRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA\nXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq\nj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD\nVR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF\nAAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV\ncSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G\nuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG\nQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8\nZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2\nKSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz\n6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug\nUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe\neOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi\nCp/HuZc=\n-----END CERTIFICATE-----\n\nT-TeleSec GlobalRoot Class 3\n============================\n-----BEGIN CERTIFICATE-----\nMIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM\nIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU\ncnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx\nMDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz\ndGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD\nZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3\nDQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK\n9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU\nNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF\niP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W\n0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA\nMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr\nAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb\nfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT\nucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h\nP0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml\ne9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==\n-----END CERTIFICATE-----\n\nEE Certification Centre Root CA\n===============================\n-----BEGIN CERTIFICATE-----\nMIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG\nEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy\ndGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw\nMTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB\nUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy\nZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM\nTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2\nrpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw\n93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN\nP2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T\nAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ\nMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF\nBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj\nxY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM\nlIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u\nuSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU\n3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM\ndcGWxZ0=\n-----END CERTIFICATE-----\n\nTURKTRUST Certificate Services Provider Root 2007\n=================================================\n-----BEGIN CERTIFICATE-----\nMIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF\nbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP\nMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg\nQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X\nDTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl\na3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN\nBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp\nbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N\nYvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv\nKUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya\nKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT\nrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC\nAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP\nBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s\nPx+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I\naE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO\nXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb\nBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK\npoRq0Tl9\n-----END CERTIFICATE-----\n\nD-TRUST Root Class 3 CA 2 2009\n==============================\n-----BEGIN CERTIFICATE-----\nMIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK\nDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe\nFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE\nLVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD\nER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA\nBF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv\nKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z\np+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC\nAwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ\n4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y\neS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw\nMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G\nPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw\nOS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm\n2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0\no3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV\ndT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph\nX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=\n-----END CERTIFICATE-----\n\nD-TRUST Root Class 3 CA 2 EV 2009\n=================================\n-----BEGIN CERTIFICATE-----\nMIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK\nDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw\nOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK\nDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw\nOTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS\negpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh\nzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T\n7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60\nsUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35\n11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv\ncop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v\nZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El\nMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp\nb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh\nc3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+\nPPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05\nnsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX\nANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA\nNCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv\nw9y4AyHqnxbxLFS1\n-----END CERTIFICATE-----\n\nPSCProcert\n==========\n-----BEGIN CERTIFICATE-----\nMIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk\nZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ\nMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz\ndGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl\ncmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw\nIwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw\nMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w\nDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD\nZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp\nY2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC\nwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA\n3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh\nRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO\nEO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2\n0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH\n0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU\ntd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw\nBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp\nr2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/\nAgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz\nNi0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId\nxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp\nZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH\nEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h\nY2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k\nZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG\n9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG\nMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG\nLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52\nZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy\nYWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v\nY3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o\ndHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq\nT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN\ng7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q\nuxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1\nn8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn\nFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo\n5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq\n3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5\npoLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y\neMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km\n-----END CERTIFICATE-----\n\nCA Disig Root R1\n================\n-----BEGIN CERTIFICATE-----\nMIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw\nEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp\nZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx\nEzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp\nc2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy\n3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8\nu8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2\nm6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk\nCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa\nYVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6\nvpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL\nLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX\nZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is\nXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV\nHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ\n04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR\nxVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B\nLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM\nCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb\nVSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85\nYmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS\nds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix\nlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N\nUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ\na7+h89n07eLw4+1knj0vllJPgFOL\n-----END CERTIFICATE-----\n\nCA Disig Root R2\n================\n-----BEGIN CERTIFICATE-----\nMIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw\nEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp\nZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx\nEzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp\nc2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC\nw3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia\nxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7\nA7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S\nGBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV\ng8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa\n5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE\nkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A\nAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i\nFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV\nHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u\nQu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM\ntCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV\nsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je\ndR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8\n1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx\nmHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01\nutI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0\nsorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg\nUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV\n7+ZtsH8tZ/3zbBt1RqPlShfppNcL\n-----END CERTIFICATE-----\n\nACCVRAIZ1\n=========\n-----BEGIN CERTIFICATE-----\nMIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB\nSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1\nMDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH\nUEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC\nDwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM\njmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0\nRGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD\naaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ\n0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG\nWuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7\n8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR\n5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J\n9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK\nQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw\nOi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu\nY3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2\nVuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM\nHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA\nQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh\nAO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA\nYwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj\nAHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA\nIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk\naHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0\ndHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2\nMV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI\nhvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E\nR9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN\nYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49\nnCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ\nTS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3\nsCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h\nI6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg\nNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd\n3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p\nEfbRD0tVNEYqi4Y7\n-----END CERTIFICATE-----\n\nTWCA Global Root CA\n===================\n-----BEGIN CERTIFICATE-----\nMIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT\nCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD\nQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK\nEwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg\nQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C\nnJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV\nr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR\nQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV\ntTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W\nKKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99\nsy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p\nyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn\nkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI\nzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC\nAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g\ncFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn\nLhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M\n8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg\n/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg\nlPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP\nA9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m\ni4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8\nEHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3\nzqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=\n-----END CERTIFICATE-----\n\nTeliaSonera Root CA v1\n======================\n-----BEGIN CERTIFICATE-----\nMIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE\nCgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4\nMTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW\nVGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+\n6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA\n3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k\nB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn\nXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH\noLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3\nF0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ\noWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7\ngUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc\nTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB\nAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW\nDNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm\nzqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx\n0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW\npb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV\nG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc\nc41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT\nJsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2\nqReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6\nY2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems\nWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=\n-----END CERTIFICATE-----\n\nE-Tugra Certification Authority\n===============================\n-----BEGIN CERTIFICATE-----\nMIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w\nDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls\nZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN\nZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw\nNTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx\nQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl\ncmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD\nDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A\nMIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd\nhQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K\nCKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g\nElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ\nBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0\nE+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz\nrt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq\njqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn\nrFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5\ndUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB\n/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG\nMA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK\nkEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO\nXKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807\nVRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo\na2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc\ndlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV\nKV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT\nDx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0\n8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G\nC7TbO6Orb1wdtn7os4I07QZcJA==\n-----END CERTIFICATE-----\n\nT-TeleSec GlobalRoot Class 2\n============================\n-----BEGIN CERTIFICATE-----\nMIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM\nIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU\ncnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx\nMDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz\ndGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD\nZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3\nDQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ\nSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F\nvudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970\n2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV\nWOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA\nMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy\nYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4\nr6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf\nvNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR\n3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN\n9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==\n-----END CERTIFICATE-----\n\nAtos TrustedRoot 2011\n=====================\n-----BEGIN CERTIFICATE-----\nMIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU\ncnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4\nMzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG\nA1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV\nhTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr\n54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+\nDgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320\nHLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR\nz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R\nl+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ\nbNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB\nCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h\nk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh\nTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9\n61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G\n3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed\n-----END CERTIFICATE-----\n\nQuoVadis Root CA 1 G3\n=====================\n-----BEGIN CERTIFICATE-----\nMIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG\nA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv\nb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN\nMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg\nRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE\nPBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm\nPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6\nPser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN\nofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l\ng6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV\n7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX\n9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f\niyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg\nt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\nAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI\nhvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC\nMTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3\nGPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct\nTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP\n+V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh\n3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa\nwx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6\nO0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0\nFU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV\nhMJKzRwuJIczYOXD\n-----END CERTIFICATE-----\n\nQuoVadis Root CA 2 G3\n=====================\n-----BEGIN CERTIFICATE-----\nMIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG\nA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv\nb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN\nMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg\nRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh\nZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY\nNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t\noIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o\nMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l\nV0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo\nL1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ\nsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD\n6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh\nlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\nAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI\nhvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66\nAarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K\npVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9\nx52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz\ndWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X\nU/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw\nmNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD\nzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN\nJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr\nO3jtZsSOeWmD3n+M\n-----END CERTIFICATE-----\n\nQuoVadis Root CA 3 G3\n=====================\n-----BEGIN CERTIFICATE-----\nMIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG\nA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv\nb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN\nMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg\nRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286\nIxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL\nMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe\n6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3\nI4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U\nVDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7\n5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi\nMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM\ndyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt\nrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\nAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI\nhvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px\nKGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS\nt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ\nTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du\nDcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib\nIh6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD\nhPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX\n0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW\ndSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2\nPpxxVJkES/1Y+Zj0\n-----END CERTIFICATE-----\n\nDigiCert Assured ID Root G2\n===========================\n-----BEGIN CERTIFICATE-----\nMIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw\nIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw\nMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL\nExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH\n35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq\nbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw\nVWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP\nYLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn\nlTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO\nw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv\n0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz\nd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW\nhsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M\njomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo\nIhNzbM8m9Yop5w==\n-----END CERTIFICATE-----\n\nDigiCert Assured ID Root G3\n===========================\n-----BEGIN CERTIFICATE-----\nMIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV\nUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD\nVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1\nMTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ\nBgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb\nRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs\nKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF\nUaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy\nYZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy\n1vUhZscv6pZjamVFkpUBtA==\n-----END CERTIFICATE-----\n\nDigiCert Global Root G2\n=======================\n-----BEGIN CERTIFICATE-----\nMIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw\nHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx\nMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3\ndy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq\nhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ\nkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO\n3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV\nBJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM\nUNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB\no0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu\n5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr\nF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U\nWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH\nQRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/\niyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\nMrY=\n-----END CERTIFICATE-----\n\nDigiCert Global Root G3\n=======================\n-----BEGIN CERTIFICATE-----\nMIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV\nUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD\nVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw\nMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k\naWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C\nAQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O\nYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP\nBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp\nYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y\n3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34\nVOKa5Vt8sycX\n-----END CERTIFICATE-----\n\nDigiCert Trusted Root G4\n========================\n-----BEGIN CERTIFICATE-----\nMIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw\nHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1\nMTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G\nCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp\npz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o\nk3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa\nvOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY\nQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6\nMUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm\nmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7\nf/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH\ndL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8\noR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud\nDwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD\nggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY\nZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr\nyF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy\n7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah\nixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN\n5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb\n/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa\n5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK\nG48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP\n82Z+\n-----END CERTIFICATE-----\n\nWoSign\n======\n-----BEGIN CERTIFICATE-----\nMIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQG\nEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNVBAMTIUNlcnRpZmljYXRpb24g\nQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJ\nBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA\nvcqNrLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1UfcIiePyO\nCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcSccf+Hb0v1naMQFXQoOXXDX\n2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2ZjC1vt7tj/id07sBMOby8w7gLJKA84X5\nKIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4Mx1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR\n+ScPewavVIMYe+HdVHpRaG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ez\nEC8wQjchzDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDaruHqk\nlWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221KmYo0SLwX3OSACCK2\n8jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvASh0JWzko/amrzgD5LkhLJuYwTKVY\nyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWvHYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0C\nAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R\n8bNLtwYgFP6HEtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1\nLOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJMuYhOZO9sxXq\nT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2eJXLOC62qx1ViC777Y7NhRCOj\ny+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VNg64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC\n2nz4SNAzqfkHx5Xh9T71XXG68pWpdIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes\n5cVAWubXbHssw1abR80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/\nEaEQPkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGcexGATVdVh\nmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+J7x6v+Db9NpSvd4MVHAx\nkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMlOtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGi\nkpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWTee5Ehr7XHuQe+w==\n-----END CERTIFICATE-----\n\nWoSign China\n============\n-----BEGIN CERTIFICATE-----\nMIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQG\nEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMMEkNBIOayg+mAmuagueiv\ngeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYD\nVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjAN\nBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k\n8H/rD195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld19AXbbQs5\nuQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExfv5RxadmWPgxDT74wwJ85\ndE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnkUkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5\nEd+w4VegG63XX9Gv2ystP9Bojg/qnw+LNVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFy\nb7Ao65vh4YOhn0pdr8yb+gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc\n76DbT52VqyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6KyX2m\n+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0GAbQOXDBGVWCvOGU6\nyke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaKJ/kR8slC/k7e3x9cxKSGhxYzoacX\nGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud\nEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUA\nA4ICAQBqinA4WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6\nyAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj/feTZU7n85iY\nr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6jBAyvd0zaziGfjk9DgNyp115\nj0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0A\nkLppRQjbbpCBhqcqBT/mhDn4t/lXX0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97\nqA4bLJyuQHCH2u2nFoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Y\njj4Du9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10lO1Hm13ZB\nONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Leie2uPAmvylezkolwQOQv\nT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR12KvxAmLBsX5VYc8T1yaw15zLKYs4SgsO\nkI26oQ==\n-----END CERTIFICATE-----\n\nCOMODO RSA Certification Authority\n==================================\n-----BEGIN CERTIFICATE-----\nMIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE\nBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG\nA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv\nbiBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC\nR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE\nChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB\ndXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn\ndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ\nFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+\n5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG\nx8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX\n2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL\nOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3\nsgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C\nGCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5\nWdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\nFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt\nrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+\nnq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg\ntZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW\nsRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp\npC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA\nzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq\nZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52\n7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I\nLaZRfyHBNVOFBkpdn627G190\n-----END CERTIFICATE-----\n\nUSERTrust RSA Certification Authority\n=====================================\n-----BEGIN CERTIFICATE-----\nMIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE\nBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK\nExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE\nBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK\nExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz\n0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j\nY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn\nRghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O\n+T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq\n/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE\nY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM\nlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8\nyexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+\neLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\nBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\nMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW\nFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ\n7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ\nEg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM\n8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi\nFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi\nyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c\nJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw\nsAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx\nQ+6IHdfGjjxDah2nGN59PRbxYvnKkKj9\n-----END CERTIFICATE-----\n\nUSERTrust ECC Certification Authority\n=====================================\n-----BEGIN CERTIFICATE-----\nMIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC\nVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\naGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv\nbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC\nVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\naGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv\nbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2\n0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez\nnPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV\nHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB\nHU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu\n9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=\n-----END CERTIFICATE-----\n\nGlobalSign ECC Root CA - R4\n===========================\n-----BEGIN CERTIFICATE-----\nMIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb\nR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD\nEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb\nR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD\nEwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl\nOQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P\nAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV\nMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF\nJzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q=\n-----END CERTIFICATE-----\n\nGlobalSign ECC Root CA - R5\n===========================\n-----BEGIN CERTIFICATE-----\nMIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb\nR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD\nEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb\nR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD\nEwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6\nSFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS\nh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd\nBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx\nuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7\nyFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3\n-----END CERTIFICATE-----\n\nStaat der Nederlanden Root CA - G3\n==================================\n-----BEGIN CERTIFICATE-----\nMIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE\nCgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g\nUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC\nTkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l\nZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y\nolQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t\nx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy\nEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K\nTj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur\nmkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5\n1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp\n07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo\nFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE\n41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB\nAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu\nyjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD\nU5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq\nKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1\nv0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA\n8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b\n8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r\nmj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq\n1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI\nJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV\ntzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk=\n-----END CERTIFICATE-----\n\nStaat der Nederlanden EV Root CA\n================================\n-----BEGIN CERTIFICATE-----\nMIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE\nCgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g\nRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M\nMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl\ncmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk\nSzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW\nO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r\n0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8\nKj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV\nXJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr\n08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV\n0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd\n74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx\nfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC\nMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa\nivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI\neK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu\nc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq\n5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN\nb/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN\nf1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi\n5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4\nWeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK\nDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy\neUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==\n-----END CERTIFICATE-----\n\nIdenTrust Commercial Root CA 1\n==============================\n-----BEGIN CERTIFICATE-----\nMIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG\nEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS\nb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES\nMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB\nIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld\nhNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/\nmNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi\n1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C\nXZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl\n3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy\nNeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV\nWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg\nxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix\nuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC\nAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI\nhvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH\n6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg\nghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt\nozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV\nYjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX\nfeu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro\nkTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe\n2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz\nZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R\ncGzM7vRX+Bi6hG6H\n-----END CERTIFICATE-----\n\nIdenTrust Public Sector Root CA 1\n=================================\n-----BEGIN CERTIFICATE-----\nMIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG\nEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv\nciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV\nUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS\nb290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy\nP4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6\nHi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI\nrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf\nqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS\nmJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn\nol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh\nLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v\niDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL\n4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B\nAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw\nDQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj\nt2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A\nmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt\nGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt\nm6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx\nNRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4\nMhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI\najjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC\nZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ\n3Wl9af0AVqW3rLatt8o+Ae+c\n-----END CERTIFICATE-----\n\nEntrust Root Certification Authority - G2\n=========================================\n-----BEGIN CERTIFICATE-----\nMIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV\nBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy\nbXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug\nb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw\nHhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT\nDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx\nOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s\neTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi\nMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP\n/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz\nHHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU\ns/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y\nTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx\nAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6\n0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z\niXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ\nRkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi\nnWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+\nvGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO\ne4pIb4tF9g==\n-----END CERTIFICATE-----\n\nEntrust Root Certification Authority - EC1\n==========================================\n-----BEGIN CERTIFICATE-----\nMIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx\nFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn\nYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl\nZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5\nIC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw\nFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs\nLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg\ndXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt\nIEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy\nAsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef\n9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE\nFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h\nvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8\nkmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G\n-----END CERTIFICATE-----\n\nCFCA EV ROOT\n============\n-----BEGIN CERTIFICATE-----\nMIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE\nCgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB\nIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw\nMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD\nDAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV\nBU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD\n7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN\nuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW\nZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7\nxzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f\npy25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K\ngWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol\nhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ\ntqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf\nBgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB\n/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB\nACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q\necsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua\n4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG\nE5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX\nBDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn\naH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy\nPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX\nkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C\nekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su\n-----END CERTIFICATE-----\n\nTÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5\n====================================================\n-----BEGIN CERTIFICATE-----\nMIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN\nBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp\nbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1Qg\nRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAw\nODA3MDFaFw0yMzA0MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0w\nSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnE\nn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRp\nZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537\njVJp45wnEFPzpALFp/kRGml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1m\nep5Fimh34khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z5UNP\n9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0hO8EuPbJbKoCPrZV\n4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QIDAQABo0IwQDAdBgNVHQ4EFgQUVpkH\nHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\nhvcNAQELBQADggEBAJ5FdnsXSDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPo\nBP5yCccLqh0lVX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq\nURawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nfpeYVhDfwwvJl\nlpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8\nB59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=\n-----END CERTIFICATE-----\n\nCertinomis - Root CA\n====================\n-----BEGIN CERTIFICATE-----\nMIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK\nQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg\nLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx\nEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD\nZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos\nP5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo\nd5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap\nz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00\n8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x\nRLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE\n6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t\nFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV\nPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH\ni5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj\nYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I\n6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF\nAAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV\nWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw\nPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX\nlCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ\ny29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9\nIff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng\nDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi\nI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM\ncyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr\nhkIGuUE=\n-----END CERTIFICATE-----\n\nOISTE WISeKey Global Root GB CA\n===============================\n-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG\nEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl\nZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw\nMzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD\nVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds\nb2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX\nscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP\nrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk\n9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o\nQnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg\nGUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB\n/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI\nhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD\ndHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0\nVQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui\nHZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic\nNc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=\n-----END CERTIFICATE-----\n\nCertification Authority of WoSign G2\n====================================\n-----BEGIN CERTIFICATE-----\nMIIDfDCCAmSgAwIBAgIQayXaioidfLwPBbOxemFFRDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQG\nEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxLTArBgNVBAMTJENlcnRpZmljYXRpb24g\nQXV0aG9yaXR5IG9mIFdvU2lnbiBHMjAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMFgx\nCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsGA1UEAxMkQ2VydGlm\naWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWduIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEAvsXEoCKASU+/2YcRxlPhuw+9YH+v9oIOH9ywjj2X4FA8jzrvZjtFB5sg+OPXJYY1kBai\nXW8wGQiHC38Gsp1ij96vkqVg1CuAmlI/9ZqD6TRay9nVYlzmDuDfBpgOgHzKtB0TiGsOqCR3A9Du\nW/PKaZE1OVbFbeP3PU9ekzgkyhjpJMuSA93MHD0JcOQg5PGurLtzaaNjOg9FD6FKmsLRY6zLEPg9\n5k4ot+vElbGs/V6r+kHLXZ1L3PR8du9nfwB6jdKgGlxNIuG12t12s9R23164i5jIFFTMaxeSt+BK\nv0mUYQs4kI9dJGwlezt52eJ+na2fmKEG/HgUYFf47oB3sQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC\nAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU+mCp62XF3RYUCE4MD42b4Pdkr2cwDQYJKoZI\nhvcNAQELBQADggEBAFfDejaCnI2Y4qtAqkePx6db7XznPWZaOzG73/MWM5H8fHulwqZm46qwtyeY\nP0nXYGdnPzZPSsvxFPpahygc7Y9BMsaV+X3avXtbwrAh449G3CE4Q3RM+zD4F3LBMvzIkRfEzFg3\nTgvMWvchNSiDbGAtROtSjFA9tWwS1/oJu2yySrHFieT801LYYRf+epSEj3m2M1m6D8QL4nCgS3gu\n+sif/a+RZQp4OBXllxcU3fngLDT4ONCEIgDAFFEYKwLcMFrw6AF8NTojrwjkr6qOKEJJLvD1mTS+\n7Q9LGOHSJDy7XUe3IfKN0QqZjuNuPq1w4I+5ysxugTH2e5x6eeRncRg=\n-----END CERTIFICATE-----\n\nCA WoSign ECC Root\n==================\n-----BEGIN CERTIFICATE-----\nMIICCTCCAY+gAwIBAgIQaEpYcIBr8I8C+vbe6LCQkDAKBggqhkjOPQQDAzBGMQswCQYDVQQGEwJD\nTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMTEkNBIFdvU2lnbiBFQ0MgUm9v\ndDAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQK\nExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAxMSQ0EgV29TaWduIEVDQyBSb290MHYwEAYHKoZI\nzj0CAQYFK4EEACIDYgAE4f2OuEMkq5Z7hcK6C62N4DrjJLnSsb6IOsq/Srj57ywvr1FQPEd1bPiU\nt5v8KB7FVMxjnRZLU8HnIKvNrCXSf4/CwVqCXjCLelTOA7WRf6qU0NGKSMyCBSah1VES1ns2o0Iw\nQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqv3VWqP2h4syhf3R\nMluARZPzA7gwCgYIKoZIzj0EAwMDaAAwZQIxAOSkhLCB1T2wdKyUpOgOPQB0TKGXa/kNUTyh2Tv0\nDaupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu\na/GRspBl9JrmkO5K\n-----END CERTIFICATE-----\n\nSZAFIR ROOT CA2\n===============\n-----BEGIN CERTIFICATE-----\nMIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG\nA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV\nBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ\nBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD\nVQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q\nqEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK\nDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE\n2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ\nckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi\nieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P\nAQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC\nAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5\nO/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67\noPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul\n4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6\n+/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw==\n-----END CERTIFICATE-----\n\nCertum Trusted Network CA 2\n===========================\n-----BEGIN CERTIFICATE-----\nMIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE\nBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1\nbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y\nayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ\nTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl\ncnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB\nIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9\n7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o\nCgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b\nRr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p\nuTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130\nGO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ\n9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB\nRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye\nhizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM\nBhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\nAQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI\nhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW\nAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA\nL55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo\nclm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM\npkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb\nw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo\nJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm\nypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX\nis7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7\nzAYspsbiDrW5viSP\n-----END CERTIFICATE-----\n\nHellenic Academic and Research Institutions RootCA 2015\n=======================================================\n-----BEGIN CERTIFICATE-----\nMIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT\nBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0\naW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl\nYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx\nMTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg\nQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV\nBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw\nMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv\nbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh\niGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+\n6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd\nFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr\ni5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F\nGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2\nfu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu\niNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc\nBw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\nAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI\nhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+\nD1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM\nd/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y\nd+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn\n82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb\ndavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F\nJej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt\nJ94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa\nJI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q\np/UsQu0yrbYhnr68\n-----END CERTIFICATE-----\n\nHellenic Academic and Research Institutions ECC RootCA 2015\n===========================================================\n-----BEGIN CERTIFICATE-----\nMIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0\naGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u\ncyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj\naCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw\nMzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj\nIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD\nVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290\nQ0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP\ndJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK\nVlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O\nBBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA\nGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn\ndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR\n-----END CERTIFICATE-----\n\nCertplus Root CA G1\n===================\n-----BEGIN CERTIFICATE-----\nMIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV\nBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe\nFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD\nZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD\nggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN\nr49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx\nQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj\nBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv\nLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2\nz4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc\n4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd\n4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj\njy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+\nob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G\nA1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY\nlwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh\n66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG\nYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/\n2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F\n6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX\nCNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe\ntUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC\nVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/\n+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+\nqsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=\n-----END CERTIFICATE-----\n\nCertplus Root CA G2\n===================\n-----BEGIN CERTIFICATE-----\nMIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT\nAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x\nNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0\ncGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA\nBM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN\nAm8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD\nAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud\nIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV\nHdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl\nvPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw==\n-----END CERTIFICATE-----\n\nOpenTrust Root CA G1\n====================\n-----BEGIN CERTIFICATE-----\nMIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV\nBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx\nMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM\nCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa\nYp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87\nay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO\nYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9\nxNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO\n9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq\n3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi\nn6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9\nURQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr\nTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px\nN3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E\nPLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv\nuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK\nn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh\nX4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80\nnR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm\nGS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/\nbw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o\n4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA\nOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx\n-----END CERTIFICATE-----\n\nOpenTrust Root CA G2\n====================\n-----BEGIN CERTIFICATE-----\nMIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV\nBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy\nMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM\nCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+\nNtmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz\n4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV\neYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt\nUdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz\n3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj\n3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz\n9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0\n0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT\ny3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59\nM4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz\nGj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI\nmUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG\nS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp\nEndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ\n6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr\ngCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo\nSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0\nYJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm\nu7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK\n-----END CERTIFICATE-----\n\nOpenTrust Root CA G3\n====================\n-----BEGIN CERTIFICATE-----\nMIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT\nAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X\nDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w\nZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B\nta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB\n/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf\nBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM\nBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta\n3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB\n-----END CERTIFICATE-----\n\nISRG Root X1\n============\n-----BEGIN CERTIFICATE-----\nMIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE\nBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD\nEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG\nEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT\nDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r\nVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1\n3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K\nb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN\nAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ\n4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf\n1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu\nhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH\nusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r\nOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G\nA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY\n9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\nubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV\n0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt\nhDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw\nTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx\ne5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA\nJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD\nYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n\nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ\nm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n-----END CERTIFICATE-----\n\nAC RAIZ FNMT-RCM\n================\n-----BEGIN CERTIFICATE-----\nMIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT\nAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw\nMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD\nTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf\nqQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr\nbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL\nj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou\n08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw\nWsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT\ntOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ\n47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC\nll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa\ni0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE\nFPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o\ndHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD\nnFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s\nD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ\nj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT\nQfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW\n+YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7\nIxjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d\n8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm\n5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG\nrp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM=\n-----END CERTIFICATE-----\n\nAmazon Root CA 1\n================\n-----BEGIN CERTIFICATE-----\nMIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD\nVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1\nMDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv\nbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH\nFzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ\ngLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t\ndHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce\nVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB\n/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3\nDQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM\nCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy\n8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa\n2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2\nxJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5\n-----END CERTIFICATE-----\n\nAmazon Root CA 2\n================\n-----BEGIN CERTIFICATE-----\nMIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD\nVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1\nMDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv\nbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4\nkHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp\nN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9\nAElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd\nfLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx\nkv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS\nbtqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0\nQ5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN\nc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+\n3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw\nDPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA\nA7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY\n+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE\nYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW\nxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ\ngj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW\naQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV\nYh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3\nKadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi\nJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw=\n-----END CERTIFICATE-----\n\nAmazon Root CA 3\n================\n-----BEGIN CERTIFICATE-----\nMIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG\nEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy\nNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ\nMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB\nf8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr\nZt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43\nrDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc\neGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw==\n-----END CERTIFICATE-----\n\nAmazon Root CA 4\n================\n-----BEGIN CERTIFICATE-----\nMIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG\nEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy\nNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ\nMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN\n/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri\n83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\nHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA\nMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1\nAE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA==\n-----END CERTIFICATE-----\n\nLuxTrust Global Root 2\n======================\n-----BEGIN CERTIFICATE-----\nMIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG\nA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh\nbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW\nMBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC\nAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm\nKb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2\nxjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC\nwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm\n1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm\nFRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF\nwpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/\na4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U\nubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ\nMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB\n/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5\nLmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT\n+Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ\nFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN\nH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW\n7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu\nZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA\nVWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR\nTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt\n/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc\n7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I\niyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr\n-----END CERTIFICATE-----\n\nTUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1\n=============================================\n-----BEGIN CERTIFICATE-----\nMIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT\nD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr\nIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g\nTWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp\nZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD\nVQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt\nc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth\nbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11\nIFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8\n6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc\nwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0\n3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9\nWSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU\nZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ\nKoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh\nAHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc\nlNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R\ne37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j\nq5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "inst/cert/shinyapps.io.pem",
    "content": "Amazon Root CA 1\n================\n-----BEGIN CERTIFICATE-----\nMIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\nADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\nb24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\nMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\nb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\nca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\nIFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\nVOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\njgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\nAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\nA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\nU5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\nN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\no/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\nrqXRfboQnoZsG4q5WTP468SQvvG5\n-----END CERTIFICATE-----\n\nAmazon Root CA 2\n================\n-----BEGIN CERTIFICATE-----\nMIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF\nADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\nb24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL\nMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\nb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK\ngXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ\nW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg\n1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K\n8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r\n2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me\nz/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR\n8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj\nmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz\n7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6\n+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI\n0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB\nAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm\nUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2\nLIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY\n+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS\nk5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl\n7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm\nbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl\nurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+\nfUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63\nn749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE\n76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H\n9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT\n4PsJYGw=\n-----END CERTIFICATE-----\n\nAmazon Root CA 3\n================\n-----BEGIN CERTIFICATE-----\nMIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5\nMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g\nUm9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG\nA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg\nQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl\nui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j\nQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr\nttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr\nBqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM\nYyRIHN8wfdVoOw==\n-----END CERTIFICATE-----\n\nAmazon Root CA 4\n================\n-----BEGIN CERTIFICATE-----\nMIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5\nMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g\nUm9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG\nA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg\nQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi\n9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk\nM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB\n/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB\nMAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw\nCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW\n1KyLa2tJElMzrdfkviT8tQp21KW8EA==\n-----END CERTIFICATE-----\n\nStarfield Services Root CA\n==========================\n-----BEGIN CERTIFICATE-----\nMIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx\nEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\nHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs\nZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5\nMDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD\nVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy\nZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy\ndmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p\nOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2\n8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K\nTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe\nhRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk\n6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw\nDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q\nAdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI\nbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB\nve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z\nqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd\niEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn\n0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN\nsSi6\n-----END CERTIFICATE-----\n\nDigiCert Assured ID Root G2\n===========================\n-----BEGIN CERTIFICATE-----\nMIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv\nb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl\ncnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi\nMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA\nn61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc\nbiJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp\nEgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA\nbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu\nYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB\nAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW\nBBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI\nQW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I\n0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni\nlmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9\nB5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv\nON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo\nIhNzbM8m9Yop5w==\n-----END CERTIFICATE-----\n\nDigiCert Global Root G2\n=======================\n-----BEGIN CERTIFICATE-----\nMIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\nMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\nMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\nb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\nq2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\ntCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\nvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\nBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\nNeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\nFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\npLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\nMrY=\n-----END CERTIFICATE-----\n\nDigiCert Assured ID CA G2\n=========================\n-----BEGIN CERTIFICATE-----\nMIIEmjCCA4KgAwIBAgIQD1/M/Ksg89+ObaPYR2fCkzANBgkqhkiG9w0BAQsFADBl\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv\nb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMjgwODAxMTIwMDAwWjBIMQswCQYDVQQG\nEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSIwIAYDVQQDExlEaWdpQ2VydCBB\nc3N1cmVkIElEIENBIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\ntFLF3hmmiyDchhlz2ZMtsf4L23l6z+g5odzcrNDI8QcZTBUm9xV9w5YZ6bRXU7jw\nZlKaaVtxrqugWHNdlNfKL27RzC4kn1aKvwdt5Gj9j+UMzc/BMx3ksvGiHwQMBMGF\nzPAVG9aUpVXCXdUt0vSOLZxzI3x2Oqz2jL9pxc2l/bmgf9uhE4v2noPxViUQ4DF2\nh/cDRzV5qyZUq8FPw9y1JUi8Pl6HHAzFei/4OvhAQogerd8lvl8chlkT5h9UNeS0\nzdv5uDrNs5C/hJqXidKZU7nZTqh08B/RD8vsu9f66HavmbyrKXa2CveC0uqZvfQZ\n0SBCj4ykeElnxyveHfOvUQIDAQABo4IBYTCCAV0wEgYDVR0TAQH/BAgwBgEB/wIB\nADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhho\ndHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6\nLy9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RHMi5jcmww\nOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ\nRFJvb3RHMi5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0\ndHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFLHP9qHl5oHFZkeN\neIR3e/mBy0qmMB8GA1UdIwQYMBaAFM7DSrmZVfK422C/qX69VrWXNqfWMA0GCSqG\nSIb3DQEBCwUAA4IBAQA/C7UkzAlgi5mvLq7ukp0JS9Vg+rMNyx3XVkBF84vz9exk\n74Y0vPnniZoy/pAgOaFFGbsVBI7SNf/iGI5x5UG5PKWo+K0AP0fwkZFnfEAN0wJy\nDezr2oLi95s2XCoqThxhtJe6t1A8Lh6klGQGkgREdyF7Y6obGQq+7hDz4VRR4MCM\nm5QHcPcYJzh0pQueTxadgp6AbksehUf/g9eGzwzHqdkkAIQ9mzpRRxjoqkaPTtSd\nkpPl/ZZcDqPzDzWXhzKfJs90lSuYy1z1fQEgFZP/X1DGT8SGzAlfv83e+FC3ja7w\nJZJ4ZTqw8UTUUpI52U2HVCMXKNtEFEyMYLeRJe+j\n-----END CERTIFICATE-----\n\nDigiCert Baltimore CA-2 G2\n==========================\n-----BEGIN CERTIFICATE-----\nMIIEYzCCA0ugAwIBAgIQAYL4CY6i5ia5GjsnhB+5rzANBgkqhkiG9w0BAQsFADBa\nMQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl\nclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE1\nMTIwODEyMDUwN1oXDTI1MDUxMDEyMDAwMFowZDELMAkGA1UEBhMCVVMxFTATBgNV\nBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEjMCEG\nA1UEAxMaRGlnaUNlcnQgQmFsdGltb3JlIENBLTIgRzIwggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQC75wD+AAFz75uI8FwIdfBccHMf/7V6H40II/3HwRM/\nsSEGvU3M2y24hxkx3tprDcFd0lHVsF5y1PBm1ITykRhBtQkmsgOWBGmVU/oHTz6+\nhjpDK7JZtavRuvRZQHJaZ7bN5lX8CSukmLK/zKkf1L+Hj4Il/UWAqeydjPl0kM8c\n+GVQr834RavIL42ONh3e6onNslLZ5QnNNnEr2sbQm8b2pFtbObYfAB8ZpPvTvgzm\n+4/dDoDmpOdaxMAvcu6R84Nnyc3KzkqwIIH95HKvCRjnT0LsTSdCTQeg3dUNdfc2\nYMwmVJihiDfwg/etKVkgz7sl4dWe5vOuwQHrtQaJ4gqPAgMBAAGjggEZMIIBFTAd\nBgNVHQ4EFgQUwBKyKHRoRmfpcCV0GgBFWwZ9XEQwHwYDVR0jBBgwFoAU5Z1ZMIJH\nWMys+ghUNoZ7OrUETfAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC\nAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp\nY2VydC5jb20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQu\nY29tL09tbmlyb290MjAyNS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYB\nBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQEL\nBQADggEBAC/iN2bDGs+RVe4pFPpQEL6ZjeIo8XQWB2k7RDA99blJ9Wg2/rcwjang\nB0lCY0ZStWnGm0nyGg9Xxva3vqt1jQ2iqzPkYoVDVKtjlAyjU6DqHeSmpqyVDmV4\n7DOMvpQ+2HCr6sfheM4zlbv7LFjgikCmbUHY2Nmz+S8CxRtwa+I6hXsdGLDRS5rB\nbxcQKegOw+FUllSlkZUIII1pLJ4vP1C0LuVXH6+kc9KhJLsNkP5FEx2noSnYZgvD\n0WyzT7QrhExHkOyL4kGJE7YHRndC/bseF/r/JUuOUFfrjsxOFT+xJd1BDKCcYm1v\nupcHi9nzBhDFKdT3uhaQqNBU4UtJx5g=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "inst/examples/diamonds/server.R",
    "content": "library(shiny)\nlibrary(ggplot2)\n\nshinyServer(function(input, output) {\n\n  dataset <- reactive(function() {\n    diamonds[sample(nrow(diamonds), input$sampleSize), ]\n  })\n\n  output$plot <- reactivePlot(function() {\n\n    p <- ggplot(dataset(), aes_string(x = input$x, y = input$y)) + geom_point()\n\n    if (input$color != \"None\")\n      p <- p + aes_string(color = input$color)\n\n    facets <- paste(input$facet_row, \"~\", input$facet_col)\n    if (facets != \". ~ .\")\n      p <- p + facet_grid(facets)\n\n    if (input$jitter)\n      p <- p + geom_jitter()\n    if (input$smooth)\n      p <- p + geom_smooth()\n\n    print(p)\n\n  }, height = 700)\n\n})\n"
  },
  {
    "path": "inst/examples/diamonds/ui.R",
    "content": "library(shiny)\nlibrary(ggplot2)\n\ndataset <- diamonds\n\nshinyUI(pageWithSidebar(\n\n  headerPanel(\"Diamonds Explorer\"),\n\n  sidebarPanel(\n\n    sliderInput(\"sampleSize\", \"Sample Size\", min = 1, max = nrow(dataset),\n                value = min(1000, nrow(dataset)), step = 500, round = 0),\n\n    selectInput(\"x\", \"X\", names(dataset)),\n    selectInput(\"y\", \"Y\", names(dataset), names(dataset)[[2]]),\n    selectInput(\"color\", \"Color\", c(\"None\", names(dataset))),\n\n    checkboxInput(\"jitter\", \"Jitter\"),\n    checkboxInput(\"smooth\", \"Smooth\"),\n\n    selectInput(\"facet_row\", \"Facet Row\", c(None = \".\", names(dataset))),\n    selectInput(\"facet_col\", \"Facet Column\", c(None = \".\", names(dataset)))\n  ),\n\n  mainPanel(\n    plotOutput(\"plot\")\n  )\n))\n"
  },
  {
    "path": "inst/examples/sessioninfo/server.R",
    "content": "library(shiny)\n\nshinyServer(function(input, output, session) {\n\n  # output sessionInfo\n  output$sessionInfo <- renderPrint({\n    sessionInfo()\n  })\n\n  # output urlInfo\n  output$urlInfo <- renderText({\n    paste(sep = \"\",\n          \"protocol: \", session$clientData$url_protocol, \"\\n\",\n          \"hostname: \", session$clientData$url_hostname, \"\\n\",\n          \"pathname: \", session$clientData$url_pathname, \"\\n\",\n          \"port: \",     session$clientData$url_port,     \"\\n\",\n          \"search: \",   session$clientData$url_search,   \"\\n\"\n    )\n  })\n\n  # output userInfo\n  output$userInfo <- renderText({\n    paste(sep = \"\",\n          \"user: \", session$user, \"\\n\")\n  })\n\n})\n"
  },
  {
    "path": "inst/examples/sessioninfo/ui.R",
    "content": "library(shiny)\n\nshinyUI(fluidPage(\n  h3(\"URL\"),\n  verbatimTextOutput(\"urlInfo\"),\n\n  h3(\"Session\"),\n  verbatimTextOutput(\"sessionInfo\"),\n\n  h3(\"User\"),\n  verbatimTextOutput(\"userInfo\")\n))\n"
  },
  {
    "path": "inst/resources/environment.py",
    "content": "#!/usr/bin/env python\n\"\"\"\nEnvironment data class abstraction that is usable as an executable module\n\n```bash\npython -m rsconnect.environment\n```\n\"\"\"\nimport collections\nimport datetime\nimport json\nimport locale\nimport os\nimport re\nimport subprocess\nimport sys\n\nimport pyproject\n\ntry:\n    import typing\n    from typing import Optional\nexcept ImportError:\n    typing = None\n\nversion_re = re.compile(r\"\\d+\\.\\d+(\\.\\d+)?\")\nconda_version_re = re.compile(r\"^(?:\\s*-\\s*)?python=(\\d+\\.\\d+(?:\\.\\d+)?)\", re.MULTILINE)\nexec_dir = os.path.dirname(sys.executable)\n\n\nEnvironment = collections.namedtuple(\n    \"Environment\", (\"conda\", \"contents\", \"error\", \"warning\", \"filename\", \"locale\", \"package_manager\", \"pip\", \"python\", \"source\", \"requires\"),\n)\n\n\ndef MakeEnvironment(\n    conda=None,  # type: Optional[str]\n    contents=\"\",  # type: Optional[str]\n    error=None,  # type: Optional[str]\n    warning=None,  # type: Optional[str]\n    filename=\"\",  # type: Optional[str]\n    locale=\"\",  # type: Optional[str]\n    package_manager=\"\",  # type: Optional[str]\n    pip=None,  # type: Optional[str]\n    python=None,  # type: Optional[str]\n    source=None,  # type: Optional[str]\n    requires=None,  # type: Optional[str]\n):\n    return Environment(conda, contents, error, warning, filename, locale, package_manager, pip, python, source, requires)\n\n\nclass EnvironmentException(Exception):\n    pass\n\n\ndef detect_environment(dirname, force_generate=False, conda_mode=False, conda=None):\n    # type: (str, bool, bool, typing.Optional[str]) -> Environment\n    \"\"\"Determine the python dependencies in the environment.\n\n    `pip freeze` will be used to introspect the environment unless `conda_mode` is\n    set to `True`.  In that case, an attempt will be made to use Conda to introspect\n    the environment.\n\n    :param: dirname Directory name\n    :param: force_generate Force the generation of an environment\n    :param: conda_mode inspect the environment assuming Conda\n    :return: a dictionary containing the package spec filename and contents if successful,\n    or a dictionary containing `error` on failure.\n    \"\"\"\n    conda = get_conda(conda)\n\n    if conda_mode and conda:\n        if force_generate:\n            result = conda_env_export(conda)\n        else:\n            result = output_file(dirname, \"environment.yml\", \"conda\") or conda_env_export(conda)\n    else:\n        if force_generate:\n            result = pip_freeze()\n        else:\n            result = output_file(dirname, \"requirements.txt\", \"pip\") or pip_freeze()\n\n    if result is not None:\n        if conda_mode and result[\"package_manager\"] != \"conda\":\n            return MakeEnvironment(\n                error=(\n                    'Conda was requested but no activated Conda environment was found. See \"conda activate '\n                    '--help\" for more information.'\n                )\n            )\n\n        result[\"python\"] = get_python_version(MakeEnvironment(**result))\n        result[\"pip\"] = get_version(\"pip\")\n        if conda:\n            result[\"conda\"] = get_conda_version(conda)\n        result[\"locale\"] = get_default_locale()\n\n        try:\n            result[\"requires\"] = pyproject.detect_python_version_requirement(dirname)\n        except Exception as exception:\n            # Any error coming from detecting python version requirement, should not halt the operation\n            # and provide a warning in case a version requirement is desired by the user.\n            result[\"warning\"] = str(exception)\n\n    return MakeEnvironment(**result)\n\n\ndef get_conda(conda=None):\n    \"\"\"get_conda tries to find the conda executable if we're in\n    a conda environment. If not, or if we cannot find the executable,\n    return None.\n    :returns: conda string path to conda or None.\n    \"\"\"\n    if os.environ.get(\"CONDA_PREFIX\", None) is None and conda is None:\n        return None\n    else:\n        return conda or os.environ.get(\"CONDA_EXE\", None)\n\n\ndef get_python_version(environment):\n    # type: (Environment) -> str\n    if environment.package_manager == \"conda\":\n        versions = conda_version_re.findall(environment.contents)\n        if len(versions) > 0:\n            version = versions[0]\n            if version.count(\".\") == 1:\n                version = version + \".0\"\n            return version\n\n    v = sys.version_info\n    return \"%d.%d.%d\" % (v[0], v[1], v[2])\n\n\ndef get_conda_version(conda):\n    try:\n        args = [conda, \"-V\"]\n        proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,)\n        stdout, stderr = proc.communicate()\n        match = version_re.search(stdout or stderr)\n        if match:\n            return match.group()\n        msg = \"Failed to get version of conda from the output of: %s - standard output: %s; standard error: %s\" % (\n            \" \".join(args),\n            stdout,\n            stderr,\n        )\n        raise EnvironmentException(msg)\n    except Exception as exception:\n        raise EnvironmentException(\"Error getting conda version: %s\" % str(exception))\n\n\ndef get_default_locale(locale_source=locale.getdefaultlocale):\n    result = \".\".join([item or \"\" for item in locale_source()])\n    return \"\" if result == \".\" else result\n\n\ndef get_version(module):\n    try:\n        args = [sys.executable, \"-m\", module, \"--version\"]\n        proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,)\n        stdout, stderr = proc.communicate()\n        match = version_re.search(stdout)\n        if match:\n            return match.group()\n\n        msg = \"Failed to get version of '%s' from the output of: %s\" % (module, \" \".join(args),)\n        raise EnvironmentException(msg)\n    except Exception as exception:\n        raise EnvironmentException(\"Error getting '%s' version: %s\" % (module, str(exception)))\n\n\ndef output_file(dirname, filename, package_manager):\n    \"\"\"Read an existing package spec file.\n\n    Returns a dictionary containing the filename and contents\n    if successful, None if the file does not exist,\n    or a dictionary containing 'error' on failure.\n    \"\"\"\n    try:\n        path = os.path.join(dirname, filename)\n        if not os.path.exists(path):\n            return None\n\n        with open(path, \"r\") as f:\n            data = f.read()\n\n        data = \"\\n\".join([line for line in data.split(\"\\n\") if \"rsconnect\" not in line])\n\n        return {\n            \"filename\": filename,\n            \"contents\": data,\n            \"source\": \"file\",\n            \"package_manager\": package_manager,\n        }\n    except Exception as exception:\n        raise EnvironmentException(\"Error reading %s: %s\" % (filename, str(exception)))\n\n\ndef pip_freeze():\n    \"\"\"Inspect the environment using `pip freeze`.\n\n    Returns a dictionary containing the filename\n    (always 'requirements.txt') and contents if successful,\n    or a dictionary containing 'error' on failure.\n    \"\"\"\n    try:\n        proc = subprocess.Popen(\n            [sys.executable, \"-m\", \"pip\", \"list\", \"--format=freeze\"],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            universal_newlines=True,\n        )\n\n        pip_stdout, pip_stderr = proc.communicate()\n        pip_status = proc.returncode\n    except Exception as exception:\n        raise EnvironmentException(\"Error during pip freeze: %s\" % str(exception))\n\n    if pip_status != 0:\n        msg = pip_stderr or (\"exited with code %d\" % pip_status)\n        raise EnvironmentException(\"Error during pip freeze: %s\" % msg)\n\n    pip_stdout = \"\\n\".join([line for line in pip_stdout.split(\"\\n\") if \"rsconnect\" not in line])\n\n    pip_stdout = (\n        \"# requirements.txt generated by rsconnect-python on \" + str(datetime.datetime.utcnow()) + \"\\n\" + pip_stdout\n    )\n\n    return {\n        \"filename\": \"requirements.txt\",\n        \"contents\": pip_stdout,\n        \"source\": \"pip_freeze\",\n        \"package_manager\": \"pip\",\n    }\n\n\ndef conda_env_export(conda):\n    \"\"\"Inspect the environment using `conda env export`\n    :param: conda path to the `conda` tool\n    :return: dictionary containing the key \"environment.yml\" and the data inside\n    \"\"\"\n    try:\n        proc = subprocess.Popen(\n            [conda, \"env\", \"export\"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,\n        )\n        conda_stdout, conda_stderr = proc.communicate()\n        conda_status = proc.returncode\n    except Exception as exception:\n        raise EnvironmentException(\"Error during conda env export: %s\" % str(exception))\n\n    if conda_status != 0:\n        msg = conda_stderr or (\"exited with code %d\" % conda_status)\n        raise EnvironmentException(\"Error during conda env export: %s\" % msg)\n\n    return {\n        \"filename\": \"environment.yml\",\n        \"contents\": conda_stdout,\n        \"source\": \"conda_env_export\",\n        \"package_manager\": \"conda\",\n    }\n\n\ndef main():\n    \"\"\"\n    Run `detect_environment` and dump the result as JSON.\n    \"\"\"\n    try:\n        if len(sys.argv) < 2:\n            raise EnvironmentException(\"Usage: %s [-fc] DIRECTORY\" % sys.argv[0])\n        # directory is always the last argument\n        directory = sys.argv[len(sys.argv) - 1]\n        flags = \"\"\n        force_generate = False\n        conda_mode = False\n        if len(sys.argv) > 2:\n            flags = sys.argv[1]\n        if \"f\" in flags:\n            force_generate = True\n        if \"c\" in flags:\n            conda_mode = True\n        json.dump(\n            detect_environment(directory, force_generate, conda_mode)._asdict(), sys.stdout, indent=4,\n        )\n    except EnvironmentException as exception:\n        json.dump(dict(error=str(exception)), sys.stdout, indent=4)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "inst/resources/pyproject.py",
    "content": "\"\"\"\nSupport for detecting various information from python projects metadata.\n\nMetadata can only be loaded from static files (e.g. pyproject.toml, setup.cfg, etc.)\nbut not from setup.py due to its dynamic nature.\n\"\"\"\n\nimport configparser\nimport pathlib\nimport re\nimport sys\n\ntry:\n    import typing\nexcept ImportError:\n    typing = None\n\nPEP440_OPERATORS_REGEX = r\"(===|==|!=|<=|>=|<|>|~=)\"\nVALID_VERSION_REQ_REGEX = rf\"^({PEP440_OPERATORS_REGEX}?\\d+(\\.[\\d\\*]+)*)+$\"\n\n\ndef detect_python_version_requirement(directory):\n    # type: (typing.Union[str, pathlib.Path]) -> typing.Optional[str]\n    \"\"\"Detect the python version requirement for a project.\n\n    The directory should contain a metadata file such as pyproject.toml,\n    setup.cfg, or .python-version.\n\n    Returns the python version requirement as a string or None if not found.\n    \"\"\"\n    for _, metadata_file in lookup_metadata_file(directory):\n        parser = get_python_version_requirement_parser(metadata_file)\n        try:\n            version_constraint = parser(metadata_file)\n        except InvalidVersionConstraintError as err:\n            # Invalid python version constraint in file, ignoring it\n            continue\n\n        if version_constraint:\n            return version_constraint\n\n    return None\n\n\ndef lookup_metadata_file(directory):\n    # type: (typing.Union[str, pathlib.Path]) -> typing.List[typing.Tuple[str, pathlib.Path]]\n    \"\"\"Given the directory of a project return the path of a usable metadata file.\n\n    The returned value is either a list of tuples [(filename, path)] or\n    an empty list [] if no metadata file was found.\n\n    The metadata files are returned in the priority they should be processed\n    to determine the python version requirements.\n    \"\"\"\n    directory = pathlib.Path(directory)\n\n    def _generate():\n        for filename in (\".python-version\", \"pyproject.toml\", \"setup.cfg\"):\n            path = directory / filename\n            if path.is_file():\n                yield (filename, path)\n\n    return list(_generate())\n\n\ndef get_python_version_requirement_parser(metadata_file):\n    # type: (pathlib.Path) -> typing.Callable[[pathlib.Path], typing.Optional[str]]\n    \"\"\"Given the metadata file, return the appropriate parser function.\n\n    The returned function takes a pathlib.Path and returns the parsed value.\n    \"\"\"\n    if metadata_file.name == \"pyproject.toml\":\n        return parse_pyproject_python_requires\n    elif metadata_file.name == \"setup.cfg\":\n        return parse_setupcfg_python_requires\n    elif metadata_file.name == \".python-version\":\n        return parse_pyversion_python_requires\n    else:\n        raise NotImplementedError(f\"Unknown metadata file type: {metadata_file.name}\")\n\n\ndef parse_pyproject_python_requires(pyproject_file):\n    #type: (pathlib.Path) -> typing.Optional[str]\n    \"\"\"Parse the project.requires-python field from a pyproject.toml file.\n\n    Assumes that the pyproject.toml file exists, is accessible and well formatted.\n\n    Returns None if the field is not found.\n    \"\"\"\n\n    try:\n        # Python 3.11+ has toml in the standard library\n        import toml\n    except ImportError:\n        try:\n            # If Python <= 3.10 tomllib is required to handle requirements\n            import tomllib as toml\n        except ImportError:\n            raise Exception(\"pyproject.toml found, tomllib package is required for Python <= 3.10 for parsing python version requirements, skipping\")\n\n    content = pyproject_file.read_text()\n    pyproject = toml.loads(content)\n\n    return pyproject.get(\"project\", {}).get(\"requires-python\", None)\n\n\ndef parse_setupcfg_python_requires(setupcfg_file):\n    #type: (pathlib.Path) -> typing.Optional[str]\n    \"\"\"Parse the options.python_requires field from a setup.cfg file.\n\n    Assumes that the setup.cfg file exists, is accessible and well formatted.\n\n    Returns None if the field is not found.\n    \"\"\"\n    config = configparser.ConfigParser()\n    config.read(setupcfg_file)\n\n    return config.get(\"options\", \"python_requires\", fallback=None)\n\n\ndef parse_pyversion_python_requires(pyversion_file):\n    #type: (pathlib.Path) -> typing.Optional[str]\n    \"\"\"Parse the python version from a .python-version file.\n\n    Assumes that the .python-version file exists, is accessible and well formatted.\n\n    Returns None if the field is not found.\n    \"\"\"\n    return adapt_python_requires(pyversion_file.read_text().strip())\n\n\ndef adapt_python_requires(python_requires):\n    #type: (str) -> str\n    \"\"\"Convert a literal python version to a PEP440 constraint.\n\n    Connect expects a PEP440 format, but the .python-version file can contain\n    plain version numbers and other formats.\n\n    We should convert them to the constraints that connect expects.\n    \"\"\"\n    current_contraints = python_requires.split(\",\")\n\n    def _adapt_contraint(constraints):\n        # type: (typing.List[str]) -> typing.Generator[str, None, None]\n        for constraint in constraints:\n            constraint = constraint.strip()\n            if \"@\" in constraint or \"-\" in constraint or \"/\" in constraint:\n                raise InvalidVersionConstraintError(f\"python specific implementations are not supported: {constraint}\")\n\n            if \"b\" in constraint or \"rc\" in constraint or \"a\" in constraint:\n                raise InvalidVersionConstraintError(f\"pre-release versions are not supported: {constraint}\")\n\n            if re.match(VALID_VERSION_REQ_REGEX, constraint) is None:\n                raise InvalidVersionConstraintError(f\"Invalid python version: {constraint}\")\n\n            if re.search(PEP440_OPERATORS_REGEX, constraint):\n                yield constraint\n            else:\n                # Convert to PEP440 format\n                if \"*\" in constraint:\n                    yield f\"=={constraint}\"\n                else:\n                    # only major specified “3” → ~=3.0 → >=3.0,<4.0\n                    # major and minor specified “3.8” or “3.8.11” → ~=3.8.0 → >=3.8.0,<3.9.0\n                    constraint = \".\".join(constraint.split(\".\")[:2] + [\"0\"])\n                    yield f\"~={constraint}\"\n\n    return \",\".join(_adapt_contraint(current_contraints))\n\n\nclass InvalidVersionConstraintError(ValueError):\n    pass\n"
  },
  {
    "path": "man/accountUsage.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/usage.R\n\\name{accountUsage}\n\\alias{accountUsage}\n\\title{Show Account Usage}\n\\usage{\naccountUsage(\n  account = NULL,\n  server = NULL,\n  usageType = \"hours\",\n  from = NULL,\n  until = NULL,\n  interval = NULL\n)\n}\n\\arguments{\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{usageType}{Use metric to retreive (for example: \"hours\")}\n\n\\item{from}{Date range starting timestamp (Unix timestamp or relative time\ndelta such as \"2d\" or \"3w\").}\n\n\\item{until}{Date range ending timestamp (Unix timestamp or relative time\ndelta such as \"2d\" or \"3w\").}\n\n\\item{interval}{Summarization interval. Data points at intervals less then this\nwill be grouped. (Number of seconds or relative time delta e.g. \"1h\").}\n}\n\\description{\nShow account usage\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function only works for ShinyApps servers.\n}\n"
  },
  {
    "path": "man/accounts.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/accounts.R\n\\name{accounts}\n\\alias{accounts}\n\\alias{accountInfo}\n\\alias{removeAccount}\n\\title{Account Management Functions}\n\\usage{\naccounts(server = NULL)\n\naccountInfo(name = NULL, server = NULL)\n\nremoveAccount(name = NULL, server = NULL)\n}\n\\arguments{\n\\item{server}{Name of the server on which the account is registered\n(optional; see \\code{\\link[=servers]{servers()}})}\n\n\\item{name}{Name of account}\n}\n\\value{\n\\code{accounts} returns a data frame with the names of all accounts\nregistered on the system and the servers on which they reside.\n\\code{accountInfo} returns a list with account details.\n}\n\\description{\nFunctions to enumerate and remove accounts on the local system. Prior to\ndeploying applications you need to register your account on the local system.\n\nSupported servers: All servers\n}\n\\details{\nYou register an account using the \\code{\\link[=setAccountInfo]{setAccountInfo()}} function (for\nShinyApps) or \\code{\\link[=connectUser]{connectUser()}} function (for other servers). You can\nsubsequently remove the account using the \\code{removeAccount} function.\n\nThe \\code{accounts} and \\code{accountInfo} functions are provided for viewing\npreviously registered accounts.\n}\n\\seealso{\nOther Account functions: \n\\code{\\link{connectApiUser}()},\n\\code{\\link{connectCloudUser}()},\n\\code{\\link{setAccountInfo}()}\n}\n\\concept{Account functions}\n"
  },
  {
    "path": "man/addAuthorizedUser.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/auth.R\n\\name{addAuthorizedUser}\n\\alias{addAuthorizedUser}\n\\title{Add authorized user to application}\n\\usage{\naddAuthorizedUser(\n  email,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  sendEmail = NULL,\n  emailMessage = NULL\n)\n}\n\\arguments{\n\\item{email}{Email address of user to add.}\n\n\\item{appDir}{Directory containing application. Defaults to\ncurrent working directory.}\n\n\\item{appName}{Name of application.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{sendEmail}{Send an email letting the user know the application\nhas been shared with them.}\n\n\\item{emailMessage}{Optional character vector of length 1 containing a\ncustom message to send in email invitation. Defaults to NULL, which\nwill use default invitation message.}\n}\n\\description{\nAdd authorized user to application\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n\\seealso{\n\\code{\\link[=removeAuthorizedUser]{removeAuthorizedUser()}} and \\code{\\link[=showUsers]{showUsers()}}\n}\n"
  },
  {
    "path": "man/addLinter.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/lint-framework.R, R/linters.R\n\\name{addLinter}\n\\alias{addLinter}\n\\title{Add a Linter}\n\\usage{\naddLinter(name, linter)\n\naddLinter(name, linter)\n}\n\\arguments{\n\\item{name}{The name of the linter, as a string.}\n\n\\item{linter}{A \\code{\\link[=linter]{linter()}}.}\n}\n\\description{\nAdd a linter, to be used in subsequent calls to \\code{\\link[=lint]{lint()}}.\n\nSupported servers: All servers\n\nAdd a linter, to be used in subsequent calls to \\code{\\link[=lint]{lint()}}.\n}\n\\examples{\naddLinter(\"no.capitals\", linter(\n\n  ## Identify lines containing capital letters -- either by name or by index\n  apply = function(content, ...) {\n    grep(\"[A-Z]\", content)\n  },\n\n  ## Only use this linter on R files (paths ending with .r or .R)\n  takes = function(paths) {\n    grep(\"[rR]$\", paths)\n  },\n\n  # Use the default message constructor\n  message = function(content, lines, ...) {\n    makeLinterMessage(\"Capital letters found on the following lines\", content, lines)\n  },\n\n  # Give a suggested prescription\n  suggest = \"Do not use capital letters in these documents.\"\n))\naddLinter(\"no.capitals\", linter(\n\n  ## Identify lines containing capital letters -- either by name or by index\n  apply = function(content, ...) {\n    grep(\"[A-Z]\", content)\n  },\n\n  ## Only use this linter on R files (paths ending with .r or .R)\n  takes = function(paths) {\n    grep(\"[rR]$\", paths)\n  },\n\n  # Use the default message constructor\n  message = function(content, lines, ...) {\n    makeLinterMessage(\"Capital letters found on the following lines\", content, lines)\n  },\n\n  # Give a suggested prescription\n  suggest = \"Do not use capital letters in these documents.\"\n))\n}\n"
  },
  {
    "path": "man/addServer.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/servers.R\n\\name{addServer}\n\\alias{addServer}\n\\alias{removeServer}\n\\alias{addServerCertificate}\n\\title{Server management}\n\\usage{\naddServer(\n  url,\n  name = NULL,\n  certificate = NULL,\n  validate = TRUE,\n  snowflakeConnectionName = NULL,\n  quiet = FALSE\n)\n\nremoveServer(name = NULL)\n\naddServerCertificate(name, certificate, quiet = FALSE)\n}\n\\arguments{\n\\item{url}{URL for the server. Can be a bare hostname like\n\\code{connect.mycompany.com} or a url like \\verb{http://posit.mycompany.com/connect}.}\n\n\\item{name}{Server name. If omitted, the server hostname is used.}\n\n\\item{certificate}{Optional. Either a path to certificate file or a\ncharacter vector containing the certificate's contents.}\n\n\\item{validate}{Validate that \\code{url} actually points to a Posit Connect\nserver?}\n\n\\item{snowflakeConnectionName}{Name for the Snowflake connection in\n\\code{connections.toml} to use for authentication or \\code{NULL} to use the default\n(when applicable).}\n\n\\item{quiet}{Suppress output and prompts where possible.}\n}\n\\description{\nThese functions manage the list of known servers:\n\\itemize{\n\\item \\code{addServer()} registers a Posit connect server. Once it has been\nregistered, you can connect to an account on the server using\n\\code{\\link[=connectUser]{connectUser()}}.\n\\item \\code{removeServer()} removes a server from the registry.\n\\item \\code{addServerCertificate()} adds a certificate to a server.\n}\n\nSupported servers: Posit Connect servers\n}\n\\examples{\n\\dontrun{\n# register a local server\naddServer(\"http://myrsconnect/\", \"myserver\")\n\n# list servers\nservers(local = TRUE)\n\n# connect to an account on the server\nconnectUser(server = \"myserver\")\n}\n}\n"
  },
  {
    "path": "man/appDependencies.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/appDependencies.R\n\\name{appDependencies}\n\\alias{appDependencies}\n\\title{Detect application dependencies}\n\\usage{\nappDependencies(\n  appDir = getwd(),\n  appFiles = NULL,\n  appFileManifest = NULL,\n  appMode = NULL,\n  dependencyResolution = c(\"strict\", \"library\")\n)\n}\n\\arguments{\n\\item{appDir}{A directory containing an application (e.g. a Shiny app\nor plumber API). Defaults to the current directory.}\n\n\\item{appFiles, appFileManifest}{Use \\code{appFiles} to specify a\ncharacter vector of files to bundle in the app or \\code{appFileManifest}\nto provide a path to a file containing a list of such files. If neither\nare supplied, will bundle all files in \\code{appDir}, apart from standard\nexclusions and files listed in a \\code{.rscignore} file. See\n\\code{\\link[=listDeploymentFiles]{listDeploymentFiles()}} for more details.}\n\n\\item{appMode}{Optional; the type of content being deployed.\nProvide this option when the inferred type of content is incorrect. This\ncan happen, for example, when static HTML content includes a downloadable\nShiny application \\code{app.R}. Accepted values include \\code{\"shiny\"}, \\code{\"api\"},\n\\code{\"rmd-static\"}, \\code{\"rmd-shiny\"}, \\code{\"quarto-static\"}, \\code{\"quarto-shiny\"},\n\\code{\"nodejs\"}, and \\code{\"static\"}. The Posit Connect API Reference contains a\nfull set of available values. Not all servers support all types of content.}\n\n\\item{dependencyResolution}{Controls how R package dependencies are resolved.\nMust be one of \\code{\"strict\"} or \\code{\"library\"}. When \\code{\"strict\"}, the\n\\code{renv.lock} file is used if present and must match the local library.\nWhen \\code{\"library\"}, the lockfile is ignored and dependencies are resolved\nfrom the locally installed library instead. This is useful when the\nlockfile is out of sync with the local library and cannot be updated\n(e.g. in CI/CD environments). Note that the deployed content will\nreflect the local library, not the lockfile.\nDefaults to \\code{\"strict\"}.}\n}\n\\value{\nA data frame with one row for each dependency (direct, indirect,\nand inferred), and 4 columns:\n\\itemize{\n\\item \\code{Package}: package name.\n\\item \\code{Version}: local version.\n\\item \\code{Source}: a short string describing the source of the package install,\nas described above.\n\\item \\code{Repository}: for CRAN and CRAN-like repositories, the URL to the\nrepository. This will be ignored by the server if it has been configured\nwith its own repository name -> repository URL mapping.\n}\n}\n\\description{\n\\code{appDependencies()} recursively detects all R package dependencies for an\napplication by parsing all \\code{.R} and \\code{.Rmd} files and looking for calls\nto \\code{library()}, \\code{require()}, \\code{requireNamespace()}, \\code{::}, and so on.\nIt then adds implicit dependencies (i.e. an \\code{.Rmd} requires Rmarkdown)\nand adds all recursive dependencies to create a complete manifest of\npackage packages need to be installed to run the app.\n\nSupported servers: All servers\n}\n\\section{Dependency discovery}{\nrsconnect use one of three mechanisms to find which packages your application\nuses:\n\\enumerate{\n\\item If \\code{renv.lock} is present, it will use the versions and sources defined in\nthat file. If you're using the lockfile for some other purpose and\ndon't want it to affect deployment, add \\code{renv.lock} to \\code{.rscignore}.\n\\item Otherwise, rsconnect will call \\code{renv::snapshot()} to find all packages\nused by your code. If you'd instead prefer to only use the packages\ndeclared in a \\code{DESCRIPTION} file, run\n\\code{renv::settings$snapshot.type(\"explicit\")} to activate renv's \"explicit\"\nmode.\n\\item Dependency resolution using renv is a new feature in rsconnect 1.0.0, and\nwhile we have done our best to test it, it still might fail for your app.\nIf this happens, please \\href{https://github.com/rstudio/rsconnect/issues}{file an issue}\nthen set \\code{options(rsconnect.packrat = TRUE)} to revert to the old\ndependency discovery mechanism.\n}\n}\n\n\\section{Remote installation}{\nWhen deployed, the app must first install all of these packages, and\nrsconnect ensures the versions used on the server will match the versions\nyou used locally. It knows how to install packages from the following\nsources:\n\\itemize{\n\\item CRAN and BioConductor (\\code{Source: CRAN} or \\code{Source: Bioconductor}). The\nremote server will ignore the specific CRAN or Bioconductor mirror that\nyou use locally, always using the CRAN/BioC mirror that has been configured\non the server.\n\\item Other CRAN like and CRAN-like repositories. These packages will have\na \\code{Source} determined by the value of \\code{getOptions(\"repos\")}. For example,\nif you've set the following options:\n\n\\if{html}{\\out{<div class=\"sourceCode R\">}}\\preformatted{options(\n   repos = c(\n     CRAN = \"https://cran.rstudio.com/\",\n     CORPORATE = \"https://corporate-packages.development.company.com\"\n   )\n)\n}\\if{html}{\\out{</div>}}\n\nThen packages installed from your corporate package repository will\nhave source \\code{CORPORATE}. Posit Connect\n\\href{https://docs.posit.co/connect/admin/appendix/configuration/#RPackageRepository}{can be configured}\nto override their repository url so that (e.g.) you can use different\npackages versions on staging and production servers.\n\\item Packages installed from GitHub, GitLab, or BitBucket, have \\code{Source}\n\\code{github}, \\code{gitlab}, and \\code{bitbucket} respectively. When deployed, the\nbundle contains the additional metadata needed to precisely recreated\nthe installed version.\n}\n\nIt's not possible to recreate the packages that you have built and installed\nfrom a directory on your local computer. This will have \\code{Source: NA} and\nwill cause the deployment to error. To resolve this issue, you'll need to\ninstall from one of the known sources described above.\n}\n\n\\section{Suggested packages}{\nThe \\code{Suggests} field is not included when determining recursive dependencies,\nso it's possible that not every package required to run your application will\nbe detected.\n\nFor example, ggplot2's \\code{geom_hex()} requires the hexbin package to be\ninstalled, but it is only suggested by ggplot2. So if your app uses\n\\code{geom_hex()} it will fail, reporting that the hexbin package is not\ninstalled.\n\nYou can overcome this problem with (e.g.) \\code{requireNamespace(hexbin)}.\nThis will tell rsconnect that your app needs the hexbin package, without\notherwise affecting your code.\n}\n\n\\examples{\n\\dontrun{\n\n# dependencies for the app in the current working dir\nappDependencies()\n\n# dependencies for an app in another directory\nappDependencies(\"~/projects/shiny/app1\")\n}\n}\n\\seealso{\n\\link{rsconnectPackages}(Using Packages with rsconnect)\n}\n"
  },
  {
    "path": "man/applicationConfigDir.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/config.R\n\\name{applicationConfigDir}\n\\alias{applicationConfigDir}\n\\title{Application Configuration Directory}\n\\usage{\napplicationConfigDir()\n}\n\\value{\nA string containing the path of the configuration folder.\n}\n\\description{\nReturns the root path used to store per user configuration data. Does not\ncheck old locations or create the path; use \\code{rsconnectConfigDir} for\nmost cases.\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/applications.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/applications.R\n\\name{applications}\n\\alias{applications}\n\\title{List Deployed Applications}\n\\usage{\napplications(account = NULL, server = NULL)\n}\n\\arguments{\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n}\n\\value{\nReturns a data frame with the following columns:\n\\tabular{ll}{\n\\code{id}         \\tab Application unique id \\cr\n\\code{name}       \\tab Name of application \\cr\n\\code{title}       \\tab Application title \\cr\n\\code{url}        \\tab URL where application can be accessed \\cr\n\n\\code{status}     \\tab Current status of application. Valid values are \\code{pending},\n\\code{deploying}, \\code{running}, \\code{terminating}, and \\code{terminated} \\cr\n\\code{size}       \\tab Instance size (small, medium, large, etc.) (on\nShinyApps.io) \\cr\n\\code{instances}  \\tab Number of instances (on ShinyApps.io) \\cr\n\\code{config_url} \\tab URL where application can be configured \\cr\n}\n}\n\\description{\nList all applications currently deployed for a given account.\n\nSupported servers: All servers\n}\n\\note{\nTo register an account you call the \\code{\\link[=setAccountInfo]{setAccountInfo()}} function.\n}\n\\examples{\n\\dontrun{\n\n# list all applications for the default account\napplications()\n\n# list all applications for a specific account\napplications(\"myaccount\")\n\n# view the list of applications in the data viewer\nView(applications())\n}\n}\n\\seealso{\n\\code{\\link[=deployApp]{deployApp()}}, \\code{\\link[=terminateApp]{terminateApp()}}\n\nOther Deployment functions: \n\\code{\\link{deployAPI}()},\n\\code{\\link{deployApp}()},\n\\code{\\link{deployDoc}()},\n\\code{\\link{deploySite}()},\n\\code{\\link{deployTFModel}()}\n}\n\\concept{Deployment functions}\n"
  },
  {
    "path": "man/configureApp.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/configureApp.R\n\\name{configureApp}\n\\alias{configureApp}\n\\title{Configure an Application}\n\\usage{\nconfigureApp(\n  appName,\n  appDir = getwd(),\n  account = NULL,\n  server = NULL,\n  redeploy = TRUE,\n  size = NULL,\n  instances = NULL,\n  logLevel = c(\"normal\", \"quiet\", \"verbose\")\n)\n}\n\\arguments{\n\\item{appName}{Name of application to configure}\n\n\\item{appDir}{Directory containing application. Defaults to\ncurrent working directory.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{redeploy}{Re-deploy application after its been configured.}\n\n\\item{size}{Configure application instance size}\n\n\\item{instances}{Configure number of application instances}\n\n\\item{logLevel}{One of \\code{\"quiet\"}, \\code{\"normal\"} or \\code{\"verbose\"}; indicates how\nmuch logging to the console is to be performed. At \\code{\"quiet\"} reports no\ninformation; at \\code{\"verbose\"}, a full diagnostic log is captured.}\n}\n\\description{\nConfigure an application running on a remote server.\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n\\examples{\n\\dontrun{\n\n# set instance size for an application\nconfigureApp(\"myapp\", size=\"xlarge\")\n}\n}\n\\seealso{\n\\code{\\link[=applications]{applications()}}, \\code{\\link[=deployApp]{deployApp()}}\n}\n"
  },
  {
    "path": "man/connectApiUser.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/accounts.R\n\\name{connectApiUser}\n\\alias{connectApiUser}\n\\alias{connectUser}\n\\title{Register account on Posit Connect}\n\\usage{\nconnectApiUser(account = NULL, server = NULL, apiKey, quiet = FALSE)\n\nconnectUser(\n  account = NULL,\n  server = NULL,\n  quiet = FALSE,\n  launch.browser = getOption(\"rsconnect.launch.browser\", interactive())\n)\n}\n\\arguments{\n\\item{account}{A name for the account to connect.}\n\n\\item{server}{The server to connect to.}\n\n\\item{apiKey}{The API key used to authenticate the user}\n\n\\item{quiet}{Whether or not to show messages and prompts while connecting the\naccount.}\n\n\\item{launch.browser}{If true, the system's default web browser will be\nlaunched automatically after the app is started. Defaults to \\code{TRUE} in\ninteractive sessions only. If a function is passed, it will be called\nafter the app is started, with the app URL as a parameter.}\n}\n\\description{\n\\code{connectUser()} and \\code{connectApiUser()} connect your Posit Connect account to\nthe rsconnect package so that it can deploy and manage applications on\nyour behalf.\n\n\\code{connectUser()} is the easiest place to start because it allows you to\nauthenticate in-browser to your Posit Connect server. \\code{connectApiUser()} is\nappropriate for non-interactive settings; you'll need to copy-and-paste the\nAPI key from your account settings.\n\nSupported servers: Posit Connect servers\n}\n\\seealso{\nOther Account functions: \n\\code{\\link{accounts}()},\n\\code{\\link{connectCloudUser}()},\n\\code{\\link{setAccountInfo}()}\n}\n\\concept{Account functions}\n"
  },
  {
    "path": "man/connectCloudUser.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/accounts.R\n\\name{connectCloudUser}\n\\alias{connectCloudUser}\n\\title{Register account on Posit Connect Cloud}\n\\usage{\nconnectCloudUser(launch.browser = TRUE)\n}\n\\arguments{\n\\item{launch.browser}{If true, the system's default web browser will be\nlaunched automatically after the app is started. Defaults to \\code{TRUE} in\ninteractive sessions only. If a function is passed, it will be called\nafter the app is started, with the app URL as a parameter.}\n}\n\\description{\n\\code{connectCloudUser()} connects your Posit Connect Cloud account to\nthe rsconnect package so that it can deploy and manage applications on\nyour behalf. It will open a browser window to authenticate, then prompt\nyou to create an account or select an account to use if you have multiple.\n\nSupported servers: Posit Connect Cloud servers\n}\n\\seealso{\nOther Account functions: \n\\code{\\link{accounts}()},\n\\code{\\link{connectApiUser}()},\n\\code{\\link{setAccountInfo}()}\n}\n\\concept{Account functions}\n"
  },
  {
    "path": "man/connectSPCSUser.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/accounts.R\n\\name{connectSPCSUser}\n\\alias{connectSPCSUser}\n\\title{Register account on Posit Connect in Snowpark Container Services}\n\\usage{\nconnectSPCSUser(\n  account = NULL,\n  server = NULL,\n  apiKey,\n  snowflakeConnectionName = NULL,\n  quiet = FALSE\n)\n}\n\\arguments{\n\\item{account}{A name for the account to connect.}\n\n\\item{server}{The server to connect to.}\n\n\\item{apiKey}{The API key used to authenticate the user}\n\n\\item{snowflakeConnectionName}{Name of the Snowflake connection in\n\\code{connections.toml} to use for authentication or \\code{NULL} to use the default\n(when applicable).}\n\n\\item{quiet}{Whether or not to show messages and prompts while connecting the\naccount.}\n}\n\\description{\n\\code{connectSPCSUser()} connects your Posit Connect account to the rsconnect\npackage so it can deploy and manage applications on your behalf.\nConfigure a\n\\href{https://docs.snowflake.com/en/developer-guide/snowflake-cli/connecting/configure-cli#location-of-the-toml-configuration-fil}{\\code{connections.toml} file}\nin the appropriate location.\n\nSPCS deployments require both Snowflake authentication (via the connection\nname) and a Posit Connect API key. The Snowflake token provides proxied\nauthentication to reach the Connect server, while the API key identifies\nthe user to Connect itself.\n\nIf \\code{snowflakeConnectionName} is not provided, \\pkg{rsconnect} will attempt to\nuse the default Snowflake connection from the \\code{connections.toml} file,\nprovided that the account matches the Connect server's URL.\n\nSupported servers: Posit Connect servers\n}\n"
  },
  {
    "path": "man/deployAPI.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/deployAPI.R\n\\name{deployAPI}\n\\alias{deployAPI}\n\\title{Deploy a Plumber API}\n\\usage{\ndeployAPI(api, ...)\n}\n\\arguments{\n\\item{api}{Path to the API project directory. Must contain either\n\\code{entrypoint.R} or \\code{plumber.R} (for plumber APIs) or \\verb{_server.yml} (for\nplumber2 APIs)}\n\n\\item{...}{Additional arguments to \\code{\\link[=deployApp]{deployApp()}}.}\n}\n\\description{\nDeploys an application consisting of plumber API routes. The given directory\nmust contain a script returning a \\code{plumb} object or a plumber API definition.\n\nSupported servers: Posit Connect and ShinyApps servers\n}\n\\details{\nDeploy a plumber API definition by either supplying a directory\ncontaining \\code{plumber.R} (an API definition) or \\code{entrypoint.R} that returns a\n\\code{plumb} object created by \\code{plumber::plumb()}. See the plumber documentation\nfor more information. Alternatively, deploy a plumber2 API by supplying a\ndirectory containing \\verb{_server.yml}.\n}\n\\seealso{\nOther Deployment functions: \n\\code{\\link{applications}()},\n\\code{\\link{deployApp}()},\n\\code{\\link{deployDoc}()},\n\\code{\\link{deploySite}()},\n\\code{\\link{deployTFModel}()}\n}\n\\concept{Deployment functions}\n"
  },
  {
    "path": "man/deployApp.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/deployApp.R\n\\name{deployApp}\n\\alias{deployApp}\n\\title{Deploy an Application}\n\\usage{\ndeployApp(\n  appDir = getwd(),\n  appFiles = NULL,\n  appFileManifest = NULL,\n  manifestPath = NULL,\n  appPrimaryDoc = NULL,\n  appSourceDoc = NULL,\n  appName = NULL,\n  appTitle = NULL,\n  envVars = NULL,\n  appId = NULL,\n  appMode = NULL,\n  contentCategory = NULL,\n  account = NULL,\n  server = NULL,\n  upload = TRUE,\n  recordDir = NULL,\n  launch.browser = getOption(\"rsconnect.launch.browser\", is_interactive()),\n  on.failure = NULL,\n  logLevel = c(\"normal\", \"quiet\", \"verbose\"),\n  lint = TRUE,\n  metadata = list(),\n  forceUpdate = NULL,\n  python = NULL,\n  forceGeneratePythonEnvironment = FALSE,\n  quarto = NA,\n  appVisibility = NULL,\n  image = NULL,\n  envManagement = NULL,\n  envManagementR = NULL,\n  envManagementPy = NULL,\n  envManagementNodejs = NULL,\n  packageRepositoryResolutionR = NULL,\n  dependencyResolution = c(\"strict\", \"library\")\n)\n}\n\\arguments{\n\\item{appDir}{A directory containing an application (e.g. a Shiny app\nor plumber API). Defaults to the current directory.}\n\n\\item{appFiles, appFileManifest}{Use \\code{appFiles} to specify a\ncharacter vector of files to bundle in the app or \\code{appFileManifest}\nto provide a path to a file containing a list of such files. If neither\nare supplied, will bundle all files in \\code{appDir}, apart from standard\nexclusions and files listed in a \\code{.rscignore} file. See\n\\code{\\link[=listDeploymentFiles]{listDeploymentFiles()}} for more details.}\n\n\\item{manifestPath}{Path to an existing \\code{manifest.json} file to use for\ndeployment. When provided, \\code{deployApp()} will use the file list and\nmetadata from this manifest instead of generating a new one. This allows\nyou to pre-generate a manifest with \\code{\\link[=writeManifest]{writeManifest()}}, customize it, check\nit into version control, and deploy from that exact manifest. If \\code{NULL}\n(the default), a new manifest will be generated. When using \\code{manifestPath},\n\\code{appFiles}, \\code{appFileManifest}, \\code{appMode}, \\code{appPrimaryDoc}, \\code{contentCategory},\n\\code{envManagement(R|Py)}, and \\code{image} are ignored since their values are taken\nfrom the manifest.}\n\n\\item{appPrimaryDoc}{If the application contains more than one document, this\nparameter indicates the primary one, as a path relative to \\code{appDir}. Can be\n\\code{NULL}, in which case the primary document is inferred from the contents\nbeing deployed.}\n\n\\item{appSourceDoc}{\\ifelse{html}{\\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\\strong{[Deprecated]}} Please use\n\\code{recordDir} instead.}\n\n\\item{appName}{Application name, a string consisting of letters, numbers,\n\\verb{_} and \\code{-}. The application name is used to identify applications on a\nserver, so must be unique.\n\nIf not specified, the first deployment will be automatically it from the\n\\code{appDir} for directory and website, and from the \\code{appPrimaryDoc} for\ndocument. On subsequent deploys, it will use the previously stored value.}\n\n\\item{appTitle}{Free-form descriptive title of application. Optional; if\nsupplied, will often be displayed in favor of the name. If ommitted,\non second and subsequent deploys, the title will be unchanged.}\n\n\\item{envVars}{A character vector giving the names of environment variables\nwhose values should be synchronised with the server (currently supported by\nConnect only). The values of the environment variables are sent over an\nencrypted connection and are not stored in the bundle, making this a safe\nway to send private data to Connect.\n\nThe values of sensitive environment variables should be set in the current\nsession via an \\code{.Renviron} file or with the help of a credential store like\n\\href{https://keyring.r-lib.org/}{keyring}. Avoid using\n\\code{\\link[=Sys.setenv]{Sys.setenv()}} for sensitive values, as that results in the value appearing\nin your \\code{.Rhistory}.\n\nThe names (not values) are stored in the deployment record so that future\ndeployments will automatically update their values. Other environment\nvariables on the server will not be affected. This means that removing an\nenvironment variable from \\code{envVars} will leave it unchanged on the server.\nTo remove it, either delete it using the Connect UI, or temporarily unset\nit (with \\code{\\link[=Sys.unsetenv]{Sys.unsetenv()}} or similar) then re-deploy.\n\nEnvironment variables are set prior to deployment so that your code\ncan use them and the first deployment can still succeed. Note that means\nthat if the deployment fails, the values will still be updated.}\n\n\\item{appId}{Use this to deploy to an exact known application, ignoring all\nexisting deployment records and \\code{appName}.\n\nYou can use this to update an existing application that is missing a\ndeployment record. If you're re-deploying an application that you\ncreated it's generally easier to use \\code{appName}; \\code{appId} is best reserved\nfor re-deploying apps created by someone else.\n\nYou can find the \\code{appId} in the following places:\n\\itemize{\n\\item For Posit Connect, it's \\code{guid} from the info tab on the content page.\n\\item For Posit Connect Cloud, it can be found in the content admin page's URL \\verb{https://connect.posit.cloud/\\{accountName\\}/content/\\{appId\\}}).\n\\item On shinyapps.io, it's the \\code{id} listed on the applications page.\n}}\n\n\\item{appMode}{Optional; the type of content being deployed.\nProvide this option when the inferred type of content is incorrect. This\ncan happen, for example, when static HTML content includes a downloadable\nShiny application \\code{app.R}. Accepted values include \\code{\"shiny\"}, \\code{\"api\"},\n\\code{\"rmd-static\"}, \\code{\"rmd-shiny\"}, \\code{\"quarto-static\"}, \\code{\"quarto-shiny\"},\n\\code{\"nodejs\"}, and \\code{\"static\"}. The Posit Connect API Reference contains a\nfull set of available values. Not all servers support all types of content.}\n\n\\item{contentCategory}{Optional; classifies the kind of content being\ndeployed (e.g. \\code{\"plot\"} or \\code{\"site\"}).}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{upload}{If \\code{TRUE} (the default) then the application is uploaded from\nthe local system prior to deployment. If \\code{FALSE} then it is re-deployed\nusing the last version that was uploaded. \\code{FALSE} is only supported on\nPosit Connect Cloud and shinyapps.io; \\code{TRUE} is required on Posit Connect.}\n\n\\item{recordDir}{Directory where deployment record is written. The default,\n\\code{NULL}, uses \\code{appDir}, since this is usually where you want the deployment\ndata to be stored. This argument is typically only needed when deploying\na directory of static files since you want to store the record with the\ncode that generated those files, not the files themselves.}\n\n\\item{launch.browser}{If true, the system's default web browser will be\nlaunched automatically after the app is started. Defaults to \\code{TRUE} in\ninteractive sessions only. If a function is passed, it will be called\nafter the app is started, with the app URL as a paramter.}\n\n\\item{on.failure}{Function to be called if the deployment fails. If a\ndeployment log URL is available, it's passed as a parameter.}\n\n\\item{logLevel}{One of \\code{\"quiet\"}, \\code{\"normal\"} or \\code{\"verbose\"}; indicates how\nmuch logging to the console is to be performed. At \\code{\"quiet\"} reports no\ninformation; at \\code{\"verbose\"}, a full diagnostic log is captured.}\n\n\\item{lint}{Lint the project before initiating deployment, to identify\npotentially problematic code?}\n\n\\item{metadata}{Additional metadata fields to save with the deployment\nrecord. These fields will be returned on subsequent calls to\n\\code{\\link[=deployments]{deployments()}}.\n\nMulti-value fields are recorded as comma-separated values and returned in\nthat form. Custom value serialization is the responsibility of the caller.}\n\n\\item{forceUpdate}{What should happen if there's no deployment record for\nthe app, but there's an app with the same name on the server? If \\code{TRUE},\nwill always update the previously-deployed app. If \\code{FALSE}, will ask\nthe user what to do, or fail if not in an interactive context.\n\nDefaults to \\code{TRUE} when called automatically by the IDE, and \\code{FALSE}\notherwise. You can override the default by setting option\n\\code{rsconnect.force.update.apps}.}\n\n\\item{python}{Full path to a python binary for use by \\code{reticulate}.\nRequired if \\code{reticulate} is a dependency of the app being deployed.\nIf python = NULL, and RETICULATE_PYTHON or RETICULATE_PYTHON_FALLBACK is\nset in the environment, its value will be used. The specified python binary\nwill be invoked to determine its version and to list the python packages\ninstalled in the environment.}\n\n\\item{forceGeneratePythonEnvironment}{Optional. If an existing\n\\code{requirements.txt} file is found, it will be overwritten when this argument\nis \\code{TRUE}.}\n\n\\item{quarto}{Should the deployed content be built by quarto?\n(\\code{TRUE}, \\code{FALSE}, or \\code{NA}). The default, \\code{NA}, will use quarto if\nthere are \\code{.qmd} files in the bundle, or if there is a\n\\verb{_quarto.yml} and \\code{.Rmd} files.\n\n(This option is ignored and quarto will always be used if the\n\\code{metadata} contains \\code{quarto_version} and \\code{quarto_engines} fields.)}\n\n\\item{appVisibility}{One of \\code{NULL}, \\code{\"private\"}, or \\code{\"public\"}; the\nvisibility of the deployment. When \\code{NULL}, no change to visibility is\nmade. Currently has an effect only on deployments to shinyapps.io.}\n\n\\item{image}{Optional. The name of the image to use when building and\nexecuting this content. If none is provided, Posit Connect will\nattempt to choose an image based on the content requirements. You can\noverride the default by setting the environment variable \\code{RSCONNECT_IMAGE}.}\n\n\\item{envManagement}{Optional. Should Posit Connect install packages\nfor this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}).\nThe default, \\code{NULL}, will not write any values to the bundle manifest,\nand Connect will fall back to the application default environment\nmanagement strategy, or the server default if no application default\nis defined.\n\n(This option is a shorthand flag which overwrites the values of\n\\code{envManagementR}, \\code{envManagementPy}, and \\code{envManagementNodejs}.)}\n\n\\item{envManagementR}{Optional. Should Posit Connect install R packages\nfor this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}). The default, \\code{NULL}, will\nnot write any values to the bundle manifest, and Connect will fall back to\nthe application default R environment management strategy, or the server\ndefault if no application default is defined.\n\n(This option is ignored when \\code{envManagement} is non-\\code{NULL}.)}\n\n\\item{envManagementPy}{Optional. Should Posit Connect install Python packages\nfor this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}). The default, \\code{NULL}, will\nnot write any values to the bundle manifest, and Connect will fall back to\nthe application default Python environment management strategy, or the\nserver default if no application default is defined.\n\n(This option is ignored when \\code{envManagement} is non-\\code{NULL}.)}\n\n\\item{envManagementNodejs}{Optional. Should Posit Connect install Node.js\npackages for this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}). The default,\n\\code{NULL}, will not write any values to the bundle manifest, and Connect will\nfall back to the application default Node.js environment management\nstrategy, or the server default if no application default is defined.\n\n(This option is ignored when \\code{envManagement} is non-\\code{NULL}.)}\n\n\\item{packageRepositoryResolutionR}{Optional. Specifies the package repository\nresolution strategy for R packages. Must be one of \\code{\"lax\"}, \\code{\"strict\"},\n\\code{\"legacy\"}, \\code{\"lockfile\"}, or \\code{NULL}. The default, \\code{NULL}, will not write\nany values to the bundle manifest and Connect will fall back to the\nserver's package repository resolution strategy.}\n\n\\item{dependencyResolution}{Controls how R package dependencies are resolved.\nMust be one of \\code{\"strict\"} or \\code{\"library\"}. When \\code{\"strict\"}, the\n\\code{renv.lock} file is used if present and must match the local library.\nWhen \\code{\"library\"}, the lockfile is ignored and dependencies are resolved\nfrom the locally installed library instead. This is useful when the\nlockfile is out of sync with the local library and cannot be updated\n(e.g. in CI/CD environments). Note that the deployed content will\nreflect the local library, not the lockfile.\nDefaults to \\code{\"strict\"}.}\n}\n\\description{\nDeploy a \\link[shiny:shiny-package]{shiny} application, an\n\\link[rmarkdown:rmarkdown-package]{RMarkdown} document, a plumber API, or HTML\ncontent to a server.\n\nSupported servers: All servers\n}\n\\details{\n\\subsection{Deployment records}{\n\nWhen deploying an app, \\code{deployApp()} will save a deployment record that\nmakes it easy to update the app on server from your local source code. This\ngenerally means that you need to only need to supply important arguments\n(e.g. \\code{appName}, \\code{appTitle}, \\code{server}/\\code{account}) on the first deploy, and\nrsconnect will reuse the same settings on subsequent deploys.\n\nThe metadata needs to make this work is stored in \\verb{\\{appDir\\}/rsconnect/}.\nYou should generally check these files into version control to ensure that\nfuture you and other collaborators will publish to the same location.\n\nIf you have lost this directory, all is not lost, as \\code{deployApp()} will\nattempt to rediscover existing deployments. This is easiest if you are\nupdating an app that you created, as you can just supply the \\code{appName}\n(and \\code{server}/\\code{account} if you have multiple accounts) and \\code{deployApp()}\nwill find the existing application account. If you need to update an app\nthat was created by someone else (that you have write permission) for, you'll\ninstead need to supply the \\code{appId}.\n}\n}\n\\examples{\n\\dontrun{\n\n# deploy the application in the current working dir\ndeployApp()\n\n# deploy an application in another directory\ndeployApp(\"~/projects/shiny/app1\")\n\n# deploy using an alternative application name and title\ndeployApp(\"~/projects/shiny/app1\", appName = \"myapp\",\n          appTitle = \"My Application\")\n\n# deploy specifying an explicit account name, then\n# redeploy with no arguments (will automatically use\n# the previously specified account)\ndeployApp(account = \"jsmith\")\ndeployApp()\n\n# deploy but don't launch a browser when completed\ndeployApp(launch.browser = FALSE)\n\n# deploy a Quarto website, using the quarto package to\n# find the Quarto binary\ndeployApp(\"~/projects/quarto/site1\")\n\n# deploy application with environment variables\n# (e.g., `SECRET_PASSWORD=XYZ` is set via an ~/.Renviron file)\nrsconnect::deployApp(envVars = c(\"SECRET_PASSWORD\"))\n\n# deploy from a pre-generated manifest\nwriteManifest(\"~/projects/shiny/app1\")\n# ...optionally customize manifest.json here...\ndeployApp(\"~/projects/shiny/app1\", manifestPath = \"manifest.json\")\n}\n}\n\\seealso{\n\\code{\\link[=applications]{applications()}}, \\code{\\link[=terminateApp]{terminateApp()}}, and \\code{\\link[=restartApp]{restartApp()}}\n\nOther Deployment functions: \n\\code{\\link{applications}()},\n\\code{\\link{deployAPI}()},\n\\code{\\link{deployDoc}()},\n\\code{\\link{deploySite}()},\n\\code{\\link{deployTFModel}()}\n}\n\\concept{Deployment functions}\n"
  },
  {
    "path": "man/deployDoc.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/deployDoc.R\n\\name{deployDoc}\n\\alias{deployDoc}\n\\title{Deploy a single document}\n\\usage{\ndeployDoc(doc, ..., logLevel = c(\"normal\", \"quiet\", \"verbose\"))\n}\n\\arguments{\n\\item{doc}{Path to the document to deploy.}\n\n\\item{...}{Additional arguments to \\code{\\link[=deployApp]{deployApp()}}. Do not supply \\code{appDir},\n\\code{appFiles}, or \\code{appPrimaryDoc}; these three parameters are automatically\ngenerated by \\code{deployDoc} from the document.}\n\n\\item{logLevel}{One of \\code{\"quiet\"}, \\code{\"normal\"} or \\code{\"verbose\"}; indicates how\nmuch logging to the console is to be performed. At \\code{\"quiet\"} reports no\ninformation; at \\code{\"verbose\"}, a full diagnostic log is captured.}\n}\n\\description{\nDeploys a single R Markdown, Quarto document, or other file (e.g. \\code{.html} or\n\\code{.pdf}).\n\nWhen deploying an \\code{.Rmd}, \\code{.Qmd}, or \\code{.html}, \\code{deployDoc()} will attempt to\nautomatically discover dependencies using \\code{\\link[rmarkdown:find_external_resources]{rmarkdown::find_external_resources()}},\nand include an \\code{.Rprofile} if present. If you find that the document is\nmissing dependencies, either specify the dependencies explicitly in the\ndocument (see \\code{\\link[rmarkdown:find_external_resources]{rmarkdown::find_external_resources()}} for details), or call\n\\code{\\link[=deployApp]{deployApp()}} directly and specify your own file list in \\code{appFiles}.\n\nSupported servers: All servers\n}\n\\examples{\n\\dontrun{\ndeployDoc(\"my-report.Rmd\")\ndeployDoc(\"static-file.html\")\n}\n}\n\\seealso{\nOther Deployment functions: \n\\code{\\link{applications}()},\n\\code{\\link{deployAPI}()},\n\\code{\\link{deployApp}()},\n\\code{\\link{deploySite}()},\n\\code{\\link{deployTFModel}()}\n}\n\\concept{Deployment functions}\n"
  },
  {
    "path": "man/deploySite.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/deploySite.R\n\\name{deploySite}\n\\alias{deploySite}\n\\title{Deploy a website}\n\\usage{\ndeploySite(\n  siteDir = getwd(),\n  siteName = NULL,\n  siteTitle = NULL,\n  account = NULL,\n  server = NULL,\n  render = c(\"none\", \"local\", \"server\"),\n  launch.browser = getOption(\"rsconnect.launch.browser\", interactive()),\n  logLevel = c(\"normal\", \"quiet\", \"verbose\"),\n  lint = FALSE,\n  metadata = list(),\n  python = NULL,\n  recordDir = NULL,\n  ...\n)\n}\n\\arguments{\n\\item{siteDir}{Directory containing website. Defaults to current directory.}\n\n\\item{siteName}{Name for the site (names must be unique within\nan account). Defaults to the base name of the specified \\code{siteDir}\nor to the name provided by a custom site generation function.}\n\n\\item{siteTitle}{Title for the site. For quarto sites only, if not\nsupplied uses the title recorded in \\verb{_quarto.yml}.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{render}{Rendering behavior for site:\n\\itemize{\n\\item \\code{\"none\"} uploads a static version of the current contents of\nthe site directory.\n\\item \\code{\"local\"} renders the site locally then uploads it.\n\\item \\code{\"server\"} uploads the source of the site to render on the server.\n}\n\nNote that for \\code{\"none\"} and \\code{\"local\"} source files (e.g. \\code{.R}, \\code{.Rmd} and\n\\code{.md}) will not be uploaded to the server.}\n\n\\item{launch.browser}{If true, the system's default web browser will be\nlaunched automatically after the app is started. Defaults to \\code{TRUE} in\ninteractive sessions only. If a function is passed, it will be called\nafter the app is started, with the app URL as a paramter.}\n\n\\item{logLevel}{One of \\code{\"quiet\"}, \\code{\"normal\"} or \\code{\"verbose\"}; indicates how\nmuch logging to the console is to be performed. At \\code{\"quiet\"} reports no\ninformation; at \\code{\"verbose\"}, a full diagnostic log is captured.}\n\n\\item{lint}{Lint the project before initiating deployment, to identify\npotentially problematic code?}\n\n\\item{metadata}{Additional metadata fields to save with the deployment\nrecord. These fields will be returned on subsequent calls to\n\\code{\\link[=deployments]{deployments()}}.\n\nMulti-value fields are recorded as comma-separated values and returned in\nthat form. Custom value serialization is the responsibility of the caller.}\n\n\\item{python}{Full path to a python binary for use by \\code{reticulate}.\nRequired if \\code{reticulate} is a dependency of the app being deployed.\nIf python = NULL, and RETICULATE_PYTHON or RETICULATE_PYTHON_FALLBACK is\nset in the environment, its value will be used. The specified python binary\nwill be invoked to determine its version and to list the python packages\ninstalled in the environment.}\n\n\\item{recordDir}{The default, \\code{NULL}, uses \\code{siteDir}.}\n\n\\item{...}{Additional arguments to \\code{\\link[=deployApp]{deployApp()}}. Do not supply \\code{appDir}\nor \\code{appFiles}; these parameters are automatically generated by\n\\code{deploySite()}.}\n}\n\\description{\nDeploy an R Markdown or quarto website to a server.\n\nSupported servers: Posit Connect, Posit Connect Cloud and ShinyApps servers\n}\n\\seealso{\nOther Deployment functions: \n\\code{\\link{applications}()},\n\\code{\\link{deployAPI}()},\n\\code{\\link{deployApp}()},\n\\code{\\link{deployDoc}()},\n\\code{\\link{deployTFModel}()}\n}\n\\concept{Deployment functions}\n"
  },
  {
    "path": "man/deployTFModel.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/deployTFModel.R\n\\name{deployTFModel}\n\\alias{deployTFModel}\n\\title{Deploy a TensorFlow saved model}\n\\usage{\ndeployTFModel(...)\n}\n\\arguments{\n\\item{...}{Additional arguments to \\code{\\link[=deployApp]{deployApp()}}.}\n}\n\\description{\nDeploys a directory containing a TensorFlow saved model.\n\nSupported servers: Posit Connect and ShinyApps servers\n}\n\\seealso{\nOther Deployment functions: \n\\code{\\link{applications}()},\n\\code{\\link{deployAPI}()},\n\\code{\\link{deployApp}()},\n\\code{\\link{deployDoc}()},\n\\code{\\link{deploySite}()}\n}\n\\concept{Deployment functions}\n"
  },
  {
    "path": "man/deployments.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/deployments.R\n\\name{deployments}\n\\alias{deployments}\n\\title{List Application Deployments}\n\\usage{\ndeployments(\n  appPath = \".\",\n  nameFilter = NULL,\n  accountFilter = NULL,\n  serverFilter = NULL,\n  excludeOrphaned = TRUE\n)\n}\n\\arguments{\n\\item{appPath}{The path to the content that was deployed, either a directory\nor an individual document.}\n\n\\item{nameFilter}{Return only deployments matching the given name (optional)}\n\n\\item{accountFilter}{Return only deployments matching the given account\n(optional)}\n\n\\item{serverFilter}{Return only deployments matching the given server\n(optional)}\n\n\\item{excludeOrphaned}{If \\code{TRUE} (the default), return only deployments\nmade by a currently registered account. Deployments made from accounts that\nare no longer registered (via e.g.\\code{\\link[=removeAccount]{removeAccount()}}) will not be\nreturned.}\n}\n\\value{\nReturns a data frame with at least following columns:\n\\tabular{ll}{\n\\code{name} \\tab Name of deployed application\\cr\n\\code{account} \\tab Account owning deployed application\\cr\n\\code{bundleId} \\tab Identifier of deployed application's bundle\\cr\n\\code{url} \\tab URL of deployed application\\cr\n\\code{deploymentFile} \\tab Name of configuration file\\cr\n}\n\nIf additional metadata has been saved with the deployment record using the\n\\code{metadata} argument to \\code{\\link[=deployApp]{deployApp()}}, the frame will include\nadditional columns.\n}\n\\description{\nList deployment records for a given application.\n\nSupported servers: All servers\n}\n\\examples{\n\\dontrun{\n\n# Return all deployments of the ~/r/myapp directory made with the 'abc'\n# account\ndeployments(\"~/r/myapp\", accountFilter=\"abc\")\n}\n}\n\\seealso{\n\\code{\\link[=applications]{applications()}} to get a list of deployments from the\nserver, and \\code{\\link[=deployApp]{deployApp()}} to create a new deployment.\n}\n"
  },
  {
    "path": "man/forgetDeployment.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/deployments.R\n\\name{forgetDeployment}\n\\alias{forgetDeployment}\n\\title{Forget Application Deployment}\n\\usage{\nforgetDeployment(\n  appPath = getwd(),\n  name = NULL,\n  account = NULL,\n  server = NULL,\n  dryRun = FALSE,\n  force = !interactive()\n)\n}\n\\arguments{\n\\item{appPath}{The path to the content that was deployed, either a directory\nor an individual document.}\n\n\\item{name}{The name of the content that was deployed (optional)}\n\n\\item{account}{The name of the account to which the content was deployed\n(optional)}\n\n\\item{server}{The name of the server to which the content was deployed\n(optional)}\n\n\\item{dryRun}{Set to TRUE to preview the files/directories to be removed\ninstead of actually removing them. Defaults to FALSE.}\n\n\\item{force}{Set to TRUE to remove files and directories without prompting.\nDefaults to FALSE in interactive sessions.}\n}\n\\value{\nNULL, invisibly.\n}\n\\description{\nForgets about an application deployment. This is useful if the application\nhas been deleted on the server, or the local deployment information needs to\nbe reset.\n\nSupported servers: All servers\n}\n\\details{\nThis method removes from disk the file containing deployment\nmetadata. If \"name\", \"account\", and \"server\" are all NULL, then all of the\ndeployments for the application are forgotten; otherwise, only the\nspecified deployment is forgotten.\n}\n"
  },
  {
    "path": "man/generateAppName.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/title.R\n\\name{generateAppName}\n\\alias{generateAppName}\n\\title{Generate Application Name}\n\\usage{\ngenerateAppName(appTitle, appPath = NULL, account = NULL, unique = TRUE)\n}\n\\arguments{\n\\item{appTitle}{A descriptive title for the application.}\n\n\\item{appPath}{The path to the application's content, either a directory\nor an individual document. Optional.}\n\n\\item{account}{The account where the application will be deployed. Optional.}\n\n\\item{unique}{Whether to try to generate a unique name.}\n}\n\\value{\nReturns a valid short name for the application.\n}\n\\description{\nGenerate a short name (identifier) for an application given an application\ntitle.\n}\n\\details{\nThis function modifies the title until it forms a suitable application name.\nSuitable application names are 3 - 64 characters long and contain only\nalphanumeric characters.\n\nThe function is intended to be used to find a name for a new application.\nIf \\code{appPath} and \\code{account} are both specified, then the returned\nname will also be unique among locally known deployments of the directory\n(note that it is not guaranteed to be unique on the server). This behavior\ncan be disabled by setting \\code{unique = FALSE}.\n}\n\\examples{\n\\dontrun{\n# Generate a short name for a sample application\ngenerateAppName(\"My Father's Country\", \"~/fathers-country\", \"myacct\")\n}\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/lint.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/lint.R\n\\name{lint}\n\\alias{lint}\n\\title{Lint a Project}\n\\usage{\nlint(project, files = NULL, appPrimaryDoc = NULL)\n}\n\\arguments{\n\\item{project}{Path to a project directory.}\n\n\\item{files}{Specific files to lint. Can be NULL, in which case all\nthe files in the directory will be linted.}\n\n\\item{appPrimaryDoc}{The primary file in the project directory. Can be NULL,\nin which case it's inferred (if possible) from the directory contents.}\n}\n\\description{\nTakes the set of active linters (see \\code{\\link[=addLinter]{addLinter()}}), and applies\nthem to all files within a project.\n\nSupported servers: All servers\n}\n"
  },
  {
    "path": "man/linter.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/lint-framework.R, R/linters.R\n\\name{linter}\n\\alias{linter}\n\\title{Create a Linter}\n\\usage{\nlinter(apply, takes, message, suggestion)\n\nlinter(apply, takes, message, suggestion)\n}\n\\arguments{\n\\item{apply}{Function that, given the content of a file, returns the indices\nat which problems were found.}\n\n\\item{takes}{Function that, given a set of paths, returns the subset of\npaths that this linter uses.}\n\n\\item{message}{Function that, given content and lines, returns an\ninformative message for the user. Typically generated with\n\\code{\\link[=makeLinterMessage]{makeLinterMessage()}}.}\n\n\\item{suggestion}{String giving a prescribed fix for the linted problem.}\n}\n\\description{\nGenerate a linter, which can identify errors or problematic regions in a\nproject.\n\nSupported servers: All servers\n\nGenerate a linter, which can identify errors or problematic regions in a\nproject.\n}\n\\examples{\naddLinter(\"no.capitals\", linter(\n\n  ## Identify lines containing capital letters -- either by name or by index\n  apply = function(content, ...) {\n    grep(\"[A-Z]\", content)\n  },\n\n  ## Only use this linter on R files (paths ending with .r or .R)\n  takes = function(paths) {\n    grep(\"[rR]$\", paths)\n  },\n\n  # Use the default message constructor\n  message = function(content, lines, ...) {\n    makeLinterMessage(\"Capital letters found on the following lines\", content, lines)\n  },\n\n  # Give a suggested prescription\n  suggest = \"Do not use capital letters in these documents.\"\n))\naddLinter(\"no.capitals\", linter(\n\n  ## Identify lines containing capital letters -- either by name or by index\n  apply = function(content, ...) {\n    grep(\"[A-Z]\", content)\n  },\n\n  ## Only use this linter on R files (paths ending with .r or .R)\n  takes = function(paths) {\n    grep(\"[rR]$\", paths)\n  },\n\n  # Use the default message constructor\n  message = function(content, lines, ...) {\n    makeLinterMessage(\"Capital letters found on the following lines\", content, lines)\n  },\n\n  # Give a suggested prescription\n  suggest = \"Do not use capital letters in these documents.\"\n))\n}\n"
  },
  {
    "path": "man/listAccountEnvVars.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/envvars.R\n\\name{listAccountEnvVars}\n\\alias{listAccountEnvVars}\n\\alias{updateAccountEnvVars}\n\\title{Maintain environment variables across multiple applications}\n\\usage{\nlistAccountEnvVars(server = NULL, account = NULL)\n\nupdateAccountEnvVars(envVars, server = NULL, account = NULL)\n}\n\\arguments{\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{envVars}{Names of environment variables to update. Their\nvalues will be automatically retrieved from the current process.\n\nIf you specify multiple environment variables, any application that\nuses any of them will be updated with all of them.}\n}\n\\value{\n\\code{listAccountEnvVars()} returns a data frame with one row\nfor each data frame. It has variables \\code{id}, \\code{guid}, \\code{name}, and\n\\code{envVars}. \\code{envVars} is a list-column.\n}\n\\description{\n\\itemize{\n\\item \\code{listAccountEnvVars()} lists the environment variables used by\nevery application published to the specified account.\n\\item \\code{updateAccountEnvVars()} updates the specified environment variables with\ntheir current values for every app that uses them.\n}\n\nSecure environment variable are currently only supported by Posit Connect\nso other server types will generate an error.\n\nSupported servers: Posit Connect servers\n}\n"
  },
  {
    "path": "man/listBundleFiles.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/bundleFiles.R\n\\name{listBundleFiles}\n\\alias{listBundleFiles}\n\\title{List Files to be Bundled}\n\\usage{\nlistBundleFiles(appDir)\n}\n\\arguments{\n\\item{appDir}{Directory containing the application.}\n}\n\\value{\nReturns a list containing the following elements:\n\\itemize{\n\\item \\code{totalFiles}: Total number of files.\n\\item \\code{totalSize}: Total size of the files (in bytes).\n\\item \\code{contents}: Paths to bundle, relative to \\code{appDir}.\n}\n}\n\\description{\n\\ifelse{html}{\\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\\strong{[Superseded]}}\n\n\\code{listBundleFiles()} has been superseded in favour of \\code{\\link[=listDeploymentFiles]{listDeploymentFiles()}}.\n\nGiven a directory containing an application, returns the names of the files\nthat by default will be bundled in the application. It works similarly to\na recursive directory listing from \\code{\\link[=list.files]{list.files()}} but enforces bundle sizes\nas described in \\code{\\link[=listDeploymentFiles]{listDeploymentFiles()}}\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/listDeploymentFiles.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/bundleFiles.R\n\\name{listDeploymentFiles}\n\\alias{listDeploymentFiles}\n\\title{Gather files to be bundled with an app}\n\\usage{\nlistDeploymentFiles(\n  appDir,\n  appFiles = NULL,\n  appFileManifest = NULL,\n  error_call = caller_env()\n)\n}\n\\arguments{\n\\item{appDir}{A directory containing an application (e.g. a Shiny app\nor plumber API). Defaults to the current directory.}\n\n\\item{appFiles, appFileManifest}{Use \\code{appFiles} to specify a\ncharacter vector of files to bundle in the app or \\code{appFileManifest}\nto provide a path to a file containing a list of such files. If neither\nare supplied, will bundle all files in \\code{appDir}, apart from standard\nexclusions and files listed in a \\code{.rscignore} file. See\n\\code{\\link[=listDeploymentFiles]{listDeploymentFiles()}} for more details.}\n\n\\item{error_call}{The call or environment for error reporting; expert\nuse only.}\n}\n\\value{\nCharacter of paths to bundle, relative to \\code{appDir}.\n}\n\\description{\nGiven an app directory, and optional \\code{appFiles} and \\code{appFileManifest}\narguments, returns vector of paths to bundle in the app. (Note that\ndocuments follow a different strategy; see \\code{\\link[=deployDoc]{deployDoc()}} for details.)\n\nWhen neither \\code{appFiles} nor \\code{appFileManifest} is supplied,\n\\code{listDeploymentFiles()} will include all files under \\code{appDir}, apart\nfrom the following:\n\\itemize{\n\\item Certain files and folders that don't need to be bundled, such as\nversion control directories, internal config files, and RStudio state,\nare automatically excluded.\n\\item You can exclude additional files by listing them in in a \\code{.rscignore}\nfile. This file must have one file or directory per line (with path\nrelative to the current directory). It doesn't support wildcards, or\nignoring files in subdirectories.\n}\n\n\\code{listDeploymentFiles()} will throw an error if the total file size exceeds\nthe maximum bundle size (as controlled by option \\code{rsconnect.max.bundle.size}),\nor the number of files exceeds the maximum file limit (as controlled by\noption \\code{rsconnect.max.bundle.files}). This prevents you from accidentally\nbundling a very large direcfory (i.e. you home directory).\n\nSupported servers: All servers\n}\n"
  },
  {
    "path": "man/makeLinterMessage.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/lint.R\n\\name{makeLinterMessage}\n\\alias{makeLinterMessage}\n\\title{Construct a Linter Message}\n\\usage{\nmakeLinterMessage(header, content, lines)\n}\n\\arguments{\n\\item{header}{A header message describing the linter.}\n\n\\item{content}{The content of the file that was linted.}\n\n\\item{lines}{The line numbers from \\code{content} that contain lint.}\n}\n\\description{\nPretty-prints a linter message. Primarily used as a helper\nfor constructing linter messages with \\code{\\link[=linter]{linter()}}.\n\nSupported servers: All servers\n}\n"
  },
  {
    "path": "man/oldApplicationConfigDir.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/configMigrate.R\n\\name{oldApplicationConfigDir}\n\\alias{oldApplicationConfigDir}\n\\title{Old Application Config Directory}\n\\usage{\noldApplicationConfigDir(appName)\n}\n\\arguments{\n\\item{appName}{The application's name (connect or rsconnect)}\n}\n\\value{\nThe old application configuration directory.\n}\n\\description{\nReturns the old application configuration directory used by rsconnect\n0.8.24 and prior. These versions wrote configuration data to XDG compliant\nlocations, but CRAN policy has since further restricted the disk locations\nthat are permitted. See:\n}\n\\details{\nhttps://cran.r-project.org/web/packages/policies.html\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/options.Rd",
    "content": "\\name{rsconnectOptions}\n\\alias{rsconnectOptions}\n\n\\title{Package Options}\n\n\\description{\nThe \\pkg{rsconnect} package supports several options that control the method used for http communications, the printing of diagnostic information for http requests, and the launching of an external browser after deployment.\n}\n\n\\details{\nSupported global options include:\n\\describe{\n   \\item{\\code{rsconnect.ca.bundle}}{Path to a custom bundle of Certificate Authority root certificates to use when connecting to servers via SSL. This option can also be specied in the environment variable \\code{RSCONNECT_CA_BUNDLE}. Leave undefined to use your system's default certificate store.}\n   \\item{\\code{rsconnect.check.certificate}}{Whether to check the SSL certificate when connecting to a remote host; defaults to \\code{TRUE}. Setting to \\code{FALSE} is insecure, but will allow you to connect to hosts using invalid certificates as a last resort.}\n   \\item{\\code{rsconnect.http.trace}}{When \\code{TRUE}, trace http calls (prints the method, path, and total milliseconds for each http request)}\n   \\item{\\code{rsconnect.http.trace.json}}{When \\code{TRUE}, trace JSON content (shows JSON payloads sent to and received from the server))}\n   \\item{\\code{rsconnect.http.verbose}}{When \\code{TRUE}, print verbose output for http connections (useful only for debugging SSL certificate or http connection problems)}\n   \\item{\\code{rsconnect.tar}}{By default, \\code{rsconnect} uses R's internal \\code{tar} implementation to compress content bundles. This may cause invalid bundles in some environments. In those cases, use this option to specify a path to an alternate \\code{tar} executable. This option can also be specified in the environment variable \\code{RSCONNECT_TAR}. Leave undefined to use the default \\code{tar} implementation.}\n   \\item{\\code{rsconnect.libcurl.options}}{A named list of additional cURL options to use when making HTTP requests. Run \\code{curl::curl_options()} to see available options.}\n   \\item{\\code{rsconnect.error.trace}}{{When \\code{TRUE}, print detailed stack traces for errors occurring during deployment.}}\n   \\item{\\code{rsconnect.launch.browser}}{When \\code{TRUE}, automatically launch a browser to view applications after they are deployed}\n   \\item{\\code{rsconnect.locale.cache}}{When \\code{FALSE}, disable the detected locale cache (Windows only). }\n   \\item{\\code{rsconnect.locale}}{Override the detected locale. }\n   \\item{\\code{rsconnect.max.bundle.size}}{The maximum size, in bytes, for deployed content. If not set, defaults to 3 GB.}\n   \\item{\\code{rsconnect.max.bundle.files}}{The maximum number of files to deploy. If not set, defaults to 10,000.}\n   \\item{\\code{rsconnect.force.update.apps}}{When \\code{TRUE}, bypasses the prompt to confirm whether you wish to update previously-deployed content}\n   \\item{\\code{rsconnect.pre.deploy}}{A function to run prior to deploying content; it receives as an argument the directory containing the content  about to be deployed.}\n   \\item{\\code{rsconnect.post.deploy}}{A function to run after successfully deploying content; it receives as an argument the directory containing the content  about to be deployed.}\n   \\item{\\code{rsconnect.python.enabled}}{When \\code{TRUE}, use the python executable specified by the \\code{RETICULATE_PYTHON} environment variable and add a \\code{python} section to the deployment manifest. By default, python is enabled when deploying to Posit Connect and disabled when deploying to shinyapps.io.}\n}\nWhen deploying content from the RStudio IDE, the rsconnect package's deployment methods are executed in a vanilla R session that doesn't execute startup scripts. This can make it challenging to ensure options are set properly prior to push-button deployment, so the rsconnect package has a parallel set of ``startup'' scripts it runs prior to deploying. The follow are run in order, if they exist, prior to deployment:\n\\describe{\n    \\item{\\code{$R_HOME/etc/rsconnect.site}}{Like \\code{Rprofile.site}; for site-wide pre-flight and options.}\n    \\item{\\code{~/.rsconnect_profile}}{Like \\code{.Rprofile}; for user-specific content.}\n    \\item{\\code{$PROJECT/.rsconnect_profile}}{Like \\code{.Rprofile} for projects; \\code{$PROJECT} here refers to the root directory of the content being deployed.}\n}\nNote that, unlike \\code{.Rprofile}, these files don't replace each other; \\emph{all three} will be run if they exist.\n}\n\n\\examples{\n\\dontrun{\n\n# trace http requests\noptions(rsconnect.http.trace = TRUE)\n\n# print verbose output for http requests\noptions(rsconnect.http.verbose = TRUE)\n\n# print JSON content\noptions(rsconnect.http.trace.json = TRUE)\n\n# don't automatically launch a browser after deployment\noptions(rsconnect.launch.browser = FALSE)\n}\n}\n\n"
  },
  {
    "path": "man/purgeApp.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/purgeApp.R\n\\name{purgeApp}\n\\alias{purgeApp}\n\\title{Purge an Application}\n\\usage{\npurgeApp(appName, account = NULL, server = NULL, quiet = FALSE)\n}\n\\arguments{\n\\item{appName}{Name of application to purge}\n\n\\item{account}{Account name. If a single account is registered on the system\nthen this parameter can be omitted.}\n\n\\item{server}{Server name. Required only if you use the same account name on\nmultiple servers (see \\code{\\link[=servers]{servers()}})}\n\n\\item{quiet}{Request that no status information be printed to the console\nduring the termination.}\n}\n\\description{\nPurge a currently archived ShinyApps application.\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function only works for ShinyApps servers.\n}\n\\examples{\n\\dontrun{\n\n# purge an application\npurgeApp(\"myapp\")\n}\n}\n\\seealso{\n\\code{\\link[=applications]{applications()}}, \\code{\\link[=deployApp]{deployApp()}}, and\n\\code{\\link[=restartApp]{restartApp()}}\n}\n"
  },
  {
    "path": "man/removeAuthorizedUser.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/auth.R\n\\name{removeAuthorizedUser}\n\\alias{removeAuthorizedUser}\n\\title{Remove authorized user from an application}\n\\usage{\nremoveAuthorizedUser(\n  user,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n)\n}\n\\arguments{\n\\item{user}{The user to remove. Can be id or email address.}\n\n\\item{appDir}{Directory containing application. Defaults to\ncurrent working directory.}\n\n\\item{appName}{Name of application.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n}\n\\description{\nRemove authorized user from an application\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n\\seealso{\n\\code{\\link[=addAuthorizedUser]{addAuthorizedUser()}} and \\code{\\link[=showUsers]{showUsers()}}\n}\n"
  },
  {
    "path": "man/resendInvitation.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/auth.R\n\\name{resendInvitation}\n\\alias{resendInvitation}\n\\title{Resend invitation for invited users of an application}\n\\usage{\nresendInvitation(\n  invite,\n  regenerate = FALSE,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n)\n}\n\\arguments{\n\\item{invite}{The invitation to resend. Can be id or email address.}\n\n\\item{regenerate}{Regenerate the invite code. Can be helpful is the\ninvitation has expired.}\n\n\\item{appDir}{Directory containing application. Defaults to\ncurrent working directory.}\n\n\\item{appName}{Name of application.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n}\n\\description{\nResend invitation for invited users of an application\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n\\seealso{\n\\code{\\link[=showInvited]{showInvited()}}\n}\n"
  },
  {
    "path": "man/restartApp.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/restartApp.R\n\\name{restartApp}\n\\alias{restartApp}\n\\title{Restart an Application}\n\\usage{\nrestartApp(appName, account = NULL, server = NULL, quiet = FALSE)\n}\n\\arguments{\n\\item{appName}{Name of application to restart}\n\n\\item{account}{Account name. If a single account is registered on the system\nthen this parameter can be omitted.}\n\n\\item{server}{Server name. Required only if you use the same account name on\nmultiple servers (see \\code{\\link[=servers]{servers()}})}\n\n\\item{quiet}{Request that no status information be printed to the console\nduring the operation.}\n}\n\\description{\nRestart an application currently running on a remote server.\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n\\examples{\n\\dontrun{\n\n# restart an application\nrestartApp(\"myapp\")\n}\n}\n\\seealso{\n\\code{\\link[=applications]{applications()}}, \\code{\\link[=deployApp]{deployApp()}}, and\n\\code{\\link[=terminateApp]{terminateApp()}}\n}\n"
  },
  {
    "path": "man/rpubsUpload.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/rpubs.R\n\\name{rpubsUpload}\n\\alias{rpubsUpload}\n\\title{Upload a file to RPubs}\n\\usage{\nrpubsUpload(title, contentFile, originalDoc, id = NULL, properties = list())\n}\n\\arguments{\n\\item{title}{The title of the document.}\n\n\\item{contentFile}{The path to the content file to upload.}\n\n\\item{originalDoc}{The document that was rendered to produce the\n\\code{contentFile}. May be \\code{NULL} if the document is not known.}\n\n\\item{id}{If this upload is an update of an existing document then the id\nparameter should specify the document id to update. Note that the id is\nprovided as an element of the list returned by successful calls to\n\\code{rpubsUpload}.}\n\n\\item{properties}{A named list containing additional document properties\n(RPubs doesn't currently expect any additional properties, this parameter\nis reserved for future use).}\n}\n\\value{\nA named list. If the upload was successful then the list contains a\n\\code{id} element that can be used to subsequently update the document as\nwell as a \\code{continueUrl} element that provides a URL that a browser\nshould be opened to in order to complete publishing of the document. If the\nupload fails then the list contains an \\code{error} element which contains\nan explanation of the error that occurred.\n}\n\\description{\nThis function publishes a file to rpubs.com. If the upload succeeds a\nlist that includes an \\code{id} and \\code{continueUrl} is returned. A browser\nshould be opened to the \\code{continueUrl} to complete publishing of the\ndocument. If an error occurs then a diagnostic message is returned in the\n\\code{error} element of the list.\n\nSupported servers: RPubs servers\n}\n\\examples{\n\\dontrun{\n# upload a document\nresult <- rpubsUpload(\"My document title\", \"Document.html\")\nif (!is.null(result$continueUrl))\n   browseURL(result$continueUrl)\nelse\n   stop(result$error)\n\n# update the same document with a new title\nupdateResult <- rpubsUpload(\"My updated title\", \"Document.html\",\n                            id = result$id)\n}\n}\n"
  },
  {
    "path": "man/rsconnect-package.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/rsconnect-package.R\n\\docType{package}\n\\name{rsconnect-package}\n\\alias{rsconnect}\n\\alias{rsconnect-package}\n\\title{rsconnect: Deploy Docs, Apps, and APIs to 'Posit Connect', 'shinyapps.io', and 'RPubs'}\n\\description{\n\\if{html}{\\figure{logo.png}{options: style='float: right' alt='logo' width='120'}}\n\nProgrammatic deployment interface for 'RPubs', 'shinyapps.io', and 'Posit Connect'. Supported content types include R Markdown documents, Shiny applications, Plumber APIs, plots, and static web content.\n}\n\\seealso{\nUseful links:\n\\itemize{\n  \\item \\url{https://rstudio.github.io/rsconnect/}\n  \\item \\url{https://github.com/rstudio/rsconnect}\n  \\item Report bugs at \\url{https://github.com/rstudio/rsconnect/issues}\n}\n\n}\n\\author{\n\\strong{Maintainer}: Aron Atkins \\email{aron@posit.co}\n\nAuthors:\n\\itemize{\n  \\item Toph Allen\n  \\item Hadley Wickham\n  \\item Jonathan McPherson\n  \\item JJ Allaire\n}\n\nOther contributors:\n\\itemize{\n  \\item Posit Software, PBC [copyright holder, funder]\n}\n\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/rsconnectConfigDir.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/config.R\n\\name{rsconnectConfigDir}\n\\alias{rsconnectConfigDir}\n\\title{rsconnect Configuration Directory}\n\\usage{\nrsconnectConfigDir(subDir = NULL)\n}\n\\arguments{\n\\item{subDir}{An optional subdirectory to be included as the last element of\nthe path.}\n}\n\\value{\nThe path to the configuration directory.\n}\n\\description{\nForms the path to a location on disk where user-level configuration data for\nthe package is stored.\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/rsconnectPackages.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/rsconnect-package.R\n\\name{rsconnectPackages}\n\\alias{rsconnectPackages}\n\\title{Using Packages with rsconnect}\n\\description{\nSee ?\\code{\\link[=appDependencies]{appDependencies()}}\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/rsconnectProxies.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/rsconnect-package.R\n\\name{rsconnectProxies}\n\\alias{rsconnectProxies}\n\\title{HTTP Proxy Configuration}\n\\description{\nPlease see \\code{vignette(\"custom-http\")}.\n}\n\\keyword{internal}\n"
  },
  {
    "path": "man/servers.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/servers.R\n\\name{servers}\n\\alias{servers}\n\\alias{serverInfo}\n\\title{Server metadata}\n\\usage{\nservers(local = FALSE)\n\nserverInfo(name = NULL)\n}\n\\arguments{\n\\item{local}{Return only local servers? (i.e. not automatically registered\ncloud servers)}\n\n\\item{name}{Server name. If omitted, you'll be prompted to pick a server.}\n}\n\\value{\n\\code{servers()} returns a data frame with registered server names and URLs.\n\\code{serverInfo()} returns a list with details for a particular server.\n}\n\\description{\n\\code{servers()} lists all known servers; \\code{serverInfo()} gets metadata about\na specific server. Cloud server \\code{shinyapps.io} is always automatically\nregistered and available.\n\nSupported servers: All servers\n}\n\\examples{\n# List all registered servers\nservers()\n\n# Get information about a server\nserverInfo(\"shinyapps.io\")\n}\n"
  },
  {
    "path": "man/setAccountInfo.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/accounts.R\n\\name{setAccountInfo}\n\\alias{setAccountInfo}\n\\title{Register account on shinyapps.io}\n\\usage{\nsetAccountInfo(name, token, secret, server = \"shinyapps.io\")\n}\n\\arguments{\n\\item{name}{Name of account to save or remove}\n\n\\item{token}{User token for the account}\n\n\\item{secret}{User secret for the account}\n\n\\item{server}{Server to associate account with.}\n}\n\\description{\nConfigure a ShinyApps account for publishing from this system.\n\nSupported servers: ShinyApps servers\n}\n\\examples{\n\\dontrun{\n\n# register an account\nsetAccountInfo(\"user\", \"token\", \"secret\")\n\n# remove the same account\nremoveAccount(\"user\")\n}\n\n}\n\\seealso{\nOther Account functions: \n\\code{\\link{accounts}()},\n\\code{\\link{connectApiUser}()},\n\\code{\\link{connectCloudUser}()}\n}\n\\concept{Account functions}\n"
  },
  {
    "path": "man/setProperty.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/configureApp.R\n\\name{setProperty}\n\\alias{setProperty}\n\\title{Set Application property}\n\\usage{\nsetProperty(\n  propertyName,\n  propertyValue,\n  appPath = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  force = FALSE\n)\n}\n\\arguments{\n\\item{propertyName}{Name of property}\n\n\\item{propertyValue}{Property value}\n\n\\item{appPath}{Directory or file that was deployed. Defaults to current\nworking directory.}\n\n\\item{appName}{Name of application}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{force}{Forcibly set the property}\n}\n\\description{\nSet a property on currently deployed ShinyApps application.\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function only works for ShinyApps servers.\n}\n\\examples{\n\\dontrun{\n\n# set instance size for an application\nsetProperty(\"application.instances.count\", 1)\n\n# disable application package cache\nsetProperty(\"application.package.cache\", FALSE)\n\n}\n}\n"
  },
  {
    "path": "man/showInvited.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/auth.R\n\\name{showInvited}\n\\alias{showInvited}\n\\title{List invited users for an application}\n\\usage{\nshowInvited(appDir = getwd(), appName = NULL, account = NULL, server = NULL)\n}\n\\arguments{\n\\item{appDir}{Directory containing application. Defaults to\ncurrent working directory.}\n\n\\item{appName}{Name of application.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n}\n\\description{\nList invited users for an application\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n\\seealso{\n\\code{\\link[=addAuthorizedUser]{addAuthorizedUser()}} and \\code{\\link[=showUsers]{showUsers()}}\n}\n"
  },
  {
    "path": "man/showLogs.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/applications.R\n\\name{showLogs}\n\\alias{showLogs}\n\\alias{getLogs}\n\\title{Application Logs}\n\\usage{\nshowLogs(\n  appPath = getwd(),\n  appFile = NULL,\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  entries = 50,\n  streaming = FALSE\n)\n\ngetLogs(\n  appPath = getwd(),\n  appFile = NULL,\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  entries = 50\n)\n}\n\\arguments{\n\\item{appPath}{The path to the directory or file that was deployed.}\n\n\\item{appFile}{The path to the R source file that contains the application\n(for single file applications).}\n\n\\item{appName}{The name of the application to show logs for. May be omitted\nif only one application deployment was made from \\code{appPath}.}\n\n\\item{account}{The account under which the application was deployed. May be\nomitted if only one account is registered on the system.}\n\n\\item{server}{Server name. Required only if you use the same account name on\nmultiple servers.}\n\n\\item{entries}{The number of log entries to show. Defaults to 50 entries.}\n\n\\item{streaming}{Deprecated. Streaming logs is not currently supported\nas the ShinyApps.io backend no longer supports this feature. If \\code{TRUE},\nan error will be thrown. Defaults to \\code{FALSE}.}\n}\n\\value{\n\\code{getLogs()} returns a data frame containing the logged lines.\n}\n\\description{\nThese functions provide access to the logs for deployed ShinyApps applications:\n\\itemize{\n\\item \\code{showLogs()} displays the logs.\n\\item \\code{getLogs()} returns the logged lines.\n}\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThese functions only work\nfor applications deployed to ShinyApps.io.\n}\n"
  },
  {
    "path": "man/showMetrics.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/usage.R\n\\name{showMetrics}\n\\alias{showMetrics}\n\\title{Show Application Metrics}\n\\usage{\nshowMetrics(\n  metricSeries,\n  metricNames,\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = \"shinyapps.io\",\n  from = NULL,\n  until = NULL,\n  interval = NULL\n)\n}\n\\arguments{\n\\item{metricSeries}{Metric series to query. Refer to the\n\\href{https://docs.posit.co/shinyapps.io/metrics.html#ApplicationMetrics}{shinyapps.io documentation}\nfor available series.}\n\n\\item{metricNames}{Metric names in the series to query. Refer to the\n\\href{https://docs.posit.co/shinyapps.io/metrics.html#ApplicationMetrics}{shinyapps.io documentation}\nfor available metrics.}\n\n\\item{appDir}{A directory containing an application (e.g. a Shiny app\nor plumber API). Defaults to the current directory.}\n\n\\item{appName}{Application name, a string consisting of letters, numbers,\n\\verb{_} and \\code{-}. The application name is used to identify applications on a\nserver, so must be unique.\n\nIf not specified, the first deployment will be automatically it from the\n\\code{appDir} for directory and website, and from the \\code{appPrimaryDoc} for\ndocument. On subsequent deploys, it will use the previously stored value.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{from}{Date range starting timestamp (Unix timestamp or relative time\ndelta such as \"2d\" or \"3w\").}\n\n\\item{until}{Date range ending timestamp (Unix timestamp or relative time\ndelta such as \"2d\" or \"3w\").}\n\n\\item{interval}{Summarization interval. Data points at intervals less then this\nwill be grouped. (Relative time delta e.g. \"120s\" or \"1h\" or \"30d\").}\n}\n\\description{\nShow application metrics of a currently deployed application.\n\nSupported servers: ShinyApps servers\n}\n"
  },
  {
    "path": "man/showProperties.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/configureApp.R\n\\name{showProperties}\n\\alias{showProperties}\n\\title{Show Application property}\n\\usage{\nshowProperties(\n  appPath = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL\n)\n}\n\\arguments{\n\\item{appPath}{Directory or file that was deployed. Defaults to current\nworking directory.}\n\n\\item{appName}{Name of application}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n}\n\\description{\nShow properties of an application deployed to ShinyApps.\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n"
  },
  {
    "path": "man/showUsage.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/usage.R\n\\name{showUsage}\n\\alias{showUsage}\n\\title{Show Application Usage}\n\\usage{\nshowUsage(\n  appDir = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  usageType = \"hours\",\n  from = NULL,\n  until = NULL,\n  interval = NULL\n)\n}\n\\arguments{\n\\item{appDir}{Directory containing application. Defaults to\ncurrent working directory.}\n\n\\item{appName}{Name of application}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{usageType}{Use metric to retreive (for example: \"hours\")}\n\n\\item{from}{Date range starting timestamp (Unix timestamp or relative time\ndelta such as \"2d\" or \"3w\").}\n\n\\item{until}{Date range ending timestamp (Unix timestamp or relative time\ndelta such as \"2d\" or \"3w\").}\n\n\\item{interval}{Summarization interval. Data points at intervals less then this\nwill be grouped. (Relative time delta e.g. \"120s\" or \"1h\" or \"30d\").}\n}\n\\description{\nShow application usage of a currently deployed application\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function only works for ShinyApps servers.\n}\n"
  },
  {
    "path": "man/showUsers.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/auth.R\n\\name{showUsers}\n\\alias{showUsers}\n\\title{List authorized users for an application}\n\\usage{\nshowUsers(appDir = getwd(), appName = NULL, account = NULL, server = NULL)\n}\n\\arguments{\n\\item{appDir}{Directory containing application. Defaults to\ncurrent working directory.}\n\n\\item{appName}{Name of application.}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n}\n\\description{\nList authorized users for an application\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only for ShinyApps servers.\n}\n\\seealso{\n\\code{\\link[=addAuthorizedUser]{addAuthorizedUser()}} and \\code{\\link[=showInvited]{showInvited()}}\n}\n"
  },
  {
    "path": "man/syncAppMetadata.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/applications.R\n\\name{syncAppMetadata}\n\\alias{syncAppMetadata}\n\\title{Update deployment records}\n\\usage{\nsyncAppMetadata(appPath = \".\")\n}\n\\arguments{\n\\item{appPath}{The path to the directory or file that was deployed.}\n}\n\\description{\nUpdate the deployment records for applications published to Posit Connect.\nThis updates application title and URL, and deletes records for deployments\nwhere the application has been deleted on the server.\n\nSupported servers: Posit Connect servers\n}\n"
  },
  {
    "path": "man/taskLog.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/tasks.R\n\\name{taskLog}\n\\alias{taskLog}\n\\title{Show task log}\n\\usage{\ntaskLog(taskId, account = NULL, server = NULL, output = NULL)\n}\n\\arguments{\n\\item{taskId}{Task Id}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{output}{Where to write output. Valid values are \\code{NULL} or \\code{stderr}}\n}\n\\description{\nWrites the task log for the given task\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only with shinyapps.io.\n}\n\\examples{\n\\dontrun{\n\n# write task log to stdout\ntaskLog(12345)\n\n# write task log to stderr\ntaskLog(12345, output=\"stderr\")\n\n}\n}\n\\seealso{\n\\code{\\link[=tasks]{tasks()}}\n}\n"
  },
  {
    "path": "man/tasks.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/tasks.R\n\\name{tasks}\n\\alias{tasks}\n\\title{List Tasks}\n\\usage{\ntasks(account = NULL, server = NULL)\n}\n\\arguments{\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n}\n\\value{\nReturns a data frame with the following columns:\n\\tabular{ll}{\n\\code{id} \\tab Task id \\cr\n\\code{action} \\tab Task action\\cr\n\\code{status} \\tab Current task status\\cr\n\\code{created_time} \\tab Task creation time\\cr\n\\code{finished_time} \\tab Task finished time\\cr\n}\n}\n\\description{\nList Tasks\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function works only with shinyapps.io.\n}\n\\examples{\n\\dontrun{\n\n# list tasks for the default account\ntasks()\n\n}\n}\n\\seealso{\n\\code{\\link[=taskLog]{taskLog()}}\n}\n"
  },
  {
    "path": "man/terminateApp.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/terminateApp.R\n\\name{terminateApp}\n\\alias{terminateApp}\n\\title{Terminate an Application}\n\\usage{\nterminateApp(appName, account = NULL, server = NULL, quiet = FALSE)\n}\n\\arguments{\n\\item{appName}{Name of application to terminate}\n\n\\item{account}{Account name. If a single account is registered on the system\nthen this parameter can be omitted.}\n\n\\item{server}{Server name. Required only if you use the same account name on\nmultiple servers (see \\code{\\link[=servers]{servers()}})}\n\n\\item{quiet}{Request that no status information be printed to the console\nduring the termination.}\n}\n\\description{\nTerminate and archive a currently deployed ShinyApps application.\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function only works for ShinyApps servers.\n}\n\\examples{\n\\dontrun{\n\n# terminate an application\nterminateApp(\"myapp\")\n}\n}\n\\seealso{\n\\code{\\link[=applications]{applications()}}, \\code{\\link[=deployApp]{deployApp()}}, and\n\\code{\\link[=restartApp]{restartApp()}}\n}\n"
  },
  {
    "path": "man/unsetProperty.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/configureApp.R\n\\name{unsetProperty}\n\\alias{unsetProperty}\n\\title{Unset Application property}\n\\usage{\nunsetProperty(\n  propertyName,\n  appPath = getwd(),\n  appName = NULL,\n  account = NULL,\n  server = NULL,\n  force = FALSE\n)\n}\n\\arguments{\n\\item{propertyName}{Name of property}\n\n\\item{appPath}{Directory or file that was deployed. Defaults to current\nworking directory.}\n\n\\item{appName}{Name of application}\n\n\\item{account, server}{Uniquely identify a remote server with either your\nuser \\code{account}, the \\code{server} name, or both. If neither are supplied, and\nthere are multiple options, you'll be prompted to pick one.\n\nUse \\code{\\link[=accounts]{accounts()}} to see the full list of available options.}\n\n\\item{force}{Forcibly unset the property}\n}\n\\description{\nUnset a property on currently deployed ShinyApps application (restoring to\nits default value)\n\nSupported servers: ShinyApps servers\n}\n\\note{\nThis function only works for ShinyApps servers.\n}\n\\examples{\n\\dontrun{\n\n# unset application package cache property to revert to default\nunsetProperty(\"application.package.cache\")\n\n}\n}\n"
  },
  {
    "path": "man/writeManifest.Rd",
    "content": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/writeManifest.R\n\\name{writeManifest}\n\\alias{writeManifest}\n\\title{Create a \\code{manifest.json}}\n\\usage{\nwriteManifest(\n  appDir = getwd(),\n  appFiles = NULL,\n  appFileManifest = NULL,\n  appPrimaryDoc = NULL,\n  appMode = NULL,\n  contentCategory = NULL,\n  python = NULL,\n  forceGeneratePythonEnvironment = FALSE,\n  quarto = NA,\n  image = NULL,\n  envManagement = NULL,\n  envManagementR = NULL,\n  envManagementPy = NULL,\n  envManagementNodejs = NULL,\n  packageRepositoryResolutionR = NULL,\n  dependencyResolution = c(\"strict\", \"library\"),\n  verbose = FALSE,\n  quiet = FALSE\n)\n}\n\\arguments{\n\\item{appDir}{A directory containing an application (e.g. a Shiny app\nor plumber API). Defaults to the current directory.}\n\n\\item{appFiles, appFileManifest}{Use \\code{appFiles} to specify a\ncharacter vector of files to bundle in the app or \\code{appFileManifest}\nto provide a path to a file containing a list of such files. If neither\nare supplied, will bundle all files in \\code{appDir}, apart from standard\nexclusions and files listed in a \\code{.rscignore} file. See\n\\code{\\link[=listDeploymentFiles]{listDeploymentFiles()}} for more details.}\n\n\\item{appPrimaryDoc}{If the application contains more than one document, this\nparameter indicates the primary one, as a path relative to \\code{appDir}. Can be\n\\code{NULL}, in which case the primary document is inferred from the contents\nbeing deployed.}\n\n\\item{appMode}{Optional; the type of content being deployed.\nProvide this option when the inferred type of content is incorrect. This\ncan happen, for example, when static HTML content includes a downloadable\nShiny application \\code{app.R}. Accepted values include \\code{\"shiny\"}, \\code{\"api\"},\n\\code{\"rmd-static\"}, \\code{\"rmd-shiny\"}, \\code{\"quarto-static\"}, \\code{\"quarto-shiny\"},\n\\code{\"nodejs\"}, and \\code{\"static\"}. The Posit Connect API Reference contains a\nfull set of available values. Not all servers support all types of content.}\n\n\\item{contentCategory}{Set this to \\code{\"site\"} if you'd deploy with\n\\code{\\link[=deploySite]{deploySite()}}; otherwise leave as is.}\n\n\\item{python}{Full path to a python binary for use by \\code{reticulate}.\nRequired if \\code{reticulate} is a dependency of the app being deployed.\nIf python = NULL, and RETICULATE_PYTHON or RETICULATE_PYTHON_FALLBACK is\nset in the environment, its value will be used. The specified python binary\nwill be invoked to determine its version and to list the python packages\ninstalled in the environment.}\n\n\\item{forceGeneratePythonEnvironment}{Optional. If an existing\n\\code{requirements.txt} file is found, it will be overwritten when this argument\nis \\code{TRUE}.}\n\n\\item{quarto}{Should the deployed content be built by quarto?\n(\\code{TRUE}, \\code{FALSE}, or \\code{NA}). The default, \\code{NA}, will use quarto if\nthere are \\code{.qmd} files in the bundle, or if there is a\n\\verb{_quarto.yml} and \\code{.Rmd} files.\n\n(This option is ignored and quarto will always be used if the\n\\code{metadata} contains \\code{quarto_version} and \\code{quarto_engines} fields.)}\n\n\\item{image}{Optional. The name of the image to use when building and\nexecuting this content. If none is provided, Posit Connect will\nattempt to choose an image based on the content requirements. You can\noverride the default by setting the environment variable \\code{RSCONNECT_IMAGE}.}\n\n\\item{envManagement}{Optional. Should Posit Connect install packages\nfor this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}).\nThe default, \\code{NULL}, will not write any values to the bundle manifest,\nand Connect will fall back to the application default environment\nmanagement strategy, or the server default if no application default\nis defined.\n\n(This option is a shorthand flag which overwrites the values of\n\\code{envManagementR}, \\code{envManagementPy}, and \\code{envManagementNodejs}.)}\n\n\\item{envManagementR}{Optional. Should Posit Connect install R packages\nfor this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}). The default, \\code{NULL}, will\nnot write any values to the bundle manifest, and Connect will fall back to\nthe application default R environment management strategy, or the server\ndefault if no application default is defined.\n\n(This option is ignored when \\code{envManagement} is non-\\code{NULL}.)}\n\n\\item{envManagementPy}{Optional. Should Posit Connect install Python packages\nfor this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}). The default, \\code{NULL}, will\nnot write any values to the bundle manifest, and Connect will fall back to\nthe application default Python environment management strategy, or the\nserver default if no application default is defined.\n\n(This option is ignored when \\code{envManagement} is non-\\code{NULL}.)}\n\n\\item{envManagementNodejs}{Optional. Should Posit Connect install Node.js\npackages for this content? (\\code{TRUE}, \\code{FALSE}, or \\code{NULL}). The default,\n\\code{NULL}, will not write any values to the bundle manifest, and Connect will\nfall back to the application default Node.js environment management\nstrategy, or the server default if no application default is defined.\n\n(This option is ignored when \\code{envManagement} is non-\\code{NULL}.)}\n\n\\item{packageRepositoryResolutionR}{Optional. Specifies the package repository\nresolution strategy for R packages. Must be one of \\code{\"lax\"}, \\code{\"strict\"},\n\\code{\"legacy\"}, \\code{\"lockfile\"}, or \\code{NULL}. The default, \\code{NULL}, will not write\nany values to the bundle manifest and Connect will fall back to the\nserver's package repository resolution strategy.}\n\n\\item{dependencyResolution}{Controls how R package dependencies are resolved.\nMust be one of \\code{\"strict\"} or \\code{\"library\"}. When \\code{\"strict\"}, the\n\\code{renv.lock} file is used if present and must match the local library.\nWhen \\code{\"library\"}, the lockfile is ignored and dependencies are resolved\nfrom the locally installed library instead. This is useful when the\nlockfile is out of sync with the local library and cannot be updated\n(e.g. in CI/CD environments). Note that the deployed content will\nreflect the local library, not the lockfile.\nDefaults to \\code{\"strict\"}.}\n\n\\item{verbose}{If \\code{TRUE}, prints detailed progress messages.}\n\n\\item{quiet}{If \\code{FALSE}, prints progress messages.}\n}\n\\description{\nUse \\code{writeManifest()} to generate a \\code{manifest.json}. Among other things,\nyou can commit this file to git to activate\n\\href{https://docs.posit.co/connect/user/git-backed/}{Git-Backed content}\nfor Posit Connect.\n\n\\code{manifest.json} contains a list of all files in the app along with their\ndependencies, so you will need to re-run \\code{writeManifest()} when either of\nthese change.\n\nSupported servers: All servers\n}\n"
  },
  {
    "path": "revdep/.gitignore",
    "content": "checks\nlibrary\nchecks.noindex\nlibrary.noindex\ncloud.noindex\ndata.sqlite\n*.html\n"
  },
  {
    "path": "revdep/README.md",
    "content": "# Revdeps\n\n## New problems (1)\n\n|package     |version |error |warning |note |\n|:-----------|:-------|:-----|:-------|:----|\n|[Rsconctdply](problems.md#rsconctdply)|0.1.3   |      |__+1__  |-1   |\n\n"
  },
  {
    "path": "revdep/cran.md",
    "content": "## revdepcheck results\n\nWe checked 24 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package.\n\n * We saw 1 new problems\n * We failed to check 0 packages\n\nIssues with CRAN packages are summarised below.\n\n### New problems\n(This reports the first line of each new failure)\n\n* Rsconctdply\n  checking dependencies in R code ... WARNING\n\n"
  },
  {
    "path": "revdep/failures.md",
    "content": "*Wow, no problems at all. :)*"
  },
  {
    "path": "revdep/problems.md",
    "content": "# Rsconctdply (0.1.3)\n\n* Email: <mailto:schimata@yotabitesllc.com>\n* GitHub mirror: <https://github.com/cran/Rsconctdply>\n\nRun `revdepcheck::cloud_details(, \"Rsconctdply\")` for more info\n\n## Newly broken\n\n*   checking dependencies in R code ... WARNING\n     ```\n     Namespace in Imports field not imported from: ‘dplyr’\n       All declared Imports should be used.\n     Missing or unexported object: ‘rsconnect::addConnectServer’\n     ```\n\n## Newly fixed\n\n*   checking dependencies in R code ... NOTE\n     ```\n     Namespace in Imports field not imported from: ‘dplyr’\n       All declared Imports should be used.\n     ```\n\n"
  },
  {
    "path": "rsconnect.Rproj",
    "content": "Version: 1.0\nProjectId: 3d8f30a6-9562-4255-8985-ba9413556e97\n\nRestoreWorkspace: No\nSaveWorkspace: No\nAlwaysSaveHistory: Yes\n\nEnableCodeIndexing: Yes\nUseSpacesForTab: Yes\nNumSpacesForTab: 2\nEncoding: UTF-8\n\nRnwWeave: Sweave\nLaTeX: pdfLaTeX\n\nAutoAppendNewline: Yes\nStripTrailingWhitespace: Yes\n\nBuildType: Package\nPackageUseDevtools: Yes\nPackageInstallArgs: --no-multiarch --with-keep.source\nPackageCheckArgs: --as-cran\nPackageRoxygenize: rd,collate,namespace\n"
  },
  {
    "path": "tests/integration/example-shiny/app.R",
    "content": "shinyApp(\n  ui = fluidPage(\"Hello\"),\n  server = function(input, output) {}\n)\n"
  },
  {
    "path": "tests/integration/setup.R",
    "content": "library(testthat)\nlibrary(rsconnect)\n\n# options(\"rsconnect.httr2\" = FALSE)\n\n# Configure the account() for testing, with cleanup\nserver <- Sys.getenv(\"CONNECT_SERVER\")\napiKey <- Sys.getenv(\"CONNECT_API_KEY\")\nif (server == \"\" || apiKey == \"\") {\n  stop(\n    \"CONNECT_SERVER and CONNECT_API_KEY must be set to run integration tests.\"\n  )\n}\n\n# Generate a unique account name\naccount <- paste0(\"testing\", strftime(Sys.time(), \"%Y%m%d%H%M%S\"))\n\naddServer(\n  server,\n  name = account,\n  quiet = TRUE\n)\nconnectApiUser(\n  account = account,\n  server = account,\n  apiKey = apiKey,\n  quiet = TRUE\n)\nwithr::defer(\n  {\n    removeAccount(account)\n    removeServer(account)\n    # also clean up any artifacts left behind\n    # do it twice, first to remove files, second to remove empty dirs\n    file.remove(\n      grep(\n        \"rsconnect/testing20\",\n        list.files(recursive = TRUE),\n        value = TRUE\n      )\n    )\n    dirs <- grep(\n      \"rsconnect/testing20\",\n      list.files(recursive = TRUE, include.dirs = TRUE),\n      value = TRUE\n    )\n    # remove those dirs, then the rsconnect dir if empty\n    file.remove(dirs)\n    file.remove(dirname(dirs))\n  },\n  teardown_env()\n)\n"
  },
  {
    "path": "tests/integration/test-deploy.R",
    "content": "test_that(\"deploy does not error\", {\n  # Also test verbose logging\n  expect_true(deployApp(\n    \"example-shiny\",\n    appTitle = \"Test\",\n    account = account,\n    logLevel = \"verbose\"\n  ))\n})\n\ntest_that(\"re-deploy does not error\", {\n  # Let's exercise the env vars as well\n  expect_true(deployApp(\"example-shiny\", envVars = \"TEST\", account = account))\n})\n\ntest_that(\"listAccountEnvVars\", {\n  envs <- listAccountEnvVars(account = account)\n  expect_true(nrow(envs) == 1)\n})\n"
  },
  {
    "path": "tests/manual/appMode.Rmd",
    "content": "# appMode overrides\n\nThese test deploying one project to multiple targets having different types,\nconfirming `deployApp()` support for its `appMode`.\n\n1. Create a new project.\n\n    ```r\n    usethis::create_project(\"~/Desktop/appmode-lies\")\n    ```\n\n2. Populate that project with content.\n\n    ```r\n    writeLines(c(\n        \"shinyApp(ui = fluidPage('Shiny lying liars lie.'), server = function(input, output){})\"\n    ), \"app.R\")\n    writeLines(c(\n        \"<html>\",\n        \"<head><title>appmode lies</title></head>\",\n        \"<body>\",\n        \"<h1>Static lying liars lie.</h1>\",\n        \"<a href='app.R'>app.R</a>,\",\n        \"<a href='plumber.R'>plumber.R</a>,\",\n        \"<a href='report.Rmd'>report.Rmd</a>\",\n        \"</body>\",\n        \"</html>\"\n    ), \"index.html\")\n    writeLines(c(\n        \"---\",\n        \"title: Reporting lying liars lie.\",\n        \"---\",\n        \"\",\n        \"Reporting lying liars lie.\"\n    ), \"report.Rmd\")\n    writeLines(c(\n        \"#* @get /lie\",\n        \"function() { 'API lying liars lie.' }\"\n    ), \"plumber.R\")\n\n3. Deploy this content in a variety of ways.\n\n    ```r\n    account <- \"<ACCOUNTNAME>\"\n    server <- \"<SERVERNAME>\"\n    \n    rsconnect::deployApp(\n        appTitle = \"lying: inference\",\n        appFiles = c(\"app.R\", \"index.html\", \"plumber.R\", \"report.Rmd\"),\n        account = account,\n        server = server\n    )\n    \n    rsconnect::deployApp(\n        appName = \"appmode-lies-plumber\",\n        appTitle = \"lying: plumber\",\n        appFiles = c(\"app.R\", \"index.html\", \"plumber.R\", \"report.Rmd\"),\n        appMode = \"api\",\n        account = account,\n        server = server\n    )\n    \n    rsconnect::deployApp(\n        appName = \"appmode-lies-shiny\",\n        appTitle = \"lying: shiny\",\n        appFiles = c(\"app.R\", \"index.html\", \"plumber.R\", \"report.Rmd\"),\n        appMode = \"shiny\",\n        account = account,\n        server = server\n    )\n    \n    rsconnect::deployApp(\n        appName = \"appmode-lies-rmarkdown\",\n        appTitle = \"lying: rmarkdown\",\n        appFiles = c(\"app.R\", \"index.html\", \"plumber.R\", \"report.Rmd\"),\n        appMode = \"rmd-static\",\n        account = account,\n        server = server\n    )\n    \n    rsconnect::deployApp(\n        appName = \"appmode-lies-static\",\n        appTitle = \"lying: static\",\n        appFiles = c(\"app.R\", \"index.html\", \"plumber.R\", \"report.Rmd\"),\n        appMode = \"static\",\n        account = account,\n        server = server\n    )\n    ```\n\n"
  },
  {
    "path": "tests/manual/dependencies.Rmd",
    "content": "# renv snapshots\n\nThese test the full end-to-end publishing experience with renv.\n\n## Archived package\n\n1.  Create a new project.\n\n    ```r\n    usethis::create_project(\"~/desktop/rsconnect-archived\")\n    ```\n\n    The remaining steps happen within this newly created project.\n\n2.  Install an old version of `rowr`.\n\n    ```r\n    devtools::install_version('rowr', '1.1.3')\n    ```\n\n3.  Run the following code to create an `app.R`:\n\n    ```r\n    writeLines(c(\n      \"library(rowr)\",\n      \"shinyApp(ui = fluidPage('shiny rowr'), server = function(input, output){})\"\n    ), \"app.R\")\n    ```\n\n4.  Deploy to shinyapps.io:\n\n    ```r\n    rsconnect::deployApp(server = \"shinyapps.io\")\n    ```\n\n5.  Deploy to colorado (Posit employees, only):\n\n    ```r\n    rsconnect::deployApp(server = \"colorado.posit.co\")\n    ```\n\nExpected behaviour:\n\n-   App successfully deploys to shinyapps.\n-   App successfully deploys to colorado.\n\n## CRAN, github, and BioC packages\n\n1.  Create a new project:\n\n    ```r\n    usethis::create_project(\"~/desktop/rsconnect-key-types\")\n    ````\n\n    The remaining steps happen within this newly created project.\n\n2.  Install CRAN, GitHub, and Bioconductor packages into your library:\n\n    ```r\n    pak::pak(c(\"rlang\", \"r-lib/waldo\", \"bioc::Biobase\"))\n    ```\n\n3.  Run the following code to create an `app.R`:\n\n    ```r\n    writeLines(c(\n      \"library(rlang)\",\n      \"library(waldo)\",\n      \"library(Biobase)\",\n      \"shinyApp(ui = fluidPage('shiny rowr'), server = function(input, output){})\"\n    ), \"app.R\")\n    ```\n\n4.  Deploy this app (analyzing your library):\n\n    ```r\n    rsconnect::deployApp()\n    ```\n\n5.  Create an renv snapshot:\n\n    ```r\n    renv::snapshot()\n    ```\n\n6.  Deploy this app (using the `renv.lock`):\n\n    ```r\n    rsconnect::deployApp()\n    ```\n\nExpected behaviour:\n\n-   App deploys without error when analyzing your R library.\n-   App deploys without error when analyzing the `renv.lock`.\n"
  },
  {
    "path": "tests/manual/deploySite.Rmd",
    "content": "# Sites\n\n## Quarto\n\n### Quarto using R (no additional dependencies)\n\n1.  Clone <https://github.com/rstudio/connect-content>\n\n1.  Open the RStudio project `bundles/quarto-website-r`.\n\n1.  When opened, you may see a message indicating that the project library\n    and lockfile are out of sync. If so, restore its environment:\n\n    ```r\n    renv::restore()\n    ```\n\n1.  Update rsconnect and renv within this project; as the lockfile probably\n    references different versions than what you want to test:\n\n    ```r\n    install.packages(c(\"renv\",\"rsconnect\"))\n    ```\n\n1.  Deploy, then re-deploy. If this is the first time deploying from this\n    directory, you will be prompted for a deployment target. The re-deploy\n    should NEVER prompt.\n\n    ```r\n    # initial deploy: renders, then publishes.\n    rsconnect::deploySite(render = \"local\")\n    # re-deply: renders, then publishes.\n    rsconnect::deploySite(render = \"local\")\n    # re-deploy: NO RENDER, only publishes.\n    rsconnect::deploySite(render = \"none\")\n    ```\n\n    The content successfully deploys as static (HTML) that was rendered on the\n    client.\n\n1.  Deploy having the content render on the server. If this is the first time\n    deploying with this title, you will be prompted for a deployment target.\n\n    ```r\n    # initial deploy: publishes on the server.\n    rsconnect::deploySite(siteName = \"quarto-website-r-renders\", render = \"server\")\n    ```\n\n    The content successfully deploys as Quarto to be rendered by Connect.\n\n### Quarto using R (having additional dependencies)\n\n1.  Clone <https://github.com/rstudio/connect-content>\n\n1.  Open the RStudio project `bundles/quarto-website-r-deps`.\n\n1.  When opened, you may see a message indicating that the project library\n    and lockfile are out of sync. If so, restore its environment:\n\n    ```r\n    renv::restore()\n    ```\n\n1.  Update rsconnect and renv within this project; as the lockfile probably\n    references different versions than what you want to test:\n\n    ```r\n    install.packages(c(\"renv\",\"rsconnect\"))\n    ```\n\n1.  Deploy this content. Rendering happens on the server. If this is the first time\n    deploying with this title, you will be prompted for a deployment target.\n\n    ```r\n    rsconnect::deploySite(render = \"server\")\n    ```\n\n    The content successfully deploys as Quarto to be rendered by Connect.\n\n## RMarkdown \n\n1.  Clone <https://github.com/rstudio/connect-content>\n\n1.  Open the RStudio project `bundles/rmd-site`.\n\n1.  When opened, you may see a message indicating that the project library\n    and lockfile are out of sync. If so, restore its environment:\n\n    ```r\n    renv::restore()\n    ```\n\n1.  Update rsconnect and renv within this project; as the lockfile probably\n    references different versions than what you want to test:\n\n    ```r\n    install.packages(c(\"renv\",\"rsconnect\"))\n    ```\n\n1.  Deploy, then re-deploy. If this is the first time deploying from this\n    directory, you will be prompted for a deployment target. The re-deploy\n    should NEVER prompt.\n\n    ```r\n    # initial deploy: renders, then publishes.\n    rsconnect::deploySite(render = \"local\")\n    # re-deply: renders, then publishes.\n    rsconnect::deploySite(render = \"local\")\n    # re-deploy: NO RENDER, only publishes.\n    rsconnect::deploySite(render = \"none\")\n    ```\n\n    The content successfully deploys as static (HTML) that was rendered on the\n    client.\n\n1.  Deploy having the content render on the server. If this is the first time\n    deploying with this title, you will be prompted for a deployment target.\n\n    ```r\n    # initial deploy: publishes on the server.\n    rsconnect::deploySite(siteName = \"rmd-site-renders\", render = \"server\")\n    ```\n\n    The content successfully deploys as R Markdown to be rendered by Connect.\n"
  },
  {
    "path": "tests/manual/publishing-dialog.Rmd",
    "content": "# Connections\n\n## Remove an account\n\n1. Open Options | Publishing.\n1. Select `colorado.posit.co` then click disconnect.\n\nSuccess:\n\n* No errors dialogs or error messages in console.\n* `nrow(rsconnect::accounts(\"colorado.posit.co\"))` returns 0.\n\n## Add a Connect account\n\n1. Open Options | Publishing.\n1. Click Connect, then \"Posit Connect\".\n1. Paste in `https://colorado.posit.co/rsc` and click next.\n1. Login connect in browser window, then click next.\n\nSuccess:\n\n* Dialog shows \"Account Verified\".\n* No errors dialogs or error messages in console.\n* `nrow(rsconnect::accounts(\"colorado.posit.co\"))` returns 1.\n\n### Add a shiny account\n\n1. Open Options | Publishing.\n1. Click Connect, then Shinyapps.io. \n1. Follow the on-screen instuctions, then click Connect Account.\n\nSuccess:\n\n1. No errors in console\n1. `nrow(rsconnect::accounts(\"shinyapps.io\"))` returns 1 or greater.\n\n# Apps\n\n1. Create a new shiny app by clicking \"New Project\" | \"New Directory\" | \"Shiny app\"\n\n### Deploy an app for the first time\n\n1. Click the deploy button then select \"colarado.posit.co\"\n\nSuccess:\n\n1. Deploy tab opens, and eventually deploy completes, and opens in connect.\n\n### Re-deploy an app\n\n1. Click the deploy button then click \"Publish\"\n\nSuccess:\n\n1. Existing colorado deployment should appear in publish dialog\n1. Deploy tab opens, and eventually deploy completes, and opens in connect.\n\n### Re-deploy an with missing metadata\n\n1.  Run `unlink(\"rsconnect\", recursive = TRUE)`\n1.  Click the deploy button then click \"Publish\".\n\nSuccess:\n\n1. Dialog asks if you want to replace existing content. \n1. After clicking \"Replace\" the deploy succeeds.\n\n### Re-deploy to deleted app\n\n1. On deployed app click \"...\" menu, then delete. \n1. Click ok, wait 30s, then refresh the page to confirm the app is deleted.\n1. Return to RStudio, then click the deploy drop down. Choose other destinations, then click deploy.\n\nSuccess:\n\n1. Deploy tab opens, and eventually deploy completes, and opens in connect.\n\nFinish up by deleting the app you just deployed.\n"
  },
  {
    "path": "tests/shinyapps-integration/example-shiny/app.R",
    "content": "shinyApp(\n  ui = fluidPage(\"Hello\"),\n  server = function(input, output) {}\n)\n"
  },
  {
    "path": "tests/shinyapps-integration/example-shiny/manifest.json",
    "content": "{\n  \"version\": 1,\n  \"locale\": \"en_US\",\n  \"platform\": \"4.5.2\",\n  \"metadata\": {\n    \"appmode\": \"shiny\",\n    \"primary_rmd\": null,\n    \"primary_html\": null,\n    \"content_category\": null,\n    \"has_parameters\": false\n  },\n  \"packages\": {\n    \"R6\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"R6\",\n        \"Title\": \"Encapsulated Classes with Reference Semantics\",\n        \"Version\": \"2.6.1\",\n        \"Authors@R\": \"c(\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\")),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n  )\",\n        \"Description\": \"Creates classes with reference semantics, similar to R's\\n    built-in reference classes. Compared to reference classes, R6 classes\\n    are simpler and lighter-weight, and they are not built on S4 classes\\n    so they do not require the methods package. These classes allow public\\n    and private members, and they support inheritance, even when the\\n    classes are defined in different packages.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://r6.r-lib.org, https://github.com/r-lib/R6\",\n        \"BugReports\": \"https://github.com/r-lib/R6/issues\",\n        \"Depends\": \"R (>= 3.6)\",\n        \"Suggests\": \"lobstr, testthat (>= 3.0.0)\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate, ggplot2, microbenchmark,\\nscales\",\n        \"Config/testthat/edition\": \"3\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2025-02-14 21:15:19 UTC; winston\",\n        \"Author\": \"Winston Chang [aut, cre],\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Winston Chang <winston@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-02-15 00:50:02 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-02-15 01:07:20 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"R6\",\n        \"RemoteRef\": \"R6\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"2.6.1\"\n      }\n    },\n    \"Rcpp\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"Rcpp\",\n        \"Title\": \"Seamless R and C++ Integration\",\n        \"Version\": \"1.1.1\",\n        \"Date\": \"2026-01-07\",\n        \"Authors@R\": \"c(person(\\\"Dirk\\\", \\\"Eddelbuettel\\\", role = c(\\\"aut\\\", \\\"cre\\\"), email = \\\"edd@debian.org\\\",\\n                    comment = c(ORCID = \\\"0000-0001-6419-907X\\\")),\\n             person(\\\"Romain\\\", \\\"Francois\\\", role = \\\"aut\\\",\\n                    comment = c(ORCID = \\\"0000-0002-2444-4226\\\")),\\n             person(\\\"JJ\\\", \\\"Allaire\\\", role = \\\"aut\\\",\\n                    comment = c(ORCID = \\\"0000-0003-0174-9868\\\")),\\n             person(\\\"Kevin\\\", \\\"Ushey\\\", role = \\\"aut\\\",\\n                    comment = c(ORCID = \\\"0000-0003-2880-7407\\\")),\\n             person(\\\"Qiang\\\", \\\"Kou\\\", role = \\\"aut\\\",\\n                    comment = c(ORCID = \\\"0000-0001-6786-5453\\\")),\\n             person(\\\"Nathan\\\", \\\"Russell\\\", role = \\\"aut\\\"),\\n             person(\\\"Iñaki\\\", \\\"Ucar\\\", role = \\\"aut\\\",\\n                    comment = c(ORCID = \\\"0000-0001-6403-5550\\\")),\\n             person(\\\"Doug\\\", \\\"Bates\\\", role = \\\"aut\\\",\\n                    comment = c(ORCID = \\\"0000-0001-8316-9503\\\")),\\n             person(\\\"John\\\", \\\"Chambers\\\", role = \\\"aut\\\"))\",\n        \"Description\": \"The 'Rcpp' package provides R functions as well as C++ classes which\\n offer a seamless integration of R and C++. Many R data types and objects can be\\n mapped back and forth to C++ equivalents which facilitates both writing of new\\n code as well as easier integration of third-party libraries. Documentation\\n about 'Rcpp' is provided by several vignettes included in this package, via the\\n 'Rcpp Gallery' site at <https://gallery.rcpp.org>, the paper by Eddelbuettel and\\n Francois (2011, <doi:10.18637/jss.v040.i08>), the book by Eddelbuettel (2013,\\n <doi:10.1007/978-1-4614-6868-4>) and the paper by Eddelbuettel and Balamuta (2018,\\n <doi:10.1080/00031305.2017.1375990>); see 'citation(\\\"Rcpp\\\")' for details.\",\n        \"Depends\": \"R (>= 3.5.0)\",\n        \"Imports\": \"methods, utils\",\n        \"Suggests\": \"tinytest, inline, rbenchmark, pkgKitten (>= 0.1.2)\",\n        \"URL\": \"https://www.rcpp.org,\\nhttps://dirk.eddelbuettel.com/code/rcpp.html,\\nhttps://github.com/RcppCore/Rcpp\",\n        \"License\": \"GPL (>= 2)\",\n        \"BugReports\": \"https://github.com/RcppCore/Rcpp/issues\",\n        \"MailingList\": \"rcpp-devel@lists.r-forge.r-project.org\",\n        \"RoxygenNote\": \"6.1.1\",\n        \"Encoding\": \"UTF-8\",\n        \"VignetteBuilder\": \"Rcpp\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2026-01-08 14:33:45 UTC; edd\",\n        \"Author\": \"Dirk Eddelbuettel [aut, cre] (ORCID:\\n    <https://orcid.org/0000-0001-6419-907X>),\\n  Romain Francois [aut] (ORCID: <https://orcid.org/0000-0002-2444-4226>),\\n  JJ Allaire [aut] (ORCID: <https://orcid.org/0000-0003-0174-9868>),\\n  Kevin Ushey [aut] (ORCID: <https://orcid.org/0000-0003-2880-7407>),\\n  Qiang Kou [aut] (ORCID: <https://orcid.org/0000-0001-6786-5453>),\\n  Nathan Russell [aut],\\n  Iñaki Ucar [aut] (ORCID: <https://orcid.org/0000-0001-6403-5550>),\\n  Doug Bates [aut] (ORCID: <https://orcid.org/0000-0001-8316-9503>),\\n  John Chambers [aut]\",\n        \"Maintainer\": \"Dirk Eddelbuettel <edd@debian.org>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2026-01-10 09:50:02 UTC\",\n        \"Built\": \"R 4.5.2; aarch64-apple-darwin20; 2026-01-10 11:50:00 UTC; unix\",\n        \"Archs\": \"Rcpp.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"Rcpp\",\n        \"RemoteRef\": \"Rcpp\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.1.1\"\n      }\n    },\n    \"base64enc\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"base64enc\",\n        \"Version\": \"0.1-6\",\n        \"Title\": \"Tools for 'base64' Encoding\",\n        \"Author\": \"Simon Urbanek [aut, cre, cph] (https://urbanek.nz, ORCID:\\n    <https://orcid.org/0000-0003-2297-1732>)\",\n        \"Authors@R\": \"person(\\\"Simon\\\", \\\"Urbanek\\\", role=c(\\\"aut\\\",\\\"cre\\\",\\\"cph\\\"), email=\\\"Simon.Urbanek@r-project.org\\\", comment=c(\\\"https://urbanek.nz\\\", ORCID=\\\"0000-0003-2297-1732\\\"))\",\n        \"Maintainer\": \"Simon Urbanek <Simon.Urbanek@r-project.org>\",\n        \"Depends\": \"R (>= 2.9.0)\",\n        \"Enhances\": \"png\",\n        \"Description\": \"Tools for handling 'base64' encoding. It is more flexible than the orphaned 'base64' package.\",\n        \"License\": \"GPL-2 | GPL-3\",\n        \"URL\": \"https://www.rforge.net/base64enc\",\n        \"BugReports\": \"https://github.com/s-u/base64enc/issues\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2026-02-02 01:51:07 UTC; rforge\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2026-02-02 06:31:10 UTC\",\n        \"Built\": \"R 4.5.2; aarch64-apple-darwin20; 2026-02-02 07:45:38 UTC; unix\",\n        \"Archs\": \"base64enc.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"base64enc\",\n        \"RemoteRef\": \"base64enc\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.1-6\"\n      }\n    },\n    \"bslib\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"bslib\",\n        \"Title\": \"Custom 'Bootstrap' 'Sass' Themes for 'shiny' and 'rmarkdown'\",\n        \"Version\": \"0.10.0\",\n        \"Authors@R\": \"c(\\n    person(\\\"Carson\\\", \\\"Sievert\\\", , \\\"carson@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0002-4958-2844\\\")),\\n    person(\\\"Joe\\\", \\\"Cheng\\\", , \\\"joe@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Garrick\\\", \\\"Aden-Buie\\\", , \\\"garrick@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-7111-0077\\\")),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\")),\\n    person(, \\\"Bootstrap contributors\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap library\\\"),\\n    person(, \\\"Twitter, Inc\\\", role = \\\"cph\\\",\\n           comment = \\\"Bootstrap library\\\"),\\n    person(\\\"Javi\\\", \\\"Aguilar\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"Bootstrap colorpicker library\\\"),\\n    person(\\\"Thomas\\\", \\\"Park\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"Bootswatch library\\\"),\\n    person(, \\\"PayPal\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"Bootstrap accessibility plugin\\\")\\n  )\",\n        \"Description\": \"Simplifies custom 'CSS' styling of both 'shiny' and\\n    'rmarkdown' via 'Bootstrap' 'Sass'. Supports 'Bootstrap' 3, 4 and 5 as\\n    well as their various 'Bootswatch' themes. An interactive widget is\\n    also provided for previewing themes in real time.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://rstudio.github.io/bslib/, https://github.com/rstudio/bslib\",\n        \"BugReports\": \"https://github.com/rstudio/bslib/issues\",\n        \"Depends\": \"R (>= 2.10)\",\n        \"Imports\": \"base64enc, cachem, fastmap (>= 1.1.1), grDevices, htmltools\\n(>= 0.5.8), jquerylib (>= 0.1.3), jsonlite, lifecycle, memoise\\n(>= 2.0.1), mime, rlang, sass (>= 0.4.9)\",\n        \"Suggests\": \"brand.yml, bsicons, curl, fontawesome, future, ggplot2,\\nknitr, lattice, magrittr, rappdirs, rmarkdown (>= 2.7), shiny\\n(>= 1.11.1), testthat, thematic, tools, utils, withr, yaml\",\n        \"Config/Needs/deploy\": \"BH, chiflights22, colourpicker, commonmark, cpp11,\\ncpsievert/chiflights22, cpsievert/histoslider, dplyr, DT,\\nggplot2, ggridges, gt, hexbin, histoslider, htmlwidgets,\\nlattice, leaflet, lubridate, markdown, modelr, plotly,\\nreactable, reshape2, rprojroot, rsconnect, rstudio/shiny,\\nscales, styler, tibble\",\n        \"Config/Needs/routine\": \"chromote, desc, renv\",\n        \"Config/Needs/website\": \"brio, crosstalk, dplyr, DT, ggplot2, glue,\\nhtmlwidgets, leaflet, lorem, palmerpenguins, plotly, purrr,\\nrprojroot, rstudio/htmltools, scales, stringr, tidyr, webshot2\",\n        \"Config/testthat/edition\": \"3\",\n        \"Config/testthat/parallel\": \"true\",\n        \"Config/testthat/start-first\": \"zzzz-bs-sass, fonts, zzz-precompile,\\ntheme-*, rmd-*\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"Collate\": \"'accordion.R' 'breakpoints.R' 'bs-current-theme.R'\\n'bs-dependencies.R' 'bs-global.R' 'bs-remove.R'\\n'bs-theme-layers.R' 'bs-theme-preset-bootswatch.R'\\n'bs-theme-preset-brand.R' 'bs-theme-preset-builtin.R'\\n'bs-theme-preset.R' 'utils.R' 'bs-theme-preview.R'\\n'bs-theme-update.R' 'bs-theme.R' 'bslib-package.R' 'buttons.R'\\n'card.R' 'deprecated.R' 'files.R' 'fill.R' 'imports.R'\\n'input-code-editor.R' 'input-dark-mode.R' 'input-submit.R'\\n'input-switch.R' 'layout.R' 'nav-items.R' 'nav-update.R'\\n'navbar_options.R' 'navs-legacy.R' 'navs.R' 'onLoad.R' 'page.R'\\n'popover.R' 'precompiled.R' 'print.R' 'shiny-devmode.R'\\n'sidebar.R' 'staticimports.R' 'toast.R' 'tooltip.R'\\n'utils-deps.R' 'utils-shiny.R' 'utils-tags.R' 'value-box.R'\\n'version-default.R' 'versions.R'\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2026-01-22 00:32:06 UTC; garrick\",\n        \"Author\": \"Carson Sievert [aut, cre] (ORCID:\\n    <https://orcid.org/0000-0002-4958-2844>),\\n  Joe Cheng [aut],\\n  Garrick Aden-Buie [aut] (ORCID:\\n    <https://orcid.org/0000-0002-7111-0077>),\\n  Posit Software, PBC [cph, fnd],\\n  Bootstrap contributors [ctb] (Bootstrap library),\\n  Twitter, Inc [cph] (Bootstrap library),\\n  Javi Aguilar [ctb, cph] (Bootstrap colorpicker library),\\n  Thomas Park [ctb, cph] (Bootswatch library),\\n  PayPal [ctb, cph] (Bootstrap accessibility plugin)\",\n        \"Maintainer\": \"Carson Sievert <carson@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2026-01-26 08:10:02 UTC\",\n        \"Built\": \"R 4.5.2; ; 2026-01-26 12:05:36 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"bslib\",\n        \"RemoteRef\": \"bslib\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.10.0\"\n      }\n    },\n    \"cachem\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"cachem\",\n        \"Version\": \"1.1.0\",\n        \"Title\": \"Cache R Objects with Automatic Pruning\",\n        \"Description\": \"Key-value stores with automatic pruning. Caches can limit\\n    either their total size or the age of the oldest object (or both),\\n    automatically pruning objects to maintain the constraints.\",\n        \"Authors@R\": \"c(\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", c(\\\"aut\\\", \\\"cre\\\")),\\n    person(family = \\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\")))\",\n        \"License\": \"MIT + file LICENSE\",\n        \"Encoding\": \"UTF-8\",\n        \"ByteCompile\": \"true\",\n        \"URL\": \"https://cachem.r-lib.org/, https://github.com/r-lib/cachem\",\n        \"Imports\": \"rlang, fastmap (>= 1.2.0)\",\n        \"Suggests\": \"testthat\",\n        \"RoxygenNote\": \"7.2.3\",\n        \"Config/Needs/routine\": \"lobstr\",\n        \"Config/Needs/website\": \"pkgdown\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2024-05-15 15:54:22 UTC; winston\",\n        \"Author\": \"Winston Chang [aut, cre],\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Winston Chang <winston@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2024-05-16 09:50:11 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-03-31 21:43:44 UTC; unix\",\n        \"Archs\": \"cachem.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"cachem\",\n        \"RemoteRef\": \"cachem\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.1.0\"\n      }\n    },\n    \"cli\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"cli\",\n        \"Title\": \"Helpers for Developing Command Line Interfaces\",\n        \"Version\": \"3.6.5\",\n        \"Authors@R\": \"c(\\n    person(\\\"Gábor\\\", \\\"Csárdi\\\", , \\\"gabor@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\")),\\n    person(\\\"Hadley\\\", \\\"Wickham\\\", role = \\\"ctb\\\"),\\n    person(\\\"Kirill\\\", \\\"Müller\\\", role = \\\"ctb\\\"),\\n    person(\\\"Salim\\\", \\\"Brüggemann\\\", , \\\"salim-b@pm.me\\\", role = \\\"ctb\\\",\\n           comment = c(ORCID = \\\"0000-0002-5329-5987\\\")),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n  )\",\n        \"Description\": \"A suite of tools to build attractive command line interfaces\\n    ('CLIs'), from semantic elements: headings, lists, alerts, paragraphs,\\n    etc. Supports custom themes via a 'CSS'-like language. It also\\n    contains a number of lower level 'CLI' elements: rules, boxes, trees,\\n    and 'Unicode' symbols with 'ASCII' alternatives. It support ANSI\\n    colors and text styles as well.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://cli.r-lib.org, https://github.com/r-lib/cli\",\n        \"BugReports\": \"https://github.com/r-lib/cli/issues\",\n        \"Depends\": \"R (>= 3.4)\",\n        \"Imports\": \"utils\",\n        \"Suggests\": \"callr, covr, crayon, digest, glue (>= 1.6.0), grDevices,\\nhtmltools, htmlwidgets, knitr, methods, processx, ps (>=\\n1.3.4.9000), rlang (>= 1.0.2.9003), rmarkdown, rprojroot,\\nrstudioapi, testthat (>= 3.2.0), tibble, whoami, withr\",\n        \"Config/Needs/website\": \"r-lib/asciicast, bench, brio, cpp11, decor, desc,\\nfansi, prettyunits, sessioninfo, tidyverse/tidytemplate,\\nusethis, vctrs\",\n        \"Config/testthat/edition\": \"3\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-04-22 12:00:18 UTC; gaborcsardi\",\n        \"Author\": \"Gábor Csárdi [aut, cre],\\n  Hadley Wickham [ctb],\\n  Kirill Müller [ctb],\\n  Salim Brüggemann [ctb] (<https://orcid.org/0000-0002-5329-5987>),\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Gábor Csárdi <gabor@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-04-23 13:50:02 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-04-23 14:06:41 UTC; unix\",\n        \"Archs\": \"cli.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"cli\",\n        \"RemoteRef\": \"cli\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"3.6.5\"\n      }\n    },\n    \"commonmark\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"commonmark\",\n        \"Type\": \"Package\",\n        \"Title\": \"High Performance CommonMark and Github Markdown Rendering in R\",\n        \"Version\": \"2.0.0\",\n        \"Authors@R\": \"c(\\n    person(\\\"Jeroen\\\", \\\"Ooms\\\", ,\\\"jeroenooms@gmail.com\\\", role = c(\\\"aut\\\", \\\"cre\\\"),\\n        comment = c(ORCID = \\\"0000-0002-4035-0289\\\")),\\n    person(\\\"John MacFarlane\\\", role = \\\"cph\\\", comment = \\\"Author of cmark\\\"))\",\n        \"Description\": \"The CommonMark specification <https://github.github.com/gfm/> defines\\n    a rationalized version of markdown syntax. This package uses the 'cmark' \\n    reference implementation for converting markdown text into various formats\\n    including html, latex and groff man. In addition it exposes the markdown\\n    parse tree in xml format. Also includes opt-in support for GFM extensions\\n    including tables, autolinks, and strikethrough text.\",\n        \"License\": \"BSD_2_clause + file LICENSE\",\n        \"URL\": \"https://docs.ropensci.org/commonmark/\\nhttps://ropensci.r-universe.dev/commonmark\",\n        \"BugReports\": \"https://github.com/r-lib/commonmark/issues\",\n        \"Suggests\": \"curl, testthat, xml2\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"Language\": \"en-US\",\n        \"Encoding\": \"UTF-8\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-07-07 13:20:39 UTC; jeroen\",\n        \"Author\": \"Jeroen Ooms [aut, cre] (ORCID: <https://orcid.org/0000-0002-4035-0289>),\\n  John MacFarlane [cph] (Author of cmark)\",\n        \"Maintainer\": \"Jeroen Ooms <jeroenooms@gmail.com>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-07-07 13:40:02 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-07-07 15:18:49 UTC; unix\",\n        \"Archs\": \"commonmark.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"commonmark\",\n        \"RemoteRef\": \"commonmark\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"2.0.0\"\n      }\n    },\n    \"digest\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"digest\",\n        \"Authors@R\": \"c(person(\\\"Dirk\\\", \\\"Eddelbuettel\\\", role = c(\\\"aut\\\", \\\"cre\\\"), email = \\\"edd@debian.org\\\",\\n                    comment = c(ORCID = \\\"0000-0001-6419-907X\\\")),\\n             person(\\\"Antoine\\\", \\\"Lucas\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-8059-9767\\\")),\\n             person(\\\"Jarek\\\", \\\"Tuszynski\\\", role=\\\"ctb\\\"),\\n             person(\\\"Henrik\\\", \\\"Bengtsson\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-7579-5165\\\")),\\n             person(\\\"Simon\\\", \\\"Urbanek\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-2297-1732\\\")),\\n             person(\\\"Mario\\\", \\\"Frasca\\\", role=\\\"ctb\\\"),\\n             person(\\\"Bryan\\\", \\\"Lewis\\\", role=\\\"ctb\\\"),\\n             person(\\\"Murray\\\", \\\"Stokely\\\", role=\\\"ctb\\\"),\\n             person(\\\"Hannes\\\", \\\"Muehleisen\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0001-8552-0029\\\")),\\n             person(\\\"Duncan\\\", \\\"Murdoch\\\", role=\\\"ctb\\\"),\\n             person(\\\"Jim\\\", \\\"Hester\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-2739-7082\\\")),\\n             person(\\\"Wush\\\", \\\"Wu\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0001-5180-0567\\\")),\\n             person(\\\"Qiang\\\", \\\"Kou\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0001-6786-5453\\\")),\\n             person(\\\"Thierry\\\", \\\"Onkelinx\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0001-8804-4216\\\")),\\n             person(\\\"Michel\\\", \\\"Lang\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0001-9754-0393\\\")),\\n             person(\\\"Viliam\\\", \\\"Simko\\\", role=\\\"ctb\\\"),\\n             person(\\\"Kurt\\\", \\\"Hornik\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-4198-9911\\\")),\\n             person(\\\"Radford\\\", \\\"Neal\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-2473-3407\\\")),\\n             person(\\\"Kendon\\\", \\\"Bell\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-9093-8312\\\")),\\n             person(\\\"Matthew\\\", \\\"de Queljoe\\\", role=\\\"ctb\\\"),\\n             person(\\\"Dmitry\\\", \\\"Selivanov\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-0492-6647\\\")),\\n             person(\\\"Ion\\\", \\\"Suruceanu\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0009-0005-6446-4909\\\")),\\n             person(\\\"Bill\\\", \\\"Denney\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-5759-428X\\\")),\\n             person(\\\"Dirk\\\", \\\"Schumacher\\\", role=\\\"ctb\\\"),\\n             person(\\\"András\\\", \\\"Svraka\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0009-0008-8480-1329\\\")),\\n             person(\\\"Sergey\\\", \\\"Fedorov\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-5970-7233\\\")),\\n             person(\\\"Will\\\", \\\"Landau\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-1878-3253\\\")),\\n             person(\\\"Floris\\\", \\\"Vanderhaeghe\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-6378-6229\\\")),\\n             person(\\\"Kevin\\\", \\\"Tappe\\\", role=\\\"ctb\\\"),\\n             person(\\\"Harris\\\", \\\"McGehee\\\", role=\\\"ctb\\\"),\\n             person(\\\"Tim\\\", \\\"Mastny\\\", role=\\\"ctb\\\"),\\n             person(\\\"Aaron\\\", \\\"Peikert\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0001-7813-818X\\\")),\\n             person(\\\"Mark\\\", \\\"van der Loo\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-9807-4686\\\")),\\n             person(\\\"Chris\\\", \\\"Muir\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-2555-3878\\\")),\\n             person(\\\"Moritz\\\", \\\"Beller\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-4852-0526\\\")),\\n             person(\\\"Sebastian\\\", \\\"Campbell\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0009-0000-5948-4503\\\")),\\n             person(\\\"Winston\\\", \\\"Chang\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-1576-2126\\\")),\\n             person(\\\"Dean\\\", \\\"Attali\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0002-5645-3493\\\")),\\n             person(\\\"Michael\\\", \\\"Chirico\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-0787-087X\\\")),\\n             person(\\\"Kevin\\\", \\\"Ushey\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-2880-7407\\\")),\\n             person(\\\"Carl\\\", \\\"Pearson\\\", role=\\\"ctb\\\", comment = c(ORCID = \\\"0000-0003-0701-7860\\\")))\",\n        \"Version\": \"0.6.39\",\n        \"Date\": \"2025-11-19\",\n        \"Title\": \"Create Compact Hash Digests of R Objects\",\n        \"Description\": \"Implementation of a function 'digest()' for the creation of hash\\n digests of arbitrary R objects (using the 'md5', 'sha-1', 'sha-256', 'crc32',\\n 'xxhash', 'murmurhash', 'spookyhash', 'blake3', 'crc32c', 'xxh3_64', and 'xxh3_128'\\n algorithms) permitting easy comparison of R language objects, as well as functions\\n such as 'hmac()' to create hash-based message authentication code. Please note that\\n this package is not meant to be deployed for cryptographic purposes for which more\\n comprehensive (and widely tested) libraries such as 'OpenSSL' should be used.\",\n        \"URL\": \"https://github.com/eddelbuettel/digest,\\nhttps://eddelbuettel.github.io/digest/,\\nhttps://dirk.eddelbuettel.com/code/digest.html\",\n        \"BugReports\": \"https://github.com/eddelbuettel/digest/issues\",\n        \"Depends\": \"R (>= 3.3.0)\",\n        \"Imports\": \"utils\",\n        \"License\": \"GPL (>= 2)\",\n        \"Suggests\": \"tinytest, simplermarkdown, rbenchmark\",\n        \"VignetteBuilder\": \"simplermarkdown\",\n        \"Encoding\": \"UTF-8\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-11-19 11:55:09 UTC; edd\",\n        \"Author\": \"Dirk Eddelbuettel [aut, cre] (ORCID:\\n    <https://orcid.org/0000-0001-6419-907X>),\\n  Antoine Lucas [ctb] (ORCID: <https://orcid.org/0000-0002-8059-9767>),\\n  Jarek Tuszynski [ctb],\\n  Henrik Bengtsson [ctb] (ORCID: <https://orcid.org/0000-0002-7579-5165>),\\n  Simon Urbanek [ctb] (ORCID: <https://orcid.org/0000-0003-2297-1732>),\\n  Mario Frasca [ctb],\\n  Bryan Lewis [ctb],\\n  Murray Stokely [ctb],\\n  Hannes Muehleisen [ctb] (ORCID:\\n    <https://orcid.org/0000-0001-8552-0029>),\\n  Duncan Murdoch [ctb],\\n  Jim Hester [ctb] (ORCID: <https://orcid.org/0000-0002-2739-7082>),\\n  Wush Wu [ctb] (ORCID: <https://orcid.org/0000-0001-5180-0567>),\\n  Qiang Kou [ctb] (ORCID: <https://orcid.org/0000-0001-6786-5453>),\\n  Thierry Onkelinx [ctb] (ORCID: <https://orcid.org/0000-0001-8804-4216>),\\n  Michel Lang [ctb] (ORCID: <https://orcid.org/0000-0001-9754-0393>),\\n  Viliam Simko [ctb],\\n  Kurt Hornik [ctb] (ORCID: <https://orcid.org/0000-0003-4198-9911>),\\n  Radford Neal [ctb] (ORCID: <https://orcid.org/0000-0002-2473-3407>),\\n  Kendon Bell [ctb] (ORCID: <https://orcid.org/0000-0002-9093-8312>),\\n  Matthew de Queljoe [ctb],\\n  Dmitry Selivanov [ctb] (ORCID: <https://orcid.org/0000-0003-0492-6647>),\\n  Ion Suruceanu [ctb] (ORCID: <https://orcid.org/0009-0005-6446-4909>),\\n  Bill Denney [ctb] (ORCID: <https://orcid.org/0000-0002-5759-428X>),\\n  Dirk Schumacher [ctb],\\n  András Svraka [ctb] (ORCID: <https://orcid.org/0009-0008-8480-1329>),\\n  Sergey Fedorov [ctb] (ORCID: <https://orcid.org/0000-0002-5970-7233>),\\n  Will Landau [ctb] (ORCID: <https://orcid.org/0000-0003-1878-3253>),\\n  Floris Vanderhaeghe [ctb] (ORCID:\\n    <https://orcid.org/0000-0002-6378-6229>),\\n  Kevin Tappe [ctb],\\n  Harris McGehee [ctb],\\n  Tim Mastny [ctb],\\n  Aaron Peikert [ctb] (ORCID: <https://orcid.org/0000-0001-7813-818X>),\\n  Mark van der Loo [ctb] (ORCID: <https://orcid.org/0000-0002-9807-4686>),\\n  Chris Muir [ctb] (ORCID: <https://orcid.org/0000-0003-2555-3878>),\\n  Moritz Beller [ctb] (ORCID: <https://orcid.org/0000-0003-4852-0526>),\\n  Sebastian Campbell [ctb] (ORCID:\\n    <https://orcid.org/0009-0000-5948-4503>),\\n  Winston Chang [ctb] (ORCID: <https://orcid.org/0000-0002-1576-2126>),\\n  Dean Attali [ctb] (ORCID: <https://orcid.org/0000-0002-5645-3493>),\\n  Michael Chirico [ctb] (ORCID: <https://orcid.org/0000-0003-0787-087X>),\\n  Kevin Ushey [ctb] (ORCID: <https://orcid.org/0000-0003-2880-7407>),\\n  Carl Pearson [ctb] (ORCID: <https://orcid.org/0000-0003-0701-7860>)\",\n        \"Maintainer\": \"Dirk Eddelbuettel <edd@debian.org>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-11-19 13:20:08 UTC\",\n        \"Built\": \"R 4.5.2; aarch64-apple-darwin20; 2025-11-22 19:17:48 UTC; unix\",\n        \"Archs\": \"digest.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"digest\",\n        \"RemoteRef\": \"digest\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.6.39\"\n      }\n    },\n    \"fastmap\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"fastmap\",\n        \"Title\": \"Fast Data Structures\",\n        \"Version\": \"1.2.0\",\n        \"Authors@R\": \"c(\\n    person(\\\"Winston\\\", \\\"Chang\\\", email = \\\"winston@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\")),\\n    person(given = \\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\")),\\n    person(given = \\\"Tessil\\\", role = \\\"cph\\\", comment = \\\"hopscotch_map library\\\")\\n    )\",\n        \"Description\": \"Fast implementation of data structures, including a key-value\\n    store, stack, and queue. Environments are commonly used as key-value stores\\n    in R, but every time a new key is used, it is added to R's global symbol\\n    table, causing a small amount of memory leakage. This can be problematic in\\n    cases where many different keys are used. Fastmap avoids this memory leak\\n    issue by implementing the map using data structures in C++.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.2.3\",\n        \"Suggests\": \"testthat (>= 2.1.1)\",\n        \"URL\": \"https://r-lib.github.io/fastmap/, https://github.com/r-lib/fastmap\",\n        \"BugReports\": \"https://github.com/r-lib/fastmap/issues\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2024-05-14 17:54:13 UTC; winston\",\n        \"Author\": \"Winston Chang [aut, cre],\\n  Posit Software, PBC [cph, fnd],\\n  Tessil [cph] (hopscotch_map library)\",\n        \"Maintainer\": \"Winston Chang <winston@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2024-05-15 09:00:07 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-03-31 21:43:00 UTC; unix\",\n        \"Archs\": \"fastmap.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"fastmap\",\n        \"RemoteRef\": \"fastmap\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.2.0\"\n      }\n    },\n    \"fontawesome\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"fontawesome\",\n        \"Version\": \"0.5.3\",\n        \"Title\": \"Easily Work with 'Font Awesome' Icons\",\n        \"Description\": \"Easily and flexibly insert 'Font Awesome' icons into 'R Markdown'\\n    documents and 'Shiny' apps. These icons can be inserted into HTML content\\n    through inline 'SVG' tags or 'i' tags. There is also a utility function for\\n    exporting 'Font Awesome' icons as 'PNG' images for those situations where\\n    raster graphics are needed.\",\n        \"Authors@R\": \"c(\\n    person(\\\"Richard\\\", \\\"Iannone\\\", , \\\"rich@posit.co\\\", c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0003-3925-190X\\\")),\\n    person(\\\"Christophe\\\", \\\"Dervieux\\\", , \\\"cderv@posit.co\\\", role = \\\"ctb\\\",\\n           comment = c(ORCID = \\\"0000-0003-4474-2498\\\")),\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", role = \\\"ctb\\\"),\\n    person(\\\"Dave\\\", \\\"Gandy\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"Font-Awesome font\\\"),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n    )\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://github.com/rstudio/fontawesome,\\nhttps://rstudio.github.io/fontawesome/\",\n        \"BugReports\": \"https://github.com/rstudio/fontawesome/issues\",\n        \"Encoding\": \"UTF-8\",\n        \"ByteCompile\": \"true\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"Depends\": \"R (>= 3.3.0)\",\n        \"Imports\": \"rlang (>= 1.0.6), htmltools (>= 0.5.1.1)\",\n        \"Suggests\": \"covr, dplyr (>= 1.0.8), gt (>= 0.9.0), knitr (>= 1.31),\\ntestthat (>= 3.0.0), rsvg\",\n        \"Config/testthat/edition\": \"3\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2024-11-16 17:06:16 UTC; riannone\",\n        \"Author\": \"Richard Iannone [aut, cre] (<https://orcid.org/0000-0003-3925-190X>),\\n  Christophe Dervieux [ctb] (<https://orcid.org/0000-0003-4474-2498>),\\n  Winston Chang [ctb],\\n  Dave Gandy [ctb, cph] (Font-Awesome font),\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Richard Iannone <rich@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2024-11-16 17:30:02 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-04-01 11:50:29 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"fontawesome\",\n        \"RemoteRef\": \"fontawesome\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.5.3\"\n      }\n    },\n    \"fs\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"fs\",\n        \"Title\": \"Cross-Platform File System Operations Based on 'libuv'\",\n        \"Version\": \"1.6.6\",\n        \"Authors@R\": \"c(\\n    person(\\\"Jim\\\", \\\"Hester\\\", role = \\\"aut\\\"),\\n    person(\\\"Hadley\\\", \\\"Wickham\\\", , \\\"hadley@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Gábor\\\", \\\"Csárdi\\\", , \\\"csardi.gabor@gmail.com\\\", role = c(\\\"aut\\\", \\\"cre\\\")),\\n    person(\\\"libuv project contributors\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv library\\\"),\\n    person(\\\"Joyent, Inc. and other Node contributors\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv library\\\"),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n  )\",\n        \"Description\": \"A cross-platform interface to file system operations, built\\n    on top of the 'libuv' C library.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://fs.r-lib.org, https://github.com/r-lib/fs\",\n        \"BugReports\": \"https://github.com/r-lib/fs/issues\",\n        \"Depends\": \"R (>= 3.6)\",\n        \"Imports\": \"methods\",\n        \"Suggests\": \"covr, crayon, knitr, pillar (>= 1.0.0), rmarkdown, spelling,\\ntestthat (>= 3.0.0), tibble (>= 1.1.0), vctrs (>= 0.3.0), withr\",\n        \"VignetteBuilder\": \"knitr\",\n        \"ByteCompile\": \"true\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate\",\n        \"Config/testthat/edition\": \"3\",\n        \"Copyright\": \"file COPYRIGHTS\",\n        \"Encoding\": \"UTF-8\",\n        \"Language\": \"en-US\",\n        \"RoxygenNote\": \"7.2.3\",\n        \"SystemRequirements\": \"GNU make\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-04-12 09:39:13 UTC; gaborcsardi\",\n        \"Author\": \"Jim Hester [aut],\\n  Hadley Wickham [aut],\\n  Gábor Csárdi [aut, cre],\\n  libuv project contributors [cph] (libuv library),\\n  Joyent, Inc. and other Node contributors [cph] (libuv library),\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Gábor Csárdi <csardi.gabor@gmail.com>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-04-12 10:40:02 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-04-12 11:59:24 UTC; unix\",\n        \"Archs\": \"fs.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"fs\",\n        \"RemoteRef\": \"fs\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.6.6\"\n      }\n    },\n    \"glue\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"glue\",\n        \"Title\": \"Interpreted String Literals\",\n        \"Version\": \"1.8.0\",\n        \"Authors@R\": \"c(\\n    person(\\\"Jim\\\", \\\"Hester\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-2739-7082\\\")),\\n    person(\\\"Jennifer\\\", \\\"Bryan\\\", , \\\"jenny@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0002-6983-2759\\\")),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n  )\",\n        \"Description\": \"An implementation of interpreted string literals, inspired by\\n    Python's Literal String Interpolation\\n    <https://www.python.org/dev/peps/pep-0498/> and Docstrings\\n    <https://www.python.org/dev/peps/pep-0257/> and Julia's Triple-Quoted\\n    String Literals\\n    <https://docs.julialang.org/en/v1.3/manual/strings/#Triple-Quoted-String-Literals-1>.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://glue.tidyverse.org/, https://github.com/tidyverse/glue\",\n        \"BugReports\": \"https://github.com/tidyverse/glue/issues\",\n        \"Depends\": \"R (>= 3.6)\",\n        \"Imports\": \"methods\",\n        \"Suggests\": \"crayon, DBI (>= 1.2.0), dplyr, knitr, magrittr, rlang,\\nrmarkdown, RSQLite, testthat (>= 3.2.0), vctrs (>= 0.3.0),\\nwaldo (>= 0.5.3), withr\",\n        \"VignetteBuilder\": \"knitr\",\n        \"ByteCompile\": \"true\",\n        \"Config/Needs/website\": \"bench, forcats, ggbeeswarm, ggplot2, R.utils,\\nrprintf, tidyr, tidyverse/tidytemplate\",\n        \"Config/testthat/edition\": \"3\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2024-09-27 16:00:45 UTC; jenny\",\n        \"Author\": \"Jim Hester [aut] (<https://orcid.org/0000-0002-2739-7082>),\\n  Jennifer Bryan [aut, cre] (<https://orcid.org/0000-0002-6983-2759>),\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Jennifer Bryan <jenny@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2024-09-30 22:30:01 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-03-31 21:41:36 UTC; unix\",\n        \"Archs\": \"glue.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"glue\",\n        \"RemoteRef\": \"glue\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.8.0\"\n      }\n    },\n    \"htmltools\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"htmltools\",\n        \"Title\": \"Tools for HTML\",\n        \"Version\": \"0.5.9\",\n        \"Authors@R\": \"c(\\n    person(\\\"Joe\\\", \\\"Cheng\\\", , \\\"joe@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Carson\\\", \\\"Sievert\\\", , \\\"carson@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0002-4958-2844\\\")),\\n    person(\\\"Barret\\\", \\\"Schloerke\\\", , \\\"barret@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0001-9986-114X\\\")),\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-1576-2126\\\")),\\n    person(\\\"Yihui\\\", \\\"Xie\\\", , \\\"yihui@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Jeff\\\", \\\"Allen\\\", role = \\\"aut\\\"),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n  )\",\n        \"Description\": \"Tools for HTML generation and output.\",\n        \"License\": \"GPL (>= 2)\",\n        \"URL\": \"https://github.com/rstudio/htmltools,\\nhttps://rstudio.github.io/htmltools/\",\n        \"BugReports\": \"https://github.com/rstudio/htmltools/issues\",\n        \"Depends\": \"R (>= 2.14.1)\",\n        \"Imports\": \"base64enc, digest, fastmap (>= 1.1.0), grDevices, rlang (>=\\n1.0.0), utils\",\n        \"Suggests\": \"Cairo, markdown, ragg, shiny, testthat, withr\",\n        \"Enhances\": \"knitr\",\n        \"Config/Needs/check\": \"knitr\",\n        \"Config/Needs/website\": \"rstudio/quillt, bench\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"Collate\": \"'colors.R' 'fill.R' 'html_dependency.R' 'html_escape.R'\\n'html_print.R' 'htmltools-package.R' 'images.R' 'known_tags.R'\\n'selector.R' 'staticimports.R' 'tag_query.R' 'utils.R' 'tags.R'\\n'template.R'\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-12-02 23:32:55 UTC; cpsievert\",\n        \"Author\": \"Joe Cheng [aut],\\n  Carson Sievert [aut, cre] (ORCID:\\n    <https://orcid.org/0000-0002-4958-2844>),\\n  Barret Schloerke [aut] (ORCID: <https://orcid.org/0000-0001-9986-114X>),\\n  Winston Chang [aut] (ORCID: <https://orcid.org/0000-0002-1576-2126>),\\n  Yihui Xie [aut],\\n  Jeff Allen [aut],\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Carson Sievert <carson@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-12-04 11:50:09 UTC\",\n        \"Built\": \"R 4.5.2; aarch64-apple-darwin20; 2025-12-04 12:12:04 UTC; unix\",\n        \"Archs\": \"htmltools.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"htmltools\",\n        \"RemoteRef\": \"htmltools\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.5.9\"\n      }\n    },\n    \"httpuv\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"httpuv\",\n        \"Title\": \"HTTP and WebSocket Server Library\",\n        \"Version\": \"1.6.16\",\n        \"Authors@R\": \"c(\\n    person(\\\"Joe\\\", \\\"Cheng\\\", , \\\"joe@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\")),\\n    person(\\\"Posit, PBC\\\", \\\"fnd\\\", role = \\\"cph\\\"),\\n    person(\\\"Hector\\\", \\\"Corrada Bravo\\\", role = \\\"ctb\\\"),\\n    person(\\\"Jeroen\\\", \\\"Ooms\\\", role = \\\"ctb\\\"),\\n    person(\\\"Andrzej\\\", \\\"Krzemienski\\\", role = \\\"cph\\\",\\n           comment = \\\"optional.hpp\\\"),\\n    person(\\\"libuv project contributors\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv library, see src/libuv/AUTHORS file\\\"),\\n    person(\\\"Joyent, Inc. and other Node contributors\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv library, see src/libuv/AUTHORS file; and http-parser library, see src/http-parser/AUTHORS file\\\"),\\n    person(\\\"Niels\\\", \\\"Provos\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: tree.h\\\"),\\n    person(\\\"Internet Systems Consortium, Inc.\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: inet_pton and inet_ntop, contained in src/libuv/src/inet.c\\\"),\\n    person(\\\"Alexander\\\", \\\"Chemeris\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: stdint-msvc2008.h (from msinttypes)\\\"),\\n    person(\\\"Google, Inc.\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: pthread-fixes.c\\\"),\\n    person(\\\"Sony Mobile Communcations AB\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: pthread-fixes.c\\\"),\\n    person(\\\"Berkeley Software Design Inc.\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c\\\"),\\n    person(\\\"Kenneth\\\", \\\"MacKay\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c\\\"),\\n    person(\\\"Emergya (Cloud4all, FP7/2007-2013, grant agreement no 289016)\\\", role = \\\"cph\\\",\\n           comment = \\\"libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c\\\"),\\n    person(\\\"Steve\\\", \\\"Reid\\\", role = \\\"aut\\\",\\n           comment = \\\"SHA-1 implementation\\\"),\\n    person(\\\"James\\\", \\\"Brown\\\", role = \\\"aut\\\",\\n           comment = \\\"SHA-1 implementation\\\"),\\n    person(\\\"Bob\\\", \\\"Trower\\\", role = \\\"aut\\\",\\n           comment = \\\"base64 implementation\\\"),\\n    person(\\\"Alexander\\\", \\\"Peslyak\\\", role = \\\"aut\\\",\\n           comment = \\\"MD5 implementation\\\"),\\n    person(\\\"Trantor Standard Systems\\\", role = \\\"cph\\\",\\n           comment = \\\"base64 implementation\\\"),\\n    person(\\\"Igor\\\", \\\"Sysoev\\\", role = \\\"cph\\\",\\n           comment = \\\"http-parser\\\")\\n  )\",\n        \"Description\": \"Provides low-level socket and protocol support for handling\\n    HTTP and WebSocket requests directly from within R. It is primarily\\n    intended as a building block for other packages, rather than making it\\n    particularly easy to create complete web applications using httpuv\\n    alone.  httpuv is built on top of the libuv and http-parser C\\n    libraries, both of which were developed by Joyent, Inc. (See LICENSE\\n    file for libuv and http-parser license information.)\",\n        \"License\": \"GPL (>= 2) | file LICENSE\",\n        \"URL\": \"https://github.com/rstudio/httpuv\",\n        \"BugReports\": \"https://github.com/rstudio/httpuv/issues\",\n        \"Depends\": \"R (>= 2.15.1)\",\n        \"Imports\": \"later (>= 0.8.0), promises, R6, Rcpp (>= 1.0.7), utils\",\n        \"Suggests\": \"callr, curl, jsonlite, testthat, websocket\",\n        \"LinkingTo\": \"later, Rcpp\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"SystemRequirements\": \"GNU make, zlib\",\n        \"Collate\": \"'RcppExports.R' 'httpuv.R' 'random_port.R' 'server.R'\\n'staticServer.R' 'static_paths.R' 'utils.R'\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-04-15 17:47:41 UTC; cg334\",\n        \"Author\": \"Joe Cheng [aut],\\n  Winston Chang [aut, cre],\\n  Posit, PBC fnd [cph],\\n  Hector Corrada Bravo [ctb],\\n  Jeroen Ooms [ctb],\\n  Andrzej Krzemienski [cph] (optional.hpp),\\n  libuv project contributors [cph] (libuv library, see src/libuv/AUTHORS\\n    file),\\n  Joyent, Inc. and other Node contributors [cph] (libuv library, see\\n    src/libuv/AUTHORS file; and http-parser library, see\\n    src/http-parser/AUTHORS file),\\n  Niels Provos [cph] (libuv subcomponent: tree.h),\\n  Internet Systems Consortium, Inc. [cph] (libuv subcomponent: inet_pton\\n    and inet_ntop, contained in src/libuv/src/inet.c),\\n  Alexander Chemeris [cph] (libuv subcomponent: stdint-msvc2008.h (from\\n    msinttypes)),\\n  Google, Inc. [cph] (libuv subcomponent: pthread-fixes.c),\\n  Sony Mobile Communcations AB [cph] (libuv subcomponent:\\n    pthread-fixes.c),\\n  Berkeley Software Design Inc. [cph] (libuv subcomponent:\\n    android-ifaddrs.h, android-ifaddrs.c),\\n  Kenneth MacKay [cph] (libuv subcomponent: android-ifaddrs.h,\\n    android-ifaddrs.c),\\n  Emergya (Cloud4all, FP7/2007-2013, grant agreement no 289016) [cph]\\n    (libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c),\\n  Steve Reid [aut] (SHA-1 implementation),\\n  James Brown [aut] (SHA-1 implementation),\\n  Bob Trower [aut] (base64 implementation),\\n  Alexander Peslyak [aut] (MD5 implementation),\\n  Trantor Standard Systems [cph] (base64 implementation),\\n  Igor Sysoev [cph] (http-parser)\",\n        \"Maintainer\": \"Winston Chang <winston@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-04-16 08:00:06 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-04-16 09:05:24 UTC; unix\",\n        \"Archs\": \"httpuv.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"httpuv\",\n        \"RemoteRef\": \"httpuv\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.6.16\"\n      }\n    },\n    \"jquerylib\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"jquerylib\",\n        \"Title\": \"Obtain 'jQuery' as an HTML Dependency Object\",\n        \"Version\": \"0.1.4\",\n        \"Authors@R\": \"c(\\n    person(\\\"Carson\\\", \\\"Sievert\\\", role = c(\\\"aut\\\", \\\"cre\\\"), email = \\\"carson@rstudio.com\\\", comment = c(ORCID = \\\"0000-0002-4958-2844\\\")),\\n    person(\\\"Joe\\\", \\\"Cheng\\\", role = \\\"aut\\\", email = \\\"joe@rstudio.com\\\"),\\n    person(family = \\\"RStudio\\\", role = \\\"cph\\\"),\\n    person(family = \\\"jQuery Foundation\\\", role = \\\"cph\\\",\\n    comment = \\\"jQuery library and jQuery UI library\\\"),\\n    person(family = \\\"jQuery contributors\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n    comment = \\\"jQuery library; authors listed in inst/lib/jquery-AUTHORS.txt\\\")\\n    )\",\n        \"Description\": \"Obtain any major version of 'jQuery' (<https://code.jquery.com/>) and use it in any webpage generated by 'htmltools' (e.g. 'shiny', 'htmlwidgets', and 'rmarkdown').\\n    Most R users don't need to use this package directly, but other R packages (e.g. 'shiny', 'rmarkdown', etc.) depend on this package to avoid bundling redundant copies of 'jQuery'.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"Encoding\": \"UTF-8\",\n        \"Config/testthat/edition\": \"3\",\n        \"RoxygenNote\": \"7.0.2\",\n        \"Imports\": \"htmltools\",\n        \"Suggests\": \"testthat\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2021-04-26 16:40:21 UTC; cpsievert\",\n        \"Author\": \"Carson Sievert [aut, cre] (<https://orcid.org/0000-0002-4958-2844>),\\n  Joe Cheng [aut],\\n  RStudio [cph],\\n  jQuery Foundation [cph] (jQuery library and jQuery UI library),\\n  jQuery contributors [ctb, cph] (jQuery library; authors listed in\\n    inst/lib/jquery-AUTHORS.txt)\",\n        \"Maintainer\": \"Carson Sievert <carson@rstudio.com>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2021-04-26 17:10:02 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-04-01 11:50:17 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"jquerylib\",\n        \"RemoteRef\": \"jquerylib\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.1.4\"\n      }\n    },\n    \"jsonlite\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"jsonlite\",\n        \"Version\": \"2.0.0\",\n        \"Title\": \"A Simple and Robust JSON Parser and Generator for R\",\n        \"License\": \"MIT + file LICENSE\",\n        \"Depends\": \"methods\",\n        \"Authors@R\": \"c(\\n    person(\\\"Jeroen\\\", \\\"Ooms\\\", role = c(\\\"aut\\\", \\\"cre\\\"), email = \\\"jeroenooms@gmail.com\\\",\\n      comment = c(ORCID = \\\"0000-0002-4035-0289\\\")),\\n    person(\\\"Duncan\\\", \\\"Temple Lang\\\", role = \\\"ctb\\\"),\\n    person(\\\"Lloyd\\\", \\\"Hilaiel\\\", role = \\\"cph\\\", comment=\\\"author of bundled libyajl\\\"))\",\n        \"URL\": \"https://jeroen.r-universe.dev/jsonlite\\nhttps://arxiv.org/abs/1403.2805\",\n        \"BugReports\": \"https://github.com/jeroen/jsonlite/issues\",\n        \"Maintainer\": \"Jeroen Ooms <jeroenooms@gmail.com>\",\n        \"VignetteBuilder\": \"knitr, R.rsp\",\n        \"Description\": \"A reasonably fast JSON parser and generator, optimized for statistical \\n    data and the web. Offers simple, flexible tools for working with JSON in R, and\\n    is particularly powerful for building pipelines and interacting with a web API. \\n    The implementation is based on the mapping described in the vignette (Ooms, 2014).\\n    In addition to converting JSON data from/to R objects, 'jsonlite' contains \\n    functions to stream, validate, and prettify JSON data. The unit tests included \\n    with the package verify that all edge cases are encoded and decoded consistently \\n    for use with dynamic data in systems and applications.\",\n        \"Suggests\": \"httr, vctrs, testthat, knitr, rmarkdown, R.rsp, sf\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"Encoding\": \"UTF-8\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-03-26 11:36:10 UTC; jeroen\",\n        \"Author\": \"Jeroen Ooms [aut, cre] (<https://orcid.org/0000-0002-4035-0289>),\\n  Duncan Temple Lang [ctb],\\n  Lloyd Hilaiel [cph] (author of bundled libyajl)\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-03-27 06:40:02 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-03-27 07:33:55 UTC; unix\",\n        \"Archs\": \"jsonlite.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"jsonlite\",\n        \"RemoteRef\": \"jsonlite\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"2.0.0\"\n      }\n    },\n    \"later\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"later\",\n        \"Title\": \"Utilities for Scheduling Functions to Execute Later with Event\\nLoops\",\n        \"Version\": \"1.4.5\",\n        \"Authors@R\": \"c(\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-1576-2126\\\")),\\n    person(\\\"Joe\\\", \\\"Cheng\\\", , \\\"joe@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Charlie\\\", \\\"Gao\\\", , \\\"charlie.gao@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0002-0750-061X\\\")),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"),\\n           comment = c(ROR = \\\"03wc8by49\\\")),\\n    person(\\\"Marcus\\\", \\\"Geelnard\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"TinyCThread library, https://tinycthread.github.io/\\\"),\\n    person(\\\"Evan\\\", \\\"Nemerson\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"TinyCThread library, https://tinycthread.github.io/\\\")\\n  )\",\n        \"Description\": \"Executes arbitrary R or C functions some time after the\\n    current time, after the R execution stack has emptied. The functions\\n    are scheduled in an event loop.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://later.r-lib.org, https://github.com/r-lib/later\",\n        \"BugReports\": \"https://github.com/r-lib/later/issues\",\n        \"Depends\": \"R (>= 3.5)\",\n        \"Imports\": \"Rcpp (>= 1.0.10), rlang\",\n        \"Suggests\": \"knitr, nanonext, promises, rmarkdown, testthat (>= 3.0.0)\",\n        \"LinkingTo\": \"Rcpp\",\n        \"VignetteBuilder\": \"knitr\",\n        \"Config/build/compilation-database\": \"true\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate\",\n        \"Config/testthat/edition\": \"3\",\n        \"Config/usethis/last-upkeep\": \"2025-07-18\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2026-01-07 15:27:55 UTC; shikokuchuo\",\n        \"Author\": \"Winston Chang [aut] (ORCID: <https://orcid.org/0000-0002-1576-2126>),\\n  Joe Cheng [aut],\\n  Charlie Gao [aut, cre] (ORCID: <https://orcid.org/0000-0002-0750-061X>),\\n  Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>),\\n  Marcus Geelnard [ctb, cph] (TinyCThread library,\\n    https://tinycthread.github.io/),\\n  Evan Nemerson [ctb, cph] (TinyCThread library,\\n    https://tinycthread.github.io/)\",\n        \"Maintainer\": \"Charlie Gao <charlie.gao@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2026-01-08 06:11:06 UTC\",\n        \"Built\": \"R 4.5.2; aarch64-apple-darwin20; 2026-01-08 07:02:57 UTC; unix\",\n        \"Archs\": \"later.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"later\",\n        \"RemoteRef\": \"later\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.4.5\"\n      }\n    },\n    \"lifecycle\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"lifecycle\",\n        \"Title\": \"Manage the Life Cycle of your Package Functions\",\n        \"Version\": \"1.0.5\",\n        \"Authors@R\": \"c(\\n    person(\\\"Lionel\\\", \\\"Henry\\\", , \\\"lionel@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\")),\\n    person(\\\"Hadley\\\", \\\"Wickham\\\", , \\\"hadley@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0003-4757-117X\\\")),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n  )\",\n        \"Description\": \"Manage the life cycle of your exported functions with shared\\n    conventions, documentation badges, and user-friendly deprecation\\n    warnings.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://lifecycle.r-lib.org/, https://github.com/r-lib/lifecycle\",\n        \"BugReports\": \"https://github.com/r-lib/lifecycle/issues\",\n        \"Depends\": \"R (>= 3.6)\",\n        \"Imports\": \"cli (>= 3.4.0), rlang (>= 1.1.0)\",\n        \"Suggests\": \"covr, knitr, lintr (>= 3.1.0), rmarkdown, testthat (>=\\n3.0.1), tibble, tidyverse, tools, vctrs, withr, xml2\",\n        \"VignetteBuilder\": \"knitr\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate, usethis\",\n        \"Config/testthat/edition\": \"3\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2026-01-07 15:00:49 UTC; lionel\",\n        \"Author\": \"Lionel Henry [aut, cre],\\n  Hadley Wickham [aut] (ORCID: <https://orcid.org/0000-0003-4757-117X>),\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Lionel Henry <lionel@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2026-01-08 08:00:02 UTC\",\n        \"Built\": \"R 4.5.2; ; 2026-01-08 12:03:10 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"lifecycle\",\n        \"RemoteRef\": \"lifecycle\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.0.5\"\n      }\n    },\n    \"magrittr\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"magrittr\",\n        \"Title\": \"A Forward-Pipe Operator for R\",\n        \"Version\": \"2.0.4\",\n        \"Authors@R\": \"c(\\n    person(\\\"Stefan Milton\\\", \\\"Bache\\\", , \\\"stefan@stefanbache.dk\\\", role = c(\\\"aut\\\", \\\"cph\\\"),\\n           comment = \\\"Original author and creator of magrittr\\\"),\\n    person(\\\"Hadley\\\", \\\"Wickham\\\", , \\\"hadley@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Lionel\\\", \\\"Henry\\\", , \\\"lionel@posit.co\\\", role = \\\"cre\\\"),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"),\\n            comment = c(ROR = \\\"03wc8by49\\\"))\\n  )\",\n        \"Description\": \"Provides a mechanism for chaining commands with a new\\n    forward-pipe operator, %>%. This operator will forward a value, or the\\n    result of an expression, into the next function call/expression.\\n    There is flexible support for the type of right-hand side expressions.\\n    For more information, see package vignette.  To quote Rene Magritte,\\n    \\\"Ceci n'est pas un pipe.\\\"\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://magrittr.tidyverse.org,\\nhttps://github.com/tidyverse/magrittr\",\n        \"BugReports\": \"https://github.com/tidyverse/magrittr/issues\",\n        \"Depends\": \"R (>= 3.4.0)\",\n        \"Suggests\": \"covr, knitr, rlang, rmarkdown, testthat\",\n        \"VignetteBuilder\": \"knitr\",\n        \"ByteCompile\": \"Yes\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-09-11 16:45:15 UTC; lionel\",\n        \"Author\": \"Stefan Milton Bache [aut, cph] (Original author and creator of\\n    magrittr),\\n  Hadley Wickham [aut],\\n  Lionel Henry [cre],\\n  Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>)\",\n        \"Maintainer\": \"Lionel Henry <lionel@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-09-12 07:20:14 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-09-12 08:49:30 UTC; unix\",\n        \"Archs\": \"magrittr.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"magrittr\",\n        \"RemoteRef\": \"magrittr\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"2.0.4\"\n      }\n    },\n    \"memoise\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"memoise\",\n        \"Title\": \"'Memoisation' of Functions\",\n        \"Version\": \"2.0.1\",\n        \"Authors@R\": \"\\n    c(person(given = \\\"Hadley\\\",\\n             family = \\\"Wickham\\\",\\n             role = \\\"aut\\\",\\n             email = \\\"hadley@rstudio.com\\\"),\\n      person(given = \\\"Jim\\\",\\n             family = \\\"Hester\\\",\\n             role = \\\"aut\\\"),\\n      person(given = \\\"Winston\\\",\\n             family = \\\"Chang\\\",\\n             role = c(\\\"aut\\\", \\\"cre\\\"),\\n             email = \\\"winston@rstudio.com\\\"),\\n      person(given = \\\"Kirill\\\",\\n             family = \\\"Müller\\\",\\n             role = \\\"aut\\\",\\n             email = \\\"krlmlr+r@mailbox.org\\\"),\\n      person(given = \\\"Daniel\\\",\\n             family = \\\"Cook\\\",\\n             role = \\\"aut\\\",\\n             email = \\\"danielecook@gmail.com\\\"),\\n      person(given = \\\"Mark\\\",\\n             family = \\\"Edmondson\\\",\\n             role = \\\"ctb\\\",\\n             email = \\\"r@sunholo.com\\\"))\",\n        \"Description\": \"Cache the results of a function so that when you\\n    call it again with the same arguments it returns the previously computed\\n    value.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://memoise.r-lib.org, https://github.com/r-lib/memoise\",\n        \"BugReports\": \"https://github.com/r-lib/memoise/issues\",\n        \"Imports\": \"rlang (>= 0.4.10), cachem\",\n        \"Suggests\": \"digest, aws.s3, covr, googleAuthR, googleCloudStorageR, httr,\\ntestthat\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.1.2\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2021-11-24 21:24:50 UTC; jhester\",\n        \"Author\": \"Hadley Wickham [aut],\\n  Jim Hester [aut],\\n  Winston Chang [aut, cre],\\n  Kirill Müller [aut],\\n  Daniel Cook [aut],\\n  Mark Edmondson [ctb]\",\n        \"Maintainer\": \"Winston Chang <winston@rstudio.com>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2021-11-26 16:11:10 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-04-01 06:31:57 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"memoise\",\n        \"RemoteRef\": \"memoise\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"2.0.1\"\n      }\n    },\n    \"mime\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"mime\",\n        \"Type\": \"Package\",\n        \"Title\": \"Map Filenames to MIME Types\",\n        \"Version\": \"0.13\",\n        \"Authors@R\": \"c(\\n    person(\\\"Yihui\\\", \\\"Xie\\\", role = c(\\\"aut\\\", \\\"cre\\\"), email = \\\"xie@yihui.name\\\", comment = c(ORCID = \\\"0000-0003-0645-5666\\\", URL = \\\"https://yihui.org\\\")),\\n    person(\\\"Jeffrey\\\", \\\"Horner\\\", role = \\\"ctb\\\"),\\n    person(\\\"Beilei\\\", \\\"Bian\\\", role = \\\"ctb\\\")\\n    )\",\n        \"Description\": \"Guesses the MIME type from a filename extension using the data\\n    derived from /etc/mime.types in UNIX-type systems.\",\n        \"Imports\": \"tools\",\n        \"License\": \"GPL\",\n        \"URL\": \"https://github.com/yihui/mime\",\n        \"BugReports\": \"https://github.com/yihui/mime/issues\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"Encoding\": \"UTF-8\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-03-17 19:54:24 UTC; runner\",\n        \"Author\": \"Yihui Xie [aut, cre] (<https://orcid.org/0000-0003-0645-5666>,\\n    https://yihui.org),\\n  Jeffrey Horner [ctb],\\n  Beilei Bian [ctb]\",\n        \"Maintainer\": \"Yihui Xie <xie@yihui.name>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-03-17 20:20:02 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-03-31 21:43:02 UTC; unix\",\n        \"Archs\": \"mime.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"mime\",\n        \"RemoteRef\": \"mime\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.13\"\n      }\n    },\n    \"otel\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"otel\",\n        \"Title\": \"OpenTelemetry R API\",\n        \"Version\": \"0.2.0\",\n        \"Authors@R\": \"\\n    person(\\\"Gábor\\\", \\\"Csárdi\\\", , \\\"csardi.gabor@gmail.com\\\", role = c(\\\"aut\\\", \\\"cre\\\"))\",\n        \"Description\": \"High-quality, ubiquitous, and portable telemetry to enable\\n    effective observability. OpenTelemetry is a collection of tools,\\n    APIs, and SDKs used to instrument, generate, collect, and export\\n    telemetry data (metrics, logs, and traces) for analysis in order to\\n    understand your software's performance and behavior.\\n    This package implements the OpenTelemetry API:\\n    <https://opentelemetry.io/docs/specs/otel/>.\\n    Use this package as a dependency if you want to instrument your R\\n    package for OpenTelemetry.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.2.9000\",\n        \"Depends\": \"R (>= 3.6.0)\",\n        \"Suggests\": \"callr, cli, glue, jsonlite, otelsdk, processx, shiny,\\nspelling, testthat (>= 3.0.0), utils, withr\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate\",\n        \"Config/testthat/edition\": \"3\",\n        \"URL\": \"https://otel.r-lib.org, https://github.com/r-lib/otel\",\n        \"Additional_repositories\": \"https://github.com/r-lib/otelsdk/releases/download/devel\",\n        \"BugReports\": \"https://github.com/r-lib/otel/issues\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2025-08-29 12:46:28 UTC; gaborcsardi\",\n        \"Author\": \"Gábor Csárdi [aut, cre]\",\n        \"Maintainer\": \"Gábor Csárdi <csardi.gabor@gmail.com>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-08-29 13:30:07 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-09-01 01:35:46 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"otel\",\n        \"RemoteRef\": \"otel\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.2.0\"\n      }\n    },\n    \"promises\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"promises\",\n        \"Title\": \"Abstractions for Promise-Based Asynchronous Programming\",\n        \"Version\": \"1.5.0\",\n        \"Authors@R\": \"c(\\n    person(\\\"Joe\\\", \\\"Cheng\\\", , \\\"joe@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Barret\\\", \\\"Schloerke\\\", , \\\"barret@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0001-9986-114X\\\")),\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-1576-2126\\\")),\\n    person(\\\"Charlie\\\", \\\"Gao\\\", , \\\"charlie.gao@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-0750-061X\\\")),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"),\\n           comment = c(ROR = \\\"03wc8by49\\\"))\\n  )\",\n        \"Description\": \"Provides fundamental abstractions for doing asynchronous\\n    programming in R using promises. Asynchronous programming is useful\\n    for allowing a single R process to orchestrate multiple tasks in the\\n    background while also attending to something else. Semantics are\\n    similar to 'JavaScript' promises, but with a syntax that is idiomatic\\n    R.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://rstudio.github.io/promises/,\\nhttps://github.com/rstudio/promises\",\n        \"BugReports\": \"https://github.com/rstudio/promises/issues\",\n        \"Depends\": \"R (>= 4.1.0)\",\n        \"Imports\": \"fastmap (>= 1.1.0), later, lifecycle, magrittr (>= 1.5), otel\\n(>= 0.2.0), R6, rlang\",\n        \"Suggests\": \"future (>= 1.21.0), knitr, mirai, otelsdk (>= 0.2.0), purrr,\\nRcpp, rmarkdown, spelling, testthat (>= 3.0.0), vembedr\",\n        \"VignetteBuilder\": \"knitr\",\n        \"Config/Needs/website\": \"rsconnect, tidyverse/tidytemplate\",\n        \"Config/testthat/edition\": \"3\",\n        \"Config/usethis/last-upkeep\": \"2025-05-27\",\n        \"Encoding\": \"UTF-8\",\n        \"Language\": \"en-US\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2025-10-31 20:26:43 UTC; barret\",\n        \"Author\": \"Joe Cheng [aut],\\n  Barret Schloerke [aut, cre] (ORCID:\\n    <https://orcid.org/0000-0001-9986-114X>),\\n  Winston Chang [aut] (ORCID: <https://orcid.org/0000-0002-1576-2126>),\\n  Charlie Gao [aut] (ORCID: <https://orcid.org/0000-0002-0750-061X>),\\n  Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>)\",\n        \"Maintainer\": \"Barret Schloerke <barret@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-11-01 06:30:02 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-11-01 08:07:07 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"promises\",\n        \"RemoteRef\": \"promises\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.5.0\"\n      }\n    },\n    \"rappdirs\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"rappdirs\",\n        \"Title\": \"Application Directories: Determine Where to Save Data, Caches,\\nand Logs\",\n        \"Version\": \"0.3.4\",\n        \"Authors@R\": \"c(\\n    person(\\\"Hadley\\\", \\\"Wickham\\\", , \\\"hadley@posit.co\\\", role = c(\\\"trl\\\", \\\"cre\\\", \\\"cph\\\")),\\n    person(\\\"Sridhar\\\", \\\"Ratnakumar\\\", role = \\\"aut\\\"),\\n    person(\\\"Trent\\\", \\\"Mick\\\", role = \\\"aut\\\"),\\n    person(\\\"ActiveState\\\", role = \\\"cph\\\",\\n           comment = \\\"R/appdir.r, R/cache.r, R/data.r, R/log.r translated from appdirs\\\"),\\n    person(\\\"Eddy\\\", \\\"Petrisor\\\", role = \\\"ctb\\\"),\\n    person(\\\"Trevor\\\", \\\"Davis\\\", role = c(\\\"trl\\\", \\\"aut\\\"),\\n           comment = c(ORCID = \\\"0000-0001-6341-4639\\\")),\\n    person(\\\"Gabor\\\", \\\"Csardi\\\", role = \\\"ctb\\\"),\\n    person(\\\"Gregory\\\", \\\"Jefferis\\\", role = \\\"ctb\\\"),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"),\\n           comment = c(ROR = \\\"03wc8by49\\\"))\\n  )\",\n        \"Description\": \"An easy way to determine which directories on the users\\n    computer you should use to save data, caches and logs. A port of\\n    Python's 'Appdirs' (<https://github.com/ActiveState/appdirs>) to R.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://rappdirs.r-lib.org, https://github.com/r-lib/rappdirs\",\n        \"BugReports\": \"https://github.com/r-lib/rappdirs/issues\",\n        \"Depends\": \"R (>= 4.1)\",\n        \"Suggests\": \"covr, roxygen2, testthat (>= 3.2.0), withr\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate\",\n        \"Config/testthat/edition\": \"3\",\n        \"Config/usethis/last-upkeep\": \"2025-05-05\",\n        \"Copyright\": \"Original python appdirs module copyright (c) 2010\\nActiveState Software Inc. R port copyright Hadley Wickham,\\nPosit, PBC.  See file LICENSE for details.\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2026-01-16 22:03:30 UTC; hadleywickham\",\n        \"Author\": \"Hadley Wickham [trl, cre, cph],\\n  Sridhar Ratnakumar [aut],\\n  Trent Mick [aut],\\n  ActiveState [cph] (R/appdir.r, R/cache.r, R/data.r, R/log.r translated\\n    from appdirs),\\n  Eddy Petrisor [ctb],\\n  Trevor Davis [trl, aut] (ORCID:\\n    <https://orcid.org/0000-0001-6341-4639>),\\n  Gabor Csardi [ctb],\\n  Gregory Jefferis [ctb],\\n  Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>)\",\n        \"Maintainer\": \"Hadley Wickham <hadley@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2026-01-17 08:40:02 UTC\",\n        \"Built\": \"R 4.5.2; aarch64-apple-darwin20; 2026-01-17 11:42:54 UTC; unix\",\n        \"Archs\": \"rappdirs.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"rappdirs\",\n        \"RemoteRef\": \"rappdirs\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.3.4\"\n      }\n    },\n    \"rlang\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"rlang\",\n        \"Version\": \"1.1.7\",\n        \"Title\": \"Functions for Base Types and Core R and 'Tidyverse' Features\",\n        \"Description\": \"A toolbox for working with base types, core R features\\n  like the condition system, and core 'Tidyverse' features like tidy\\n  evaluation.\",\n        \"Authors@R\": \"c(\\n    person(\\\"Lionel\\\", \\\"Henry\\\", ,\\\"lionel@posit.co\\\", c(\\\"aut\\\", \\\"cre\\\")),\\n    person(\\\"Hadley\\\", \\\"Wickham\\\", ,\\\"hadley@posit.co\\\", \\\"aut\\\"),\\n    person(given = \\\"mikefc\\\",\\n           email = \\\"mikefc@coolbutuseless.com\\\",\\n           role = \\\"cph\\\",\\n           comment = \\\"Hash implementation based on Mike's xxhashlite\\\"),\\n    person(given = \\\"Yann\\\",\\n           family = \\\"Collet\\\",\\n           role = \\\"cph\\\",\\n           comment = \\\"Author of the embedded xxHash library\\\"),\\n    person(given = \\\"Posit, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n    )\",\n        \"License\": \"MIT + file LICENSE\",\n        \"ByteCompile\": \"true\",\n        \"Biarch\": \"true\",\n        \"Depends\": \"R (>= 4.0.0)\",\n        \"Imports\": \"utils\",\n        \"Suggests\": \"cli (>= 3.1.0), covr, crayon, desc, fs, glue, knitr,\\nmagrittr, methods, pillar, pkgload, rmarkdown, stats, testthat\\n(>= 3.2.0), tibble, usethis, vctrs (>= 0.2.3), withr\",\n        \"Enhances\": \"winch\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"URL\": \"https://rlang.r-lib.org, https://github.com/r-lib/rlang\",\n        \"BugReports\": \"https://github.com/r-lib/rlang/issues\",\n        \"Config/build/compilation-database\": \"true\",\n        \"Config/testthat/edition\": \"3\",\n        \"Config/Needs/website\": \"dplyr, tidyverse/tidytemplate\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2026-01-08 10:35:58 UTC; lionel\",\n        \"Author\": \"Lionel Henry [aut, cre],\\n  Hadley Wickham [aut],\\n  mikefc [cph] (Hash implementation based on Mike's xxhashlite),\\n  Yann Collet [cph] (Author of the embedded xxHash library),\\n  Posit, PBC [cph, fnd]\",\n        \"Maintainer\": \"Lionel Henry <lionel@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2026-01-09 12:10:02 UTC\",\n        \"Built\": \"R 4.5.2; aarch64-apple-darwin20; 2026-01-09 16:51:14 UTC; unix\",\n        \"Archs\": \"rlang.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"rlang\",\n        \"RemoteRef\": \"rlang\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.1.7\"\n      }\n    },\n    \"sass\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"sass\",\n        \"Version\": \"0.4.10\",\n        \"Title\": \"Syntactically Awesome Style Sheets ('Sass')\",\n        \"Description\": \"An 'SCSS' compiler, powered by the 'LibSass' library. With this,\\n    R developers can use variables, inheritance, and functions to generate\\n    dynamic style sheets. The package uses the 'Sass CSS' extension language,\\n    which is stable, powerful, and CSS compatible.\",\n        \"Authors@R\": \"c(\\n    person(\\\"Joe\\\", \\\"Cheng\\\", , \\\"joe@rstudio.com\\\", \\\"aut\\\"),\\n    person(\\\"Timothy\\\", \\\"Mastny\\\", , \\\"tim.mastny@gmail.com\\\", \\\"aut\\\"),\\n    person(\\\"Richard\\\", \\\"Iannone\\\", , \\\"rich@rstudio.com\\\", \\\"aut\\\",\\n       comment = c(ORCID = \\\"0000-0003-3925-190X\\\")),\\n    person(\\\"Barret\\\", \\\"Schloerke\\\", , \\\"barret@rstudio.com\\\", \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0001-9986-114X\\\")),\\n    person(\\\"Carson\\\", \\\"Sievert\\\", , \\\"carson@rstudio.com\\\", c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0002-4958-2844\\\")),\\n    person(\\\"Christophe\\\", \\\"Dervieux\\\", , \\\"cderv@rstudio.com\\\", c(\\\"ctb\\\"),\\n           comment = c(ORCID = \\\"0000-0003-4474-2498\\\")),\\n    person(family = \\\"RStudio\\\", role = c(\\\"cph\\\", \\\"fnd\\\")),\\n    person(family = \\\"Sass Open Source Foundation\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"LibSass library\\\"),\\n    person(\\\"Greter\\\", \\\"Marcel\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"LibSass library\\\"),\\n    person(\\\"Mifsud\\\", \\\"Michael\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"LibSass library\\\"),\\n    person(\\\"Hampton\\\", \\\"Catlin\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"LibSass library\\\"),\\n    person(\\\"Natalie\\\", \\\"Weizenbaum\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"LibSass library\\\"),\\n    person(\\\"Chris\\\", \\\"Eppstein\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"LibSass library\\\"),\\n    person(\\\"Adams\\\", \\\"Joseph\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"json.cpp\\\"),\\n    person(\\\"Trifunovic\\\", \\\"Nemanja\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"utf8.h\\\")\\n    )\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://rstudio.github.io/sass/, https://github.com/rstudio/sass\",\n        \"BugReports\": \"https://github.com/rstudio/sass/issues\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"SystemRequirements\": \"GNU make\",\n        \"Imports\": \"fs (>= 1.2.4), rlang (>= 0.4.10), htmltools (>= 0.5.1), R6,\\nrappdirs\",\n        \"Suggests\": \"testthat, knitr, rmarkdown, withr, shiny, curl\",\n        \"VignetteBuilder\": \"knitr\",\n        \"Config/testthat/edition\": \"3\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2025-04-11 18:34:19 UTC; cpsievert\",\n        \"Author\": \"Joe Cheng [aut],\\n  Timothy Mastny [aut],\\n  Richard Iannone [aut] (<https://orcid.org/0000-0003-3925-190X>),\\n  Barret Schloerke [aut] (<https://orcid.org/0000-0001-9986-114X>),\\n  Carson Sievert [aut, cre] (<https://orcid.org/0000-0002-4958-2844>),\\n  Christophe Dervieux [ctb] (<https://orcid.org/0000-0003-4474-2498>),\\n  RStudio [cph, fnd],\\n  Sass Open Source Foundation [ctb, cph] (LibSass library),\\n  Greter Marcel [ctb, cph] (LibSass library),\\n  Mifsud Michael [ctb, cph] (LibSass library),\\n  Hampton Catlin [ctb, cph] (LibSass library),\\n  Natalie Weizenbaum [ctb, cph] (LibSass library),\\n  Chris Eppstein [ctb, cph] (LibSass library),\\n  Adams Joseph [ctb, cph] (json.cpp),\\n  Trifunovic Nemanja [ctb, cph] (utf8.h)\",\n        \"Maintainer\": \"Carson Sievert <carson@rstudio.com>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-04-11 19:50:02 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-04-11 21:17:02 UTC; unix\",\n        \"Archs\": \"sass.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"sass\",\n        \"RemoteRef\": \"sass\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.4.10\"\n      }\n    },\n    \"shiny\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Type\": \"Package\",\n        \"Package\": \"shiny\",\n        \"Title\": \"Web Application Framework for R\",\n        \"Version\": \"1.12.1\",\n        \"Authors@R\": \"c(\\n    person(\\\"Winston\\\", \\\"Chang\\\", , \\\"winston@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-1576-2126\\\")),\\n    person(\\\"Joe\\\", \\\"Cheng\\\", , \\\"joe@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"JJ\\\", \\\"Allaire\\\", , \\\"jj@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Carson\\\", \\\"Sievert\\\", , \\\"carson@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\"),\\n           comment = c(ORCID = \\\"0000-0002-4958-2844\\\")),\\n    person(\\\"Barret\\\", \\\"Schloerke\\\", , \\\"barret@posit.co\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0001-9986-114X\\\")),\\n    person(\\\"Garrick\\\", \\\"Aden-Buie\\\", , \\\"garrick@adenbuie.com\\\", role = \\\"aut\\\",\\n           comment = c(ORCID = \\\"0000-0002-7111-0077\\\")),\\n    person(\\\"Yihui\\\", \\\"Xie\\\", , \\\"yihui@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Jeff\\\", \\\"Allen\\\", role = \\\"aut\\\"),\\n    person(\\\"Jonathan\\\", \\\"McPherson\\\", , \\\"jonathan@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Alan\\\", \\\"Dipert\\\", role = \\\"aut\\\"),\\n    person(\\\"Barbara\\\", \\\"Borges\\\", role = \\\"aut\\\"),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"),\\n           comment = c(ROR = \\\"03wc8by49\\\")),\\n    person(, \\\"jQuery Foundation\\\", role = \\\"cph\\\",\\n           comment = \\\"jQuery library and jQuery UI library\\\"),\\n    person(, \\\"jQuery contributors\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt\\\"),\\n    person(, \\\"jQuery UI contributors\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt\\\"),\\n    person(\\\"Mark\\\", \\\"Otto\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap library\\\"),\\n    person(\\\"Jacob\\\", \\\"Thornton\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap library\\\"),\\n    person(, \\\"Bootstrap contributors\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap library\\\"),\\n    person(, \\\"Twitter, Inc\\\", role = \\\"cph\\\",\\n           comment = \\\"Bootstrap library\\\"),\\n    person(\\\"Prem Nawaz\\\", \\\"Khan\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap accessibility plugin\\\"),\\n    person(\\\"Victor\\\", \\\"Tsaran\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap accessibility plugin\\\"),\\n    person(\\\"Dennis\\\", \\\"Lembree\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap accessibility plugin\\\"),\\n    person(\\\"Srinivasu\\\", \\\"Chakravarthula\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap accessibility plugin\\\"),\\n    person(\\\"Cathy\\\", \\\"O'Connor\\\", role = \\\"ctb\\\",\\n           comment = \\\"Bootstrap accessibility plugin\\\"),\\n    person(, \\\"PayPal, Inc\\\", role = \\\"cph\\\",\\n           comment = \\\"Bootstrap accessibility plugin\\\"),\\n    person(\\\"Stefan\\\", \\\"Petre\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"Bootstrap-datepicker library\\\"),\\n    person(\\\"Andrew\\\", \\\"Rowls\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"Bootstrap-datepicker library\\\"),\\n    person(\\\"Brian\\\", \\\"Reavis\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"selectize.js library\\\"),\\n    person(\\\"Salmen\\\", \\\"Bejaoui\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"selectize-plugin-a11y library\\\"),\\n    person(\\\"Denis\\\", \\\"Ineshin\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"ion.rangeSlider library\\\"),\\n    person(\\\"Sami\\\", \\\"Samhuri\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"Javascript strftime library\\\"),\\n    person(, \\\"SpryMedia Limited\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"DataTables library\\\"),\\n    person(\\\"Ivan\\\", \\\"Sagalaev\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"highlight.js library\\\"),\\n    person(\\\"R Core Team\\\", role = c(\\\"ctb\\\", \\\"cph\\\"),\\n           comment = \\\"tar implementation from R\\\")\\n  )\",\n        \"Description\": \"Makes it incredibly easy to build interactive web\\n    applications with R. Automatic \\\"reactive\\\" binding between inputs and\\n    outputs and extensive prebuilt widgets make it possible to build\\n    beautiful, responsive, and powerful applications with minimal effort.\",\n        \"License\": \"GPL-3 | file LICENSE\",\n        \"URL\": \"https://shiny.posit.co/, https://github.com/rstudio/shiny\",\n        \"BugReports\": \"https://github.com/rstudio/shiny/issues\",\n        \"Depends\": \"methods, R (>= 3.0.2)\",\n        \"Imports\": \"bslib (>= 0.6.0), cachem (>= 1.1.0), cli, commonmark (>=\\n2.0.0), fastmap (>= 1.1.1), fontawesome (>= 0.4.0), glue (>=\\n1.3.2), grDevices, htmltools (>= 0.5.4), httpuv (>= 1.5.2),\\njsonlite (>= 0.9.16), later (>= 1.0.0), lifecycle (>= 0.2.0),\\nmime (>= 0.3), otel, promises (>= 1.5.0), R6 (>= 2.0), rlang\\n(>= 0.4.10), sourcetools, tools, utils, withr, xtable\",\n        \"Suggests\": \"Cairo (>= 1.5-5), coro (>= 1.1.0), datasets, DT, dygraphs,\\nfuture, ggplot2, knitr (>= 1.6), magrittr, markdown, mirai,\\notelsdk (>= 0.2.0), ragg, reactlog (>= 1.0.0), rmarkdown, sass,\\nshowtext, testthat (>= 3.2.1), watcher, yaml\",\n        \"Config/Needs/check\": \"shinytest2\",\n        \"Config/testthat/edition\": \"3\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.3\",\n        \"Collate\": \"'globals.R' 'app-state.R' 'app_template.R' 'bind-cache.R'\\n'bind-event.R' 'bookmark-state-local.R' 'bookmark-state.R'\\n'bootstrap-deprecated.R' 'bootstrap-layout.R' 'conditions.R'\\n'map.R' 'utils.R' 'bootstrap.R' 'busy-indicators-spinners.R'\\n'busy-indicators.R' 'cache-utils.R' 'deprecated.R' 'devmode.R'\\n'diagnose.R' 'extended-task.R' 'fileupload.R' 'graph.R'\\n'reactives.R' 'reactive-domains.R' 'history.R' 'hooks.R'\\n'html-deps.R' 'image-interact-opts.R' 'image-interact.R'\\n'imageutils.R' 'input-action.R' 'input-checkbox.R'\\n'input-checkboxgroup.R' 'input-date.R' 'input-daterange.R'\\n'input-file.R' 'input-numeric.R' 'input-password.R'\\n'input-radiobuttons.R' 'input-select.R' 'input-slider.R'\\n'input-submit.R' 'input-text.R' 'input-textarea.R'\\n'input-utils.R' 'insert-tab.R' 'insert-ui.R' 'jqueryui.R'\\n'knitr.R' 'middleware-shiny.R' 'middleware.R' 'timer.R'\\n'shiny.R' 'mock-session.R' 'modal.R' 'modules.R'\\n'notifications.R' 'otel-attr-srcref.R' 'otel-collect.R'\\n'otel-enable.R' 'otel-error.R' 'otel-label.R'\\n'otel-reactive-update.R' 'otel-session.R' 'otel-shiny.R'\\n'otel-with.R' 'priorityqueue.R' 'progress.R' 'react.R'\\n'reexports.R' 'render-cached-plot.R' 'render-plot.R'\\n'render-table.R' 'run-url.R' 'runapp.R' 'serializers.R'\\n'server-input-handlers.R' 'server-resource-paths.R' 'server.R'\\n'shiny-options.R' 'shiny-package.R' 'shinyapp.R' 'shinyui.R'\\n'shinywrappers.R' 'showcase.R' 'snapshot.R' 'staticimports.R'\\n'tar.R' 'test-export.R' 'test-server.R' 'test.R'\\n'update-input.R' 'utils-lang.R' 'utils-tags.R'\\n'version_bs_date_picker.R' 'version_ion_range_slider.R'\\n'version_jquery.R' 'version_jqueryui.R' 'version_selectize.R'\\n'version_strftime.R' 'viewer.R'\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2025-12-08 21:22:26 UTC; barret\",\n        \"Author\": \"Winston Chang [aut] (ORCID: <https://orcid.org/0000-0002-1576-2126>),\\n  Joe Cheng [aut],\\n  JJ Allaire [aut],\\n  Carson Sievert [aut, cre] (ORCID:\\n    <https://orcid.org/0000-0002-4958-2844>),\\n  Barret Schloerke [aut] (ORCID: <https://orcid.org/0000-0001-9986-114X>),\\n  Garrick Aden-Buie [aut] (ORCID:\\n    <https://orcid.org/0000-0002-7111-0077>),\\n  Yihui Xie [aut],\\n  Jeff Allen [aut],\\n  Jonathan McPherson [aut],\\n  Alan Dipert [aut],\\n  Barbara Borges [aut],\\n  Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>),\\n  jQuery Foundation [cph] (jQuery library and jQuery UI library),\\n  jQuery contributors [ctb, cph] (jQuery library; authors listed in\\n    inst/www/shared/jquery-AUTHORS.txt),\\n  jQuery UI contributors [ctb, cph] (jQuery UI library; authors listed in\\n    inst/www/shared/jqueryui/AUTHORS.txt),\\n  Mark Otto [ctb] (Bootstrap library),\\n  Jacob Thornton [ctb] (Bootstrap library),\\n  Bootstrap contributors [ctb] (Bootstrap library),\\n  Twitter, Inc [cph] (Bootstrap library),\\n  Prem Nawaz Khan [ctb] (Bootstrap accessibility plugin),\\n  Victor Tsaran [ctb] (Bootstrap accessibility plugin),\\n  Dennis Lembree [ctb] (Bootstrap accessibility plugin),\\n  Srinivasu Chakravarthula [ctb] (Bootstrap accessibility plugin),\\n  Cathy O'Connor [ctb] (Bootstrap accessibility plugin),\\n  PayPal, Inc [cph] (Bootstrap accessibility plugin),\\n  Stefan Petre [ctb, cph] (Bootstrap-datepicker library),\\n  Andrew Rowls [ctb, cph] (Bootstrap-datepicker library),\\n  Brian Reavis [ctb, cph] (selectize.js library),\\n  Salmen Bejaoui [ctb, cph] (selectize-plugin-a11y library),\\n  Denis Ineshin [ctb, cph] (ion.rangeSlider library),\\n  Sami Samhuri [ctb, cph] (Javascript strftime library),\\n  SpryMedia Limited [ctb, cph] (DataTables library),\\n  Ivan Sagalaev [ctb, cph] (highlight.js library),\\n  R Core Team [ctb, cph] (tar implementation from R)\",\n        \"Maintainer\": \"Carson Sievert <carson@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2025-12-09 09:30:02 UTC\",\n        \"Built\": \"R 4.5.2; ; 2026-02-08 18:43:38 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"shiny\",\n        \"RemoteRef\": \"shiny\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.12.1\"\n      }\n    },\n    \"sourcetools\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"sourcetools\",\n        \"Type\": \"Package\",\n        \"Title\": \"Tools for Reading, Tokenizing and Parsing R Code\",\n        \"Version\": \"0.1.7-1\",\n        \"Author\": \"Kevin Ushey\",\n        \"Maintainer\": \"Kevin Ushey <kevinushey@gmail.com>\",\n        \"Description\": \"Tools for the reading and tokenization of R code. The\\n    'sourcetools' package provides both an R and C++ interface for the tokenization\\n    of R code, and helpers for interacting with the tokenized representation of R\\n    code.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"Depends\": \"R (>= 3.0.2)\",\n        \"Suggests\": \"testthat\",\n        \"RoxygenNote\": \"5.0.1\",\n        \"BugReports\": \"https://github.com/kevinushey/sourcetools/issues\",\n        \"Encoding\": \"UTF-8\",\n        \"NeedsCompilation\": \"yes\",\n        \"Packaged\": \"2023-01-31 18:03:04 UTC; kevin\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2023-02-01 10:10:02 UTC\",\n        \"Built\": \"R 4.5.0; aarch64-apple-darwin20; 2025-03-31 21:43:26 UTC; unix\",\n        \"Archs\": \"sourcetools.so.dSYM\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"sourcetools\",\n        \"RemoteRef\": \"sourcetools\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"0.1.7-1\"\n      }\n    },\n    \"withr\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"withr\",\n        \"Title\": \"Run Code 'With' Temporarily Modified Global State\",\n        \"Version\": \"3.0.2\",\n        \"Authors@R\": \"c(\\n    person(\\\"Jim\\\", \\\"Hester\\\", role = \\\"aut\\\"),\\n    person(\\\"Lionel\\\", \\\"Henry\\\", , \\\"lionel@posit.co\\\", role = c(\\\"aut\\\", \\\"cre\\\")),\\n    person(\\\"Kirill\\\", \\\"Müller\\\", , \\\"krlmlr+r@mailbox.org\\\", role = \\\"aut\\\"),\\n    person(\\\"Kevin\\\", \\\"Ushey\\\", , \\\"kevinushey@gmail.com\\\", role = \\\"aut\\\"),\\n    person(\\\"Hadley\\\", \\\"Wickham\\\", , \\\"hadley@posit.co\\\", role = \\\"aut\\\"),\\n    person(\\\"Winston\\\", \\\"Chang\\\", role = \\\"aut\\\"),\\n    person(\\\"Jennifer\\\", \\\"Bryan\\\", role = \\\"ctb\\\"),\\n    person(\\\"Richard\\\", \\\"Cotton\\\", role = \\\"ctb\\\"),\\n    person(\\\"Posit Software, PBC\\\", role = c(\\\"cph\\\", \\\"fnd\\\"))\\n  )\",\n        \"Description\": \"A set of functions to run code 'with' safely and temporarily\\n    modified global state. Many of these functions were originally a part\\n    of the 'devtools' package, this provides a simple package with limited\\n    dependencies to provide access to these functions.\",\n        \"License\": \"MIT + file LICENSE\",\n        \"URL\": \"https://withr.r-lib.org, https://github.com/r-lib/withr#readme\",\n        \"BugReports\": \"https://github.com/r-lib/withr/issues\",\n        \"Depends\": \"R (>= 3.6.0)\",\n        \"Imports\": \"graphics, grDevices\",\n        \"Suggests\": \"callr, DBI, knitr, methods, rlang, rmarkdown (>= 2.12),\\nRSQLite, testthat (>= 3.0.0)\",\n        \"VignetteBuilder\": \"knitr\",\n        \"Config/Needs/website\": \"tidyverse/tidytemplate\",\n        \"Config/testthat/edition\": \"3\",\n        \"Encoding\": \"UTF-8\",\n        \"RoxygenNote\": \"7.3.2\",\n        \"Collate\": \"'aaa.R' 'collate.R' 'connection.R' 'db.R' 'defer-exit.R'\\n'standalone-defer.R' 'defer.R' 'devices.R' 'local_.R' 'with_.R'\\n'dir.R' 'env.R' 'file.R' 'language.R' 'libpaths.R' 'locale.R'\\n'makevars.R' 'namespace.R' 'options.R' 'par.R' 'path.R' 'rng.R'\\n'seed.R' 'wrap.R' 'sink.R' 'tempfile.R' 'timezone.R'\\n'torture.R' 'utils.R' 'with.R'\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2024-10-28 10:58:18 UTC; lionel\",\n        \"Author\": \"Jim Hester [aut],\\n  Lionel Henry [aut, cre],\\n  Kirill Müller [aut],\\n  Kevin Ushey [aut],\\n  Hadley Wickham [aut],\\n  Winston Chang [aut],\\n  Jennifer Bryan [ctb],\\n  Richard Cotton [ctb],\\n  Posit Software, PBC [cph, fnd]\",\n        \"Maintainer\": \"Lionel Henry <lionel@posit.co>\",\n        \"Repository\": \"CRAN\",\n        \"Date/Publication\": \"2024-10-28 13:30:02 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-03-31 21:42:13 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"withr\",\n        \"RemoteRef\": \"withr\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"3.0.2\"\n      }\n    },\n    \"xtable\": {\n      \"Source\": \"CRAN\",\n      \"Repository\": \"https://cloud.r-project.org\",\n      \"description\": {\n        \"Package\": \"xtable\",\n        \"Version\": \"1.8-4\",\n        \"Date\": \"2019-04-08\",\n        \"Title\": \"Export Tables to LaTeX or HTML\",\n        \"Authors@R\": \"c(person(\\\"David B.\\\", \\\"Dahl\\\", role=\\\"aut\\\"),\\n             person(\\\"David\\\", \\\"Scott\\\", role=c(\\\"aut\\\",\\\"cre\\\"),\\n               email=\\\"d.scott@auckland.ac.nz\\\"),\\n             person(\\\"Charles\\\", \\\"Roosen\\\", role=\\\"aut\\\"),\\n             person(\\\"Arni\\\", \\\"Magnusson\\\", role=\\\"aut\\\"),\\n             person(\\\"Jonathan\\\", \\\"Swinton\\\", role=\\\"aut\\\"),\\n             person(\\\"Ajay\\\", \\\"Shah\\\", role=\\\"ctb\\\"),\\n             person(\\\"Arne\\\", \\\"Henningsen\\\", role=\\\"ctb\\\"),\\n             person(\\\"Benno\\\", \\\"Puetz\\\", role=\\\"ctb\\\"),\\n             person(\\\"Bernhard\\\", \\\"Pfaff\\\", role=\\\"ctb\\\"),\\n             person(\\\"Claudio\\\", \\\"Agostinelli\\\", role=\\\"ctb\\\"),\\n             person(\\\"Claudius\\\", \\\"Loehnert\\\", role=\\\"ctb\\\"),\\n             person(\\\"David\\\", \\\"Mitchell\\\", role=\\\"ctb\\\"),\\n             person(\\\"David\\\", \\\"Whiting\\\", role=\\\"ctb\\\"),\\n             person(\\\"Fernando da\\\", \\\"Rosa\\\", role=\\\"ctb\\\"),\\n             person(\\\"Guido\\\", \\\"Gay\\\", role=\\\"ctb\\\"),\\n             person(\\\"Guido\\\", \\\"Schulz\\\", role=\\\"ctb\\\"),\\n             person(\\\"Ian\\\", \\\"Fellows\\\", role=\\\"ctb\\\"),\\n             person(\\\"Jeff\\\", \\\"Laake\\\", role=\\\"ctb\\\"),\\n             person(\\\"John\\\", \\\"Walker\\\", role=\\\"ctb\\\"),\\n             person(\\\"Jun\\\", \\\"Yan\\\", role=\\\"ctb\\\"),\\n             person(\\\"Liviu\\\", \\\"Andronic\\\", role=\\\"ctb\\\"),\\n             person(\\\"Markus\\\", \\\"Loecher\\\", role=\\\"ctb\\\"),\\n             person(\\\"Martin\\\", \\\"Gubri\\\", role=\\\"ctb\\\"),\\n             person(\\\"Matthieu\\\", \\\"Stigler\\\", role=\\\"ctb\\\"),\\n             person(\\\"Robert\\\", \\\"Castelo\\\", role=\\\"ctb\\\"),\\n             person(\\\"Seth\\\", \\\"Falcon\\\", role=\\\"ctb\\\"),\\n             person(\\\"Stefan\\\", \\\"Edwards\\\", role=\\\"ctb\\\"),\\n             person(\\\"Sven\\\", \\\"Garbade\\\", role=\\\"ctb\\\"),\\n             person(\\\"Uwe\\\", \\\"Ligges\\\", role=\\\"ctb\\\"))\",\n        \"Maintainer\": \"David Scott <d.scott@auckland.ac.nz>\",\n        \"Imports\": \"stats, utils\",\n        \"Suggests\": \"knitr, plm, zoo, survival\",\n        \"VignetteBuilder\": \"knitr\",\n        \"Description\": \"Coerce data to LaTeX and HTML tables.\",\n        \"URL\": \"http://xtable.r-forge.r-project.org/\",\n        \"Depends\": \"R (>= 2.10.0)\",\n        \"License\": \"GPL (>= 2)\",\n        \"Repository\": \"CRAN\",\n        \"NeedsCompilation\": \"no\",\n        \"Packaged\": \"2019-04-21 10:56:51 UTC; dsco036\",\n        \"Author\": \"David B. Dahl [aut],\\n  David Scott [aut, cre],\\n  Charles Roosen [aut],\\n  Arni Magnusson [aut],\\n  Jonathan Swinton [aut],\\n  Ajay Shah [ctb],\\n  Arne Henningsen [ctb],\\n  Benno Puetz [ctb],\\n  Bernhard Pfaff [ctb],\\n  Claudio Agostinelli [ctb],\\n  Claudius Loehnert [ctb],\\n  David Mitchell [ctb],\\n  David Whiting [ctb],\\n  Fernando da Rosa [ctb],\\n  Guido Gay [ctb],\\n  Guido Schulz [ctb],\\n  Ian Fellows [ctb],\\n  Jeff Laake [ctb],\\n  John Walker [ctb],\\n  Jun Yan [ctb],\\n  Liviu Andronic [ctb],\\n  Markus Loecher [ctb],\\n  Martin Gubri [ctb],\\n  Matthieu Stigler [ctb],\\n  Robert Castelo [ctb],\\n  Seth Falcon [ctb],\\n  Stefan Edwards [ctb],\\n  Sven Garbade [ctb],\\n  Uwe Ligges [ctb]\",\n        \"Date/Publication\": \"2019-04-21 12:20:03 UTC\",\n        \"Built\": \"R 4.5.0; ; 2025-03-31 21:41:48 UTC; unix\",\n        \"RemoteType\": \"standard\",\n        \"RemotePkgRef\": \"xtable\",\n        \"RemoteRef\": \"xtable\",\n        \"RemoteRepos\": \"https://cran.rstudio.com\",\n        \"RemoteSha\": \"1.8-4\"\n      }\n    }\n  },\n  \"files\": {\n    \"app.R\": {\n      \"checksum\": \"19b8966ed2c3c8fdcb56305ba1712af4\"\n    }\n  },\n  \"users\": null\n}\n"
  },
  {
    "path": "tests/shinyapps-integration/setup.R",
    "content": "library(testthat)\nlibrary(rsconnect)\n\n# Skip if shinyapps.io credentials are not available\nshinyapps_name <- Sys.getenv(\"SHINYAPPS_NAME\")\nshinyapps_token <- Sys.getenv(\"SHINYAPPS_TOKEN\")\nshinyapps_secret <- Sys.getenv(\"SHINYAPPS_SECRET\")\n\nif (shinyapps_name == \"\" || shinyapps_token == \"\" || shinyapps_secret == \"\") {\n  stop(\n    \"SHINYAPPS_NAME, SHINYAPPS_TOKEN, and SHINYAPPS_SECRET must be set to run shinyapps.io integration tests.\"\n  )\n}\noriginal_repos <- getOption(\"repos\")\nif (\"RSPM\" %in% names(original_repos) && !\"CRAN\" %in% names(original_repos)) {\n  cran_repos <- original_repos\n  names(cran_repos)[names(cran_repos) == \"RSPM\"] <- \"CRAN\"\n  options(repos = cran_repos)\n  withr::defer(options(repos = original_repos), teardown_env())\n}\n# Register the shinyapps.io account for testing\nrsconnect::setAccountInfo(\n  name = shinyapps_name,\n  token = shinyapps_token,\n  secret = shinyapps_secret\n)\n\n# apps from this run will have names starting with this prefix. we use this on\n# cleanup to know which apps to purge from the test account.\nrun_prefix <- paste(sample(c(letters, LETTERS, 0:9), 5), collapse = \"\")\n\nwithr::defer(\n  {\n    tryCatch(\n      {\n        apps <- applications(account = shinyapps_name)\n        to_purge <- apps[grepl(paste0(\"^\", run_prefix), apps$name), \"name\"]\n        # purge applications from the current run\n        lapply(to_purge, purgeApp, account = shinyapps_name)\n      },\n      error = function(e) {\n        warning(\n          \"Unable to clean up test applications from shinyapps.io account\"\n        )\n      }\n    )\n    removeAccount(shinyapps_name)\n    # Clean up any rsconnect deployment artifacts\n    files <- grep(\n      paste0(\"rsconnect/shinyapps.io/\", shinyapps_name),\n      list.files(recursive = TRUE),\n      value = TRUE\n    )\n    file.remove(files)\n    dirs <- grep(\n      paste0(\"rsconnect/shinyapps.io/\", shinyapps_name),\n      list.files(recursive = TRUE, include.dirs = TRUE),\n      value = TRUE\n    )\n    file.remove(dirs)\n    file.remove(dirname(dirs))\n  },\n  teardown_env()\n)\n"
  },
  {
    "path": "tests/shinyapps-integration/test-shinyapps-deploy.R",
    "content": "test_that(\"can deploy to shinyapps.io and terminate\", {\n  app_name <- paste0(\n    run_prefix,\n    \"-rsconnect-test-\",\n    strftime(Sys.time(), \"%Y%m%d%H%M%S\")\n  )\n  expect_no_error(\n    deployApp(\n      appDir = \"example-shiny\",\n      appName = app_name,\n      account = shinyapps_name,\n      forceUpdate = TRUE,\n      manifestPath = \"example-shiny/manifest.json\"\n    )\n  )\n  expect_no_error(\n    terminateApp(app_name, account = shinyapps_name)\n  )\n})\n"
  },
  {
    "path": "tests/testthat/_snaps/account-find.md",
    "content": "# validates its arguments\n\n    Code\n      findAccount(1, NULL)\n    Condition\n      Error:\n      ! `account` must be a single string or `NULL`, not the number 1.\n    Code\n      findAccount(NULL, 1)\n    Condition\n      Error:\n      ! `server` must be a single string or `NULL`, not the number 1.\n\n# error if no accounts\n\n    Code\n      findAccount()\n    Condition\n      Error:\n      ! No accounts registered.\n      i To register an account, call `rsconnect::connectCloudUser()` (Posit Connect Cloud), `rsconnect::connectUser()` (Posit Connect), or `rsconnect::setAccountInfo()` (shinyapps.io).\n\n# error if no matching account\n\n    Code\n      findAccount(\"unknown\", NULL)\n    Condition\n      Error:\n      ! Can't find any accounts with `account` = \"unknown\".\n      i Available account names: \"albert\".\n    Code\n      findAccount(NULL, \"unknown\")\n    Condition\n      Error:\n      ! Can't find any accounts with `server` = \"unknown\".\n      i Known servers are \"example.com\".\n    Code\n      findAccount(\"unknown\", \"unknown\")\n    Condition\n      Error:\n      ! Can't find account with `name` = \"unknown\" and `server` = \"unknown\"\n      i Call `accounts()` to see available options.\n\n# error if ambiguous accounts in non-interactive environment\n\n    Code\n      findAccount()\n    Condition\n      Error:\n      ! Found multiple accounts.\n      Please disambiguate by setting `server` and/or `account`.\n      i Available servers: \"x\" and \"y\".\n      i Available account names: \"a\" and \"b\".\n    Code\n      findAccount(\"a\", NULL)\n    Condition\n      Error:\n      ! Found multiple accounts for `account` = \"a\".\n      Please disambiguate by setting `server`.\n      i Available servers: \"x\" and \"y\".\n    Code\n      findAccount(NULL, \"y\")\n    Condition\n      Error:\n      ! Found multiple accounts for `server` = \"y\".\n      Please disambiguate by setting `account`.\n      i Known account names are \"a\" and \"b\".\n\n# prompted to pick account in interactive environment\n\n    Code\n      out <- findAccount()\n    Message\n      Found multiple accounts.\n      Which one do you want to use?\n      1: server: x / username: a\n      2: server: y / username: a\n      3: server: y / username: b\n      Selection: 2\n\n"
  },
  {
    "path": "tests/testthat/_snaps/accounts.md",
    "content": "# secrets are hidden from casual inspection\n\n    Code\n      accountInfo(\"1\")$secret\n    Output\n      [1] \"SECRET... (redacted)\"\n    Code\n      accountInfo(\"2\")$private_key\n    Output\n      [1] \"SECRET... (redacted)\"\n    Code\n      accountInfo(\"3\")$apiKey\n    Output\n      [1] \"SECRET... (redacted)\"\n\n# setAccountInfo() gives nice error on bad copy and paste\n\n    Code\n      setAccountInfo(\"name\", \"token\", \"<SECRET>\")\n    Condition\n      Error in `setAccountInfo()`:\n      ! You've copied and pasted the wrong thing.\n      i Either click 'Show secret' or 'Copy to clipboard'.\n\n"
  },
  {
    "path": "tests/testthat/_snaps/appDependencies.md",
    "content": "# infers correct packages for each source\n\n    Code\n      inferRPackageDependencies(simulateMetadata(\"rmd-static\"))\n    Output\n      [1] \"rmarkdown\"\n    Code\n      inferRPackageDependencies(simulateMetadata(\"rmd-static\", hasParameters = TRUE))\n    Output\n      [1] \"rmarkdown\" \"shiny\"    \n    Code\n      inferRPackageDependencies(simulateMetadata(\"quarto-static\"))\n    Output\n      [1] \"rmarkdown\"\n    Code\n      inferRPackageDependencies(simulateMetadata(\"quarto-shiny\"))\n    Output\n      [1] \"rmarkdown\" \"shiny\"    \n    Code\n      inferRPackageDependencies(simulateMetadata(\"rmd-shiny\"))\n    Output\n      [1] \"rmarkdown\" \"shiny\"    \n    Code\n      inferRPackageDependencies(simulateMetadata(\"shiny\"))\n    Output\n      [1] \"shiny\"\n    Code\n      inferRPackageDependencies(simulateMetadata(\"api\", plumberInfo = \"plumber\"))\n    Output\n      [1] \"plumber\"\n    Code\n      inferRPackageDependencies(simulateMetadata(\"api\", documentsHavePython = TRUE,\n        plumberInfo = \"plumber\"))\n    Output\n      [1] \"plumber\"    \"reticulate\"\n    Code\n      inferRPackageDependencies(simulateMetadata(\"api\", plumberInfo = \"plumber2\"))\n    Output\n      [1] \"plumber2\"\n\n"
  },
  {
    "path": "tests/testthat/_snaps/appMetadata-quarto.md",
    "content": "# quartoInspect requires quarto\n\n    Code\n      quartoInspect()\n    Condition\n      Error in `quartoInspect()`:\n      ! `quarto` not found.\n      i Check that it is installed and available on your `PATH`.\n\n# quartoInspect produces an error when a document cannot be inspected\n\n    Code\n      quartoInspect(dir, \"bad.qmd\")\n    Condition\n      Error in `quartoInspect()`:\n      ! Failed to run `quarto inspect` against your content:\n      ERROR: Validation of YAML front matter failed.\n\n# quartoInspect produces an error when a project cannot be inspected\n\n    Code\n      quartoInspect(dir, \"bad.qmd\")\n    Condition\n      Error in `quartoInspect()`:\n      ! Failed to run `quarto inspect` against your content:\n      ERROR: Unsupported project type unsupported\n\n"
  },
  {
    "path": "tests/testthat/_snaps/appMetadata.md",
    "content": "# validates quarto argument\n\n    Code\n      appMetadata(dir, c(\"foo.Rmd\"), quarto = 1)\n    Condition\n      Error in `appMetadata()`:\n      ! `quarto` must be `TRUE`, `FALSE`, or `NA`, not the number 1.\n\n# Shiny Quarto without an appropriate engine is an error\n\n    Code\n      appMetadata(dir, files)\n    Condition\n      Error in `appMetadata()`:\n      ! The Quarto document requires a server but does not use an executable engine.\n      Consider including some executable code, specifying an engine, or removing the server configuration.\n\n# errors if no files with needed extension\n\n    Code\n      inferAppPrimaryDoc(NULL, \"a.R\", \"static\")\n    Condition\n      Error in `inferAppPrimaryDoc()`:\n      ! Failed to determine `appPrimaryDoc` for \"static\" content.\n      x No files matching \"\\\\.html?$\".\n    Code\n      inferAppPrimaryDoc(NULL, \"a.html\", \"rmd-static\")\n    Condition\n      Error in `inferAppPrimaryDoc()`:\n      ! Failed to determine `appPrimaryDoc` for \"rmd-static\" content.\n      x No files matching \"\\\\.rmd$\".\n    Code\n      inferAppPrimaryDoc(NULL, \"a.html\", \"rmd-shiny\")\n    Condition\n      Error in `inferAppPrimaryDoc()`:\n      ! Failed to determine `appPrimaryDoc` for \"rmd-shiny\" content.\n      x No files matching \"\\\\.rmd$\".\n    Code\n      inferAppPrimaryDoc(NULL, \"a.html\", \"quarto-static\")\n    Condition\n      Error in `inferAppPrimaryDoc()`:\n      ! Failed to determine `appPrimaryDoc` for \"quarto-static\" content.\n      x No files matching \"\\\\.(r|rmd|qmd)$\".\n    Code\n      inferAppPrimaryDoc(NULL, \"a.html\", \"quarto-shiny\")\n    Condition\n      Error in `inferAppPrimaryDoc()`:\n      ! Failed to determine `appPrimaryDoc` for \"quarto-shiny\" content.\n      x No files matching \"\\\\.(rmd|qmd)$\".\n\n"
  },
  {
    "path": "tests/testthat/_snaps/applications.md",
    "content": "# syncAppMetadata deletes deployment records if needed\n\n    Code\n      syncAppMetadata(app)\n    Message\n      Deleting deployment record for deleted app 123.\n\n"
  },
  {
    "path": "tests/testthat/_snaps/bundle.md",
    "content": "# removes renv/packrat activation\n\n    Code\n      tweakRProfile(path)\n      writeLines(readLines(path))\n    Output\n      # Modified by rsconnect package <VERSION> on <NOW>\n      # Line 1\n      # renv initialization disabled in published application\n      # source(\"renv/activate.R\")\n      # Line 3\n      # Packrat initialization disabled in published application\n      # source(\"packrat/init.R\")\n      # Line 5\n\n"
  },
  {
    "path": "tests/testthat/_snaps/bundleFiles.md",
    "content": "# can read all files from directory\n\n    Code\n      listDeploymentFiles(dir)\n    Condition\n      Error:\n      ! No content to deploy.\n      x `appDir` is empty.\n\n# can read selected files from directory\n\n    Code\n      out <- listDeploymentFiles(dir, c(\"b.R\", \"c.R\"))\n    Condition\n      Warning:\n      All files listed in `appFiles` must exist.\n      Problems: 'c.R'\n\n---\n\n    Code\n      listDeploymentFiles(dir, character())\n    Condition\n      Error:\n      ! No content to deploy.\n      x `appFiles` didn't match any files in `appDir`.\n\n# can read selected files from manifest\n\n    Code\n      out <- listDeploymentFiles(dir, appFileManifest = file.path(dir, \"manifest\"))\n    Condition\n      Warning:\n      All files listed in `appFileManifest` must exist.\n      Problems: 'c.R'\n\n---\n\n    Code\n      listDeploymentFiles(dir, appFileManifest = file.path(dir, \"manifest\"))\n    Condition\n      Error:\n      ! No content to deploy.\n      x `appFileManifest` contains no usable files.\n\n# checks its inputs\n\n    Code\n      listDeploymentFiles(dir)\n    Condition\n      Error:\n      ! No content to deploy.\n      x `appDir` is empty.\n    Code\n      listDeploymentFiles(dir, appFiles = \"a.R\", appFileManifest = \"b.R\")\n    Condition\n      Error:\n      ! Must specify at most one of `appFiles` and `appFileManifest`\n    Code\n      listDeploymentFiles(dir, appFiles = 1)\n    Condition\n      Error:\n      ! `appFiles` must be a character vector or `NULL`, not the number 1.\n    Code\n      listDeploymentFiles(dir, appFileManifest = \"doestexist\")\n    Condition\n      Error:\n      ! `appFileManifest`, \"doestexist\", does not exist.\n\n# drops drops non-existent files with warning\n\n    Code\n      out <- explodeFiles(dir, c(\"a\", \"d\"))\n    Condition\n      Warning:\n      All files listed in `appFiles` must exist.\n      Problems: 'd'\n\n# generate nicely formatted messages\n\n    Code\n      explodeFiles(dir, c(\"a\", \"b\"))\n    Condition\n      Error in `enforceBundleLimits()`:\n      ! `appDir` ('<TEMPDIR>') is too large to be deployed.\n      x The maximum number of files is 1.\n      x This directory contains at least 2 files.\n      i Remove some files or adjust the rsconnect.max.bundle.files option.\n        e.g., options(rsconnect.max.bundle.files = 15000)\n        See `?rsconnect::rsconnectOptions` for additional guidance.\n    Code\n      explodeFiles(dir, \"c\")\n    Condition\n      Error in `enforceBundleLimits()`:\n      ! `appDir` ('<TEMPDIR>') is too large to be deployed.\n      x The maximum size is 5 bytes.\n      x This directory is at least ?? bytes.\n      i Remove some files or adjust the rsconnect.max.bundle.size option.\n        e.g., options(rsconnect.max.bundle.size = 6 * 1024^3)\n        See `?rsconnect::rsconnectOptions` for additional guidance.\n\n# detectLongNames produces informative warning if needed\n\n    Code\n      detectLongNames(dir, 0)\n    Condition\n      Warning:\n      The bundle contains files with user/group names longer than 0.\n      x Files: 'a.r', 'b.r', and 'c.r'\n      x Long user and group names cause the internal R tar implementation to produce invalid archives\n      i Set the `rsconnect.tar` option or the `RSCONNECT_TAR` environment variable to the path to a tar executable.\n\n"
  },
  {
    "path": "tests/testthat/_snaps/bundlePackage.md",
    "content": "# can snapshot deps with renv\n\n    Code\n      pkgs <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies\n      v Found 2 dependencies\n\n# can snapshot deps with packrat (option)\n\n    Code\n      pkgs <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies with packrat\n      v Found 2 dependencies\n\n# can snapshot deps with packrat (env var)\n\n    Code\n      pkgs <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies with packrat\n      v Found 2 dependencies\n\n# can capture deps from renv lockfile\n\n    Code\n      pkgs <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies from renv.lock\n      v Found 3 dependencies\n\n# can capture deps from renv lockfile in custom location (RENV_PATHS_LOCKFILE)\n\n    Code\n      pkgs <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies from renv.lock\n      v Found 3 dependencies\n\n# can capture deps from renv lockfile with renv profile\n\n    Code\n      pkgs <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies from renv.lock\n      v Found 3 dependencies\n\n# can capture deps with packrat even when renv lockfile present\n\n    Code\n      pkgs <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies with packrat\n      v Found 2 dependencies\n\n# error if can't find source\n\n    Code\n      . <- bundlePackages(app_dir)\n    Message\n      i Capturing R dependencies\n      v Found 1 dependency\n    Condition\n      Error:\n      ! All packages must be installed from a reproducible location.\n      x Can't re-install packages installed from source: shiny.\n      i See `rsconnect::appDependencies()` for more details.\n\n"
  },
  {
    "path": "tests/testthat/_snaps/bundlePackagePackrat.md",
    "content": "# uninstalled packages error\n\n    Code\n      snapshotPackratDependencies(app)\n    Condition\n      Error in `addPackratSnapshot()`:\n      ! Failed to snapshot dependencies\n      Caused by error:\n      ! Unable to retrieve package records for the following packages:\n      - 'doesntexist1', 'doesntexist2'\n\n"
  },
  {
    "path": "tests/testthat/_snaps/bundlePackageRenv.md",
    "content": "# large directories are analyzed\n\n    Code\n      deps <- snapshotRenvDependencies(app_dir)\n\n# errors when renv::snapshot fails\n\n    Code\n      snapshotRenvDependencies(app_dir)\n    Condition\n      Error in `snapshotRenvDependencies()`:\n      ! Failed to snapshot dependencies with renv.\n      i For example, you have a locally-developed package that is installed from disk.\n      Caused by error in `renv::snapshot()`:\n      ! can't snapshot this package\n\n# errors if library and project are inconsistent\n\n    Code\n      parseRenvDependencies(file.path(app_dir, \"renv.lock\"), app_dir)\n    Condition\n      Error in `parseRenvDependencies()`:\n      ! Library and lockfile are out of sync\n      i Use renv::restore() or renv::snapshot() to synchronise\n      i Or ignore the lockfile by adding to your .rscignore\n      i Or set `dependencyResolution = \"library\"` to ignore the lockfile and use the local library instead\n\n"
  },
  {
    "path": "tests/testthat/_snaps/bundlePython.md",
    "content": "# throws error if environment.py fails\n\n    Code\n      inferPythonEnv(\".\", pythonPathOrSkip())\n    Condition\n      Error in `inferPythonEnv()`:\n      ! Error reading requirements.txt: [Errno 13] Permission denied: './requirements.txt'\n\n"
  },
  {
    "path": "tests/testthat/_snaps/client-connect.md",
    "content": "# leading timestamps are stripped\n\n    Code\n      stripConnectTimestamps(c(\n        \"2024/04/24 13:08:04.901698921 [rsc-session] Content GUID: 3bfbd98a-6d6d-41bd-a15f-cab52025742f\",\n        \"2024/04/24 13:08:04.901734307 [rsc-session] Content ID: 43888\",\n        \"2024/04/24 13:08:04.901742487 [rsc-session] Bundle ID: 94502\",\n        \"2024/04/24 13:08:04.901747536 [rsc-session] Variant ID: 6465\"))\n    Output\n      [1] \"[rsc-session] Content GUID: 3bfbd98a-6d6d-41bd-a15f-cab52025742f\"\n      [2] \"[rsc-session] Content ID: 43888\"                                 \n      [3] \"[rsc-session] Bundle ID: 94502\"                                  \n      [4] \"[rsc-session] Variant ID: 6465\"                                  \n\n# non-leading timestamps remain\n\n    Code\n      stripConnectTimestamps(c(\n        \"this message has a timestamp 2024/04/24 13:08:04.901698921 within a line\"))\n    Output\n      [1] \"this message has a timestamp 2024/04/24 13:08:04.901698921 within a line\"\n\n# messages without recognized timestamps are unmodified\n\n    Code\n      stripConnectTimestamps(c(\"this message has no timestamp\",\n        \"2024/04/24 13:08 this message timestamp has a different format\"))\n    Output\n      [1] \"this message has no timestamp\"                                 \n      [2] \"2024/04/24 13:08 this message timestamp has a different format\"\n\n# waitForTask\n\n    Code\n      invisible(client$waitForTask(101, quiet = FALSE))\n    Output\n      [rsc-session] Content GUID: 3bfbd98a-6d6d-41bd-a15f-cab52025742f\n      [rsc-session] Content ID: 43888\n      [rsc-session] Bundle ID: 94502\n      [rsc-session] Variant ID: 6465\n\n---\n\n    Code\n      invisible(client$waitForTask(42, quiet = TRUE))\n\n# getDefaultSnowflakeConnectionName errors when default connection doesn't match server\n\n    Code\n      getDefaultSnowflakeConnectionName(\n        \"https://prefix-org-account.snowflakecomputing.app/__api__\")\n    Condition\n      Error in `getDefaultSnowflakeConnectionName()`:\n      ! The default Snowflake connection account \"different-xyz789\" does not appear to match the Connect server.\n      i Pass `snowflakeConnectionName` to use a different connection.\n\n# getDefaultSnowflakeConnectionName errors when no default connection exists\n\n    Code\n      getDefaultSnowflakeConnectionName(\n        \"https://prefix-org-account.snowflakecomputing.app/__api__\")\n    Condition\n      Error in `getDefaultSnowflakeConnectionName()`:\n      ! No default `snowflakeConnectionName`.\n      i Provide `snowflakeConnectionName` explicitly.\n      Caused by error in `snowflakeauth::snowflake_connection()`:\n      ! No default connection configured\n\n"
  },
  {
    "path": "tests/testthat/_snaps/cookies.md",
    "content": "# Invalid cookies fail parsing\n\n    Code\n      cookie <- parseCookie(\"x=1; Path=/something/else\", \"/path\")\n    Condition\n      Warning in `parseCookie()`:\n      Invalid path set for cookie on request for '/path': x=1; Path=/something/else\n\n---\n\n    Code\n      cookie <- parseCookie(\"mycookie;\")\n    Condition\n      Warning in `parseCookie()`:\n      Unable to parse set-cookie header: mycookie;\n\n"
  },
  {
    "path": "tests/testthat/_snaps/deployApp.md",
    "content": "# appDir must be an existing directory\n\n    Code\n      deployApp(1)\n    Condition\n      Error in `deployApp()`:\n      ! `appDir` must be a single string, not the number 1.\n    Code\n      deployApp(\"doesntexist\")\n    Condition\n      Error in `deployApp()`:\n      ! `appDir`, \"doesntexist\", does not exist.\n\n# appPrimaryDoc must exist, if supplied\n\n    Code\n      deployApp(dir, appPrimaryDoc = c(\"foo.Rmd\", \"bar.Rmd\"))\n    Condition\n      Error in `deployApp()`:\n      ! `appPrimaryDoc` must be a single string, not a character vector.\n    Code\n      deployApp(dir, appPrimaryDoc = \"foo.Rmd\")\n    Condition\n      Error in `deployApp()`:\n      ! `appPrimaryDoc` not found inside `appDir`\n\n# startup scripts are logged by default\n\n    Code\n      runStartupScripts(\".\")\n    Message\n      i Running ./.rsconnect_profile\n\n# deployHook executes function if set\n\n    Code\n      . <- runDeploymentHook(\"PATH\", \"rsconnect.pre.deploy\", verbose = TRUE)\n    Output\n      Invoking `rsconnect.pre.deploy` hook\n\n# applicationDeleted() errors or prompts as needed\n\n    Code\n      applicationDeleted(client, target, app)\n    Condition\n      Error in `applicationDeleted()`:\n      ! Failed to find existing content on server; it's probably been deleted.\n      i Use `forgetDeployment()` to remove outdated record and try again.\n      i Or use `applications()` to see other content deployed to the the server.\n\n---\n\n    Code\n      . <- applicationDeleted(client, target, app)\n    Message\n      Failed to find existing content on server; it's probably been deleted.\n      What do you want to do?\n      1: Give up and try again later\n      2: Delete existing deployment record & deploy this content as a new item\n      Selection: 2\n\n# deployApp() errors if envVars is given a named vector\n\n    Code\n      deployApp(local_temp_app(), envVars = c(FLAG = \"true\"))\n    Condition\n      Error in `deployApp()`:\n      ! `envVars` must be a character vector containing only environment variable names.\n      i Set environment variables with `Sys.setenv() or an `.Renviron` file.\n      i Use `unname()` to remove the names from the vector passed to `envVars`.\n\n# manifest must contain required fields\n\n    Code\n      deployApp(appDir, manifestPath = \"manifest.json\")\n    Condition\n      Error in `deployApp()`:\n      ! Invalid manifest file.\n      x Manifest must contain metadata$appmode.\n\n---\n\n    Code\n      deployApp(appDir, manifestPath = \"manifest.json\")\n    Condition\n      Error in `deployApp()`:\n      ! Invalid manifest file.\n      x Manifest must contain metadata$appmode.\n\n# manifest must contain files\n\n    Code\n      deployApp(appDir, manifestPath = \"manifest.json\")\n    Condition\n      Error in `deployApp()`:\n      ! Invalid manifest file.\n      x Manifest contains no files.\n\n# all files in manifest must exist in appDir\n\n    Code\n      deployApp(appDir, manifestPath = \"manifest.json\")\n    Condition\n      Error in `deployApp()`:\n      ! Files listed in manifest are missing from `appDir`.\n      x Missing files: 'missing.R'\n\n"
  },
  {
    "path": "tests/testthat/_snaps/deployDoc.md",
    "content": "# deployDoc correctly reports bad path\n\n    Code\n      deployDoc(\"doesntexist.Rmd\")\n    Condition\n      Error in `deployDoc()`:\n      ! `doc`, \"doesntexist.Rmd\", does not exist.\n\n"
  },
  {
    "path": "tests/testthat/_snaps/deploymentTarget.md",
    "content": "# errors if no accounts\n\n    Code\n      findDeploymentTarget()\n    Condition\n      Error:\n      ! No accounts registered.\n      i To register an account, call `rsconnect::connectCloudUser()` (Posit Connect Cloud), `rsconnect::connectUser()` (Posit Connect), or `rsconnect::setAccountInfo()` (shinyapps.io).\n\n# errors if unknown account or server\n\n    Code\n      findDeploymentTarget(server = \"unknown\")\n    Condition\n      Error:\n      ! Can't find any accounts with `server` = \"unknown\".\n      i Known servers are \"bar\".\n    Code\n      findDeploymentTarget(account = \"john\")\n    Condition\n      Error:\n      ! Can't find any accounts with `account` = \"john\".\n      i Available account names: \"foo\".\n\n# errors if no previous deployments and multiple accounts\n\n    Code\n      findDeploymentTarget(app_dir)\n    Condition\n      Error:\n      ! Found multiple accounts.\n      Please disambiguate by setting `server` and/or `account`.\n      i Available servers: \"foo1\" and \"foo2\".\n      i Available account names: \"ron\".\n    Code\n      findDeploymentTarget(app_dir, appName = \"test\")\n    Condition\n      Error:\n      ! Found multiple accounts.\n      Please disambiguate by setting `server` and/or `account`.\n      i Available servers: \"foo1\" and \"foo2\".\n      i Available account names: \"ron\".\n\n# handles accounts if only server specified\n\n    Code\n      findDeploymentTarget(app_dir, server = \"foo\")\n    Condition\n      Error:\n      ! Found multiple accounts for `server` = \"foo\".\n      Please disambiguate by setting `account`.\n      i Known account names are \"john\" and \"ron\".\n\n# errors/prompts if multiple deployments\n\n    Code\n      findDeploymentTarget(app_dir, appName = \"test\")\n    Condition\n      Error:\n      ! This directory has been previously deployed in multiple places.\n      Please use `appName`, `server` or `account` to disambiguate.\n      Known applications:\n      * test (server: server1.com / username: ron): <https://server1.com/ron/123>\n      * test (server: server2.com / username: ron): <https://server2.com/ron/123>\n    Code\n      findDeploymentTarget(app_dir)\n    Condition\n      Error:\n      ! This directory has been previously deployed in multiple places.\n      Please use `appName`, `server` or `account` to disambiguate.\n      Known applications:\n      * test (server: server1.com / username: ron): <https://server1.com/ron/123>\n      * test (server: server2.com / username: ron): <https://server2.com/ron/123>\n\n---\n\n    Code\n      target <- findDeploymentTarget(app_dir)\n    Message\n      This directory has been previously deployed in multiple places.\n      Which deployment do you want to use?\n      1: test (server: server1.com / username: ron): <https://server1.com/ron/123>\n      2: test (server: server2.com / username: ron): <https://server2.com/ron/123>\n      Selection: 1\n\n# succeeds if there are no deployments and a single account\n\n    Code\n      findDeploymentTarget(app_dir)\n    Condition\n      Error:\n      ! Discovered a previously deployed app named \"remotename\"\n      (View it at <app-url>)\n      i Set `forceUpdate = TRUE` to update it.\n      i Supply a unique `appName` to deploy a new application.\n\n# shouldUpdateApp errors when non-interactive\n\n    Code\n      shouldUpdateApp(app, \"my_app-1\")\n    Condition\n      Error:\n      ! Discovered a previously deployed app named \"my_app\"\n      (View it at <https://example.com>)\n      i Set `forceUpdate = TRUE` to update it.\n      i Supply a unique `appName` to deploy a new application.\n\n# shouldUpdateApp handles 3 options\n\n    Code\n      one <- shouldUpdateApp(app, \"my_app-1\")\n    Message\n      Discovered a previously deployed app named \"my_app\"\n      (View it at <https://example.com>)\n      What do you want to do?\n      1: Update the existing app.\n      2: Create a new app with automatically generated name (\"my_app-1\").\n      3: Abort this deployment and supply a custom `appName`.\n      Selection: 1\n    Code\n      two <- shouldUpdateApp(app, \"my_app-1\")\n    Message\n      Discovered a previously deployed app named \"my_app\"\n      (View it at <https://example.com>)\n      What do you want to do?\n      1: Update the existing app.\n      2: Create a new app with automatically generated name (\"my_app-1\").\n      3: Abort this deployment and supply a custom `appName`.\n      Selection: 2\n    Code\n      three <- shouldUpdateApp(app, \"my_app-1\")\n    Message\n      Discovered a previously deployed app named \"my_app\"\n      (View it at <https://example.com>)\n      What do you want to do?\n      1: Update the existing app.\n      2: Create a new app with automatically generated name (\"my_app-1\").\n      3: Abort this deployment and supply a custom `appName`.\n      Selection: 3\n    Condition\n      Error:\n      ! Quitting...\n\n"
  },
  {
    "path": "tests/testthat/_snaps/deployments-find.md",
    "content": "# error when no deployments and no accounts\n\n    Code\n      findDeployment(app, appName = \"placeholder\")\n    Condition\n      Error in `accountInfo()`:\n      ! No accounts registered.\n      i To register an account, call `rsconnect::connectCloudUser()` (Posit Connect Cloud), `rsconnect::connectUser()` (Posit Connect), or `rsconnect::setAccountInfo()` (shinyapps.io).\n\n# disambiguates multiple deployments\n\n    Code\n      findDeployment(app)\n    Condition\n      Error:\n      ! This directory has been previously deployed in multiple places.\n      Please use `appName`, `server` or `account` to disambiguate.\n      Known applications:\n      * test1 (server: example.com / username: ron): <https://example.com/ron/123>\n      * test2 (server: example.com / username: ron): <https://example.com/ron/123>\n\n---\n\n    Code\n      dep <- findDeployment(app)\n    Message\n      This directory has been previously deployed in multiple places.\n      Which deployment do you want to use?\n      1: test1 (server: example.com / username: ron): <https://example.com/ron/123>\n      2: test2 (server: example.com / username: ron): <https://example.com/ron/123>\n      Selection: 2\n\n"
  },
  {
    "path": "tests/testthat/_snaps/deployments.md",
    "content": "# addToDeploymentHistory() adds needed new lines\n\n    Code\n      addToDeploymentHistory(\"path\", list(x = 1))\n      writeLines(readLines(deploymentHistoryPath()))\n    Output\n      x: 1\n      appPath: path\n    Code\n      addToDeploymentHistory(\"path\", list(x = 2))\n      writeLines(readLines(deploymentHistoryPath()))\n    Output\n      x: 2\n      appPath: path\n      \n      x: 1\n      appPath: path\n\n# addToDeploymentHistory() caps records at rsconnect.max.history.records\n\n    Code\n      for (i in 1:5) {\n        addToDeploymentHistory(\"path\", list(x = i))\n      }\n      writeLines(readLines(deploymentHistoryPath()))\n    Output\n      x: 5\n      appPath: path\n      \n      x: 4\n      appPath: path\n      \n      x: 3\n      appPath: path\n\n# addToDeploymentHistory() preserves only the new record when max_records = 1L\n\n    Code\n      addToDeploymentHistory(\"path\", list(x = 3))\n      writeLines(readLines(deploymentHistoryPath()))\n    Output\n      x: 3\n      appPath: path\n\n# addToDeploymentHistory() restarts when history exceeds rsconnect.max.history.bytes\n\n    Code\n      addToDeploymentHistory(\"path\", list(x = 3))\n      writeLines(readLines(deploymentHistoryPath()))\n    Output\n      x: 3\n      appPath: path\n\n"
  },
  {
    "path": "tests/testthat/_snaps/http-libcurl.md",
    "content": "# can trace JSON\n\n    Code\n      . <- POST_JSON(service, list(), \"\", list(a = 1, b = 2))\n    Output\n      << {\n      <<   \"a\": 1,\n      <<   \"b\": 2\n      << }\n      >> {\"a\":[1],\"b\":[2]}\n\n"
  },
  {
    "path": "tests/testthat/_snaps/http.md",
    "content": "# authHeaders() picks correct method based on supplied fields\n\n    Code\n      str(authHeaders(list(secret = openssl::base64_encode(\"123\")), url, \"GET\"))\n    Output\n      List of 3\n       $ Date              : chr \"Thu, 09 Mar 2023 14:29:00 GMT\"\n       $ X-Auth-Signature  : chr \"ZmQwNjkxNGVmZmZiN2FjNzkzZTkwYzE4OTg1Y2M5NTIxNGZjNTcxY2I5M2RhYzFiNWIxODY5NjFjODMzYjE3ZA==; version=1\"\n       $ X-Content-Checksum: chr \"d41d8cd98f00b204e9800998ecf8427e\"\n    Code\n      str(authHeaders(list(private_key = key), url, \"GET\"))\n    Output\n      List of 3\n       $ Date              : chr \"Thu, 09 Mar 2023 14:29:00 GMT\"\n       $ X-Auth-Signature  : chr \"mk4e1sdK0Gy9Uex2nJMtkntdT/boQWRakSRB6iYw9hmP2zMHQjvynY+Kc5hqbGAK7tbzG52fC+5MQSOUapNKBF6GNnVe1cp2jFq4pmhEL2yhlkB\"| __truncated__\n       $ X-Content-Checksum: chr \"1B2M2Y8AsgTpgAmY7PhCfg==\"\n\n# includes body in error if available\n\n    Code\n      handleResponse(resp_text)\n    Condition\n      Error:\n      ! <http://example.com/error> failed with HTTP status 400\n      Failed\n    Code\n      handleResponse(resp_json)\n    Condition\n      Error:\n      ! <http://example.com/error> failed with HTTP status 400\n      failed\n    Code\n      handleResponse(resp_html)\n    Condition\n      Error:\n      ! <http://example.com/error> failed with HTTP status 400\n      Failed\n\n# but still gives got error if no body\n\n    Code\n      handleResponse(resp_text)\n    Condition\n      Error:\n      ! <http://example.com/error> failed with HTTP status 400\n    Code\n      handleResponse(resp_json)\n    Condition\n      Error:\n      ! <http://example.com/error> failed with HTTP status 400\n      Unexpected json response:\n    Code\n      handleResponse(resp_html)\n    Condition\n      Error:\n      ! <http://example.com/error> failed with HTTP status 400\n\n# errors contain method\n\n    Code\n      GET(service, list(), path = \"status/404\")\n    Condition\n      Error in `GET()`:\n      ! <http://127.0.0.1:{port}/status/404> failed with HTTP status 404\n    Code\n      POST(service, list(), path = \"status/403\")\n    Condition\n      Error in `POST()`:\n      ! <http://127.0.0.1:{port}/status/403> failed with HTTP status 403\n\n"
  },
  {
    "path": "tests/testthat/_snaps/ide.md",
    "content": "# getAppById() fails where expected\n\n    Code\n      getAppById(\"123\", \"susan\", \"unknown\", \"unknown.com\")\n    Condition\n      Error in `getAppById()`:\n      ! Can't find server with url \"unknown.com\".\n    Code\n      getAppById(\"123\", \"robert\", \"unknown\", \"https://example.com\")\n    Condition\n      Error in `getAppById()`:\n      ! Can't find account \"robert\" on server \"example.com\".\n\n# registerUserToken registers a user token\n\n    Code\n      accountInfo(\"the-account\")\n    Output\n      $name\n      [1] \"the-account\"\n      \n      $server\n      [1] \"the-server\"\n      \n      $accountId\n      [1] \"42\"\n      \n      $token\n      [1] \"the-token\"\n      \n      $private_key\n      [1] \"the-pr... (redacted)\"\n      \n      $username\n      [1] \"the-account\"\n      \n\n"
  },
  {
    "path": "tests/testthat/_snaps/lint.md",
    "content": "# lints give have useful print method\n\n    Code\n      lint(test_path(\"test-rmd-bad-case\"))\n    Output\n      ---------\n      index.Rmd\n      ---------\n      The following lines contain paths to files not matching in case sensitivity:\n      29: ![](img/rstudio.svg)    ['img/rstudio.svg' -> 'img/RStudio.svg']\n      \n      Filepaths are case-sensitive on deployment server.\n    Code\n      lint(test_path(\"shinyapp-appR\"))\n    Output\n      No problems found\n\n"
  },
  {
    "path": "tests/testthat/_snaps/linters.md",
    "content": "# linter warns about absolute paths and relative paths\n\n    Code\n      result\n    Output\n      ---------------------\n      ShinyPresentation.Rmd\n      ---------------------\n      The following lines contain absolute paths:\n      15: Here's some internal help: [Helpful Link](/Users/MrBurns/)\n      \n      --------\n      server.R\n      --------\n      The following lines contain absolute paths:\n      12:     otherFile <- read.table(\"~/.rsconnect-tests/local-file.txt\")\n      \n      The following lines contain paths to files not matching in case sensitivity:\n      28:     file <- read.csv(\"data/college.txt\") ## bad    ['data/college.txt' -> 'data/College.txt']\n      \n      Paths should be to files within the project directory.\n      Filepaths are case-sensitive on deployment server.\n\n"
  },
  {
    "path": "tests/testthat/_snaps/secret.md",
    "content": "# print and str obfuscate output\n\n    Code\n      x <- secret(\"THIS IS MY PASSWORD: foo\")\n      x\n    Output\n      [1] \"THIS I... (redacted)\"\n    Code\n      str(x)\n    Output\n       THIS I... (redacted)\n\n"
  },
  {
    "path": "tests/testthat/_snaps/servers.md",
    "content": "# servers() redacts the certificate\n\n    Code\n      servers()\n    Output\n                       name                                url          certificate\n      1         cert_test_a            https://localhost:4567/ -----B... (redacted)\n      2        shinyapps.io        https://api.shinyapps.io/v1 Amazon... (redacted)\n      3 connect.posit.cloud https://api.connect.posit.cloud/v1 -----B... (redacted)\n\n# serverInfo() redacts the certificate\n\n    Code\n      str(serverInfo(\"shinyapps.io\"))\n    Output\n      List of 3\n       $ name       : chr \"shinyapps.io\"\n       $ url        : chr \"https://api.shinyapps.io/v1\"\n       $ certificate: Amazon... (redacted)\n\n---\n\n    Code\n      str(serverInfo(\"connect.posit.cloud\"))\n    Output\n      List of 3\n       $ name       : chr \"connect.posit.cloud\"\n       $ url        : chr \"https://api.connect.posit.cloud/v1\"\n       $ certificate: -----B... (redacted)\n\n# serverInfo() errors if server not present\n\n    Code\n      serverInfo(\"foo\")\n    Condition\n      Error in `findServer()`:\n      ! Can't find `server` with name \"foo\".\n      i Known servers are \"shinyapps.io\" and \"connect.posit.cloud\".\n\n# addServer() errors if url not a connect server\n\n    Code\n      addServer(url)\n    Condition\n      Error in `addServer()`:\n      ! `url` does not appear to be a Posit Connect server.\n\n# addServer() and addServerCertificate() inform about their actions\n\n    Code\n      addServer(\"https://example.com\", validate = FALSE)\n    Message\n      Server 'example.com' added successfully: https://example.com\n    Code\n      addServerCertificate(\"example.com\", certificate = cert)\n    Message\n      Certificate added to server 'example.com'\n\n# certificates can't be attached to plain http servers\n\n    Code\n      addServerCertificate(\"test\", cert)\n    Condition\n      Error in `addServerCertificate()`:\n      ! Certificates may only be attached to servers that use the HTTPS protocol.\n      i Specify an HTTPS URL for the server, or omit the certificate.\n\n# findServer() errors if no servers\n\n    Code\n      findServer()\n    Condition\n      Error in `findServer()`:\n      ! No local servers have been registered\n\n# findServer() errors/prompts of multiple servers present\n\n    Code\n      findServer()\n    Condition\n      Error in `findServer()`:\n      ! Multiple servers found.\n      i Use `server` to pick one of \"myserver\" and \"yourserver\".\n\n---\n\n    Code\n      out <- findServer()\n    Message\n      Multiple servers found.\n      Which one do you want to use?\n      1: myserver\n      2: yourserver\n      Selection: 2\n\n# findServer checks server name\n\n    Code\n      findServer(1)\n    Condition\n      Error:\n      ! `server` must be a single string, not the number 1.\n    Code\n      findServer(\"foo\")\n    Condition\n      Error in `findServer()`:\n      ! Can't find `server` with name \"foo\".\n      i Known servers are \"shinyapps.io\" and \"connect.posit.cloud\".\n\n"
  },
  {
    "path": "tests/testthat/_snaps/writeManifest.md",
    "content": "# Deploying a Quarto project without Quarto is an error\n\n    Code\n      makeManifest(appDir)\n    Condition\n      Error in `quartoInspect()`:\n      ! `quarto` not found.\n      i Check that it is installed and available on your `PATH`.\n\n"
  },
  {
    "path": "tests/testthat/certs/example.com.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEMzCCApugAwIBAgIQdirX302cRVUmWALMQvTOWTANBgkqhkiG9w0BAQsFADB7\nMR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExKDAmBgNVBAsMH2Fyb25A\nbWlubWVpLmxvY2FsIChBcm9uIEF0a2lucykxLzAtBgNVBAMMJm1rY2VydCBhcm9u\nQG1pbm1laS5sb2NhbCAoQXJvbiBBdGtpbnMpMB4XDTI1MDYxMzE1NTQzMFoXDTI3\nMDkxMzE1NTQzMFowTjEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRp\nZmljYXRlMSMwIQYDVQQLDBphcm9uQG1hY3Jvc3MgKEFyb24gQXRraW5zKTCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALrJghnx6PCOIdIsD8sGqfx+5T7L\nDySPlXMX3RIeYEVyj1hCja48Cl/bwMbb/w8MnTJCbSeRqm2vkmK4AOZcFE/6OxOj\n/1/xtZKvLkCTTTSH+AZWONIJ3tGKd48sOO6feAolBcSKVr+q3x675fa1QsJsiUqT\njl7aYS1DsAyd2zY0PX3N2vznnLLvye8bh8JL66IE82RKdP9m/PHthKBYE4pFeSIL\nYBHAO+q8qqeODQvPUhJPKtZBM9fWpYYVILN85zz7DvQgwAMcfthFXfo23HrDCpFB\nS001IPvjsLggSTcnurqgHYZx7QJqWiCO04Np3SNCtyCLAc4cfE9U/iVwGRkCAwEA\nAaNgMF4wDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1Ud\nIwQYMBaAFD1VjbWByB6KUTimanEvpztYpGkjMBYGA1UdEQQPMA2CC2V4YW1wbGUu\nY29tMA0GCSqGSIb3DQEBCwUAA4IBgQB1gQvPaP6gpFRUMp80P7KeYMzbDoaRBEWS\nPZxqY1T1kGWejCf1nVLi/I0UaOxfDE1CX9MM2bujqJwOOide/oZ9uPbP874gr58m\nx7kgt8lBmHfSAmADUQLd+steGCjBcucULDYmiiQ5/oc96BeuI5oS/Z2PhRnZehd8\n1X+IZ5em3lofJveEvu50AU7ZlDY3JAzeeV54kQWmaeBEipY2Zv7+t4wbVBB/NsVx\n/2oDw9Y2/Y/iM3ggXOyN38cu9s8Mk8H+/ff3lCt2GXDELl8Qax6OXRBv/IyHgsdE\ny9gYDPH0iCtgjt6mi0IY3Nr86UV3BOaav+gKypABVSjzeL5I8ujf1/dcUsukb/fL\nsQhsAoIXge8pKBvICdtpfEMDQMpvq2+ecnNU9qY1k/suOF90RQWTMTtsD+xYAw8y\nWOBeDZU3Arc9Mmpo/o3LnIv2fI4g0V1ubkRu4JdN5LAOR5Kq40RJv6cFCPq+HOcU\n5snF1aYf5/nBXd1druV+2nVYd7nMSlU=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/testthat/certs/invalid.crt",
    "content": "Do you remember still the falling stars\nthat like swift horses through the heavens raced\nand suddenly leaped across the hurdles\nof our wishes--do you recall? And we\ndid make so many! For there were countless numbers\nof stars: each time we looked above we were\nastounded by the swiftness of their daring play,\nwhile in our hearts we felt safe and secure\nwatching these brilliant bodies disintegrate,\nknowing somehow we had survived their fall.\n\nRainer Maria Rilke\n"
  },
  {
    "path": "tests/testthat/certs/localhost.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEMjCCApqgAwIBAgIRAPKPR6q/usdid1xZCt4KDTswDQYJKoZIhvcNAQELBQAw\nezEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSgwJgYDVQQLDB9hcm9u\nQG1pbm1laS5sb2NhbCAoQXJvbiBBdGtpbnMpMS8wLQYDVQQDDCZta2NlcnQgYXJv\nbkBtaW5tZWkubG9jYWwgKEFyb24gQXRraW5zKTAeFw0yNTA2MTMxNTU0MjBaFw0y\nNzA5MTMxNTU0MjBaME4xJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0\naWZpY2F0ZTEjMCEGA1UECwwaYXJvbkBtYWNyb3NzIChBcm9uIEF0a2lucykwggEi\nMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmwb+mIjNzPDYUEQEoKUqY8tbh\nmIonWeLXNGW67EwW2PBa2JUIhFJlw37dBQaSz9VKdxe3+N1JWclM78L9oYbqWYKy\nY2GRoUiDJj2YBy5KrsnUITbvTi6bNzw/Fsilj06UpaQcfN7cThuRV8G9btcaawKc\n1Qhkrxu21QbDSgdBH2O/rbWgH2ng54dgv0a96ZnpHLkXgv7orAkBM2KR29CS1++U\nf1eSD+ZpUfgW8o4HDRpBiwisNvPiz/ZaAtVxQ/L5AkFx71EOFcYxW+DTqCzw01Xi\ndR9WQZ0luNW+KFVcCGJLFxdvc73wSc8wJ8xSpI47TbBzy+tlFNUgZ/txH57tAgMB\nAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNV\nHSMEGDAWgBQ9VY21gcgeilE4pmpxL6c7WKRpIzAUBgNVHREEDTALgglsb2NhbGhv\nc3QwDQYJKoZIhvcNAQELBQADggGBAActgJvtVJRFLZ7icPNP+kt5xd+o9LCXAtLu\n+xK4FoRSPebTTe7p3wWudd55R+GnvjxvsIhhBVyezOXSCBzshWLQ4lMEqmfy6viy\nbQoRGESapmb4Kpe980N6BK8k5dgP4zb3+DLvwHIw8DUGVHQp7FO3MkIsGxlzs5zv\nTArLP++QGvDFYPRmVUFilCrNvNs3cEP44zy5ujJ93wcoEU9D2Lv7/jwWXPJsn2Cb\n6xuPRfKTppm5e89wpZmtq3PNalt1twtaTyHTaLcjkyqS7D8dlZbFF5fpPC1RSvtm\n+sG8AQdaWuXH9I9TeqLv6z42AAiXN0y8qUl8zgpnPXsIPP+Opnc/ktPufPOiVwy2\nwrFETvv64mpI8Tu28RtPw6pzFkIzl+tFo34FXwBYqzjsBuO29GYXaGaVcLRQsJpF\ndoF7IflhsBtqOVOuMRuOGGEi2WB4qBtal/rRPEIaBP1GLAswCpnl09MfHedQv6g4\nSYhsW+/ijqdHBhL1Wa7Ed78J7ryYYA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/testthat/certs/sample.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDszCCApugAwIBAgIJAJhKUgOoHiBhMA0GCSqGSIb3DQEBCwUAMIGAMQswCQYD\nVQQGEwJVUzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1JlZG1vbmQxEDAOBgNVBAoM\nB1JTdHVkaW8xGzAZBgNVBAMMEnJzdHVkaW8tbWVnYS5sb2NhbDEjMCEGCSqGSIb3\nDQEJARYUam9uYXRoYW5AcnN0dWRpby5jb20wHhcNMTcwOTAxMjIxNTI5WhcNMjAw\nNTI3MjIxNTI5WjBVMQswCQYDVQQGEwJVUzENMAsGA1UECAwETm9uZTELMAkGA1UE\nBwwCTkIxDTALBgNVBAoMBE5vbmUxGzAZBgNVBAMMEnJzdHVkaW8tbWVnYS5sb2Nh\nbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANq8Hgk1vyt7N8mL0qpc\n8wVrVavFNo23G887/SDJ/OIQljd3PLY1zJ+KAIZjZ1DNWG3DCfZv3ghser3gi5x/\nTTLVI2AMrxYafV9KLOic35ngH6Aj6fgXZC0U0uaqlx5cdd+2SFIgs2l2IZMWXM8o\nYLcdes+L5yOfJPlKkASz2Yh9aBCZR6Xf1Bg2M1brwAeMtBB+dXcnXgNAHD0u+dOS\n3PMlUugjRPH4b0gmMVnSjLANGGVbJAJiIq2k0ZblgixjiAGpampTTe+ml9KQL38C\nHWiJkSTuC6ipZDyjk9BhJTz2fH3wR0yJsk/J3o8hm5u0WBlfttv/VMtsWlhBAc6A\n4HkCAwEAAaNaMFgwHwYDVR0jBBgwFoAU21Bn94udZnSVWlZHji9jfc33Cb0wCQYD\nVR0TBAIwADALBgNVHQ8EBAMCBPAwHQYDVR0RBBYwFIIScnN0dWRpby1tZWdhLmxv\nY2FsMA0GCSqGSIb3DQEBCwUAA4IBAQCIbnzfsTL80Ls8LmiSuZ6WEG2+9fgWxXYq\nVmOq+y0LJboe7Bf86FsbI8KNDJ3SEU0XFDDNio/txRoNomrSX2HKOUwoc9z8jGNV\nSWusvlOaaMbH0+Vrid1OMqy8Nk3yyZdhc3cAtN6juG//T/9c8XWA68qFq+W6TIUp\nqFpNsfcyN0NjP9pu4ANQzt5vTpVaqkmxHr1d53gAw89hHsBrgMLDpSeQRAnQNt6G\n0mcCAwBfEqTK9lDzGiS8vnnD2ovFsQllRyJbzWSttQ7NvTa/nvCs1GfTTOK02K9G\nqXn256VmSEjzZgWrKB87Md1/ySOUxi5M8VGoVBRJ7mfuad2/h4S1\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/testthat/certs/two-cas.crt",
    "content": "Amazon Root CA 1\n================\n-----BEGIN CERTIFICATE-----\nMIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\nADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\nb24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\nMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\nb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\nca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\nIFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\nVOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\njgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\nAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\nA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\nU5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\nN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\no/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\nrqXRfboQnoZsG4q5WTP468SQvvG5\n-----END CERTIFICATE-----\n\nAmazon Root CA 2\n================\n-----BEGIN CERTIFICATE-----\nMIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF\nADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\nb24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL\nMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\nb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK\ngXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ\nW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg\n1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K\n8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r\n2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me\nz/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR\n8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj\nmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz\n7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6\n+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI\n0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB\nAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm\nUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2\nLIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY\n+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS\nk5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl\n7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm\nbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl\nurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+\nfUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63\nn749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE\n76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H\n9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT\n4PsJYGw=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/testthat/helper-content.R",
    "content": "# A basic Quarto website that uses Python.\nquarto_website_py_files <- list(\n  \"_quarto.yml\" = c(\n    \"project:\",\n    \"  type: website\",\n    \"\",\n    \"website:\",\n    \"  title: quarto-website-py\",\n    \"  navbar:\",\n    \"    background: primary\",\n    \"    left:\",\n    \"      - href: index.qmd\",\n    \"        text: Home\",\n    \"      - about.qmd\",\n    \"\",\n    \"format:\",\n    \"  html:\",\n    \"    theme: cosmo\",\n    \"    css: styles.css\",\n    \"\",\n    \"editor: visual\"\n  ),\n  index.qmd = c(\n    \"---\",\n    \"title: quarto-website-py\",\n    \"jupyter: python3\",\n    \"---\",\n    \"\",\n    \"This is a Quarto website.\",\n    \"\",\n    \"To learn more about Quarto websites visit <https://quarto.org/docs/websites>.\",\n    \"\",\n    \"```{python}\",\n    \"1 + 1\",\n    \"```\"\n  ),\n  requirements.txt = \"jupyter\",\n  styles.css = \"/* css styles */\"\n)\n\n# A basic Quarto website that uses Python and has a .python-version file.\nquarto_website_py_python_version_files <- utils::modifyList(\n  quarto_website_py_files,\n  list(\n    \".python-version\" = \"3.8\",\n    about.qmd = c(\n      \"---\",\n      \"title: About\",\n      \"jupyter: python3\",\n      \"---\",\n      \"\",\n      \"About this site\",\n      \"\",\n      \"```{python}\",\n      \"1 + 1\",\n      \"```\"\n    )\n  )\n)\n\n# A basic Quarto website that uses Python and has a setup.cfg file.\nquarto_website_py_setup_cfg_files <- utils::modifyList(\n  quarto_website_py_files,\n  list(\n    setup.cfg = c(\n      \"[metadata]\",\n      \"name = a-quarto-project\",\n      \"version = 0.1.0\",\n      \"description = Add your description here\",\n      \"\",\n      \"[options]\",\n      \"python_requires = >=3.9\"\n    )\n  )\n)\n\nquarto_website_r_files <- list(\n  \"_quarto.yml\" = c(\n    \"project:\",\n    \"  type: website\",\n    \"\",\n    \"website:\",\n    \"  title: quarto-website-r\",\n    \"  navbar:\",\n    \"    background: primary\",\n    \"    left:\",\n    \"      - href: index.qmd\",\n    \"        text: Home\",\n    \"      - about.qmd\",\n    \"\",\n    \"format:\",\n    \"  html:\",\n    \"    theme: cosmo\",\n    \"    css: styles.css\",\n    \"\",\n    \"editor: visual\"\n  ),\n  about.qmd = c(\n    \"---\",\n    \"title: About\",\n    \"---\",\n    \"\",\n    \"About this site\",\n    \"\",\n    \"```{r}\",\n    \"1 + 1\",\n    \"```    \"\n  ),\n  index.qmd = c(\n    \"---\",\n    \"title: quarto-website-r\",\n    \"---\",\n    \"\",\n    \"This is a Quarto website.\",\n    \"\",\n    \"To learn more about Quarto websites visit <https://quarto.org/docs/websites>.\",\n    \"\",\n    \"```{r}\",\n    \"1 + 1\",\n    \"```\"\n  ),\n  styles.css = \"/* css styles */\"\n)\n\nquarto_website_r_py_files <- list(\n  \"_quarto.yml\" = c(\n    \"project:\",\n    \"  type: website\",\n    \"\",\n    \"website:\",\n    \"  title: quarto-website-r-py\",\n    \"  navbar:\",\n    \"    background: primary\",\n    \"    left:\",\n    \"      - href: index.qmd\",\n    \"        text: Home\",\n    \"      - about.qmd\",\n    \"\",\n    \"format:\",\n    \"  html:\",\n    \"    theme: cosmo\",\n    \"    css: styles.css\",\n    \"\",\n    \"editor: visual\"\n  ),\n  about.qmd = c(\n    \"---\",\n    \"title: About\",\n    \"---\",\n    \"\",\n    \"About this site\",\n    \"\",\n    \"```{r}\",\n    \"# This is an R chunk\",\n    \"1 + 1\",\n    \"```\",\n    \"\",\n    \"```{python}\",\n    \"# This is a Python chunk\",\n    \"1 + 1\",\n    \"```\"\n  ),\n  index.qmd = c(\n    \"---\",\n    \"title: quarto-website-r-py\",\n    \"---\",\n    \"\",\n    \"This is a Quarto website.\",\n    \"\",\n    \"To learn more about Quarto websites visit <https://quarto.org/docs/websites>.\",\n    \"\",\n    \"```{r}\",\n    \"# This is an R chunk\",\n    \"1 + 1\",\n    \"```\",\n    \"\",\n    \"```{python}\",\n    \"# This is a Python chunk\",\n    \"1 + 1\",\n    \"```\"\n  ),\n  pyproject.toml = c(\n    \"[project]\",\n    'name = \"a-quarto-project\"',\n    'version = \"0.1.0\"',\n    'description = \"Add your description here\"',\n    'requires-python = \">=3.11\"'\n  ),\n  requirements.txt = c(\n    \"# stub requirements.txt\",\n    \"# Its presence indicates the need for Python dependencies.\"\n  ),\n  styles.css = \"/* css styles */\"\n)\n\nquarto_project_r_shiny_files <- list(\n  \"_quarto.yml\" = c(\n    \"project:\",\n    '  title: \"quarto-proj-r-shiny\"'\n  ),\n\n  \"quarto-proj-r-shiny.qmd\" = c(\n    \"---\",\n    'title: \"quarto-proj-r-shiny\"',\n    \"resource_files:\",\n    \"- _quarto.yml\",\n    \"format: \",\n    \"  html:\",\n    \"    page-layout: custom\",\n    \"server: shiny\",\n    \"---\",\n    \"\",\n    \"```{r}\",\n    \"#| panel: sidebar\",\n    \"vars <- setdiff(names(iris), 'Species')\",\n    \"selectInput('xcol', 'X Variable', vars)\",\n    \"selectInput('ycol', 'Y Variable', vars, selected = vars[[2]])\",\n    \"numericInput('clusters', 'Cluster count', 3, min = 1, max = 9)\",\n    \"```\",\n    \"\",\n    \"```{r}\",\n    \"#| panel: fill\",\n    \"plotOutput('plot1')\",\n    \"```\",\n    \"\",\n    \"```{r}\",\n    \"#| context: server\",\n    \"selectedData <- reactive({\",\n    \"    iris[, c(input$xcol, input$ycol)]\",\n    \"  })\",\n    \"\",\n    \"clusters <- reactive({\",\n    \"  kmeans(selectedData(), input$clusters)\",\n    \"})\",\n    \"\",\n    \"output$plot1 <- renderPlot({\",\n    \"  palette(c('#E41A1C', '#377EB8', '#4DAF4A', '#984EA3',\",\n    \"    '#FF7F00', '#FFFF33', '#A65628', '#F781BF', '#999999'))\",\n    \"\",\n    \"  par(mar = c(5.1, 4.1, 0, 1))\",\n    \"  plot(selectedData(),\",\n    \"       col = clusters()$cluster,\",\n    \"       pch = 20, cex = 3)\",\n    \"  points(clusters()$centers, pch = 4, cex = 4, lwd = 4)\",\n    \"})\",\n    \"```\"\n  )\n)\n"
  },
  {
    "path": "tests/testthat/helper-http.R",
    "content": "cache <- new_environment()\n\nhttpbin_service <- function() {\n  app <- env_cache(\n    cache,\n    \"test_app\",\n    webfakes::new_app_process(webfakes::httpbin_app())\n  )\n\n  parseHttpUrl(app$url())\n}\n\nstrip_port <- function(service) {\n  function(x) gsub(service$port, \"{port}\", x)\n}\n\nlocal_cookie_store <- function(env = caller_env()) {\n  nms <- env_names(.cookieStore)\n  zaps <- rep_named(nms, list(zap()))\n\n  old <- env_bind(.cookieStore, !!!zaps)\n  withr::defer(env_bind(.cookieStore, !!!old), envir = env)\n}\n\nskip_on_http_failure <- function(code) {\n  tryCatch(\n    code,\n    rsconnect_http = function(cnd) {\n      testthat::skip(\"http request failed\")\n    }\n  )\n}\n\n# Service that 404s for server settings.\nservice_settings_404 <- function() {\n  app <- env_cache(\n    cache,\n    \"service_settings_404\",\n    {\n      json_app <- webfakes::new_app()\n      json_app$use(webfakes::mw_json())\n      json_app$get(\"/__api__/server_settings\", function(req, res) {\n        res$set_status(404L)$send_json(list(\n          error = jsonlite::unbox(\"not found\")\n        ))\n      })\n      app <- webfakes::new_app_process(json_app)\n    }\n  )\n  parseHttpUrl(app$url())\n}\n\n# Service that 200s for server settings.\nservice_settings_200 <- function() {\n  app <- env_cache(\n    cache,\n    \"service_settings_200\",\n    {\n      json_app <- webfakes::new_app()\n      json_app$use(webfakes::mw_json())\n      json_app$get(\"/__api__/server_settings\", function(req, res) {\n        res$set_status(200L)$send_json(list(data = jsonlite::unbox(\"ok\")))\n      })\n      app <- webfakes::new_app_process(json_app)\n    }\n  )\n  parseHttpUrl(app$url())\n}\n\n# Service that redirects for server settings.\nservice_redirect <- function(target) {\n  app <- env_cache(\n    cache,\n    \"service_redirect\",\n    {\n      json_app <- webfakes::new_app()\n      json_app$use(webfakes::mw_json())\n      json_app$get(\"/__api__/server_settings\", function(req, res) {\n        res$redirect(target)\n      })\n      app <- webfakes::new_app_process(json_app)\n    }\n  )\n  parseHttpUrl(app$url())\n}\n\n\n# Generic tests of various http methods -----------------------------------\n\ntest_http_GET <- function() {\n  service <- httpbin_service()\n\n  # Perform the request\n  resp <- GET(service, authInfo = NULL, path = \"get\")\n  expect_equal(attr(resp, \"httpContentType\"), \"application/json\")\n  expect_equal(resp$path, \"/get\")\n}\n\ntest_http_POST_JSON <- function() {\n  service <- httpbin_service()\n\n  body <- list(a = 1, b = 2, c = 3)\n  resp <- POST_JSON(service, authInfo = NULL, path = \"post\", json = body)\n  expect_equal(resp$json, body)\n}\n\ntest_http_POST_empty <- function() {\n  service <- httpbin_service()\n\n  resp <- POST(service, authInfo = NULL, path = \"post\")\n  expect_equal(resp$json, set_names(list()))\n}\n\ntest_http_POST_file <- function() {\n  service <- httpbin_service()\n\n  path <- withr::local_tempfile()\n  con <- file(path, \"wb\")\n  writeLines(c(\"1\", \"2\", \"3\"), con = con)\n  close(con)\n\n  resp <- POST(\n    service,\n    authInfo = NULL,\n    path = \"post\",\n    contentType = \"text/plain\",\n    file = path\n  )\n  expect_equal(resp$data, \"1\\n2\\n3\\n\")\n}\n\ntest_http_headers <- function() {\n  service <- httpbin_service()\n\n  resp <- GET(service, authInfo = list(apiKey = \"abc123\"), path = \"get\")\n  expect_equal(resp$headers$Authorization, \"Key abc123\")\n\n  resp <- POST(service, authInfo = list(apiKey = \"abc123\"), path = \"post\")\n  expect_equal(resp$headers$Authorization, \"Key abc123\")\n}\n"
  },
  {
    "path": "tests/testthat/helper-paths.R",
    "content": "pythonPathOrSkip <- function() {\n  skip_if_not_installed(\"reticulate\")\n\n  if (!reticulate::py_available(TRUE)) {\n    skip(\"python not found by reticulate\")\n  }\n  path <- reticulate::py_config()$python\n\n  pipMissing <- system2(path, \"-m pip help\", stdout = NULL, stderr = NULL)\n  if (pipMissing != 0) {\n    skip(\"pip is not installed\")\n  }\n\n  path\n}\n\n# quarto ------------------------------------------------------------------\n\nskip_if_no_quarto <- function() {\n  quarto <- quarto_path()\n  skip_if(is.null(quarto), \"quarto cli is not installed\")\n\n  invisible()\n}\n"
  },
  {
    "path": "tests/testthat/helper.R",
    "content": "showDcf <- function(df) {\n  write.dcf(df, stdout())\n  invisible()\n}\n\n# Create and use a directory as temporary replacement for R_USER_CONFIG_DIR to\n# avoid having tests overwrite the \"official\" configuration locations.\nlocal_temp_config <- function(env = caller_env()) {\n  path <- withr::local_tempdir(.local_envir = env)\n  withr::local_envvar(R_USER_CONFIG_DIR = path, .local_envir = env)\n}\n\nlocal_temp_app <- function(files = list(), env = caller_env()) {\n  dir <- withr::local_tempdir(.local_envir = env)\n\n  for (name in names(files)) {\n    content <- files[[name]]\n    hier <- dirname(name)\n    if (!hier == \".\") {\n      dir.create(file.path(dir, hier), recursive = TRUE)\n    }\n    if (length(content) > 0) {\n      writeLines(content, file.path(dir, name))\n    } else {\n      file.create(file.path(dir, name))\n    }\n  }\n\n  dir\n}\n\n\nlocal_shiny_bundle <- function(appName, appDir, appPrimaryDoc, python = NULL) {\n  appFiles <- bundleFiles(appDir)\n  appMetadata <- appMetadata(appDir, appFiles, appPrimaryDoc = appPrimaryDoc)\n\n  tarfile <- bundleApp(\n    appName,\n    appDir,\n    appFiles = appFiles,\n    appMetadata = appMetadata,\n    pythonConfig = pythonConfigurator(python),\n    quiet = TRUE\n  )\n  bundleTempDir <- tempfile()\n  utils::untar(tarfile, exdir = bundleTempDir)\n  unlink(tarfile)\n\n  defer(unlink(bundleTempDir, recursive = TRUE), env = caller_env())\n  bundleTempDir\n}\n\n\n# Servers and accounts ----------------------------------------------------\n\naddTestAccount <- function(\n  account = \"ron\",\n  server = \"example.com\",\n  userId = account\n) {\n  registerAccount(server, account, userId, apiKey = \"123\")\n  invisible()\n}\n\naddTestServer <- function(\n  name = NULL,\n  url = \"https://example.com\",\n  certificate = NULL\n) {\n  if (is.null(name)) {\n    serverUrl <- parseHttpUrl(url)\n    name <- serverUrl$host\n  }\n\n  registerServer(\n    url = url,\n    name = name,\n    certificate = certificate\n  )\n  invisible()\n}\naddTestDeployment <- function(\n  path,\n  appName = \"test\",\n  appTitle = \"\",\n  appId = \"123\",\n  account = \"ron\",\n  envVars = NULL,\n  username = account,\n  server = \"example.com\",\n  url = paste0(\"https://\", server, \"/\", username, \"/\", appId),\n  hostUrl = NULL,\n  version = deploymentRecordVersion,\n  metadata = list()\n) {\n  saveDeployment(\n    path,\n    createDeployment(\n      appName = appName,\n      appTitle = appTitle,\n      appId = appId,\n      envVars = envVars,\n      account = account,\n      username = username,\n      server = server,\n      version = version\n    ),\n    application = list(id = appId, url = url),\n    hostUrl = hostUrl,\n    metadata = metadata,\n    addToHistory = FALSE\n  )\n}\n\n# adding a top-level manifest field is allowed,\n# but requires coordination with the hosted team\n# to avoid upstream issues. In particular,\n# shinyapps.io enforces a strict manifest schema\n# that will need to be updated to accomodate the change\n#\n# this relates primarily to adding a new always present\n# top level field from within writeManifest\nexpect_known_manifest_fields <- function(manifest) {\n  known_fields <- c(\n    \"version\",\n    \"environment\",\n    \"platform\",\n    \"locale\",\n    \"python\",\n    \"nodejs\",\n    \"metadata\",\n    \"quarto\",\n    \"packages\",\n    \"files\",\n    \"users\"\n  )\n  expect_in(names(manifest), known_fields)\n}\n"
  },
  {
    "path": "tests/testthat/multibyte-characters/app.R",
    "content": "# 定义用户界面\nfluidPage(\n  # 标题\n  titlePanel(\"麻麻再也不用担心我的Shiny应用不能显示中文了\"),\n\n  # 侧边栏布局\n  sidebarLayout(\n    sidebarPanel(\n      selectInput(\n        \"dataset\",\n        \"请选一个数据：\",\n        choices = c(\"岩石\", \"pressure\", \"cars\")\n      ),\n\n      uiOutput(\"rockvars\"),\n\n      numericInput(\"obs\", \"查看多少行数据？\", 5),\n\n      checkboxInput(\"summary\", \"显示概要\", TRUE)\n    ),\n\n    # 展示一个HTML表格\n    mainPanel(\n      conditionalPanel(\"input.dataset === '岩石'\", plotOutput(\"rockplot\")),\n\n      verbatimTextOutput(\"summary这里也可以用中文\"),\n\n      tableOutput(\"view\")\n    )\n  )\n)\n"
  },
  {
    "path": "tests/testthat/packages/latin1package/DESCRIPTION",
    "content": "Package: latin1package\nType: Package\nTitle: Provides some funky characters.\nVersion: 0.1.0\nAuthor: Jens Frhling\nMaintainer: Jens Frhling <some.guy@i.made.him.up>\nDescription: a test package\nLicense: GPL-2\nEncoding: latin1\nLazyData: true\n"
  },
  {
    "path": "tests/testthat/packages/latin1package/NAMESPACE",
    "content": "exportPattern(\"^[[:alpha:]]+\")\n"
  },
  {
    "path": "tests/testthat/packages/latin1package/R/hello.R",
    "content": "hello <- function() {\n  print(\"Hello, world!\")\n}\n"
  },
  {
    "path": "tests/testthat/packages/latin1package/man/hello.Rd",
    "content": "\\name{hello}\n\\alias{hello}\n\\title{Hello, World!}\n\\usage{\nhello()\n}\n\\description{\nPrints 'Hello, world!'.\n}\n\\examples{\nhello()\n}\n"
  },
  {
    "path": "tests/testthat/packages/utf8package/DESCRIPTION",
    "content": "Package: utf8package\nType: Package\nTitle: Provides some funky characters.\nVersion: 0.1.0\nAuthor: Jens Fröhling\nMaintainer: Jens Fröhling <some.guy@i.made.him.up>\nDescription: a test package\nLicense: GPL-2\nDepends: R (>= 3.5.0)\nEncoding: UTF-8\nLazyData: true\n"
  },
  {
    "path": "tests/testthat/packages/utf8package/NAMESPACE",
    "content": "exportPattern(\"^[[:alpha:]]+\")\n"
  },
  {
    "path": "tests/testthat/packages/utf8package/R/hello.R",
    "content": "hello <- function() {\n  print(\"Hello, world!\")\n}\n"
  },
  {
    "path": "tests/testthat/packages/utf8package/man/hello.Rd",
    "content": "\\name{hello}\n\\alias{hello}\n\\title{Hello, World!}\n\\usage{\nhello()\n}\n\\description{\nPrints 'Hello, world!'.\n}\n\\examples{\nhello()\n}\n"
  },
  {
    "path": "tests/testthat/packages/windows1251package/DESCRIPTION",
    "content": "Package: windows1251package\nType: Package\nTitle: Provides some funky characters.\nVersion: 0.1.0\nAuthor:  \nMaintainer:   <some.guy@i.made.him.up>\nDescription: a test package\nLicense: GPL-2\nEncoding: Windows-1251\nLazyData: true\n"
  },
  {
    "path": "tests/testthat/packages/windows1251package/NAMESPACE",
    "content": "exportPattern(\"^[[:alpha:]]+\")\n"
  },
  {
    "path": "tests/testthat/packages/windows1251package/R/hello.R",
    "content": "hello <- function() {\n  print(\"Hello, world!\")\n}\n"
  },
  {
    "path": "tests/testthat/packages/windows1251package/man/hello.Rd",
    "content": "\\name{hello}\n\\alias{hello}\n\\title{Hello, World!}\n\\usage{\nhello()\n}\n\\description{\nPrints 'Hello, world!'.\n}\n\\examples{\nhello()\n}\n"
  },
  {
    "path": "tests/testthat/quarto-doc-long-chunk/index.qmd",
    "content": "---\ntitle: \"long chunk is long\"\nformat: html\n---\n\n```{r}\n#| eval: FALSE\n\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n### test_test_test_test_test_test_test_test_test_test\n```\n"
  },
  {
    "path": "tests/testthat/quarto-doc-none/quarto-doc-none.qmd",
    "content": "---\ntitle: \"quarto-doc-none\"\n---\n\n## Quarto\n\nQuarto enables you to weave together content and executable code into a finished document. To learn more about Quarto see <https://quarto.org>.\n"
  },
  {
    "path": "tests/testthat/renv-recommended/dependences.R",
    "content": "library(MASS)\n"
  },
  {
    "path": "tests/testthat/shiny-app-in-subdir/my-app/server.R",
    "content": "# Stub server.R\n#\n# Its presence (along with ui.R) indicates that this directory contains a\n# Shiny application.\n"
  },
  {
    "path": "tests/testthat/shiny-app-in-subdir/my-app/ui.r",
    "content": "# Stub ui.R\n#\n# Its presence (along with server.R) indicates that this directory contains a\n# Shiny application.\n"
  },
  {
    "path": "tests/testthat/shiny-rmds/non-shiny-rmd.Rmd",
    "content": "---\ntitle: Not a Shiny R Markdown Document\noutput: html_document\n---\n\n\n\n"
  },
  {
    "path": "tests/testthat/shiny-rmds/shiny-rmd-dashes.Rmd",
    "content": "---\ntitle: Shiny R Markdown with Dashes\noutput: html_document\nruntime: shiny\n---\n\n\n"
  },
  {
    "path": "tests/testthat/shiny-rmds/shiny-rmd-dots.Rmd",
    "content": "---\ntitle: Shiny R Markdown with Dots\noutput: html_document\nruntime: shiny\n...\n\nHello, world!\n\n\n"
  },
  {
    "path": "tests/testthat/shinyapp-appR/app.R",
    "content": "server <- function(input, output) {\n  output$distPlot <- renderPlot({\n    hist(rnorm(input$obs), col = \"darkgray\", border = \"white\")\n  })\n}\nui <- fluidPage(\n  sidebarLayout(\n    sidebarPanel(\n      sliderInput(\n        \"obs\",\n        \"Number of observations:\",\n        min = 10,\n        max = 500,\n        value = 100\n      )\n    ),\n    mainPanel(plotOutput(\"distPlot\"))\n  )\n)\nshinyApp(ui = ui, server = server)\n"
  },
  {
    "path": "tests/testthat/shinyapp-appR/rsconnect/colorado.posit.co/hadley/shinyapp-appR.dcf",
    "content": "name: shinyapp-appR\ntitle:\nusername: hadley\naccount: hadley\nserver: colorado.posit.co\nhostUrl: https://colorado.posit.co/rsc/__api__\nappId: 15717\nbundleId: 71906\nurl: https://colorado.posit.co/rsc/content/87507bba-4a69-4e19-becd-748b9406bad2/\n"
  },
  {
    "path": "tests/testthat/shinyapp-simple/server.R",
    "content": "library(shiny)\nshinyServer(function(input, output) {\n  output$distPlot <- renderPlot({\n    dist <- rnorm(input$obs)\n    hist(dist)\n  })\n  output$obs <- renderText({\n    paste(input$obs, \"\\n\", input$obs)\n  })\n})\n"
  },
  {
    "path": "tests/testthat/shinyapp-simple/shinyapp-simple.Rproj",
    "content": "Version: 1.0\n\nRestoreWorkspace: Default\nSaveWorkspace: Default\nAlwaysSaveHistory: Default\n\nEnableCodeIndexing: Yes\nUseSpacesForTab: Yes\nNumSpacesForTab: 2\nEncoding: UTF-8\n\nRnwWeave: Sweave\nLaTeX: pdfLaTeX\n"
  },
  {
    "path": "tests/testthat/shinyapp-simple/ui.R",
    "content": "library(shiny)\nshinyUI(pageWithSidebar(\n  headerPanel(\"Hello, Shiny!\"),\n  sidebarPanel(\n    sliderInput(\n      \"obs\",\n      \"Number of observations:\",\n      min = 1,\n      max = 1000,\n      value = 500\n    )\n  ),\n  mainPanel(\n    plotOutput(\"distPlot\")\n  )\n))\n"
  },
  {
    "path": "tests/testthat/shinyapp-singleR/single.R",
    "content": "server <- function(input, output) {\n  output$distPlot <- renderPlot({\n    hist(rnorm(input$obs), col = \"darkgray\", border = \"white\")\n  })\n}\nui <- fluidPage(\n  sidebarLayout(\n    sidebarPanel(\n      sliderInput(\n        \"obs\",\n        \"Number of observations:\",\n        min = 10,\n        max = 500,\n        value = 100\n      )\n    ),\n    mainPanel(plotOutput(\"distPlot\"))\n  )\n)\nshinyApp(ui = ui, server = server)\n"
  },
  {
    "path": "tests/testthat/shinyapp-with-absolute-paths/ShinyDocument.Rmd",
    "content": "---\ntitle: \"Shiny Document\"\nauthor: \"Phoenix Wright\"\ndate: \"July 16, 2014\"\noutput: html_document\nruntime: shiny\n---\n\nThis R Markdown document is made interactive using Shiny. Unlike the more traditional workflow of creating static reports, you can now create documents that allow your readers to change the assumptions underlying your analysis and see the results immediately. \n\nTo learn more, see [Interative Documents](http://rmarkdown.rstudio.com/authoring_shiny.html).\n\n## Inputs and Outputs\n\nYou can embed Shiny inputs and outputs in your document. Outputs are automatically updated whenever inputs change.  This demonstrates how a standard R plot can be made interactive by wrapping it in the Shiny `renderPlot` function. The `selectInput` and `sliderInput` functions create the input widgets used to drive the plot.\n\n```{r, echo=FALSE}\ninputPanel(\n  selectInput(\"n_breaks\", label = \"Number of bins:\",\n              choices = c(10, 20, 35, 50), selected = 20),\n\n  sliderInput(\"bw_adjust\", label = \"Bandwidth adjustment:\",\n              min = 0.2, max = 2, value = 1, step = 0.2)\n)\n\nrenderPlot({\n  hist(faithful$eruptions, probability = TRUE, breaks = as.numeric(input$n_breaks),\n       xlab = \"Duration (minutes)\", main = \"Geyser eruption duration\")\n\n  dens <- density(faithful$eruptions, adjust = input$bw_adjust)\n  lines(dens, col = \"blue\")\n})\n```\n\n## Embedded Application\n\nIt's also possible to embed an entire Shiny application within an R Markdown document using the `shinyAppDir` function. This example embeds a Shiny application located in another directory:\n\n```{r, echo=FALSE}\nshinyAppDir(\n  file.path(\"~/home/file.txt\"),\n  options = list(\n    width = \"100%\", height = 550\n  )\n)\n```\n\nNote the use of the `height` parameter to determine how much vertical space the embedded application should occupy.\n\nYou can also use the `shinyApp` function to define an application inline rather then in an external directory.\n\nIn all of R code chunks above the `echo = FALSE` attribute is used. This is to prevent the R code within the chunk from rendering in the document alongside the Shiny components.\n\n\n\n"
  },
  {
    "path": "tests/testthat/shinyapp-with-absolute-paths/ShinyPresentation.Rmd",
    "content": "---\ntitle: \"Shiny Presentation\"\nauthor: \"Miles Edgeworth\"\ndate: \"July 16, 2014\"\noutput: ioslides_presentation\nruntime: shiny\n---\n\n## Shiny Presentation\n\nThis R Markdown presentation is made interactive using Shiny. The viewers of the presentation can change the assumptions underlying what's presented and see the results immediately. \n\nTo learn more, see [Interative Documents](http://rmarkdown.rstudio.com/authoring_shiny.html).\n\nHere's some internal help: [Helpful Link](/Users/MrBurns/)\nAnd another: [Favourite Goats](../../goats.txt)\n\n## Slide with Interactive Plot\n\n```{r, echo=FALSE}\ninputPanel(\n  selectInput(\"n_breaks\", label = \"Number of bins:\",\n              choices = c(10, 20, 35, 50), selected = 20),\n\n  sliderInput(\"bw_adjust\", label = \"Bandwidth adjustment:\",\n              min = 0.2, max = 2, value = 1, step = 0.2)\n)\n\nrenderPlot({\n  hist(faithful$eruptions, probability = TRUE, breaks = as.numeric(input$n_breaks),\n       xlab = \"Duration (minutes)\", main = \"Geyser eruption duration\")\n\n  dens <- density(faithful$eruptions, adjust = input$bw_adjust)\n  lines(dens, col = \"blue\")\n})\n```\n\n## Slide with Bullets\n\n- Bullet 1\n- Bullet 2\n- Bullet 3\n\n## Slide with R Code and Output\n\n```{r}\nsummary(cars)\n```\n\n\n"
  },
  {
    "path": "tests/testthat/shinyapp-with-absolute-paths/data/College.txt",
    "content": "fake,42,data\n"
  },
  {
    "path": "tests/testthat/shinyapp-with-absolute-paths/server.R",
    "content": "# This is the server logic for a Shiny web application.\n# You can find out more about building applications with Shiny here:\n#\n# http://shiny.rstudio.com\n#\n\nlibrary(shiny)\n\nshinyServer(function(input, output) {\n  output$distPlot <- renderPlot({\n    # read a file on disk\n    otherFile <- read.table(\"~/.rsconnect-tests/local-file.txt\")\n    anotherFile <- readLines(\"../../foo.bar\")\n    serverFile <- \"\\\\\\\\server\\\\path\\\\to\\\\file\"\n    validWeblink <- \"//www.google.com/\"\n\n    # generate bins based on input$bins from ui.R\n    x <- faithful[, 2]\n    bins <- seq(min(x), max(x), length.out = input$bins + 1)\n\n    # don't warn on this line\n    text <- paste0(\"x:\", round(new_row$x, 2), \" y:\", round(new_row$y, 2))\n\n    # draw the histogram with the specified number of bins\n    hist(x, breaks = bins, col = \"darkgray\", border = \"white\")\n\n    ## read a csv file\n    file <- read.csv(\"data/college.txt\") ## bad\n    file <- read.csv(\"data/College.txt\") ## okay\n\n    ## don't warn about absolute paths that could be URL query paths\n    file <- paste(\"/applcations\")\n  })\n})\n"
  },
  {
    "path": "tests/testthat/shinyapp-with-absolute-paths/ui.R",
    "content": "# This is the user-interface definition of a Shiny web application.\n# You can find out more about building applications with Shiny here:\n#\n# http://shiny.rstudio.com\n#\n\nlibrary(shiny)\n\nshinyUI(fluidPage(\n  # Application title\n  titlePanel(\"Old Faithful Geyser Data\"),\n\n  # Some image\n  img(src = \"/images/icon.png\"),\n\n  # Sidebar with a slider input for number of bins\n  sidebarLayout(\n    sidebarPanel(\n      sliderInput(\"bins\", \"Number of bins:\", min = 1, max = 50, value = 30)\n    ),\n\n    # Show a plot of the generated distribution\n    mainPanel(\n      plotOutput(\"distPlot\")\n    )\n  )\n))\n"
  },
  {
    "path": "tests/testthat/shinyapp-with-browser/server.R",
    "content": "library(shiny)\nshinyServer(function(input, output) {\n  output$distPlot <- renderPlot({\n    # open Google. no reason, just do it\n    browseURL(\"https://www.google.com/\")\n    dist <- rnorm(input$obs)\n    hist(dist)\n    # stop to examine state\n    browser()\n  })\n  output$obs <- renderText({\n    paste(input$obs, \"\\n\", input$obs)\n  })\n})\n"
  },
  {
    "path": "tests/testthat/shinyapp-with-browser/ui.R",
    "content": "library(shiny)\nshinyUI(pageWithSidebar(\n  headerPanel(\"Hello, Shiny!\"),\n  sidebarPanel(\n    sliderInput(\n      \"obs\",\n      \"Number of observations:\",\n      min = 1,\n      max = 1000,\n      value = 500\n    )\n  ),\n  mainPanel(\n    plotOutput(\"distPlot\")\n  )\n))\n"
  },
  {
    "path": "tests/testthat/static-with-quarto-yaml/_quarto.yml",
    "content": "project:\n  title: \"slideshow\"\n"
  },
  {
    "path": "tests/testthat/static-with-quarto-yaml/slideshow.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\"><head>\n<script src=\"slideshow_files/libs/clipboard/clipboard.min.js\"></script>\n<script src=\"slideshow_files/libs/quarto-html/tabby.min.js\"></script>\n<script src=\"slideshow_files/libs/quarto-html/popper.min.js\"></script>\n<script src=\"slideshow_files/libs/quarto-html/tippy.umd.min.js\"></script>\n<link href=\"slideshow_files/libs/quarto-html/tippy.css\" rel=\"stylesheet\">\n<link href=\"slideshow_files/libs/quarto-html/quarto-html.min.css\" rel=\"stylesheet\">\n<link href=\"slideshow_files/libs/quarto-html/quarto-syntax-highlighting.css\" rel=\"stylesheet\" id=\"quarto-text-highlighting-styles\"><meta charset=\"utf-8\">\n  <meta name=\"generator\" content=\"quarto-0.9.583\">\n\n  <title>slideshow</title>\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui\">\n  <link rel=\"stylesheet\" href=\"slideshow_files/libs/revealjs/dist/reset.css\">\n  <link rel=\"stylesheet\" href=\"slideshow_files/libs/revealjs/dist/reveal.css\">\n  <style>\n    code{white-space: pre-wrap;}\n    span.smallcaps{font-variant: small-caps;}\n    span.underline{text-decoration: underline;}\n    div.column{display: inline-block; vertical-align: top; width: 50%;}\n    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}\n    ul.task-list{list-style: none;}\n  </style>\n  <link rel=\"stylesheet\" href=\"slideshow_files/libs/revealjs/dist/theme/quarto.css\" id=\"theme\">\n  <link href=\"slideshow_files/libs/revealjs/plugin/quarto-line-highlight/line-highlight.css\" rel=\"stylesheet\">\n  <link href=\"slideshow_files/libs/revealjs/plugin/reveal-menu/menu.css\" rel=\"stylesheet\">\n  <link href=\"slideshow_files/libs/revealjs/plugin/reveal-menu/quarto-menu.css\" rel=\"stylesheet\">\n  <link href=\"slideshow_files/libs/revealjs/plugin/quarto-support/footer.css\" rel=\"stylesheet\">\n  <style type=\"text/css\">\n\n  .callout {\n    margin-top: 1em;\n    margin-bottom: 1em;  \n    border-radius: .25rem;\n  }\n\n  .callout.callout-style-simple { \n    padding: 0em 0.5em;\n    border-left: solid #acacac .3rem;\n    border-right: solid 1px silver;\n    border-top: solid 1px silver;\n    border-bottom: solid 1px silver;\n    display: flex;\n  }\n\n  .callout.callout-style-default {\n    border-left: solid #acacac .3rem;\n    border-right: solid 1px silver;\n    border-top: solid 1px silver;\n    border-bottom: solid 1px silver;\n  }\n\n  .callout .callout-body-container {\n    flex-grow: 1;\n  }\n\n  .callout.callout-style-simple .callout-body {\n    font-size: 1rem;\n    font-weight: 400;\n  }\n\n  .callout.callout-style-default .callout-body {\n    font-size: 0.9rem;\n    font-weight: 400;\n  }\n\n  .callout.callout-captioned.callout-style-simple .callout-body {\n    margin-top: 0.2em;\n  }\n\n  .callout:not(.callout-captioned) .callout-body {\n      display: flex;\n  }\n\n  .callout:not(.no-icon).callout-captioned.callout-style-simple .callout-content {\n    padding-left: 1.6em;\n  }\n\n  .callout.callout-captioned .callout-header {\n    padding-top: 0.2em;\n    margin-bottom: -0.2em;\n  }\n\n  .callout.callout-captioned .callout-caption  p {\n    margin-top: 0.5em;\n    margin-bottom: 0.5em;\n  }\n    \n  .callout.callout-captioned.callout-style-simple .callout-content  p {\n    margin-top: 0;\n  }\n\n  .callout.callout-captioned.callout-style-default .callout-content  p {\n    margin-top: 0.7em;\n  }\n\n  .callout.callout-style-simple div.callout-caption {\n    border-bottom: none;\n    font-size: .9rem;\n    font-weight: 600;\n    opacity: 75%;\n  }\n\n  .callout.callout-style-default  div.callout-caption {\n    border-bottom: none;\n    font-weight: 600;\n    opacity: 85%;\n    font-size: 0.9rem;\n    padding-left: 0.5em;\n    padding-right: 0.5em;\n  }\n\n  .callout.callout-style-default div.callout-content {\n    padding-left: 0.5em;\n    padding-right: 0.5em;\n  }\n\n  .callout.callout-style-simple .callout-icon::before {\n    height: 1rem;\n    width: 1rem;\n    display: inline-block;\n    content: \"\";\n    background-repeat: no-repeat;\n    background-size: 1rem 1rem;\n  }\n\n  .callout.callout-style-default .callout-icon::before {\n    height: 0.9rem;\n    width: 0.9rem;\n    display: inline-block;\n    content: \"\";\n    background-repeat: no-repeat;\n    background-size: 0.9rem 0.9rem;\n  }\n\n  .callout-caption {\n    display: flex\n  }\n    \n  .callout-icon::before {\n    margin-top: 1rem;\n    padding-right: .5rem;\n  }\n\n  .callout.no-icon::before {\n    display: none !important;\n  }\n\n  .callout.callout-captioned .callout-body > .callout-content > :last-child {\n    margin-bottom: 0.5rem;\n  }\n\n  .callout.callout-captioned .callout-icon::before {\n    margin-top: .5rem;\n    padding-right: .5rem;\n  }\n\n  .callout:not(.callout-captioned) .callout-icon::before {\n    margin-top: 1rem;\n    padding-right: .5rem;\n  }\n\n  /* Callout Types */\n\n  div.callout-note {\n    border-left-color: #4582ec !important;\n  }\n\n  div.callout-note .callout-icon::before {\n    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAAEU0lEQVRYCcVXTWhcVRQ+586kSUMMxkyaElstCto2SIhitS5Ek8xUKV2poatCcVHtUlFQk8mbaaziwpWgglJwVaquitBOfhQXFlqlzSJpFSpIYyXNjBNiTCck7x2/8/LeNDOZxDuEkgOXe++553zfefee+/OYLOXFk3+1LLrRdiO81yNqZ6K9cG0P3MeFaMIQjXssE8Z1JzLO9ls20MBZX7oG8w9GxB0goaPrW5aNMp1yOZIa7Wv6o2ykpLtmAPs/vrG14Z+6d4jpbSKuhdcSyq9wGMPXjonwmESXrriLzFGOdDBLB8Y6MNYBu0dRokSygMA/mrun8MGFN3behm6VVAwg4WR3i6FvYK1T7MHo9BK7ydH+1uurECoouk5MPRyVSBrBHMYwVobG2aOXM07sWrn5qgB60rc6mcwIDJtQrnrEr44kmy+UO9r0u9O5/YbkS9juQckLed3DyW2XV/qWBBB3ptvI8EUY3I9p/67OW+g967TNr3Sotn3IuVlfMLVnsBwH4fsnebJvyGm5GeIUA3jljERmrv49SizPYuq+z7c2H/jlGC+Ghhupn/hcapqmcudB9jwJ/3jvnvu6vu5lVzF1fXyZuZZ7U8nRmVzytvT+H3kilYvH09mLWrQdwFSsFEsxFVs5fK7A0g8gMZjbif4ACpKbjv7gNGaD8bUrlk8x+KRflttr22JEMRUbTUwwDQScyzPgedQHZT0xnx7ujw2jfVfExwYHwOsDTjLdJ2ebmeQIlJ7neo41s/DrsL3kl+W2lWvAga0tR3zueGr6GL78M3ifH0rGXrBC2aAR8uYcIA5gwV8zIE8onoh8u0Fca/ciF7j1uOzEnqcIm59sEXoGc0+z6+H45V1CvAvHcD7THztu669cnp+L0okAeIc6zjbM/24LgGM1gZk7jnRu1aQWoU9sfUOuhrmtaPIO3YY1KLLWZaEO5TKUbMY5zx8W9UJ6elpLwKXbsaZ4EFl7B4bMtDv0iRipKoDQT2sNQI9b1utXFdYisi+wzZ/ri/1m7QfDgEuvgUUEIJPq3DhX/5DWNqIXDOweC2wvIR90Oq3lDpdMIgD2r0dXvGdsEW5H6x6HLRJYU7C69VefO1x8Gde1ZFSJLfWS1jbCnhtOPxmpfv2LXOA2Xk2tvnwKKPFuZ/oRmwBwqRQDcKNeVQkYcOjtWVBuM/JuYw5b6isojIkYxyYAFn5K7ZBF10fea52y8QltAg6jnMqNHFBmGkQ1j+U43HMi2xMar1Nv0zGsf1s8nUsmUtPOOrbFIR8bHFDMB5zL13Gmr/kGlCkUzedTzzmzsaJXhYawnA3UmARpiYj5ooJZiUoxFRtK3X6pgNPv+IZVPcnwbOl6f+aBaO1CNvPW9n9LmCp01nuSaTRF2YxHqZ8DYQT6WsXT+RD6eUztwYLZ8rM+rcPxamv1VQzFUkzFXvkiVrySGQgJNvXHJAxiU3/NwiC03rSf05VBaPtu/Z7/B8Yn/w7eguloAAAAAElFTkSuQmCC');\n  }\n\n  div.callout-note.callout-style-default .callout-caption {\n    background-color: #dae6fb\n  }\n\n  div.callout-important {\n    border-left-color: #d9534f !important;\n  }\n\n  div.callout-important .callout-icon::before {\n    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAAEKklEQVRYCcVXTWhcVRS+575MJym48A+hSRFr00ySRQhURRfd2HYjk2SSTokuBCkU2o0LoSKKraKIBTcuFCoidGFD08nkBzdREbpQ1EDNIv8qSGMFUboImMSZd4/f9zJv8ibJMC8xJQfO3HPPPef7zrvvvnvviIkpC9nsw0UttFunbUhpFzFtarSd6WJkStVMw5xyVqYTvkwfzuf/5FgtkVoB0729j1rjXwThS7Vio+Mo6DNnvLfahoZ+i/o32lULuJ3NNiz7q6+pyAUkJaFF6JwaM2lUJlV0MlnQn5aTRbEu0SEqHUa0A4AdiGuB1kFXRfVyg5d87+Dg4DL6m2TLAub60ilj7A1Ec4odSAc8X95sHh7+ZRPCFo6Fnp7HfU/fBng/hi10CjCnWnJjsxvDNxWw0NfV6Rv5GgP3I3jGWXumdTD/3cbEOP2ZbOZp69yniG3FQ9z1jD7bnBu9Fc2tKGC2q+uAJOQHBDRiZX1x36o7fWBs7J9ownbtO+n0/qWkvW7UPIfc37WgT6ZGR++EOJyeQDSb9UB+DZ1G6DdLDzyS+b/kBCYGsYgJbSQHuThGKRcw5xdeQf8YdNHsc6ePXrlSYMBuSIAFTGAtQo+VuALo4BX83N190NWZWbynBjhOHsmNfFWLeL6v+ynsA58zDvvAC8j5PkbOcXCMg2PZFk3q8MjI7WAG/Dp9AwP7jdGBOOQkAvlFUB+irtm16I1Zw9YBcpGTGXYmk3kQIC/Cds55l+iMI3jqhjAuaoe+am2Jw5GT3Nbz3CkE12NavmzN5+erJW7046n/CH1RO/RVa8lBLozXk9uqykkGAyRXLWlLv5jyp4RFsG5vGVzpDLnIjTWgnRy2Rr+tDKvRc7Y8AyZq10jj8DqXdnIRNtFZb+t/ZRtXcDiVnzpqx8mPcDWxgARUqx0W1QB9MeUZiNrV4qP+Ehc+BpNgATsTX8ozYKL2NtFYAHc84fG7ndxUPr+AR/iQSns7uSUufAymwDOb2+NjK27lEFocm/EE2WpyIy/Hi66MWuMKJn8RvxIcj87IM5Vh9663ziW36kR0HNenXuxmfaD8JC7tfKbrhFr7LiZCrMjrzTeGx+PmkosrkNzW94ObzwocJ7A1HokLolY+AvkTiD/q1H0cN48c5EL8Crkttsa/AXQVDmutfyku0E7jShx49XqV3MFK8IryDhYVbj7Sj2P2eBxwcXoe8T8idsKKPRcnZw1b+slFTubwUwhktrfnAt7J++jwQtLZcm3sr9LQrjRzz6cfMv9aLvgmnAGvpoaGLxM4mAEaLV7iAzQ3oU0IvD5x9ix3yF2RAAuYAOO2f7PEFWCXZ4C9Pb2UsgDeVnFSpbFK7/IWu7TPTvBqzbGdCHOJQSxiEjt6IyZmxQyEJHv6xyQsYk//moVFsN2zP6fRImjfq7/n/wFDguUQFNEwugAAAABJRU5ErkJggg==');\n  }\n\n  div.callout-important.callout-style-default .callout-caption {\n    background-color: #f7dddc\n  }\n\n  div.callout-warning {\n    border-left-color: #f0ad4e !important;\n  }\n\n  div.callout-warning .callout-icon::before {\n    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAAETklEQVRYCeVWW2gcVRg+58yaTUnizqbipZeX4uWhBEniBaoUX1Ioze52t7sRq6APio9V9MEaoWlVsFasRq0gltaAPuxms8lu0gcviE/FFOstVbSIxgcv6SU7EZqmdc7v9+9mJtNks51NTUH84ed889/PP+cmxP+d5FIbMJmNbpREu4WUkiTtCicKny0l1pIKmBzovF2S+hIJHX8iEu3hZJ5lNZGqyRrGSIQpq15AzF28jgpeY6yk6GVdrfFqdrD6Iw+QlB8g0YS2g7dyQmXM/IDhBhT0UCiRf59lfqmmDvzRt6kByV/m4JjtzuaujMUM2c5Z2d6JdKrRb3K2q6mA+oYVz8JnDdKPmmNthzkAk/lN63sYPgevrguc72aZX/L9C6x09GYyxBgCX4NlvyGUHOKELlm5rXeR1kchuChJt4SSwyddZRXgvwMGvYo4QSlk3/zkHD8UHxwVJA6zjZZqP8v8kK8OWLnIZtLyCAJagYC4rTGW/9Pqj92N/c+LUaAj27movwbi19tk/whRCIE7Q9vyI6yvRpftAKVTdUjOW40X3h5OXsKCdmFcx0xlLJoSuQngnrJe7Kcjm4OMq9FlC7CMmScQANuNvjfP3PjGXDBaUQmbp296S5L4DrpbrHN1T87ZVEZVCzg1FF0Ft+dKrlLukI+/c9ENo+TvlTDbYFvuKPtQ9+l052rXrgKoWkDAFnvh0wTOmYn8R5f4k/jN/fZiCM1tQx9jQQ4ANhqG4hiL0qIFTGViG9DKB7GYzgubnpofgYRwO+DFjh0Zin2m4b/97EDkXkc+f6xYAPX0KK2I/7fUQuwzuwo/L3AkcjugPNixC8cHf0FyPjWlItmLxWw4Ou9YsQCr5fijMGoD/zpdRy95HRysyXA74MWOnscpO4j2y3HAVisw85hX5+AFBRSHt4ShfLFkIMXTqyKFc46xdzQM6XbAi702a7sy04J0+feReMFKp5q9esYLCqAZYw/k14E/xcLLsFElaornTuJB0svMuJINy8xkIYuL+xPAlWRceH6+HX7THJ0djLUom46zREu7tTkxwmf/FdOZ/sh6Q8qvEAiHpm4PJ4a/doJe0gH1t+aHRgCzOvBvJedEK5OFE5jpm4AGP2a8Dxe3gGJ/pAutug9Gp6he92CsSsWBaEcxGx0FHytmIpuqGkOpldqNYQK8cSoXvd+xLxXADw0kf6UkJNFtdo5MOgaLjiQOQHcn+A6h5NuL2s0qsC2LOM75PcF3yr5STuBSAcGG+meA14K/CI21HcS4LBT6tv0QAh8Dr5l93AhZzG5ZJ4VxAqdZUEl9z7WJ4aN+svMvwHHL21UKTd1mqvChH7/Za5xzXBBKrUcB0TQ+Ulgkfbi/H/YT5EptrGzsEK7tR1B7ln9BBwckYfMiuSqklSznIuoIIOM42MQO+QnduCoFCI0bpkzjCjddHPN/F+2Yu+sd9bKNpVwHhbS3LluK/0zgfwD0xYI5dXuzlQAAAABJRU5ErkJggg==');\n  }\n\n  div.callout-warning.callout-style-default .callout-caption {\n    background-color: #fcefdc\n  }\n\n  div.callout-tip {\n    border-left-color: #02b875 !important;\n  }\n\n  div.callout-tip .callout-icon::before {\n    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADr0lEQVRYCe1XTWgTQRj9ZjZV8a9SPIkKgj8I1bMHsUWrqYLVg4Ue6v9BwZOxSYsIerFao7UiUryIqJcqgtpimhbBXoSCVxUFe9CTiogUrUp2Pt+3aUI2u5vdNh4dmMzOzHvvezuz8xNFM0mjnbXaNu1MvFWRXkXEyE6aYOYJpdW4IXuA4r0fo8qqSMDBU0v1HJUgVieAXxzCsdE/YJTdFcVIZQNMyhruOMJKXYFoLfIfIvVIMWdsrd+Rpd86ZmyzzjJmLStqRn0v8lzkb4rVIXvnpScOJuAn2ACC65FkPzEdEy4TPWRLJ2h7z4cArXzzaOdKlbOvKKX25Wl00jSnrwVxAg3o4dRxhO13RBSdNvH0xSARv3adTXbBdTf64IWO2vH0LT+cv4GR1DJt+DUItaQogeBX/chhbTBxEiZ6gftlDNXTrvT7co4ub5A6gp9HIcHvzTa46OS5fBeP87Qm0fQkr4FsYgVQ7Qg+ZayaDg9jhg1GkWj8RG6lkeSacrrHgDaxdoBiZPg+NXV/KifMuB6//JmYH4CntVEHy/keA6x4h4CU5oFy8GzrBS18cLJMXcljAKB6INjWsRcuZBWVaS3GDrqB7rdapVIeA+isQ57Eev9eCqzqOa81CY05VLd6SamW2wA2H3SiTbnbSxmzfp7WtKZkqy4mdyAlGx7ennghYf8voqp9cLSgKdqNfa6RdRsAAkPwRuJZNbpByn+RrJi1RXTwdi8RQF6ymDwGMAtZ6TVE+4uoKh+MYkcLsT0Hk8eAienbiGdjJHZTpmNjlbFJNKDVAp2fJlYju6IreQxQ08UJDNYdoLSl6AadO+fFuCQqVMB1NJwPm69T04Wv5WhfcWyfXQB+wXRs1pt+nCknRa0LVzSA/2B+a9+zQJadb7IyyV24YAxKp2Jqs3emZTuNnKxsah+uabKbMk7CbTgJx/zIgQYErIeTKRQ9yD9wxVof5YolPHqaWo7TD6tJlh7jQnK5z2n3+fGdggIOx2kaa2YI9QWarc5Ce1ipNWMKeSG4DysFF52KBmTNMmn5HqCFkwy34rDg05gDwgH3bBi+sgFhN/e8QvRn8kbamCOhgrZ9GJhFDgfcMHzFb6BAtjKpFhzTjwv1KCVuxHvCbsSiEz4CANnj84cwHdFXAbAOJ4LTSAawGWFn5tDhLMYz6nWeU2wJfIhmIJBefcd/A5FWQWGgrWzyORZ3Q6HuV+Jf0Bj+BTX69fm1zWgK7By1YTXchFDORywnfQ7GpzOo6S+qECrsx2ifVQAAAABJRU5ErkJggg==');\n  }\n\n  div.callout-tip.callout-style-default .callout-caption {\n    background-color: #ccf1e3\n  }\n\n  div.callout-caution {\n    border-left-color: #fd7e14 !important;\n  }\n\n  div.callout-caution .callout-icon::before {\n    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAACV0lEQVRYCdVWzWoUQRCuqp2ICBLJXgITZL1EfQDBW/bkzUMUD7klD+ATSHBEfAIfQO+iXsWDxJsHL96EHAwhgzlkg8nBg25XWb0zIb0zs9muYYWkoKeru+vn664fBqElyZNuyh167NXJ8Ut8McjbmEraKHkd7uAnAFku+VWdb3reSmRV8PKSLfZ0Gjn3a6Xlcq9YGb6tADjn+lUfTXtVmaZ1KwBIvFI11rRXlWlatwIAAv2asaa9mlB9wwygiDX26qaw1yYPzFXg2N1GgG0FMF8Oj+VIx7E/03lHx8UhvYyNZLN7BwSPgekXXLribw7w5/c8EF+DBK5idvDVYtEEwMeYefjjLAdEyQ3M9nfOkgnPTEkYU+sxMq0BxNR6jExrAI31H1rzvLEfRIdgcv1XEdj6QTQAS2wtstEALLG1yEZ3QhH6oDX7ExBSFEkFINXH98NTrme5IOaaA7kIfiu2L8A3qhH9zRbukdCqdsA98TdElyeMe5BI8Rs2xHRIsoTSSVFfCFCWGPn9XHb4cdobRIWABNf0add9jakDjQJpJ1bTXOJXnnRXHRf+dNL1ZV1MBRCXhMbaHqGI1JkKIL7+i8uffuP6wVQAzO7+qVEbF6NbS0LJureYcWXUUhH66nLR5rYmva+2tjRFtojkM2aD76HEGAD3tPtKM309FJg5j/K682ywcWJ3PASCcycH/22u+Bh7Aa0ehM2Fu4z0SAE81HF9RkB21c5bEn4Dzw+/qNOyXr3DCTQDMBOdhi4nAgiFDGCinIa2owCEChUwD8qzd03PG+qdW/4fDzjUMcE1ZpIAAAAASUVORK5CYII=');\n  }\n\n  div.callout-caution.callout-style-default .callout-caption {\n    background-color: #ffe5d0\n  }\n\n  </style>\n  <style type=\"text/css\">\n    .reveal div.sourceCode {\n      margin: 0;\n      overflow: auto;\n    }\n    .reveal div.hanging-indent {\n      margin-left: 1em;\n      text-indent: -1em;\n    }\n    .reveal .slide:not(.center) {\n      height: 100%;\n    }\n    .reveal .slide.scrollable {\n      overflow-y: auto;\n    }\n    .reveal .footnotes {\n      height: 100%;\n      overflow-y: auto;\n    }\n    .reveal .slide .absolute {\n      position: absolute;\n      display: block;\n    }\n    .reveal .footnotes ol {\n      counter-reset: ol;\n      list-style-type: none; \n      margin-left: 0;\n    }\n    .reveal .footnotes ol li:before {\n      counter-increment: ol;\n      content: counter(ol) \". \"; \n    }\n    .reveal .footnotes ol li > p:first-child {\n      display: inline-block;\n    }\n    .reveal .slide ul,\n    .reveal .slide ol {\n      margin-bottom: 0.5em;\n    }\n    .reveal .slide ul li,\n    .reveal .slide ol li {\n      margin-top: 0.4em;\n      margin-bottom: 0.2em;\n    }\n    .reveal .slide ul[role=\"tablist\"] li {\n      margin-bottom: 0;\n    }\n    .reveal .slide ul li > *:first-child,\n    .reveal .slide ol li > *:first-child {\n      margin-block-start: 0;\n    }\n    .reveal .slide ul li > *:last-child,\n    .reveal .slide ol li > *:last-child {\n      margin-block-end: 0;\n    }\n    .reveal .slide .columns:nth-child(3) {\n      margin-block-start: 0.8em;\n    }\n    .reveal blockquote {\n      box-shadow: none;\n    }\n    .reveal .tippy-content>* {\n      margin-top: 0.2em;\n      margin-bottom: 0.7em;\n    }\n    .reveal .tippy-content>*:last-child {\n      margin-bottom: 0.2em;\n    }\n    .reveal .slide > img.stretch.quarto-figure-center,\n    .reveal .slide > img.r-stretch.quarto-figure-center {\n      display: block;\n      margin-left: auto;\n      margin-right: auto; \n    }\n    .reveal .slide > img.stretch.quarto-figure-left,\n    .reveal .slide > img.r-stretch.quarto-figure-left  {\n      display: block;\n      margin-left: 0;\n      margin-right: auto; \n    }\n    .reveal .slide > img.stretch.quarto-figure-right,\n    .reveal .slide > img.r-stretch.quarto-figure-right  {\n      display: block;\n      margin-left: auto;\n      margin-right: 0; \n    }\n  </style>\n</head>\n<body>\n  <div class=\"reveal\">\n    <div class=\"slides\">\n\n<section id=\"title-slide\" class=\"center\">\n  <h1 class=\"title\">slideshow</h1>\n</section>\n\n<section id=\"quarto\" class=\"slide level2\">\n<h2>Quarto</h2>\n<p>Quarto enables you to weave together content and executable code into a finished presentation. To learn more about Quarto presentations see <a href=\"https://quarto.org/docs/presentations/\" class=\"uri\">https://quarto.org/docs/presentations/</a>.</p>\n</section>\n<section id=\"bullets\" class=\"slide level2\">\n<h2>Bullets</h2>\n<p>When you click the <strong>Render</strong> button a document will be generated that includes:</p>\n<ul>\n<li>Content authored with markdown</li>\n<li>Output from executable code</li>\n</ul>\n</section>\n<section id=\"code\" class=\"slide level2\">\n<h2>Code</h2>\n<p>When you click the <strong>Render</strong> button a presentation will be generated that includes both content and the output of embedded code. You can embed code like this:</p>\n<div class=\"cell\">\n<div class=\"cell-output cell-output-stdout\">\n<pre><code>[1] 2</code></pre>\n</div>\n</div>\n<div class=\"footer footer-default\">\n\n</div>\n</section>\n    </div>\n  </div>\n\n  <script>window.backupDefine = window.define; window.define = undefined;</script>\n  <script src=\"slideshow_files/libs/revealjs/dist/reveal.js\"></script>\n  <!-- reveal.js plugins -->\n  <script src=\"slideshow_files/libs/revealjs/plugin/quarto-line-highlight/line-highlight.js\"></script>\n  <script src=\"slideshow_files/libs/revealjs/plugin/pdf-export/pdfexport.js\"></script>\n  <script src=\"slideshow_files/libs/revealjs/plugin/reveal-menu/menu.js\"></script>\n  <script src=\"slideshow_files/libs/revealjs/plugin/reveal-menu/quarto-menu.js\"></script>\n  <script src=\"slideshow_files/libs/revealjs/plugin/quarto-support/support.js\"></script>\n  \n\n  <script src=\"slideshow_files/libs/revealjs/plugin/notes/notes.js\"></script>\n  <script src=\"slideshow_files/libs/revealjs/plugin/search/search.js\"></script>\n  <script src=\"slideshow_files/libs/revealjs/plugin/zoom/zoom.js\"></script>\n  <script src=\"slideshow_files/libs/revealjs/plugin/math/math.js\"></script>\n  <script>window.define = window.backupDefine; window.backupDefine = undefined;</script>\n\n  <script>\n\n      // Full list of configuration options available at:\n      // https://revealjs.com/config/\n      Reveal.initialize({\n'controlsAuto': true,\n'previewLinksAuto': false,\n'smaller': false,\n'pdfSeparateFragments': false,\n'autoAnimateEasing': \"ease\",\n'autoAnimateDuration': 1,\n'autoAnimateUnmatched': true,\n'menu': {\"side\":\"left\",\"useTextContentForMissingTitles\":true,\"markers\":false,\"loadIcons\":false,\"custom\":[{\"title\":\"Tools\",\"icon\":\"<i class=\\\"fas fa-gear\\\"></i>\",\"content\":\"<ul class=\\\"slide-menu-items\\\">\\n<li class=\\\"slide-tool-item active\\\" data-item=\\\"0\\\"><a href=\\\"#\\\" onclick=\\\"RevealMenuToolHandlers.fullscreen(event)\\\"><kbd>f</kbd> Fullscreen</a></li>\\n<li class=\\\"slide-tool-item\\\" data-item=\\\"1\\\"><a href=\\\"#\\\" onclick=\\\"RevealMenuToolHandlers.speakerMode(event)\\\"><kbd>s</kbd> Speaker View</a></li>\\n<li class=\\\"slide-tool-item\\\" data-item=\\\"2\\\"><a href=\\\"#\\\" onclick=\\\"RevealMenuToolHandlers.overview(event)\\\"><kbd>o</kbd> Slide Overview</a></li>\\n<li class=\\\"slide-tool-item\\\" data-item=\\\"3\\\"><a href=\\\"#\\\" onclick=\\\"RevealMenuToolHandlers.overview(event)\\\"><kbd>e</kbd> PDF Export Mode</a></li>\\n<li class=\\\"slide-tool-item\\\" data-item=\\\"4\\\"><a href=\\\"#\\\" onclick=\\\"RevealMenuToolHandlers.keyboardHelp(event)\\\"><kbd>?</kbd> Keyboard Help</a></li>\\n</ul>\"}],\"openButton\":true},\n'smaller': false,\n \n        // Display controls in the bottom right corner\n        controls: false,\n\n        // Help the user learn the controls by providing hints, for example by\n        // bouncing the down arrow when they first encounter a vertical slide\n        controlsTutorial: false,\n\n        // Determines where controls appear, \"edges\" or \"bottom-right\"\n        controlsLayout: 'edges',\n\n        // Visibility rule for backwards navigation arrows; \"faded\", \"hidden\"\n        // or \"visible\"\n        controlsBackArrows: 'faded',\n\n        // Display a presentation progress bar\n        progress: true,\n\n        // Display the page number of the current slide\n        slideNumber: false,\n\n        // 'all', 'print', or 'speaker'\n        showSlideNumber: 'all',\n\n        // Add the current slide number to the URL hash so that reloading the\n        // page/copying the URL will return you to the same slide\n        hash: true,\n\n        // Start with 1 for the hash rather than 0\n        hashOneBasedIndex: false,\n\n        // Flags if we should monitor the hash and change slides accordingly\n        respondToHashChanges: true,\n\n        // Push each slide change to the browser history\n        history: true,\n\n        // Enable keyboard shortcuts for navigation\n        keyboard: true,\n\n        // Enable the slide overview mode\n        overview: true,\n\n        // Disables the default reveal.js slide layout (scaling and centering)\n        // so that you can use custom CSS layout\n        disableLayout: false,\n\n        // Vertical centering of slides\n        center: false,\n\n        // Enables touch navigation on devices with touch input\n        touch: true,\n\n        // Loop the presentation\n        loop: false,\n\n        // Change the presentation direction to be RTL\n        rtl: false,\n\n        // see https://revealjs.com/vertical-slides/#navigation-mode\n        navigationMode: 'linear',\n\n        // Randomizes the order of slides each time the presentation loads\n        shuffle: false,\n\n        // Turns fragments on and off globally\n        fragments: true,\n\n        // Flags whether to include the current fragment in the URL,\n        // so that reloading brings you to the same fragment position\n        fragmentInURL: false,\n\n        // Flags if the presentation is running in an embedded mode,\n        // i.e. contained within a limited portion of the screen\n        embedded: false,\n\n        // Flags if we should show a help overlay when the questionmark\n        // key is pressed\n        help: true,\n\n        // Flags if it should be possible to pause the presentation (blackout)\n        pause: true,\n\n        // Flags if speaker notes should be visible to all viewers\n        showNotes: false,\n\n        // Global override for autoplaying embedded media (null/true/false)\n        autoPlayMedia: null,\n\n        // Global override for preloading lazy-loaded iframes (null/true/false)\n        preloadIframes: null,\n\n        // Number of milliseconds between automatically proceeding to the\n        // next slide, disabled when set to 0, this value can be overwritten\n        // by using a data-autoslide attribute on your slides\n        autoSlide: 0,\n\n        // Stop auto-sliding after user input\n        autoSlideStoppable: true,\n\n        // Use this method for navigation when auto-sliding\n        autoSlideMethod: null,\n\n        // Specify the average time in seconds that you think you will spend\n        // presenting each slide. This is used to show a pacing timer in the\n        // speaker view\n        defaultTiming: null,\n\n        // Enable slide navigation via mouse wheel\n        mouseWheel: false,\n\n        // The display mode that will be used to show slides\n        display: 'block',\n\n        // Hide cursor if inactive\n        hideInactiveCursor: true,\n\n        // Time before the cursor is hidden (in ms)\n        hideCursorTime: 5000,\n\n        // Opens links in an iframe preview overlay\n        previewLinks: false,\n\n        // Transition style (none/fade/slide/convex/concave/zoom)\n        transition: 'none',\n\n        // Transition speed (default/fast/slow)\n        transitionSpeed: 'default',\n\n        // Transition style for full page slide backgrounds\n        // (none/fade/slide/convex/concave/zoom)\n        backgroundTransition: 'none',\n\n        // Number of slides away from the current that are visible\n        viewDistance: 3,\n\n        // Number of slides away from the current that are visible on mobile\n        // devices. It is advisable to set this to a lower number than\n        // viewDistance in order to save resources.\n        mobileViewDistance: 2,\n\n        // The \"normal\" size of the presentation, aspect ratio will be preserved\n        // when the presentation is scaled to fit different resolutions. Can be\n        // specified using percentage units.\n        width: 1050,\n\n        height: 700,\n\n        // Factor of the display size that should remain empty around the content\n        margin: 0.1,\n\n        math: {\n          mathjax: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js',\n          config: 'TeX-AMS_HTML-full',\n          tex2jax: {\n            inlineMath: [['\\\\(','\\\\)']],\n            displayMath: [['\\\\[','\\\\]']],\n            balanceBraces: true,\n            processEscapes: false,\n            processRefs: true,\n            processEnvironments: true,\n            preview: 'TeX',\n            skipTags: ['script','noscript','style','textarea','pre','code'],\n            ignoreClass: 'tex2jax_ignore',\n            processClass: 'tex2jax_process'\n          },\n        },\n\n        // reveal.js plugins\n        plugins: [QuartoLineHighlight, PdfExport, RevealMenu, QuartoSupport,\n\n          RevealMath,\n          RevealNotes,\n          RevealSearch,\n          RevealZoom\n        ]\n      });\n    </script>\n    \n    <script>\n      // htmlwidgets need to know to resize themselves when slides are shown/hidden.\n      // Fire the \"slideenter\" event (handled by htmlwidgets.js) when the current\n      // slide changes (different for each slide format).\n      (function () {\n        function fireSlideChanged(previousSlide, currentSlide) {\n\n          // dispatch for htmlwidgets\n          const event = window.document.createEvent(\"Event\");\n          event.initEvent(\"slideenter\", true, true);\n          window.document.dispatchEvent(event);\n\n          // dispatch for shiny\n          if (window.jQuery) {\n            if (previousSlide) {\n              window.jQuery(previousSlide).trigger(\"hidden\");\n            }\n            if (currentSlide) {\n              window.jQuery(currentSlide).trigger(\"shown\");\n            }\n          }\n        }\n\n        // hookup for reveal\n        if (window.Reveal) {\n          window.Reveal.addEventListener(\"slidechanged\", function(event) {\n            fireSlideChanged(event.previousSlide, event.currentSlide);\n          });\n        }\n\n        // hookup for slidy\n        if (window.w3c_slidy) {\n          window.w3c_slidy.add_observer(function (slide_num) {\n            // slide_num starts at position 1\n            fireSlideChanged(null, w3c_slidy.slides[slide_num - 1]);\n          });\n        }\n\n      })();\n    </script>\n\n    <script id=\"quarto-html-after-body\" type=\"application/javascript\">\n    window.document.addEventListener(\"DOMContentLoaded\", function (event) {\n      const tabsets =  window.document.querySelectorAll(\".panel-tabset-tabby\")\n      tabsets.forEach(function(tabset) {\n        const tabby = new Tabby('#' + tabset.id);\n      });\n      const clipboard = new window.ClipboardJS('.code-copy-button', {\n        target: function(trigger) {\n          return trigger.previousElementSibling;\n        }\n      });\n      clipboard.on('success', function(e) {\n        // button target\n        const button = e.trigger;\n        // don't keep focus\n        button.blur();\n        // flash \"checked\"\n        button.classList.add('code-copy-button-checked');\n        var currentTitle = button.getAttribute(\"title\");\n        button.setAttribute(\"title\", \"Copied!\");\n        setTimeout(function() {\n          button.setAttribute(\"title\", currentTitle);\n          button.classList.remove('code-copy-button-checked');\n        }, 1000);\n        // clear code selection\n        e.clearSelection();\n      });\n      function tippyHover(el, contentFn) {\n        const config = {\n          allowHTML: true,\n          content: contentFn,\n          maxWidth: 500,\n          delay: 100,\n          arrow: false,\n          appendTo: function(el) {\n              return el.closest('section.slide') || el.parentElement;\n          },\n          interactive: true,\n          interactiveBorder: 10,\n          theme: 'quarto-reveal',\n          placement: 'bottom-start'\n        };\n          config['offset'] = [0,0];\n          config['maxWidth'] = 700;\n        window.tippy(el, config); \n      }\n      const noterefs = window.document.querySelectorAll('a[role=\"doc-noteref\"]');\n      for (var i=0; i<noterefs.length; i++) {\n        const ref = noterefs[i];\n        tippyHover(ref, function() {\n          let href = ref.getAttribute('href');\n          try { href = new URL(href).hash; } catch {}\n          const id = href.replace(/^#\\/?/, \"\");\n          const note = window.document.getElementById(id);\n          return note.innerHTML;\n        });\n      }\n      var bibliorefs = window.document.querySelectorAll('a[role=\"doc-biblioref\"]');\n      for (var i=0; i<bibliorefs.length; i++) {\n        const ref = bibliorefs[i];\n        const cites = ref.parentNode.getAttribute('data-cites').split(' ');\n        tippyHover(ref, function() {\n          var popup = window.document.createElement('div');\n          cites.forEach(function(cite) {\n            var citeDiv = window.document.createElement('div');\n            citeDiv.classList.add('hanging-indent');\n            citeDiv.classList.add('csl-entry');\n            var biblioDiv = window.document.getElementById('ref-' + cite);\n            if (biblioDiv) {\n              citeDiv.innerHTML = biblioDiv.innerHTML;\n            }\n            popup.appendChild(citeDiv);\n          });\n          return popup.innerHTML;\n        });\n      }\n    });\n    </script>\n    \n\n</body></html>"
  },
  {
    "path": "tests/testthat/test-account-find.R",
    "content": "test_that(\"validates its arguments\", {\n  expect_snapshot(error = TRUE, {\n    findAccount(1, NULL)\n    findAccount(NULL, 1)\n  })\n})\n\ntest_that(\"error if no accounts\", {\n  local_temp_config()\n  expect_snapshot(findAccount(), error = TRUE)\n})\n\ntest_that(\"error if no matching account\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"albert\")\n\n  expect_snapshot(error = TRUE, {\n    findAccount(\"unknown\", NULL)\n    findAccount(NULL, \"unknown\")\n    findAccount(\"unknown\", \"unknown\")\n  })\n})\n\ntest_that(\"error if ambiguous accounts in non-interactive environment\", {\n  local_temp_config()\n  addTestServer(\"a\")\n  addTestServer(\"b\")\n  addTestAccount(\"a\", \"x\")\n  addTestAccount(\"a\", \"y\")\n  addTestAccount(\"b\", \"y\")\n\n  expect_snapshot(error = TRUE, {\n    findAccount()\n    findAccount(\"a\", NULL)\n    findAccount(NULL, \"y\")\n  })\n})\n\ntest_that(\"prompted to pick account in interactive environment\", {\n  local_temp_config()\n  addTestServer(\"a\")\n  addTestServer(\"b\")\n  addTestAccount(\"a\", \"x\")\n  addTestAccount(\"a\", \"y\")\n  addTestAccount(\"b\", \"y\")\n\n  simulate_user_input(2)\n  expect_snapshot(out <- findAccount())\n  expect_equal(out, list(name = \"a\", server = \"y\"))\n})\n\ntest_that(\"returns account + server when uniquely identified\", {\n  local_temp_config()\n  addTestServer(\"a\")\n  addTestAccount(\"a\", \"x\")\n\n  expect_equal(findAccount(NULL, NULL), list(name = \"a\", server = \"x\"))\n  expect_equal(findAccount(\"a\", NULL), list(name = \"a\", server = \"x\"))\n  expect_equal(findAccount(NULL, \"x\"), list(name = \"a\", server = \"x\"))\n  expect_equal(findAccount(\"a\", \"x\"), list(name = \"a\", server = \"x\"))\n})\n"
  },
  {
    "path": "tests/testthat/test-accounts.R",
    "content": "test_that(\"no accounts returns empty data frame\", {\n  local_temp_config()\n\n  expect_equal(\n    accounts(),\n    data.frame(\n      name = character(),\n      server = character(),\n      stringsAsFactors = FALSE\n    )\n  )\n})\n\ntest_that(\"hasAccounts works\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"john\")\n\n  expect_true(hasAccount(\"john\", \"example.com\"))\n  expect_false(hasAccount(\"john\", \"example2.com\"))\n  expect_false(hasAccount(\"mary\", \"example.com\"))\n})\n\ntest_that(\"secrets are hidden from casual inspection\", {\n  local_temp_config()\n  registerAccount(\"server\", \"1\", \"id\", token = \"token\", secret = \"SECRET\")\n  registerAccount(\"server\", \"2\", \"id\", token = \"token\", private_key = \"SECRET\")\n  registerAccount(\"server\", \"3\", \"id\", apiKey = \"SECRET\")\n\n  expect_snapshot({\n    accountInfo(\"1\")$secret\n    accountInfo(\"2\")$private_key\n    accountInfo(\"3\")$apiKey\n  })\n})\n\ntest_that(\"setAccountInfo() gives nice error on bad copy and paste\", {\n  expect_snapshot(setAccountInfo(\"name\", \"token\", \"<SECRET>\"), error = TRUE)\n})\n\ntest_that(\"accountInfo() returns account information\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"john\")\n\n  accountDetails <- accountInfo(\"john\", \"example.com\")\n  expect_equal(accountDetails$name, \"john\")\n  expect_equal(accountDetails$username, \"john\")\n  expect_equal(accountDetails$server, \"example.com\")\n})\n\ntest_that(\"accountInfo() returns account information\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"john\")\n\n  accountDetails <- accountInfo(\"john\", \"example.com\")\n  expect_equal(accountDetails$name, \"john\")\n  # username is included for backwards compatibility. (#1024)\n  expect_equal(accountDetails$username, \"john\")\n  expect_equal(accountDetails$server, \"example.com\")\n})\n\ntest_that(\"accountInfo() returns pre-rsconnect-1.0.0 account information\", {\n  local_temp_config()\n  addTestServer()\n\n  # Subset of rsconnect-0.8.29 account fields.\n  fields <- list(\n    username = \"john\",\n    server = \"example.com\",\n    accountId = \"john\"\n  )\n\n  path <- accountConfigFile(\"john\", \"example.com\")\n  dir.create(dirname(path), recursive = TRUE, showWarnings = FALSE)\n  write.dcf(compact(fields), path, width = 100)\n\n  accountDetails <- accountInfo(\"john\", \"example.com\")\n  # name copied from username, as \"name\" is the current field name.\n  expect_equal(accountDetails$name, \"john\")\n  # username retained for backwards compatibility.\n  expect_equal(accountDetails$username, \"john\")\n  expect_equal(accountDetails$server, \"example.com\")\n})\n\ntest_that(\"registerAccount stores snowflakeConnectionName\", {\n  local_temp_config()\n\n  # Register an account with snowflakeConnectionName\n  registerAccount(\n    serverName = \"example.com\",\n    accountName = \"testuser\",\n    accountId = \"user123\",\n    snowflakeConnectionName = \"test_connection\"\n  )\n\n  # Check the account info has the snowflakeConnectionName\n  info <- accountInfo(\"testuser\", \"example.com\")\n  expect_equal(info$snowflakeConnectionName, \"test_connection\")\n})\n\ntest_that(\"findAccountInfo redacts snowflakeToken\", {\n  local_temp_config()\n\n  # Create mock account info with snowflakeToken\n  fields <- list(\n    name = \"testuser\",\n    server = \"example.com\",\n    accountId = \"user123\",\n    snowflakeToken = \"sensitive_token_data\"\n  )\n\n  path <- accountConfigFile(\"testuser\", \"example.com\")\n  dir.create(dirname(path), recursive = TRUE, showWarnings = FALSE)\n  write.dcf(compact(fields), path, width = 100)\n\n  # Get account info and check that snowflakeToken is redacted\n  info <- findAccountInfo(\"testuser\", \"example.com\")\n  expect_s3_class(info$snowflakeToken, \"rsconnect_secret\")\n})\n\ntest_that(\"connectSPCSUser accepts NULL snowflakeConnectionName and gets default\", {\n  local_temp_config()\n\n  local_mocked_bindings(\n    findServer = function(server) \"test_server\",\n    checkConnectServer = function(server) invisible(),\n    serverInfo = function(server) {\n      list(url = \"https://prefix-account.snowflakecomputing.app/__api__\")\n    },\n    getDefaultSnowflakeConnectionName = function(url) {\n      \"default\"\n    },\n    getSPCSAuthedUser = function(server, apiKey, snowflakeConnectionName) {\n      # Verify we have the default connection name.\n      expect_equal(snowflakeConnectionName, \"default\")\n      list(id = \"user123\", username = \"testuser\")\n    }\n  )\n\n  expect_no_error(\n    connectSPCSUser(\n      account = \"testuser\",\n      server = \"test_server\",\n      apiKey = \"test_api_key\",\n      snowflakeConnectionName = NULL,\n      quiet = TRUE\n    )\n  )\n})\n\ntest_that(\"connectSPCSUser works with explicit snowflakeConnectionName\", {\n  local_temp_config()\n\n  local_mocked_bindings(\n    findServer = function(server) \"test_server\",\n    checkConnectServer = function(server) invisible(),\n    serverInfo = function(server) {\n      list(url = \"https://prefix-account.snowflakecomputing.app/__api__\")\n    },\n    getSPCSAuthedUser = function(server, apiKey, snowflakeConnectionName) {\n      # Verify we have the correct connection name.\n      expect_equal(snowflakeConnectionName, \"test_connection\")\n      list(id = \"user123\", username = \"testuser\")\n    }\n  )\n\n  expect_no_error(\n    connectSPCSUser(\n      account = \"testuser\",\n      server = \"test_server\",\n      apiKey = \"test_api_key\",\n      snowflakeConnectionName = \"test_connection\",\n      quiet = TRUE\n    )\n  )\n})\n\ntest_that(\"getSPCSAuthedUser passes snowflakeConnectionName to clientForAccount\", {\n  local_temp_config()\n\n  local_mocked_bindings(\n    serverInfo = function(server) {\n      list(url = \"https://prefix-account.snowflakecomputing.app/__api__\")\n    },\n    clientForAccount = function(account) {\n      # Check that snowflakeConnectionName is passed through\n      expect_equal(account$snowflakeConnectionName, \"test_connection\")\n      list(currentUser = function() list(id = \"user123\", username = \"testuser\"))\n    }\n  )\n\n  result <- getSPCSAuthedUser(\n    \"test_server\",\n    \"test_api_key\",\n    \"test_connection\"\n  )\n\n  expect_equal(result$username, \"testuser\")\n})\n"
  },
  {
    "path": "tests/testthat/test-appDependencies.R",
    "content": "test_that(\"appDependencies includes implicit deps\", {\n  skip_on_cran()\n\n  withr::local_options(renv.verbose = TRUE)\n\n  path <- local_temp_app(list(\"test.Rmd\" = \"\"))\n  deps <- appDependencies(path)\n\n  expect_true(\"rmarkdown\" %in% deps$Package)\n})\n\ntest_that(\"appDependencies includes implicit deps when appMode forced\", {\n  skip_on_cran()\n  skip_if_not_installed(\"plumber\")\n  skip_if_not_installed(\"shiny\")\n  skip_if_not_installed(\"rmarkdown\")\n\n  withr::local_options(renv.verbose = TRUE)\n\n  dir <- local_temp_app(list(\n    \"app.R\" = \"\",\n    \"plumber.R\" = \"\",\n    \"report.Rmd\" = \"\",\n    \"index.html\" = \"\"\n  ))\n  files <- c(\"app.R\", \"plumber.R\", \"report.Rmd\", \"index.html\")\n\n  # unless forced to \"static\", the presence of *.Rmd forces rmarkdown as an R\n  # dependency, regardless of appMode.\n\n  # inference indicates a Plumber API.\n  deps <- appDependencies(dir)\n  expect_true(\"plumber\" %in% deps$Package)\n  expect_false(\"shiny\" %in% deps$Package)\n  expect_true(\"rmarkdown\" %in% deps$Package)\n\n  deps <- appDependencies(dir, appMode = \"api\")\n  expect_true(\"plumber\" %in% deps$Package)\n  expect_false(\"shiny\" %in% deps$Package)\n  expect_true(\"rmarkdown\" %in% deps$Package)\n\n  deps <- appDependencies(dir, appMode = \"shiny\")\n  expect_false(\"plumber\" %in% deps$Package)\n  expect_true(\"shiny\" %in% deps$Package)\n  expect_true(\"rmarkdown\" %in% deps$Package)\n\n  deps <- appDependencies(dir, appMode = \"rmd-static\")\n  expect_false(\"plumber\" %in% deps$Package)\n  expect_false(\"shiny\" %in% deps$Package)\n  expect_true(\"rmarkdown\" %in% deps$Package)\n\n  deps <- appDependencies(dir, appMode = \"static\")\n  expect_equal(\n    deps,\n    data.frame(\n      Package = character(),\n      Version = character(),\n      Source = character(),\n      Repository = character(),\n      stringsAsFactors = FALSE\n    )\n  )\n})\n\ntest_that(\"static project doesn't have deps\", {\n  skip_on_cran()\n\n  path <- local_temp_app(list(\"index.html\" = \"\"))\n  deps <- appDependencies(path)\n\n  expect_equal(\n    deps,\n    data.frame(\n      Package = character(),\n      Version = character(),\n      Source = character(),\n      Repository = character(),\n      stringsAsFactors = FALSE\n    )\n  )\n})\n\ntest_that(\"infers correct packages for each source\", {\n  skip_on_cran()\n\n  simulateMetadata <- function(\n    appMode,\n    hasParameters = FALSE,\n    documentsHavePython = FALSE,\n    plumberInfo = NULL\n  ) {\n    list(\n      appMode = appMode,\n      hasParameters = hasParameters,\n      documentsHavePython = documentsHavePython,\n      plumberInfo = plumberInfo\n    )\n  }\n\n  # Simple regression test in preparation for refactoring\n  expect_snapshot({\n    inferRPackageDependencies(simulateMetadata(\"rmd-static\"))\n    inferRPackageDependencies(simulateMetadata(\n      \"rmd-static\",\n      hasParameters = TRUE\n    ))\n    inferRPackageDependencies(simulateMetadata(\"quarto-static\"))\n    inferRPackageDependencies(simulateMetadata(\"quarto-shiny\"))\n    inferRPackageDependencies(simulateMetadata(\"rmd-shiny\"))\n    inferRPackageDependencies(simulateMetadata(\"shiny\"))\n    inferRPackageDependencies(simulateMetadata(\"api\", plumberInfo = \"plumber\"))\n    inferRPackageDependencies(simulateMetadata(\n      \"api\",\n      documentsHavePython = TRUE,\n      plumberInfo = \"plumber\"\n    ))\n    inferRPackageDependencies(simulateMetadata(\"api\", plumberInfo = \"plumber2\"))\n  })\n})\n\ntest_that(\"needsR returns FALSE for nodejs\", {\n  metadata <- list(appMode = \"nodejs\")\n  expect_false(needsR(metadata))\n})\n"
  },
  {
    "path": "tests/testthat/test-appMetadata-quarto.R",
    "content": "# quarto ------------------------------------------------------------------\n\nfakeQuartoMetadata <- function(version, engines) {\n  # See quarto-r/R/publish.R lines 396 and 113.\n  metadata <- list()\n  metadata$quarto_version <- version\n  metadata$quarto_engines <- I(engines)\n  return(metadata)\n}\n\ntest_that(\"inferQuartoInfo correctly detects document info when quarto is provided alone\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  quarto_info <- inferQuartoInfo(\n    metadata = list(),\n    appDir = test_path(\"quarto-doc-none\"),\n    appPrimaryDoc = \"quarto-doc-none.qmd\"\n  )\n  expect_named(quarto_info, c(\"version\", \"engines\"))\n  expect_equal(quarto_info$engines, I(c(\"markdown\")))\n})\n\ntest_that(\"inferQuartoInfo correctly detects website info when quarto is provided alone\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  app_dir <- local_temp_app(quarto_website_r_files)\n  quarto_info <- inferQuartoInfo(\n    appDir = app_dir,\n    appPrimaryDoc = NULL,\n    metadata = list()\n  )\n  expect_named(quarto_info, c(\"version\", \"engines\"))\n  expect_equal(quarto_info$engines, I(c(\"knitr\")))\n})\n\ntest_that(\"inferQuartoInfo prefers metadata over quarto inspect\", {\n  metadata <- fakeQuartoMetadata(\n    version = \"99.9.9\",\n    engines = c(\"internal-combustion\")\n  )\n\n  app_dir <- local_temp_app(quarto_website_r_files)\n  quarto_info <- inferQuartoInfo(\n    appDir = app_dir,\n    appPrimaryDoc = NULL,\n    metadata = metadata\n  )\n  expect_equal(\n    quarto_info,\n    list(\n      version = \"99.9.9\",\n      engines = I(\"internal-combustion\")\n    )\n  )\n})\n\ntest_that(\"quartoInspect requires quarto\", {\n  local_mocked_bindings(quarto_path = function() NULL)\n  expect_snapshot(error = TRUE, {\n    quartoInspect()\n  })\n})\n\ntest_that(\"quartoInspect identifies Quarto projects (website)\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  app_dir <- local_temp_app(quarto_website_r_files)\n  inspect <- quartoInspect(app_dir)\n  expect_true(all(c(\"quarto\", \"engines\") %in% names(inspect)))\n})\n\ntest_that(\"quartoInspect identifies Quarto projects (shiny)\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  app_dir <- local_temp_app(quarto_project_r_shiny_files)\n  inspect <- quartoInspect(app_dir)\n  expect_true(all(c(\"quarto\", \"engines\") %in% names(inspect)))\n})\n\ntest_that(\"quartoInspect identifies Quarto documents\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  inspect <- quartoInspect(\n    appDir = test_path(\"quarto-doc-none\"),\n    appPrimaryDoc = \"quarto-doc-none.qmd\"\n  )\n  expect_true(all(c(\"quarto\", \"engines\") %in% names(inspect)))\n})\n\ntest_that(\"quartoInspect processes content within paths containing spaces\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  parent <- withr::local_tempdir()\n  dir <- file.path(parent, \"space dir\")\n  dir.create(dir)\n  writeLines(\n    c(\n      \"---\",\n      \"title: space path\",\n      \"---\",\n      \"this is a document within a path having spaces.\"\n    ),\n    file.path(dir, \"index.qmd\")\n  )\n  inspect <- quartoInspect(dir, \"index.qmd\")\n  expect_equal(inspect$engines, c(\"markdown\"))\n})\n\ntest_that(\"quartoInspect processes content with filenames containing spaces\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  dir <- local_temp_app(list(\n    \"space file.qmd\" = c(\n      \"---\",\n      \"title: space name\",\n      \"---\",\n      \"this is a document with a filename having spaces.\"\n    )\n  ))\n  inspect <- quartoInspect(dir, \"space file.qmd\")\n  expect_equal(inspect$engines, c(\"markdown\"))\n})\n\n# Some versions of Quarto show Quarto stack traces with source references when\n# inspect fails.\n#\n# Only preserve:\n#\n#   Error in `quartoInspect()`:\n#   ! Failed to run `quarto inspect` against your content:\n#   ERROR: Unsupported project type unsupported\nstrip_quarto_trace <- function(lines) {\n  head(lines, 3)\n}\n\ntest_that(\"quartoInspect produces an error when a document cannot be inspected\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  # Suppress colors from Quarto errors.\n  withr::local_envvar(NO_COLOR = \"1\")\n\n  dir <- local_temp_app(list(\n    \"bad.qmd\" = c(\n      \"---\",\n      \"format: unsupported\",\n      \"---\",\n      \"this is a document using an unsupported format.\"\n    )\n  ))\n  expect_snapshot(\n    quartoInspect(dir, \"bad.qmd\"),\n    error = TRUE,\n    transform = strip_quarto_trace\n  )\n})\n\ntest_that(\"quartoInspect produces an error when a project cannot be inspected\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n\n  # Suppress colors from Quarto errors.\n  withr::local_envvar(NO_COLOR = \"1\")\n\n  dir <- local_temp_app(\n    list(\n      \"_quarto.yml\" = c(\n        \"project:\",\n        \"  type: unsupported\"\n      ),\n      \"bad.qmd\" = c(\n        \"---\",\n        \"title: bad\",\n        \"---\",\n        \"this is a document using an unsupported format.\"\n      )\n    )\n  )\n  expect_snapshot(\n    quartoInspect(dir, \"bad.qmd\"),\n    error = TRUE,\n    transform = strip_quarto_trace\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-appMetadata.R",
    "content": "# appMetadata -------------------------------------------------------------\n\ntest_that(\"quarto affects mode inference\", {\n  skip_on_cran()\n\n  dir <- local_temp_app(list(\"foo.Rmd\" = \"\"))\n\n  metadata <- appMetadata(dir, c(\"foo.Rmd\"))\n  expect_equal(metadata$appMode, \"rmd-static\")\n\n  metadata <- appMetadata(\n    dir,\n    c(\"foo.Rmd\"),\n    metadata = list(quarto_version = 1)\n  )\n  expect_equal(metadata$appMode, \"quarto-static\")\n})\n\ntest_that(\"validates quarto argument\", {\n  skip_on_cran()\n\n  dir <- local_temp_app(list(\"foo.Rmd\" = \"\"))\n  expect_snapshot(appMetadata(dir, c(\"foo.Rmd\"), quarto = 1), error = TRUE)\n})\n\ntest_that(\"handles special case of appPrimaryDoc as R file\", {\n  dir <- local_temp_app(list(\"foo.R\" = \"\"))\n  metadata <- appMetadata(dir, c(\"foo.R\"), appPrimaryDoc = \"foo.R\")\n  expect_equal(metadata$appMode, \"shiny\")\n})\n\n# https://github.com/rstudio/rsconnect/issues/942\ntest_that(\"files beneath the root are not ignored when determining app-mode\", {\n  dir <- local_temp_app(list(\"app.R\" = \"\", \"plumber/api/plumber.R\" = \"\"))\n  metadata <- appMetadata(dir, c(\"app.R\", \"plumber/api/plumber.R\"))\n  expect_equal(metadata$appMode, \"shiny\")\n})\n\ntest_that(\"content type (appMode) is inferred and can be overridden\", {\n  dir <- local_temp_app(list(\n    \"app.R\" = \"\",\n    \"plumber.R\" = \"\",\n    \"report.Rmd\" = \"\",\n    \"index.html\" = \"\"\n  ))\n  files <- c(\"app.R\", \"plumber.R\", \"report.Rmd\", \"index.html\")\n\n  metadata <- appMetadata(dir, files)\n  expect_equal(metadata$appMode, \"api\")\n\n  metadata <- appMetadata(dir, files, appMode = \"shiny\")\n  expect_equal(metadata$appMode, \"shiny\")\n\n  metadata <- appMetadata(dir, files, appMode = \"rmd-static\")\n  expect_equal(metadata$appMode, \"rmd-static\")\n\n  metadata <- appMetadata(dir, files, appMode = \"static\")\n  expect_equal(metadata$appMode, \"static\")\n})\n\ntest_that(\"Shiny Quarto without an appropriate engine is an error\", {\n  skip_on_cran()\n\n  dir <- local_temp_app(list(\n    \"index.qmd\" = c(\"---\", \"server: shiny\", \"---\")\n  ))\n  files <- list.files(dir)\n  expect_snapshot(appMetadata(dir, files), error = TRUE)\n})\n\ntest_that(\"Shiny Quarto with the knitr engine is OK\", {\n  skip_on_cran()\n\n  dir <- local_temp_app(list(\n    \"index.qmd\" = c(\"---\", \"server: shiny\", \"engine: knitr\", \"---\")\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_contains(metadata$quartoInfo$engines, \"knitr\")\n})\n\ntest_that(\"Shiny Quarto with the jupyter engine is OK\", {\n  skip_on_cran()\n\n  dir <- local_temp_app(list(\n    \"index.qmd\" = c(\"---\", \"server: shiny\", \"engine: jupyter\", \"---\")\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_contains(metadata$quartoInfo$engines, \"jupyter\")\n})\n\ntest_that(\"correct extra packages for APIs are detected\", {\n  dir_plumber <- local_temp_app(list(\"plumber.R\" = \"\"))\n  files_plumber <- list.files(dir_plumber)\n  metadata_plumber <- appMetadata(dir_plumber, files_plumber)\n  expect_equal(metadata_plumber$plumberInfo, \"plumber\")\n\n  dir_plumber2 <- local_temp_app(list(\"_server.yml\" = \"engine: 'plumber2'\"))\n  files_plumber2 <- list.files(dir_plumber2)\n  metadata_plumber2 <- appMetadata(dir_plumber2, files_plumber2)\n  expect_equal(metadata_plumber2$plumberInfo, \"plumber2\")\n\n  dir_other <- local_temp_app(list(\"app.R\" = \"\"))\n  files_other <- list.files(dir_other)\n  metadata_other <- appMetadata(dir_other, files_other)\n  expect_null(metadata_other$plumberInfo, \"other\")\n})\n\ntest_that(\"appMode = 'nodejs' can be set explicitly on a non-nodejs directory\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\",\n    \"app.R\" = \"\"\n  ))\n  files <- list.files(dir)\n  # app.R takes priority by default\n  metadata <- appMetadata(dir, files)\n  expect_equal(metadata$appMode, \"shiny\")\n\n  # can override to force nodejs\n  metadata <- appMetadata(dir, files, appMode = \"nodejs\")\n  expect_equal(metadata$appMode, \"nodejs\")\n  expect_equal(metadata$nodejsInfo$entrypoint, \"app.js\")\n})\n\ntest_that(\"appMetadata errors when package-lock.json is missing\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  expect_error(\n    appMetadata(dir, files),\n    \"package-lock.json\"\n  )\n})\n\ntest_that(\"appMetadata returns nodejsInfo for nodejs content\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\", \"engines\": {\"node\": \">=22.18.0\"}}',\n    \"package-lock.json\" = '{\"lockfileVersion\": 3}',\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_equal(metadata$appMode, \"nodejs\")\n  expect_equal(metadata$nodejsInfo$entrypoint, \"app.js\")\n  expect_equal(metadata$nodejsInfo$enginesNode, \">=22.18.0\")\n})\n\ntest_that(\"appMetadata warns when entrypoint file is missing\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"dist/server.js\"}',\n    \"package-lock.json\" = \"{}\"\n  ))\n  files <- list.files(dir)\n  expect_warning(\n    appMetadata(dir, files),\n    \"dist/server.js\"\n  )\n})\n\ntest_that(\"appMetadata allows appMode override from nodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = \"{}\",\n    \"index.html\" = \"\"\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files, appMode = \"static\")\n  expect_equal(metadata$appMode, \"static\")\n  expect_null(metadata$nodejsInfo)\n})\n\ntest_that(\"appMetadata returns NULL appPrimaryDoc for nodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_null(metadata$appPrimaryDoc)\n})\n\ntest_that(\"appMetadata returns FALSE hasParameters for nodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_false(metadata$hasParameters)\n})\n\ntest_that(\"appMetadata returns FALSE documentsHavePython for nodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_false(metadata$documentsHavePython)\n})\n\ntest_that(\"appMetadata returns NULL quartoInfo for nodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_null(metadata$quartoInfo)\n})\n\ntest_that(\"appMetadata returns NULL plumberInfo for nodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  metadata <- appMetadata(dir, files)\n  expect_null(metadata$plumberInfo)\n})\n\n# checkLayout -------------------------------------------------------------\n\n# inferAppMode ------------------------------------------------------------\n\ntest_that(\"can infer mode for API with plumber.R\", {\n  dir <- local_temp_app(list(\"plumber.R\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"api\", primaryFile = \"plumber.R\")\n  )\n})\n\ntest_that(\"can infer mode for API with entrypoint.R\", {\n  dir <- local_temp_app(list(\"entrypoint.R\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"api\", primaryFile = \"entrypoint.R\")\n  )\n})\n\ntest_that(\"can infer mode for API with _server.yml\", {\n  dir <- local_temp_app(list(\"_server.yml\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"api\", primaryFile = \"_server.yml\")\n  )\n})\n\ntest_that(\"can infer mode for shiny apps with app.R\", {\n  dir <- local_temp_app(list(\"app.R\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"shiny\", primaryFile = \"app.R\")\n  )\n})\n\ntest_that(\"can infer mode for shiny apps with server.R\", {\n  dir <- local_temp_app(list(\"server.R\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"shiny\", primaryFile = \"server.R\")\n  )\n})\n\ntest_that(\"can infer mode for static rmd\", {\n  dir <- local_temp_app(list(\"foo.Rmd\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"rmd-static\", primaryFile = \"foo.Rmd\")\n  )\n})\n\ntest_that(\"can infer mode for rmd as static quarto with guidance\", {\n  dir <- local_temp_app(list(\"foo.Rmd\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths, usesQuarto = TRUE),\n    list(appMode = \"quarto-static\", primaryFile = \"foo.Rmd\")\n  )\n})\n\ntest_that(\"can infer mode for rmd as shiny quarto with guidance\", {\n  # Static R Markdown treated as rmd-shiny for shinyapps targets\n  dir <- local_temp_app(list(\"foo.Rmd\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths, isShinyappsServer = TRUE),\n    list(appMode = \"rmd-shiny\", primaryFile = \"foo.Rmd\")\n  )\n})\n\ntest_that(\"can infer mode for static quarto\", {\n  dir <- local_temp_app(list(\"foo.qmd\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"quarto-static\", primaryFile = \"foo.qmd\")\n  )\n\n  dir <- local_temp_app(list(\"_quarto.yml\" = \"\", \"foo.qmd\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"quarto-static\", primaryFile = \"foo.qmd\")\n  )\n\n  dir <- local_temp_app(list(\"_quarto.yml\" = \"\", \"foo.rmd\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"quarto-static\", primaryFile = \"foo.rmd\")\n  )\n\n  dir <- local_temp_app(list(\"_quarto.yml\" = \"\", \"foo.r\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"quarto-static\", primaryFile = \"foo.r\")\n  )\n\n  dir <- local_temp_app(list(\"foo.r\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"quarto-static\", primaryFile = \"foo.r\")\n  )\n})\n\ntest_that(\"can infer mode for shiny rmd docs\", {\n  yaml_runtime <- function(runtime) {\n    c(\"---\", paste0(\"runtime: \", runtime), \"---\")\n  }\n\n  dir <- local_temp_app(list(\"index.Rmd\" = yaml_runtime(\"shiny\")))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"rmd-shiny\", primaryFile = \"index.Rmd\")\n  )\n\n  dir <- local_temp_app(list(\"index.Rmd\" = yaml_runtime(\"shinyrmd\")))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"rmd-shiny\", primaryFile = \"index.Rmd\")\n  )\n\n  dir <- local_temp_app(list(\"index.Rmd\" = yaml_runtime(\"shiny_prerendered\")))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"rmd-shiny\", primaryFile = \"index.Rmd\")\n  )\n\n  # can pair server.R with shiny runtime\n  dir <- local_temp_app(list(\n    \"index.Rmd\" = yaml_runtime(\"shiny\"),\n    \"server.R\" = \"\"\n  ))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"rmd-shiny\", primaryFile = \"index.Rmd\")\n  )\n\n  # Beats static rmarkdowns\n  dir <- local_temp_app(list(\n    \"index.Rmd\" = yaml_runtime(\"shiny\"),\n    \"foo.Rmd\" = \"\"\n  ))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"rmd-shiny\", primaryFile = \"index.Rmd\")\n  )\n})\n\ntest_that(\"can infer mode for shiny qmd docs\", {\n  yaml_runtime <- function(runtime) {\n    c(\n      \"---\",\n      paste0(\"runtime: \", runtime),\n      paste0(\"engine: knitr\"),\n      \"---\"\n    )\n  }\n\n  dir <- local_temp_app(list(\"index.Qmd\" = yaml_runtime(\"shiny\")))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"quarto-shiny\", primaryFile = \"index.Qmd\")\n  )\n\n  # Can force Rmd to use quarto\n  dir <- local_temp_app(list(\"index.Rmd\" = yaml_runtime(\"shiny\")))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths, usesQuarto = TRUE),\n    list(appMode = \"quarto-shiny\", primaryFile = \"index.Rmd\")\n  )\n\n  # Prefers quarto if both present\n  dir <- local_temp_app(list(\n    \"index.Qmd\" = yaml_runtime(\"shiny\"),\n    \"index.Rmd\" = yaml_runtime(\"shiny\")\n  ))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"quarto-shiny\", primaryFile = \"index.Qmd\")\n  )\n})\n\ntest_that(\"Shiny R Markdown files are detected correctly\", {\n  expect_true(isShinyRmd(test_path(\"shiny-rmds/shiny-rmd-dashes.Rmd\")))\n  expect_true(isShinyRmd(test_path(\"shiny-rmds/shiny-rmd-dots.Rmd\")))\n  expect_false(isShinyRmd(test_path(\"shiny-rmds/non-shiny-rmd.Rmd\")))\n})\n\ntest_that(\"shiny metadata process correctly\", {\n  expect_false(is_shiny_prerendered(NULL, NULL))\n  expect_true(is_shiny_prerendered(\"shiny_prerendered\", NULL))\n  expect_true(is_shiny_prerendered(\"shinyrmd\", NULL))\n  expect_true(is_shiny_prerendered(NULL, \"shiny\"))\n  expect_true(is_shiny_prerendered(NULL, list(type = \"shiny\")))\n})\n\ntest_that(\"otherwise, fallsback to static deploy\", {\n  dir <- local_temp_app(list(\"a.html\" = \"\", \"b.html\" = \"\"))\n  paths <- list.files(dir)\n  expect_equal(\n    inferAppMode(dir, paths),\n    list(appMode = \"static\", primaryFile = NULL)\n  )\n})\n\ntest_that(\"package.json is detected as nodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = '{\"lockfileVersion\": 3}',\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"nodejs\")\n})\n\ntest_that(\"R files take priority over package.json\", {\n  dir <- local_temp_app(list(\n    \"app.R\" = \"\",\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = \"{}\"\n  ))\n  files <- list.files(dir)\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"shiny\")\n})\n\ntest_that(\"Rmd files take priority over package.json\", {\n  dir <- local_temp_app(list(\n    \"index.Rmd\" = \"\",\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = \"{}\"\n  ))\n  files <- list.files(dir)\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"rmd-static\")\n})\n\ntest_that(\"plumber API takes priority over package.json\", {\n  dir <- local_temp_app(list(\n    \"plumber.R\" = \"\",\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = \"{}\"\n  ))\n  files <- list.files(dir)\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"api\")\n})\n\ntest_that(\"Quarto .qmd files take priority over package.json\", {\n  dir <- local_temp_app(list(\n    \"index.qmd\" = \"\",\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = \"{}\"\n  ))\n  files <- list.files(dir)\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"quarto-static\")\n})\n\ntest_that(\"server.R takes priority over package.json\", {\n  dir <- local_temp_app(list(\n    \"server.R\" = \"\",\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = \"{}\"\n  ))\n  files <- list.files(dir)\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"shiny\")\n})\n\ntest_that(\"package.json takes priority over static HTML\", {\n  dir <- local_temp_app(list(\n    \"index.html\" = \"<html></html>\",\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n  files <- list.files(dir)\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"nodejs\")\n})\n\ntest_that(\"package.json in subdirectory does not trigger nodejs\", {\n  dir <- local_temp_app(list(\n    \"index.html\" = \"<html></html>\",\n    \"subdir/package.json\" = '{\"name\": \"test\"}'\n  ))\n  files <- c(\"index.html\", \"subdir/package.json\")\n  result <- inferAppMode(dir, files)\n  expect_equal(result$appMode, \"static\")\n})\n\n# inferAppPrimaryDoc ------------------------------------------------------\n\ntest_that(\"leaves addPrimaryDoc unchanged or not a document\", {\n  expect_equal(inferAppPrimaryDoc(\"foo.Rmd\"), \"foo.Rmd\")\n  expect_equal(inferAppPrimaryDoc(NULL, appMode = \"shiny\"), NULL)\n  expect_equal(inferAppPrimaryDoc(NULL, appMode = \"api\"), NULL)\n})\n\ntest_that(\"uses index file if present\", {\n  files <- c(\"index.html\", \"index.Rmd\", \"a.html\", \"b.html\", \"a.Rmd\", \"b.Rmd\")\n  expect_equal(inferAppPrimaryDoc(NULL, files, \"static\"), \"index.html\")\n  expect_equal(inferAppPrimaryDoc(NULL, files, \"rmd-shiny\"), \"index.Rmd\")\n})\n\ntest_that(\"otherwise fails back to first file with matching extensions\", {\n  files <- c(\".Rprofile\", \"a.html\", \"b.html\", \"a.Rmd\", \"b.Rmd\")\n  expect_equal(inferAppPrimaryDoc(NULL, files, \"static\"), \"a.html\")\n  expect_equal(inferAppPrimaryDoc(NULL, files, \"rmd-static\"), \"a.Rmd\")\n  expect_equal(inferAppPrimaryDoc(NULL, files, \"rmd-shiny\"), \"a.Rmd\")\n  expect_equal(inferAppPrimaryDoc(NULL, files, \"quarto-static\"), \"a.Rmd\")\n  expect_equal(inferAppPrimaryDoc(NULL, files, \"quarto-shiny\"), \"a.Rmd\")\n})\n\ntest_that(\"can use R, Rmd, and qmd files for Quarto modes\", {\n  expect_equal(\n    inferAppPrimaryDoc(NULL, c(\".Rprofile\", \"foo.R\"), \"quarto-static\"),\n    \"foo.R\"\n  )\n  expect_equal(\n    inferAppPrimaryDoc(NULL, c(\".Rprofile\", \"foo.Rmd\"), \"quarto-static\"),\n    \"foo.Rmd\"\n  )\n  expect_equal(\n    inferAppPrimaryDoc(NULL, c(\".Rprofile\", \"foo.qmd\"), \"quarto-static\"),\n    \"foo.qmd\"\n  )\n  expect_equal(\n    inferAppPrimaryDoc(NULL, c(\".Rprofile\", \"foo.Rmd\"), \"quarto-shiny\"),\n    \"foo.Rmd\"\n  )\n  expect_equal(\n    inferAppPrimaryDoc(NULL, c(\".Rprofile\", \"foo.qmd\"), \"quarto-shiny\"),\n    \"foo.qmd\"\n  )\n})\n\ntest_that(\"errors if no files with needed extension\", {\n  expect_snapshot(error = TRUE, {\n    inferAppPrimaryDoc(NULL, \"a.R\", \"static\")\n    inferAppPrimaryDoc(NULL, \"a.html\", \"rmd-static\")\n    inferAppPrimaryDoc(NULL, \"a.html\", \"rmd-shiny\")\n    inferAppPrimaryDoc(NULL, \"a.html\", \"quarto-static\")\n    inferAppPrimaryDoc(NULL, \"a.html\", \"quarto-shiny\")\n  })\n})\n\n# appHasParameters --------------------------------------------------------\n\ntest_that(\"non-documents don't have parameters\", {\n  dir <- local_temp_app(list(\"foo.R\" = \"\"))\n\n  expect_false(appHasParameters(dir, \"foo.R\", \"static\"))\n  expect_false(appHasParameters(dir, \"foo.R\", \"shiny\"))\n})\n\ntest_that(\"documents don't have parameters if part of a site\", {\n  dir <- local_temp_app(list(\"index.Rmd\" = c(\"---\", \"params: [1, 2]\", \"---\")))\n\n  expect_false(appHasParameters(dir, \"index.Rmd\", \"rmd-static\", \"site\"))\n  expect_false(appHasParameters(dir, \"index.Rmd\", \"qmd-shiny\", \"site\"))\n})\n\ntest_that(\"non-Rmd files don't have parameters\", {\n  dir <- local_temp_app(list(\"app.r\" = c(\"\")))\n  expect_false(appHasParameters(dir, \"app.R\", \"rmd-shiny\"))\n})\n\ntest_that(\"otherwise look at yaml metadata\", {\n  dir <- local_temp_app(list(\"index.Rmd\" = c(\"---\", \"params: [1, 2]\", \"---\")))\n  expect_true(appHasParameters(dir, \"index.Rmd\", \"rmd-shiny\"))\n\n  dir <- local_temp_app(list(\"index.Rmd\" = c(\"---\", \"params: ~\", \"---\")))\n  expect_false(appHasParameters(dir, \"index.Rmd\", \"rmd-shiny\"))\n})\n\n# detectPythonInDocuments -------------------------------------------------\n\ntest_that(\"dir without Rmds doesn't have have python\", {\n  dir <- local_temp_app()\n  expect_false(detectPythonInDocuments(dir))\n\n  dir <- local_temp_app(list(\"foo.R\" = \"\"))\n  expect_false(detectPythonInDocuments(dir))\n})\n\ntest_that(\"Rmd or qmd with python chunk has python\", {\n  dir <- local_temp_app(list(\"foo.qmd\" = c(\"```{r}\", \"1+1\", \"````\")))\n  expect_false(detectPythonInDocuments(dir))\n\n  dir <- local_temp_app(list(\"foo.Rmd\" = c(\"```{python}\", \"1+1\", \"````\")))\n  expect_true(detectPythonInDocuments(dir))\n\n  dir <- local_temp_app(list(\"foo.qmd\" = c(\"```{python}\", \"1+1\", \"````\")))\n  expect_true(detectPythonInDocuments(dir))\n})\n\n# inferPlumberInfo --------------------------------------------------------\n\ntest_that(\"inferPlumberInfo returns engine from _server.yml\", {\n  dir <- local_temp_app(list(\"_server.yml\" = \"engine: 'plumber2'\"))\n  expect_equal(inferPlumberInfo(dir), \"plumber2\")\n})\n\ntest_that(\"inferPlumberInfo returns engine from _server.yaml\", {\n  dir <- local_temp_app(list(\"_server.yaml\" = \"engine: 'plumber2'\"))\n  expect_equal(inferPlumberInfo(dir), \"plumber2\")\n})\n\ntest_that(\"inferPlumberInfo returns 'plumber' for plumber APIs\", {\n  dir <- local_temp_app(list(\"plumber.R\" = \"\"))\n  expect_equal(inferPlumberInfo(dir), \"plumber\")\n})\n\ntest_that(\"inferPlumberInfo errors if both _server.yml and _server.yaml present\", {\n  dir <- local_temp_app(\n    list(\n      \"_server.yml\" = \"engine: 'plumber2'\",\n      \"_server.yaml\" = \"engine: 'plumber2'\"\n    )\n  )\n  expect_error(inferPlumberInfo(dir))\n})\n"
  },
  {
    "path": "tests/testthat/test-applications.R",
    "content": "test_that(\"syncAppMetadata updates deployment records\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\")\n\n  app <- local_temp_app()\n  addTestDeployment(app, appId = \"123\", metadata = list(when = 123))\n  local_mocked_bindings(clientForAccount = function(...) {\n    list(\n      getApplication = function(...) list(title = \"newtitle\", url = \"newurl\")\n    )\n  })\n\n  syncAppMetadata(app)\n  deps <- deployments(app)\n  expect_equal(deps$title, \"newtitle\")\n  expect_equal(deps$url, \"newurl\")\n  expect_equal(deps$when, NULL)\n})\n\ntest_that(\"syncAppMetadata deletes deployment records if needed\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\")\n\n  app <- local_temp_app()\n  addTestDeployment(app, appId = \"123\", metadata = list(when = 123))\n  local_mocked_bindings(clientForAccount = function(...) {\n    list(\n      getApplication = function(...) abort(class = \"rsconnect_http_404\")\n    )\n  })\n\n  expect_snapshot(syncAppMetadata(app))\n  expect_equal(nrow(deployments(app)), 0)\n})\n"
  },
  {
    "path": "tests/testthat/test-bundle.R",
    "content": "test_that(\"simple Shiny app bundle is runnable\", {\n  skip_on_cran()\n  skip_if_not_installed(\"shiny\")\n  bundleTempDir <- local_shiny_bundle(\n    \"simple_shiny\",\n    test_path(\"shinyapp-simple\"),\n    NULL\n  )\n  expect_true(inherits(shiny::shinyAppDir(bundleTempDir), \"shiny.appobj\"))\n})\n\ntest_that(\"app.R Shiny app bundle is runnable\", {\n  skip_on_cran()\n  skip_if_not_installed(\"shiny\")\n\n  # shiny:::shinyAppDir() attach shiny so do it here so we can do it quietly\n  library(shiny, warn.conflicts = FALSE, quietly = TRUE)\n\n  bundleTempDir <- local_shiny_bundle(\n    \"app_r_shiny\",\n    test_path(\"shinyapp-appR\"),\n    NULL\n  )\n  expect_true(inherits(shiny::shinyAppDir(bundleTempDir), \"shiny.appobj\"))\n})\n\ntest_that(\"single-file Shiny app bundle is runnable\", {\n  skip_on_cran()\n  skip_if_not_installed(\"shiny\")\n\n  bundleTempDir <- local_shiny_bundle(\n    \"app_r_shiny\",\n    test_path(\"shinyapp-singleR\"),\n    \"single.R\"\n  )\n  expect_true(inherits(shiny::shinyAppDir(bundleTempDir), \"shiny.appobj\"))\n})\n\ntest_that(\"simple Rmd as primary not identified as parameterized when parameterized Rmd in bundle\", {\n  skip_on_cran()\n  bundleTempDir <- local_shiny_bundle(\n    \"rmd primary\",\n    test_path(\"test-rmds\"),\n    \"simple.Rmd\"\n  )\n  manifest <- jsonlite::fromJSON(file.path(bundleTempDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"simple.Rmd\")\n  expect_equal(manifest$metadata$has_parameters, FALSE)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"parameterized Rmd identified as parameterized when other Rmd in bundle\", {\n  skip_on_cran()\n  bundleTempDir <- local_shiny_bundle(\n    \"rmd primary\",\n    test_path(\"test-rmds\"),\n    \"parameterized.Rmd\"\n  )\n  manifest <- jsonlite::fromJSON(file.path(bundleTempDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"parameterized.Rmd\")\n  expect_equal(manifest$metadata$has_parameters, TRUE)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"primary doc can be inferred (and non-parameterized dispite an included parameterized\", {\n  skip_on_cran()\n  bundleTempDir <- local_shiny_bundle(\n    \"rmd primary\",\n    test_path(\"test-rmds\"),\n    NULL\n  )\n  manifest <- jsonlite::fromJSON(file.path(bundleTempDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"index.Rmd\")\n  expect_equal(manifest$metadata$has_parameters, FALSE)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Rmd with reticulate as a dependency includes python in the manifest\", {\n  skip_on_cran()\n  skip_if_not_installed(\"reticulate\")\n  python <- pythonPathOrSkip()\n\n  bundleTempDir <- local_shiny_bundle(\n    \"reticulated rmd\",\n    test_path(\"test-reticulate-rmds\"),\n    NULL,\n    python = python\n  )\n\n  manifest <- jsonlite::fromJSON(file.path(bundleTempDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"index.Rmd\")\n  expect_true(\"reticulate\" %in% names(manifest$packages))\n  expect_known_manifest_fields(manifest)\n  expect_true(file.exists(file.path(\n    bundleTempDir,\n    manifest$python$package_manager$package_file\n  )))\n})\n\ntest_that(\"Rmd with reticulate as an inferred dependency includes reticulate and python in the manifest\", {\n  skip_on_cran()\n  skip_if_not_installed(\"reticulate\")\n  python <- pythonPathOrSkip()\n\n  bundleTempDir <- local_shiny_bundle(\n    \"reticulated rmd\",\n    test_path(\"test-reticulate-rmds\"),\n    \"implicit.Rmd\",\n    python = python\n  )\n\n  manifest <- jsonlite::fromJSON(file.path(bundleTempDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"implicit.Rmd\")\n  expect_true(\"reticulate\" %in% names(manifest$packages))\n  expect_known_manifest_fields(manifest)\n  expect_true(file.exists(file.path(\n    bundleTempDir,\n    manifest$python$package_manager$package_file\n  )))\n})\n\ntest_that(\"Rmd without a python block doesn't include reticulate or python in the manifest\", {\n  skip_on_cran()\n\n  bundleTempDir <- local_shiny_bundle(\n    \"plain rmd\",\n    test_path(\"test-rmds\"),\n    \"simple.Rmd\",\n    python = NULL\n  )\n\n  manifest <- jsonlite::fromJSON(file.path(bundleTempDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"simple.Rmd\")\n  expect_false(\"reticulate\" %in% names(manifest$packages))\n  expect_equal(manifest$python, NULL)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Rmd without a python block doesn't include reticulate or python in the manifest even if python specified\", {\n  skip_on_cran()\n  skip_if_not_installed(\"reticulate\")\n  python <- pythonPathOrSkip()\n\n  bundleTempDir <- local_shiny_bundle(\n    \"plain rmd\",\n    test_path(\"test-rmds\"),\n    \"simple.Rmd\",\n    python = python\n  )\n\n  manifest <- jsonlite::fromJSON(file.path(bundleTempDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"simple.Rmd\")\n  expect_false(\"reticulate\" %in% names(manifest$packages))\n  expect_equal(manifest$python, NULL)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"tarImplementation: checks environment variable and option before using default\", {\n  tar_implementation <- function(option, envvar) {\n    withr::local_options(rsconnect.tar = option)\n    withr::local_envvar(RSCONNECT_TAR = envvar)\n    getTarImplementation()\n  }\n\n  # Environment variable only set should use environment varaible\n  expect_equal(tar_implementation(NULL, \"envvar\"), \"envvar\")\n\n  # Option only set should use option\n  expect_equal(tar_implementation(\"option\", NA), \"option\")\n\n  # Both environment variable and option set should use option\n  expect_equal(tar_implementation(\"option\", \"envvar\"), \"option\")\n\n  # Neither set should use \"internal\"\n  expect_equal(tar_implementation(NULL, NA), \"internal\")\n})\n\n# tweakRProfile -----------------------------------------------------------\n\ntest_that(\".Rprofile tweaked automatically\", {\n  dir <- withr::local_tempdir()\n  writeLines('source(\"renv/activate.R\")', file.path(dir, \".Rprofile\"))\n\n  bundled <- bundleAppDir(dir, list.files(dir, all.files = TRUE))\n  expect_match(\n    readLines(file.path(bundled, \".Rprofile\")),\n    \"Modified by rsconnect\",\n    all = FALSE\n  )\n})\n\ntest_that(\"bundleAppDir copies profile-specific renv.lock into bundle\", {\n  skip_if_not_installed(\"renv\")\n\n  app_dir <- local_temp_app(list(\"app.R\" = \"1 + 1\"))\n\n  # Create a default renv.lock and a profile-specific one\n  writeLines('{\"R\":{\"Version\":\"4.3.0\"}}', file.path(app_dir, \"renv.lock\"))\n  profile_dir <- file.path(app_dir, \"renv\", \"profiles\", \"dev\")\n  dir.create(profile_dir, recursive = TRUE)\n  writeLines(\n    '{\"R\":{\"Version\":\"4.4.0\"}}',\n    file.path(profile_dir, \"renv.lock\")\n  )\n\n  withr::local_envvar(RENV_PROFILE = \"dev\")\n  appFiles <- list.files(app_dir, all.files = TRUE, recursive = TRUE)\n  bundled <- bundleAppDir(app_dir, appFiles)\n\n  # The bundle should contain the profile-specific lockfile, not the default\n  lock_content <- readLines(file.path(bundled, \"renv.lock\"))\n  expect_match(lock_content, \"4.4.0\", all = FALSE)\n})\n\ntest_that(\".Rprofile without renv/packrt left as is\", {\n  lines <- c(\"1 + 1\", \"# Line 2\", \"library(foo)\")\n  path <- withr::local_tempfile(lines = lines)\n\n  tweakRProfile(path)\n  expect_equal(readLines(path), lines)\n})\n\ntest_that(\"removes renv/packrat activation\", {\n  path <- withr::local_tempfile(\n    lines = c(\n      \"# Line 1\",\n      'source(\"renv/activate.R\")',\n      \"# Line 3\",\n      'source(\"packrat/init.R\")',\n      \"# Line 5\"\n    )\n  )\n\n  expect_snapshot(\n    {\n      tweakRProfile(path)\n      writeLines(readLines(path))\n    },\n    transform = function(x) {\n      x <- gsub(\"on \\\\d{4}.+\", \"on <NOW>\", x)\n      x <- gsub(packageVersion(\"rsconnect\"), \"<VERSION>\", x, fixed = TRUE)\n      x\n    }\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-bundleFiles.R",
    "content": "# listDeploymentFiles ------------------------------------------------------\n\ntest_that(\"can read all files from directory\", {\n  dir <- local_temp_app(list(\"a.R\" = \"\", \"b.R\" = \"\"))\n  expect_equal(listDeploymentFiles(dir), c(\"a.R\", \"b.R\"))\n  expect_equal(listDeploymentFiles(dir, NULL, NULL), c(\"a.R\", \"b.R\"))\n\n  dir <- local_temp_app()\n  expect_snapshot(listDeploymentFiles(dir), error = TRUE)\n})\n\ntest_that(\"can read selected files from directory\", {\n  dir <- local_temp_app(list(\"a.R\" = \"\", \"b.R\" = \"\"))\n  expect_equal(listDeploymentFiles(dir, \"b.R\"), \"b.R\")\n  expect_snapshot(out <- listDeploymentFiles(dir, c(\"b.R\", \"c.R\")))\n  expect_equal(out, \"b.R\")\n  expect_snapshot(listDeploymentFiles(dir, character()), error = TRUE)\n})\n\ntest_that(\"can read selected files from manifest\", {\n  dir <- local_temp_app(list(\n    \"a.R\" = \"\",\n    \"b.R\" = \"\",\n    \"manifest\" = \"b.R\"\n  ))\n  expect_equal(\n    listDeploymentFiles(dir, appFileManifest = file.path(dir, \"manifest\")),\n    \"b.R\"\n  )\n\n  dir <- local_temp_app(list(\n    \"a.R\" = \"\",\n    \"b.R\" = \"\",\n    \"manifest\" = c(\"b.R\", \"c.R\")\n  ))\n  expect_snapshot(\n    out <- listDeploymentFiles(\n      dir,\n      appFileManifest = file.path(dir, \"manifest\")\n    ),\n  )\n  expect_equal(out, \"b.R\")\n\n  # errors if no matching files\n  dir <- local_temp_app(list(\n    \"manifest\" = \"\"\n  ))\n  expect_snapshot(\n    listDeploymentFiles(dir, appFileManifest = file.path(dir, \"manifest\")),\n    error = TRUE\n  )\n})\n\ntest_that(\"checks its inputs\", {\n  dir <- local_temp_app()\n  expect_snapshot(error = TRUE, {\n    listDeploymentFiles(dir)\n    listDeploymentFiles(dir, appFiles = \"a.R\", appFileManifest = \"b.R\")\n    listDeploymentFiles(dir, appFiles = 1)\n    listDeploymentFiles(dir, appFileManifest = \"doestexist\")\n  })\n})\n\n\n# listBundleFiles ---------------------------------------------------------\n\ntest_that(\"bundle directories are recursively enumerated\", {\n  dir <- withr::local_tempdir()\n\n  # tree that resembles the case from https://github.com/rstudio/rsconnect/issues/464\n  files <- c(\n    \"app.R\",\n    \"index.htm\",\n    \"models/abcd/a_b_pt1/a/b/c1/1.RDS\",\n    \"models/abcd/a_b_pt1/a/b/c1/2.RDS\",\n    \"models/abcd/a_b_pt1/a/b/c1/3.RDS\",\n    \"models/abcd/a_b_pt1/a/b/c1/4.RDS\",\n    \"models/abcd/a_b_pt1/a/b/c1/5.RDS\"\n  )\n  dirCreate(file.path(dir, \"models/abcd/a_b_pt1/a/b/c1/\"))\n  file.create(file.path(dir, files))\n\n  size <- sum(file.info(file.path(dir, files))$size)\n  result <- listBundleFiles(dir)\n\n  # Files are included in the list, count, and sizes, not directories.\n  # Paths are enumerated relative to the target directory, not absolute paths.\n  expect_identical(result$contents, files)\n  expect_equal(result$totalSize, size)\n  expect_equal(result$totalFiles, length(files))\n})\n\ntest_that(\"ignores RStudio files\", {\n  dir <- withr::local_tempdir()\n  file.create(file.path(dir, c(\"test.Rproj\", \".Rproj.user\", \"rsconnect\")))\n\n  expect_equal(bundleFiles(dir), character())\n})\n\ntest_that(\"ignores knitr cache directories\", {\n  dir <- withr::local_tempdir()\n  dirCreate(file.path(dir, c(\"foo_cache\", \"bar_cache\")))\n  file.create(file.path(dir, c(\"foo_cache\", \"bar_cache\"), \"contents\"))\n  file.create(file.path(dir, c(\"foo.Rmd\")))\n\n  expect_setequal(bundleFiles(dir), c(\"bar_cache/contents\", \"foo.Rmd\"))\n})\n\ntest_that(\"ignores files anywhere in path\", {\n  dir <- withr::local_tempdir()\n  dirCreate(file.path(dir, \"a/b/c\"))\n  file.create(file.path(dir, c(\"x\", \"a/b/.gitignore\", \"a/b/c/.DS_Store\")))\n\n  expect_equal(bundleFiles(dir), \"x\")\n})\n\ntest_that(\"ignores files listed in .rscignore\", {\n  dir <- local_temp_app()\n  dirCreate(file.path(dir, \"a\"))\n  file.create(file.path(dir, c(\"x\", \"a/y\")))\n  expect_setequal(bundleFiles(dir), c(\"x\", \"a/y\"))\n\n  writeLines(\"x\", file.path(dir, \".rscignore\"))\n  expect_setequal(bundleFiles(dir), \"a/y\")\n\n  writeLines(\"y\", file.path(dir, \"a/.rscignore\"))\n  expect_setequal(bundleFiles(dir), character())\n})\n\ntest_that(\"ignores temporary files\", {\n  ignored <- ignoreBundleFiles(\n    dir = \".\",\n    contents = c(\"foo.xlsx\", \"~$foo.xlsx\", \"foo.csv\", \"foo.csv~\")\n  )\n  expect_equal(ignored, c(\"foo.xlsx\", \"foo.csv\"))\n})\n\ntest_that(\"ignores Python virtual envs (non-Windows)\", {\n  dir <- withr::local_tempdir()\n\n  names <- c(\n    # well-known names ...\n    \".env\",\n    \".venv\",\n    \"venv\",\n\n    # other names ...\n    \"test\"\n  )\n\n  dirCreate(file.path(dir, names, \"bin\"))\n  file.create(file.path(dir, names, \"bin\", \"python\"))\n\n  expect_equal(bundleFiles(dir), character())\n})\n\ntest_that(\"ignores Python virtual envs (Windows)\", {\n  dir <- withr::local_tempdir()\n\n  names <- c(\n    # well-known names ...\n    \".env\",\n    \".venv\",\n    \"venv\",\n\n    # other names ...\n    \"test\"\n  )\n\n  dirCreate(file.path(dir, names, \"Scripts\"))\n  file.create(file.path(dir, names, \"Scripts\", \"python.exe\"))\n\n  expect_equal(bundleFiles(dir), character())\n})\n\ntest_that(\"ignores Python virtual envs (Windows-GUI)\", {\n  dir <- withr::local_tempdir()\n\n  names <- c(\n    # well-known names ...\n    \".env\",\n    \".venv\",\n    \"venv\",\n\n    # other names ...\n    \"test\"\n  )\n\n  dirCreate(file.path(dir, names, \"Scripts\"))\n  file.create(file.path(dir, names, \"Scripts\", \"pythonw.exe\"))\n\n  expect_equal(bundleFiles(dir), character())\n})\n\ntest_that(\"ignores Python virtual envs (Windows-debug)\", {\n  dir <- withr::local_tempdir()\n\n  names <- c(\n    # well-known names ...\n    \".env\",\n    \".venv\",\n    \"venv\",\n\n    # other names ...\n    \"test\"\n  )\n\n  dirCreate(file.path(dir, names, \"Scripts\"))\n  file.create(file.path(dir, names, \"Scripts\", \"pythond.exe\"))\n\n  expect_equal(bundleFiles(dir), character())\n})\n\ntest_that(\"preserves well-known names when not Python virtual environment\", {\n  dir <- withr::local_tempdir()\n  file.create(file.path(dir, c(\".env\", \".venv\", \"venv\")))\n\n  expect_setequal(bundleFiles(dir), c(\".env\", \".venv\", \"venv\"))\n})\n\n# explodeFiles ------------------------------------------------------------\n\ntest_that(\"returns relative paths\", {\n  dir <- withr::local_tempdir()\n  dirCreate(file.path(dir, \"x\"))\n  file.create(file.path(dir, \"x\", c(\"a\", \"b\", \"c\")))\n\n  expect_equal(explodeFiles(dir, \"x\"), c(\"x/a\", \"x/b\", \"x/c\"))\n})\n\ntest_that(\"drops drops non-existent files with warning\", {\n  dir <- withr::local_tempdir()\n  file.create(file.path(dir, c(\"a\", \"b\", \"c\")))\n\n  expect_snapshot(out <- explodeFiles(dir, c(\"a\", \"d\")))\n  expect_equal(out, \"a\")\n})\n\ntest_that(\"expands files and directory\", {\n  dir <- withr::local_tempdir()\n  dirCreate(file.path(dir, \"x\"))\n  file.create(file.path(dir, \"x\", c(\"a\", \"b\")))\n  file.create(file.path(dir, \"c\"))\n\n  expect_equal(explodeFiles(dir, c(\"x\", \"c\")), c(\"x/a\", \"x/b\", \"c\"))\n})\n\ntest_that(\"can include nested files/directories\", {\n  dir <- withr::local_tempdir()\n  dirCreate(file.path(dir, \"x\", \"y\"))\n  file.create(file.path(dir, \"x\", c(\"a\", \"b\", \"c\")))\n  file.create(file.path(dir, \"x\", \"y\", c(\"d\", \"e\")))\n\n  expect_equal(explodeFiles(dir, \"x/a\"), \"x/a\")\n  expect_equal(explodeFiles(dir, \"x/y\"), c(\"x/y/d\", \"x/y/e\"))\n  expect_equal(explodeFiles(dir, c(\"x/a\", \"x/y/d\")), c(\"x/a\", \"x/y/d\"))\n})\n\ntest_that(\"doesn't ignore user supplied files\", {\n  dir <- withr::local_tempdir()\n  dirCreate(file.path(dir, \"x\", \"y\"))\n  file.create(file.path(dir, \"x\", \"packrat\"))\n  file.create(file.path(dir, \"x\", \"y\", \"packrat\"))\n\n  expect_equal(explodeFiles(dir, \"x\"), c(\"x/packrat\", \"x/y/packrat\"))\n})\n\n# enforceBundleLimits -----------------------------------------------------\n\ntest_that(\"explodeFiles() and bundleFiles() both eagerly enforce limits\", {\n  dir <- withr::local_tempdir()\n  dirCreate(file.path(dir, c(\"a\", \"b\")))\n  file.create(file.path(dir, \"a\", letters))\n  file.create(file.path(dir, \"b\", letters))\n\n  withr::local_options(rsconnect.max.bundle.files = 1)\n\n  # there are 52 files total, so eagerly implies we stop after one directory\n  expect_error(explodeFiles(dir, c(\"a\", \"b\")), \"at least 2\")\n  expect_error(bundleFiles(dir), \"at least 2\")\n})\n\ntest_that(\"generate nicely formatted messages\", {\n  dir <- withr::local_tempdir()\n  file.create(file.path(dir, c(\"a\", \"b\")))\n  writeLines(letters, file.path(dir, \"c\"))\n\n  withr::local_options(\n    rsconnect.max.bundle.files = 1,\n    rsconnect.max.bundle.size = 5\n  )\n\n  expect_snapshot(\n    {\n      explodeFiles(dir, c(\"a\", \"b\"))\n      explodeFiles(dir, \"c\")\n    },\n    error = TRUE,\n    transform = function(x) {\n      x <- gsub(dir, \"<TEMPDIR>\", x, fixed = TRUE)\n      # file size is different on windows because of \\r\\n\n      x <- gsub(\"\\\\d{2,} bytes\", \"?? bytes\", x)\n      x\n    }\n  )\n})\n\n# detectLongNames ---------------------------------------------------------\n\ntest_that(\"detectLongNames produces informative warning if needed\", {\n  skip_on_os(\"windows\")\n\n  dir <- local_temp_app(c(\"a.r\" = \"\", \"b.r\" = \"\", \"c.r\" = \"\"))\n  expect_snapshot(detectLongNames(dir, 0))\n  expect_silent(detectLongNames(dir, Inf))\n})\n"
  },
  {
    "path": "tests/testthat/test-bundleNodejs.R",
    "content": "# inferNodejsInfo -----------------------------------------------------------\n# (mirrors test-bundlePython.R which tests getPython, inferPythonEnv, etc.)\n\ntest_that(\"inferNodejsInfo reads main field for entrypoint\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"server.js\"}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_equal(info$entrypoint, \"server.js\")\n})\n\ntest_that(\"inferNodejsInfo defaults to index.js when main is missing\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_equal(info$entrypoint, \"index.js\")\n})\n\ntest_that(\"inferNodejsInfo defaults to index.js when main is empty string\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"\"}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_equal(info$entrypoint, \"index.js\")\n})\n\ntest_that(\"inferNodejsInfo handles TypeScript entrypoint\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.ts\"}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_equal(info$entrypoint, \"app.ts\")\n})\n\ntest_that(\"inferNodejsInfo reads engines.node\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"engines\": {\"node\": \">=22.18.0\"}}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_equal(info$enginesNode, \">=22.18.0\")\n})\n\ntest_that(\"inferNodejsInfo returns NULL enginesNode when engines not set\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_null(info$enginesNode)\n})\n\ntest_that(\"inferNodejsInfo returns NULL enginesNode when engines has no node field\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"engines\": {\"npm\": \">=8.0.0\"}}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_null(info$enginesNode)\n})\n\ntest_that(\"inferNodejsInfo detects missing package-lock.json\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}'\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_false(info$hasLockfile)\n})\n\ntest_that(\"inferNodejsInfo detects present package-lock.json\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = \"{}\"\n  ))\n  info <- inferNodejsInfo(dir)\n  expect_true(info$hasLockfile)\n})\n\n# Node.js bundle file exclusions --------------------------------------------\n\ntest_that(\"node_modules is excluded from bundle files\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"app.js\" = \"\"\n  ))\n  dir.create(file.path(dir, \"node_modules\", \"express\"), recursive = TRUE)\n  file.create(file.path(dir, \"node_modules\", \"express\", \"index.js\"))\n\n  files <- bundleFiles(dir)\n  expect_false(any(grepl(\"node_modules\", files)))\n})\n\ntest_that(\".npm is excluded from bundle files\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"app.js\" = \"\"\n  ))\n  dir.create(file.path(dir, \".npm\"), recursive = TRUE)\n  file.create(file.path(dir, \".npm\", \"cache-file\"))\n\n  files <- bundleFiles(dir)\n  expect_false(any(grepl(\"\\\\.npm\", files)))\n})\n\ntest_that(\"node_modules excluded via listDeploymentFiles\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"app.js\" = \"\"\n  ))\n  dir.create(file.path(dir, \"node_modules\", \"express\"), recursive = TRUE)\n  file.create(file.path(dir, \"node_modules\", \"express\", \"index.js\"))\n\n  files <- listDeploymentFiles(dir)\n  expect_false(any(grepl(\"node_modules\", files)))\n  expect_true(\"package.json\" %in% files)\n  expect_true(\"app.js\" %in% files)\n})\n\ntest_that(\"deeply nested node_modules is excluded\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"app.js\" = \"\"\n  ))\n  dir.create(\n    file.path(dir, \"node_modules\", \"express\", \"node_modules\", \"qs\"),\n    recursive = TRUE\n  )\n  file.create(file.path(\n    dir,\n    \"node_modules\",\n    \"express\",\n    \"node_modules\",\n    \"qs\",\n    \"index.js\"\n  ))\n\n  files <- bundleFiles(dir)\n  expect_false(any(grepl(\"node_modules\", files)))\n})\n\ntest_that(\"non-nodejs files alongside package.json are included in bundle\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"app.js\" = \"\",\n    \"public/index.html\" = \"<html></html>\",\n    \"lib/utils.js\" = \"module.exports = {};\"\n  ))\n\n  files <- bundleFiles(dir)\n  expect_true(\"app.js\" %in% files)\n  expect_true(\"public/index.html\" %in% files)\n  expect_true(\"lib/utils.js\" %in% files)\n})\n"
  },
  {
    "path": "tests/testthat/test-bundlePackage.R",
    "content": "test_that(\"can snapshot deps with renv\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  app_dir <- local_temp_app(list(foo.R = \"library(foreign); library(MASS)\"))\n\n  expect_snapshot(pkgs <- bundlePackages(app_dir))\n\n  # renv is not a dependency\n  expect_named(pkgs, c(\"foreign\", \"MASS\"), ignore.order = TRUE)\n  expect_named(pkgs$foreign, c(\"Source\", \"Repository\", \"description\"))\n  expect_named(pkgs$MASS, c(\"Source\", \"Repository\", \"description\"))\n\n  # No renv lockfile left behind\n  expect_equal(list.files(app_dir), \"foo.R\")\n})\n\ntest_that(\"can snapshot deps with packrat (option)\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(rsconnect.packrat = TRUE)\n\n  app_dir <- local_temp_app(list(foo.R = \"library(foreign); library(MASS)\"))\n\n  expect_snapshot(pkgs <- bundlePackages(app_dir))\n\n  expect_named(pkgs, c(\"foreign\", \"MASS\"), ignore.order = TRUE)\n  expect_named(pkgs$foreign, c(\"Source\", \"Repository\", \"description\"))\n  expect_named(pkgs$MASS, c(\"Source\", \"Repository\", \"description\"))\n\n  # No packrat lockfile left behind\n  expect_equal(list.files(app_dir), \"foo.R\")\n})\n\ntest_that(\"can snapshot deps with packrat (env var)\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_envvar(RSCONNECT_PACKRAT = \"TRUE\")\n\n  app_dir <- local_temp_app(list(foo.R = \"library(foreign); library(MASS)\"))\n\n  expect_snapshot(pkgs <- bundlePackages(app_dir))\n\n  expect_named(pkgs, c(\"foreign\", \"MASS\"), ignore.order = TRUE)\n  expect_named(pkgs$foreign, c(\"Source\", \"Repository\", \"description\"))\n  expect_named(pkgs$MASS, c(\"Source\", \"Repository\", \"description\"))\n\n  # No packrat lockfile left behind\n  expect_equal(list.files(app_dir), \"foo.R\")\n})\n\ntest_that(\"can capture deps from renv lockfile\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(foo.R = \"library(foreign); library(MASS)\"))\n  renv::snapshot(app_dir, prompt = FALSE)\n\n  expect_snapshot(pkgs <- bundlePackages(app_dir))\n\n  # renv is included by the renv.lock\n  expect_named(pkgs, c(\"foreign\", \"MASS\", \"renv\"), ignore.order = TRUE)\n  expect_named(pkgs$foreign, c(\"Source\", \"Repository\", \"description\"))\n  expect_named(pkgs$MASS, c(\"Source\", \"Repository\", \"description\"))\n\n  # No renv directory left behind, but the renv.lock is preserved.\n  expect_equal(list.files(app_dir), c(\"foo.R\", \"renv.lock\"))\n})\n\ntest_that(\"can capture deps from renv lockfile in custom location (RENV_PATHS_LOCKFILE)\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(foo.R = \"library(foreign); library(MASS)\"))\n\n  # Create renv.lock in the standard location first\n\n  renv::snapshot(app_dir, prompt = FALSE)\n\n  # Move the lockfile to a custom location outside the app dir\n  custom_lock_dir <- withr::local_tempdir()\n  custom_lock_path <- file.path(custom_lock_dir, \"renv.lock\")\n  file.rename(file.path(app_dir, \"renv.lock\"), custom_lock_path)\n\n  # Set RENV_PATHS_LOCKFILE to point to the custom location\n  withr::local_envvar(RENV_PATHS_LOCKFILE = custom_lock_path)\n\n  # Should find the lockfile at the custom location and capture deps.\n  expect_snapshot(pkgs <- bundlePackages(app_dir))\n\n  # renv is included by the renv.lock\n  expect_named(pkgs, c(\"foreign\", \"MASS\", \"renv\"), ignore.order = TRUE)\n  expect_named(pkgs$foreign, c(\"Source\", \"Repository\", \"description\"))\n  expect_named(pkgs$MASS, c(\"Source\", \"Repository\", \"description\"))\n})\n\ntest_that(\"can capture deps from renv lockfile with renv profile\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(foo.R = \"library(foreign); library(MASS)\"))\n\n  # Set RENV_PROFILE so renv resolves the lockfile from the profile path\n  withr::local_envvar(RENV_PROFILE = \"testing\")\n  renv::snapshot(app_dir, prompt = FALSE)\n\n  # Should find the lockfile at the profile location and capture deps.\n  expect_snapshot(pkgs <- bundlePackages(app_dir))\n\n  # renv is included by the renv.lock\n  expect_named(pkgs, c(\"foreign\", \"MASS\", \"renv\"), ignore.order = TRUE)\n  expect_named(pkgs$foreign, c(\"Source\", \"Repository\", \"description\"))\n  expect_named(pkgs$MASS, c(\"Source\", \"Repository\", \"description\"))\n})\n\ntest_that(\"appDependencies finds lockfile with relative RENV_PATHS_LOCKFILE\", {\n  skip_on_cran()\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  # the lockfile lives at a custom path inside a directory (renv/), specified with\n  # RENV_PATHS_LOCKFILE to point to it with a relative path.\n  #\n  #   app_dir/\n  #     app.R\n  #     renv/\n  #       renv_custom_location.lock\n  app_dir <- local_temp_app(list(\n    \"app.R\" = \"library(foreign); library(MASS)\"\n  ))\n\n  # Snapshot first (writes to the standard renv.lock location), then\n  # move the lockfile into renv/ under a custom name.\n  # we can't just set `RENV_PATHS_LOCKFILE` and then snapshot because\n  # renv interprets the relative path wrt CWD, not the project dir.\n  renv::snapshot(app_dir, prompt = FALSE)\n  dir.create(file.path(app_dir, \"renv\"))\n  file.rename(\n    file.path(app_dir, \"renv.lock\"),\n    file.path(app_dir, \"renv\", \"renv_custom_location.lock\")\n  )\n\n  withr::local_envvar(\n    RENV_PATHS_LOCKFILE = \"renv/renv_custom_location.lock\"\n  )\n\n  withr::local_dir(app_dir)\n  deps <- appDependencies(app_dir)\n  expect_true(\"foreign\" %in% deps$Package)\n  expect_true(\"MASS\" %in% deps$Package)\n  # renv is in the lockfile, but not the app. So if it is in the deps,\n  #then we know we are using the lockfile.\n  expect_true(\"renv\" %in% deps$Package)\n})\n\ntest_that(\"can capture deps with packrat even when renv lockfile present\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_envvar(RSCONNECT_PACKRAT = \"TRUE\")\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(foo.R = \"library(foreign); library(MASS)\"))\n  renv::snapshot(app_dir, prompt = FALSE)\n\n  expect_snapshot(pkgs <- bundlePackages(app_dir))\n\n  # renv is not a dependency (not using renv.lock)\n  expect_named(pkgs, c(\"foreign\", \"MASS\"), ignore.order = TRUE)\n  expect_named(pkgs$foreign, c(\"Source\", \"Repository\", \"description\"))\n  expect_named(pkgs$MASS, c(\"Source\", \"Repository\", \"description\"))\n\n  # The (incoming) renv.lock is discarded, mirroring rsconnect pre 1.0.0.\n  expect_equal(list.files(app_dir), \"foo.R\")\n})\n\n# -------------------------------------------------------------------------\n\ntest_that(\"error if can't find source\", {\n  local_mocked_bindings(snapshotRenvDependencies = function(...) {\n    data.frame(\n      Package = \"shiny\",\n      Source = NA,\n      Repository = NA,\n      stringsAsFactors = FALSE\n    )\n  })\n\n  app_dir <- withr::local_tempdir()\n  writeLines(\n    con = file.path(app_dir, \"index.Rmd\"),\n    c(\n      \"```{r}\",\n      \"library(shiny)\",\n      \"```\"\n    )\n  )\n\n  expect_snapshot(\n    . <- bundlePackages(app_dir),\n    error = TRUE\n  )\n})\n\ntest_that(\"usePackrat respects the environment variable\", {\n  expect_false(usePackrat())\n\n  withr::local_envvar(RSCONNECT_PACKRAT = \"TRUE\")\n  expect_true(usePackrat())\n\n  withr::local_envvar(RSCONNECT_PACKRAT = \"FALSE\")\n  expect_false(usePackrat())\n})\n\ntest_that(\"usePackrat respects the option\", {\n  expect_false(usePackrat())\n\n  withr::local_options(rsconnect.packrat = TRUE)\n  expect_true(usePackrat())\n\n  withr::local_options(rsconnect.packrat = FALSE)\n  expect_false(usePackrat())\n})\n\ntest_that(\"usePackrat prefers the environment variable over the option\", {\n  expect_false(usePackrat())\n\n  withr::local_envvar(RSCONNECT_PACKRAT = \"TRUE\")\n  withr::local_options(rsconnect.packrat = FALSE)\n  expect_true(usePackrat())\n\n  withr::local_envvar(RSCONNECT_PACKRAT = \"FALSE\")\n  withr::local_options(rsconnect.packrat = TRUE)\n  expect_false(usePackrat())\n})\n\ntest_that(\"package_record works\", {\n  latin1Record <- package_record(\n    \"latin1package\",\n    lib_dir = test_path(\"packages\")\n  )\n  expect_equal(latin1Record$Author, \"Jens Fröhling\")\n\n  utf8Record <- package_record(\"utf8package\", lib_dir = test_path(\"packages\"))\n  expect_equal(utf8Record$Author, \"Jens Fröhling\")\n\n  windows1251Record <- package_record(\n    \"windows1251package\",\n    lib_dir = test_path(\"packages\")\n  )\n  expect_equal(windows1251Record$Author, \"Сергей Брин\")\n})\n"
  },
  {
    "path": "tests/testthat/test-bundlePackagePackrat.R",
    "content": "test_that(\"non-R apps don't have packages\", {\n  app_dir <- local_temp_app(list(index.html = \"\"))\n  out <- snapshotPackratDependencies(app_dir)\n  expect_equal(out, data.frame())\n})\n\ntest_that(\"manifest has correct data types\", {\n  app <- local_temp_app(list(\"index.Rmd\" = \"\"))\n\n  deps <- snapshotPackratDependencies(app)\n  expect_type(deps$description, \"list\")\n  expect_type(deps$description[[1]], \"list\")\n})\n\ntest_that(\"uninstalled packages error\", {\n  app <- local_temp_app(list(\n    \"index.Rmd\" = c(\n      \"```{r}\",\n      \"library(doesntexist1)\",\n      \"library(doesntexist2)\",\n      \"```\"\n    )\n  ))\n  expect_snapshot(\n    snapshotPackratDependencies(app),\n    error = TRUE,\n    transform = function(x) gsub('\"', \"'\", x, fixed = TRUE)\n  )\n})\n\ntest_that(\"recommended packages are snapshotted\", {\n  skip_if_not_installed(\"MASS\")\n  app <- local_temp_app(list(\n    \"index.Rmd\" = c(\n      \"```{r}\",\n      \"library(MASS)\",\n      \"```\"\n    )\n  ))\n  deps <- snapshotPackratDependencies(app)\n  expect_true(\"MASS\" %in% deps$Package)\n})\n\ntest_that(\"works with BioC packages\", {\n  skip_on_cran()\n  skip_on_ci()\n  skip_if_not_installed(\"Biobase\")\n  app <- local_temp_app(list(\n    \"index.Rmd\" = c(\n      \"```{r}\",\n      \"library(Biobase)\",\n      \"```\"\n    )\n  ))\n  withr::local_options(\n    repos = c(\n      CRAN = \"https://cran.rstudio.com\",\n      BioC = \"https://bioconductor.org/packages/release/bioc\"\n    )\n  )\n\n  deps <- snapshotPackratDependencies(app)\n\n  Biobase <- deps[deps$Package == \"Biobase\", ]\n  expect_equal(Biobase$Source, \"Bioconductor\")\n  expect_equal(\n    Biobase$Repository,\n    \"https://bioconductor.org/packages/release/bioc\"\n  )\n\n  BiocGenerics <- deps[deps$Package == \"BiocGenerics\", ]\n  expect_equal(BiocGenerics$Source, \"Bioconductor\")\n  expect_equal(\n    BiocGenerics$Repository,\n    \"https://bioconductor.org/packages/release/bioc\"\n  )\n})\n\n# addPackratSnapshot() ----------------------------------------------------\n\ntest_that(\"cleans up implicit dependency files\", {\n  dir <- withr::local_tempdir()\n  addPackratSnapshot(dir, \"rlang\")\n  expect_equal(list.files(dir), \"packrat\")\n})\n\n# standardizePackratPackage -----------------------------------------\n\ntest_that(\"standardizeRepos adds names and normalizes paths\", {\n  repos <- c(ONE = \"https://cran1.com\", \"https://cran2.com/\")\n  expect_equal(\n    standardizeRepos(repos),\n    c(ONE = \"https://cran1.com\", repo_2 = \"https://cran2.com\")\n  )\n})\n\ntest_that(\"SCM records are left alone\", {\n  bitbucket <- list(Package = \"pkg\", Source = \"bitbucket\")\n  gitlab <- list(Package = \"pkg\", Source = \"gitlab\")\n  github <- list(Package = \"pkg\", Source = \"github\")\n\n  expect_equal(\n    standardizePackratPackage(bitbucket),\n    list(Source = \"bitbucket\", Repository = NA_character_)\n  )\n  expect_equal(\n    standardizePackratPackage(gitlab),\n    list(Source = \"gitlab\", Repository = NA_character_)\n  )\n  expect_equal(\n    standardizePackratPackage(github),\n    list(Source = \"github\", Repository = NA_character_)\n  )\n})\n\ntest_that(\"CRAN & BioC get normalized repo\", {\n  CRAN <- list(Package = \"pkg1\", Source = \"CRAN\")\n  Bioconductor <- list(Package = \"pkg2\", Source = \"Bioconductor\")\n\n  packages <- data.frame(\n    Package = c(\"pkg1\", \"pkg2\", \"pkg3\"),\n    Repository = paste0(\n      c(\"https://a.com\", \"https://b.com\", \"https://cran.com\"),\n      \"/src/contrib\"\n    ),\n    stringsAsFactors = FALSE\n  )\n\n  expect_equal(\n    standardizePackratPackage(CRAN, packages),\n    list(Source = \"CRAN\", Repository = \"https://a.com\")\n  )\n  expect_equal(\n    standardizePackratPackage(Bioconductor, packages),\n    list(Source = \"Bioconductor\", Repository = \"https://b.com\")\n  )\n})\n\ntest_that(\"packages installed from other repos get correctly named\", {\n  pkg <- list(Package = \"pkg\", Source = \"https://test2.com\")\n  packages <- as.matrix(data.frame(\n    Package = \"pkg\",\n    Version = \"1.0.0\",\n    Repository = \"https://test2.com/src/contrib\",\n    stringsAsFactors = FALSE\n  ))\n  repos <- c(TEST1 = \"https://test1.com\", TEST2 = \"https://test2.com\")\n\n  expect_equal(\n    standardizePackratPackage(pkg, packages, repos = repos),\n    list(Source = \"TEST2\", Repository = \"https://test2.com\")\n  )\n})\n\ntest_that(\"source packages can't be installed\", {\n  source <- list(Package = \"pkg1\", Source = \"source\")\n  expect_equal(\n    standardizePackratPackage(source),\n    list(Source = NA_character_, Repository = NA_character_)\n  )\n})\n\ntest_that(\"locally installed CRAN packages are handled correctly\", {\n  packages <- as.matrix(data.frame(\n    Package = \"pkg\",\n    Version = \"1.0.0\",\n    Repository = \"https://cran.com/src/contrib\",\n    stringsAsFactors = FALSE\n  ))\n  repos <- c(CRAN = \"https://cran.com\")\n\n  local <- list(\n    Package = \"pkg\",\n    Source = \"CustomCRANLikeRepository\",\n    Version = \"1.0.0\"\n  )\n  expect_equal(\n    standardizePackratPackage(local, packages, repos),\n    list(Source = \"CRAN\", Repository = \"https://cran.com\")\n  )\n\n  local_dev <- list(\n    Package = \"pkg\",\n    Source = \"CustomCRANLikeRepository\",\n    Version = \"1.0.0.9000\"\n  )\n  expect_equal(\n    standardizePackratPackage(local_dev, packages, repos),\n    list(Source = NA_character_, Repository = NA_character_)\n  )\n\n  archived <- list(\n    Package = \"pkg2\",\n    Source = \"CustomCRANLikeRepository\",\n    Version = \"1.0.0\"\n  )\n  expect_equal(\n    standardizePackratPackage(archived, packages, repos),\n    list(Source = NA_character_, Repository = NA_character_)\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-bundlePackageRenv.R",
    "content": "# snapshotRenvDependencies() ----------------------------------------------\n\ntest_that(\"non-R apps don't have packages\", {\n  skip_on_cran()\n  app_dir <- local_temp_app(list(index.html = \"\"))\n  out <- snapshotRenvDependencies(app_dir)\n  expect_equal(out, data.frame())\n})\n\ntest_that(\"manifest has correct data types\", {\n  skip_on_cran()\n  withr::local_options(renv.verbose = TRUE)\n  app <- local_temp_app(list(\"index.Rmd\" = \"\"))\n  deps <- snapshotRenvDependencies(app)\n  expect_type(deps$description, \"list\")\n  expect_type(deps$description[[1]], \"list\")\n})\n\ntest_that(\"recommended packages are snapshotted\", {\n  skip_on_cran()\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = TRUE)\n  app <- local_temp_app(list(\n    \"index.Rmd\" = c(\n      \"```{r}\",\n      \"library(MASS)\",\n      \"```\"\n    )\n  ))\n  deps <- snapshotRenvDependencies(app)\n  expect_true(\"MASS\" %in% deps$Package)\n})\n\ntest_that(\"extra packages are snapshotted\", {\n  skip_on_cran()\n  skip_if_not_installed(\"foreign\")\n\n  withr::local_options(renv.verbose = TRUE)\n  app <- local_temp_app(list(\"index.Rmd\" = \"\"))\n  deps <- snapshotRenvDependencies(app, extraPackages = c(\"foreign\"))\n  expect_true(\"foreign\" %in% deps$Package)\n})\n\ntest_that(\"works with BioC packages\", {\n  skip_on_cran()\n  skip_on_ci()\n  skip_if_not_installed(\"BiocManager\")\n  skip_if_not_installed(\"Biobase\")\n\n  app <- local_temp_app(list(\n    \"index.R\" = c(\n      \"library(Biobase)\"\n    )\n  ))\n  biocRepos <- BiocManager::repositories()\n  withr::local_options(repos = biocRepos)\n  expect_no_condition(\n    {\n      deps <- snapshotRenvDependencies(app)\n    },\n    class = \"rsconnect_biocRepos\"\n  )\n  Biobase <- deps[deps$Package == \"Biobase\", ]\n  expect_equal(Biobase$Source, \"Bioconductor\")\n  expect_equal(Biobase$Repository, biocRepos[[\"BioCsoft\"]])\n\n  withr::local_options(\n    repos = c(\n      CRAN = \"https://cran.rstudio.com\"\n    )\n  )\n  expect_condition(\n    deps <- snapshotRenvDependencies(app),\n    class = \"rsconnect_biocRepos\"\n  )\n\n  Biobase <- deps[deps$Package == \"Biobase\", ]\n  expect_equal(Biobase$Source, \"Bioconductor\")\n  expect_equal(Biobase$Repository, biocRepos(\".\")[[1]])\n})\n\n# https://github.com/rstudio/rsconnect/issues/968\ntest_that(\"large directories are analyzed\", {\n  skip_on_cran()\n  skip_if_not_installed(\"foreign\")\n\n  app_dir <- local_temp_app(list(\"foo.R\" = \"library(foreign)\"))\n  data_dir <- file.path(app_dir, \"data\")\n  dir.create(data_dir)\n  for (each in seq_len(1001)) {\n    writeLines(character(0), file.path(data_dir, paste0(each, \".txt\")))\n  }\n  expect_snapshot(\n    deps <- snapshotRenvDependencies(app_dir)\n  )\n  expect_contains(deps$Package, \"foreign\")\n})\n\ntest_that(\"errors when renv::snapshot fails\", {\n  local_mocked_bindings(\n    dependencies = function(...) data.frame(Package = \"fakepkg\"),\n    snapshot = function(...) stop(\"can't snapshot this package\"),\n    .package = \"renv\"\n  )\n\n  app_dir <- local_temp_app(list(\"foo.R\" = \"library(foreign)\"))\n  expect_snapshot(\n    snapshotRenvDependencies(app_dir),\n    error = TRUE\n  )\n})\n\n# parseRenvDependencies ---------------------------------------------------\n\ntest_that(\"gets DESCRIPTION from renv & system libraries\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(\"foo.R\" = \"library(foreign); library(MASS)\"))\n  renv::snapshot(app_dir, prompt = FALSE)\n\n  deps <- parseRenvDependencies(file.path(app_dir, \"renv.lock\"), app_dir)\n  expect_setequal(deps$Package, c(\"foreign\", \"MASS\", \"renv\"))\n\n  expect_type(deps$description, \"list\")\n  expect_type(deps$description[[which(deps$Package == \"foreign\")]], \"list\")\n  expect_type(deps$description[[which(deps$Package == \"MASS\")]], \"list\")\n})\n\n\ntest_that(\"errors if library and project are inconsistent\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(\"foo.R\" = \"library(foreign); library(MASS)\"))\n  renv::snapshot(app_dir, prompt = FALSE)\n  renv::record(\"MASS@0.1.1\", project = app_dir)\n\n  expect_snapshot(\n    parseRenvDependencies(file.path(app_dir, \"renv.lock\"), app_dir),\n    error = TRUE\n  )\n})\n\ntest_that(\"dependencyResolution = 'library' bypasses lockfile and uses local library\", {\n  skip_on_cran()\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(\"foo.R\" = \"library(MASS)\"))\n  renv::snapshot(app_dir, prompt = FALSE)\n  renv::record(\"MASS@0.1.1\", project = app_dir)\n\n  # Without dependencyResolution = \"library\", this would error with \"out of sync\"\n  deps <- computePackageDependencies(\n    app_dir,\n    quiet = TRUE,\n    dependencyResolution = \"library\"\n  )\n  expect_true(\"MASS\" %in% deps$Package)\n  mass_dep <- deps[deps$Package == \"MASS\", ]\n  expect_true(mass_dep$Version != \"0.1.1\")\n})\n\n# standardizeRenvPackage -----------------------------------------\n\ntest_that(\"SCM get names translated\", {\n  bitbucket <- list(Package = \"pkg\", Source = \"Bitbucket\")\n  gitlab <- list(Package = \"pkg\", Source = \"GitLab\")\n  github <- list(Package = \"pkg\", Source = \"GitHub\")\n\n  expect_equal(\n    standardizeRenvPackage(bitbucket),\n    list(Package = \"pkg\", Source = \"bitbucket\")\n  )\n  expect_equal(\n    standardizeRenvPackage(gitlab),\n    list(Package = \"pkg\", Source = \"gitlab\")\n  )\n  expect_equal(\n    standardizeRenvPackage(github),\n    list(Package = \"pkg\", Source = \"github\")\n  )\n})\n\ntest_that(\"BioC gets normalized repo\", {\n  Bioconductor <- list(Package = \"pkg\", Source = \"Bioconductor\")\n\n  packages <- data.frame(\n    Package = \"pkg\",\n    Repository = \"https://b.com/src/contrib\",\n    stringsAsFactors = FALSE\n  )\n\n  expect_equal(\n    standardizeRenvPackage(Bioconductor, packages),\n    list(Package = \"pkg\", Source = \"Bioconductor\", Repository = \"https://b.com\")\n  )\n})\n\ntest_that(\"BioC prefers biocPackages over availablePackages (#1314)\", {\n  bioc_pkg <- list(Package = \"S4Vectors\", Source = \"Bioconductor\")\n\n  avail <- data.frame(\n    Package = \"S4Vectors\",\n    Repository = \"https://cran.rstudio.com/src/contrib/Transit\",\n    stringsAsFactors = FALSE\n  )\n\n  bioc <- data.frame(\n    Package = \"S4Vectors\",\n    Repository = \"https://bioconductor.org/packages/3.22/bioc/src/contrib\",\n    stringsAsFactors = FALSE\n  )\n\n  result <- standardizeRenvPackage(bioc_pkg, avail, biocPackages = bioc)\n  expect_equal(result$Repository, \"https://bioconductor.org/packages/3.22/bioc\")\n})\n\ntest_that(\"has special handling for CRAN packages\", {\n  packages <- as.matrix(data.frame(\n    Package = \"pkg\",\n    Version = \"1.0.0\",\n    Repository = \"https://cran.com/src/contrib\",\n    stringsAsFactors = FALSE\n  ))\n  repos <- c(CRAN = \"https://cran.com\")\n\n  spec <- function(version, source = \"Repository\", repo = \"CRAN\") {\n    list(Package = \"pkg\", Version = version, Source = source, Repository = repo)\n  }\n\n  expect_equal(\n    standardizeRenvPackage(spec(\"1.0.0\"), packages, repos),\n    spec(\"1.0.0\", \"CRAN\", \"https://cran.com\")\n  )\n\n  expect_equal(\n    standardizeRenvPackage(spec(\"1.0.0.9000\"), packages, repos),\n    spec(\"1.0.0.9000\", NA_character_, NA_character_)\n  )\n})\n\ntest_that(\"packages installed from other repos get correctly named\", {\n  pkg <- list(\n    Package = \"pkg\",\n    Source = \"Repository\",\n    Repository = \"https://test2.com\"\n  )\n  packages <- as.matrix(data.frame(\n    Package = \"pkg\",\n    Version = \"1.0.0\",\n    Repository = \"https://test2.com/src/contrib\",\n    stringsAsFactors = FALSE\n  ))\n  repos <- c(TEST1 = \"https://test1.com\", TEST2 = \"https://test2.com\")\n\n  expect_equal(\n    standardizeRenvPackage(pkg, packages, repos = repos),\n    list(Package = \"pkg\", Source = \"TEST2\", Repository = \"https://test2.com\")\n  )\n})\n\ntest_that(\"packages available in multiple repos use the one from renv.lock\", {\n  # When a package is available in multiple repositories,\n  # standardizeRenvPackage should respect the Repository field from renv.lock\n  # rather than just using the first match from availablePackages\n\n  # Simulate availablePackages with both packages available from both repos\n  # Note: available.packages() returns first match with \"duplicates\" filter\n  packages <- rbind(\n    c(\n      Package = \"pkg1\",\n      Version = \"1.0.0\",\n      Repository = \"https://cran.com/src/contrib\"\n    ),\n    c(\n      Package = \"pkg2\",\n      Version = \"2.0.0\",\n      Repository = \"https://cran.com/src/contrib\"\n    ),\n    c(\n      Package = \"pkg1\",\n      Version = \"1.0.0\",\n      Repository = \"https://og-cran.com/src/contrib\"\n    ),\n    c(\n      Package = \"pkg2\",\n      Version = \"2.0.0\",\n      Repository = \"https://og-cran.com/src/contrib\"\n    )\n  )\n\n  repos <- c(CRAN = \"https://cran.com\", OG_CRAN = \"https://og-cran.com\")\n\n  # pkg1 should come from OG_CRAN (as specified in renv.lock)\n  pkg1 <- list(\n    Package = \"pkg1\",\n    Version = \"1.0.0\",\n    Source = \"Repository\",\n    Repository = \"OG_CRAN\"\n  )\n\n  # pkg2 should come from CRAN (as specified in renv.lock)\n  pkg2 <- list(\n    Package = \"pkg2\",\n    Version = \"2.0.0\",\n    Source = \"Repository\",\n    Repository = \"CRAN\"\n  )\n\n  expect_equal(\n    standardizeRenvPackage(pkg1, packages, repos = repos),\n    list(\n      Package = \"pkg1\",\n      Version = \"1.0.0\",\n      Source = \"OG_CRAN\",\n      Repository = \"https://og-cran.com\"\n    )\n  )\n\n  expect_equal(\n    standardizeRenvPackage(pkg2, packages, repos = repos),\n    list(\n      Package = \"pkg2\",\n      Version = \"2.0.0\",\n      Source = \"CRAN\",\n      Repository = \"https://cran.com\"\n    )\n  )\n})\n\ntest_that(\"unknown source packages get NA source + repository\", {\n  source <- list(Package = \"pkg\", Source = \"unknown\", Repository = \"useless\")\n  expect_equal(\n    standardizeRenvPackage(source),\n    list(Package = \"pkg\", Source = NA_character_, Repository = NA_character_)\n  )\n})\n\ntest_that(\"Local packages resolve from a configured repo when available\", {\n  pkg <- list(Package = \"pkg\", Version = \"1.0.0\", Source = \"Local\")\n\n  packages <- as.matrix(data.frame(\n    Package = \"pkg\",\n    Version = \"1.0.0\",\n    Repository = \"https://cran.com/src/contrib\",\n    stringsAsFactors = FALSE\n  ))\n  repos <- c(CRAN = \"https://cran.com\")\n\n  expect_equal(\n    standardizeRenvPackage(pkg, packages, repos = repos),\n    list(\n      Package = \"pkg\",\n      Version = \"1.0.0\",\n      Source = \"CRAN\",\n      Repository = \"https://cran.com\"\n    )\n  )\n})\n\ntest_that(\"Local packages get NA source + repository when not in any repo\", {\n  pkg <- list(Package = \"pkg\", Version = \"1.0.0\", Source = \"Local\")\n\n  packages <- as.matrix(data.frame(\n    Package = \"otherpkg\",\n    Version = \"1.0.0\",\n    Repository = \"https://cran.com/src/contrib\",\n    stringsAsFactors = FALSE\n  ))\n  repos <- c(CRAN = \"https://cran.com\")\n\n  expect_equal(\n    standardizeRenvPackage(pkg, packages, repos = repos),\n    list(\n      Package = \"pkg\",\n      Version = \"1.0.0\",\n      Source = NA_character_,\n      Repository = NA_character_\n    )\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-bundlePython.R",
    "content": "test_that(\"getPython looks in argument, RETICULATE_PYTHON, then RETICULATE_PYTHON_FALLBACK\", {\n  skip_on_cran()\n\n  withr::local_envvar(\n    RETICULATE_PYTHON = \"~/python\",\n    RETICULATE_PYTHON_FALLBACK = \"~/fallback\"\n  )\n  expect_equal(getPython(\"~/supplied\"), path.expand(\"~/supplied\"))\n  expect_equal(getPython(NULL), path.expand(\"~/python\"))\n\n  withr::local_envvar(\n    RETICULATE_PYTHON = NA,\n    RETICULATE_PYTHON_FALLBACK = \"~/fallback\"\n  )\n  expect_equal(getPython(NULL), path.expand(\"~/fallback\"))\n\n  withr::local_envvar(\n    RETICULATE_PYTHON = NA,\n    RETICULATE_PYTHON_FALLBACK = NA\n  )\n  expect_equal(getPython(NULL), NULL)\n})\n\ntest_that(\"rsconnect.python.enabled overrides getPythonForTarget() default\", {\n  skip_on_cran()\n\n  expect_equal(getPythonForTarget(\"p\", list(server = \"shinyapps.io\")), NULL)\n  expect_equal(getPythonForTarget(\"p\", list(server = \"example.com\")), \"p\")\n\n  withr::local_options(rsconnect.python.enabled = FALSE)\n  expect_equal(getPythonForTarget(\"p\", list(server = \"shinyapps.io\")), NULL)\n  expect_equal(getPythonForTarget(\"p\", list(server = \"example.com\")), NULL)\n\n  withr::local_options(rsconnect.python.enabled = TRUE)\n  expect_equal(getPythonForTarget(\"p\", list(server = \"shinyapps.io\")), \"p\")\n  expect_equal(getPythonForTarget(\"p\", list(server = \"example.com\")), \"p\")\n})\n\ntest_that(\"can infer env from existing directory\", {\n  skip_on_cran()\n\n  env <- inferPythonEnv(test_path(\"test-reticulate-rmds\"), pythonPathOrSkip())\n  expect_named(env, c(\"version\", \"requires\", \"package_manager\"))\n  expect_named(\n    env$package_manager,\n    c(\"name\", \"version\", \"package_file\", \"contents\")\n  )\n})\n\n\ntest_that(\"doesn't override existing requirements.txt by default\", {\n  skip_on_cran()\n\n  dir <- local_temp_app(list(requirements.txt = \"pip\"))\n  env <- inferPythonEnv(dir, pythonPathOrSkip())\n  expect_equal(env$package_manager$contents, \"pip\\n\")\n\n  env <- inferPythonEnv(dir, pythonPathOrSkip(), forceGenerate = TRUE)\n  expect_match(env$package_manager$contents, \"generated by rsconnect-python\")\n})\n\ntest_that(\"throws error if environment.py fails\", {\n  skip_on_cran()\n  skip_on_os(\"windows\")\n\n  dir <- local_temp_app(list(requirements.txt = \"\\\\\"))\n  Sys.chmod(file.path(dir, \"requirements.txt\"), \"000\")\n\n  withr::local_dir(dir)\n  expect_snapshot(inferPythonEnv(\".\", pythonPathOrSkip()), error = TRUE)\n})\n\ntest_that(\"warning is shown for environment.py errors discovering python version requirements\", {\n  skip_on_cran()\n  skip_on_os(\"windows\")\n\n  dir <- local_temp_app(list(\n    requirements.txt = \"pip\",\n    \".python-version\" = \"broken$syntax=]\"\n  ))\n  Sys.chmod(file.path(dir, \".python-version\"), \"000\")\n\n  expect_warning(inferPythonEnv(dir, pythonPathOrSkip()), \"Permission denied\")\n})\n"
  },
  {
    "path": "tests/testthat/test-cert.R",
    "content": "test_that(\"system and server cert stores are concatenated\", {\n  local_temp_config()\n\n  serverCertificateFile <- test_path(\"certs/localhost.pem\")\n  serverCertificate <- paste(\n    c(\n      # this in-memory certificate has duplication, which\n      # is removed in the concatenated result.\n      readLines(con = serverCertificateFile, warn = FALSE),\n      readLines(con = serverCertificateFile, warn = FALSE)\n    ),\n    collapse = \"\\n\"\n  )\n\n  caCertificateFile <- test_path(\"certs/example.com.pem\")\n\n  withr::local_options(rsconnect.ca.bundle = caCertificateFile)\n\n  # create and then read the temporary certificate file\n  concatenated <- createCertificateFile(serverCertificate)\n  withr::defer(unlink(concatenated))\n\n  # the result is the concatenation (ca first) without duplicates.\n  expect_equal(\n    openssl::read_cert_bundle(concatenated),\n    openssl::read_cert_bundle(\n      paste0(\n        sapply(\n          c(\n            openssl::read_cert_bundle(caCertificateFile),\n            openssl::read_cert_bundle(serverCertificateFile)\n          ),\n          openssl::write_pem\n        ),\n        collapse = \"\"\n      )\n    )\n  )\n})\n\ntest_that(\"invalid certificates cannot be added\", {\n  local_temp_config()\n\n  expect_error(addTestServer(\n    url = \"https://localhost:4567/\",\n    name = \"cert_test_e\",\n    certificate = test_path(\"certs/invalid.crt\")\n  ))\n})\n\ntest_that(\"certificates not used when making plain http connections\", {\n  local_temp_config()\n  expect_null(requestCertificate(\"http\", test_path(\"certs/localhost.pem\")))\n})\n\ntest_that(\"certificates used when making https connections\", {\n  local_temp_config()\n  cert <- requestCertificate(\"https\", test_path(\"certs/localhost.pem\"))\n  # we expect to get a cert file\n  expect_true(file.exists(cert))\n\n  # clean up\n  unlink(cert)\n})\n"
  },
  {
    "path": "tests/testthat/test-client-connect.R",
    "content": "test_that(\"leading timestamps are stripped\", {\n  expect_snapshot(\n    stripConnectTimestamps(\n      c(\n        \"2024/04/24 13:08:04.901698921 [rsc-session] Content GUID: 3bfbd98a-6d6d-41bd-a15f-cab52025742f\",\n        \"2024/04/24 13:08:04.901734307 [rsc-session] Content ID: 43888\",\n        \"2024/04/24 13:08:04.901742487 [rsc-session] Bundle ID: 94502\",\n        \"2024/04/24 13:08:04.901747536 [rsc-session] Variant ID: 6465\"\n      )\n    )\n  )\n})\n\ntest_that(\"non-leading timestamps remain\", {\n  expect_snapshot(\n    stripConnectTimestamps(\n      c(\n        \"this message has a timestamp 2024/04/24 13:08:04.901698921 within a line\"\n      )\n    )\n  )\n})\n\ntest_that(\"messages without recognized timestamps are unmodified\", {\n  expect_snapshot(\n    stripConnectTimestamps(\n      c(\n        \"this message has no timestamp\",\n        \"2024/04/24 13:08 this message timestamp has a different format\"\n      )\n    )\n  )\n})\n\ntest_that(\"waitForTask\", {\n  skip_if_not_installed(\"webfakes\")\n\n  task_app <- webfakes::new_app()\n  task_app$use(webfakes::mw_json())\n  task_app$get(\"/v1/tasks/:id\", function(req, res) {\n    res$set_status(200L)$send_json(\n      list(\n        id = I(req$params$id),\n        user_id = I(42),\n        output = c(\n          \"2024/04/24 13:08:04.901698921 [rsc-session] Content GUID: 3bfbd98a-6d6d-41bd-a15f-cab52025742f\",\n          \"2024/04/24 13:08:04.901734307 [rsc-session] Content ID: 43888\",\n          \"2024/04/24 13:08:04.901742487 [rsc-session] Bundle ID: 94502\",\n          \"2024/04/24 13:08:04.901747536 [rsc-session] Variant ID: 6465\"\n        ),\n        result = NULL,\n        finished = TRUE,\n        code = 0,\n        error = \"\",\n        last = 4\n      ),\n      auto_unbox = TRUE\n    )\n  })\n  app <- webfakes::new_app_process(task_app)\n  service <- parseHttpUrl(app$url())\n\n  authInfo <- list(\n    secret = NULL,\n    private_key = NULL,\n    apiKey = \"the-api-key\",\n    protocol = \"https\",\n    certificate = NULL\n  )\n  client <- connectClient(service, authInfo)\n\n  # task messages are logged when not quiet.\n  expect_snapshot(invisible(client$waitForTask(101, quiet = FALSE)))\n  # task messages are not logged when quiet.\n  expect_snapshot(invisible(client$waitForTask(42, quiet = TRUE)))\n})\n\n# NOTE: These tests expect that you're already running connect; the tests\n# will speak to that running connect process (if it can find it)\nfindConnect <- function() {\n  connect <- Sys.which(\"connect\")\n  if (connect == \"\") {\n    possibleLocs <- c(\n      \"~/git/connect/bin\"\n    )\n    for (loc in possibleLocs) {\n      if (file.exists(file.path(loc, \"connect\"))) {\n        return(normalizePath(file.path(loc, \"connect\")))\n      }\n    }\n    stop(\"Couldn't find an appropriate 'connect' binary\")\n  }\n}\n\n# Tests for Snowflake authentication with auto-detection\n\ntest_that(\"getDefaultSnowflakeConnectionName auto-detects matching default connection\", {\n  local_mocked_bindings(\n    snowflake_connection = function(name = NULL) {\n      expect_null(name)\n      list(\n        account = \"org-account\",\n        name = \"default\"\n      )\n    },\n    .package = \"snowflakeauth\"\n  )\n\n  result <- getDefaultSnowflakeConnectionName(\n    \"https://prefix-org-account.snowflakecomputing.app/__api__\"\n  )\n\n  expect_equal(result, \"default\")\n})\n\ntest_that(\"getDefaultSnowflakeConnectionName normalizes underscores to hyphens\", {\n  local_mocked_bindings(\n    snowflake_connection = function(name = NULL) {\n      expect_null(name)\n      list(\n        account = \"org_account\",\n        name = \"default\"\n      )\n    },\n    .package = \"snowflakeauth\"\n  )\n\n  result <- getDefaultSnowflakeConnectionName(\n    \"https://prefix-org-account.snowflakecomputing.app/__api__\"\n  )\n\n  expect_equal(result, \"default\")\n})\n\ntest_that(\"getDefaultSnowflakeConnectionName errors when default connection doesn't match server\", {\n  local_mocked_bindings(\n    snowflake_connection = function(name = NULL) {\n      list(\n        account = \"different-xyz789\",\n        name = \"default\"\n      )\n    },\n    .package = \"snowflakeauth\"\n  )\n\n  expect_snapshot(\n    getDefaultSnowflakeConnectionName(\n      \"https://prefix-org-account.snowflakecomputing.app/__api__\"\n    ),\n    error = TRUE\n  )\n})\n\ntest_that(\"getDefaultSnowflakeConnectionName errors when no default connection exists\", {\n  local_mocked_bindings(\n    snowflake_connection = function(name = NULL) {\n      stop(\"No default connection configured\")\n    },\n    .package = \"snowflakeauth\"\n  )\n\n  expect_snapshot(\n    getDefaultSnowflakeConnectionName(\n      \"https://prefix-org-account.snowflakecomputing.app/__api__\"\n    ),\n    error = TRUE\n  )\n})\n\ntest_that(\"extractSnowflakeAccount handles various hostname formats\", {\n  # Non-privatelink SPCS format.\n  expect_equal(\n    extractSnowflakeAccount(\"prefix-org-account.snowflakecomputing.app\"),\n    \"org-account\"\n  )\n  # Privatelink format. The .privatelink suffix is part of the account name.\n  expect_equal(\n    extractSnowflakeAccount(\"prefix-org-account.privatelink.snowflake.app\"),\n    \"org-account.privatelink\"\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-client-connectCloud.R",
    "content": "test_that(\"awaitCompletion\", {\n  skip_if_not_installed(\"webfakes\")\n\n  revision_app <- webfakes::new_app()\n  revision_app$use(webfakes::mw_json())\n  revision_app$get(\"/revisions/:id\", function(req, res) {\n    res$set_status(200L)$send_json(\n      list(\n        id = I(req$params$id),\n        content_id = \"content789\",\n        publish_result = \"success\",\n        status = \"published\",\n        url = \"https://example.posit.cloud/content/123\",\n        publish_error_details = NULL\n      ),\n      auto_unbox = TRUE\n    )\n  })\n  app <- webfakes::new_app_process(revision_app)\n  service <- parseHttpUrl(app$url())\n\n  authInfo <- list(\n    server = \"connect.posit.cloud\",\n    name = \"some-user\",\n    username = \"some-user\",\n    accountId = \"123\",\n    accessToken = \"current-token\",\n    refreshToken = \"refresh-token\"\n  )\n  client <- connectCloudClient(service, authInfo)\n\n  # test successful completion\n  result <- client$awaitCompletion(\"rev123\")\n  expect_true(result$success)\n  expect_equal(\n    result$url,\n    \"https://connect.posit.cloud/some-user/content/content789\"\n  )\n  expect_null(result$error)\n})\n\ntest_that(\"awaitCompletion handles failure\", {\n  skip_if_not_installed(\"webfakes\")\n\n  revision_app <- webfakes::new_app()\n  revision_app$use(webfakes::mw_json())\n  revision_app$get(\"/revisions/:id\", function(req, res) {\n    res$set_status(200L)$send_json(\n      list(\n        id = I(req$params$id),\n        content_id = \"content789\",\n        publish_result = \"failure\",\n        status = \"published\",\n        url = NULL,\n        publish_error_details = \"Deployment failed due to missing dependencies\"\n      ),\n      auto_unbox = TRUE\n    )\n  })\n  app <- webfakes::new_app_process(revision_app)\n  service <- parseHttpUrl(app$url())\n\n  authInfo <- list(\n    server = \"connect.posit.cloud\",\n    name = \"some-user\",\n    username = \"some-user\",\n    accountId = \"123\",\n    accessToken = \"current-token\",\n    refreshToken = \"refresh-token\"\n  )\n  client <- connectCloudClient(service, authInfo)\n\n  # test failure case\n  result <- client$awaitCompletion(\"rev456\")\n  expect_false(result$success)\n  expect_equal(\n    result$url,\n    \"https://connect.posit.cloud/some-user/content/content789\"\n  )\n  expect_equal(result$error, \"Deployment failed due to missing dependencies\")\n})\n\ntest_that(\"awaitCompletion handles failure with logs\", {\n  skip_if_not_installed(\"webfakes\")\n\n  # Mock revision API that returns failure with log channel\n  cloudApiApp <- webfakes::new_app()\n  cloudApiApp$use(webfakes::mw_json())\n  cloudApiApp$get(\"/revisions/:id\", function(req, res) {\n    res$set_status(200L)$send_json(\n      list(\n        id = I(req$params$id),\n        content_id = \"content789\",\n        publish_result = \"failure\",\n        status = \"published\",\n        url = NULL,\n        publish_error_details = \"Deployment failed due to missing dependencies\",\n        publish_log_channel = \"log-channel-123\"\n      ),\n      auto_unbox = TRUE\n    )\n  })\n\n  # Mock authorization API\n  cloudApiApp$post(\"/authorization\", function(req, res) {\n    res$set_status(200L)$send_json(\n      list(\n        authorized = TRUE,\n        token = \"auth-token-xyz\"\n      ),\n      auto_unbox = TRUE\n    )\n  })\n\n  # Mock logs API\n  logs_app <- webfakes::new_app()\n  logs_app$use(webfakes::mw_json())\n  logs_app$get(\"/v1/logs/:channel\", function(req, res) {\n    res$set_status(200L)$send_json(\n      list(\n        data = list(\n          list(\n            timestamp = 1234567890 * 1000000,\n            message = \"Starting deployment...\",\n            level = \"info\"\n          ),\n          list(\n            timestamp = 1234567891 * 1000000,\n            message = \"Your app is busted!!\",\n            level = \"error\"\n          )\n        )\n      ),\n      auto_unbox = TRUE\n    )\n  })\n\n  # Start the main app and logs app\n  app <- webfakes::new_app_process(cloudApiApp)\n  logs_app_process <- webfakes::new_app_process(logs_app)\n\n  service <- parseHttpUrl(app$url())\n  authInfo <- list(\n    server = \"connect.posit.cloud\",\n    name = \"some-user\",\n    username = \"some-user\",\n    accountId = \"123\",\n    accessToken = \"current-token\",\n    refreshToken = \"refresh-token\"\n  )\n\n  # Mock connectCloudUrls and connectCloudLogsClient\n  local_mocked_bindings(\n    connectCloudUrls = function() {\n      list(logs = logs_app_process$url(), ui = \"https://connect.posit.cloud\")\n    },\n    connectCloudLogsClient = function() {\n      list(\n        getLogs = function(logChannel, authToken) {\n          logsUrl <- logs_app_process$url()\n          service <- parseHttpUrl(paste0(logsUrl, \"/v1\"))\n\n          authInfo <- list(\n            accessToken = authToken\n          )\n\n          path <- paste0(\n            \"/logs/\",\n            logChannel,\n            \"?traversal_direction=backward&limit=1500\"\n          )\n          response <- GET(service, authInfo, path)\n          response\n        }\n      )\n    }\n  )\n\n  client <- connectCloudClient(service, authInfo)\n\n  # Test failure case with logs - capture stderr output\n  stderr_output <- capture.output(\n    {\n      result <- client$awaitCompletion(\"rev456\")\n    },\n    type = \"message\"\n  )\n\n  # Check the result object\n  expect_false(result$success)\n  expect_equal(\n    result$url,\n    \"https://connect.posit.cloud/some-user/content/content789\"\n  )\n  expect_equal(result$error, \"Deployment failed due to missing dependencies\")\n\n  # Check that logs were printed to stderr\n  stderr_text <- paste(stderr_output, collapse = \"\\n\")\n  info <- paste0(\"stderr_text was:\\n\", stderr_text)\n\n  expect_match(stderr_text, \"Begin Publishing Log\")\n  expect_match(stderr_text, \"End Publishing Log\")\n  expect_match(stderr_text, \"Starting deployment...\")\n  expect_match(stderr_text, \"Your app is busted!!\")\n  expect_match(stderr_text, \"INFO:\")\n  expect_match(stderr_text, \"ERROR:\")\n})\n\ntest_that(\"withTokenRefreshRetry passes through successful requests\", {\n  skip_if_not_installed(\"webfakes\")\n\n  # Mock a successful API call\n  mock_request_fn <- function(service, authInfo, path) {\n    list(success = TRUE, data = \"test response\")\n  }\n\n  service <- list(host = \"example.com\", port = 443, protocol = \"https\")\n  authInfo <- list(\n    server = \"connect.posit.cloud\",\n    name = \"some-user\",\n    username = \"some-user\",\n    accountId = \"123\",\n    accessToken = \"current-token\",\n    refreshToken = \"refresh-token\"\n  )\n  client <- connectCloudClient(service, authInfo)\n\n  result <- client$withTokenRefreshRetry(\n    mock_request_fn,\n    \"/test\"\n  )\n\n  expect_equal(result$success, TRUE)\n  expect_equal(result$data, \"test response\")\n})\n\ntest_that(\"withTokenRefreshRetry handles 401 with successful token refresh\", {\n  skip_if_not_installed(\"webfakes\")\n\n  call_count <- 0\n  mock_request_fn <- function(service, authInfo, path) {\n    call_count <<- call_count + 1\n    if (call_count == 1) {\n      # First call fails with 401\n      err <- structure(\n        list(message = \"HTTP 401\"),\n        class = c(\"rsconnect_http_401\", \"rsconnect_http\", \"error\", \"condition\")\n      )\n      stop(err)\n    } else {\n      # Second call succeeds\n      list(success = TRUE, data = \"success after refresh\")\n    }\n  }\n\n  # Mock cloudAuthClient and registerAccount\n  register_called <- FALSE\n  local_mocked_bindings(\n    cloudAuthClient = function() {\n      list(\n        exchangeToken = function(request) {\n          expect_equal(request$grant_type, \"refresh_token\")\n          expect_equal(request$refresh_token, \"refresh-token\")\n          list(\n            access_token = \"new-access-token\",\n            refresh_token = \"new-refresh-token\"\n          )\n        }\n      )\n    },\n    registerAccount = function(\n      server,\n      name,\n      accountId,\n      accessToken,\n      refreshToken\n    ) {\n      register_called <<- TRUE\n      expect_equal(server, \"connect.posit.cloud\")\n      expect_equal(name, \"test-user\")\n      expect_equal(accountId, \"123\")\n      expect_equal(accessToken, \"new-access-token\")\n      expect_equal(refreshToken, \"new-refresh-token\")\n    }\n  )\n\n  service <- list(host = \"example.com\", port = 443, protocol = \"https\")\n  authInfo <- list(\n    server = \"connect.posit.cloud\",\n    name = \"test-user\",\n    accountId = \"123\",\n    accessToken = \"current-token\",\n    refreshToken = \"refresh-token\"\n  )\n  client <- connectCloudClient(service, authInfo)\n\n  result <- client$withTokenRefreshRetry(mock_request_fn, \"/test\")\n\n  expect_equal(result$success, TRUE)\n  expect_equal(result$data, \"success after refresh\")\n  expect_equal(call_count, 2)\n  expect_true(register_called)\n})\n"
  },
  {
    "path": "tests/testthat/test-client.R",
    "content": "test_that(\"shinyapps accounts create shinyapps clients\", {\n  account <- list(server = \"shinyapps.io\")\n  client <- clientForAccount(account)\n  expect_equal(client$service(), \"shinyapps.io\")\n})\n\ntest_that(\"connect cloud accounts create connect cloud clients\", {\n  account <- list(server = \"connect.posit.cloud\")\n  client <- clientForAccount(account)\n  expect_equal(client$service(), \"connect.posit.cloud\")\n})\n\ntest_that(\"connect accounts create connect clients\", {\n  local_temp_config()\n\n  addTestServer(\"example.com\")\n  account <- list(server = \"example.com\")\n  client <- clientForAccount(account)\n  expect_equal(client$service(), \"connect\")\n})\n"
  },
  {
    "path": "tests/testthat/test-config.R",
    "content": "test_that(\"account file returned with server name\", {\n  local_temp_config()\n  registerAccount(\"simple\", \"alice\", 13, apiKey = \"alice-api-key\")\n\n  expected <- normalizePath(file.path(\n    rsconnectConfigDir(\"accounts\"),\n    \"simple/alice.dcf\"\n  ))\n  dir <- accountConfigFile(\"alice\", server = \"simple\")\n  expect_equal(dir, expected)\n})\n\ntest_that(\"account file containing pattern characters found with server name\", {\n  local_temp_config()\n  registerAccount(\n    \"complex\",\n    \"hatter+mad@example.com\",\n    42,\n    apiKey = \"hatter-api-key\"\n  )\n\n  # https://github.com/rstudio/rsconnect/issues/620\n  expected <- normalizePath(file.path(\n    rsconnectConfigDir(\"accounts\"),\n    \"complex/hatter+mad@example.com.dcf\"\n  ))\n  dir <- accountConfigFile(\"hatter+mad@example.com\", server = \"complex\")\n  expect_equal(dir, expected)\n})\n\ntest_that(\"isDocumentPath\", {\n  stuff <- local_temp_app(list(\n    \"shiny.app/app.R\" = c(),\n    \"doc/research.Rmd\" = c()\n  ))\n  expect_false(isDocumentPath(file.path(stuff, \"shiny.app\")))\n  expect_false(isDocumentPath(file.path(stuff, \"doc\")))\n  expect_true(isDocumentPath(file.path(stuff, \"doc/research.Rmd\")))\n})\n"
  },
  {
    "path": "tests/testthat/test-cookies.R",
    "content": "test_that(\"Parsing cookies works\", {\n  # Note that the Expires field is ignored\n  cookie <- parseCookie(\n    \"mycookie=myvalue; Path=/; Expires=Sat, 24 Jun 2017 16:16:05 GMT; Max-Age=3600; HttpOnly\",\n    \"/\"\n  )\n  expect_equal(cookie$name, \"mycookie\")\n  expect_equal(cookie$value, \"myvalue\")\n  expect_true(cookie$expires > Sys.time() + 3595)\n  expect_true(cookie$expires < Sys.time() + 3605)\n  expect_equal(cookie$path, \"/\")\n  expect_false(cookie$secure)\n\n  # Max-Age with no semicolon, non-root path\n  cookie <- parseCookie(\"mycookie2=myvalue2; Secure; Path=/test; max-AGE=3600\")\n  expect_equal(cookie$name, \"mycookie2\")\n  expect_equal(cookie$value, \"myvalue2\")\n  expect_true(cookie$expires > Sys.time() + 3595)\n  expect_true(cookie$expires < Sys.time() + 3605)\n  expect_equal(cookie$path, \"/test\")\n  expect_true(cookie$secure)\n\n  # Path with no semicolon, no max age\n  cookie <- parseCookie(\"mycookie2=myvalue2; PATH=/test\")\n  expect_equal(cookie$name, \"mycookie2\")\n  expect_equal(cookie$value, \"myvalue2\")\n  expect_true(cookie$expires > (Sys.time() + 10^9))\n  expect_equal(cookie$path, \"/test\")\n  expect_false(cookie$secure)\n\n  # No path, value with no semicolon\n  cookie <- parseCookie(\"mycookie2=myvalue2\")\n  expect_equal(cookie$name, \"mycookie2\")\n  expect_equal(cookie$value, \"myvalue2\")\n  expect_true(cookie$expires > (Sys.time() + 10^9))\n  expect_equal(cookie$path, \"/\")\n  expect_false(cookie$secure)\n\n  # Trailing secure\n  cookie <- parseCookie(\"mycookie2=myvalue2; Secure\")\n  expect_equal(cookie$name, \"mycookie2\")\n  expect_equal(cookie$value, \"myvalue2\")\n  expect_true(cookie$expires > (Sys.time() + 10^9))\n  expect_equal(cookie$path, \"/\")\n  expect_true(cookie$secure)\n\n  # Full cookie with spaces around =s\n  cookie <- parseCookie(\n    \"mycookie = myvalue; SEcure; Path = /; Expires = Sat, 24 Jun 2017 16:16:05 GMT; Max-Age = 3600; HttpOnly\"\n  )\n  expect_equal(cookie$name, \"mycookie\")\n  expect_equal(cookie$value, \"myvalue\")\n  expect_true(cookie$secure)\n  expect_true(cookie$expires > Sys.time() + 3595)\n  expect_true(cookie$expires < Sys.time() + 3605)\n  expect_equal(cookie$path, \"/\")\n\n  # Value-less cookie\n  cookie <- parseCookie(\"mycookie=; Path = /\")\n  expect_equal(cookie$name, \"mycookie\")\n  expect_equal(cookie$value, \"\")\n  expect_equal(cookie$path, \"/\")\n\n  # prove #229 is fixed\n  cookie <- parseCookie(\n    \"my_cookie-which/uses%20strange?characters=foo_-%20+?1234bar; Path = /\"\n  )\n  expect_equal(cookie$name, \"my_cookie-which/uses%20strange?characters\")\n  expect_equal(cookie$value, \"foo_-%20+?1234bar\")\n  expect_equal(cookie$path, \"/\")\n})\n\ntest_that(\"cookie parsing uses expires= when no max-age=\", {\n  expect_equal(\n    parseCookie(\n      \"mycookie=myvalue; Path=/; Secure; HttpOnly; Expires=Sat, 24 Jun 2017 16:16:05 GMT\"\n    ),\n    list(\n      name = \"mycookie\",\n      value = \"myvalue\",\n      expires = curl::parse_date(\n        \"Sat, 24 Jun 2017 16:16:05 GMT\"\n      ),\n      path = \"/\",\n      secure = TRUE\n    )\n  )\n})\n\ntest_that(\"Invalid cookies fail parsing\", {\n  # Invalid path, doesn't match request's path\n  expect_snapshot(cookie <- parseCookie(\"x=1; Path=/something/else\", \"/path\"))\n  expect_null(cookie)\n\n  # Invalid key/val format\n  expect_snapshot(cookie <- parseCookie(\"mycookie;\"))\n  expect_null(cookie)\n})\n\ntest_that(\"cookies can be stored\", {\n  local_cookie_store()\n\n  parsedUrl <- parseHttpUrl(\"http://fakedomain:123/test/stuff\")\n  expect_warning(\n    {\n      storeCookies(\n        parsedUrl,\n        c(\n          \"mycookie=myvalue; Path=/; Max-Age=3600; HttpOnly\",\n          \"anotherCookie=what; Path=/test; Max-Age=100\",\n          \"wrongpath=huh; Path=/uhoh; Max-Age=100\",\n          \"secureCookie=123; Secure\",\n          \"third=cookie; Path=/; Max-Age=500\"\n        )\n      )\n    },\n    \"Invalid path set for cookie\"\n  )\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 4)\n\n  # Check the first cookie\n  co <- cookies[cookies$name == \"mycookie\", ]\n  expect_equal(co$name, \"mycookie\")\n  expect_equal(co$value, \"myvalue\")\n  expect_true(co$expires > Sys.time() + 3595)\n  expect_true(co$expires < Sys.time() + 3605)\n  expect_equal(co$path, \"/\")\n  expect_false(co$secure)\n\n  # And the other\n  co <- cookies[cookies$name == \"anotherCookie\", ]\n  expect_equal(co$name, \"anotherCookie\")\n  expect_equal(co$value, \"what\")\n  expect_true(co$expires > Sys.time() + 95)\n  expect_true(co$expires < Sys.time() + 105)\n  expect_equal(co$path, \"/test\")\n  expect_false(co$secure)\n\n  # Third\n  co <- cookies[cookies$name == \"third\", ]\n  expect_equal(co$name, \"third\")\n  expect_equal(co$value, \"cookie\")\n  expect_true(co$expires > Sys.time() + 495)\n  expect_true(co$expires < Sys.time() + 505)\n  expect_equal(co$path, \"/\")\n  expect_false(co$secure)\n\n  # Fourth\n  co <- cookies[cookies$name == \"secureCookie\", ]\n  expect_equal(co$name, \"secureCookie\")\n  expect_equal(co$value, \"123\")\n  expect_true(co$secure)\n})\n\ntest_that(\"duplicate cookies overwrite one another\", {\n  local_cookie_store()\n\n  parsedUrl <- parseHttpUrl(\"http://fakedomain:123/test/stuff\")\n  storeCookies(parsedUrl, \"mycookie=myvalue; Path=/; Max-Age=3600\")\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 1)\n\n  # Add another valid cookie, same domain, same name, different path\n  storeCookies(parsedUrl, \"mycookie=myvalue; Path=/test; Max-Age=3600\")\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 2)\n\n  # Duplicate cookie should overwrite\n  storeCookies(parsedUrl, \"mycookie=myvalue; Path=/test; Max-Age=99\")\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 2)\n\n  # Confirm that the stored cookie is the more recent one.\n  co <- cookies[cookies$path == \"/test\", ]\n  expect_true(co$expires > Sys.time() + 94)\n  expect_true(co$expires < Sys.time() + 104)\n})\n\ntest_that(\"appending cookie headers works\", {\n  local_cookie_store()\n\n  parsedUrl <- parseHttpUrl(\"http://fakedomain:123/test/stuff\")\n\n  # Nothing to append, no-op\n  headers <- appendCookieHeaders(parsedUrl, c(header1 = 123, header2 = \"abc\"))\n  expect_length(headers, 2)\n  expect_equal(headers[[\"header1\"]], \"123\")\n  expect_equal(headers[[\"header2\"]], \"abc\")\n\n  # Store a cookie\n  storeCookies(parsedUrl, \"cookie1=value1; Path=/; Max-Age=3600\")\n\n  headers <- appendCookieHeaders(parsedUrl, c(header1 = 123, header2 = \"abc\"))\n  expect_length(headers, 3)\n  expect_equal(headers[[\"header1\"]], \"123\")\n  expect_equal(headers[[\"header2\"]], \"abc\")\n  expect_equal(headers[[\"cookie\"]], \"cookie1=value1\")\n\n  # Store a couple more cookies\n  storeCookies(parsedUrl, \"cookie2=value2; Path=/test; Max-Age=3600\")\n  # This one has the wrong path, will be filtered out\n  storeCookies(\n    parseHttpUrl(\"http://fakedomain:123/another\"),\n    \"cookie3=value3; Path=/another; Max-Age=3600\"\n  )\n\n  headers <- appendCookieHeaders(parsedUrl, c(header1 = 123, header2 = \"abc\"))\n  expect_length(headers, 3)\n  expect_equal(headers[[\"header1\"]], \"123\")\n  expect_equal(headers[[\"header2\"]], \"abc\")\n  expect_equal(headers[[\"cookie\"]], \"cookie2=value2; cookie1=value1\")\n\n  # If you already have a cookie header, you end up with two\n  headers <- appendCookieHeaders(parsedUrl, c(cookie = \"existing=value\"))\n  expect_length(headers, 2)\n  expect_equal(headers[1], c(cookie = \"existing=value\"))\n  expect_equal(headers[2], c(cookie = \"cookie2=value2; cookie1=value1\"))\n\n  # Add a secure cookie\n  storeCookies(\n    parsedUrl,\n    \"securecookie=secureval; Path=/; Max-Age=3600; Secure\"\n  )\n  headers <- appendCookieHeaders(parsedUrl, c())\n  expect_equal(headers[[\"cookie\"]], \"cookie2=value2; cookie1=value1\")\n\n  # But over a secure channel, you'd include the secure cookie\n  headers <- appendCookieHeaders(\n    parseHttpUrl(\"https://fakedomain:123/test/stuff\"),\n    c()\n  )\n  expect_equal(\n    headers[[\"cookie\"]],\n    \"securecookie=secureval; cookie2=value2; cookie1=value1\"\n  )\n})\n\ntest_that(\"expires= format errors treated as session cookies\", {\n  local_cookie_store()\n\n  parsedUrl <- parseHttpUrl(\"http://fakedomain:123/test/stuff\")\n\n  storeCookies(parsedUrl, \"mycookie=myvalue; expires=notadate\")\n  headers <- appendCookieHeaders(parsedUrl, NULL)\n  expect_equal(\n    headers[[\"cookie\"]],\n    \"mycookie=myvalue\"\n  )\n})\n\ntest_that(\"max-age= expired cookies are removed\", {\n  local_cookie_store()\n\n  parsedUrl <- parseHttpUrl(\"http://fakedomain:123/test/stuff\")\n\n  # Expired cookies are removed from the store and not included in the request\n  storeCookies(parsedUrl, \"expiredkey=something; Max-Age=-1\")\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 1)\n\n  # Now it will recognize that it's expired and remove it\n  headers <- appendCookieHeaders(parsedUrl, NULL)\n  expect_null(headers)\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 0)\n\n  # And with multiple cookies, it still removes only the expired one\n  storeCookies(parsedUrl, \"expiredkey=something; Max-Age=-1\")\n  storeCookies(parsedUrl, \"notexpiredkey=something\")\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 2)\n\n  # Now it will recognize that it's expired and remove it\n  headers <- appendCookieHeaders(parsedUrl, c())\n  expect_length(headers, 1)\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 1)\n})\n\ntest_that(\"expires= expired cookies are removed\", {\n  local_cookie_store()\n\n  parsedUrl <- parseHttpUrl(\"http://fakedomain:123/test/stuff\")\n\n  # Expired cookies are removed from the store and not included in the request\n  storeCookies(\n    parsedUrl,\n    \"expiredkey=something; expires=Sat, 24 Jun 2017 16:16:05 GMT\"\n  )\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 1)\n\n  # Now it will recognize that it's expired and remove it\n  headers <- appendCookieHeaders(parsedUrl, NULL)\n  expect_null(headers)\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 0)\n\n  # And with multiple cookies, it still removes only the expired one\n  storeCookies(\n    parsedUrl,\n    \"expiredkey=something; expires=Sat, 24 Jun 2017 16:16:05 GMT\"\n  )\n  storeCookies(parsedUrl, \"notexpiredkey=something\")\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 2)\n\n  # Now it will recognize that it's expired and remove it\n  headers <- appendCookieHeaders(parsedUrl, c())\n  expect_length(headers, 1)\n\n  cookies <- get(\"fakedomain:123\", envir = .cookieStore)\n  expect_equal(nrow(cookies), 1)\n})\n\ntest_that(\"getCookieHost works\", {\n  expect_equal(getCookieHost(parseHttpUrl(\"http://rstudio.com\")), \"rstudio.com\")\n  expect_equal(\n    getCookieHost(parseHttpUrl(\"http://rstudio.com:3939\")),\n    \"rstudio.com:3939\"\n  )\n  expect_equal(getCookieHost(parseHttpUrl(\"http://127.0.0.1\")), \"127.0.0.1\")\n  expect_equal(\n    getCookieHost(parseHttpUrl(\"http://127.0.0.1:3939\")),\n    \"127.0.0.1:3939\"\n  )\n})\n\ntest_that(\"getting and clearing cookies works\", {\n  local_cookie_store()\n\n  all <- getCookies()\n  expect_null(all)\n\n  # Add a few cookies\n  domain1 <- parseHttpUrl(\"http://domain1/test/stuff\")\n  storeCookies(domain1, \"c1=v1\")\n  storeCookies(domain1, \"c2=v2\")\n\n  domain2 <- parseHttpUrl(\"http://domain2:3939/test/stuff\")\n  storeCookies(domain2, \"c3=v3\")\n\n  d1c <- getCookies(\"domain1\")\n  expect_equal(nrow(d1c), 2)\n  expect_equal(d1c$host[1], \"domain1\")\n\n  d2c <- getCookies(\"domain2\")\n  expect_null(d2c)\n  d2c <- getCookies(\"domain2\", 3939)\n  expect_equal(nrow(d2c), 1)\n  expect_equal(d2c$host[1], \"domain2:3939\")\n\n  all <- getCookies()\n  expect_equal(nrow(all), 3)\n\n  # Delete cookies from one domain\n  clearCookies(\"domain2\", 3939)\n\n  d2c <- getCookies(\"domain2\", 3939)\n  expect_null(d2c)\n\n  all <- getCookies()\n  expect_equal(nrow(all), 2)\n\n  # Clear all cookies\n  clearCookies()\n\n  d1c <- getCookies(\"domain1\")\n  expect_null(d1c)\n\n  all <- getCookies()\n  expect_null(all)\n})\n"
  },
  {
    "path": "tests/testthat/test-deployApp.R",
    "content": "test_that(\"appDir must be an existing directory\", {\n  expect_snapshot(error = TRUE, {\n    deployApp(1)\n    deployApp(\"doesntexist\")\n  })\n})\n\ntest_that(\"appPrimaryDoc must exist, if supplied\", {\n  skip_on_cran()\n  dir <- local_temp_app()\n\n  expect_snapshot(error = TRUE, {\n    deployApp(dir, appPrimaryDoc = c(\"foo.Rmd\", \"bar.Rmd\"))\n    deployApp(dir, appPrimaryDoc = \"foo.Rmd\")\n  })\n})\n\ntest_that(\"startup scripts are logged by default\", {\n  dir <- local_temp_app()\n  withr::local_dir(dir)\n  writeLines(\"1 + 1\", file.path(dir, \".rsconnect_profile\"))\n\n  expect_snapshot(runStartupScripts(\".\"))\n})\n\n# record directory --------------------------------------------------------\n\ntest_that(\"findRecordPath() uses recordDir, then appPrimaryDoc, then appDir\", {\n  expect_equal(findRecordPath(\"a\"), \"a\")\n  expect_equal(findRecordPath(\"a\", recordDir = \"b\"), \"b\")\n  expect_equal(findRecordPath(\"a\", appPrimaryDoc = \"c\"), \"a/c\")\n})\n\n# app visibility ----------------------------------------------------------\n\ntest_that(\"needsVisibilityChange() returns FALSE when no change needed\", {\n  dummyApp <- function(visibility) {\n    list(\n      deployment = list(\n        properties = list(\n          application.visibility = visibility\n        )\n      )\n    )\n  }\n\n  expect_false(needsVisibilityChange(\"connect.com\"))\n  expect_false(needsVisibilityChange(\"shinyapps.io\", dummyApp(\"public\"), NULL))\n  expect_false(needsVisibilityChange(\n    \"shinyapps.io\",\n    dummyApp(\"public\"),\n    \"public\"\n  ))\n  expect_true(needsVisibilityChange(\"shinyapps.io\", dummyApp(NULL), \"private\"))\n  expect_true(needsVisibilityChange(\n    \"shinyapps.io\",\n    dummyApp(\"public\"),\n    \"private\"\n  ))\n})\n\ntest_that(\"checkConnectSupportsNodejs errors for old server versions\", {\n  client <- list(\n    serverSettings = function() list(version = \"2025.12.0\")\n  )\n  expect_error(\n    checkConnectSupportsNodejs(client),\n    \"2026.04.0\"\n  )\n})\n\ntest_that(\"checkConnectSupportsNodejs passes for supported server versions\", {\n  client <- list(\n    serverSettings = function() list(version = \"2026.04.0\")\n  )\n  expect_no_error(checkConnectSupportsNodejs(client))\n\n  client <- list(\n    serverSettings = function() list(version = \"2026.05.0\")\n  )\n  expect_no_error(checkConnectSupportsNodejs(client))\n\n  client <- list(\n    serverSettings = function() list(version = \"2026.05.0-dev+54-sdlkfjsd\")\n  )\n  expect_no_error(checkConnectSupportsNodejs(client))\n})\n\ntest_that(\"checkConnectSupportsNodejs messages when version is unavailable\", {\n  client <- list(\n    serverSettings = function() list(version = \"\")\n  )\n  expect_message(\n    checkConnectSupportsNodejs(client),\n    \"Could not determine\"\n  )\n})\n\ntest_that(\"checkConnectSupportsNodejs messages when serverSettings errors\", {\n  client <- list(\n    serverSettings = function() stop(\"connection failed\")\n  )\n  expect_message(\n    checkConnectSupportsNodejs(client),\n    \"Could not determine\"\n  )\n})\n\ntest_that(\"checkConnectSupportsNodejs messages when version is unparseable\", {\n  client <- list(\n    serverSettings = function() list(version = \"not-a-version\")\n  )\n  expect_message(\n    checkConnectSupportsNodejs(client),\n    \"Could not determine\"\n  )\n})\n\ntest_that(\"connectVersionLt compares versions correctly\", {\n  expect_true(connectVersionLt(\"2025.12.0\", \"2026.04.0\"))\n  expect_false(connectVersionLt(\"2026.04.0\", \"2026.04.0\"))\n  expect_false(connectVersionLt(\"2026.05.0\", \"2026.04.0\"))\n  expect_false(connectVersionLt(\"2027.01.0\", \"2026.04.0\"))\n})\n\ntest_that(\"connectVersionLt handles dev versions gracefully\", {\n  expect_false(connectVersionLt(\"2026.04.0-dev+67\", \"2026.04.0\"))\n})\n\ntest_that(\"connectVersionLt returns NA for unparseable or missing input\", {\n  expect_true(is.na(connectVersionLt(\"garbage\", \"2026.04.0\")))\n  expect_true(is.na(connectVersionLt(NULL, \"2026.04.0\")))\n  expect_true(is.na(connectVersionLt(\"\", \"2026.04.0\")))\n})\n\ntest_that(\"deployHook executes function if set\", {\n  withr::local_options(rsconnect.pre.deploy = NULL)\n  expect_equal(\n    runDeploymentHook(\"PATH\", \"rsconnect.pre.deploy\"),\n    NULL\n  )\n\n  withr::local_options(rsconnect.pre.deploy = function(path) path)\n  expect_equal(\n    runDeploymentHook(\"PATH\", \"rsconnect.pre.deploy\"),\n    \"PATH\"\n  )\n  expect_snapshot(\n    . <- runDeploymentHook(\"PATH\", \"rsconnect.pre.deploy\", verbose = TRUE)\n  )\n})\n\n# deleted apps ------------------------------------------------------------\n\ntest_that(\"applicationDeleted() errors or prompts as needed\", {\n  local_temp_config()\n  addTestServer(\"s\")\n  addTestAccount(\"a\", \"s\")\n  app <- local_temp_app()\n  addTestDeployment(app, appName = \"name\", account = \"a\", server = \"s\")\n  target <- createDeployment(\"name\", \"title\", \"id\", NULL, \"a\", \"a\", \"s\", 1)\n  client <- list(createApplication = function(...) NULL)\n\n  expect_snapshot(applicationDeleted(client, target, app), error = TRUE)\n  expect_length(dir(app, recursive = TRUE), 1)\n\n  simulate_user_input(2)\n  expect_snapshot(. <- applicationDeleted(client, target, app))\n  expect_length(dir(app, recursive = TRUE), 0)\n})\n\n# envvars -----------------------------------------------------------------\n\ntest_that(\"deployApp() errors if envVars is given a named vector\", {\n  expect_snapshot(error = TRUE, {\n    deployApp(local_temp_app(), envVars = c(\"FLAG\" = \"true\"))\n  })\n})\n\n# with manifestPath arg ---------------------------------------------------\n\ntest_that(\"manifestPath must exist\", {\n  skip_on_cran()\n  appDir <- local_temp_app(list(app.R = \"# shiny app\"))\n\n  expect_error(\n    deployApp(appDir, manifestPath = \"manifest.json\"),\n    \"Manifest file not found\"\n  )\n})\n\ntest_that(\"manifest file must be valid JSON\", {\n  skip_on_cran()\n  appDir <- local_temp_app(list(app.R = \"# shiny app\"))\n  writeLines(\"not valid json {\", file.path(appDir, \"manifest.json\"))\n\n  expect_error(\n    deployApp(appDir, manifestPath = \"manifest.json\"),\n    \"invalid string in json text\"\n  )\n})\n\ntest_that(\"manifest must contain required fields\", {\n  skip_on_cran()\n  appDir <- local_temp_app(list(app.R = \"# shiny app\"))\n\n  # Empty manifest\n  writeLines(\"{}\", file.path(appDir, \"manifest.json\"))\n  expect_snapshot(error = TRUE, {\n    deployApp(appDir, manifestPath = \"manifest.json\")\n  })\n\n  # Manifest without appmode\n  writeLines(\n    '{\"metadata\": {}, \"files\": {}}',\n    file.path(appDir, \"manifest.json\")\n  )\n  expect_snapshot(error = TRUE, {\n    deployApp(appDir, manifestPath = \"manifest.json\")\n  })\n})\n\ntest_that(\"manifest must contain files\", {\n  skip_on_cran()\n  appDir <- local_temp_app(list(app.R = \"# shiny app\"))\n  writeLines(\n    '{\"metadata\": {\"appmode\": \"shiny\"}, \"files\": {}}',\n    file.path(appDir, \"manifest.json\")\n  )\n\n  expect_snapshot(error = TRUE, {\n    deployApp(appDir, manifestPath = \"manifest.json\")\n  })\n})\n\ntest_that(\"all files in manifest must exist in appDir\", {\n  skip_on_cran()\n  appDir <- local_temp_app(list(app.R = \"# shiny app\"))\n  writeManifest(appDir, quiet = TRUE)\n\n  # Add a non-existent file to the manifest\n  manifestPath <- file.path(appDir, \"manifest.json\")\n  manifest <- jsonlite::fromJSON(manifestPath)\n  manifest$files$missing.R <- list(checksum = \"abc123\")\n  writeLines(\n    jsonlite::toJSON(manifest, auto_unbox = TRUE),\n    manifestPath\n  )\n\n  expect_snapshot(error = TRUE, {\n    deployApp(appDir, manifestPath = \"manifest.json\")\n  })\n})\n\ntest_that(\"manifestPath ignored when NULL\", {\n  skip_on_cran()\n  appDir <- local_temp_app(list(app.R = \"# shiny app\"))\n\n  # Should work without manifest\n  expect_no_error({\n    # Will error later in deployment, but not due to missing manifest\n    tryCatch(\n      deployApp(appDir, manifestPath = NULL, server = \"fake-server\"),\n      error = function(e) {\n        # Expected to fail on server lookup, not manifest\n        print(e)\n        expect_false(grepl(\"manifest\", e$message, ignore.case = TRUE))\n      }\n    )\n  })\n})\n\n# confirmDependencySourceLibrary -------------------------------------------\n\ntest_that(\"confirmDependencySourceLibrary proceeds when user answers Y\", {\n  simulate_user_input(\"Y\")\n  expect_message(\n    confirmDependencySourceLibrary(),\n    \"renv.lock will be ignored\"\n  )\n})\n\ntest_that(\"confirmDependencySourceLibrary proceeds on empty input (default Y)\", {\n  simulate_user_input(\"\")\n  expect_message(\n    confirmDependencySourceLibrary(),\n    \"renv.lock will be ignored\"\n  )\n})\n\ntest_that(\"confirmDependencySourceLibrary aborts when user answers n\", {\n  simulate_user_input(\"n\")\n  expect_error(\n    suppressMessages(confirmDependencySourceLibrary()),\n    \"Deployment cancelled\"\n  )\n})\n\ntest_that(\"confirmDependencySourceLibrary informs non-interactively\", {\n  expect_message(\n    confirmDependencySourceLibrary(),\n    \"renv.lock.*will be ignored\"\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-deployDoc.R",
    "content": "test_that(\"deployDoc correctly reports bad path\", {\n  skip_on_cran()\n  expect_snapshot(deployDoc(\"doesntexist.Rmd\"), error = TRUE)\n})\n\n# standardizeSingleDocDeployment ------------------------------------------\n\ntest_that(\"turns appDir into appDir + appPrimarySourceDoc\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\"foo.R\" = \"\"))\n\n  doc <- standardizeSingleDocDeployment(file.path(dir, \"foo.R\"))\n  expect_equal(doc$appDir, normalizePath(dir))\n  expect_equal(doc$appPrimaryDoc, \"foo.R\")\n})\n\ntest_that(\"shiny rmd deploys whole directory\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.Rmd\" = c(\n      \"---\",\n      \"runtime: shiny\",\n      \"---\"\n    )\n  ))\n  doc <- standardizeSingleDocDeployment(file.path(dir, \"foo.Rmd\"))\n  expect_equal(doc$appFiles, NULL)\n})\n\ntest_that(\"regular rmd deploys file and dependencies\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.Rmd\" = c(\n      \"---\",\n      \"resource_files: [foo.csv]\",\n      \"---\"\n    ),\n    \"foo.csv\" = \"\"\n  ))\n\n  doc <- standardizeSingleDocDeployment(file.path(dir, \"foo.Rmd\"), quiet = TRUE)\n  expect_equal(doc$appFiles, c(\"foo.Rmd\", \"foo.csv\"))\n})\n\ntest_that(\"regular rmd deploys .Rprofile, if present\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.Rmd\" = \"\",\n    \".Rprofile\" = \"\"\n  ))\n\n  doc <- standardizeSingleDocDeployment(file.path(dir, \"foo.Rmd\"), quiet = TRUE)\n  expect_equal(doc$appFiles, c(\"foo.Rmd\", \".Rprofile\"))\n})\n\ntest_that(\"regular rmd deploys requirements.txt, if present\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.Rmd\" = \"\",\n    \"requirements.txt\" = \"\"\n  ))\n\n  doc <- standardizeSingleDocDeployment(file.path(dir, \"foo.Rmd\"), quiet = TRUE)\n  expect_equal(doc$appFiles, c(\"foo.Rmd\", \"requirements.txt\"))\n})\n\ntest_that(\"regular rmd deploys renv.lock, if present\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.Rmd\" = \"\",\n    \"renv.lock\" = \"\"\n  ))\n\n  doc <- standardizeSingleDocDeployment(file.path(dir, \"foo.Rmd\"), quiet = TRUE)\n  expect_equal(doc$appFiles, c(\"foo.Rmd\", \"renv.lock\"))\n})\n\ntest_that(\"regular html does not deploy .Rprofile\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.html\" = \"\",\n    \".Rprofile\" = \"\"\n  ))\n\n  doc <- standardizeSingleDocDeployment(\n    file.path(dir, \"foo.html\"),\n    quiet = TRUE\n  )\n  expect_equal(doc$appFiles, c(\"foo.html\"))\n})\n\ntest_that(\"regular html does not deploy requirements.txt\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.html\" = \"\",\n    \"requirements.txt\" = \"\"\n  ))\n\n  doc <- standardizeSingleDocDeployment(\n    file.path(dir, \"foo.html\"),\n    quiet = TRUE\n  )\n  expect_equal(doc$appFiles, c(\"foo.html\"))\n})\n\ntest_that(\"regular html does not deploy renv.lock\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\n    \"foo.html\" = \"\",\n    \"renv.lock\" = \"\"\n  ))\n\n  doc <- standardizeSingleDocDeployment(\n    file.path(dir, \"foo.html\"),\n    quiet = TRUE\n  )\n  expect_equal(doc$appFiles, c(\"foo.html\"))\n})\n\ntest_that(\"other types deploy that one file\", {\n  skip_on_cran()\n  dir <- local_temp_app(list(\"foo.R\" = \"\"))\n  doc <- standardizeSingleDocDeployment(file.path(dir, \"foo.R\"))\n  expect_equal(doc$appFiles, \"foo.R\")\n})\n"
  },
  {
    "path": "tests/testthat/test-deploySite.R",
    "content": "test_that(\"can extract quarto metadata\", {\n  skip_on_cran()\n  skip_if_no_quarto()\n  skip_if_not_installed(\"quarto\")\n\n  app <- local_temp_app(list(\n    `_quarto.yaml` = c(\n      \"project:\",\n      \"  type: website\",\n      \"\",\n      \"website:\",\n      \"  title: 'website-quarto'\"\n    )\n  ))\n\n  site <- quartoSite(app)\n  expect_equal(site$title, \"website-quarto\")\n  expect_equal(site$output_dir, normalizePath(file.path(app, \"_site\")))\n})\n\ntest_that(\"can extract rmarkdown metadata\", {\n  skip_on_cran()\n  skip_if_not_installed(\"rmarkdown\")\n\n  app <- local_temp_app(list(\n    `_site.yml` = 'name: \"my-website\"'\n  ))\n\n  site <- rmarkdownSite(app)\n  expect_equal(site$name, \"my-website\")\n  expect_equal(site$title, NULL)\n  expect_equal(site$output_dir, normalizePath(file.path(app, \"_site\")))\n})\n"
  },
  {
    "path": "tests/testthat/test-deploymentTarget.R",
    "content": "test_that(\"errors if no accounts\", {\n  local_temp_config()\n\n  expect_snapshot(findDeploymentTarget(), error = TRUE)\n})\n\ntest_that(\"errors if unknown account or server\", {\n  local_temp_config()\n  addTestServer(\"bar\")\n  addTestAccount(\"foo\", \"bar\")\n\n  expect_snapshot(error = TRUE, {\n    findDeploymentTarget(server = \"unknown\")\n    findDeploymentTarget(account = \"john\")\n  })\n})\n\ntest_that(\"errors if no previous deployments and multiple accounts\", {\n  local_temp_config()\n  addTestServer(\"foo1\")\n  addTestServer(\"foo2\")\n  addTestAccount(\"ron\", \"foo1\")\n  addTestAccount(\"ron\", \"foo2\")\n\n  app_dir <- withr::local_tempdir()\n  file.create(file.path(app_dir, \"app.R\"))\n\n  expect_snapshot(error = TRUE, {\n    findDeploymentTarget(app_dir)\n    findDeploymentTarget(app_dir, appName = \"test\")\n  })\n})\n\ntest_that(\"uses appId given a local deployment record; created by a local account\", {\n  # Demonstrates that the deployment record is sufficient without a call to\n  # the remote server.\n  local_temp_config()\n  addTestServer(\"local\")\n  addTestAccount(\"leslie\", \"local\")\n\n  app_dir <- withr::local_tempdir()\n  addTestDeployment(\n    app_dir,\n    appName = \"local-record\",\n    appId = \"the-appid\",\n    account = \"leslie\",\n    server = \"local\"\n  )\n\n  target <- findDeploymentTarget(app_dir, appId = \"the-appid\")\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"leslie\")\n  expect_equal(accountDetails$server, \"local\")\n  expect_equal(deployment$appId, \"the-appid\")\n  expect_equal(deployment$name, \"local-record\")\n  expect_equal(deployment$username, \"leslie\")\n  expect_equal(deployment$account, \"leslie\")\n  expect_equal(deployment$server, \"local\")\n})\n\ntest_that(\"uses appId given a local deployment record; created by a collaborator\", {\n  # Demonstrates that the target account does not need to be the account that\n  # created the deployment record. The deployment record is sufficient without\n  # a call to the remote server.\n  local_temp_config()\n  addTestServer(\"local\")\n  addTestAccount(\"leslie\", \"local\")\n\n  app_dir <- withr::local_tempdir()\n  addTestDeployment(\n    app_dir,\n    appName = \"local-record\",\n    appId = \"the-appid\",\n    account = \"ron\",\n    server = \"local\"\n  )\n\n  target <- findDeploymentTarget(app_dir, appId = \"the-appid\")\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"leslie\")\n  expect_equal(accountDetails$server, \"local\")\n  expect_equal(deployment$appId, \"the-appid\")\n  expect_equal(deployment$name, \"local-record\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"local\")\n})\n\ntest_that(\"uses appId without local deployment record; created by local account\", {\n  local_temp_config()\n  addTestServer(\"local\")\n  addTestAccount(\"leslie\", \"local\")\n\n  local_mocked_bindings(\n    getApplication = function(...) {\n      data.frame(\n        id = \"the-appid\",\n        name = \"remote-record\",\n        owner_username = \"leslie\",\n        stringsAsFactors = FALSE\n      )\n    }\n  )\n\n  app_dir <- withr::local_tempdir()\n\n  target <- findDeploymentTarget(app_dir, appId = \"the-appid\")\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"leslie\")\n  expect_equal(accountDetails$server, \"local\")\n  expect_equal(deployment$appId, \"the-appid\")\n  expect_equal(deployment$name, \"remote-record\")\n  expect_equal(deployment$username, \"leslie\")\n  expect_equal(deployment$account, \"leslie\")\n  expect_equal(deployment$server, \"local\")\n})\n\ntest_that(\"uses appId without local deployment record; created by collaborator\", {\n  local_temp_config()\n  addTestServer(\"local\")\n  addTestAccount(\"leslie\", \"local\")\n\n  app_dir <- withr::local_tempdir()\n\n  local_mocked_bindings(\n    getApplication = function(...) {\n      data.frame(\n        id = \"the-appid\",\n        name = \"remote-record\",\n        owner_username = \"ron\",\n        stringsAsFactors = FALSE\n      )\n    }\n  )\n\n  target <- findDeploymentTarget(app_dir, appId = \"the-appid\")\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"leslie\")\n  expect_equal(accountDetails$server, \"local\")\n  expect_equal(deployment$appId, \"the-appid\")\n  expect_equal(deployment$name, \"remote-record\")\n  expect_equal(deployment$username, \"ron\")\n  # note: account+server does not correspond to the \"ron\" account, but this is\n  # the best we can do, as we do not have the original deployment record.\n  expect_equal(deployment$account, \"leslie\")\n  expect_equal(deployment$server, \"local\")\n})\n\n\ntest_that(\"handles accounts if only server specified\", {\n  local_temp_config()\n  addTestServer(\"foo\")\n  addTestAccount(\"ron\", \"foo\")\n  addTestAccount(\"john\", \"foo\")\n  local_mocked_bindings(getAppByName = function(...) NULL)\n\n  app_dir <- withr::local_tempdir()\n  file.create(file.path(app_dir, \"app.R\"))\n\n  expect_snapshot(findDeploymentTarget(app_dir, server = \"foo\"), error = TRUE)\n\n  target <- findDeploymentTarget(\n    app_dir,\n    server = \"foo\",\n    account = \"ron\"\n  )\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"foo\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"foo\")\n})\n\ntest_that(\"errors/prompts if multiple deployments\", {\n  local_temp_config()\n  addTestServer(\"server1.com\")\n  addTestServer(\"server2.com\")\n  addTestAccount(\"ron\", \"server1.com\")\n  addTestAccount(\"ron\", \"server2.com\")\n\n  app_dir <- withr::local_tempdir()\n  addTestDeployment(app_dir, server = \"server1.com\")\n  addTestDeployment(app_dir, server = \"server2.com\")\n\n  expect_snapshot(error = TRUE, {\n    findDeploymentTarget(app_dir, appName = \"test\")\n    findDeploymentTarget(app_dir)\n  })\n\n  simulate_user_input(1)\n  expect_snapshot(target <- findDeploymentTarget(app_dir))\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"server1.com\")\n  expect_equal(deployment$name, \"test\")\n})\n\ntest_that(\"succeeds if there's a single existing deployment\", {\n  local_temp_config()\n  addTestServer(\"example.com\")\n  addTestAccount(\"ron\")\n\n  app_dir <- withr::local_tempdir()\n  addTestDeployment(\n    app_dir,\n    appName = \"test\",\n    appId = \"1\",\n    username = \"ron\",\n    account = \"ron\",\n    server = \"example.com\",\n    version = \"999\"\n  )\n  expect_equal(\n    nrow(deployments(\n      app_dir,\n      accountFilter = \"ron\",\n      serverFilter = \"example.com\"\n    )),\n    1\n  )\n  expect_equal(nrow(deployments(app_dir)), 1)\n\n  target <- findDeploymentTarget(app_dir)\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"example.com\")\n  expect_equal(deployment$appId, \"1\")\n  expect_equal(deployment$name, \"test\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"example.com\")\n  expect_equal(deployment$version, \"999\")\n\n  target <- findDeploymentTarget(app_dir, appName = \"test\")\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"example.com\")\n  expect_equal(deployment$appId, \"1\")\n  expect_equal(deployment$name, \"test\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"example.com\")\n  expect_equal(deployment$version, \"999\")\n})\n\ntest_that(\"appId is used even when name does not match\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\")\n\n  app_dir <- withr::local_tempdir()\n  addTestDeployment(app_dir, appName = \"test\", appId = \"1\", username = \"ron\")\n  addTestDeployment(app_dir, appName = \"second\", appId = \"2\", username = \"ron\")\n\n  target <- findDeploymentTarget(app_dir, appName = \"mismatched\", appId = \"1\")\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"example.com\")\n  expect_equal(deployment$appId, \"1\")\n})\n\ntest_that(\"new title overrides existing title\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\")\n\n  app_dir <- withr::local_tempdir()\n  addTestDeployment(app_dir, appTitle = \"old title\")\n\n  target <- findDeploymentTarget(app_dir)\n  deployment <- target$deployment\n  expect_equal(deployment$title, \"old title\")\n\n  target <- findDeploymentTarget(app_dir, appTitle = \"new title\")\n  deployment <- target$deployment\n  expect_equal(deployment$title, \"new title\")\n})\n\ntest_that(\"new env vars overrides existing\", {\n  local_temp_config()\n  app <- local_temp_app()\n  addTestServer()\n  addTestAccount()\n  addTestDeployment(app, envVars = \"TEST1\")\n\n  target <- findDeploymentTarget(app)\n  deployment <- target$deployment\n  expect_equal(deployment$envVars, \"TEST1\")\n\n  target <- findDeploymentTarget(app, envVars = \"TEST2\")\n  deployment <- target$deployment\n  expect_equal(deployment$envVars, \"TEST2\")\n\n  # And check that it works with vectors\n  addTestDeployment(app, envVars = c(\"TEST1\", \"TEST2\"))\n  target <- findDeploymentTarget(app)\n  deployment <- target$deployment\n  expect_equal(deployment$envVars, c(\"TEST1\", \"TEST2\"))\n\n  target <- findDeploymentTarget(app, envVars = \"TEST2\")\n  deployment <- target$deployment\n  expect_equal(deployment$envVars, \"TEST2\")\n})\n\ntest_that(\"empty character vector removes env vars\", {\n  local_temp_config()\n  app <- local_temp_app()\n  addTestServer()\n  addTestAccount()\n  addTestDeployment(app, envVars = \"TEST1\")\n\n  target <- findDeploymentTarget(app, envVars = character())\n  deployment <- target$deployment\n  expect_equal(deployment$envVars, character())\n})\n\ntest_that(\"succeeds if there are no deployments and a single account\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\")\n  local_mocked_bindings(\n    getAppByName = function(...) {\n      data.frame(\n        name = \"remotename\",\n        url = \"app-url\",\n        stringsAsFactors = FALSE\n      )\n    }\n  )\n\n  app_dir <- dirCreate(file.path(withr::local_tempdir(), \"my_app\"))\n\n  expect_snapshot(error = TRUE, {\n    findDeploymentTarget(app_dir)\n  })\n\n  simulate_user_input(1)\n  target <- findDeploymentTarget(app_dir)\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"example.com\")\n  expect_equal(deployment$name, \"remotename\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"example.com\")\n\n  target <- findDeploymentTarget(app_dir, forceUpdate = TRUE)\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"example.com\")\n  expect_equal(deployment$name, \"remotename\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"example.com\")\n\n  target <- findDeploymentTarget(\n    app_dir,\n    envVars = c(\"TEST1\", \"TEST2\"),\n    forceUpdate = TRUE\n  )\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"example.com\")\n  expect_equal(deployment$name, \"remotename\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"example.com\")\n  expect_equal(deployment$envVars, c(\"TEST1\", \"TEST2\"))\n\n  target <- findDeploymentTarget(app_dir, appName = \"foo\", forceUpdate = TRUE)\n  accountDetails <- target$accountDetails\n  deployment <- target$deployment\n  expect_equal(accountDetails$name, \"ron\")\n  expect_equal(accountDetails$server, \"example.com\")\n  expect_equal(deployment$name, \"remotename\")\n  expect_equal(deployment$username, \"ron\")\n  expect_equal(deployment$account, \"ron\")\n  expect_equal(deployment$server, \"example.com\")\n})\n\ntest_that(\"default title is the empty string\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\")\n  local_mocked_bindings(\n    getAppByName = function(...) {\n      data.frame(\n        name = \"remotename\",\n        url = \"app-url\",\n        stringsAsFactors = FALSE\n      )\n    }\n  )\n\n  app_dir <- withr::local_tempdir()\n  target <- findDeploymentTarget(app_dir, forceUpdate = TRUE)\n  deployment <- target$deployment\n  expect_equal(deployment$title, \"\")\n})\n\nconfirm_existing_app_used <- function(server) {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\", server = server)\n  local_mocked_bindings(\n    getAppByName = function(...) {\n      data.frame(\n        name = \"my_app\",\n        id = 123,\n        url = \"http://example.com/test\",\n        stringsAsFactors = FALSE\n      )\n    },\n    shouldUpdateApp = function(...) TRUE\n  )\n\n  app_dir <- withr::local_tempdir()\n  target <- findDeploymentTarget(app_dir, appName = \"my_app\", server = server)\n  deployment <- target$deployment\n  expect_equal(deployment$appId, 123)\n}\n\ntest_that(\"can find existing application on server & use it\", {\n  confirm_existing_app_used(\"example.com\")\n})\n\ntest_that(\"can find existing application on shinyapps.io & use it\", {\n  confirm_existing_app_used(\"shinyapps.io\")\n})\n\nconfirm_existing_app_not_used <- function(server) {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"ron\", server = server)\n  local_mocked_bindings(\n    getAppByName = function(...) {\n      data.frame(\n        name = \"my_app\",\n        id = 123,\n        url = \"http://example.com/test\",\n        stringsAsFactors = FALSE\n      )\n    },\n    shouldUpdateApp = function(...) FALSE\n  )\n\n  app_dir <- withr::local_tempdir()\n  target <- findDeploymentTarget(app_dir, appName = \"my_app\", server = server)\n  deployment <- target$deployment\n  expect_equal(deployment$name, \"my_app-1\")\n  expect_equal(deployment$appId, NULL)\n}\n\ntest_that(\"can find existing application on server & not use it\", {\n  confirm_existing_app_not_used(\"example.com\")\n})\n\ntest_that(\"can find existing application on shinyapps.io & not use it\", {\n  confirm_existing_app_not_used(\"shinyapps.io\")\n})\n\n# helpers -----------------------------------------------------------------\n\ntest_that(\"shouldUpdateApp errors when non-interactive\", {\n  app <- list(name = \"my_app\", url = \"https://example.com\")\n\n  expect_snapshot(shouldUpdateApp(app, \"my_app-1\"), error = TRUE)\n})\n\ntest_that(\"forceUpdate shortcircuits shouldUpdateApp\", {\n  expect_true(shouldUpdateApp(forceUpdate = TRUE))\n})\n\ntest_that(\"shouldUpdateApp handles 3 options\", {\n  app <- list(name = \"my_app\", url = \"https://example.com\")\n\n  simulate_user_input(c(1, 2, 3))\n  expect_snapshot(error = TRUE, {\n    one <- shouldUpdateApp(app, \"my_app-1\")\n    two <- shouldUpdateApp(app, \"my_app-1\")\n    three <- shouldUpdateApp(app, \"my_app-1\")\n  })\n  expect_equal(one, TRUE)\n  expect_equal(two, FALSE)\n})\n\ntest_that(\"findUnique always returns unique name\", {\n  expect_equal(findUnique(\"x\", c(\"x\", \"y\")), \"x-1\")\n  expect_equal(findUnique(\"x\", c(\"x\", \"x-1\")), \"x-2\")\n  expect_equal(findUnique(\"x\", c(\"x\", \"x-1\", \"x-2\")), \"x-3\")\n})\n\ntest_that(\"createDeploymentFromApplication promotes fields\", {\n  expect_equal(\n    createDeploymentFromApplication(\n      application = list(\n        id = \"1\",\n        name = \"app-name\",\n        title = \"app-title\",\n        owner_username = \"alice.username\"\n      ),\n      accountDetails = list(\n        name = \"alice\",\n        server = \"example.com\"\n      )\n    ),\n    list(\n      name = \"app-name\",\n      title = \"app-title\",\n      envVars = NULL,\n      appId = \"1\",\n      username = \"alice.username\",\n      account = \"alice\",\n      server = \"example.com\",\n      version = deploymentRecordVersion\n    )\n  )\n})\n\ntest_that(\"updateDeployment updates fields\", {\n  expect_equal(\n    updateDeployment(\n      list(\n        name = \"app-name\",\n        title = \"app-title\",\n        envVars = NULL,\n        appId = \"1\",\n        username = \"alice.username\",\n        account = \"alice\",\n        server = \"example.com\",\n        version = deploymentRecordVersion\n      ),\n      appTitle = \"updated-title\",\n      envVars = c(\"VAR-NAME\")\n    ),\n    list(\n      name = \"app-name\",\n      title = \"updated-title\",\n      envVars = c(\"VAR-NAME\"),\n      appId = \"1\",\n      username = \"alice.username\",\n      account = \"alice\",\n      server = \"example.com\",\n      version = deploymentRecordVersion\n    )\n  )\n})\n\ntest_that(\"updateDeployment ignores NULL updates\", {\n  expect_equal(\n    updateDeployment(\n      list(\n        name = \"app-name\",\n        title = \"app-title\",\n        envVars = c(\"VAR-NAME\"),\n        appId = \"1\",\n        username = \"alice.username\",\n        account = \"alice\",\n        server = \"example.com\",\n        version = deploymentRecordVersion\n      ),\n      appTitle = NULL,\n      envVars = NULL\n    ),\n    list(\n      name = \"app-name\",\n      title = \"app-title\",\n      envVars = c(\"VAR-NAME\"),\n      appId = \"1\",\n      username = \"alice.username\",\n      account = \"alice\",\n      server = \"example.com\",\n      version = deploymentRecordVersion\n    )\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-deployments-find.R",
    "content": "test_that(\"error when no deployments and no accounts\", {\n  local_temp_config()\n\n  app <- local_temp_app()\n\n  expect_snapshot(findDeployment(app, appName = \"placeholder\"), error = TRUE)\n})\n\ntest_that(\"returns stubbed details when single account has no deployments\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount()\n\n  app <- local_temp_app()\n\n  # derived from directory name.\n  dep <- findDeployment(app)\n  expect_equal(\n    dep,\n    list(name = basename(app), account = \"ron\", server = \"example.com\")\n  )\n\n  # name given.\n  dep <- findDeployment(app, appName = \"placeholder\")\n  expect_equal(\n    dep,\n    list(name = \"placeholder\", account = \"ron\", server = \"example.com\")\n  )\n})\n\ntest_that(\"finds single deployment\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount()\n\n  app <- local_temp_app()\n  addTestDeployment(app)\n\n  dep <- findDeployment(app)\n  expect_equal(dep$name, \"test\")\n})\n\ntest_that(\"disambiguates multiple deployments\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount()\n\n  app <- local_temp_app()\n  addTestDeployment(app, \"test1\")\n  addTestDeployment(app, \"test2\")\n  expect_snapshot(findDeployment(app), error = TRUE)\n\n  simulate_user_input(2)\n  expect_snapshot(dep <- findDeployment(app))\n  expect_equal(dep$name, \"test2\")\n})\n"
  },
  {
    "path": "tests/testthat/test-deployments.R",
    "content": "test_that(\"deployments() works empty data frame if no deployments\", {\n  dir <- local_temp_app()\n  out <- deployments(dir)\n  expect_s3_class(out, \"data.frame\")\n  expect_named(out, c(deploymentFields, \"deploymentFile\"))\n  expect_equal(nrow(out), 0)\n})\n\ntest_that(\"combines fields across deployments\", {\n  local_temp_config()\n  dir <- local_temp_app()\n\n  writeDeploymentRecord(\n    list(x = 1),\n    deploymentConfigFile(dir, \"app1\", \"account\", \"server\")\n  )\n  writeDeploymentRecord(\n    list(y = 1),\n    deploymentConfigFile(dir, \"app2\", \"account\", \"server\")\n  )\n\n  out <- deployments(dir, excludeOrphaned = FALSE)\n  expect_s3_class(out, \"data.frame\")\n  expect_named(out, c(deploymentFields, \"x\", \"y\", \"deploymentFile\"))\n  expect_equal(nrow(out), 2)\n})\n\ntest_that(\"deployments() can filter\", {\n  local_temp_config()\n  addTestServer(\"foo\")\n  addTestServer(\"bar\")\n  addTestAccount(\"foo\", \"foo\")\n  addTestAccount(\"bar\", \"bar\")\n  dir <- local_temp_app()\n  addTestDeployment(dir, appName = \"my-app\", account = \"foo\", server = \"foo\")\n  addTestDeployment(dir, appName = \"my-app2\", account = \"bar\", server = \"bar\")\n\n  out <- deployments(dir)\n  expect_s3_class(out, \"data.frame\")\n  expect_equal(nrow(out), 2)\n\n  out <- deployments(dir, nameFilter = \"my-app\")\n  expect_equal(nrow(out), 1)\n\n  out <- deployments(dir, serverFilter = \"foo\")\n  expect_equal(nrow(out), 1)\n\n  out <- deployments(dir, accountFilter = \"foo\")\n  expect_equal(nrow(out), 1)\n})\n\ntest_that(\"deployments() can exclude orphans\", {\n  dir <- local_temp_app()\n  addTestDeployment(dir, server = \"bar1\")\n\n  out <- deployments(dir)\n  expect_equal(nrow(out), 0)\n\n  out <- deployments(dir, excludeOrphaned = FALSE)\n  expect_equal(nrow(out), 1)\n})\n\ntest_that(\"can read/write metadata\", {\n  dir <- local_temp_app()\n\n  addTestDeployment(dir, metadata = list(meta1 = \"one\", meta2 = \"two\"))\n  out <- deployments(dir, excludeOrphaned = FALSE)\n  expect_equal(out$meta1, \"one\")\n  expect_equal(out$meta2, \"two\")\n})\n\ntest_that(\"can read/write metadata having multiple values\", {\n  dir <- local_temp_app()\n\n  addTestDeployment(dir, metadata = list(engines = c(\"knitr\", \"markdown\")))\n  out <- deployments(dir, excludeOrphaned = FALSE)\n  expect_equal(nrow(out), 1)\n  expect_equal(out$engines, \"knitr, markdown\")\n})\n\ntest_that(\"can read/write version\", {\n  dir <- local_temp_app()\n\n  addTestDeployment(dir, version = \"999\")\n  out <- deployments(dir, excludeOrphaned = FALSE)\n  expect_equal(out$version, \"999\")\n})\n\ntest_that(\"can read/write missing version\", {\n  # also tests we can read files written by previous versions of package\n  dir <- local_temp_app()\n\n  path <- addTestDeployment(dir, version = NA)\n  out <- deployments(dir, excludeOrphaned = FALSE)\n\n  expect_false(\"version\" %in% rownames(read.dcf(path)))\n  expect_equal(out$version, NA)\n})\n\ntest_that(\"can read/write env vars\", {\n  app <- local_temp_app()\n  addTestDeployment(app, \"test1\", envVars = c(\"TEST1\", \"TEST2\"))\n  addTestDeployment(app, \"test2\")\n\n  deps <- deployments(app, excludeOrphaned = FALSE)\n  expect_equal(deps$envVars, list(c(\"TEST1\", \"TEST2\"), character()))\n})\n\ntest_that(\"can read/write empty env vars\", {\n  # also tests we can read files written by previous versions of package\n  app <- local_temp_app()\n\n  # With empty character vector\n  path <- addTestDeployment(app, \"test1\", envVars = character())\n  deps <- deployments(app, excludeOrphaned = FALSE)\n  expect_false(\"envVars\" %in% rownames(read.dcf(path)))\n  expect_equal(deps$envVars, list(character()))\n\n  # Or with empty string\n  path <- addTestDeployment(app, \"test1\", envVars = \"\")\n  deps <- deployments(app, excludeOrphaned = FALSE)\n  expect_false(\"envVars\" %in% rownames(read.dcf(path)))\n  expect_equal(deps$envVars, list(character()))\n})\n\ntest_that(\"can read/write env vars\", {\n  app <- local_temp_app()\n  addTestDeployment(app, \"test1\", envVars = c(\"TEST1\", \"TEST2\"))\n  addTestDeployment(app, \"test2\")\n\n  deps <- deployments(app, excludeOrphaned = FALSE)\n  expect_equal(deps$envVars, list(c(\"TEST1\", \"TEST2\"), character()))\n})\n\ntest_that(\"saveDeployment appends to global history\", {\n  local_temp_config()\n  addTestServer(\"foo\")\n  addTestAccount(\"bar\", \"foo\")\n\n  dir <- local_temp_app()\n\n  saveDeployment(\n    dir,\n    createDeployment(\n      appName = \"my-app\",\n      appTitle = \"\",\n      appId = 10,\n      envVars = \"abc\", # ensure there's an envVars column in output\n      account = \"foo\",\n      username = \"foo\",\n      server = \"bar\",\n      version = 1\n    ),\n    application = list(id = 1),\n    hostUrl = NULL\n  )\n\n  history <- read.dcf(deploymentHistoryPath())\n  expect_equal(nrow(history), 1)\n  expect_setequal(colnames(history), c(deploymentFields, \"appPath\"))\n})\n\ntest_that(\"saveDeployment captures hostUrl\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"foo\")\n\n  dir <- local_temp_app()\n  saveDeployment(\n    dir,\n    createDeployment(\n      appName = \"my-app\",\n      appTitle = \"\",\n      appId = 10,\n      envVars = NULL,\n      account = \"foo\",\n      username = \"foo\",\n      server = \"example.com\",\n      version = 1\n    ),\n    application = list(id = 10)\n  )\n\n  out <- deployments(dir)\n  expect_equal(out$hostUrl, \"https://example.com\")\n})\n\ntest_that(\"addToDeploymentHistory() adds needed new lines\", {\n  local_temp_config()\n\n  expect_snapshot({\n    addToDeploymentHistory(\"path\", list(x = 1))\n    writeLines(readLines(deploymentHistoryPath()))\n    addToDeploymentHistory(\"path\", list(x = 2))\n    writeLines(readLines(deploymentHistoryPath()))\n  })\n})\n\ntest_that(\"addToDeploymentHistory() caps records at rsconnect.max.history.records\", {\n  local_temp_config()\n\n  withr::local_options(rsconnect.max.history.records = 3L)\n\n  expect_snapshot({\n    for (i in 1:5) {\n      addToDeploymentHistory(\"path\", list(x = i))\n    }\n    writeLines(readLines(deploymentHistoryPath()))\n  })\n})\n\ntest_that(\"addToDeploymentHistory() preserves only the new record when max_records = 1L\", {\n  local_temp_config()\n\n  # Create existing history before capping.\n  addToDeploymentHistory(\"path\", list(x = 1))\n  addToDeploymentHistory(\"path\", list(x = 2))\n\n  withr::local_options(rsconnect.max.history.records = 1L)\n\n  expect_snapshot({\n    addToDeploymentHistory(\"path\", list(x = 3))\n    writeLines(readLines(deploymentHistoryPath()))\n  })\n})\n\ntest_that(\"addToDeploymentHistory() restarts when history exceeds rsconnect.max.history.bytes\", {\n  local_temp_config()\n\n  # Create some records before constraining the size of the history file.\n  addToDeploymentHistory(\"path\", list(x = 1))\n  addToDeploymentHistory(\"path\", list(x = 2))\n\n  withr::local_options(rsconnect.max.history.bytes = 0L)\n\n  expect_snapshot({\n    addToDeploymentHistory(\"path\", list(x = 3))\n    writeLines(readLines(deploymentHistoryPath()))\n  })\n})\n"
  },
  {
    "path": "tests/testthat/test-http-httr2.R",
    "content": "test_that(\"basic HTTP methods work (httr2)\", {\n  skip_if_not_installed(\"webfakes\")\n\n  test_http_GET()\n  test_http_POST_JSON()\n  test_http_POST_empty()\n  test_http_POST_file()\n  test_http_headers()\n})\n"
  },
  {
    "path": "tests/testthat/test-http-libcurl.R",
    "content": "withr::local_options(rsconnect.httr2 = FALSE)\n\ntest_that(\"basic HTTP methods work\", {\n  skip_if_not_installed(\"webfakes\")\n\n  test_http_GET()\n  test_http_POST_JSON()\n  test_http_POST_empty()\n  test_http_POST_file()\n  test_http_headers()\n})\n\ntest_that(\"can trace JSON\", {\n  skip_if_not_installed(\"webfakes\")\n\n  withr::local_options(rsconnect.http.trace.json = TRUE)\n  service <- httpbin_service()\n\n  json_app <- webfakes::new_app()\n  json_app$use(webfakes::mw_json())\n  json_app$post(\"/\", function(req, res) {\n    res$set_status(200L)$send_json(req$json)\n  })\n  app <- webfakes::new_app_process(json_app)\n  service <- parseHttpUrl(app$url())\n\n  expect_snapshot({\n    . <- POST_JSON(service, list(), \"\", list(a = 1, b = 2))\n  })\n})\n\ntest_that(\"can get and set cookies\", {\n  skip_on_cran()\n  skip_if_not_installed(\"webfakes\")\n\n  # uses live httpbin since webfakes doesn't support cookie endpoints\n  service <- parseHttpUrl(\"http://httpbin.org/\")\n\n  # Setting\n  skip_on_http_failure(GET(service, list(), \"cookies/set\", query = \"a=1\"))\n  cookies <- getCookies(service$host)\n  expect_equal(cookies$name, \"a\")\n  expect_equal(cookies$value, \"1\")\n\n  # Overwriting\n  skip_on_http_failure(GET(service, list(), \"cookies/set\", query = \"a=2&b=1\"))\n  cookies <- getCookies(service$host)\n  expect_equal(cookies$name, c(\"b\", \"a\"))\n  expect_equal(cookies$value, c(\"1\", \"2\"))\n\n  # Preserved across calls\n  skip_on_http_failure(out <- GET(service, list(), \"cookies\"))\n  expect_equal(out$cookies, list(a = \"2\", b = \"1\"))\n})\n"
  },
  {
    "path": "tests/testthat/test-http.R",
    "content": "# headers -----------------------------------------------------------------\n\ntest_that(\"authHeaders() picks correct method based on supplied fields\", {\n  skip_on_cran() # CRAN may not have an openssl that permits SHA-1 signatures.\n\n  url <- \"https://example.com\"\n\n  expect_equal(\n    authHeaders(list(), url, \"GET\"),\n    list(\"X-Auth-Token\" = \"anonymous-access\")\n  )\n  expect_equal(\n    authHeaders(list(apiKey = \"123\"), url, \"GET\"),\n    list(Authorization = \"Key 123\")\n  )\n  expect_equal(\n    authHeaders(list(accessToken = \"abc123\"), url, \"GET\"),\n    list(Authorization = \"Bearer abc123\")\n  )\n\n  local_mocked_bindings(\n    rfc2616Date = function() \"Thu, 09 Mar 2023 14:29:00 GMT\"\n  )\n\n  # Dummy key created with\n  # openssl::base64_encode(openssl::rsa_keygen(2048L))\n  key_string <- \"-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/eiSQAKXADslq\nGGsbsQu2eEgEHD06BtUhaeU1nvsY7a6u12xpG0OAGYWhnGhR+1K/3qoZQQNmN0MC\nZV0zhueREu3YaqtNwXbnbGCqp7tsLsx2cb2TZscmBNXkOLah2PlsBTfInNlKrpKH\nwsMtsL7yruXwJ767ey8JujMAqO90jj57idfhkr4IU47EL8DiIHTOAYLddKe/d4AL\nskoEFQM37SJ9JUwZUsvz2eqEXFPV4QrS93T79sA7QfH5zOYTLKPEDxV0U7AYUVKW\nbBieVRlu3DIQfdsqXwYYzHUR+HBlxe1VowCS5p5c6coNLdb+RElcSSY2Gd06eBg0\nwSoxkyT3AgMBAAECggEAHmQinYCczkkKiv5pMbH+K+4XcB+TUDx5Y55NNR+Qtzoy\neanTmTMsmY5zeV076Zc8YRvUX8HD6ltnFWBFVMJaytn4SltT6TmFx+GZzjxlMRyU\nc1BGSLkNbulhkaG2yyWHITAK1Jqgmovu0gGFvSDKjfZYpK+KRHOe2apmIfquVw9d\n6CLm7swx30x8qFKEACc3iM/Mcc9uKOWn+NjVKqUVfU/9ZmNxmjZ9e3wkwieI1mSB\ngq9q7fmCHHkGWbUkGMphgRyssaJbz+bn/Wz2uwevvtebkTOzvWtKhUGgDByCCCtq\nJ1ehPCjdRjylb6C5tLbyeng5QnYC7uZJmRsEOUwMEQKBgQD6PsHXwKJ8JRlDGu4G\nJQ9fKzYA+B+No0GKtGuuRDD8tJouj5rb1dZt181UHUW/UtjNpz8j8l1RN2gPcu/v\nVjwSXDcZHohv4cgQfRCN47wREEEb/LP/fhxIt2H320vh5qvwdJJoGvGoJfR5vO1X\nysdIPBajIgnEo4U7cawaNfeS1QKBgQDD4WgXUb0AYvK6lggVVqgNzxxMQRWCQSGb\ny4RISHlC2TpftpbLFdr6fHuf8bzGq462xG5MFMMGBsbESXKNW18QA+uIBIDwttaj\nAfQ4+PNu4m+2Ump+RcGu2MYoBJoxjMx00Ba76cEMF0+X+RO4zcfwZ6Y869Fakq+D\n7rn4ZetGmwKBgQCMtgsjeUMkUWwCCrt6ow4gslh8dQixCPKKvuapp9hv0FG+CqvG\nH1iijSz8tjUI3tnf0cI0QUztpR0TSsrVpoTCwi2NJ1kKqEdp1hkf38VZRu2FgjPo\nXw4iaVNiHmJt1NorrDDC7xuhNC5i4bQHoJMr7/W+px4c/uGkykc+ucfLPQKBgQCG\n/E/KOibgHFAfawLZCaW4FnDuz68t2wp5HY/kbCU8fwxuJxrVixMjqSNcfq9TzagE\npWtI/MnE3midnevWJBBnrfvi+Q+OUsGpBdCybkT7tgm8ACGpMRMfFf3AWCOWX+wJ\n19jC2HyTg4DzPs9rfEv7jMIPm4bjPtC7P4li94FiXwKBgBTh5tPUEvG0chZvqRT2\ng0vvWgJGF52FCXBij3dnNl1eNRQYbDI+hNbZYcHCKHKaOoDWaqYhyjLk6Tz0LhLe\nXWAlOP6tE2UbEgi10wyaEI9EyfXg1mgiHlSg+oZMCx05TUE6PrzddS6qUOJfN7P3\na3hEFijsjg/+FDMr+iAVzjry\n-----END PRIVATE KEY-----\"\n  key <- openssl::base64_encode(openssl::read_key(key_string))\n\n  expect_snapshot({\n    str(authHeaders(list(secret = openssl::base64_encode(\"123\")), url, \"GET\"))\n    str(authHeaders(list(private_key = key), url, \"GET\"))\n  })\n\n  # and that signRequestPrivateKey() is the same as openssl equivalent\n  # authHeaders does this conversion internally\n  private_key <- openssl::read_key(\n    openssl::base64_decode(key),\n    der = TRUE\n  )\n\n  # an alternative implementation with openssl\n  signRequestPrivateKeyOpenSSL <- function(private_key, canonicalRequest) {\n    rawsig <- openssl::signature_create(\n      charToRaw(canonicalRequest),\n      key = private_key\n    )\n    openssl::base64_encode(rawsig)\n  }\n\n  expect_identical(\n    signRequestPrivateKey(private_key, url),\n    signRequestPrivateKeyOpenSSL(private_key, url)\n  )\n})\n\ntest_that(\"can add user specific headers\", {\n  skip_if_not_installed(\"webfakes\")\n\n  withr::local_options(rsconnect.http.headers = c(a = \"1\", b = \"2\"))\n\n  service <- httpbin_service()\n  json <- GET(service, list(), \"get\")\n  expect_equal(json$headers$a, \"1\")\n  expect_equal(json$headers$b, \"2\")\n})\n\ntest_that(\"can add user specific cookies\", {\n  skip_on_cran()\n  skip_if_not_installed(\"webfakes\")\n\n  # uses live httpbin since webfakes doesn't support cookie endpoints\n  withr::local_options(rsconnect.http.cookies = c(\"a=1\", \"b=2\"))\n  service <- parseHttpUrl(\"http://httpbin.org/\")\n\n  skip_on_http_failure(json <- GET(service, list(), \"cookies\"))\n  expect_equal(json$cookies, list(a = \"1\", b = \"2\"))\n\n  withr::local_options(rsconnect.http.cookies = c(\"c=3\", \"d=4\"))\n  skip_on_http_failure(POST(service, list(), \"post\"))\n  skip_on_http_failure(json <- GET(service, list(), \"cookies\"))\n  expect_equal(json$cookies, list(a = \"1\", b = \"2\", c = \"3\", d = \"4\"))\n})\n\n# handleResponse ----------------------------------------------------------\n\ntest_that(\"includes body in error if available\", {\n  service <- parseHttpUrl(\"http://example.com/error\")\n  service$method <- \"GET\"\n\n  resp_text <- list(\n    req = service,\n    status = 400,\n    contentType = \"plain/text\",\n    content = \"Failed\"\n  )\n  resp_json <- list(\n    req = service,\n    status = 400,\n    contentType = \"application/json\",\n    content = '{\"error\": \"failed\"}'\n  )\n  resp_html <- list(\n    req = service,\n    status = 400,\n    contentType = \"text/html\",\n    content = \"<body>Failed</body>\"\n  )\n\n  expect_snapshot(error = TRUE, {\n    handleResponse(resp_text)\n    handleResponse(resp_json)\n    handleResponse(resp_html)\n  })\n})\n\ntest_that(\"but still gives got error if no body\", {\n  service <- parseHttpUrl(\"http://example.com/error\")\n\n  resp_text <- list(\n    req = service,\n    status = 400,\n    contentType = \"plain/text\",\n    content = \"\"\n  )\n  resp_json <- list(\n    req = service,\n    status = 400,\n    contentType = \"application/json\",\n    content = \"\"\n  )\n  resp_html <- list(\n    req = service,\n    status = 400,\n    contentType = \"text/html\",\n    content = \"\"\n  )\n\n  expect_snapshot(error = TRUE, {\n    handleResponse(resp_text)\n    handleResponse(resp_json)\n    handleResponse(resp_html)\n  })\n})\n\ntest_that(\"errors contain method\", {\n  skip_if_not_installed(\"webfakes\")\n\n  service <- httpbin_service()\n  expect_snapshot(\n    error = TRUE,\n    {\n      GET(service, list(), path = \"status/404\")\n      POST(service, list(), path = \"status/403\")\n    },\n    transform = strip_port(service)\n  )\n})\n\ntest_that(\"http error includes status in error class\", {\n  skip_if_not_installed(\"webfakes\")\n\n  service <- httpbin_service()\n  expect_error(\n    GET(service, list(), path = \"status/404\"),\n    class = \"rsconnect_http_404\"\n  )\n  expect_error(\n    GET(service, list(), path = \"status/403\"),\n    class = \"rsconnect_http_403\"\n  )\n})\n\ntest_that(\"handles redirects\", {\n  skip_if_not_installed(\"webfakes\")\n\n  service <- httpbin_service()\n  out <- GET(service, list(), \"absolute-redirect/3\")\n  expect_equal(out$url, paste0(buildHttpUrl(service), \"get\"))\n\n  out <- GET(service, list(), \"relative-redirect/3\")\n  expect_equal(out$url, paste0(buildHttpUrl(service), \"get\"))\n})\n\n# parse/build -------------------------------------------------------------\n\ntest_that(\"URL parsing works\", {\n  p <- parseHttpUrl(\"http://yahoo.com\")\n  expect_equal(p$protocol, \"http\")\n  expect_equal(p$host, \"yahoo.com\")\n  expect_equal(p$port, \"\")\n  expect_equal(p$path, \"\") #TODO: bug? Should default to /?\n\n  p <- parseHttpUrl(\"https://rstudio.com/about\")\n  expect_equal(p$protocol, \"https\")\n  expect_equal(p$host, \"rstudio.com\")\n  expect_equal(p$port, \"\")\n  expect_equal(p$path, \"/about\")\n\n  p <- parseHttpUrl(\"http://127.0.0.1:3939/stuff/here/?who-knows\")\n  expect_equal(p$protocol, \"http\")\n  expect_equal(p$host, \"127.0.0.1\")\n  expect_equal(p$port, \"3939\")\n  expect_equal(p$path, \"/stuff/here/?who-knows\") #TODO: bug?\n})\n\ntest_that(\"parse and build are symmetric\", {\n  round_trip <- function(x) {\n    expect_equal(buildHttpUrl(parseHttpUrl(x)), x)\n  }\n\n  round_trip(\"http://google.com\")\n  round_trip(\"http://google.com:80\")\n  round_trip(\"https://google.com:80/a/b\")\n  round_trip(\"https://google.com:80/a/b/\")\n})\n\ntest_that(\"rcf2616 returns an ASCII date and undoes changes to the locale\", {\n  old <- Sys.getlocale(\"LC_TIME\")\n  defer(Sys.setlocale(\"LC_TIME\", old))\n\n  date <- rfc2616Date(\n    time = as.POSIXct(\"2024-01-01 01:02:03\", tz = \"America/New_York\")\n  )\n  expect_equal(date, \"Mon, 01 Jan 2024 06:02:03 GMT\")\n  expect_equal(Sys.getlocale(\"LC_TIME\"), old)\n})\n"
  },
  {
    "path": "tests/testthat/test-ide.R",
    "content": "test_that(\"validateServerUrl() when not Connect\", {\n  skip_on_cran()\n  skip_if_not_installed(\"webfakes\")\n\n  service <- service_settings_404()\n\n  url <- buildHttpUrl(service)\n  expect_false(validateServerUrl(url)$valid)\n})\n\ntest_that(\"validateServerUrl() when Connect\", {\n  skip_on_cran()\n  skip_if_not_installed(\"webfakes\")\n\n  service <- service_settings_200()\n  url <- buildHttpUrl(service)\n  expected_url <- paste0(url, \"__api__\")\n\n  redirect <- service_redirect(paste0(url, \"__api__/server_settings\"))\n  redirect_url <- buildHttpUrl(redirect)\n\n  # Full server URL.\n  result <- validateServerUrl(url)\n  expect_true(result$valid, info = url)\n  expect_equal(result$url, expected_url, info = url)\n\n  # Overspecified (includes /__api__)\n  result <- validateServerUrl(expected_url)\n  expect_true(result$valid, info = expected_url)\n  expect_equal(result$url, expected_url, info = expected_url)\n\n  # Incomplete (lacks path).\n  # Lack of protocol is not easily tested because validateConnectUrl()\n  # prefers https://.\n  partial_url <- paste0(\n    service$protocol,\n    \"://\",\n    service$host,\n    \":\",\n    service$port\n  )\n  result <- validateServerUrl(partial_url)\n  expect_true(result$valid, info = partial_url)\n  expect_equal(result$url, expected_url, info = partial_url)\n\n  # Redirects\n  result <- validateServerUrl(redirect_url)\n  expect_true(result$valid)\n  expect_equal(result$url, expected_url)\n})\n\ntest_that(\"validateServerUrl() hosted\", {\n  skip_on_cran()\n\n  expect_false(validateServerUrl(\"https://shinyapps.io\")$valid)\n})\n\ntest_that(\"getAppById() fails where expected\", {\n  local_temp_config()\n  addTestServer()\n  addTestAccount(\"susan\")\n\n  expect_snapshot(error = TRUE, {\n    getAppById(\"123\", \"susan\", \"unknown\", \"unknown.com\")\n    getAppById(\"123\", \"robert\", \"unknown\", \"https://example.com\")\n  })\n})\n\ncurrent_user_service <- function() {\n  app <- env_cache(\n    cache,\n    \"current_user_app\",\n    {\n      json_app <- webfakes::new_app()\n      json_app$use(webfakes::mw_json())\n      json_app$get(\"/users/current\", function(req, res) {\n        res$set_status(200L)$send_json(list(\n          username = jsonlite::unbox(\"susan\")\n        ))\n      })\n      app <- webfakes::new_app_process(json_app)\n    }\n  )\n  parseHttpUrl(app$url())\n}\n\ntest_that(\"getUserFromRawToken having a single matching server\", {\n  skip_if_not_installed(\"webfakes\")\n\n  local_temp_config()\n\n  service <- current_user_service()\n  url <- buildHttpUrl(service)\n\n  addTestServer(\"test\", url = url)\n\n  token <- generateToken()\n  claimUrl <- url\n\n  user <- getUserFromRawToken(claimUrl, token$token, token$private_key)\n  expect_equal(user$username, \"susan\")\n})\n\ntest_that(\"getUserFromRawToken having multiple matching servers\", {\n  skip_if_not_installed(\"webfakes\")\n\n  local_temp_config()\n\n  service <- current_user_service()\n  url <- buildHttpUrl(service)\n\n  addTestServer(\"test\", url = url)\n  addTestServer(\"test2\", url = url)\n\n  token <- generateToken()\n  claimUrl <- url\n\n  user <- getUserFromRawToken(claimUrl, token$token, token$private_key)\n  expect_equal(user$username, \"susan\")\n})\n\ntest_that(\"registerUserToken registers a user token\", {\n  # Checks the arguments as supplied by RStudio as of\n  # https://github.com/rstudio/rstudio/blob/320fff61d4205104d7c42219e0449d1ae35a78b0/src/cpp/session/modules/SessionRSConnect.R#L302-L303\n  local_temp_config()\n\n  registerUserToken(\n    serverName = \"the-server\",\n    accountName = \"the-account\",\n    userId = 42,\n    token = \"the-token\",\n    privateKey = \"the-private-key\"\n  )\n\n  expect_snapshot({\n    accountInfo(\"the-account\")\n  })\n})\n"
  },
  {
    "path": "tests/testthat/test-identityFederation.R",
    "content": "# Helper to create a clean API key cache for tests\nlocal_api_key_cache <- function(env = caller_env()) {\n  nms <- env_names(apiKeyCache)\n  zaps <- rep_named(nms, list(zap()))\n\n  old <- env_bind(apiKeyCache, !!!zaps)\n  withr::defer(env_bind(apiKeyCache, !!!old), envir = env)\n}\n\ntest_that(\"cache stores and retrieves API keys with expiry\", {\n  local_api_key_cache()\n\n  expect_null(getCachedApiKey(\"https://example.com\"))\n\n  # Cache with future expiry\n  future_expiry <- Sys.time() + 3600\n  cacheApiKey(\"https://example.com\", \"test-api-key\", future_expiry)\n  expect_equal(getCachedApiKey(\"https://example.com\"), \"test-api-key\")\n})\n\ntest_that(\"cache returns NULL for expired keys\", {\n  local_api_key_cache()\n\n  # Cache with past expiry (already expired)\n  past_expiry <- Sys.time() - 100\n  cacheApiKey(\"https://example.com\", \"expired-key\", past_expiry)\n\n  # Should return NULL because key is expired\n  expect_null(getCachedApiKey(\"https://example.com\"))\n})\n\ntest_that(\"cache respects expiry buffer\", {\n  local_api_key_cache()\n\n  # Cache with expiry just inside the buffer (should be treated as expired)\n  # Default buffer is 60 seconds\n  almost_expired <- Sys.time() + 30\n  cacheApiKey(\"https://example.com\", \"almost-expired-key\", almost_expired)\n\n  # Should return NULL because within buffer\n  expect_null(getCachedApiKey(\"https://example.com\"))\n})\n\ntest_that(\"cache works without expiry (NULL expiry)\", {\n  local_api_key_cache()\n\n  # Cache without expiry\n  cacheApiKey(\"https://example.com\", \"no-expiry-key\", NULL)\n\n  # Should still return the key\n  expect_equal(getCachedApiKey(\"https://example.com\"), \"no-expiry-key\")\n})\n\ntest_that(\"attemptIdentityFederation returns cached key if available and not expired\", {\n  local_api_key_cache()\n\n  cacheApiKey(\"https://example.com\", \"cached-api-key\", Sys.time() + 3600)\n\n  # Even without Workbench env var, should return cached key\n  withr::local_envvar(POSIT_PRODUCT = \"\", RS_SERVER_ADDRESS = \"\")\n\n  result <- attemptIdentityFederation(\"https://example.com\")\n  expect_equal(result, \"cached-api-key\")\n})\n\ntest_that(\"attemptIdentityFederation returns NULL when not in Workbench\", {\n  local_api_key_cache()\n\n  withr::local_envvar(POSIT_PRODUCT = \"\", RS_SERVER_ADDRESS = \"\")\n\n  expect_null(attemptIdentityFederation(\"https://example.com\"))\n})\n\ntest_that(\"hasNoCredentials correctly detects missing credentials\", {\n  # No credentials at all\n  expect_true(hasNoCredentials(list(server = \"example.com\")))\n\n  # Has apiKey\n  expect_false(hasNoCredentials(list(server = \"example.com\", apiKey = \"key\")))\n\n  # Has token\n  expect_false(hasNoCredentials(list(server = \"example.com\", token = \"tok\")))\n\n  # Has secret\n  expect_false(hasNoCredentials(list(server = \"example.com\", secret = \"sec\")))\n\n  # Has private_key\n  expect_false(\n    hasNoCredentials(list(server = \"example.com\", private_key = \"pk\"))\n  )\n\n  # Has accessToken\n  expect_false(\n    hasNoCredentials(list(server = \"example.com\", accessToken = \"at\"))\n  )\n})\n\ntest_that(\"clientForAccount attempts identity federation for Connect without credentials\", {\n  local_temp_config()\n  local_api_key_cache()\n\n  addTestServer(\"example.com\")\n\n  # Mock successful identity federation\n  local_mocked_bindings(\n    attemptIdentityFederation = function(serverUrl) \"ephemeral-api-key\"\n  )\n\n  account <- list(server = \"example.com\")\n  client <- clientForAccount(account)\n\n  expect_equal(client$service(), \"connect\")\n})\n\ntest_that(\"clientForAccount skips identity federation when credentials exist\", {\n  local_temp_config()\n  local_api_key_cache()\n\n  addTestServer(\"example.com\")\n\n  # Mock - should not be called\n  attempted <- FALSE\n  local_mocked_bindings(\n    attemptIdentityFederation = function(serverUrl) {\n      attempted <<- TRUE\n      \"ephemeral-api-key\"\n    }\n  )\n\n  # Account with existing API key\n  account <- list(server = \"example.com\", apiKey = \"existing-key\")\n  client <- clientForAccount(account)\n\n  expect_equal(client$service(), \"connect\")\n  expect_false(attempted)\n})\n\ntest_that(\"clientForAccount skips identity federation for ShinyApps\", {\n  # Mock - should not be called\n  attempted <- FALSE\n  local_mocked_bindings(\n    attemptIdentityFederation = function(serverUrl) {\n      attempted <<- TRUE\n      \"ephemeral-api-key\"\n    }\n  )\n\n  account <- list(server = \"shinyapps.io\")\n  client <- clientForAccount(account)\n\n  expect_equal(client$service(), \"shinyapps.io\")\n  expect_false(attempted)\n})\n\ntest_that(\"clientForAccount skips identity federation for Connect Cloud\", {\n  # Mock - should not be called\n  attempted <- FALSE\n  local_mocked_bindings(\n    attemptIdentityFederation = function(serverUrl) {\n      attempted <<- TRUE\n      \"ephemeral-api-key\"\n    }\n  )\n\n  account <- list(server = \"connect.posit.cloud\")\n  client <- clientForAccount(account)\n\n  expect_equal(client$service(), \"connect.posit.cloud\")\n  expect_false(attempted)\n})\n"
  },
  {
    "path": "tests/testthat/test-lint.R",
    "content": "test_that(\"lints give have useful print method\", {\n  expect_snapshot({\n    lint(test_path(\"test-rmd-bad-case\"))\n    lint(test_path(\"shinyapp-appR\"))\n  })\n})\n\ntest_that(\"The linter believes that the Shiny example apps are okay\", {\n  examples <- list.files(\n    system.file(\"examples\", package = \"shiny\"),\n    full.names = TRUE\n  )\n  if (length(examples)) {\n    results <- lapply(examples, lint)\n\n    expect_output(lints <- lapply(results, print))\n    lapply(lints, function(project) {\n      lapply(project, function(file) {\n        lapply(file, function(linter) {\n          expect_true(length(linter$indices) == 0)\n        })\n      })\n    })\n  }\n})\n"
  },
  {
    "path": "tests/testthat/test-linters.R",
    "content": "test_that(\"linter warns about absolute paths and relative paths\", {\n  dirCreate(\"~/.rsconnect-tests\")\n  file.create(\"~/.rsconnect-tests/local-file.txt\")\n  withr::defer(unlink(\"~/.rsconnect-tests\", recursive = TRUE))\n\n  result <- lint(test_path(\"shinyapp-with-absolute-paths\"))\n  expect_snapshot(result)\n\n  absPathLintedIndices <- result[[\"server.R\"]]$absolute.paths$indices\n  expect_identical(as.numeric(absPathLintedIndices), 12)\n})\n\ntest_that(\"The linter identifies files not matching in case sensitivity\", {\n  result <- lint(test_path(\"shinyapp-with-absolute-paths\"))\n  server.R <- result[[\"server.R\"]]\n  filepath.capitalization <- server.R[[\"filepath.capitalization\"]]\n  expect_equal(as.integer(filepath.capitalization$indices), 28)\n})\n\ntest_that(\"The linter identifies files with Markdown links not matching in case sensitivity\", {\n  result <- lint(test_path(\"test-rmd-bad-case\"))\n  index.Rmd <- result[[\"index.Rmd\"]]\n  filepath.capitalization <- index.Rmd[[\"filepath.capitalization\"]]\n  expect_equal(as.integer(filepath.capitalization$indices), 29)\n})\n\ntest_that(\"The linter identifies browser() statements correctly\", {\n  result <- lint(\"shinyapp-with-browser\")\n  server.R <- result[[\"server.R\"]]\n  browseLines <- server.R[[\"browser\"]]\n  expect_true(browseLines$indices == 9)\n})\n\ntest_that(\"The linter identifies browseURL() statements correctly\", {\n  result <- lint(\"shinyapp-with-browser\")\n  server.R <- result[[\"server.R\"]]\n  browseLines <- server.R[[\"browseURL\"]]\n  expect_true(browseLines$indices == 5)\n})\n\ntest_that(\"The linter accepts a plumber API\", {\n  result <- lint(\"test-plumber\")\n  expect_false(is.null(result$plumber.R))\n})\n\ntest_that(\"Linters can run on files with multibyte characters\", {\n  lint(\"multibyte-characters\")\n  expect_true(TRUE) # didn't stop\n})\n"
  },
  {
    "path": "tests/testthat/test-locale.R",
    "content": "test_that(\"locale is en_US on mac GHA\", {\n  skip_on_os(c(\"linux\", \"windows\"))\n  skip_if_not(identical(Sys.getenv(\"GITHUB_ACTIONS\"), \"true\"))\n\n  expect_equal(detectLocale(), \"en_US\")\n})\n\ntest_that(\"locale is en_US on mac GHA\", {\n  skip_on_os(c(\"linux\", \"mac\"))\n  skip_if_not(identical(Sys.getenv(\"GITHUB_ACTIONS\"), \"true\"))\n\n  expect_equal(windowsLocale(), \"en_US\")\n})\n\ntest_that(\"normalizeLocale handles common forms\", {\n  expect_equal(normalizeLocale(\"en-US\"), \"en_US\")\n  expect_equal(normalizeLocale(\"az-Cyrl\"), \"az\")\n  expect_equal(normalizeLocale(\"az-Cyrl-AZ\"), \"az_AZ\")\n})\n"
  },
  {
    "path": "tests/testthat/test-plumber/plumber.R",
    "content": "#* @get /foo\nfooRoute <- function() {\n  \"hello world\"\n}\n"
  },
  {
    "path": "tests/testthat/test-reticulate-rmds/implicit.Rmd",
    "content": "---\ntitle: An example R Markdown document that uses reticulate to run python content\n---\n\n```{python}\nfrom pprint import pprint\nxs = range(10)\npprint([(x, x**2, x**3) for x in xs])\n```\n"
  },
  {
    "path": "tests/testthat/test-reticulate-rmds/index.Rmd",
    "content": "---\ntitle: An example R Markdown document that uses reticulate to run python content\n---\n\n```{r}\nlibrary(reticulate)\n```\n\n```{python}\nfrom pprint import pprint\nxs = range(10)\npprint([(x, x**2, x**3) for x in xs])\n```\n"
  },
  {
    "path": "tests/testthat/test-rmd-bad-case/index.Rmd",
    "content": "---\ntitle: \"rmarkdown with bad link case\"\n---\n\nOne of the embedded images uses `img/rstudio.svg` even though the on-disk filename\nis `img/RStudio.svg`. This will render fine on a Mac, for example, but will err\nwhen rendered on Linux.\n\nThe Mac filesystem is case-preserving, but case-insensitive. The Linux\nfilesystem is case-sensitive.\n\nThe IDE and `rsconnect::deployDoc` attempt to deploy the as-linked filenames.\nWith both `img/rstudio.svg` and `img/RStudio.svg` links, the bundle has two copies of\nthe SVG!\n\nYou can use `rsconnect::deployApp` directly to see the lint error in the R\nconsole:\n\n```r\nrsconnect::deployApp(\n  appDir = getwd(), \n  appFiles = c(\"index.Rmd\", \"img/RStudio.svg\"))\n```\n\nAnd now, the links!\n\nThis one (line 29) is all lower-case and should trigger the error.\n\n![](img/rstudio.svg)\n\nThis one is mixed-case and matches the on-disk filename.\n\n![](img/RStudio.svg)\n\n"
  },
  {
    "path": "tests/testthat/test-rmds/index.Rmd",
    "content": "---\ntitle: The index R Markdown document\n---\n\nFor those times that you do not specify a primary.\n"
  },
  {
    "path": "tests/testthat/test-rmds/parameterized.Rmd",
    "content": "---\ntitle: Parameterized R Markdown document\nparams:\n  color: red\n---\n\nParameters are `r params$color`.\n"
  },
  {
    "path": "tests/testthat/test-rmds/simple.Rmd",
    "content": "---\ntitle: Simple R Markdown document\n---\n\nR Markdown is so simple.\n"
  },
  {
    "path": "tests/testthat/test-secret.R",
    "content": "test_that(\"NULLs aren't secret\", {\n  expect_equal(secret(NULL), NULL)\n})\n\ntest_that(\"print and str obfuscate output\", {\n  expect_snapshot({\n    x <- secret(\"THIS IS MY PASSWORD: foo\")\n    x\n    str(x)\n  })\n})\n\ntest_that(\"can put a secret in a data frame\", {\n  x <- secret(\"x\")\n  df <- data.frame(x)\n  expect_equal(df$x, x)\n})\n"
  },
  {
    "path": "tests/testthat/test-servers.R",
    "content": "test_that(\"servers() can return 0 row data frame\", {\n  local_temp_config()\n\n  out <- servers(local = TRUE)\n  expect_equal(nrow(out), 0)\n  expect_named(out, c(\"name\", \"url\", \"certificate\"))\n})\n\ntest_that(\"servers() redacts the certificate\", {\n  local_temp_config()\n\n  # add a server with a sample certificate\n  addTestServer(\n    url = \"https://localhost:4567/\",\n    name = \"cert_test_a\",\n    certificate = test_path(\"certs/sample.crt\")\n  )\n\n  expect_snapshot(servers())\n})\n\ntest_that(\"serverInfo() redacts the certificate\", {\n  expect_snapshot({\n    str(serverInfo(\"shinyapps.io\"))\n  })\n})\n\ntest_that(\"serverInfo() redacts the certificate\", {\n  expect_snapshot({\n    str(serverInfo(\"connect.posit.cloud\"))\n  })\n})\n\ntest_that(\"serverInfo() errors if server not present\", {\n  local_temp_config()\n\n  expect_snapshot(serverInfo(\"foo\"), error = TRUE)\n})\n\ntest_that(\"normalizes connect urls\", {\n  expected <- \"https://myserver.com/__api__\"\n\n  expect_equal(ensureConnectServerUrl(\"https://myserver.com\"), expected)\n  expect_equal(ensureConnectServerUrl(\"https://myserver.com/\"), expected)\n  expect_equal(ensureConnectServerUrl(\"https://myserver.com/__api__\"), expected)\n  expect_equal(\n    ensureConnectServerUrl(\"https://myserver.com/__api__/\"),\n    expected\n  )\n})\n\n\n# addServer ---------------------------------------------------------------\n\ntest_that(\"addServer() name defaults to hostname & port of url\", {\n  expect_equal(serverName(\"https://example.com/abc\"), \"example.com\")\n  expect_equal(serverName(\"https://example.com:8787/abc\"), \"example.com:8787\")\n})\n\ntest_that(\"addServer() normalises url\", {\n  skip_on_cran()\n  skip_if_not_installed(\"webfakes\")\n\n  local_temp_config()\n\n  service <- service_settings_200()\n  url <- buildHttpUrl(service)\n  expected_url <- paste0(url, \"__api__\")\n\n  # Incomplete (lacks path).\n  # Lack of protocol is not easily tested because validateConnectUrl()\n  # prefers https://.\n  partial_url <- paste0(\n    service$protocol,\n    \"://\",\n    service$host,\n    \":\",\n    service$port\n  )\n  addServer(partial_url, name = \"connect\", quiet = TRUE)\n  info <- serverInfo(\"connect\")\n  expect_equal(info$url, expected_url)\n})\n\ntest_that(\"addServer() errors if url not a connect server\", {\n  skip_if_not_installed(\"webfakes\")\n\n  local_temp_config()\n\n  service <- httpbin_service()\n  url <- buildHttpUrl(service)\n  expect_snapshot(addServer(url), error = TRUE)\n})\n\ntest_that(\"addServer() and addServerCertificate() inform about their actions\", {\n  local_temp_config()\n\n  cert <- test_path(\"certs/sample.crt\")\n  expect_snapshot({\n    addServer(\"https://example.com\", validate = FALSE)\n    addServerCertificate(\"example.com\", certificate = cert)\n  })\n})\n\ntest_that(\"can save certificates\", {\n  local_temp_config()\n\n  addTestServer(\"test\", certificate = test_path(\"certs/sample.crt\"))\n\n  info <- serverInfo(\"test\")\n  certLines <- paste(readLines(test_path(\"certs/sample.crt\")), collapse = \"\\n\")\n  expect_equal(info$certificate, secret(certLines))\n})\n\ntest_that(\"can add certificates after creation\", {\n  local_temp_config()\n\n  addTestServer(\"test\")\n  addServerCertificate(\n    \"test\",\n    certificate = test_path(\"certs/sample.crt\"),\n    quiet = TRUE\n  )\n\n  info <- serverInfo(\"test\")\n  certLines <- paste(readLines(test_path(\"certs/sample.crt\")), collapse = \"\\n\")\n  expect_equal(info$certificate, secret(certLines))\n})\n\ntest_that(\"can store multiple certificates can exist in one dcf\", {\n  local_temp_config()\n\n  addTestServer(\"test\", certificate = test_path(\"certs/two-cas.crt\"))\n\n  info <- serverInfo(\"test\")\n  certLines <- paste(readLines(test_path(\"certs/two-cas.crt\")), collapse = \"\\n\")\n  expect_equal(info$certificate, secret(certLines))\n})\n\ntest_that(\"certificates can't be attached to plain http servers\", {\n  local_temp_config()\n\n  addTestServer(\"test\", \"http://example.com\")\n  cert <- test_path(\"certs/sample.crt\")\n  expect_snapshot(addServerCertificate(\"test\", cert), error = TRUE)\n})\n\n# cloud servers -----------------------------------------------------------\n\ntest_that(\"only shinyapps.io is identified as shinyapps.io\", {\n  expect_true(isShinyappsServer(\"shinyapps.io\"))\n  expect_false(isShinyappsServer(\"connect.internal\"))\n})\n\ntest_that(\"only shinyapps.io is identified as shinyapps.io\", {\n  checkShinyappsServer(\"shinyapps.io\")\n  expect_error(checkShinyappsServer(\"connect.internal\"))\n})\n\ntest_that(\"predefined servers includes shinyapps and connect cloud\", {\n  local_temp_config()\n\n  out <- servers()\n  expect_equal(nrow(out), 2)\n  expect_named(out, c(\"name\", \"url\", \"certificate\"))\n  expect_setequal(out$name, c(\"shinyapps.io\", \"connect.posit.cloud\"))\n})\n\n# findServer --------------------------------------------------------------\n\ntest_that(\"findServer() errors if no servers\", {\n  local_temp_config()\n  expect_snapshot(findServer(), error = TRUE)\n})\n\ntest_that(\"findServer() picks server if only one present\", {\n  local_mocked_bindings(servers = function(...) {\n    data.frame(name = \"myserver\", stringsAsFactors = FALSE)\n  })\n  expect_equal(findServer(), \"myserver\")\n})\n\ntest_that(\"findServer() errors/prompts of multiple servers present\", {\n  local_mocked_bindings(servers = function(...) {\n    data.frame(name = c(\"myserver\", \"yourserver\"), stringsAsFactors = FALSE)\n  })\n  expect_snapshot(findServer(), error = TRUE)\n\n  simulate_user_input(2)\n  expect_snapshot(out <- findServer())\n  expect_equal(out, \"yourserver\")\n})\n\ntest_that(\"findServer checks server name\", {\n  local_temp_config()\n\n  expect_snapshot(error = TRUE, {\n    findServer(1)\n    findServer(\"foo\")\n  })\n})\n\ntest_that(\"isSPCSServer correctly identifies Snowpark Container Services servers\", {\n  local_temp_config()\n\n  # Register a server with a snowflakecomputing.app URL\n  addTestServer(\n    url = \"https://test-abc123.snowflakecomputing.app/__api__\",\n    name = \"spcs_server\"\n  )\n\n  # Mock serverInfo to return the URL for our test\n  local_mocked_bindings(\n    serverInfo = function(server) {\n      if (server == \"spcs_server\") {\n        return(list(url = \"https://test-abc123.snowflakecomputing.app/__api__\"))\n      }\n      list(url = \"https://example.com\")\n    }\n  )\n\n  expect_true(isSPCSServer(\"spcs_server\"))\n  expect_false(isSPCSServer(\"example.com\"))\n})\n\ntest_that(\"addServer accepts snowflakeConnectionName parameter\", {\n  local_temp_config()\n\n  # Mock validateConnectUrl to avoid actual HTTP requests\n  local_mocked_bindings(\n    validateConnectUrl = function(url, certificate, snowflakeConnectionName) {\n      if (!is.null(snowflakeConnectionName)) {\n        expect_equal(snowflakeConnectionName, \"test_connection\")\n      }\n      list(valid = TRUE, url = url)\n    }\n  )\n\n  # Run addServer with snowflakeConnectionName\n  addServer(\n    url = \"https://test-abc123.snowflakecomputing.app\",\n    name = \"spcs_server\",\n    snowflakeConnectionName = \"test_connection\",\n    quiet = TRUE,\n    validate = TRUE\n  )\n\n  # Check server was added\n  server_list <- servers(local = TRUE)\n  expect_true(\"spcs_server\" %in% server_list$name)\n})\n"
  },
  {
    "path": "tests/testthat/test-spcs.R",
    "content": "test_that(\"isSPCSServer correctly identifies Snowpark Container Services servers\", {\n  local_temp_config()\n\n  # Register a server with a snowflakecomputing.app URL\n  addTestServer(\n    url = \"https://test-abc123.snowflakecomputing.app/__api__\",\n    name = \"spcs_server\"\n  )\n\n  # Mock serverInfo to return the URL for our test\n  local_mocked_bindings(\n    serverInfo = function(server) {\n      if (server == \"spcs_server\") {\n        return(list(url = \"https://test-abc123.snowflakecomputing.app/__api__\"))\n      }\n      list(url = \"https://example.com\")\n    }\n  )\n\n  expect_true(isSPCSServer(\"spcs_server\"))\n  expect_false(isSPCSServer(\"example.com\"))\n})\n\ntest_that(\"authHeaders handles snowflakeToken\", {\n  authInfo <- list(\n    snowflakeToken = list(\n      Authorization = \"Snowflake Token=\\\"mock_token\\\"\",\n      `X-Custom-Header` = \"custom-value\"\n    )\n  )\n\n  headers <- authHeaders(authInfo, \"GET\", \"/path\")\n\n  expect_equal(headers$Authorization, \"Snowflake Token=\\\"mock_token\\\"\")\n  expect_equal(headers$`X-Custom-Header`, \"custom-value\")\n})\n\ntest_that(\"authHeaders handles snowflakeToken with API key\", {\n  authInfo <- list(\n    apiKey = secret(\"the-api-key\"),\n    snowflakeToken = list(\n      Authorization = \"Snowflake Token=\\\"mock_token\\\"\",\n      `X-Custom-Header` = \"custom-value\"\n    )\n  )\n\n  # Test authHeaders\n  headers <- authHeaders(authInfo, \"GET\", \"/path\")\n\n  # Verify snowflakeToken headers were used\n  expect_equal(headers$Authorization, \"Snowflake Token=\\\"mock_token\\\"\")\n  expect_equal(headers$`X-RSC-Authorization`, \"Key the-api-key\")\n  expect_equal(headers$`X-Custom-Header`, \"custom-value\")\n})\n\ntest_that(\"authHeaders handles snowflakeToken with token + secret\", {\n  local_mocked_bindings(\n    rfc2616Date = function() \"Thu, 09 Mar 2023 14:29:00 GMT\"\n  )\n\n  authInfo <- list(\n    token = \"connect-token-123\",\n    secret = openssl::base64_encode(\"shared-secret\"),\n    snowflakeToken = list(\n      Authorization = \"Snowflake Token=\\\"mock_token\\\"\"\n    )\n  )\n\n  headers <- authHeaders(authInfo, \"GET\", \"/path\")\n\n  # Verify snowflakeToken header\n  expect_equal(headers$Authorization, \"Snowflake Token=\\\"mock_token\\\"\")\n\n  # Verify signature headers are present\n  expect_equal(headers$`X-Auth-Token`, \"connect-token-123\")\n  expect_equal(headers$Date, \"Thu, 09 Mar 2023 14:29:00 GMT\")\n  expect_true(!is.null(headers$`X-Auth-Signature`))\n  expect_true(!is.null(headers$`X-Content-Checksum`))\n})\n\ntest_that(\"authHeaders handles snowflakeToken with token + private_key\", {\n  local_mocked_bindings(\n    rfc2616Date = function() \"Thu, 09 Mar 2023 14:29:00 GMT\"\n  )\n\n  # Use same test key as test-http.R\n  key_string <- \"-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/eiSQAKXADslq\nGGsbsQu2eEgEHD06BtUhaeU1nvsY7a6u12xpG0OAGYWhnGhR+1K/3qoZQQNmN0MC\nZV0zhueREu3YaqtNwXbnbGCqp7tsLsx2cb2TZscmBNXkOLah2PlsBTfInNlKrpKH\nwsMtsL7yruXwJ767ey8JujMAqO90jj57idfhkr4IU47EL8DiIHTOAYLddKe/d4AL\nskoEFQM37SJ9JUwZUsvz2eqEXFPV4QrS93T79sA7QfH5zOYTLKPEDxV0U7AYUVKW\nbBieVRlu3DIQfdsqXwYYzHUR+HBlxe1VowCS5p5c6coNLdb+RElcSSY2Gd06eBg0\nwSoxkyT3AgMBAAECggEAHmQinYCczkkKiv5pMbH+K+4XcB+TUDx5Y55NNR+Qtzoy\neanTmTMsmY5zeV076Zc8YRvUX8HD6ltnFWBFVMJaytn4SltT6TmFx+GZzjxlMRyU\nc1BGSLkNbulhkaG2yyWHITAK1Jqgmovu0gGFvSDKjfZYpK+KRHOe2apmIfquVw9d\n6CLm7swx30x8qFKEACc3iM/Mcc9uKOWn+NjVKqUVfU/9ZmNxmjZ9e3wkwieI1mSB\ngq9q7fmCHHkGWbUkGMphgRyssaJbz+bn/Wz2uwevvtebkTOzvWtKhUGgDByCCCtq\nJ1ehPCjdRjylb6C5tLbyeng5QnYC7uZJmRsEOUwMEQKBgQD6PsHXwKJ8JRlDGu4G\nJQ9fKzYA+B+No0GKtGuuRDD8tJouj5rb1dZt181UHUW/UtjNpz8j8l1RN2gPcu/v\nVjwSXDcZHohv4cgQfRCN47wREEEb/LP/fhxIt2H320vh5qvwdJJoGvGoJfR5vO1X\nysdIPBajIgnEo4U7cawaNfeS1QKBgQDD4WgXUb0AYvK6lggVVqgNzxxMQRWCQSGb\ny4RISHlC2TpftpbLFdr6fHuf8bzGq462xG5MFMMGBsbESXKNW18QA+uIBIDwttaj\nAfQ4+PNu4m+2Ump+RcGu2MYoBJoxjMx00Ba76cEMF0+X+RO4zcfwZ6Y869Fakq+D\n7rn4ZetGmwKBgQCMtgsjeUMkUWwCCrt6ow4gslh8dQixCPKKvuapp9hv0FG+CqvG\nH1iijSz8tjUI3tnf0cI0QUztpR0TSsrVpoTCwi2NJ1kKqEdp1hkf38VZRu2FgjPo\nXw4iaVNiHmJt1NorrDDC7xuhNC5i4bQHoJMr7/W+px4c/uGkykc+ucfLPQKBgQCG\n/E/KOibgHFAfawLZCaW4FnDuz68t2wp5HY/kbCU8fwxuJxrVixMjqSNcfq9TzagE\npWtI/MnE3midnevWJBBnrfvi+Q+OUsGpBdCybkT7tgm8ACGpMRMfFf3AWCOWX+wJ\n19jC2HyTg4DzPs9rfEv7jMIPm4bjPtC7P4li94FiXwKBgBTh5tPUEvG0chZvqRT2\ng0vvWgJGF52FCXBij3dnNl1eNRQYbDI+hNbZYcHCKHKaOoDWaqYhyjLk6Tz0LhLe\nXWAlOP6tE2UbEgi10wyaEI9EyfXg1mgiHlSg+oZMCx05TUE6PrzddS6qUOJfN7P3\na3hEFijsjg/+FDMr+iAVzjry\n-----END PRIVATE KEY-----\"\n  key <- openssl::base64_encode(openssl::read_key(key_string))\n\n  authInfo <- list(\n    token = \"connect-token-456\",\n    private_key = key,\n    snowflakeToken = list(\n      Authorization = \"Snowflake Token=\\\"mock_token\\\"\"\n    )\n  )\n\n  headers <- authHeaders(authInfo, \"GET\", \"/path\")\n\n  # Verify snowflakeToken header\n  expect_equal(headers$Authorization, \"Snowflake Token=\\\"mock_token\\\"\")\n\n  # Verify signature headers are present\n  expect_equal(headers$`X-Auth-Token`, \"connect-token-456\")\n  expect_equal(headers$Date, \"Thu, 09 Mar 2023 14:29:00 GMT\")\n  expect_true(!is.null(headers$`X-Auth-Signature`))\n  expect_true(!is.null(headers$`X-Content-Checksum`))\n})\n\ntest_that(\"registerAccount stores snowflakeConnectionName\", {\n  local_temp_config()\n\n  # Register an account with snowflakeConnectionName\n  registerAccount(\n    serverName = \"example.com\",\n    accountName = \"testuser\",\n    accountId = \"user123\",\n    snowflakeConnectionName = \"test_connection\"\n  )\n\n  # Check the account info has the snowflakeConnectionName\n  info <- accountInfo(\"testuser\", \"example.com\")\n  expect_equal(info$snowflakeConnectionName, \"test_connection\")\n})\n\ntest_that(\"registerAccount stores both apiKey and snowflakeConnectionName for SPCS accounts\", {\n  local_temp_config()\n\n  # Register an SPCS account with both apiKey and snowflakeConnectionName\n  registerAccount(\n    serverName = \"spcs.example.com\",\n    accountName = \"spcsuser\",\n    accountId = \"user456\",\n    apiKey = \"test-api-key-789\",\n    snowflakeConnectionName = \"spcs_connection\"\n  )\n\n  # Check the account info has both fields\n  info <- accountInfo(\"spcsuser\", \"spcs.example.com\")\n  expect_equal(info$snowflakeConnectionName, \"spcs_connection\")\n  expect_equal(as.character(info$apiKey), \"test-api-key-789\")\n})\n"
  },
  {
    "path": "tests/testthat/test-title.R",
    "content": "test_that(\"generated application names do not exceed maximum length\", {\n  title <- \"Good heavens, this title is simply enormous! I can't imagine why anyone would use such a cumbersome moniker.\"\n  name <- generateAppName(title)\n  expect_lt(nchar(name), 65)\n})\n\ntest_that(\"empty titles are rejected\", {\n  title <- \"\"\n  expect_error(generateAppName(title))\n})\n\ntest_that(\"path is used to generate valid title if title isn't specified\", {\n  title <- NULL\n  path <- \"shiny-app-in-subdir\"\n  name <- generateAppName(title, path)\n  expect_gt(nchar(name), 5)\n})\n\ntest_that(\"invalid characters are removed from titles\", {\n  title <- \"Free!* (* = With $5 Donation)\"\n  name <- generateAppName(title)\n  expect_false(grepl(\"[!*=$]\", name))\n})\n\ntest_that(\"valid characters are not removed from titles\", {\n  title <- \"inexpensive_kittens-5\"\n  name <- generateAppName(title)\n  expect_identical(title, name)\n})\n"
  },
  {
    "path": "tests/testthat/test-utils.R",
    "content": "test_that(\"file_path_sans_ext removes extensions\", {\n  # extensions are removed.\n  expect_equal(\n    file_path_sans_ext(c(\"noext\", \"extension.ext\", \"doubledot..ext\")),\n    c(\"noext\", \"extension\", \"doubledot.\")\n  )\n\n  # compression extensions are removed first (when explicitly requested).\n  expect_equal(\n    file_path_sans_ext(\n      c(\n        \"noext\",\n        \"extension.ext\",\n        \"doubledot..ext\",\n        \"compressed.gz\",\n        \"compressext.ext.bz2\",\n        \"compressdoubledot..ext.xz\"\n      ),\n      compression = TRUE\n    ),\n    c(\n      \"noext\",\n      \"extension\",\n      \"doubledot.\",\n      \"compressed\",\n      \"compressext\",\n      \"compressdoubledot.\"\n    )\n  )\n})\n\n# rbind_fill --------------------------------------------------------------\n\ntest_that(\"adds missing columns\", {\n  dfs <- list(data.frame(x = 1), data.frame(y = 2))\n  out <- rbind_fill(dfs)\n  expect_equal(out, data.frame(x = c(1, NA), y = c(NA, 2)))\n})\n\ntest_that(\"order of col_names has preference\", {\n  dfs <- list(data.frame(y = 1, x = 2))\n  out <- rbind_fill(dfs, c(\"x\", \"y\"))\n  expect_equal(out, data.frame(x = 2, y = 1))\n})\n\ntest_that(\"uses col_names if no inputs\", {\n  expect_equal(rbind_fill(list()), data.frame())\n  expect_equal(\n    rbind_fill(list(), c(\"x\", \"y\")),\n    data.frame(x = logical(), y = logical())\n  )\n})\n\ntest_that(\"can work with empty data frames\", {\n  out <- rbind_fill(list(data.frame(x = 1), data.frame()))\n  expect_equal(out, data.frame(x = 1))\n})\n\n# hashing -----------------------------------------------------------------\n\ntest_that(\"we can hash an empty file\", {\n  emptyFile <- withr::local_tempfile()\n  file.create(emptyFile)\n\n  # computed by openssl::md5(\"\")\n  expect_equal(fileMD5(emptyFile), \"d41d8cd98f00b204e9800998ecf8427e\")\n  expect_equal(fileMD5(NULL), \"d41d8cd98f00b204e9800998ecf8427e\")\n})\n\ntest_that(\"we can hash a file with well known contents\", {\n  path <- withr::local_tempfile()\n  # Open in binary mode so the contents are identical on all platforms\n  # (otherwise the file contains a \\n on Unix and \\r\\n on Windows, which\n  # hash differently)\n  con <- file(path, open = \"wb\")\n  writeLines(\"go bananas!\", con)\n  close(con)\n\n  expect_equal(fileMD5(path), \"52d2daa95d288f3c01e4d4d87f85727e\")\n})\n\ntest_that(\"truthy is truthy\", {\n  # fallback-to-default checks\n  expect_false(truthy(c()))\n  expect_true(truthy(c(), default = TRUE))\n  expect_false(truthy(NA))\n  expect_true(truthy(NA, default = TRUE))\n\n  # true value checks\n  expect_true(truthy(TRUE, default = FALSE))\n  expect_true(truthy(\"TRUE\", default = FALSE))\n  expect_true(truthy(\"True\", default = FALSE))\n  expect_true(truthy(\"true\", default = FALSE))\n  expect_true(truthy(\"T\", default = FALSE))\n  expect_true(truthy(\"1\", default = FALSE))\n  expect_true(truthy(1, default = FALSE))\n  expect_true(truthy(42, default = FALSE))\n\n  # false value checks\n  expect_false(truthy(FALSE, default = TRUE))\n  expect_false(truthy(\"FALSE\", default = TRUE))\n  expect_false(truthy(\"False\", default = TRUE))\n  expect_false(truthy(\"false\", default = TRUE))\n  expect_false(truthy(\"F\", default = TRUE))\n  expect_false(truthy(\"0\", default = TRUE))\n  expect_false(truthy(0, default = TRUE))\n  expect_false(truthy(\"nonsense\", default = TRUE))\n})\n\ntest_that(\"json sanitizer handles long inputts\", {\n  skip_if_no_quarto()\n  skip_on_os(c(\"mac\", \"linux\", \"solaris\"))\n\n  document <- test_path(\"quarto-doc-long-chunk\", \"index.qmd\")\n  qinspect <- system2(\n    \"quarto\",\n    c(\"inspect\", shQuote(document)),\n    stdout = TRUE,\n    stderr = TRUE\n  )\n\n  expect_error(jsonlite::fromJSON(qinspect))\n  expect_no_error(\n    jsonlite::fromJSON(\n      sanitizeSystem2json(qinspect)\n    )\n  )\n})\n"
  },
  {
    "path": "tests/testthat/test-writeManifest.R",
    "content": "skip_on_cran()\n\nmakeManifest <- function(appDir, appPrimaryDoc = NULL, ...) {\n  writeManifest(appDir, appPrimaryDoc = appPrimaryDoc, ..., quiet = TRUE)\n  manifestFile <- file.path(appDir, \"manifest.json\")\n  data <- readLines(manifestFile, warn = FALSE, encoding = \"UTF-8\")\n  manifestJson <- jsonlite::fromJSON(data)\n  unlink(manifestFile)\n  manifestJson\n}\n\ntest_that(\"renv.lock is included for renv projects\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(app.R = \"library(foreign); library(MASS)\"))\n  renv::snapshot(app_dir, prompt = FALSE)\n\n  manifest <- makeManifest(app_dir)\n  # note: we don't see an .Rprofile here because we only renv::snapshot and\n  # do not fully create an renv project.\n  expect_named(manifest$files, c(\"app.R\", \"renv.lock\"))\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"renv.lock is not included for non-renv projects\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  app_dir <- local_temp_app(list(app.R = \"library(foreign); library(MASS)\"))\n\n  manifest <- makeManifest(app_dir)\n  expect_named(manifest$files, c(\"app.R\"))\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Rmd with reticulate as a dependency includes python in the manifest\", {\n  skip_if_not_installed(\"reticulate\")\n  python <- pythonPathOrSkip()\n\n  appDir <- test_path(\"test-reticulate-rmds\")\n  manifest <- makeManifest(appDir, python = python)\n  requirements_file <- file.path(\n    appDir,\n    manifest$python$package_manager$package_file\n  )\n  expect_equal(requirements_file, \"test-reticulate-rmds/requirements.txt\")\n  defer(unlink(requirements_file))\n\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"index.Rmd\")\n  expect_true(file.exists(requirements_file))\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Rmd with reticulate as an inferred dependency includes reticulate and python in the manifest\", {\n  skip_if_not_installed(\"reticulate\")\n  python <- pythonPathOrSkip()\n\n  appDir <- test_path(\"test-reticulate-rmds\")\n  manifest <- makeManifest(appDir, \"implicit.Rmd\", python = python)\n  requirements_file <- file.path(\n    appDir,\n    manifest$python$package_manager$package_file\n  )\n  expect_equal(requirements_file, \"test-reticulate-rmds/requirements.txt\")\n  defer(unlink(requirements_file))\n\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"implicit.Rmd\")\n  expect_true(file.exists(requirements_file))\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Rmd without a python block doesn't include reticulate or python in the manifest\", {\n  manifest <- makeManifest(test_path(\"test-rmds\"), \"simple.Rmd\", python = NULL)\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"simple.Rmd\")\n  expect_null(manifest$python)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Rmd without a python block doesn't include reticulate or python in the manifest even if python specified\", {\n  skip_if_not_installed(\"reticulate\")\n  python <- pythonPathOrSkip()\n\n  manifest <- makeManifest(\"test-rmds\", \"simple.Rmd\", python = python)\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_equal(manifest$metadata$primary_rmd, \"simple.Rmd\")\n  expect_equal(manifest$python, NULL)\n  # Confirm that we have removed packrat entries from our file listing but\n  # retain entries for other files.\n  filenames <- names(manifest$files)\n  expect_false(any(grepl(\"^packrat/\", filenames, perl = TRUE)), filenames)\n  expect_true(any(grepl(\"simple.Rmd\", filenames, fixed = TRUE)), filenames)\n  expect_known_manifest_fields(manifest)\n})\n\n# Quarto Tests\n\ntest_that(\"Quarto website includes quarto in the manifest\", {\n  skip_if_no_quarto()\n\n  appDir <- local_temp_app(quarto_website_r_files)\n  manifest <- makeManifest(appDir, quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-static\")\n  expect_equal(manifest$quarto$engines, \"knitr\")\n  expect_equal(manifest$metadata$primary_rmd, \"index.qmd\")\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Quarto document includes quarto in the manifest\", {\n  skip_if_no_quarto()\n\n  appDir <- test_path(\"quarto-doc-none\")\n  appPrimaryDoc <- \"quarto-doc-none.qmd\"\n  manifest <- makeManifest(appDir, appPrimaryDoc, quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-static\")\n  expect_equal(manifest$quarto$engines, \"markdown\")\n  expect_equal(manifest$metadata$primary_rmd, \"quarto-doc-none.qmd\")\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Specifying quarto arg includes quarto in the manifest, even with no appPrimaryDoc specified (.qmd)\", {\n  skip_if_no_quarto()\n\n  appDir <- test_path(\"quarto-doc-none\")\n  appPrimaryDoc <- NULL\n  manifest <- makeManifest(appDir, appPrimaryDoc, quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-static\")\n  expect_equal(manifest$quarto$engines, \"markdown\")\n  expect_equal(manifest$metadata$primary_rmd, \"quarto-doc-none.qmd\")\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Specifying quarto arg includes quarto in the manifest, even with no appPrimaryDoc specified (.Rmd)\", {\n  skip_if_no_quarto()\n\n  appDir <- test_path(\"shiny-rmds\")\n  appPrimaryDoc <- NULL\n  manifest <- makeManifest(appDir, appPrimaryDoc, quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-shiny\")\n  expect_equal(manifest$quarto$engines, \"knitr\")\n  expect_equal(manifest$metadata$primary_rmd, \"non-shiny-rmd.Rmd\")\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"specifying quarto arg with non-quarto app does not include quarto in the manifest\", {\n  skip_if_no_quarto()\n\n  appDir <- test_path(\"shinyapp-singleR\")\n  appPrimaryDoc <- \"single.R\"\n  manifest <- makeManifest(appDir, appPrimaryDoc, quarto = TRUE)\n\n  expect_null(manifest$quarto)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Quarto shiny project includes quarto in the manifest\", {\n  skip_if_no_quarto()\n\n  appDir <- local_temp_app(quarto_project_r_shiny_files)\n  manifest <- makeManifest(appDir, quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-shiny\")\n  expect_equal(manifest$quarto$engines, \"knitr\")\n  expect_equal(manifest$metadata$primary_rmd, \"quarto-proj-r-shiny.qmd\")\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Quarto R + Python website includes quarto and python in the manifest\", {\n  skip_if_not_installed(\"reticulate\")\n  skip_if_no_quarto()\n  python <- pythonPathOrSkip()\n\n  appDir <- local_temp_app(quarto_website_r_py_files)\n  manifest <- makeManifest(appDir, python = python, quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-static\")\n  expect_equal(manifest$quarto$engines, \"knitr\")\n  expect_equal(manifest$metadata$primary_rmd, \"index.qmd\")\n\n  expect_true(all(c(\"quarto\", \"python\") %in% names(manifest)))\n  expect_true(\"reticulate\" %in% names(manifest$packages))\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Quarto Python-only website gets correct manifest data\", {\n  skip_if_not_installed(\"reticulate\")\n  skip_if_no_quarto()\n\n  python <- pythonPathOrSkip()\n\n  appDir <- local_temp_app(quarto_website_py_files)\n  manifest <- makeManifest(appDir, python = python, quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-static\")\n  expect_equal(manifest$quarto$engines, \"jupyter\")\n  expect_equal(manifest$metadata$primary_rmd, \"index.qmd\")\n\n  # We expect Quarto and Python metadata, but no R packages.\n  expect_true(all(c(\"quarto\", \"python\") %in% names(manifest)))\n  expect_null(manifest$packages)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Deploying a Quarto project without Quarto is an error\", {\n  local_mocked_bindings(quarto_path = function() NULL)\n\n  appDir <- local_temp_app(quarto_website_r_files)\n  expect_snapshot(makeManifest(appDir), error = TRUE)\n})\n\ntest_that(\"Deploying R Markdown content with Quarto gives a Quarto app mode\", {\n  skip_if_no_quarto()\n\n  manifest <- makeManifest(test_path(\"test-rmds\"), \"simple.Rmd\", quarto = TRUE)\n\n  expect_equal(manifest$metadata$appmode, \"quarto-static\")\n  expect_equal(manifest$quarto$engines, \"knitr\")\n  expect_equal(manifest$metadata$primary_rmd, \"simple.Rmd\")\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Deploying static content with _quarto.yaml succeeds without quartoInfo\", {\n  manifest <- makeManifest(test_path(\"static-with-quarto-yaml\"))\n\n  expect_equal(manifest$metadata$appmode, \"static\")\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"environment.image is not set when image is not provided\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"shinyapp-simple\")\n\n  manifest <- makeManifest(appDir)\n  expect_null(manifest$environment)\n})\n\ntest_that(\"TensorFlow models are identified\", {\n  app_dir <- local_temp_app(list(\n    \"1/saved_model.pb\" = \"fake-saved-model\"\n  ))\n  manifest <- makeManifest(app_dir)\n  expect_equal(manifest$metadata$appmode, \"tensorflow-saved-model\")\n  expect_null(manifest$packages)\n  expect_named(manifest$files, c(\"1/saved_model.pb\"))\n})\n\ntest_that(\"environment.image is set when image is provided\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"shinyapp-simple\")\n\n  manifest <- makeManifest(appDir, image = \"rstudio/content-base:latest\")\n  expect_equal(manifest$environment$image, \"rstudio/content-base:latest\")\n})\n\ntest_that(\"environment.image is set and uses a provided image even when RSCONNECT_IMAGE is set\", {\n  withr::local_options(renv.verbose = TRUE)\n  withr::local_envvar(RSCONNECT_IMAGE = \"rstudio/content-base:older\")\n\n  appDir <- test_path(\"shinyapp-simple\")\n\n  manifest <- makeManifest(appDir, image = \"rstudio/content-base:latest\")\n  expect_equal(manifest$environment$image, \"rstudio/content-base:latest\")\n})\n\ntest_that(\"environment.image is not set when RSCONNECT_IMAGE is empty\", {\n  withr::local_options(renv.verbose = TRUE)\n  withr::local_envvar(RSCONNECT_IMAGE = \"\")\n\n  appDir <- test_path(\"shinyapp-simple\")\n\n  manifest <- makeManifest(appDir)\n  expect_null(manifest$environment)\n})\n\ntest_that(\"environment.image is set when RSCONNECT_IMAGE is nonempty\", {\n  withr::local_options(renv.verbose = TRUE)\n  withr::local_envvar(RSCONNECT_IMAGE = \"rstudio/content-base:latest\")\n\n  appDir <- test_path(\"shinyapp-simple\")\n\n  manifest <- makeManifest(appDir)\n  expect_equal(manifest$environment$image, \"rstudio/content-base:latest\")\n})\n\ntest_that(\"Sets environment.environment_management in the manifest if envManagement is defined\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"shinyapp-simple\")\n\n  # test shorthand arg\n  manifest <- makeManifest(\n    appDir,\n    envManagement = FALSE,\n    envManagementR = TRUE,\n    envManagementPy = TRUE\n  )\n  expect_equal(manifest$environment$environment_management$r, FALSE)\n  expect_equal(manifest$environment$environment_management$python, FALSE)\n\n  # test R and Python args\n  manifest <- makeManifest(appDir, envManagementR = TRUE)\n  expect_equal(manifest$environment$environment_management$r, TRUE)\n  expect_null(manifest$environment$environment_management$python)\n  manifest <- makeManifest(appDir, envManagementPy = TRUE)\n  expect_equal(manifest$environment$environment_management$python, TRUE)\n  expect_null(manifest$environment$environment_management$r)\n\n  # environment_management is not defined when envManagementR and envManagementPy are NULL\n  manifest <- makeManifest(appDir, image = \"rstudio/content-base:latest\")\n  expect_null(manifest$environment$environment_management)\n})\n\ntest_that(\"environment.r.requires - DESCRIPTION file - existing Depends R is added\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"packages/utf8package\")\n  manifest <- makeManifest(appDir, appPrimaryDoc = \"R/hello.R\")\n  expect_equal(manifest$environment$r$requires, \">= 3.5.0\")\n})\n\ntest_that(\"environment.r.requires - renv.lock - Existing R version is added\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  appDir <- local_temp_app(list(app.R = \"library(foreign); library(MASS)\"))\n  renv::snapshot(appDir, prompt = FALSE)\n\n  manifest <- makeManifest(appDir)\n\n  maj <- R.Version()$major\n  min1 <- strsplit(R.Version()$minor, \"\\\\.\")[[1]][1]\n  expected <- sprintf(\"~=%s.%s.0\", maj, min1)\n  expect_equal(\n    manifest$environment$r$requires,\n    expected\n  )\n})\n\ntest_that(\"versionFromLockfile formats various R versions with patch .0\", {\n  versions <- c(\"3\", \"3.8\", \"3.8.11\", \"3.25\", \"4.0.2\", \"10.5.3\")\n  expected <- c(\n    \"~=3.0\",\n    \"~=3.8.0\",\n    \"~=3.8.0\",\n    \"~=3.25.0\",\n    \"~=4.0.0\",\n    \"~=10.5.0\"\n  )\n\n  for (i in seq_along(versions)) {\n    # Create a temporary directory with a renv.lock file\n    # containing the specified R version\n    appDir <- local_temp_app(list(\n      renv.lock = jsonlite::toJSON(\n        list(R = list(Version = versions[i])),\n        auto_unbox = TRUE\n      )\n    ))\n\n    res <- versionFromLockfile(appDir)\n    expect_equal(\n      res,\n      expected[i],\n      info = paste(\n        \"Input:\",\n        versions[i],\n        \"→ got\",\n        res,\n        \"but expected\",\n        expected[i]\n      )\n    )\n  }\n})\n\ntest_that(\"environment.r.requires - DESCRIPTION file takes precedence over renv.lock\", {\n  skip_if_not_installed(\"foreign\")\n  skip_if_not_installed(\"MASS\")\n\n  withr::local_options(renv.verbose = FALSE)\n\n  descFile <- \"\nPackage: oneshinyapp\nTitle: Target for tests\nVersion: 0.1.0\nDescription: a test package\nDepends: R (>= 1.42.0)\n\"\n  appDir <- local_temp_app(list(\n    app.R = \"library(foreign); library(MASS)\",\n    DESCRIPTION = descFile\n  ))\n  renv::snapshot(appDir, prompt = FALSE)\n\n  manifest <- makeManifest(appDir)\n  # Not sure if this is a valid or common scenario\n  # but here we are testing that DESCRIPTION file \"Depends\" takes precedence\n  expect_equal(manifest$environment$r$requires, \">= 1.42.0\")\n})\n\ntest_that(\"environment.r.requires - No DESCRIPTION and No renv.lock\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"packages/windows1251package\")\n  manifest <- makeManifest(appDir, appPrimaryDoc = \"R/hello.R\")\n  expect_null(manifest$environment)\n})\n\ntest_that(\"environment.python.requires - .python-version file\", {\n  skip_if_not_installed(\"reticulate\")\n  skip_if_no_quarto()\n\n  withr::local_options(renv.verbose = TRUE)\n\n  python <- pythonPathOrSkip()\n\n  appDir <- local_temp_app(quarto_website_py_python_version_files)\n  manifest <- makeManifest(appDir, python = python, quarto = TRUE)\n  expect_equal(manifest$environment$python$requires, \"~=3.8.0\")\n})\n\ntest_that(\"environment.python.requires - pyproject.toml\", {\n  skip_if_not_installed(\"reticulate\")\n  skip_if_no_quarto()\n\n  withr::local_options(renv.verbose = TRUE)\n\n  python <- pythonPathOrSkip()\n\n  appDir <- local_temp_app(quarto_website_r_py_files)\n  manifest <- makeManifest(appDir, python = python, quarto = TRUE)\n  expect_equal(manifest$environment$python$requires, \">=3.11\")\n})\n\ntest_that(\"environment.python.requires - setup.cfg\", {\n  skip_if_not_installed(\"reticulate\")\n  skip_if_no_quarto()\n\n  withr::local_options(renv.verbose = TRUE)\n\n  python <- pythonPathOrSkip()\n\n  appDir <- local_temp_app(quarto_website_py_setup_cfg_files)\n  manifest <- makeManifest(appDir, python = python, quarto = TRUE)\n  expect_equal(manifest$environment$python$requires, \">=3.9\")\n})\n\ntest_that(\".python-version contents formats various Python versions with patch .0\", {\n  python <- pythonPathOrSkip()\n  versions <- c(\n    \"3\",\n    \"3.8\",\n    \"3.8.11\",\n    \"3.25\",\n    \"4.0.2\",\n    \"10.5.3\",\n    # Existent specifier tokena are respected\n    \">=3.11\",\n    \"==3.99.0\"\n  )\n  expected <- c(\n    \"~=3.0\",\n    \"~=3.8.0\",\n    \"~=3.8.0\",\n    \"~=3.25.0\",\n    \"~=4.0.0\",\n    \"~=10.5.0\",\n    \">=3.11\",\n    \"==3.99.0\"\n  )\n\n  indexQmd <- \"\n---\ntitle: \\\"quarto-website-py\\\"\njupyter: python3\n---\nThis is a Quarto website.\n```{python}\n1 + 1\n```\n\"\n\n  for (i in seq_along(versions)) {\n    # Create a temporary directory with a .python-version file\n    # containing the specified Python version\n    appDir <- local_temp_app(list(\n      index.qmd = indexQmd,\n      `.python-version` = versions[i]\n    ))\n\n    manifest <- makeManifest(appDir, python = python, quarto = TRUE)\n    expect_equal(\n      manifest$environment$python$requires,\n      expected[i],\n      info = paste(\n        \"Input:\",\n        versions[i],\n        \"→ got\",\n        res,\n        \"but expected\",\n        expected[i]\n      )\n    )\n  }\n})\n\n# appMode Inference tests\n\ntest_that(\"content type (appMode) is inferred and can be overridden\", {\n  appDir <- local_temp_app(list(\n    \"app.R\" = \"\",\n    \"index.html\" = \"\",\n    \"plumber.R\" = \"\",\n    \"report.Rmd\" = \"\"\n  ))\n  files <- c(\"app.R\", \"index.html\", \"plumber.R\", \"report.Rmd\")\n\n  manifest <- makeManifest(appDir)\n  expect_equal(manifest$metadata$appmode, \"api\")\n  expect_named(manifest$files, files)\n\n  manifest <- makeManifest(appDir, appMode = \"shiny\")\n  expect_equal(manifest$metadata$appmode, \"shiny\")\n  expect_named(manifest$files, files)\n\n  manifest <- makeManifest(appDir, appMode = \"rmd-static\")\n  expect_equal(manifest$metadata$appmode, \"rmd-static\")\n  expect_named(manifest$files, files)\n\n  manifest <- makeManifest(appDir, appMode = \"static\")\n  expect_equal(manifest$metadata$appmode, \"static\")\n  expect_named(manifest$files, files)\n})\n\ntest_that(\"environment.r.package_repository_resolution - lockfile mode\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"shinyapp-simple\")\n  manifest <- makeManifest(appDir, packageRepositoryResolutionR = \"lockfile\")\n  expect_equal(manifest$environment$r$package_repository_resolution, \"lockfile\")\n})\n\ntest_that(\"environment.r.package_repository_resolution - all valid modes\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"shinyapp-simple\")\n\n  # Test lax mode\n  manifest <- makeManifest(appDir, packageRepositoryResolutionR = \"lax\")\n  expect_equal(manifest$environment$r$package_repository_resolution, \"lax\")\n\n  # Test strict mode\n  manifest <- makeManifest(appDir, packageRepositoryResolutionR = \"strict\")\n  expect_equal(manifest$environment$r$package_repository_resolution, \"strict\")\n\n  # Test legacy mode\n  manifest <- makeManifest(appDir, packageRepositoryResolutionR = \"legacy\")\n  expect_equal(manifest$environment$r$package_repository_resolution, \"legacy\")\n\n  # Test lockfile mode\n  manifest <- makeManifest(appDir, packageRepositoryResolutionR = \"lockfile\")\n  expect_equal(manifest$environment$r$package_repository_resolution, \"lockfile\")\n})\n\ntest_that(\"environment.r.package_repository_resolution - NULL does not add field\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"shinyapp-simple\")\n  manifest <- makeManifest(appDir, packageRepositoryResolutionR = NULL)\n  expect_null(manifest$environment$r$package_repository_resolution)\n})\n\ntest_that(\"environment.r.package_repository_resolution - invalid value throws error\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"shinyapp-simple\")\n  expect_error(\n    makeManifest(appDir, packageRepositoryResolutionR = \"invalid\"),\n    \"should be one of \\\"lax\\\", \\\"strict\\\", \\\"legacy\\\", \\\"lockfile\\\"\"\n  )\n})\n\ntest_that(\"environment.r.package_repository_resolution - combined with requires\", {\n  withr::local_options(renv.verbose = TRUE)\n\n  appDir <- test_path(\"packages/utf8package\")\n  manifest <- makeManifest(\n    appDir,\n    appPrimaryDoc = \"R/hello.R\",\n    packageRepositoryResolutionR = \"lockfile\"\n  )\n  expect_equal(manifest$environment$r$requires, \">= 3.5.0\")\n  expect_equal(manifest$environment$r$package_repository_resolution, \"lockfile\")\n})\n\ntest_that(\"Node.js app gets correct manifest data\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\", \"engines\": {\"node\": \">=22.18.0\"}}',\n    \"package-lock.json\" = '{\"lockfileVersion\": 3}',\n    \"app.js\" = \"const http = require('http');\"\n  ))\n\n  manifest <- makeManifest(dir)\n  expect_equal(manifest$metadata$appmode, \"nodejs\")\n  expect_equal(manifest$metadata$entrypoint, \"app.js\")\n  expect_equal(manifest$metadata$has_parameters, FALSE)\n  expect_null(manifest$packages)\n  expect_null(manifest$quarto)\n  expect_null(manifest$python)\n  expect_type(manifest$nodejs, \"list\")\n  expect_named(manifest$files, c(\"app.js\", \"package-lock.json\", \"package.json\"))\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"Node.js minimal app (no main, no engines) gets correct manifest data\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\"}',\n    \"package-lock.json\" = '{\"lockfileVersion\": 3}',\n    \"index.js\" = \"\"\n  ))\n\n  manifest <- makeManifest(dir)\n  expect_equal(manifest$metadata$appmode, \"nodejs\")\n  expect_equal(manifest$metadata$entrypoint, \"index.js\")\n  expect_null(manifest$environment)\n  expect_known_manifest_fields(manifest)\n})\n\ntest_that(\"environment.nodejs.requires is set from engines.node\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\", \"engines\": {\"node\": \">=22.18.0\"}}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n\n  manifest <- makeManifest(dir)\n  expect_equal(manifest$environment$nodejs$requires, \">=22.18.0\")\n})\n\ntest_that(\"Sets environment.environment_management.nodejs in the manifest if envManagementNodejs is defined\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n\n  manifest <- makeManifest(dir, envManagementNodejs = TRUE)\n  expect_true(manifest$environment$environment_management$nodejs)\n\n  manifest <- makeManifest(dir, envManagementNodejs = FALSE)\n  expect_false(manifest$environment$environment_management$nodejs)\n\n  manifest <- makeManifest(dir)\n  expect_null(manifest$environment$environment_management)\n})\n\ntest_that(\"envManagement shorthand overrides envManagementNodejs\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n\n  manifest <- makeManifest(\n    dir,\n    envManagement = FALSE,\n    envManagementNodejs = TRUE\n  )\n  expect_false(manifest$environment$environment_management$nodejs)\n})\n\ntest_that(\"Node.js manifest nodejs field serializes as empty JSON object\", {\n  dir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\"}',\n    \"package-lock.json\" = \"{}\",\n    \"app.js\" = \"\"\n  ))\n\n  writeManifest(dir, quiet = TRUE)\n  jsonStr <- readLines(file.path(dir, \"manifest.json\"), warn = FALSE)\n  jsonStr <- paste(jsonStr, collapse = \"\\n\")\n  expect_match(jsonStr, '\"nodejs\":\\\\s*\\\\{\\\\s*\\\\}', perl = TRUE)\n  unlink(file.path(dir, \"manifest.json\"))\n})\n\ntest_that(\"Node.js end-to-end bundle produces valid tar.gz\", {\n  appDir <- local_temp_app(list(\n    \"package.json\" = '{\"name\": \"test\", \"main\": \"app.js\", \"engines\": {\"node\": \">=22.18.0\"}}',\n    \"package-lock.json\" = '{\"lockfileVersion\": 3}',\n    \"app.js\" = \"const http = require('http');\"\n  ))\n  appFiles <- bundleFiles(appDir)\n  appMeta <- appMetadata(appDir, appFiles)\n\n  bundlePath <- bundleApp(\n    appName = \"test-nodejs\",\n    appDir = appDir,\n    appFiles = appFiles,\n    appMetadata = appMeta,\n    quiet = TRUE\n  )\n  on.exit(unlink(bundlePath))\n\n  expect_true(file.exists(bundlePath))\n\n  extractDir <- tempfile()\n  on.exit(unlink(extractDir, recursive = TRUE), add = TRUE)\n  utils::untar(bundlePath, exdir = extractDir)\n\n  expect_true(file.exists(file.path(extractDir, \"manifest.json\")))\n  expect_true(file.exists(file.path(extractDir, \"app.js\")))\n  expect_true(file.exists(file.path(extractDir, \"package.json\")))\n  expect_true(file.exists(file.path(extractDir, \"package-lock.json\")))\n\n  manifest <- jsonlite::fromJSON(file.path(extractDir, \"manifest.json\"))\n  expect_equal(manifest$metadata$appmode, \"nodejs\")\n  expect_equal(manifest$metadata$entrypoint, \"app.js\")\n  expect_known_manifest_fields(manifest)\n})\n"
  },
  {
    "path": "tests/testthat.R",
    "content": "library(testthat)\nlibrary(rsconnect)\n\ntest_check(\"rsconnect\")\n"
  },
  {
    "path": "vignettes/.gitignore",
    "content": "*.html\n*.R\n"
  },
  {
    "path": "vignettes/custom-http.Rmd",
    "content": "---\ntitle: \"Customising HTTP requests\"\noutput: rmarkdown::html_vignette\nvignette: >\n  %\\VignetteIndexEntry{Customising HTTP requests}\n  %\\VignetteEngine{knitr::rmarkdown}\n  %\\VignetteEncoding{UTF-8}\n---\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#>\"\n)\n```\n\n```{r setup}\nlibrary(rsconnect)\n```\n\nDepending on the configuration of your environment, you may need to cutomize the way that rsconnect sends http requests. Typically, this is required for some special \n\n## `.rsconnect_profile`\n\nWhen deploying content from the RStudio IDE, the rsconnect package's deployment methods are executed in a vanilla R session that doesn't execute startup scripts. This can make it challenging to ensure options are set properly prior to push-button deployment, so the rsconnect package has a parallel set of \"startup\" scripts it runs prior to deploying. \n\nThe following are run in order, if they exist, prior to deployment:\n\n* `$R_HOME/etc/rsconnect.site`: Like `Rprofile.site` for site-wide pre-flight and options.\n  This is typically used by system administrators.\n  \n* `~/.rsconnect_profile`, like `.Rprofile`; this will affect all apps that you deploy.\n\n* `.rsconnect_profile`, like `.Rprofile`; this will affect the current app.\n  Unlike `.Rprofile`, if `~/.rsconnect_profile`, is present, it will also be run.\n\n## HTTP Proxy Environment Variable\n\nThe most straightforward way to specify a proxy is to set the `HTTPS_PROXY` environment variable. For example, you could add the following code to your `.rsconnect_profile`:\n\n```R\nSys.setenv(https_proxy = \"https://proxy.example.com\")\n```\n\nProxy settings can include a host-name, port, and username/password if necessary. The following\nare all valid values for the `http_proxy` environment variable:\n\n* `http://proxy.example.com/`\n* `http://proxy.example.com:1080/`\n* `http://username:password@proxy.example.com:1080/`\n\n## Custom headers and cookies\n\nIf you need to supply additional headers or cookies, you can use the options `rsconnect.http.headers` and `rsconnect.http.cookies` respectively. `rsconnect.http.headers` needs a named vector of header names and values:\n\n```{r}\noptions(\n  rsconnect.http.headers = c(\n    \"CustomHeader1\" = \"CustomValue\", \"CustomHeader2\" = \"CustomValue2\"\n  )\n)\n```\n\nWhile `rsconnect.http.cookies` expects cookies formatted the same way that a webserver expects them:\n\n```{r}\noptions(\n  rsconnect.http.headers = c(\"cookie1=value1\", \"cookie2=value2\")\n)\n```\n\nAnd you can supply other cookie parameters if needed:\n\n```{r}\noptions(\n  rsconnect.http.headers = \"cookie1=value1; Expires=Thu, 31 Oct 2021 07:28:00 GMT; Secure\"\n)\n```\n\nThe custom headers are set first, so will be overridden by the headers that rsconnect needs to correctly operate. Similarly, cookies will be set prior to the first request, and will be overriden by anything returned by the server.\n\n## Other custom options\n\nFinally, you can supply any additional options supported by `curl::curl_options()` with `rsconnect.libcurl.options`, e.g.\n\n```R\noptions(rsconnect.libcurl.options = list(proxy = \"http://proxy.example.com\")\n```\n\nRun `curl::curl_options()` to see a list of options.\n"
  }
]